blob: 0b0434dab895963f0132fcbabe7ca99d93b3e8a2 [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00003 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00004 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00006 */
7
bungemand3ebb482015-08-05 13:57:49 -07008#include "SkBitmapDevice.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +00009#include "SkCanvas.h"
reedd5fa1a42014-08-09 11:08:05 -070010#include "SkCanvasPriv.h"
bungemand3ebb482015-08-05 13:57:49 -070011#include "SkClipStack.h"
reeddbc3cef2015-04-29 12:18:57 -070012#include "SkColorFilter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013#include "SkDraw.h"
reed3cb38402015-02-06 08:36:15 -080014#include "SkDrawable.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000015#include "SkDrawFilter.h"
16#include "SkDrawLooper.h"
joshualitt5f5a8d72015-02-25 14:09:45 -080017#include "SkErrorInternals.h"
piotaixrb5fae932014-09-24 13:03:30 -070018#include "SkImage.h"
reed262a71b2015-12-05 13:07:27 -080019#include "SkImage_Base.h"
senorblanco900c3672016-04-27 11:31:23 -070020#include "SkImageFilter.h"
21#include "SkImageFilterCache.h"
msarettc573a402016-08-02 08:05:56 -070022#include "SkLatticeIter.h"
reed262a71b2015-12-05 13:07:27 -080023#include "SkMatrixUtils.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000024#include "SkMetaData.h"
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"
reed@google.com00177082011-10-12 14:34:30 +000028#include "SkRasterClip.h"
reed96472de2014-12-10 09:53:42 -080029#include "SkReadPixelsRec.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000030#include "SkRRect.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000031#include "SkSmallAllocator.h"
robertphillips4418dba2016-03-07 12:45:14 -080032#include "SkSpecialImage.h"
reed@google.com97af1a62012-08-28 12:19:02 +000033#include "SkSurface_Base.h"
fmalita7ba7aa72014-08-29 09:46:36 -070034#include "SkTextBlob.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000035#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000036#include "SkTLazy.h"
danakj8f757f52014-11-04 11:48:43 -080037#include "SkTraceEvent.h"
bungemand3ebb482015-08-05 13:57:49 -070038
39#include <new>
reed@android.com8a1c16f2008-12-17 15:59:43 +000040
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000041#if SK_SUPPORT_GPU
robertphillips7354a4b2015-12-16 05:08:27 -080042#include "GrContext.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000043#include "GrRenderTarget.h"
bsalomon614d8f92016-07-13 15:42:40 -070044#include "SkGrPriv.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000045#endif
46
reede3b38ce2016-01-08 09:18:44 -080047#define RETURN_ON_NULL(ptr) do { if (nullptr == (ptr)) return; } while (0)
48
reed2d1afab2016-06-29 14:33:11 -070049//#define SK_SUPPORT_PRECHECK_CLIPRECT
50
reedc83a2972015-07-16 07:40:45 -070051/*
52 * Return true if the drawing this rect would hit every pixels in the canvas.
53 *
54 * Returns false if
55 * - rect does not contain the canvas' bounds
56 * - paint is not fill
57 * - paint would blur or otherwise change the coverage of the rect
58 */
59bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
60 ShaderOverrideOpacity overrideOpacity) const {
bungeman99fe8222015-08-20 07:57:51 -070061 static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
62 (int)kNone_ShaderOverrideOpacity,
63 "need_matching_enums0");
64 static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
65 (int)kOpaque_ShaderOverrideOpacity,
66 "need_matching_enums1");
67 static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
68 (int)kNotOpaque_ShaderOverrideOpacity,
69 "need_matching_enums2");
reedc83a2972015-07-16 07:40:45 -070070
71 const SkISize size = this->getBaseLayerSize();
72 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
73 if (!this->getClipStack()->quickContains(bounds)) {
74 return false;
75 }
76
77 if (rect) {
reed6092b6e2016-07-10 11:45:34 -070078 if (!this->getTotalMatrix().isScaleTranslate()) {
reedc83a2972015-07-16 07:40:45 -070079 return false; // conservative
80 }
reed6092b6e2016-07-10 11:45:34 -070081
82 SkRect devRect;
83 this->getTotalMatrix().mapRectScaleTranslate(&devRect, *rect);
84 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -070085 return false;
86 }
87 }
88
89 if (paint) {
90 SkPaint::Style paintStyle = paint->getStyle();
91 if (!(paintStyle == SkPaint::kFill_Style ||
92 paintStyle == SkPaint::kStrokeAndFill_Style)) {
93 return false;
94 }
95 if (paint->getMaskFilter() || paint->getLooper()
96 || paint->getPathEffect() || paint->getImageFilter()) {
97 return false; // conservative
98 }
99 }
100 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
101}
102
103///////////////////////////////////////////////////////////////////////////////////////////////////
104
reedd990e2f2014-12-22 11:58:30 -0800105static bool gIgnoreSaveLayerBounds;
106void SkCanvas::Internal_Private_SetIgnoreSaveLayerBounds(bool ignore) {
107 gIgnoreSaveLayerBounds = ignore;
108}
109bool SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds() {
110 return gIgnoreSaveLayerBounds;
111}
112
reed0acf1b42014-12-22 16:12:38 -0800113static bool gTreatSpriteAsBitmap;
114void SkCanvas::Internal_Private_SetTreatSpriteAsBitmap(bool spriteAsBitmap) {
115 gTreatSpriteAsBitmap = spriteAsBitmap;
116}
117bool SkCanvas::Internal_Private_GetTreatSpriteAsBitmap() {
118 return gTreatSpriteAsBitmap;
119}
120
reed@google.comda17f752012-08-16 18:27:05 +0000121// experimental for faster tiled drawing...
122//#define SK_ENABLE_CLIP_QUICKREJECT
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
reed4a8126e2014-09-22 07:29:03 -0700175static uint32_t filter_paint_flags(const SkSurfaceProps& props, uint32_t flags) {
176 const uint32_t propFlags = props.flags();
177 if (propFlags & SkSurfaceProps::kDisallowDither_Flag) {
178 flags &= ~SkPaint::kDither_Flag;
179 }
180 if (propFlags & SkSurfaceProps::kDisallowAntiAlias_Flag) {
181 flags &= ~SkPaint::kAntiAlias_Flag;
182 }
183 return flags;
184}
185
186///////////////////////////////////////////////////////////////////////////////
187
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000188/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000189 The clip/matrix/proc are fields that reflect the top of the save/restore
190 stack. Whenever the canvas changes, it marks a dirty flag, and then before
191 these are used (assuming we're not on a layer) we rebuild these cache
192 values: they reflect the top of the save stack, but translated and clipped
193 by the device's XY offset and bitmap-bounds.
194*/
195struct DeviceCM {
196 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000197 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000198 SkRasterClip fClip;
reed@google.com6f8f2922011-03-04 22:27:10 +0000199 SkPaint* fPaint; // may be null (in the future)
reed61f501f2015-04-29 08:34:00 -0700200 const SkMatrix* fMatrix;
201 SkMatrix fMatrixStorage;
reed8c30a812016-04-20 16:36:51 -0700202 SkMatrix fStashedMatrix; // original CTM; used by imagefilter in saveLayer
reed@android.com8a1c16f2008-12-17 15:59:43 +0000203
reed96e657d2015-03-10 17:30:07 -0700204 DeviceCM(SkBaseDevice* device, const SkPaint* paint, SkCanvas* canvas,
reed7503d602016-07-15 14:23:29 -0700205 bool conservativeRasterClip, const SkMatrix& stashed)
halcanary96fcdcc2015-08-27 07:41:13 -0700206 : fNext(nullptr)
reedd9544982014-09-09 18:46:22 -0700207 , fClip(conservativeRasterClip)
reed8c30a812016-04-20 16:36:51 -0700208 , fStashedMatrix(stashed)
reedd9544982014-09-09 18:46:22 -0700209 {
reed2c9e2002016-07-25 08:05:22 -0700210 SkSafeRef(device);
reed@google.com4b226022011-01-11 18:32:13 +0000211 fDevice = device;
halcanary96fcdcc2015-08-27 07:41:13 -0700212 fPaint = paint ? new SkPaint(*paint) : nullptr;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000213 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000214
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000215 ~DeviceCM() {
reed2c9e2002016-07-25 08:05:22 -0700216 SkSafeUnref(fDevice);
halcanary385fe4d2015-08-26 13:07:48 -0700217 delete fPaint;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000218 }
reed@google.com4b226022011-01-11 18:32:13 +0000219
mtkleinfeaadee2015-04-08 11:25:48 -0700220 void reset(const SkIRect& bounds) {
221 SkASSERT(!fPaint);
222 SkASSERT(!fNext);
223 SkASSERT(fDevice);
224 fClip.setRect(bounds);
225 }
226
reed@google.com045e62d2011-10-24 12:19:46 +0000227 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
228 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000229 int x = fDevice->getOrigin().x();
230 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231 int width = fDevice->width();
232 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000233
reed@android.com8a1c16f2008-12-17 15:59:43 +0000234 if ((x | y) == 0) {
235 fMatrix = &totalMatrix;
236 fClip = totalClip;
237 } else {
238 fMatrixStorage = totalMatrix;
239 fMatrixStorage.postTranslate(SkIntToScalar(-x),
240 SkIntToScalar(-y));
241 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000242
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243 totalClip.translate(-x, -y, &fClip);
244 }
245
reed@google.com045e62d2011-10-24 12:19:46 +0000246 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247
248 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000249
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000251 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252 SkRegion::kDifference_Op);
253 }
reed@google.com4b226022011-01-11 18:32:13 +0000254
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000255 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
256
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257#ifdef SK_DEBUG
258 if (!fClip.isEmpty()) {
259 SkIRect deviceR;
260 deviceR.set(0, 0, width, height);
261 SkASSERT(deviceR.contains(fClip.getBounds()));
262 }
263#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000264 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265};
266
267/* This is the record we keep for each save/restore level in the stack.
268 Since a level optionally copies the matrix and/or stack, we have pointers
269 for these fields. If the value is copied for this level, the copy is
270 stored in the ...Storage field, and the pointer points to that. If the
271 value is not copied for this level, we ignore ...Storage, and just point
272 at the corresponding value in the previous level in the stack.
273*/
274class SkCanvas::MCRec {
275public:
reed1f836ee2014-07-07 07:49:34 -0700276 SkDrawFilter* fFilter; // the current filter (or null)
reedd9544982014-09-09 18:46:22 -0700277 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000278 /* If there are any layers in the stack, this points to the top-most
279 one that is at or below this level in the stack (so we know what
280 bitmap/device to draw into from this level. This value is NOT
281 reference counted, since the real owner is either our fLayer field,
282 or a previous one in a lower level.)
283 */
reed2ff1fce2014-12-11 07:07:37 -0800284 DeviceCM* fTopLayer;
285 SkRasterClip fRasterClip;
286 SkMatrix fMatrix;
287 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288
vjiaoblacke5de1302016-07-13 14:05:28 -0700289 // This is the current cumulative depth (aggregate of all done translateZ calls)
290 SkScalar fCurDrawDepth;
291
reedd9544982014-09-09 18:46:22 -0700292 MCRec(bool conservativeRasterClip) : fRasterClip(conservativeRasterClip) {
halcanary96fcdcc2015-08-27 07:41:13 -0700293 fFilter = nullptr;
294 fLayer = nullptr;
295 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800296 fMatrix.reset();
297 fDeferredSaveCount = 0;
vjiaoblacke5de1302016-07-13 14:05:28 -0700298 fCurDrawDepth = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700299
reedd9544982014-09-09 18:46:22 -0700300 // don't bother initializing fNext
301 inc_rec();
302 }
vjiaoblacke5de1302016-07-13 14:05:28 -0700303 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix),
304 fCurDrawDepth(prev.fCurDrawDepth) {
reedd9544982014-09-09 18:46:22 -0700305 fFilter = SkSafeRef(prev.fFilter);
halcanary96fcdcc2015-08-27 07:41:13 -0700306 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700307 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800308 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700309
reed@android.com8a1c16f2008-12-17 15:59:43 +0000310 // don't bother initializing fNext
311 inc_rec();
312 }
313 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000314 SkSafeUnref(fFilter);
halcanary385fe4d2015-08-26 13:07:48 -0700315 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000316 dec_rec();
317 }
mtkleinfeaadee2015-04-08 11:25:48 -0700318
319 void reset(const SkIRect& bounds) {
320 SkASSERT(fLayer);
321 SkASSERT(fDeferredSaveCount == 0);
322
323 fMatrix.reset();
324 fRasterClip.setRect(bounds);
325 fLayer->reset(bounds);
326 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000327};
328
329class SkDrawIter : public SkDraw {
330public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000331 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000332 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000333 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000334 canvas->updateDeviceCMCache();
335
reed687fa1c2015-04-07 08:00:56 -0700336 fClipStack = canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000337 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000338 fSkipEmptyClips = skipEmptyClips;
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() {
342 // skip over recs with empty clips
343 if (fSkipEmptyClips) {
344 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
345 fCurrLayer = fCurrLayer->fNext;
346 }
347 }
348
reed@google.comf68c5e22012-02-24 16:38:58 +0000349 const DeviceCM* rec = fCurrLayer;
350 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000351
352 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000353 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000354 fDevice = rec->fDevice;
reed41e010c2015-06-09 12:16:53 -0700355 if (!fDevice->accessPixels(&fDst)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700356 fDst.reset(fDevice->imageInfo(), nullptr, 0);
reed41e010c2015-06-09 12:16:53 -0700357 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000358 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000359 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000360
361 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700362 // fCurrLayer may be nullptr now
reed@android.com199f1082009-06-10 02:12:47 +0000363
reed@android.com8a1c16f2008-12-17 15:59:43 +0000364 return true;
365 }
366 return false;
367 }
reed@google.com4b226022011-01-11 18:32:13 +0000368
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000369 SkBaseDevice* getDevice() const { return fDevice; }
reed1e7f5e72016-04-27 07:49:17 -0700370 const SkRasterClip& getClip() const { return *fRC; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000371 int getX() const { return fDevice->getOrigin().x(); }
372 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000373 const SkMatrix& getMatrix() const { return *fMatrix; }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000374 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000375
reed@android.com8a1c16f2008-12-17 15:59:43 +0000376private:
377 SkCanvas* fCanvas;
378 const DeviceCM* fCurrLayer;
379 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000380 SkBool8 fSkipEmptyClips;
381
382 typedef SkDraw INHERITED;
383};
384
385/////////////////////////////////////////////////////////////////////////////
386
reeddbc3cef2015-04-29 12:18:57 -0700387static SkPaint* set_if_needed(SkLazyPaint* lazy, const SkPaint& orig) {
388 return lazy->isValid() ? lazy->get() : lazy->set(orig);
389}
390
391/**
392 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700393 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700394 */
reedd053ce92016-03-22 10:17:23 -0700395static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700396 SkImageFilter* imgf = paint.getImageFilter();
397 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700398 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700399 }
400
reedd053ce92016-03-22 10:17:23 -0700401 SkColorFilter* imgCFPtr;
402 if (!imgf->asAColorFilter(&imgCFPtr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700403 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700404 }
reedd053ce92016-03-22 10:17:23 -0700405 sk_sp<SkColorFilter> imgCF(imgCFPtr);
reeddbc3cef2015-04-29 12:18:57 -0700406
407 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700408 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700409 // there is no existing paint colorfilter, so we can just return the imagefilter's
410 return imgCF;
411 }
412
413 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
414 // and we need to combine them into a single colorfilter.
reedd053ce92016-03-22 10:17:23 -0700415 return SkColorFilter::MakeComposeFilter(std::move(imgCF), sk_ref_sp(paintCF));
reeddbc3cef2015-04-29 12:18:57 -0700416}
417
senorblanco87e066e2015-10-28 11:23:36 -0700418/**
419 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
420 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
421 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
422 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
423 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
424 * conservative "effective" bounds based on the settings in the paint... with one exception. This
425 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
426 * deliberately ignored.
427 */
428static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
429 const SkRect& rawBounds,
430 SkRect* storage) {
431 SkPaint tmpUnfiltered(paint);
432 tmpUnfiltered.setImageFilter(nullptr);
433 if (tmpUnfiltered.canComputeFastBounds()) {
434 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
435 } else {
436 return rawBounds;
437 }
438}
439
reed@android.com8a1c16f2008-12-17 15:59:43 +0000440class AutoDrawLooper {
441public:
senorblanco87e066e2015-10-28 11:23:36 -0700442 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
443 // paint. It's used to determine the size of the offscreen layer for filters.
444 // If null, the clip will be used instead.
reed4a8126e2014-09-22 07:29:03 -0700445 AutoDrawLooper(SkCanvas* canvas, const SkSurfaceProps& props, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000446 bool skipLayerForImageFilter = false,
senorblanco87e066e2015-10-28 11:23:36 -0700447 const SkRect* rawBounds = nullptr) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000448 fCanvas = canvas;
fmalita53d9f1c2016-01-25 06:23:54 -0800449#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450 fFilter = canvas->getDrawFilter();
fmalita77650002016-01-21 18:47:11 -0800451#else
452 fFilter = nullptr;
453#endif
reed4a8126e2014-09-22 07:29:03 -0700454 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000455 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700456 fTempLayerForImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000457 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000458
reedd053ce92016-03-22 10:17:23 -0700459 auto simplifiedCF = image_to_color_filter(fOrigPaint);
reeddbc3cef2015-04-29 12:18:57 -0700460 if (simplifiedCF) {
461 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reedd053ce92016-03-22 10:17:23 -0700462 paint->setColorFilter(std::move(simplifiedCF));
halcanary96fcdcc2015-08-27 07:41:13 -0700463 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700464 fPaint = paint;
465 }
466
467 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700468 /**
469 * We implement ImageFilters for a given draw by creating a layer, then applying the
470 * imagefilter to the pixels of that layer (its backing surface/image), and then
471 * we call restore() to xfer that layer to the main canvas.
472 *
473 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
474 * 2. Generate the src pixels:
475 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
476 * return (fPaint). We then draw the primitive (using srcover) into a cleared
477 * buffer/surface.
478 * 3. Restore the layer created in #1
479 * The imagefilter is passed the buffer/surface from the layer (now filled with the
480 * src pixels of the primitive). It returns a new "filtered" buffer, which we
481 * draw onto the previous layer using the xfermode from the original paint.
482 */
reed@google.com8926b162012-03-23 15:36:36 +0000483 SkPaint tmp;
reeddbc3cef2015-04-29 12:18:57 -0700484 tmp.setImageFilter(fPaint->getImageFilter());
reedcfb6bdf2016-03-29 11:32:50 -0700485 tmp.setXfermode(sk_ref_sp(fPaint->getXfermode()));
senorblanco87e066e2015-10-28 11:23:36 -0700486 SkRect storage;
487 if (rawBounds) {
488 // Make rawBounds include all paint outsets except for those due to image filters.
489 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
490 }
reedbfd5f172016-01-07 11:28:08 -0800491 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &tmp),
reed76033be2015-03-14 10:54:31 -0700492 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700493 fTempLayerForImageFilter = true;
494 // we remove the imagefilter/xfermode inside doNext()
reed@google.com8926b162012-03-23 15:36:36 +0000495 }
496
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000497 if (SkDrawLooper* looper = paint.getLooper()) {
498 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
499 looper->contextSize());
500 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000501 fIsSimple = false;
502 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700503 fLooperContext = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000504 // can we be marked as simple?
reed5c476fb2015-04-20 08:04:21 -0700505 fIsSimple = !fFilter && !fTempLayerForImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000506 }
piotaixrb5fae932014-09-24 13:03:30 -0700507
reed4a8126e2014-09-22 07:29:03 -0700508 uint32_t oldFlags = paint.getFlags();
509 fNewPaintFlags = filter_paint_flags(props, oldFlags);
510 if (fIsSimple && (fNewPaintFlags != oldFlags)) {
reeddbc3cef2015-04-29 12:18:57 -0700511 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700512 paint->setFlags(fNewPaintFlags);
513 fPaint = paint;
514 // if we're not simple, doNext() will take care of calling setFlags()
515 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000516 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000517
reed@android.com8a1c16f2008-12-17 15:59:43 +0000518 ~AutoDrawLooper() {
reed5c476fb2015-04-20 08:04:21 -0700519 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000520 fCanvas->internalRestore();
521 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000522 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000523 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000524
reed@google.com4e2b3d32011-04-07 14:18:59 +0000525 const SkPaint& paint() const {
526 SkASSERT(fPaint);
527 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000528 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000529
reed@google.com129ec222012-05-15 13:24:09 +0000530 bool next(SkDrawFilter::Type drawType) {
531 if (fDone) {
532 return false;
533 } else if (fIsSimple) {
534 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000535 return !fPaint->nothingToDraw();
536 } else {
537 return this->doNext(drawType);
538 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000539 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000540
reed@android.com8a1c16f2008-12-17 15:59:43 +0000541private:
reeddbc3cef2015-04-29 12:18:57 -0700542 SkLazyPaint fLazyPaintInit; // base paint storage in case we need to modify it
543 SkLazyPaint fLazyPaintPerLooper; // per-draw-looper storage, so the looper can modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000544 SkCanvas* fCanvas;
545 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000546 SkDrawFilter* fFilter;
547 const SkPaint* fPaint;
548 int fSaveCount;
reed4a8126e2014-09-22 07:29:03 -0700549 uint32_t fNewPaintFlags;
reed5c476fb2015-04-20 08:04:21 -0700550 bool fTempLayerForImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000551 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000552 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000553 SkDrawLooper::Context* fLooperContext;
554 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000555
556 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000557};
558
reed@google.com129ec222012-05-15 13:24:09 +0000559bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
halcanary96fcdcc2015-08-27 07:41:13 -0700560 fPaint = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000561 SkASSERT(!fIsSimple);
reed5c476fb2015-04-20 08:04:21 -0700562 SkASSERT(fLooperContext || fFilter || fTempLayerForImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000563
reeddbc3cef2015-04-29 12:18:57 -0700564 SkPaint* paint = fLazyPaintPerLooper.set(fLazyPaintInit.isValid() ?
565 *fLazyPaintInit.get() : fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700566 paint->setFlags(fNewPaintFlags);
reed@google.com129ec222012-05-15 13:24:09 +0000567
reed5c476fb2015-04-20 08:04:21 -0700568 if (fTempLayerForImageFilter) {
halcanary96fcdcc2015-08-27 07:41:13 -0700569 paint->setImageFilter(nullptr);
570 paint->setXfermode(nullptr);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000571 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000572
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000573 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000574 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000575 return false;
576 }
577 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000578 if (!fFilter->filter(paint, drawType)) {
579 fDone = true;
580 return false;
581 }
halcanary96fcdcc2015-08-27 07:41:13 -0700582 if (nullptr == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000583 // no looper means we only draw once
584 fDone = true;
585 }
586 }
587 fPaint = paint;
588
589 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000590 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000591 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000592 }
593
594 // call this after any possible paint modifiers
595 if (fPaint->nothingToDraw()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700596 fPaint = nullptr;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000597 return false;
598 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000599 return true;
600}
601
reed@android.com8a1c16f2008-12-17 15:59:43 +0000602////////// macros to place around the internal draw calls //////////////////
603
reed262a71b2015-12-05 13:07:27 -0800604#define LOOPER_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
605 this->predrawNotify(); \
606 AutoDrawLooper looper(this, fProps, paint, skipLayerForFilter, bounds); \
607 while (looper.next(SkDrawFilter::kBitmap_Type)) { \
608 SkDrawIter iter(this);
609
610
reed@google.com8926b162012-03-23 15:36:36 +0000611#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000612 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700613 AutoDrawLooper looper(this, fProps, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000614 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000615 SkDrawIter iter(this);
616
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000617#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000618 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700619 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000620 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000621 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000622
reedc83a2972015-07-16 07:40:45 -0700623#define LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, type, bounds, auxOpaque) \
624 this->predrawNotify(bounds, &paint, auxOpaque); \
625 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
626 while (looper.next(type)) { \
627 SkDrawIter iter(this);
628
reed@google.com4e2b3d32011-04-07 14:18:59 +0000629#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000630
631////////////////////////////////////////////////////////////////////////////
632
mtkleinfeaadee2015-04-08 11:25:48 -0700633void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
634 this->restoreToCount(1);
635 fCachedLocalClipBounds.setEmpty();
636 fCachedLocalClipBoundsDirty = true;
637 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());
643}
644
reedd9544982014-09-09 18:46:22 -0700645SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
reed42b73eb2015-11-20 13:42:42 -0800646 if (device && device->forceConservativeRasterClip()) {
647 flags = InitFlags(flags | kConservativeRasterClip_InitFlag);
648 }
649 // Since init() is only called once by our constructors, it is safe to perform this
650 // const-cast.
651 *const_cast<bool*>(&fConservativeRasterClip) = SkToBool(flags & kConservativeRasterClip_InitFlag);
652
reed@google.comc0784db2013-12-13 21:16:12 +0000653 fCachedLocalClipBounds.setEmpty();
654 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000655 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000656 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700657 fDeviceCMDirty = true;
reed2ff1fce2014-12-11 07:07:37 -0800658 fSaveCount = 1;
halcanary96fcdcc2015-08-27 07:41:13 -0700659 fMetaData = nullptr;
vjiaoblack95302da2016-07-21 10:25:54 -0700660#ifdef SK_EXPERIMENTAL_SHADOWING
661 fLights = nullptr;
662#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000663
halcanary385fe4d2015-08-26 13:07:48 -0700664 fClipStack.reset(new SkClipStack);
reed687fa1c2015-04-07 08:00:56 -0700665
reed@android.com8a1c16f2008-12-17 15:59:43 +0000666 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700667 new (fMCRec) MCRec(fConservativeRasterClip);
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());
reedf92c8662014-08-18 08:02:43 -0700683 }
684 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000685}
686
reed@google.comcde92112011-07-06 20:00:52 +0000687SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000688 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700689 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800690 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000691{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000692 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000693
halcanary96fcdcc2015-08-27 07:41:13 -0700694 this->init(nullptr, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000695}
696
reedd9544982014-09-09 18:46:22 -0700697static SkBitmap make_nopixels(int width, int height) {
698 SkBitmap bitmap;
699 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
700 return bitmap;
701}
702
703class SkNoPixelsBitmapDevice : public SkBitmapDevice {
704public:
robertphillipsfcf78292015-06-19 11:49:52 -0700705 SkNoPixelsBitmapDevice(const SkIRect& bounds, const SkSurfaceProps& surfaceProps)
706 : INHERITED(make_nopixels(bounds.width(), bounds.height()), surfaceProps)
reed78e27682014-11-19 08:04:34 -0800707 {
708 this->setOrigin(bounds.x(), bounds.y());
709 }
reedd9544982014-09-09 18:46:22 -0700710
711private:
piotaixrb5fae932014-09-24 13:03:30 -0700712
reedd9544982014-09-09 18:46:22 -0700713 typedef SkBitmapDevice INHERITED;
714};
715
reed96a857e2015-01-25 10:33:58 -0800716SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000717 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800718 , fProps(SkSurfacePropsCopyOrDefault(props))
reed42b73eb2015-11-20 13:42:42 -0800719 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000720{
721 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700722
halcanary385fe4d2015-08-26 13:07:48 -0700723 this->init(new SkNoPixelsBitmapDevice(SkIRect::MakeWH(width, height), fProps),
724 kDefault_InitFlags)->unref();
reedd9544982014-09-09 18:46:22 -0700725}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000726
reed78e27682014-11-19 08:04:34 -0800727SkCanvas::SkCanvas(const SkIRect& bounds, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700728 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700729 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800730 , fConservativeRasterClip(false)
reedd9544982014-09-09 18:46:22 -0700731{
732 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700733
halcanary385fe4d2015-08-26 13:07:48 -0700734 this->init(new SkNoPixelsBitmapDevice(bounds, fProps), flags)->unref();
reedd9544982014-09-09 18:46:22 -0700735}
736
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000737SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000738 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700739 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800740 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000741{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000742 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700743
reedd9544982014-09-09 18:46:22 -0700744 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000745}
746
robertphillipsfcf78292015-06-19 11:49:52 -0700747SkCanvas::SkCanvas(SkBaseDevice* device, InitFlags flags)
748 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700749 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800750 , fConservativeRasterClip(false)
robertphillipsfcf78292015-06-19 11:49:52 -0700751{
752 inc_canvas();
753
754 this->init(device, flags);
755}
756
reed4a8126e2014-09-22 07:29:03 -0700757SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700758 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700759 , fProps(props)
reed42b73eb2015-11-20 13:42:42 -0800760 , fConservativeRasterClip(false)
reed3716fd02014-09-21 09:39:55 -0700761{
762 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700763
halcanary385fe4d2015-08-26 13:07:48 -0700764 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700765 this->init(device, kDefault_InitFlags);
766}
reed29c857d2014-09-21 10:25:07 -0700767
reed4a8126e2014-09-22 07:29:03 -0700768SkCanvas::SkCanvas(const SkBitmap& bitmap)
769 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
770 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800771 , fConservativeRasterClip(false)
reed4a8126e2014-09-22 07:29:03 -0700772{
773 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700774
halcanary385fe4d2015-08-26 13:07:48 -0700775 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700776 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000777}
778
779SkCanvas::~SkCanvas() {
780 // free up the contents of our deque
781 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000782
reed@android.com8a1c16f2008-12-17 15:59:43 +0000783 this->internalRestore(); // restore the last, since we're going away
784
halcanary385fe4d2015-08-26 13:07:48 -0700785 delete fMetaData;
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000786
reed@android.com8a1c16f2008-12-17 15:59:43 +0000787 dec_canvas();
788}
789
fmalita53d9f1c2016-01-25 06:23:54 -0800790#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000791SkDrawFilter* SkCanvas::getDrawFilter() const {
792 return fMCRec->fFilter;
793}
794
795SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
reed51985e32015-04-11 08:04:56 -0700796 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000797 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
798 return filter;
799}
fmalita77650002016-01-21 18:47:11 -0800800#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000801
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000802SkMetaData& SkCanvas::getMetaData() {
803 // metadata users are rare, so we lazily allocate it. If that changes we
804 // can decide to just make it a field in the device (rather than a ptr)
halcanary96fcdcc2015-08-27 07:41:13 -0700805 if (nullptr == fMetaData) {
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000806 fMetaData = new SkMetaData;
807 }
808 return *fMetaData;
809}
810
reed@android.com8a1c16f2008-12-17 15:59:43 +0000811///////////////////////////////////////////////////////////////////////////////
812
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000813void SkCanvas::flush() {
reedea5a6512016-07-07 16:44:27 -0700814 this->onFlush();
815}
816
817void SkCanvas::onFlush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000818 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000819 if (device) {
820 device->flush();
821 }
822}
823
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000824SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000825 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000826 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
827}
828
senorblancoafc7cce2016-02-02 18:44:15 -0800829SkIRect SkCanvas::getTopLayerBounds() const {
830 SkBaseDevice* d = this->getTopDevice();
831 if (!d) {
832 return SkIRect::MakeEmpty();
833 }
834 return SkIRect::MakeXYWH(d->getOrigin().x(), d->getOrigin().y(), d->width(), d->height());
835}
836
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000837SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000838 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000839 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000840 SkASSERT(rec && rec->fLayer);
841 return rec->fLayer->fDevice;
842}
843
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000844SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000845 if (updateMatrixClip) {
846 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
847 }
reed@google.com9266fed2011-03-30 00:18:03 +0000848 return fMCRec->fTopLayer->fDevice;
849}
850
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000851bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
reedc7ec7c92016-07-25 08:29:10 -0700852 if (kUnknown_SkColorType == bitmap->colorType()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000853 return false;
854 }
855
856 bool weAllocated = false;
halcanary96fcdcc2015-08-27 07:41:13 -0700857 if (nullptr == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700858 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000859 return false;
860 }
861 weAllocated = true;
862 }
863
reedcf01e312015-05-23 19:14:51 -0700864 SkAutoPixmapUnlock unlocker;
865 if (bitmap->requestLock(&unlocker)) {
866 const SkPixmap& pm = unlocker.pixmap();
867 if (this->readPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), x, y)) {
868 return true;
869 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000870 }
871
872 if (weAllocated) {
halcanary96fcdcc2015-08-27 07:41:13 -0700873 bitmap->setPixelRef(nullptr);
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000874 }
875 return false;
876}
reed@google.com51df9e32010-12-23 19:29:18 +0000877
bsalomon@google.comc6980972011-11-02 19:57:21 +0000878bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000879 SkIRect r = srcRect;
880 const SkISize size = this->getBaseLayerSize();
881 if (!r.intersect(0, 0, size.width(), size.height())) {
882 bitmap->reset();
883 return false;
884 }
885
reed84825042014-09-02 12:50:45 -0700886 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000887 // bitmap will already be reset.
888 return false;
889 }
890 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
891 bitmap->reset();
892 return false;
893 }
894 return true;
895}
896
reed96472de2014-12-10 09:53:42 -0800897bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000898 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000899 if (!device) {
900 return false;
901 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000902 const SkISize size = this->getBaseLayerSize();
mtkleinf0f14112014-12-12 08:46:25 -0800903
reed96472de2014-12-10 09:53:42 -0800904 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
905 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000906 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000907 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000908
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000909 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800910 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000911}
912
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000913bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
reedcf01e312015-05-23 19:14:51 -0700914 SkAutoPixmapUnlock unlocker;
915 if (bitmap.requestLock(&unlocker)) {
916 const SkPixmap& pm = unlocker.pixmap();
917 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000918 }
919 return false;
920}
921
922bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
923 int x, int y) {
924 switch (origInfo.colorType()) {
925 case kUnknown_SkColorType:
926 case kIndex_8_SkColorType:
927 return false;
928 default:
929 break;
930 }
halcanary96fcdcc2015-08-27 07:41:13 -0700931 if (nullptr == pixels || rowBytes < origInfo.minRowBytes()) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000932 return false;
933 }
934
935 const SkISize size = this->getBaseLayerSize();
936 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
937 if (!target.intersect(0, 0, size.width(), size.height())) {
938 return false;
939 }
940
941 SkBaseDevice* device = this->getDevice();
942 if (!device) {
943 return false;
944 }
945
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000946 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700947 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000948
949 // if x or y are negative, then we have to adjust pixels
950 if (x > 0) {
951 x = 0;
952 }
953 if (y > 0) {
954 y = 0;
955 }
956 // here x,y are either 0 or negative
957 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
958
reed4af35f32014-06-27 17:47:49 -0700959 // Tell our owning surface to bump its generation ID
reedc83a2972015-07-16 07:40:45 -0700960 const bool completeOverwrite = info.dimensions() == size;
961 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700962
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000963 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000964 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000965}
reed@google.com51df9e32010-12-23 19:29:18 +0000966
junov@google.com4370aed2012-01-18 16:21:08 +0000967SkCanvas* SkCanvas::canvasForDrawIter() {
968 return this;
969}
970
reed@android.com8a1c16f2008-12-17 15:59:43 +0000971//////////////////////////////////////////////////////////////////////////////
972
reed@android.com8a1c16f2008-12-17 15:59:43 +0000973void SkCanvas::updateDeviceCMCache() {
974 if (fDeviceCMDirty) {
975 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700976 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000977 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000978
halcanary96fcdcc2015-08-27 07:41:13 -0700979 if (nullptr == layer->fNext) { // only one layer
980 layer->updateMC(totalMatrix, totalClip, *fClipStack, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000981 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000982 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000983 do {
reed687fa1c2015-04-07 08:00:56 -0700984 layer->updateMC(totalMatrix, clip, *fClipStack, &clip);
halcanary96fcdcc2015-08-27 07:41:13 -0700985 } while ((layer = layer->fNext) != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000986 }
987 fDeviceCMDirty = false;
988 }
989}
990
reed@android.com8a1c16f2008-12-17 15:59:43 +0000991///////////////////////////////////////////////////////////////////////////////
992
reed2ff1fce2014-12-11 07:07:37 -0800993void SkCanvas::checkForDeferredSave() {
994 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800995 this->doSave();
996 }
997}
998
reedf0090cb2014-11-26 08:55:51 -0800999int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -08001000#ifdef SK_DEBUG
1001 int count = 0;
1002 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
1003 for (;;) {
1004 const MCRec* rec = (const MCRec*)iter.next();
1005 if (!rec) {
1006 break;
1007 }
1008 count += 1 + rec->fDeferredSaveCount;
1009 }
1010 SkASSERT(count == fSaveCount);
1011#endif
1012 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -08001013}
1014
1015int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -08001016 fSaveCount += 1;
1017 fMCRec->fDeferredSaveCount += 1;
1018 return this->getSaveCount() - 1; // return our prev value
1019}
1020
1021void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -08001022 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -07001023
1024 SkASSERT(fMCRec->fDeferredSaveCount > 0);
1025 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001026 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -08001027}
1028
1029void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -08001030 if (fMCRec->fDeferredSaveCount > 0) {
1031 SkASSERT(fSaveCount > 1);
1032 fSaveCount -= 1;
1033 fMCRec->fDeferredSaveCount -= 1;
1034 } else {
1035 // check for underflow
1036 if (fMCStack.count() > 1) {
1037 this->willRestore();
1038 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -07001039 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001040 this->internalRestore();
1041 this->didRestore();
1042 }
reedf0090cb2014-11-26 08:55:51 -08001043 }
1044}
1045
1046void SkCanvas::restoreToCount(int count) {
1047 // sanity check
1048 if (count < 1) {
1049 count = 1;
1050 }
mtkleinf0f14112014-12-12 08:46:25 -08001051
reedf0090cb2014-11-26 08:55:51 -08001052 int n = this->getSaveCount() - count;
1053 for (int i = 0; i < n; ++i) {
1054 this->restore();
1055 }
1056}
1057
reed2ff1fce2014-12-11 07:07:37 -08001058void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001059 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -07001060 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +00001061 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +00001062
reed687fa1c2015-04-07 08:00:56 -07001063 fClipStack->save();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001064}
1065
reed4960eee2015-12-18 07:09:18 -08001066bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
reed@google.comb93ba452014-03-10 19:47:58 +00001067#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001068 return !(saveLayerFlags & SkCanvas::kDontClipToLayer_PrivateSaveLayerFlag);
reed@google.comb93ba452014-03-10 19:47:58 +00001069#else
1070 return true;
1071#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001072}
1073
reed4960eee2015-12-18 07:09:18 -08001074bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -07001075 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001076 SkIRect clipBounds;
1077 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001078 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +00001079 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001080
reed96e657d2015-03-10 17:30:07 -07001081 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
1082
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001083 if (imageFilter) {
senorblancoe5e79842016-03-21 14:51:59 -07001084 clipBounds = imageFilter->filterBounds(clipBounds, ctm);
senorblancodb64af32015-12-09 10:11:43 -08001085 if (bounds && !imageFilter->canComputeFastBounds()) {
1086 bounds = nullptr;
1087 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001088 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001089 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -07001090 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001091 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +00001092
reed96e657d2015-03-10 17:30:07 -07001093 ctm.mapRect(&r, *bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001094 r.roundOut(&ir);
1095 // early exit if the layer's bounds are clipped out
1096 if (!ir.intersect(clipBounds)) {
reed4960eee2015-12-18 07:09:18 -08001097 if (BoundsAffectsClip(saveLayerFlags)) {
reed9b3aa542015-03-11 08:47:12 -07001098 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001099 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001100 }
junov@chromium.orga907ac32012-02-24 21:54:07 +00001101 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001102 }
1103 } else { // no user bounds, so just use the clip
1104 ir = clipBounds;
1105 }
reed180aec42015-03-11 10:39:04 -07001106 SkASSERT(!ir.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001107
reed4960eee2015-12-18 07:09:18 -08001108 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -07001109 // Simplify the current clips since they will be applied properly during restore()
reed9b3aa542015-03-11 08:47:12 -07001110 fCachedLocalClipBoundsDirty = true;
reed687fa1c2015-04-07 08:00:56 -07001111 fClipStack->clipDevRect(ir, SkRegion::kReplace_Op);
reed180aec42015-03-11 10:39:04 -07001112 fMCRec->fRasterClip.setRect(ir);
junov@chromium.orga907ac32012-02-24 21:54:07 +00001113 }
1114
1115 if (intersection) {
1116 *intersection = ir;
1117 }
1118 return true;
1119}
1120
reed4960eee2015-12-18 07:09:18 -08001121
reed4960eee2015-12-18 07:09:18 -08001122int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
1123 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001124}
1125
reed70ee31b2015-12-10 13:44:45 -08001126int SkCanvas::saveLayerPreserveLCDTextRequests(const SkRect* bounds, const SkPaint* paint) {
reed4960eee2015-12-18 07:09:18 -08001127 return this->saveLayer(SaveLayerRec(bounds, paint, kPreserveLCDText_SaveLayerFlag));
1128}
1129
1130int SkCanvas::saveLayer(const SaveLayerRec& origRec) {
1131 SaveLayerRec rec(origRec);
1132 if (gIgnoreSaveLayerBounds) {
1133 rec.fBounds = nullptr;
1134 }
1135 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
1136 fSaveCount += 1;
1137 this->internalSaveLayer(rec, strategy);
1138 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -08001139}
1140
reeda2217ef2016-07-20 06:04:34 -07001141void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
1142 SkBaseDevice* dst, const SkMatrix& ctm,
1143 const SkClipStack* clipStack) {
1144 SkDraw draw;
1145 SkRasterClip rc;
1146 rc.setRect(SkIRect::MakeWH(dst->width(), dst->height()));
1147 if (!dst->accessPixels(&draw.fDst)) {
1148 draw.fDst.reset(dst->imageInfo(), nullptr, 0);
robertphillips7354a4b2015-12-16 05:08:27 -08001149 }
reeda2217ef2016-07-20 06:04:34 -07001150 draw.fMatrix = &SkMatrix::I();
1151 draw.fRC = &rc;
1152 draw.fClipStack = clipStack;
1153 draw.fDevice = dst;
robertphillips7354a4b2015-12-16 05:08:27 -08001154
1155 SkPaint p;
robertphillips372177e2016-03-30 07:32:28 -07001156 p.setImageFilter(filter->makeWithLocalMatrix(ctm));
reeda2217ef2016-07-20 06:04:34 -07001157
1158 int x = src->getOrigin().x() - dst->getOrigin().x();
1159 int y = src->getOrigin().y() - dst->getOrigin().y();
1160 auto special = src->snapSpecial();
1161 if (special) {
1162 dst->drawSpecial(draw, special.get(), x, y, p);
1163 }
robertphillips7354a4b2015-12-16 05:08:27 -08001164}
reed70ee31b2015-12-10 13:44:45 -08001165
reed129ed1c2016-02-22 06:42:31 -08001166static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, bool isOpaque,
1167 const SkPaint* paint) {
1168 // need to force L32 for now if we have an image filter. Once filters support other colortypes
1169 // e.g. sRGB or F16, we can remove this check
brianosman52ede1d2016-06-20 08:25:02 -07001170 // SRGBTODO: Can we remove this check now?
reed129ed1c2016-02-22 06:42:31 -08001171 const bool hasImageFilter = paint && paint->getImageFilter();
1172
1173 SkAlphaType alphaType = isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
1174 if ((prev.bytesPerPixel() < 4) || hasImageFilter) {
1175 // force to L32
1176 return SkImageInfo::MakeN32(w, h, alphaType);
1177 } else {
1178 // keep the same characteristics as the prev
brianosman52ede1d2016-06-20 08:25:02 -07001179 return SkImageInfo::Make(w, h, prev.colorType(), alphaType, sk_ref_sp(prev.colorSpace()));
reed129ed1c2016-02-22 06:42:31 -08001180 }
1181}
1182
reed4960eee2015-12-18 07:09:18 -08001183void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
1184 const SkRect* bounds = rec.fBounds;
1185 const SkPaint* paint = rec.fPaint;
1186 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1187
reed@google.comb93ba452014-03-10 19:47:58 +00001188#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001189 saveLayerFlags &= ~kDontClipToLayer_PrivateSaveLayerFlag;
reed@google.comb93ba452014-03-10 19:47:58 +00001190#endif
1191
reed8c30a812016-04-20 16:36:51 -07001192 SkLazyPaint lazyP;
1193 SkImageFilter* imageFilter = paint ? paint->getImageFilter() : NULL;
1194 SkMatrix stashedMatrix = fMCRec->fMatrix;
reed8c30a812016-04-20 16:36:51 -07001195 SkMatrix remainder;
1196 SkSize scale;
1197 /*
1198 * ImageFilters (so far) do not correctly handle matrices (CTM) that contain rotation/skew/etc.
1199 * but they do handle scaling. To accommodate this, we do the following:
1200 *
1201 * 1. Stash off the current CTM
1202 * 2. Decompose the CTM into SCALE and REMAINDER
1203 * 3. Wack the CTM to be just SCALE, and wrap the imagefilter with a MatrixImageFilter that
1204 * contains the REMAINDER
1205 * 4. Proceed as usual, allowing the client to draw into the layer (now with a scale-only CTM)
1206 * 5. During restore, we process the MatrixImageFilter, which applies REMAINDER to the output
1207 * of the original imagefilter, and draw that (via drawSprite)
1208 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1209 *
1210 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1211 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1212 */
reed96a04f32016-04-25 09:25:15 -07001213 if (imageFilter && !stashedMatrix.isScaleTranslate() && !imageFilter->canHandleComplexCTM() &&
reed8c30a812016-04-20 16:36:51 -07001214 stashedMatrix.decomposeScale(&scale, &remainder))
1215 {
1216 // We will restore the matrix (which we are overwriting here) in restore via fStashedMatrix
1217 this->internalSetMatrix(SkMatrix::MakeScale(scale.width(), scale.height()));
1218 SkPaint* p = lazyP.set(*paint);
1219 p->setImageFilter(SkImageFilter::MakeMatrixFilter(remainder,
1220 SkFilterQuality::kLow_SkFilterQuality,
1221 sk_ref_sp(imageFilter)));
1222 imageFilter = p->getImageFilter();
1223 paint = p;
1224 }
reed8c30a812016-04-20 16:36:51 -07001225
junov@chromium.orga907ac32012-02-24 21:54:07 +00001226 // do this before we create the layer. We don't call the public save() since
1227 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001228 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001229
1230 fDeviceCMDirty = true;
1231
1232 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001233 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
reed2ff1fce2014-12-11 07:07:37 -08001234 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001235 }
1236
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001237 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1238 // the clipRectBounds() call above?
1239 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001240 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001241 }
1242
reed4960eee2015-12-18 07:09:18 -08001243 bool isOpaque = SkToBool(saveLayerFlags & kIsOpaque_SaveLayerFlag);
reed8dc0ccb2015-03-20 06:32:52 -07001244 SkPixelGeometry geo = fProps.pixelGeometry();
1245 if (paint) {
reed76033be2015-03-14 10:54:31 -07001246 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001247 if (paint->getImageFilter() || paint->getColorFilter()) {
reed76033be2015-03-14 10:54:31 -07001248 isOpaque = false;
reed8dc0ccb2015-03-20 06:32:52 -07001249 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001250 }
1251 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001252
robertphillips5139e502016-07-19 05:10:40 -07001253 SkBaseDevice* priorDevice = this->getTopDevice();
reeda2217ef2016-07-20 06:04:34 -07001254 if (nullptr == priorDevice) {
reedb2db8982014-11-13 12:41:02 -08001255 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001256 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001257 }
reedb2db8982014-11-13 12:41:02 -08001258
robertphillips5139e502016-07-19 05:10:40 -07001259 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), isOpaque,
reed129ed1c2016-02-22 06:42:31 -08001260 paint);
1261
robertphillips5139e502016-07-19 05:10:40 -07001262 SkAutoTUnref<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001263 {
reed70ee31b2015-12-10 13:44:45 -08001264 const bool preserveLCDText = kOpaque_SkAlphaType == info.alphaType() ||
reed4960eee2015-12-18 07:09:18 -08001265 (saveLayerFlags & kPreserveLCDText_SaveLayerFlag);
reeddaa57bf2015-05-15 10:39:17 -07001266 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
reed70ee31b2015-12-10 13:44:45 -08001267 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
reedcd4051e2016-07-15 09:41:26 -07001268 preserveLCDText);
robertphillips5139e502016-07-19 05:10:40 -07001269 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1270 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001271 SkErrorInternals::SetError(kInternalError_SkError,
1272 "Unable to create device for layer.");
1273 return;
reed61f501f2015-04-29 08:34:00 -07001274 }
bungeman@google.come25c6842011-08-17 14:53:54 +00001275 }
robertphillips5139e502016-07-19 05:10:40 -07001276 newDevice->setOrigin(ir.fLeft, ir.fTop);
robertphillips7354a4b2015-12-16 05:08:27 -08001277
robertphillips5139e502016-07-19 05:10:40 -07001278 DeviceCM* layer = new DeviceCM(newDevice, paint, this, fConservativeRasterClip, stashedMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001279
1280 layer->fNext = fMCRec->fTopLayer;
1281 fMCRec->fLayer = layer;
1282 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001283
1284 if (rec.fBackdrop) {
1285 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice,
1286 fMCRec->fMatrix, this->getClipStack());
1287 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001288}
1289
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001290int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001291 if (0xFF == alpha) {
1292 return this->saveLayer(bounds, nullptr);
1293 } else {
1294 SkPaint tmpPaint;
1295 tmpPaint.setAlpha(alpha);
1296 return this->saveLayer(bounds, &tmpPaint);
1297 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001298}
1299
reed@android.com8a1c16f2008-12-17 15:59:43 +00001300void SkCanvas::internalRestore() {
1301 SkASSERT(fMCStack.count() != 0);
1302
1303 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001304 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001305
reed687fa1c2015-04-07 08:00:56 -07001306 fClipStack->restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001307
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001308 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001309 DeviceCM* layer = fMCRec->fLayer; // may be null
1310 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001311 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001312
1313 // now do the normal restore()
1314 fMCRec->~MCRec(); // balanced in save()
1315 fMCStack.pop_back();
1316 fMCRec = (MCRec*)fMCStack.back();
1317
1318 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1319 since if we're being recorded, we don't want to record this (the
1320 recorder will have already recorded the restore).
1321 */
bsalomon49f085d2014-09-05 13:34:00 -07001322 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001323 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001324 const SkIPoint& origin = layer->fDevice->getOrigin();
reed7503d602016-07-15 14:23:29 -07001325 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(), layer->fPaint);
reed8c30a812016-04-20 16:36:51 -07001326 // restore what we smashed in internalSaveLayer
1327 fMCRec->fMatrix = layer->fStashedMatrix;
reed@google.com8926b162012-03-23 15:36:36 +00001328 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001329 fDeviceCMDirty = true;
halcanary385fe4d2015-08-26 13:07:48 -07001330 delete layer;
reedb679ca82015-04-07 04:40:48 -07001331 } else {
1332 // we're at the root
reeda499f902015-05-01 09:34:31 -07001333 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001334 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001335 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001336 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001337 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001338}
1339
reede8f30622016-03-23 18:59:25 -07001340sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001341 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001342 props = &fProps;
1343 }
1344 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001345}
1346
reede8f30622016-03-23 18:59:25 -07001347sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001348 SkBaseDevice* dev = this->getDevice();
reede8f30622016-03-23 18:59:25 -07001349 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001350}
1351
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001352SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001353 return this->onImageInfo();
1354}
1355
1356SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001357 SkBaseDevice* dev = this->getDevice();
1358 if (dev) {
1359 return dev->imageInfo();
1360 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001361 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001362 }
1363}
1364
brianosman898235c2016-04-06 07:38:23 -07001365bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001366 return this->onGetProps(props);
1367}
1368
1369bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001370 SkBaseDevice* dev = this->getDevice();
1371 if (dev) {
1372 if (props) {
1373 *props = fProps;
1374 }
1375 return true;
1376 } else {
1377 return false;
1378 }
1379}
1380
reed6ceeebd2016-03-09 14:26:26 -08001381#ifdef SK_SUPPORT_LEGACY_PEEKPIXELS_PARMS
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001382const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
reed884e97c2015-05-26 11:31:54 -07001383 SkPixmap pmap;
reed6ceeebd2016-03-09 14:26:26 -08001384 if (this->peekPixels(&pmap)) {
1385 if (info) {
1386 *info = pmap.info();
1387 }
1388 if (rowBytes) {
1389 *rowBytes = pmap.rowBytes();
1390 }
1391 return pmap.addr();
reed884e97c2015-05-26 11:31:54 -07001392 }
reed6ceeebd2016-03-09 14:26:26 -08001393 return nullptr;
1394}
1395#endif
1396
1397bool SkCanvas::peekPixels(SkPixmap* pmap) {
1398 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001399}
1400
reed884e97c2015-05-26 11:31:54 -07001401bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001402 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001403 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001404}
1405
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001406void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001407 SkPixmap pmap;
1408 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001409 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001410 }
1411 if (info) {
1412 *info = pmap.info();
1413 }
1414 if (rowBytes) {
1415 *rowBytes = pmap.rowBytes();
1416 }
1417 if (origin) {
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001418 *origin = this->getTopDevice(false)->getOrigin();
1419 }
reed884e97c2015-05-26 11:31:54 -07001420 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001421}
1422
reed884e97c2015-05-26 11:31:54 -07001423bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001424 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001425 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001426}
1427
reed@android.com8a1c16f2008-12-17 15:59:43 +00001428/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001429
reed7503d602016-07-15 14:23:29 -07001430void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001431 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001432 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001433 paint = &tmp;
1434 }
reed@google.com4b226022011-01-11 18:32:13 +00001435
reed@google.com8926b162012-03-23 15:36:36 +00001436 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reeda2217ef2016-07-20 06:04:34 -07001437
reed@android.com8a1c16f2008-12-17 15:59:43 +00001438 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001439 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001440 paint = &looper.paint();
1441 SkImageFilter* filter = paint->getImageFilter();
1442 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
senorblancof35566e2016-04-18 10:32:02 -07001443 if (filter) {
reeda2217ef2016-07-20 06:04:34 -07001444 dstDev->drawSpecial(iter, srcDev->snapSpecial().get(), pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001445 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001446 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001447 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001448 }
reeda2217ef2016-07-20 06:04:34 -07001449
reed@google.com4e2b3d32011-04-07 14:18:59 +00001450 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001451}
1452
reed32704672015-12-16 08:27:10 -08001453/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001454
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001455void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001456 SkMatrix m;
1457 m.setTranslate(dx, dy);
1458 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001459}
1460
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001461void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001462 SkMatrix m;
1463 m.setScale(sx, sy);
1464 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001465}
1466
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001467void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001468 SkMatrix m;
1469 m.setRotate(degrees);
1470 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001471}
1472
bungeman7438bfc2016-07-12 15:01:19 -07001473void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1474 SkMatrix m;
1475 m.setRotate(degrees, px, py);
1476 this->concat(m);
1477}
1478
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001479void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001480 SkMatrix m;
1481 m.setSkew(sx, sy);
1482 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001483}
1484
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001485void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001486 if (matrix.isIdentity()) {
1487 return;
1488 }
1489
reed2ff1fce2014-12-11 07:07:37 -08001490 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001491 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001492 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001493 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001494
1495 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001496}
1497
reed8c30a812016-04-20 16:36:51 -07001498void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001499 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001500 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001501 fMCRec->fMatrix = matrix;
reed8c30a812016-04-20 16:36:51 -07001502}
1503
1504void SkCanvas::setMatrix(const SkMatrix& matrix) {
1505 this->checkForDeferredSave();
1506 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001507 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001508}
1509
reed@android.com8a1c16f2008-12-17 15:59:43 +00001510void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001511 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001512}
1513
vjiaoblack95302da2016-07-21 10:25:54 -07001514#ifdef SK_EXPERIMENTAL_SHADOWING
vjiaoblacke5de1302016-07-13 14:05:28 -07001515void SkCanvas::translateZ(SkScalar z) {
1516 this->checkForDeferredSave();
1517 this->fMCRec->fCurDrawDepth += z;
1518 this->didTranslateZ(z);
1519}
1520
1521SkScalar SkCanvas::getZ() const {
1522 return this->fMCRec->fCurDrawDepth;
1523}
1524
vjiaoblack95302da2016-07-21 10:25:54 -07001525void SkCanvas::setLights(sk_sp<SkLights> lights) {
1526 this->fLights = lights;
1527}
1528
1529sk_sp<SkLights> SkCanvas::getLights() const {
1530 return this->fLights;
1531}
1532#endif
1533
reed@android.com8a1c16f2008-12-17 15:59:43 +00001534//////////////////////////////////////////////////////////////////////////////
1535
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001536void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed2d1afab2016-06-29 14:33:11 -07001537 if (!fAllowSoftClip) {
1538 doAA = false;
1539 }
1540
1541#ifdef SK_SUPPORT_PRECHECK_CLIPRECT
1542 // Check if we can quick-accept the clip call (and do nothing)
1543 //
reed74467162016-06-30 08:15:35 -07001544 if (SkRegion::kIntersect_Op == op && !doAA && fMCRec->fMatrix.isScaleTranslate()) {
reed6092b6e2016-07-10 11:45:34 -07001545 SkRect devR;
1546 fMCRec->fMatrix.mapRectScaleTranslate(&devR, rect);
reed2d1afab2016-06-29 14:33:11 -07001547 // NOTE: this check is CTM specific, since we might round differently with a different
1548 // CTM. Thus this is only 100% reliable if there is not global CTM scale to be
1549 // applied later (i.e. if this is going into a picture).
1550 if (devR.round().contains(fMCRec->fRasterClip.getBounds())) {
1551#if 0
1552 SkDebugf("ignored clipRect [%g %g %g %g]\n",
1553 rect.left(), rect.top(), rect.right(), rect.bottom());
1554#endif
1555 return;
1556 }
1557 }
1558#endif
1559
reed2ff1fce2014-12-11 07:07:37 -08001560 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001561 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1562 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001563}
1564
1565void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001566#ifdef SK_ENABLE_CLIP_QUICKREJECT
1567 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001568 if (fMCRec->fRasterClip.isEmpty()) {
reed2d1afab2016-06-29 14:33:11 -07001569 return;
reed@google.comda17f752012-08-16 18:27:05 +00001570 }
1571
reed@google.com3b3e8952012-08-16 20:53:31 +00001572 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001573 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001574 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001575
reed687fa1c2015-04-07 08:00:56 -07001576 fClipStack->clipEmpty();
reed2d1afab2016-06-29 14:33:11 -07001577 (void)fMCRec->fRasterClip.setEmpty();
1578 return;
reed@google.comda17f752012-08-16 18:27:05 +00001579 }
1580 }
1581#endif
1582
reed74467162016-06-30 08:15:35 -07001583 const bool isScaleTrans = fMCRec->fMatrix.isScaleTranslate();
reedc64eff52015-11-21 12:39:45 -08001584 SkRect devR;
reed74467162016-06-30 08:15:35 -07001585 if (isScaleTrans) {
reed6092b6e2016-07-10 11:45:34 -07001586 fMCRec->fMatrix.mapRectScaleTranslate(&devR, rect);
reedc64eff52015-11-21 12:39:45 -08001587 }
bsalomonac8cabd2015-11-20 18:53:07 -08001588
reed2d1afab2016-06-29 14:33:11 -07001589#ifndef SK_SUPPORT_PRECHECK_CLIPRECT
reedc64eff52015-11-21 12:39:45 -08001590 if (SkRegion::kIntersect_Op == op &&
1591 kHard_ClipEdgeStyle == edgeStyle
reed74467162016-06-30 08:15:35 -07001592 && isScaleTrans)
reedc64eff52015-11-21 12:39:45 -08001593 {
1594 if (devR.round().contains(fMCRec->fRasterClip.getBounds())) {
1595#if 0
1596 SkDebugf("------- ignored clipRect [%g %g %g %g]\n",
1597 rect.left(), rect.top(), rect.right(), rect.bottom());
1598#endif
1599 return;
1600 }
1601 }
reed2d1afab2016-06-29 14:33:11 -07001602#endif
reedc64eff52015-11-21 12:39:45 -08001603
1604 AutoValidateClip avc(this);
1605
1606 fDeviceCMDirty = true;
1607 fCachedLocalClipBoundsDirty = true;
1608
reed74467162016-06-30 08:15:35 -07001609 if (isScaleTrans) {
reedc64eff52015-11-21 12:39:45 -08001610 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1611 fClipStack->clipDevRect(devR, op, isAA);
senorblancoafc7cce2016-02-02 18:44:15 -08001612 fMCRec->fRasterClip.op(devR, this->getTopLayerBounds(), op, isAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001613 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001614 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001615 // and clip against that, since it can handle any matrix. However, to
1616 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1617 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001618 SkPath path;
1619
1620 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001621 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001622 }
1623}
1624
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001625void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001626 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001627 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001628 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001629 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1630 } else {
1631 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001632 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001633}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001634
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001635void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001636 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001637 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001638 AutoValidateClip avc(this);
1639
1640 fDeviceCMDirty = true;
1641 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001642 if (!fAllowSoftClip) {
1643 edgeStyle = kHard_ClipEdgeStyle;
1644 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001645
reed687fa1c2015-04-07 08:00:56 -07001646 fClipStack->clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001647
senorblancoafc7cce2016-02-02 18:44:15 -08001648 fMCRec->fRasterClip.op(transformedRRect, this->getTopLayerBounds(), op,
robertphillips125f19a2015-11-23 09:00:05 -08001649 kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001650 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001651 }
1652
1653 SkPath path;
1654 path.addRRect(rrect);
1655 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001656 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001657}
1658
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001659void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001660 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001661 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001662
1663 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1664 SkRect r;
1665 if (path.isRect(&r)) {
1666 this->onClipRect(r, op, edgeStyle);
1667 return;
1668 }
1669 SkRRect rrect;
1670 if (path.isOval(&r)) {
1671 rrect.setOval(r);
1672 this->onClipRRect(rrect, op, edgeStyle);
1673 return;
1674 }
1675 if (path.isRRect(&rrect)) {
1676 this->onClipRRect(rrect, op, edgeStyle);
1677 return;
1678 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001679 }
robertphillips39f05382015-11-24 09:30:12 -08001680
1681 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001682}
1683
1684void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001685#ifdef SK_ENABLE_CLIP_QUICKREJECT
1686 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001687 if (fMCRec->fRasterClip.isEmpty()) {
reed2d1afab2016-06-29 14:33:11 -07001688 return;
reed@google.comda17f752012-08-16 18:27:05 +00001689 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001690
reed@google.com3b3e8952012-08-16 20:53:31 +00001691 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001692 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001693 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001694
reed687fa1c2015-04-07 08:00:56 -07001695 fClipStack->clipEmpty();
reed2d1afab2016-06-29 14:33:11 -07001696 (void)fMCRec->fRasterClip.setEmpty();
1697 return;
reed@google.comda17f752012-08-16 18:27:05 +00001698 }
1699 }
1700#endif
1701
reed@google.com5c3d1472011-02-22 19:12:23 +00001702 AutoValidateClip avc(this);
1703
reed@android.com8a1c16f2008-12-17 15:59:43 +00001704 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001705 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001706 if (!fAllowSoftClip) {
1707 edgeStyle = kHard_ClipEdgeStyle;
1708 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001709
1710 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001711 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001712
reed@google.comfe701122011-11-08 19:41:23 +00001713 // Check if the transfomation, or the original path itself
1714 // made us empty. Note this can also happen if we contained NaN
1715 // values. computing the bounds detects this, and will set our
1716 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1717 if (devPath.getBounds().isEmpty()) {
1718 // resetting the path will remove any NaN or other wanky values
1719 // that might upset our scan converter.
1720 devPath.reset();
1721 }
1722
reed@google.com5c3d1472011-02-22 19:12:23 +00001723 // if we called path.swap() we could avoid a deep copy of this path
reed687fa1c2015-04-07 08:00:56 -07001724 fClipStack->clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001725
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001726 if (fAllowSimplifyClip) {
fmalita1a481fe2015-02-04 07:39:34 -08001727 bool clipIsAA = getClipStack()->asPath(&devPath);
1728 if (clipIsAA) {
1729 edgeStyle = kSoft_ClipEdgeStyle;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001730 }
fmalita1a481fe2015-02-04 07:39:34 -08001731
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001732 op = SkRegion::kReplace_Op;
1733 }
1734
senorblancoafc7cce2016-02-02 18:44:15 -08001735 fMCRec->fRasterClip.op(devPath, this->getTopLayerBounds(), op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001736}
1737
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001738void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed2ff1fce2014-12-11 07:07:37 -08001739 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001740 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001741}
1742
1743void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001744 AutoValidateClip avc(this);
1745
reed@android.com8a1c16f2008-12-17 15:59:43 +00001746 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001747 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001748
reed@google.com5c3d1472011-02-22 19:12:23 +00001749 // todo: signal fClipStack that we have a region, and therefore (I guess)
1750 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001751 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001752
reed1f836ee2014-07-07 07:49:34 -07001753 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001754}
1755
reed@google.com819c9212011-02-23 18:56:55 +00001756#ifdef SK_DEBUG
1757void SkCanvas::validateClip() const {
1758 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001759 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001760 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001761 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001762 return;
1763 }
1764
reed@google.com819c9212011-02-23 18:56:55 +00001765 SkIRect ir;
1766 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001767 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001768
reed687fa1c2015-04-07 08:00:56 -07001769 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001770 const SkClipStack::Element* element;
halcanary96fcdcc2015-08-27 07:41:13 -07001771 while ((element = iter.next()) != nullptr) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001772 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001773 case SkClipStack::Element::kRect_Type:
1774 element->getRect().round(&ir);
1775 tmpClip.op(ir, element->getOp());
1776 break;
1777 case SkClipStack::Element::kEmpty_Type:
1778 tmpClip.setEmpty();
1779 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001780 default: {
1781 SkPath path;
1782 element->asPath(&path);
senorblancoafc7cce2016-02-02 18:44:15 -08001783 tmpClip.op(path, this->getTopLayerBounds(), element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001784 break;
1785 }
reed@google.com819c9212011-02-23 18:56:55 +00001786 }
1787 }
reed@google.com819c9212011-02-23 18:56:55 +00001788}
1789#endif
1790
reed@google.com90c07ea2012-04-13 13:50:27 +00001791void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001792 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001793 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001794
halcanary96fcdcc2015-08-27 07:41:13 -07001795 while ((element = iter.next()) != nullptr) {
fmalitac3b589a2014-06-05 12:40:07 -07001796 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001797 }
1798}
1799
reed@google.com5c3d1472011-02-22 19:12:23 +00001800///////////////////////////////////////////////////////////////////////////////
1801
reed@google.com754de5f2014-02-24 19:38:20 +00001802bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001803 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001804}
1805
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001806bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001807 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001808}
1809
reed@google.com3b3e8952012-08-16 20:53:31 +00001810bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001811 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001812 return true;
1813
reed1f836ee2014-07-07 07:49:34 -07001814 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001815 return true;
1816 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001817
reed1f836ee2014-07-07 07:49:34 -07001818 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001819 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001820 fMCRec->fMatrix.mapRect(&dst, rect);
reedb07a94f2014-11-19 05:03:18 -08001821 return !SkIRect::Intersects(dst.roundOut(), fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001822 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001823 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001824
reed@android.coma380ae42009-07-21 01:17:02 +00001825 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001826 // TODO: should we use | instead, or compare all 4 at once?
1827 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001828 return true;
1829 }
reed@google.comc0784db2013-12-13 21:16:12 +00001830 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001831 return true;
1832 }
1833 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001834 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001835}
1836
reed@google.com3b3e8952012-08-16 20:53:31 +00001837bool SkCanvas::quickReject(const SkPath& path) const {
1838 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001839}
1840
reed@google.com3b3e8952012-08-16 20:53:31 +00001841bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001842 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001843 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001844 return false;
1845 }
1846
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001847 SkMatrix inverse;
1848 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001849 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001850 if (bounds) {
1851 bounds->setEmpty();
1852 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001853 return false;
1854 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001855
bsalomon49f085d2014-09-05 13:34:00 -07001856 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001857 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001858 // adjust it outwards in case we are antialiasing
1859 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001860
reed@google.com8f4d2302013-12-17 16:44:46 +00001861 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1862 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001863 inverse.mapRect(bounds, r);
1864 }
1865 return true;
1866}
1867
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001868bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001869 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001870 if (clip.isEmpty()) {
1871 if (bounds) {
1872 bounds->setEmpty();
1873 }
1874 return false;
1875 }
1876
bsalomon49f085d2014-09-05 13:34:00 -07001877 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001878 *bounds = clip.getBounds();
1879 }
1880 return true;
1881}
1882
reed@android.com8a1c16f2008-12-17 15:59:43 +00001883const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001884 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001885}
1886
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001887const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001888 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001889}
1890
robertphillips175dd9b2016-04-28 14:32:04 -07001891GrDrawContext* SkCanvas::internal_private_accessTopLayerDrawContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001892 SkBaseDevice* dev = this->getTopDevice();
robertphillips175dd9b2016-04-28 14:32:04 -07001893 return dev ? dev->accessDrawContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001894}
1895
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001896GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001897 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001898 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001899}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001900
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001901void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1902 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001903 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001904 if (outer.isEmpty()) {
1905 return;
1906 }
1907 if (inner.isEmpty()) {
1908 this->drawRRect(outer, paint);
1909 return;
1910 }
1911
1912 // We don't have this method (yet), but technically this is what we should
1913 // be able to assert...
1914 // SkASSERT(outer.contains(inner));
1915 //
1916 // For now at least check for containment of bounds
1917 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1918
1919 this->onDrawDRRect(outer, inner, paint);
1920}
1921
reed41af9662015-01-05 07:49:08 -08001922// These need to stop being virtual -- clients need to override the onDraw... versions
1923
1924void SkCanvas::drawPaint(const SkPaint& paint) {
1925 this->onDrawPaint(paint);
1926}
1927
1928void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1929 this->onDrawRect(r, paint);
1930}
1931
1932void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1933 this->onDrawOval(r, paint);
1934}
1935
1936void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1937 this->onDrawRRect(rrect, paint);
1938}
1939
1940void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1941 this->onDrawPoints(mode, count, pts, paint);
1942}
1943
1944void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
1945 const SkPoint texs[], const SkColor colors[], SkXfermode* xmode,
1946 const uint16_t indices[], int indexCount, const SkPaint& paint) {
1947 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
1948 indices, indexCount, paint);
1949}
1950
1951void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1952 this->onDrawPath(path, paint);
1953}
1954
reeda85d4d02015-05-06 12:56:48 -07001955void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001956 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001957 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001958}
1959
reede47829b2015-08-06 10:02:53 -07001960void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1961 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001962 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001963 if (dst.isEmpty() || src.isEmpty()) {
1964 return;
1965 }
1966 this->onDrawImageRect(image, &src, dst, paint, constraint);
1967}
reed41af9662015-01-05 07:49:08 -08001968
reed84984ef2015-07-17 07:09:43 -07001969void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1970 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001971 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001972 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001973}
1974
reede47829b2015-08-06 10:02:53 -07001975void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
1976 SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001977 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001978 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
1979 constraint);
1980}
reede47829b2015-08-06 10:02:53 -07001981
reed4c21dc52015-06-25 12:32:03 -07001982void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1983 const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001984 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07001985 if (dst.isEmpty()) {
1986 return;
1987 }
msarett552bca92016-08-03 06:53:26 -07001988 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
1989 this->onDrawImageNine(image, center, dst, paint);
1990 } else {
reede47829b2015-08-06 10:02:53 -07001991 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001992 }
reed4c21dc52015-06-25 12:32:03 -07001993}
1994
reed41af9662015-01-05 07:49:08 -08001995void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001996 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001997 return;
1998 }
reed41af9662015-01-05 07:49:08 -08001999 this->onDrawBitmap(bitmap, dx, dy, paint);
2000}
2001
reede47829b2015-08-06 10:02:53 -07002002void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07002003 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002004 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07002005 return;
2006 }
reede47829b2015-08-06 10:02:53 -07002007 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08002008}
2009
reed84984ef2015-07-17 07:09:43 -07002010void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2011 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002012 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002013}
2014
reede47829b2015-08-06 10:02:53 -07002015void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2016 SrcRectConstraint constraint) {
2017 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2018 constraint);
2019}
reede47829b2015-08-06 10:02:53 -07002020
reed41af9662015-01-05 07:49:08 -08002021void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2022 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07002023 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002024 return;
2025 }
msarett552bca92016-08-03 06:53:26 -07002026 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), center)) {
2027 this->onDrawBitmapNine(bitmap, center, dst, paint);
2028 } else {
reeda5517e22015-07-14 10:54:12 -07002029 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002030 }
msarett552bca92016-08-03 06:53:26 -07002031
reed41af9662015-01-05 07:49:08 -08002032}
2033
msarettc573a402016-08-02 08:05:56 -07002034void SkCanvas::drawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice, const SkRect& dst,
2035 const SkPaint* paint) {
2036 sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap);
2037 this->drawImageLattice(image.get(), lattice, dst, paint);
2038}
2039
2040void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2041 const SkPaint* paint) {
2042 RETURN_ON_NULL(image);
2043 if (dst.isEmpty()) {
2044 return;
2045 }
msarett552bca92016-08-03 06:53:26 -07002046 if (SkLatticeIter::Valid(image->width(), image->height(), lattice)) {
2047 this->onDrawImageLattice(image, lattice, dst, paint);
2048 } else {
msarettc573a402016-08-02 08:05:56 -07002049 this->drawImageRect(image, dst, paint);
2050 }
msarettc573a402016-08-02 08:05:56 -07002051}
2052
reed71c3c762015-06-24 10:29:17 -07002053void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2054 const SkColor colors[], int count, SkXfermode::Mode mode,
2055 const SkRect* cull, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002056 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002057 if (count <= 0) {
2058 return;
2059 }
2060 SkASSERT(atlas);
2061 SkASSERT(xform);
2062 SkASSERT(tex);
2063 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
2064}
2065
reedf70b5312016-03-04 16:36:20 -08002066void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2067 if (key) {
2068 this->onDrawAnnotation(rect, key, value);
2069 }
2070}
2071
reede47829b2015-08-06 10:02:53 -07002072void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2073 const SkPaint* paint, SrcRectConstraint constraint) {
2074 if (src) {
2075 this->drawImageRect(image, *src, dst, paint, constraint);
2076 } else {
2077 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2078 dst, paint, constraint);
2079 }
2080}
2081void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2082 const SkPaint* paint, SrcRectConstraint constraint) {
2083 if (src) {
2084 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2085 } else {
2086 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2087 dst, paint, constraint);
2088 }
2089}
2090
tomhudsoncb3bd182016-05-18 07:24:16 -07002091void SkCanvas::temporary_internal_describeTopLayer(SkMatrix* matrix, SkIRect* clip_bounds) {
2092 SkIRect layer_bounds = this->getTopLayerBounds();
2093 if (matrix) {
2094 *matrix = this->getTotalMatrix();
2095 matrix->preTranslate(-layer_bounds.left(), -layer_bounds.top());
2096 }
2097 if (clip_bounds) {
2098 this->getClipDeviceBounds(clip_bounds);
2099 clip_bounds->offset(-layer_bounds.left(), -layer_bounds.top());
2100 }
2101}
2102
reed@android.com8a1c16f2008-12-17 15:59:43 +00002103//////////////////////////////////////////////////////////////////////////////
2104// These are the virtual drawing methods
2105//////////////////////////////////////////////////////////////////////////////
2106
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002107void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002108 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002109 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2110 }
2111}
2112
reed41af9662015-01-05 07:49:08 -08002113void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002114 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002115 this->internalDrawPaint(paint);
2116}
2117
2118void SkCanvas::internalDrawPaint(const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002119 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002120
2121 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002122 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002123 }
2124
reed@google.com4e2b3d32011-04-07 14:18:59 +00002125 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002126}
2127
reed41af9662015-01-05 07:49:08 -08002128void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2129 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002130 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002131 if ((long)count <= 0) {
2132 return;
2133 }
2134
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002135 SkRect r, storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002136 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002137 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002138 // special-case 2 points (common for drawing a single line)
2139 if (2 == count) {
2140 r.set(pts[0], pts[1]);
2141 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00002142 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002143 }
senorblanco87e066e2015-10-28 11:23:36 -07002144 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2145 return;
2146 }
2147 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002148 }
reed@google.coma584aed2012-05-16 14:06:02 +00002149
halcanary96fcdcc2015-08-27 07:41:13 -07002150 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002151
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002152 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002153
reed@android.com8a1c16f2008-12-17 15:59:43 +00002154 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002155 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002156 }
reed@google.com4b226022011-01-11 18:32:13 +00002157
reed@google.com4e2b3d32011-04-07 14:18:59 +00002158 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002159}
2160
reed41af9662015-01-05 07:49:08 -08002161void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002162 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002163 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002164 const SkRect* bounds = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002165 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08002166 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
2167 // To prevent accidental rejecting at this stage, we have to sort it before we check.
2168 SkRect tmp(r);
2169 tmp.sort();
2170
senorblanco87e066e2015-10-28 11:23:36 -07002171 if (this->quickReject(paint.computeFastBounds(tmp, &storage))) {
2172 return;
2173 }
2174 bounds = &r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002175 }
reed@google.com4b226022011-01-11 18:32:13 +00002176
reedc83a2972015-07-16 07:40:45 -07002177 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002178
2179 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002180 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002181 }
2182
reed@google.com4e2b3d32011-04-07 14:18:59 +00002183 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002184}
2185
reed41af9662015-01-05 07:49:08 -08002186void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002187 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002188 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002189 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002190 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002191 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2192 return;
2193 }
2194 bounds = &oval;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002195 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002196
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002197 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002198
2199 while (iter.next()) {
2200 iter.fDevice->drawOval(iter, oval, looper.paint());
2201 }
2202
2203 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002204}
2205
reed41af9662015-01-05 07:49:08 -08002206void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002207 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002208 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002209 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002210 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002211 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2212 return;
2213 }
2214 bounds = &rrect.getBounds();
reed@google.com4ed0fb72012-12-12 20:48:18 +00002215 }
2216
2217 if (rrect.isRect()) {
2218 // call the non-virtual version
2219 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002220 return;
2221 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002222 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002223 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2224 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002225 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002226
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002227 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002228
2229 while (iter.next()) {
2230 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2231 }
2232
2233 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002234}
2235
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002236void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2237 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002238 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002239 const SkRect* bounds = nullptr;
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002240 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002241 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2242 return;
2243 }
2244 bounds = &outer.getBounds();
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002245 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002246
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002247 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002248
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002249 while (iter.next()) {
2250 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2251 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002252
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002253 LOOPER_END
2254}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002255
reed41af9662015-01-05 07:49:08 -08002256void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002257 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002258 if (!path.isFinite()) {
2259 return;
2260 }
2261
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002262 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002263 const SkRect* bounds = nullptr;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002264 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002265 const SkRect& pathBounds = path.getBounds();
senorblanco87e066e2015-10-28 11:23:36 -07002266 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2267 return;
2268 }
2269 bounds = &pathBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002270 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002271
2272 const SkRect& r = path.getBounds();
2273 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002274 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002275 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002276 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002277 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002278 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002279
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002280 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002281
2282 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002283 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002284 }
2285
reed@google.com4e2b3d32011-04-07 14:18:59 +00002286 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002287}
2288
reed262a71b2015-12-05 13:07:27 -08002289bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002290 if (!paint.getImageFilter()) {
2291 return false;
2292 }
2293
2294 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002295 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002296 return false;
2297 }
2298
2299 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2300 // Once we can filter and the filter will return a result larger than itself, we should be
2301 // able to remove this constraint.
2302 // skbug.com/4526
2303 //
2304 SkPoint pt;
2305 ctm.mapXY(x, y, &pt);
2306 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2307 return ir.contains(fMCRec->fRasterClip.getBounds());
2308}
2309
reeda85d4d02015-05-06 12:56:48 -07002310void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002311 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002312 SkRect bounds = SkRect::MakeXYWH(x, y,
2313 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002314 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002315 SkRect tmp = bounds;
2316 if (paint) {
2317 paint->computeFastBounds(tmp, &tmp);
2318 }
2319 if (this->quickReject(tmp)) {
2320 return;
2321 }
reeda85d4d02015-05-06 12:56:48 -07002322 }
halcanary9d524f22016-03-29 09:03:52 -07002323
reeda85d4d02015-05-06 12:56:48 -07002324 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002325 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002326 paint = lazy.init();
2327 }
reed262a71b2015-12-05 13:07:27 -08002328
reeda2217ef2016-07-20 06:04:34 -07002329 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002330 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2331 *paint);
2332 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002333 special = this->getDevice()->makeSpecial(image);
2334 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002335 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002336 }
2337 }
2338
reed262a71b2015-12-05 13:07:27 -08002339 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
2340
reeda85d4d02015-05-06 12:56:48 -07002341 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002342 const SkPaint& pnt = looper.paint();
reeda2217ef2016-07-20 06:04:34 -07002343 if (special) {
2344 SkPoint pt;
2345 iter.fMatrix->mapXY(x, y, &pt);
2346 iter.fDevice->drawSpecial(iter, special.get(),
2347 SkScalarRoundToInt(pt.fX),
2348 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002349 } else {
2350 iter.fDevice->drawImage(iter, image, x, y, pnt);
2351 }
reeda85d4d02015-05-06 12:56:48 -07002352 }
halcanary9d524f22016-03-29 09:03:52 -07002353
reeda85d4d02015-05-06 12:56:48 -07002354 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002355}
2356
msarettc573a402016-08-02 08:05:56 -07002357void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2358 const SkPaint* paint) {
2359 if (nullptr == paint || paint->canComputeFastBounds()) {
2360 SkRect storage;
2361 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2362 return;
2363 }
2364 }
2365
2366 SkLazyPaint lazy;
2367 if (nullptr == paint) {
2368 paint = lazy.init();
2369 }
2370
2371 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
2372
2373 while (iter.next()) {
2374 iter.fDevice->drawImageLattice(iter, image, lattice, dst, looper.paint());
2375 }
2376
2377 LOOPER_END
2378}
2379
reed41af9662015-01-05 07:49:08 -08002380void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002381 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002382 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
halcanary96fcdcc2015-08-27 07:41:13 -07002383 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002384 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002385 if (paint) {
2386 paint->computeFastBounds(dst, &storage);
2387 }
2388 if (this->quickReject(storage)) {
2389 return;
2390 }
reeda85d4d02015-05-06 12:56:48 -07002391 }
2392 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002393 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002394 paint = lazy.init();
2395 }
halcanary9d524f22016-03-29 09:03:52 -07002396
senorblancoc41e7e12015-12-07 12:51:30 -08002397 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002398 image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002399
reeda85d4d02015-05-06 12:56:48 -07002400 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002401 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002402 }
halcanary9d524f22016-03-29 09:03:52 -07002403
reeda85d4d02015-05-06 12:56:48 -07002404 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002405}
2406
reed41af9662015-01-05 07:49:08 -08002407void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002408 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002409 SkDEBUGCODE(bitmap.validate();)
2410
reed33366972015-10-08 09:22:02 -07002411 if (bitmap.drawsNothing()) {
2412 return;
2413 }
2414
2415 SkLazyPaint lazy;
2416 if (nullptr == paint) {
2417 paint = lazy.init();
2418 }
2419
2420 const SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2421
2422 SkRect storage;
2423 const SkRect* bounds = nullptr;
2424 if (paint->canComputeFastBounds()) {
2425 bitmap.getBounds(&storage);
2426 matrix.mapRect(&storage);
senorblanco87e066e2015-10-28 11:23:36 -07002427 SkRect tmp = storage;
2428 if (this->quickReject(paint->computeFastBounds(tmp, &tmp))) {
2429 return;
2430 }
2431 bounds = &storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002432 }
reed@google.com4b226022011-01-11 18:32:13 +00002433
reeda2217ef2016-07-20 06:04:34 -07002434 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002435 bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(), bitmap.height(),
2436 *paint);
2437 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002438 special = this->getDevice()->makeSpecial(bitmap);
2439 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002440 drawAsSprite = false;
2441 }
2442 }
2443
reed262a71b2015-12-05 13:07:27 -08002444 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds)
reed33366972015-10-08 09:22:02 -07002445
2446 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002447 const SkPaint& pnt = looper.paint();
reeda2217ef2016-07-20 06:04:34 -07002448 if (special) {
reed262a71b2015-12-05 13:07:27 -08002449 SkPoint pt;
2450 iter.fMatrix->mapXY(x, y, &pt);
reeda2217ef2016-07-20 06:04:34 -07002451 iter.fDevice->drawSpecial(iter, special.get(),
2452 SkScalarRoundToInt(pt.fX),
2453 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002454 } else {
2455 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
2456 }
reed33366972015-10-08 09:22:02 -07002457 }
reeda2217ef2016-07-20 06:04:34 -07002458
reed33366972015-10-08 09:22:02 -07002459 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002460}
2461
reed@google.com9987ec32011-09-07 11:57:52 +00002462// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002463void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002464 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002465 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002466 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002467 return;
2468 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002469
halcanary96fcdcc2015-08-27 07:41:13 -07002470 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002471 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002472 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2473 return;
2474 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002475 }
reed@google.com3d608122011-11-21 15:16:16 +00002476
reed@google.com33535f32012-09-25 15:37:50 +00002477 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002478 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002479 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002480 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002481
senorblancoc41e7e12015-12-07 12:51:30 -08002482 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002483 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002484
reed@google.com33535f32012-09-25 15:37:50 +00002485 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002486 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002487 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002488
reed@google.com33535f32012-09-25 15:37:50 +00002489 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002490}
2491
reed41af9662015-01-05 07:49:08 -08002492void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002493 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002494 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002495 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002496 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002497}
2498
reed4c21dc52015-06-25 12:32:03 -07002499void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2500 const SkPaint* paint) {
2501 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
halcanary9d524f22016-03-29 09:03:52 -07002502
halcanary96fcdcc2015-08-27 07:41:13 -07002503 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002504 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002505 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2506 return;
2507 }
reed@google.com3d608122011-11-21 15:16:16 +00002508 }
halcanary9d524f22016-03-29 09:03:52 -07002509
reed4c21dc52015-06-25 12:32:03 -07002510 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002511 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002512 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002513 }
halcanary9d524f22016-03-29 09:03:52 -07002514
senorblancoc41e7e12015-12-07 12:51:30 -08002515 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002516
reed4c21dc52015-06-25 12:32:03 -07002517 while (iter.next()) {
2518 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002519 }
halcanary9d524f22016-03-29 09:03:52 -07002520
reed4c21dc52015-06-25 12:32:03 -07002521 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002522}
2523
reed41af9662015-01-05 07:49:08 -08002524void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2525 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002526 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002527 SkDEBUGCODE(bitmap.validate();)
2528
halcanary96fcdcc2015-08-27 07:41:13 -07002529 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002530 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002531 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2532 return;
2533 }
reed4c21dc52015-06-25 12:32:03 -07002534 }
halcanary9d524f22016-03-29 09:03:52 -07002535
reed4c21dc52015-06-25 12:32:03 -07002536 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002537 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002538 paint = lazy.init();
2539 }
halcanary9d524f22016-03-29 09:03:52 -07002540
senorblancoc41e7e12015-12-07 12:51:30 -08002541 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002542
reed4c21dc52015-06-25 12:32:03 -07002543 while (iter.next()) {
2544 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2545 }
halcanary9d524f22016-03-29 09:03:52 -07002546
reed4c21dc52015-06-25 12:32:03 -07002547 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002548}
2549
reed@google.comf67e4cf2011-03-15 20:56:58 +00002550class SkDeviceFilteredPaint {
2551public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002552 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002553 uint32_t filteredFlags = device->filterTextFlags(paint);
2554 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002555 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002556 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002557 fPaint = newPaint;
2558 } else {
2559 fPaint = &paint;
2560 }
2561 }
2562
reed@google.comf67e4cf2011-03-15 20:56:58 +00002563 const SkPaint& paint() const { return *fPaint; }
2564
2565private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002566 const SkPaint* fPaint;
2567 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002568};
2569
bungeman@google.com52c748b2011-08-22 21:30:43 +00002570void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2571 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002572 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002573 draw.fDevice->drawRect(draw, r, paint);
2574 } else {
2575 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002576 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002577 draw.fDevice->drawRect(draw, r, p);
2578 }
2579}
2580
2581void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2582 const char text[], size_t byteLength,
2583 SkScalar x, SkScalar y) {
halcanary96fcdcc2015-08-27 07:41:13 -07002584 SkASSERT(byteLength == 0 || text != nullptr);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002585
2586 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07002587 if (text == nullptr || byteLength == 0 ||
reed1e7f5e72016-04-27 07:49:17 -07002588 draw.fRC->isEmpty() ||
halcanary96fcdcc2015-08-27 07:41:13 -07002589 (paint.getAlpha() == 0 && paint.getXfermode() == nullptr)) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002590 return;
2591 }
2592
2593 SkScalar width = 0;
2594 SkPoint start;
2595
2596 start.set(0, 0); // to avoid warning
2597 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2598 SkPaint::kStrikeThruText_Flag)) {
2599 width = paint.measureText(text, byteLength);
2600
2601 SkScalar offsetX = 0;
2602 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2603 offsetX = SkScalarHalf(width);
2604 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2605 offsetX = width;
2606 }
2607 start.set(x - offsetX, y);
2608 }
2609
2610 if (0 == width) {
2611 return;
2612 }
2613
2614 uint32_t flags = paint.getFlags();
2615
2616 if (flags & (SkPaint::kUnderlineText_Flag |
2617 SkPaint::kStrikeThruText_Flag)) {
2618 SkScalar textSize = paint.getTextSize();
2619 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2620 SkRect r;
2621
2622 r.fLeft = start.fX;
2623 r.fRight = start.fX + width;
2624
2625 if (flags & SkPaint::kUnderlineText_Flag) {
2626 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2627 start.fY);
2628 r.fTop = offset;
2629 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002630 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002631 }
2632 if (flags & SkPaint::kStrikeThruText_Flag) {
2633 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2634 start.fY);
2635 r.fTop = offset;
2636 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002637 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002638 }
2639 }
2640}
2641
reed@google.come0d9ce82014-04-23 04:00:17 +00002642void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2643 const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002644 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002645
2646 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002647 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002648 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002649 DrawTextDecorations(iter, dfp.paint(),
2650 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002651 }
2652
reed@google.com4e2b3d32011-04-07 14:18:59 +00002653 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002654}
2655
reed@google.come0d9ce82014-04-23 04:00:17 +00002656void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2657 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002658 SkPoint textOffset = SkPoint::Make(0, 0);
2659
halcanary96fcdcc2015-08-27 07:41:13 -07002660 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002661
reed@android.com8a1c16f2008-12-17 15:59:43 +00002662 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002663 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002664 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002665 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002666 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002667
reed@google.com4e2b3d32011-04-07 14:18:59 +00002668 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002669}
2670
reed@google.come0d9ce82014-04-23 04:00:17 +00002671void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2672 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002673
2674 SkPoint textOffset = SkPoint::Make(0, constY);
2675
halcanary96fcdcc2015-08-27 07:41:13 -07002676 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002677
reed@android.com8a1c16f2008-12-17 15:59:43 +00002678 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002679 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002680 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002681 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002682 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002683
reed@google.com4e2b3d32011-04-07 14:18:59 +00002684 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002685}
2686
reed@google.come0d9ce82014-04-23 04:00:17 +00002687void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2688 const SkMatrix* matrix, const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002689 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002690
reed@android.com8a1c16f2008-12-17 15:59:43 +00002691 while (iter.next()) {
2692 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002693 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002694 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002695
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002696 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002697}
2698
reed45561a02016-07-07 12:47:17 -07002699void SkCanvas::onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2700 const SkRect* cullRect, const SkPaint& paint) {
2701 if (cullRect && this->quickReject(*cullRect)) {
2702 return;
2703 }
2704
2705 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
2706
2707 while (iter.next()) {
2708 iter.fDevice->drawTextRSXform(iter, text, byteLength, xform, looper.paint());
2709 }
2710
2711 LOOPER_END
2712}
2713
fmalita00d5c2c2014-08-21 08:53:26 -07002714void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2715 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002716
fmalita85d5eb92015-03-04 11:20:12 -08002717 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002718 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002719 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002720 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002721 SkRect tmp;
2722 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2723 return;
2724 }
2725 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002726 }
2727
fmalita024f9962015-03-03 19:08:17 -08002728 // We cannot filter in the looper as we normally do, because the paint is
2729 // incomplete at this point (text-related attributes are embedded within blob run paints).
2730 SkDrawFilter* drawFilter = fMCRec->fFilter;
halcanary96fcdcc2015-08-27 07:41:13 -07002731 fMCRec->fFilter = nullptr;
fmalita024f9962015-03-03 19:08:17 -08002732
fmalita85d5eb92015-03-04 11:20:12 -08002733 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002734
fmalitaaa1b9122014-08-28 14:32:24 -07002735 while (iter.next()) {
2736 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002737 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002738 }
2739
fmalitaaa1b9122014-08-28 14:32:24 -07002740 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002741
2742 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002743}
2744
reed@google.come0d9ce82014-04-23 04:00:17 +00002745// These will become non-virtual, so they always call the (virtual) onDraw... method
2746void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2747 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002748 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002749 this->onDrawText(text, byteLength, x, y, paint);
2750}
2751void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2752 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002753 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002754 this->onDrawPosText(text, byteLength, pos, paint);
2755}
2756void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2757 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002758 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002759 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2760}
2761void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2762 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002763 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002764 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2765}
reed45561a02016-07-07 12:47:17 -07002766void SkCanvas::drawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2767 const SkRect* cullRect, const SkPaint& paint) {
2768 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextRSXform()");
2769 if (byteLength) {
2770 this->onDrawTextRSXform(text, byteLength, xform, cullRect, paint);
2771 }
2772}
fmalita00d5c2c2014-08-21 08:53:26 -07002773void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2774 const SkPaint& paint) {
reede3b38ce2016-01-08 09:18:44 -08002775 RETURN_ON_NULL(blob);
danakj9881d632014-11-26 12:41:06 -08002776 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
reede3b38ce2016-01-08 09:18:44 -08002777 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002778}
reed@google.come0d9ce82014-04-23 04:00:17 +00002779
reed41af9662015-01-05 07:49:08 -08002780void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2781 const SkPoint verts[], const SkPoint texs[],
2782 const SkColor colors[], SkXfermode* xmode,
2783 const uint16_t indices[], int indexCount,
2784 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002785 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
halcanary96fcdcc2015-08-27 07:41:13 -07002786 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
reed@google.com4b226022011-01-11 18:32:13 +00002787
reed@android.com8a1c16f2008-12-17 15:59:43 +00002788 while (iter.next()) {
2789 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002790 colors, xmode, indices, indexCount,
2791 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002792 }
reed@google.com4b226022011-01-11 18:32:13 +00002793
reed@google.com4e2b3d32011-04-07 14:18:59 +00002794 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002795}
2796
dandovb3c9d1c2014-08-12 08:34:29 -07002797void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2798 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002799 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
halcanary96fcdcc2015-08-27 07:41:13 -07002800 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002801 return;
2802 }
mtklein6cfa73a2014-08-13 13:33:49 -07002803
dandovecfff212014-08-04 10:02:00 -07002804 // Since a patch is always within the convex hull of the control points, we discard it when its
2805 // bounding rectangle is completely outside the current clip.
2806 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002807 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002808 if (this->quickReject(bounds)) {
2809 return;
2810 }
mtklein6cfa73a2014-08-13 13:33:49 -07002811
dandovb3c9d1c2014-08-12 08:34:29 -07002812 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2813}
2814
2815void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2816 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2817
halcanary96fcdcc2015-08-27 07:41:13 -07002818 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002819
dandovecfff212014-08-04 10:02:00 -07002820 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002821 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002822 }
mtklein6cfa73a2014-08-13 13:33:49 -07002823
dandovecfff212014-08-04 10:02:00 -07002824 LOOPER_END
2825}
2826
reeda8db7282015-07-07 10:22:31 -07002827void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
reede3b38ce2016-01-08 09:18:44 -08002828 RETURN_ON_NULL(dr);
2829 if (x || y) {
2830 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2831 this->onDrawDrawable(dr, &matrix);
2832 } else {
2833 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002834 }
2835}
2836
reeda8db7282015-07-07 10:22:31 -07002837void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reede3b38ce2016-01-08 09:18:44 -08002838 RETURN_ON_NULL(dr);
2839 if (matrix && matrix->isIdentity()) {
2840 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002841 }
reede3b38ce2016-01-08 09:18:44 -08002842 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002843}
2844
2845void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2846 SkRect bounds = dr->getBounds();
2847 if (matrix) {
2848 matrix->mapRect(&bounds);
2849 }
2850 if (this->quickReject(bounds)) {
2851 return;
2852 }
2853 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002854}
2855
reed71c3c762015-06-24 10:29:17 -07002856void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2857 const SkColor colors[], int count, SkXfermode::Mode mode,
2858 const SkRect* cull, const SkPaint* paint) {
2859 if (cull && this->quickReject(*cull)) {
2860 return;
2861 }
2862
2863 SkPaint pnt;
2864 if (paint) {
2865 pnt = *paint;
2866 }
halcanary9d524f22016-03-29 09:03:52 -07002867
halcanary96fcdcc2015-08-27 07:41:13 -07002868 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr)
reed71c3c762015-06-24 10:29:17 -07002869 while (iter.next()) {
2870 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, mode, pnt);
2871 }
2872 LOOPER_END
2873}
2874
reedf70b5312016-03-04 16:36:20 -08002875void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2876 SkASSERT(key);
2877
2878 SkPaint paint;
2879 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, nullptr)
2880 while (iter.next()) {
2881 iter.fDevice->drawAnnotation(iter, rect, key, value);
2882 }
2883 LOOPER_END
2884}
2885
reed@android.com8a1c16f2008-12-17 15:59:43 +00002886//////////////////////////////////////////////////////////////////////////////
2887// These methods are NOT virtual, and therefore must call back into virtual
2888// methods, rather than actually drawing themselves.
2889//////////////////////////////////////////////////////////////////////////////
2890
2891void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002892 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002893 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002894 SkPaint paint;
2895
2896 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002897 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002898 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002899 }
2900 this->drawPaint(paint);
2901}
2902
reed@android.com845fdac2009-06-23 03:01:32 +00002903void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002904 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002905 SkPaint paint;
2906
2907 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002908 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002909 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002910 }
2911 this->drawPaint(paint);
2912}
2913
2914void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002915 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002916 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002917
reed@android.com8a1c16f2008-12-17 15:59:43 +00002918 pt.set(x, y);
2919 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2920}
2921
2922void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002923 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002924 SkPoint pt;
2925 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002926
reed@android.com8a1c16f2008-12-17 15:59:43 +00002927 pt.set(x, y);
2928 paint.setColor(color);
2929 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2930}
2931
2932void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2933 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002934 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002935 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002936
reed@android.com8a1c16f2008-12-17 15:59:43 +00002937 pts[0].set(x0, y0);
2938 pts[1].set(x1, y1);
2939 this->drawPoints(kLines_PointMode, 2, pts, paint);
2940}
2941
2942void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2943 SkScalar right, SkScalar bottom,
2944 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002945 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002946 SkRect r;
2947
2948 r.set(left, top, right, bottom);
2949 this->drawRect(r, paint);
2950}
2951
2952void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2953 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002954 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002955 if (radius < 0) {
2956 radius = 0;
2957 }
2958
2959 SkRect r;
2960 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002961 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002962}
2963
2964void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2965 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002966 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002967 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002968 SkRRect rrect;
2969 rrect.setRectXY(r, rx, ry);
2970 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002971 } else {
2972 this->drawRect(r, paint);
2973 }
2974}
2975
reed@android.com8a1c16f2008-12-17 15:59:43 +00002976void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2977 SkScalar sweepAngle, bool useCenter,
2978 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002979 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002980 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2981 this->drawOval(oval, paint);
2982 } else {
2983 SkPath path;
2984 if (useCenter) {
2985 path.moveTo(oval.centerX(), oval.centerY());
2986 }
2987 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2988 if (useCenter) {
2989 path.close();
2990 }
2991 this->drawPath(path, paint);
2992 }
2993}
2994
2995void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2996 const SkPath& path, SkScalar hOffset,
2997 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002998 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002999 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00003000
reed@android.com8a1c16f2008-12-17 15:59:43 +00003001 matrix.setTranslate(hOffset, vOffset);
3002 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
3003}
3004
reed@android.comf76bacf2009-05-13 14:00:33 +00003005///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07003006
3007/**
3008 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
3009 * against the playback cost of recursing into the subpicture to get at its actual ops.
3010 *
3011 * For now we pick a conservatively small value, though measurement (and other heuristics like
3012 * the type of ops contained) may justify changing this value.
3013 */
3014#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07003015
reedd5fa1a42014-08-09 11:08:05 -07003016void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08003017 RETURN_ON_NULL(picture);
3018
reed1c2c4412015-04-30 13:09:24 -07003019 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
reede3b38ce2016-01-08 09:18:44 -08003020 if (matrix && matrix->isIdentity()) {
3021 matrix = nullptr;
3022 }
3023 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
3024 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
3025 picture->playback(this);
3026 } else {
3027 this->onDrawPicture(picture, matrix, paint);
reedd5fa1a42014-08-09 11:08:05 -07003028 }
3029}
robertphillips9b14f262014-06-04 05:40:44 -07003030
reedd5fa1a42014-08-09 11:08:05 -07003031void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
3032 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07003033 if (!paint || paint->canComputeFastBounds()) {
3034 SkRect bounds = picture->cullRect();
3035 if (paint) {
3036 paint->computeFastBounds(bounds, &bounds);
3037 }
3038 if (matrix) {
3039 matrix->mapRect(&bounds);
3040 }
3041 if (this->quickReject(bounds)) {
3042 return;
3043 }
3044 }
3045
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003046 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07003047 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003048}
3049
vjiaoblack95302da2016-07-21 10:25:54 -07003050#ifdef SK_EXPERIMENTAL_SHADOWING
3051void SkCanvas::drawShadowedPicture(const SkPicture* picture,
3052 const SkMatrix* matrix,
3053 const SkPaint* paint) {
3054 RETURN_ON_NULL(picture);
3055
3056 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawShadowedPicture()");
3057
3058 this->onDrawShadowedPicture(picture, matrix, paint);
3059}
3060
3061void SkCanvas::onDrawShadowedPicture(const SkPicture* picture,
3062 const SkMatrix* matrix,
3063 const SkPaint* paint) {
3064 this->onDrawPicture(picture, matrix, paint);
3065}
3066#endif
3067
reed@android.com8a1c16f2008-12-17 15:59:43 +00003068///////////////////////////////////////////////////////////////////////////////
3069///////////////////////////////////////////////////////////////////////////////
3070
3071SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
bungeman99fe8222015-08-20 07:57:51 -07003072 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003073
3074 SkASSERT(canvas);
3075
3076 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
3077 fDone = !fImpl->next();
3078}
3079
3080SkCanvas::LayerIter::~LayerIter() {
3081 fImpl->~SkDrawIter();
3082}
3083
3084void SkCanvas::LayerIter::next() {
3085 fDone = !fImpl->next();
3086}
3087
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003088SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003089 return fImpl->getDevice();
3090}
3091
3092const SkMatrix& SkCanvas::LayerIter::matrix() const {
3093 return fImpl->getMatrix();
3094}
3095
3096const SkPaint& SkCanvas::LayerIter::paint() const {
3097 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003098 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003099 paint = &fDefaultPaint;
3100 }
3101 return *paint;
3102}
3103
reed1e7f5e72016-04-27 07:49:17 -07003104const SkRasterClip& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +00003105int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
3106int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003107
3108///////////////////////////////////////////////////////////////////////////////
3109
fmalitac3b589a2014-06-05 12:40:07 -07003110SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003111
3112///////////////////////////////////////////////////////////////////////////////
3113
3114static bool supported_for_raster_canvas(const SkImageInfo& info) {
3115 switch (info.alphaType()) {
3116 case kPremul_SkAlphaType:
3117 case kOpaque_SkAlphaType:
3118 break;
3119 default:
3120 return false;
3121 }
3122
3123 switch (info.colorType()) {
3124 case kAlpha_8_SkColorType:
3125 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00003126 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003127 break;
3128 default:
3129 return false;
3130 }
3131
3132 return true;
3133}
3134
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003135SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
3136 if (!supported_for_raster_canvas(info)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003137 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003138 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003139
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003140 SkBitmap bitmap;
3141 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003142 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003143 }
halcanary385fe4d2015-08-26 13:07:48 -07003144 return new SkCanvas(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003145}
reedd5fa1a42014-08-09 11:08:05 -07003146
3147///////////////////////////////////////////////////////////////////////////////
3148
3149SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003150 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07003151 : fCanvas(canvas)
3152 , fSaveCount(canvas->getSaveCount())
3153{
bsalomon49f085d2014-09-05 13:34:00 -07003154 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003155 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07003156 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003157 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07003158 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003159 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07003160 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003161 canvas->save();
3162 }
mtklein6cfa73a2014-08-13 13:33:49 -07003163
bsalomon49f085d2014-09-05 13:34:00 -07003164 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003165 canvas->concat(*matrix);
3166 }
3167}
3168
3169SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
3170 fCanvas->restoreToCount(fSaveCount);
3171}
reede8f30622016-03-23 18:59:25 -07003172
3173#ifdef SK_SUPPORT_LEGACY_NEW_SURFACE_API
3174SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
3175 return this->makeSurface(info, props).release();
3176}
3177#endif