blob: 2aa3f5ef2144a7c8757f26b1bc603f207113b5ef [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"
reed262a71b2015-12-05 13:07:27 -080022#include "SkMatrixUtils.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000023#include "SkMetaData.h"
reed4c21dc52015-06-25 12:32:03 -070024#include "SkNinePatchIter.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"
robertphillips7354a4b2015-12-16 05:08:27 -080044#include "SkGr.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
reed61f501f2015-04-29 08:34:00 -0700203 const bool fDeviceIsBitmapDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204
reed96e657d2015-03-10 17:30:07 -0700205 DeviceCM(SkBaseDevice* device, const SkPaint* paint, SkCanvas* canvas,
reed8c30a812016-04-20 16:36:51 -0700206 bool conservativeRasterClip, bool deviceIsBitmapDevice, const SkMatrix& stashed)
halcanary96fcdcc2015-08-27 07:41:13 -0700207 : fNext(nullptr)
reedd9544982014-09-09 18:46:22 -0700208 , fClip(conservativeRasterClip)
reed8c30a812016-04-20 16:36:51 -0700209 , fStashedMatrix(stashed)
reed61f501f2015-04-29 08:34:00 -0700210 , fDeviceIsBitmapDevice(deviceIsBitmapDevice)
reedd9544982014-09-09 18:46:22 -0700211 {
halcanary96fcdcc2015-08-27 07:41:13 -0700212 if (nullptr != device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000213 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000214 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215 }
reed@google.com4b226022011-01-11 18:32:13 +0000216 fDevice = device;
halcanary96fcdcc2015-08-27 07:41:13 -0700217 fPaint = paint ? new SkPaint(*paint) : nullptr;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000218 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000219
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000220 ~DeviceCM() {
bsalomon49f085d2014-09-05 13:34:00 -0700221 if (fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000222 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000223 fDevice->unref();
224 }
halcanary385fe4d2015-08-26 13:07:48 -0700225 delete fPaint;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000226 }
reed@google.com4b226022011-01-11 18:32:13 +0000227
mtkleinfeaadee2015-04-08 11:25:48 -0700228 void reset(const SkIRect& bounds) {
229 SkASSERT(!fPaint);
230 SkASSERT(!fNext);
231 SkASSERT(fDevice);
232 fClip.setRect(bounds);
233 }
234
reed@google.com045e62d2011-10-24 12:19:46 +0000235 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
236 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000237 int x = fDevice->getOrigin().x();
238 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000239 int width = fDevice->width();
240 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000241
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242 if ((x | y) == 0) {
243 fMatrix = &totalMatrix;
244 fClip = totalClip;
245 } else {
246 fMatrixStorage = totalMatrix;
247 fMatrixStorage.postTranslate(SkIntToScalar(-x),
248 SkIntToScalar(-y));
249 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000250
reed@android.com8a1c16f2008-12-17 15:59:43 +0000251 totalClip.translate(-x, -y, &fClip);
252 }
253
reed@google.com045e62d2011-10-24 12:19:46 +0000254 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255
256 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000257
reed@android.com8a1c16f2008-12-17 15:59:43 +0000258 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000259 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260 SkRegion::kDifference_Op);
261 }
reed@google.com4b226022011-01-11 18:32:13 +0000262
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000263 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
264
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265#ifdef SK_DEBUG
266 if (!fClip.isEmpty()) {
267 SkIRect deviceR;
268 deviceR.set(0, 0, width, height);
269 SkASSERT(deviceR.contains(fClip.getBounds()));
270 }
271#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000272 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000273};
274
275/* This is the record we keep for each save/restore level in the stack.
276 Since a level optionally copies the matrix and/or stack, we have pointers
277 for these fields. If the value is copied for this level, the copy is
278 stored in the ...Storage field, and the pointer points to that. If the
279 value is not copied for this level, we ignore ...Storage, and just point
280 at the corresponding value in the previous level in the stack.
281*/
282class SkCanvas::MCRec {
283public:
reed1f836ee2014-07-07 07:49:34 -0700284 SkDrawFilter* fFilter; // the current filter (or null)
reedd9544982014-09-09 18:46:22 -0700285 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000286 /* If there are any layers in the stack, this points to the top-most
287 one that is at or below this level in the stack (so we know what
288 bitmap/device to draw into from this level. This value is NOT
289 reference counted, since the real owner is either our fLayer field,
290 or a previous one in a lower level.)
291 */
reed2ff1fce2014-12-11 07:07:37 -0800292 DeviceCM* fTopLayer;
293 SkRasterClip fRasterClip;
294 SkMatrix fMatrix;
295 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000296
vjiaoblack6d3fb892016-07-12 14:50:41 -0700297 // This is the current cumulative depth (aggregate of all done translateZ calls)
298 SkScalar fCurDrawDepth;
299
reedd9544982014-09-09 18:46:22 -0700300 MCRec(bool conservativeRasterClip) : fRasterClip(conservativeRasterClip) {
halcanary96fcdcc2015-08-27 07:41:13 -0700301 fFilter = nullptr;
302 fLayer = nullptr;
303 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800304 fMatrix.reset();
305 fDeferredSaveCount = 0;
vjiaoblack6d3fb892016-07-12 14:50:41 -0700306 fCurDrawDepth = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700307
reedd9544982014-09-09 18:46:22 -0700308 // don't bother initializing fNext
309 inc_rec();
310 }
vjiaoblack6d3fb892016-07-12 14:50:41 -0700311 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix),
312 fCurDrawDepth(prev.fCurDrawDepth) {
reedd9544982014-09-09 18:46:22 -0700313 fFilter = SkSafeRef(prev.fFilter);
halcanary96fcdcc2015-08-27 07:41:13 -0700314 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700315 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800316 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700317
reed@android.com8a1c16f2008-12-17 15:59:43 +0000318 // don't bother initializing fNext
319 inc_rec();
320 }
321 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000322 SkSafeUnref(fFilter);
halcanary385fe4d2015-08-26 13:07:48 -0700323 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000324 dec_rec();
325 }
mtkleinfeaadee2015-04-08 11:25:48 -0700326
327 void reset(const SkIRect& bounds) {
328 SkASSERT(fLayer);
329 SkASSERT(fDeferredSaveCount == 0);
330
331 fMatrix.reset();
332 fRasterClip.setRect(bounds);
333 fLayer->reset(bounds);
334 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000335};
336
337class SkDrawIter : public SkDraw {
338public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000339 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000340 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000341 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000342 canvas->updateDeviceCMCache();
343
reed687fa1c2015-04-07 08:00:56 -0700344 fClipStack = canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000345 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000346 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347 }
reed@google.com4b226022011-01-11 18:32:13 +0000348
reed@android.com8a1c16f2008-12-17 15:59:43 +0000349 bool next() {
350 // skip over recs with empty clips
351 if (fSkipEmptyClips) {
352 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
353 fCurrLayer = fCurrLayer->fNext;
354 }
355 }
356
reed@google.comf68c5e22012-02-24 16:38:58 +0000357 const DeviceCM* rec = fCurrLayer;
358 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000359
360 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000361 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000362 fDevice = rec->fDevice;
reed41e010c2015-06-09 12:16:53 -0700363 if (!fDevice->accessPixels(&fDst)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700364 fDst.reset(fDevice->imageInfo(), nullptr, 0);
reed41e010c2015-06-09 12:16:53 -0700365 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000366 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000367 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000368
369 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700370 // fCurrLayer may be nullptr now
reed@android.com199f1082009-06-10 02:12:47 +0000371
reed@android.com8a1c16f2008-12-17 15:59:43 +0000372 return true;
373 }
374 return false;
375 }
reed@google.com4b226022011-01-11 18:32:13 +0000376
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000377 SkBaseDevice* getDevice() const { return fDevice; }
reed1e7f5e72016-04-27 07:49:17 -0700378 const SkRasterClip& getClip() const { return *fRC; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000379 int getX() const { return fDevice->getOrigin().x(); }
380 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000381 const SkMatrix& getMatrix() const { return *fMatrix; }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000382 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000383
reed@android.com8a1c16f2008-12-17 15:59:43 +0000384private:
385 SkCanvas* fCanvas;
386 const DeviceCM* fCurrLayer;
387 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000388 SkBool8 fSkipEmptyClips;
389
390 typedef SkDraw INHERITED;
391};
392
393/////////////////////////////////////////////////////////////////////////////
394
reeddbc3cef2015-04-29 12:18:57 -0700395static SkPaint* set_if_needed(SkLazyPaint* lazy, const SkPaint& orig) {
396 return lazy->isValid() ? lazy->get() : lazy->set(orig);
397}
398
399/**
400 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700401 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700402 */
reedd053ce92016-03-22 10:17:23 -0700403static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700404 SkImageFilter* imgf = paint.getImageFilter();
405 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700406 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700407 }
408
reedd053ce92016-03-22 10:17:23 -0700409 SkColorFilter* imgCFPtr;
410 if (!imgf->asAColorFilter(&imgCFPtr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700411 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700412 }
reedd053ce92016-03-22 10:17:23 -0700413 sk_sp<SkColorFilter> imgCF(imgCFPtr);
reeddbc3cef2015-04-29 12:18:57 -0700414
415 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700416 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700417 // there is no existing paint colorfilter, so we can just return the imagefilter's
418 return imgCF;
419 }
420
421 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
422 // and we need to combine them into a single colorfilter.
reedd053ce92016-03-22 10:17:23 -0700423 return SkColorFilter::MakeComposeFilter(std::move(imgCF), sk_ref_sp(paintCF));
reeddbc3cef2015-04-29 12:18:57 -0700424}
425
senorblanco87e066e2015-10-28 11:23:36 -0700426/**
427 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
428 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
429 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
430 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
431 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
432 * conservative "effective" bounds based on the settings in the paint... with one exception. This
433 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
434 * deliberately ignored.
435 */
436static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
437 const SkRect& rawBounds,
438 SkRect* storage) {
439 SkPaint tmpUnfiltered(paint);
440 tmpUnfiltered.setImageFilter(nullptr);
441 if (tmpUnfiltered.canComputeFastBounds()) {
442 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
443 } else {
444 return rawBounds;
445 }
446}
447
reed@android.com8a1c16f2008-12-17 15:59:43 +0000448class AutoDrawLooper {
449public:
senorblanco87e066e2015-10-28 11:23:36 -0700450 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
451 // paint. It's used to determine the size of the offscreen layer for filters.
452 // If null, the clip will be used instead.
reed4a8126e2014-09-22 07:29:03 -0700453 AutoDrawLooper(SkCanvas* canvas, const SkSurfaceProps& props, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000454 bool skipLayerForImageFilter = false,
senorblanco87e066e2015-10-28 11:23:36 -0700455 const SkRect* rawBounds = nullptr) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000456 fCanvas = canvas;
fmalita53d9f1c2016-01-25 06:23:54 -0800457#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000458 fFilter = canvas->getDrawFilter();
fmalita77650002016-01-21 18:47:11 -0800459#else
460 fFilter = nullptr;
461#endif
reed4a8126e2014-09-22 07:29:03 -0700462 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000463 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700464 fTempLayerForImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000465 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000466
reedd053ce92016-03-22 10:17:23 -0700467 auto simplifiedCF = image_to_color_filter(fOrigPaint);
reeddbc3cef2015-04-29 12:18:57 -0700468 if (simplifiedCF) {
469 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reedd053ce92016-03-22 10:17:23 -0700470 paint->setColorFilter(std::move(simplifiedCF));
halcanary96fcdcc2015-08-27 07:41:13 -0700471 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700472 fPaint = paint;
473 }
474
475 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700476 /**
477 * We implement ImageFilters for a given draw by creating a layer, then applying the
478 * imagefilter to the pixels of that layer (its backing surface/image), and then
479 * we call restore() to xfer that layer to the main canvas.
480 *
481 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
482 * 2. Generate the src pixels:
483 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
484 * return (fPaint). We then draw the primitive (using srcover) into a cleared
485 * buffer/surface.
486 * 3. Restore the layer created in #1
487 * The imagefilter is passed the buffer/surface from the layer (now filled with the
488 * src pixels of the primitive). It returns a new "filtered" buffer, which we
489 * draw onto the previous layer using the xfermode from the original paint.
490 */
reed@google.com8926b162012-03-23 15:36:36 +0000491 SkPaint tmp;
reeddbc3cef2015-04-29 12:18:57 -0700492 tmp.setImageFilter(fPaint->getImageFilter());
reedcfb6bdf2016-03-29 11:32:50 -0700493 tmp.setXfermode(sk_ref_sp(fPaint->getXfermode()));
senorblanco87e066e2015-10-28 11:23:36 -0700494 SkRect storage;
495 if (rawBounds) {
496 // Make rawBounds include all paint outsets except for those due to image filters.
497 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
498 }
reedbfd5f172016-01-07 11:28:08 -0800499 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &tmp),
reed76033be2015-03-14 10:54:31 -0700500 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700501 fTempLayerForImageFilter = true;
502 // we remove the imagefilter/xfermode inside doNext()
reed@google.com8926b162012-03-23 15:36:36 +0000503 }
504
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000505 if (SkDrawLooper* looper = paint.getLooper()) {
506 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
507 looper->contextSize());
508 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000509 fIsSimple = false;
510 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700511 fLooperContext = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000512 // can we be marked as simple?
reed5c476fb2015-04-20 08:04:21 -0700513 fIsSimple = !fFilter && !fTempLayerForImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000514 }
piotaixrb5fae932014-09-24 13:03:30 -0700515
reed4a8126e2014-09-22 07:29:03 -0700516 uint32_t oldFlags = paint.getFlags();
517 fNewPaintFlags = filter_paint_flags(props, oldFlags);
518 if (fIsSimple && (fNewPaintFlags != oldFlags)) {
reeddbc3cef2015-04-29 12:18:57 -0700519 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700520 paint->setFlags(fNewPaintFlags);
521 fPaint = paint;
522 // if we're not simple, doNext() will take care of calling setFlags()
523 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000524 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000525
reed@android.com8a1c16f2008-12-17 15:59:43 +0000526 ~AutoDrawLooper() {
reed5c476fb2015-04-20 08:04:21 -0700527 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000528 fCanvas->internalRestore();
529 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000530 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000531 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000532
reed@google.com4e2b3d32011-04-07 14:18:59 +0000533 const SkPaint& paint() const {
534 SkASSERT(fPaint);
535 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000536 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000537
reed@google.com129ec222012-05-15 13:24:09 +0000538 bool next(SkDrawFilter::Type drawType) {
539 if (fDone) {
540 return false;
541 } else if (fIsSimple) {
542 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000543 return !fPaint->nothingToDraw();
544 } else {
545 return this->doNext(drawType);
546 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000547 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000548
reed@android.com8a1c16f2008-12-17 15:59:43 +0000549private:
reeddbc3cef2015-04-29 12:18:57 -0700550 SkLazyPaint fLazyPaintInit; // base paint storage in case we need to modify it
551 SkLazyPaint fLazyPaintPerLooper; // per-draw-looper storage, so the looper can modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000552 SkCanvas* fCanvas;
553 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000554 SkDrawFilter* fFilter;
555 const SkPaint* fPaint;
556 int fSaveCount;
reed4a8126e2014-09-22 07:29:03 -0700557 uint32_t fNewPaintFlags;
reed5c476fb2015-04-20 08:04:21 -0700558 bool fTempLayerForImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000559 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000560 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000561 SkDrawLooper::Context* fLooperContext;
562 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000563
564 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000565};
566
reed@google.com129ec222012-05-15 13:24:09 +0000567bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
halcanary96fcdcc2015-08-27 07:41:13 -0700568 fPaint = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000569 SkASSERT(!fIsSimple);
reed5c476fb2015-04-20 08:04:21 -0700570 SkASSERT(fLooperContext || fFilter || fTempLayerForImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000571
reeddbc3cef2015-04-29 12:18:57 -0700572 SkPaint* paint = fLazyPaintPerLooper.set(fLazyPaintInit.isValid() ?
573 *fLazyPaintInit.get() : fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700574 paint->setFlags(fNewPaintFlags);
reed@google.com129ec222012-05-15 13:24:09 +0000575
reed5c476fb2015-04-20 08:04:21 -0700576 if (fTempLayerForImageFilter) {
halcanary96fcdcc2015-08-27 07:41:13 -0700577 paint->setImageFilter(nullptr);
578 paint->setXfermode(nullptr);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000579 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000580
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000581 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000582 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000583 return false;
584 }
585 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000586 if (!fFilter->filter(paint, drawType)) {
587 fDone = true;
588 return false;
589 }
halcanary96fcdcc2015-08-27 07:41:13 -0700590 if (nullptr == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000591 // no looper means we only draw once
592 fDone = true;
593 }
594 }
595 fPaint = paint;
596
597 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000598 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000599 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000600 }
601
602 // call this after any possible paint modifiers
603 if (fPaint->nothingToDraw()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700604 fPaint = nullptr;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000605 return false;
606 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000607 return true;
608}
609
reed@android.com8a1c16f2008-12-17 15:59:43 +0000610////////// macros to place around the internal draw calls //////////////////
611
reed262a71b2015-12-05 13:07:27 -0800612#define LOOPER_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
613 this->predrawNotify(); \
614 AutoDrawLooper looper(this, fProps, paint, skipLayerForFilter, bounds); \
615 while (looper.next(SkDrawFilter::kBitmap_Type)) { \
616 SkDrawIter iter(this);
617
618
reed@google.com8926b162012-03-23 15:36:36 +0000619#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000620 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700621 AutoDrawLooper looper(this, fProps, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000622 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000623 SkDrawIter iter(this);
624
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000625#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000626 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700627 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000628 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000629 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000630
reedc83a2972015-07-16 07:40:45 -0700631#define LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, type, bounds, auxOpaque) \
632 this->predrawNotify(bounds, &paint, auxOpaque); \
633 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
634 while (looper.next(type)) { \
635 SkDrawIter iter(this);
636
reed@google.com4e2b3d32011-04-07 14:18:59 +0000637#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000638
639////////////////////////////////////////////////////////////////////////////
640
mtkleinfeaadee2015-04-08 11:25:48 -0700641void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
642 this->restoreToCount(1);
643 fCachedLocalClipBounds.setEmpty();
644 fCachedLocalClipBoundsDirty = true;
645 fClipStack->reset();
646 fMCRec->reset(bounds);
647
648 // We're peering through a lot of structs here. Only at this scope do we
649 // know that the device is an SkBitmapDevice (really an SkNoPixelsBitmapDevice).
650 static_cast<SkBitmapDevice*>(fMCRec->fLayer->fDevice)->setNewSize(bounds.size());
651}
652
reedd9544982014-09-09 18:46:22 -0700653SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
reed42b73eb2015-11-20 13:42:42 -0800654 if (device && device->forceConservativeRasterClip()) {
655 flags = InitFlags(flags | kConservativeRasterClip_InitFlag);
656 }
657 // Since init() is only called once by our constructors, it is safe to perform this
658 // const-cast.
659 *const_cast<bool*>(&fConservativeRasterClip) = SkToBool(flags & kConservativeRasterClip_InitFlag);
660
reed@google.comc0784db2013-12-13 21:16:12 +0000661 fCachedLocalClipBounds.setEmpty();
662 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000663 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000664 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700665 fDeviceCMDirty = true;
reed2ff1fce2014-12-11 07:07:37 -0800666 fSaveCount = 1;
halcanary96fcdcc2015-08-27 07:41:13 -0700667 fMetaData = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000668
halcanary385fe4d2015-08-26 13:07:48 -0700669 fClipStack.reset(new SkClipStack);
reed687fa1c2015-04-07 08:00:56 -0700670
reed@android.com8a1c16f2008-12-17 15:59:43 +0000671 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700672 new (fMCRec) MCRec(fConservativeRasterClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000673
reeda499f902015-05-01 09:34:31 -0700674 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
675 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
reed8c30a812016-04-20 16:36:51 -0700676 new (fDeviceCMStorage) DeviceCM(nullptr, nullptr, nullptr, fConservativeRasterClip, false,
677 fMCRec->fMatrix);
reedb679ca82015-04-07 04:40:48 -0700678
reed@android.com8a1c16f2008-12-17 15:59:43 +0000679 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000680
halcanary96fcdcc2015-08-27 07:41:13 -0700681 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000682
reedf92c8662014-08-18 08:02:43 -0700683 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700684 // The root device and the canvas should always have the same pixel geometry
685 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reedf92c8662014-08-18 08:02:43 -0700686 device->onAttachToCanvas(this);
687 fMCRec->fLayer->fDevice = SkRef(device);
reed78e27682014-11-19 08:04:34 -0800688 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
reedf92c8662014-08-18 08:02:43 -0700689 }
690 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000691}
692
reed@google.comcde92112011-07-06 20:00:52 +0000693SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000694 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700695 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800696 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000697{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000698 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000699
halcanary96fcdcc2015-08-27 07:41:13 -0700700 this->init(nullptr, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000701}
702
reedd9544982014-09-09 18:46:22 -0700703static SkBitmap make_nopixels(int width, int height) {
704 SkBitmap bitmap;
705 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
706 return bitmap;
707}
708
709class SkNoPixelsBitmapDevice : public SkBitmapDevice {
710public:
robertphillipsfcf78292015-06-19 11:49:52 -0700711 SkNoPixelsBitmapDevice(const SkIRect& bounds, const SkSurfaceProps& surfaceProps)
712 : INHERITED(make_nopixels(bounds.width(), bounds.height()), surfaceProps)
reed78e27682014-11-19 08:04:34 -0800713 {
714 this->setOrigin(bounds.x(), bounds.y());
715 }
reedd9544982014-09-09 18:46:22 -0700716
717private:
piotaixrb5fae932014-09-24 13:03:30 -0700718
reedd9544982014-09-09 18:46:22 -0700719 typedef SkBitmapDevice INHERITED;
720};
721
reed96a857e2015-01-25 10:33:58 -0800722SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000723 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800724 , fProps(SkSurfacePropsCopyOrDefault(props))
reed42b73eb2015-11-20 13:42:42 -0800725 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000726{
727 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700728
halcanary385fe4d2015-08-26 13:07:48 -0700729 this->init(new SkNoPixelsBitmapDevice(SkIRect::MakeWH(width, height), fProps),
730 kDefault_InitFlags)->unref();
reedd9544982014-09-09 18:46:22 -0700731}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000732
reed78e27682014-11-19 08:04:34 -0800733SkCanvas::SkCanvas(const SkIRect& bounds, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700734 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700735 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800736 , fConservativeRasterClip(false)
reedd9544982014-09-09 18:46:22 -0700737{
738 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700739
halcanary385fe4d2015-08-26 13:07:48 -0700740 this->init(new SkNoPixelsBitmapDevice(bounds, fProps), flags)->unref();
reedd9544982014-09-09 18:46:22 -0700741}
742
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000743SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000744 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700745 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800746 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000747{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000748 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700749
reedd9544982014-09-09 18:46:22 -0700750 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000751}
752
robertphillipsfcf78292015-06-19 11:49:52 -0700753SkCanvas::SkCanvas(SkBaseDevice* device, InitFlags flags)
754 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700755 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800756 , fConservativeRasterClip(false)
robertphillipsfcf78292015-06-19 11:49:52 -0700757{
758 inc_canvas();
759
760 this->init(device, flags);
761}
762
reed4a8126e2014-09-22 07:29:03 -0700763SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700764 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700765 , fProps(props)
reed42b73eb2015-11-20 13:42:42 -0800766 , fConservativeRasterClip(false)
reed3716fd02014-09-21 09:39:55 -0700767{
768 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700769
halcanary385fe4d2015-08-26 13:07:48 -0700770 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700771 this->init(device, kDefault_InitFlags);
772}
reed29c857d2014-09-21 10:25:07 -0700773
reed4a8126e2014-09-22 07:29:03 -0700774SkCanvas::SkCanvas(const SkBitmap& bitmap)
775 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
776 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800777 , fConservativeRasterClip(false)
reed4a8126e2014-09-22 07:29:03 -0700778{
779 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700780
halcanary385fe4d2015-08-26 13:07:48 -0700781 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700782 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000783}
784
785SkCanvas::~SkCanvas() {
786 // free up the contents of our deque
787 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000788
reed@android.com8a1c16f2008-12-17 15:59:43 +0000789 this->internalRestore(); // restore the last, since we're going away
790
halcanary385fe4d2015-08-26 13:07:48 -0700791 delete fMetaData;
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000792
reed@android.com8a1c16f2008-12-17 15:59:43 +0000793 dec_canvas();
794}
795
fmalita53d9f1c2016-01-25 06:23:54 -0800796#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000797SkDrawFilter* SkCanvas::getDrawFilter() const {
798 return fMCRec->fFilter;
799}
800
801SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
reed51985e32015-04-11 08:04:56 -0700802 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000803 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
804 return filter;
805}
fmalita77650002016-01-21 18:47:11 -0800806#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000807
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000808SkMetaData& SkCanvas::getMetaData() {
809 // metadata users are rare, so we lazily allocate it. If that changes we
810 // can decide to just make it a field in the device (rather than a ptr)
halcanary96fcdcc2015-08-27 07:41:13 -0700811 if (nullptr == fMetaData) {
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000812 fMetaData = new SkMetaData;
813 }
814 return *fMetaData;
815}
816
reed@android.com8a1c16f2008-12-17 15:59:43 +0000817///////////////////////////////////////////////////////////////////////////////
818
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000819void SkCanvas::flush() {
reedea5a6512016-07-07 16:44:27 -0700820 this->onFlush();
821}
822
823void SkCanvas::onFlush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000824 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000825 if (device) {
826 device->flush();
827 }
828}
829
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000830SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000831 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000832 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
833}
834
senorblancoafc7cce2016-02-02 18:44:15 -0800835SkIRect SkCanvas::getTopLayerBounds() const {
836 SkBaseDevice* d = this->getTopDevice();
837 if (!d) {
838 return SkIRect::MakeEmpty();
839 }
840 return SkIRect::MakeXYWH(d->getOrigin().x(), d->getOrigin().y(), d->width(), d->height());
841}
842
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000843SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000844 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000845 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000846 SkASSERT(rec && rec->fLayer);
847 return rec->fLayer->fDevice;
848}
849
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000850SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000851 if (updateMatrixClip) {
852 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
853 }
reed@google.com9266fed2011-03-30 00:18:03 +0000854 return fMCRec->fTopLayer->fDevice;
855}
856
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000857bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
858 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
859 return false;
860 }
861
862 bool weAllocated = false;
halcanary96fcdcc2015-08-27 07:41:13 -0700863 if (nullptr == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700864 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000865 return false;
866 }
867 weAllocated = true;
868 }
869
reedcf01e312015-05-23 19:14:51 -0700870 SkAutoPixmapUnlock unlocker;
871 if (bitmap->requestLock(&unlocker)) {
872 const SkPixmap& pm = unlocker.pixmap();
873 if (this->readPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), x, y)) {
874 return true;
875 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000876 }
877
878 if (weAllocated) {
halcanary96fcdcc2015-08-27 07:41:13 -0700879 bitmap->setPixelRef(nullptr);
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000880 }
881 return false;
882}
reed@google.com51df9e32010-12-23 19:29:18 +0000883
bsalomon@google.comc6980972011-11-02 19:57:21 +0000884bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000885 SkIRect r = srcRect;
886 const SkISize size = this->getBaseLayerSize();
887 if (!r.intersect(0, 0, size.width(), size.height())) {
888 bitmap->reset();
889 return false;
890 }
891
reed84825042014-09-02 12:50:45 -0700892 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000893 // bitmap will already be reset.
894 return false;
895 }
896 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
897 bitmap->reset();
898 return false;
899 }
900 return true;
901}
902
reed96472de2014-12-10 09:53:42 -0800903bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000904 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000905 if (!device) {
906 return false;
907 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000908 const SkISize size = this->getBaseLayerSize();
mtkleinf0f14112014-12-12 08:46:25 -0800909
reed96472de2014-12-10 09:53:42 -0800910 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
911 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000912 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000913 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000914
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000915 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800916 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000917}
918
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000919bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
920 if (bitmap.getTexture()) {
921 return false;
922 }
reedcf01e312015-05-23 19:14:51 -0700923
924 SkAutoPixmapUnlock unlocker;
925 if (bitmap.requestLock(&unlocker)) {
926 const SkPixmap& pm = unlocker.pixmap();
927 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000928 }
929 return false;
930}
931
932bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
933 int x, int y) {
934 switch (origInfo.colorType()) {
935 case kUnknown_SkColorType:
936 case kIndex_8_SkColorType:
937 return false;
938 default:
939 break;
940 }
halcanary96fcdcc2015-08-27 07:41:13 -0700941 if (nullptr == pixels || rowBytes < origInfo.minRowBytes()) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000942 return false;
943 }
944
945 const SkISize size = this->getBaseLayerSize();
946 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
947 if (!target.intersect(0, 0, size.width(), size.height())) {
948 return false;
949 }
950
951 SkBaseDevice* device = this->getDevice();
952 if (!device) {
953 return false;
954 }
955
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000956 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700957 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000958
959 // if x or y are negative, then we have to adjust pixels
960 if (x > 0) {
961 x = 0;
962 }
963 if (y > 0) {
964 y = 0;
965 }
966 // here x,y are either 0 or negative
967 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
968
reed4af35f32014-06-27 17:47:49 -0700969 // Tell our owning surface to bump its generation ID
reedc83a2972015-07-16 07:40:45 -0700970 const bool completeOverwrite = info.dimensions() == size;
971 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700972
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000973 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000974 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000975}
reed@google.com51df9e32010-12-23 19:29:18 +0000976
junov@google.com4370aed2012-01-18 16:21:08 +0000977SkCanvas* SkCanvas::canvasForDrawIter() {
978 return this;
979}
980
reed@android.com8a1c16f2008-12-17 15:59:43 +0000981//////////////////////////////////////////////////////////////////////////////
982
reed@android.com8a1c16f2008-12-17 15:59:43 +0000983void SkCanvas::updateDeviceCMCache() {
984 if (fDeviceCMDirty) {
985 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700986 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000987 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000988
halcanary96fcdcc2015-08-27 07:41:13 -0700989 if (nullptr == layer->fNext) { // only one layer
990 layer->updateMC(totalMatrix, totalClip, *fClipStack, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000991 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000992 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000993 do {
reed687fa1c2015-04-07 08:00:56 -0700994 layer->updateMC(totalMatrix, clip, *fClipStack, &clip);
halcanary96fcdcc2015-08-27 07:41:13 -0700995 } while ((layer = layer->fNext) != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000996 }
997 fDeviceCMDirty = false;
998 }
999}
1000
reed@android.com8a1c16f2008-12-17 15:59:43 +00001001///////////////////////////////////////////////////////////////////////////////
1002
reed2ff1fce2014-12-11 07:07:37 -08001003void SkCanvas::checkForDeferredSave() {
1004 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -08001005 this->doSave();
1006 }
1007}
1008
reedf0090cb2014-11-26 08:55:51 -08001009int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -08001010#ifdef SK_DEBUG
1011 int count = 0;
1012 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
1013 for (;;) {
1014 const MCRec* rec = (const MCRec*)iter.next();
1015 if (!rec) {
1016 break;
1017 }
1018 count += 1 + rec->fDeferredSaveCount;
1019 }
1020 SkASSERT(count == fSaveCount);
1021#endif
1022 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -08001023}
1024
1025int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -08001026 fSaveCount += 1;
1027 fMCRec->fDeferredSaveCount += 1;
1028 return this->getSaveCount() - 1; // return our prev value
1029}
1030
1031void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -08001032 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -07001033
1034 SkASSERT(fMCRec->fDeferredSaveCount > 0);
1035 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001036 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -08001037}
1038
1039void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -08001040 if (fMCRec->fDeferredSaveCount > 0) {
1041 SkASSERT(fSaveCount > 1);
1042 fSaveCount -= 1;
1043 fMCRec->fDeferredSaveCount -= 1;
1044 } else {
1045 // check for underflow
1046 if (fMCStack.count() > 1) {
1047 this->willRestore();
1048 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -07001049 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001050 this->internalRestore();
1051 this->didRestore();
1052 }
reedf0090cb2014-11-26 08:55:51 -08001053 }
1054}
1055
1056void SkCanvas::restoreToCount(int count) {
1057 // sanity check
1058 if (count < 1) {
1059 count = 1;
1060 }
mtkleinf0f14112014-12-12 08:46:25 -08001061
reedf0090cb2014-11-26 08:55:51 -08001062 int n = this->getSaveCount() - count;
1063 for (int i = 0; i < n; ++i) {
1064 this->restore();
1065 }
1066}
1067
reed2ff1fce2014-12-11 07:07:37 -08001068void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001069 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -07001070 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +00001071 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +00001072
reed687fa1c2015-04-07 08:00:56 -07001073 fClipStack->save();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001074}
1075
reed4960eee2015-12-18 07:09:18 -08001076bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
reed@google.comb93ba452014-03-10 19:47:58 +00001077#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001078 return !(saveLayerFlags & SkCanvas::kDontClipToLayer_PrivateSaveLayerFlag);
reed@google.comb93ba452014-03-10 19:47:58 +00001079#else
1080 return true;
1081#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001082}
1083
reed4960eee2015-12-18 07:09:18 -08001084bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -07001085 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001086 SkIRect clipBounds;
1087 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001088 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +00001089 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001090
reed96e657d2015-03-10 17:30:07 -07001091 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
1092
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001093 if (imageFilter) {
senorblancoe5e79842016-03-21 14:51:59 -07001094 clipBounds = imageFilter->filterBounds(clipBounds, ctm);
senorblancodb64af32015-12-09 10:11:43 -08001095 if (bounds && !imageFilter->canComputeFastBounds()) {
1096 bounds = nullptr;
1097 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001098 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001099 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -07001100 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001101 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +00001102
reed96e657d2015-03-10 17:30:07 -07001103 ctm.mapRect(&r, *bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001104 r.roundOut(&ir);
1105 // early exit if the layer's bounds are clipped out
1106 if (!ir.intersect(clipBounds)) {
reed4960eee2015-12-18 07:09:18 -08001107 if (BoundsAffectsClip(saveLayerFlags)) {
reed9b3aa542015-03-11 08:47:12 -07001108 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001109 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001110 }
junov@chromium.orga907ac32012-02-24 21:54:07 +00001111 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001112 }
1113 } else { // no user bounds, so just use the clip
1114 ir = clipBounds;
1115 }
reed180aec42015-03-11 10:39:04 -07001116 SkASSERT(!ir.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001117
reed4960eee2015-12-18 07:09:18 -08001118 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -07001119 // Simplify the current clips since they will be applied properly during restore()
reed9b3aa542015-03-11 08:47:12 -07001120 fCachedLocalClipBoundsDirty = true;
reed687fa1c2015-04-07 08:00:56 -07001121 fClipStack->clipDevRect(ir, SkRegion::kReplace_Op);
reed180aec42015-03-11 10:39:04 -07001122 fMCRec->fRasterClip.setRect(ir);
junov@chromium.orga907ac32012-02-24 21:54:07 +00001123 }
1124
1125 if (intersection) {
1126 *intersection = ir;
1127 }
1128 return true;
1129}
1130
reed4960eee2015-12-18 07:09:18 -08001131
reed4960eee2015-12-18 07:09:18 -08001132int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
1133 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001134}
1135
reed70ee31b2015-12-10 13:44:45 -08001136int SkCanvas::saveLayerPreserveLCDTextRequests(const SkRect* bounds, const SkPaint* paint) {
reed4960eee2015-12-18 07:09:18 -08001137 return this->saveLayer(SaveLayerRec(bounds, paint, kPreserveLCDText_SaveLayerFlag));
1138}
1139
1140int SkCanvas::saveLayer(const SaveLayerRec& origRec) {
1141 SaveLayerRec rec(origRec);
1142 if (gIgnoreSaveLayerBounds) {
1143 rec.fBounds = nullptr;
1144 }
1145 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
1146 fSaveCount += 1;
1147 this->internalSaveLayer(rec, strategy);
1148 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -08001149}
1150
reedbfd5f172016-01-07 11:28:08 -08001151static void draw_filter_into_device(SkBaseDevice* src, const SkImageFilter* filter,
1152 SkBaseDevice* dst, const SkMatrix& ctm) {
robertphillips7354a4b2015-12-16 05:08:27 -08001153
1154 SkBitmap srcBM;
1155
1156#if SK_SUPPORT_GPU
robertphillips175dd9b2016-04-28 14:32:04 -07001157 // TODO: remove this virtual usage of accessRenderTarget! It is preventing
1158 // removal of the virtual on SkBaseDevice.
robertphillips7354a4b2015-12-16 05:08:27 -08001159 GrRenderTarget* srcRT = src->accessRenderTarget();
1160 if (srcRT && !srcRT->asTexture() && dst->accessRenderTarget()) {
1161 // When both the src & the dst are on the gpu but the src doesn't have a texture,
1162 // we create a temporary texture for the draw.
1163 // TODO: we should actually only copy the portion of the source needed to apply the image
1164 // filter
1165 GrContext* context = srcRT->getContext();
bsalomon5ec26ae2016-02-25 08:33:02 -08001166 SkAutoTUnref<GrTexture> tex(context->textureProvider()->createTexture(srcRT->desc(),
1167 SkBudgeted::kYes));
robertphillips7354a4b2015-12-16 05:08:27 -08001168
1169 context->copySurface(tex, srcRT);
1170
1171 GrWrapTextureInBitmap(tex, src->width(), src->height(), src->isOpaque(), &srcBM);
1172 } else
1173#endif
1174 {
1175 srcBM = src->accessBitmap(false);
1176 }
1177
1178 SkCanvas c(dst);
1179
1180 SkPaint p;
robertphillips372177e2016-03-30 07:32:28 -07001181 p.setImageFilter(filter->makeWithLocalMatrix(ctm));
reedbfd5f172016-01-07 11:28:08 -08001182 const SkScalar x = SkIntToScalar(src->getOrigin().x());
1183 const SkScalar y = SkIntToScalar(src->getOrigin().y());
1184 c.drawBitmap(srcBM, x, y, &p);
robertphillips7354a4b2015-12-16 05:08:27 -08001185}
reed70ee31b2015-12-10 13:44:45 -08001186
reed129ed1c2016-02-22 06:42:31 -08001187static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, bool isOpaque,
1188 const SkPaint* paint) {
1189 // need to force L32 for now if we have an image filter. Once filters support other colortypes
1190 // e.g. sRGB or F16, we can remove this check
brianosman52ede1d2016-06-20 08:25:02 -07001191 // SRGBTODO: Can we remove this check now?
reed129ed1c2016-02-22 06:42:31 -08001192 const bool hasImageFilter = paint && paint->getImageFilter();
1193
1194 SkAlphaType alphaType = isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
1195 if ((prev.bytesPerPixel() < 4) || hasImageFilter) {
1196 // force to L32
1197 return SkImageInfo::MakeN32(w, h, alphaType);
1198 } else {
1199 // keep the same characteristics as the prev
brianosman52ede1d2016-06-20 08:25:02 -07001200 return SkImageInfo::Make(w, h, prev.colorType(), alphaType, sk_ref_sp(prev.colorSpace()));
reed129ed1c2016-02-22 06:42:31 -08001201 }
1202}
1203
reed4960eee2015-12-18 07:09:18 -08001204void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
1205 const SkRect* bounds = rec.fBounds;
1206 const SkPaint* paint = rec.fPaint;
1207 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1208
reed@google.comb93ba452014-03-10 19:47:58 +00001209#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001210 saveLayerFlags &= ~kDontClipToLayer_PrivateSaveLayerFlag;
reed@google.comb93ba452014-03-10 19:47:58 +00001211#endif
1212
reed8c30a812016-04-20 16:36:51 -07001213 SkLazyPaint lazyP;
1214 SkImageFilter* imageFilter = paint ? paint->getImageFilter() : NULL;
1215 SkMatrix stashedMatrix = fMCRec->fMatrix;
reed8c30a812016-04-20 16:36:51 -07001216 SkMatrix remainder;
1217 SkSize scale;
1218 /*
1219 * ImageFilters (so far) do not correctly handle matrices (CTM) that contain rotation/skew/etc.
1220 * but they do handle scaling. To accommodate this, we do the following:
1221 *
1222 * 1. Stash off the current CTM
1223 * 2. Decompose the CTM into SCALE and REMAINDER
1224 * 3. Wack the CTM to be just SCALE, and wrap the imagefilter with a MatrixImageFilter that
1225 * contains the REMAINDER
1226 * 4. Proceed as usual, allowing the client to draw into the layer (now with a scale-only CTM)
1227 * 5. During restore, we process the MatrixImageFilter, which applies REMAINDER to the output
1228 * of the original imagefilter, and draw that (via drawSprite)
1229 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1230 *
1231 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1232 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1233 */
reed96a04f32016-04-25 09:25:15 -07001234 if (imageFilter && !stashedMatrix.isScaleTranslate() && !imageFilter->canHandleComplexCTM() &&
reed8c30a812016-04-20 16:36:51 -07001235 stashedMatrix.decomposeScale(&scale, &remainder))
1236 {
1237 // We will restore the matrix (which we are overwriting here) in restore via fStashedMatrix
1238 this->internalSetMatrix(SkMatrix::MakeScale(scale.width(), scale.height()));
1239 SkPaint* p = lazyP.set(*paint);
1240 p->setImageFilter(SkImageFilter::MakeMatrixFilter(remainder,
1241 SkFilterQuality::kLow_SkFilterQuality,
1242 sk_ref_sp(imageFilter)));
1243 imageFilter = p->getImageFilter();
1244 paint = p;
1245 }
reed8c30a812016-04-20 16:36:51 -07001246
junov@chromium.orga907ac32012-02-24 21:54:07 +00001247 // do this before we create the layer. We don't call the public save() since
1248 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001249 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001250
1251 fDeviceCMDirty = true;
1252
1253 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001254 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
reed2ff1fce2014-12-11 07:07:37 -08001255 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001256 }
1257
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001258 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1259 // the clipRectBounds() call above?
1260 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001261 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001262 }
1263
reed4960eee2015-12-18 07:09:18 -08001264 bool isOpaque = SkToBool(saveLayerFlags & kIsOpaque_SaveLayerFlag);
reed8dc0ccb2015-03-20 06:32:52 -07001265 SkPixelGeometry geo = fProps.pixelGeometry();
1266 if (paint) {
reed76033be2015-03-14 10:54:31 -07001267 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001268 if (paint->getImageFilter() || paint->getColorFilter()) {
reed76033be2015-03-14 10:54:31 -07001269 isOpaque = false;
reed8dc0ccb2015-03-20 06:32:52 -07001270 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001271 }
1272 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001273
reedb2db8982014-11-13 12:41:02 -08001274 SkBaseDevice* device = this->getTopDevice();
halcanary96fcdcc2015-08-27 07:41:13 -07001275 if (nullptr == device) {
reedb2db8982014-11-13 12:41:02 -08001276 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001277 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001278 }
reedb2db8982014-11-13 12:41:02 -08001279
reed129ed1c2016-02-22 06:42:31 -08001280 SkImageInfo info = make_layer_info(device->imageInfo(), ir.width(), ir.height(), isOpaque,
1281 paint);
1282
reed61f501f2015-04-29 08:34:00 -07001283 bool forceSpriteOnRestore = false;
1284 {
reed70ee31b2015-12-10 13:44:45 -08001285 const bool preserveLCDText = kOpaque_SkAlphaType == info.alphaType() ||
reed4960eee2015-12-18 07:09:18 -08001286 (saveLayerFlags & kPreserveLCDText_SaveLayerFlag);
reeddaa57bf2015-05-15 10:39:17 -07001287 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
reed70ee31b2015-12-10 13:44:45 -08001288 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
1289 preserveLCDText, false);
reed61f501f2015-04-29 08:34:00 -07001290 SkBaseDevice* newDev = device->onCreateDevice(createInfo, paint);
halcanary96fcdcc2015-08-27 07:41:13 -07001291 if (nullptr == newDev) {
reed61f501f2015-04-29 08:34:00 -07001292 // If onCreateDevice didn't succeed, try raster (e.g. PDF couldn't handle the paint)
robertphillips9a53fd72015-06-22 09:46:59 -07001293 const SkSurfaceProps surfaceProps(fProps.flags(), createInfo.fPixelGeometry);
1294 newDev = SkBitmapDevice::Create(createInfo.fInfo, surfaceProps);
halcanary96fcdcc2015-08-27 07:41:13 -07001295 if (nullptr == newDev) {
reed61f501f2015-04-29 08:34:00 -07001296 SkErrorInternals::SetError(kInternalError_SkError,
1297 "Unable to create device for layer.");
1298 return;
1299 }
1300 forceSpriteOnRestore = true;
1301 }
1302 device = newDev;
bungeman@google.come25c6842011-08-17 14:53:54 +00001303 }
reed@google.com6f8f2922011-03-04 22:27:10 +00001304 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips7354a4b2015-12-16 05:08:27 -08001305
reedbfd5f172016-01-07 11:28:08 -08001306 if (rec.fBackdrop) {
1307 draw_filter_into_device(fMCRec->fTopLayer->fDevice, rec.fBackdrop, device, fMCRec->fMatrix);
robertphillips7354a4b2015-12-16 05:08:27 -08001308 }
1309
reed8c30a812016-04-20 16:36:51 -07001310 DeviceCM* layer = new DeviceCM(device, paint, this, fConservativeRasterClip,
1311 forceSpriteOnRestore, stashedMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001312 device->unref();
1313
1314 layer->fNext = fMCRec->fTopLayer;
1315 fMCRec->fLayer = layer;
1316 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reed@android.com8a1c16f2008-12-17 15:59:43 +00001317}
1318
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001319int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001320 if (0xFF == alpha) {
1321 return this->saveLayer(bounds, nullptr);
1322 } else {
1323 SkPaint tmpPaint;
1324 tmpPaint.setAlpha(alpha);
1325 return this->saveLayer(bounds, &tmpPaint);
1326 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001327}
1328
reed@android.com8a1c16f2008-12-17 15:59:43 +00001329void SkCanvas::internalRestore() {
1330 SkASSERT(fMCStack.count() != 0);
1331
1332 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001333 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001334
reed687fa1c2015-04-07 08:00:56 -07001335 fClipStack->restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001336
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001337 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001338 DeviceCM* layer = fMCRec->fLayer; // may be null
1339 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001340 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001341
1342 // now do the normal restore()
1343 fMCRec->~MCRec(); // balanced in save()
1344 fMCStack.pop_back();
1345 fMCRec = (MCRec*)fMCStack.back();
1346
1347 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1348 since if we're being recorded, we don't want to record this (the
1349 recorder will have already recorded the restore).
1350 */
bsalomon49f085d2014-09-05 13:34:00 -07001351 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001352 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001353 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001354 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
reed61f501f2015-04-29 08:34:00 -07001355 layer->fPaint, layer->fDeviceIsBitmapDevice);
reed8c30a812016-04-20 16:36:51 -07001356 // restore what we smashed in internalSaveLayer
1357 fMCRec->fMatrix = layer->fStashedMatrix;
reed@google.com8926b162012-03-23 15:36:36 +00001358 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001359 fDeviceCMDirty = true;
halcanary385fe4d2015-08-26 13:07:48 -07001360 delete layer;
reedb679ca82015-04-07 04:40:48 -07001361 } else {
1362 // we're at the root
reeda499f902015-05-01 09:34:31 -07001363 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001364 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001365 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001366 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001367 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001368}
1369
reede8f30622016-03-23 18:59:25 -07001370sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001371 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001372 props = &fProps;
1373 }
1374 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001375}
1376
reede8f30622016-03-23 18:59:25 -07001377sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001378 SkBaseDevice* dev = this->getDevice();
reede8f30622016-03-23 18:59:25 -07001379 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001380}
1381
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001382SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001383 return this->onImageInfo();
1384}
1385
1386SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001387 SkBaseDevice* dev = this->getDevice();
1388 if (dev) {
1389 return dev->imageInfo();
1390 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001391 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001392 }
1393}
1394
brianosman898235c2016-04-06 07:38:23 -07001395bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001396 return this->onGetProps(props);
1397}
1398
1399bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001400 SkBaseDevice* dev = this->getDevice();
1401 if (dev) {
1402 if (props) {
1403 *props = fProps;
1404 }
1405 return true;
1406 } else {
1407 return false;
1408 }
1409}
1410
reed6ceeebd2016-03-09 14:26:26 -08001411#ifdef SK_SUPPORT_LEGACY_PEEKPIXELS_PARMS
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001412const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
reed884e97c2015-05-26 11:31:54 -07001413 SkPixmap pmap;
reed6ceeebd2016-03-09 14:26:26 -08001414 if (this->peekPixels(&pmap)) {
1415 if (info) {
1416 *info = pmap.info();
1417 }
1418 if (rowBytes) {
1419 *rowBytes = pmap.rowBytes();
1420 }
1421 return pmap.addr();
reed884e97c2015-05-26 11:31:54 -07001422 }
reed6ceeebd2016-03-09 14:26:26 -08001423 return nullptr;
1424}
1425#endif
1426
1427bool SkCanvas::peekPixels(SkPixmap* pmap) {
1428 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001429}
1430
reed884e97c2015-05-26 11:31:54 -07001431bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001432 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001433 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001434}
1435
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001436void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001437 SkPixmap pmap;
1438 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001439 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001440 }
1441 if (info) {
1442 *info = pmap.info();
1443 }
1444 if (rowBytes) {
1445 *rowBytes = pmap.rowBytes();
1446 }
1447 if (origin) {
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001448 *origin = this->getTopDevice(false)->getOrigin();
1449 }
reed884e97c2015-05-26 11:31:54 -07001450 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001451}
1452
reed884e97c2015-05-26 11:31:54 -07001453bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001454 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001455 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001456}
1457
reed@android.com8a1c16f2008-12-17 15:59:43 +00001458/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001459
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001460void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed61f501f2015-04-29 08:34:00 -07001461 const SkPaint* paint, bool deviceIsBitmapDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001462 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001463 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001464 paint = &tmp;
1465 }
reed@google.com4b226022011-01-11 18:32:13 +00001466
reed@google.com8926b162012-03-23 15:36:36 +00001467 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001468 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001469 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001470 paint = &looper.paint();
1471 SkImageFilter* filter = paint->getImageFilter();
1472 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
senorblancof35566e2016-04-18 10:32:02 -07001473 if (filter) {
robertphillips4418dba2016-03-07 12:45:14 -08001474 const SkBitmap& srcBM = srcDev->accessBitmap(false);
reed1eca1162016-04-25 12:29:38 -07001475 dstDev->drawSpriteWithFilter(iter, srcBM, pos.x(), pos.y(), *paint);
reed61f501f2015-04-29 08:34:00 -07001476 } else if (deviceIsBitmapDevice) {
reed9572a102015-05-26 19:22:17 -07001477 const SkBitmap& src = static_cast<SkBitmapDevice*>(srcDev)->fBitmap;
reed61f501f2015-04-29 08:34:00 -07001478 dstDev->drawSprite(iter, src, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001479 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001480 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001481 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001482 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001483 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001484}
1485
reed32704672015-12-16 08:27:10 -08001486/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001487
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001488void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001489 SkMatrix m;
1490 m.setTranslate(dx, dy);
1491 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001492}
1493
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001494void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001495 SkMatrix m;
1496 m.setScale(sx, sy);
1497 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001498}
1499
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001500void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001501 SkMatrix m;
1502 m.setRotate(degrees);
1503 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001504}
1505
bungeman7438bfc2016-07-12 15:01:19 -07001506void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1507 SkMatrix m;
1508 m.setRotate(degrees, px, py);
1509 this->concat(m);
1510}
1511
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001512void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001513 SkMatrix m;
1514 m.setSkew(sx, sy);
1515 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001516}
1517
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001518void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001519 if (matrix.isIdentity()) {
1520 return;
1521 }
1522
reed2ff1fce2014-12-11 07:07:37 -08001523 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001524 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001525 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001526 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001527
1528 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001529}
1530
reed8c30a812016-04-20 16:36:51 -07001531void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001532 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001533 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001534 fMCRec->fMatrix = matrix;
reed8c30a812016-04-20 16:36:51 -07001535}
1536
1537void SkCanvas::setMatrix(const SkMatrix& matrix) {
1538 this->checkForDeferredSave();
1539 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001540 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001541}
1542
reed@android.com8a1c16f2008-12-17 15:59:43 +00001543void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001544 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001545}
1546
vjiaoblack6d3fb892016-07-12 14:50:41 -07001547void SkCanvas::translateZ(SkScalar z) {
1548 this->checkForDeferredSave();
1549 this->fMCRec->fCurDrawDepth += z;
1550 this->didTranslateZ(z);
1551}
1552
1553SkScalar SkCanvas::getZ() const {
1554 return this->fMCRec->fCurDrawDepth;
1555}
1556
reed@android.com8a1c16f2008-12-17 15:59:43 +00001557//////////////////////////////////////////////////////////////////////////////
1558
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001559void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed2d1afab2016-06-29 14:33:11 -07001560 if (!fAllowSoftClip) {
1561 doAA = false;
1562 }
1563
1564#ifdef SK_SUPPORT_PRECHECK_CLIPRECT
1565 // Check if we can quick-accept the clip call (and do nothing)
1566 //
reed74467162016-06-30 08:15:35 -07001567 if (SkRegion::kIntersect_Op == op && !doAA && fMCRec->fMatrix.isScaleTranslate()) {
reed6092b6e2016-07-10 11:45:34 -07001568 SkRect devR;
1569 fMCRec->fMatrix.mapRectScaleTranslate(&devR, rect);
reed2d1afab2016-06-29 14:33:11 -07001570 // NOTE: this check is CTM specific, since we might round differently with a different
1571 // CTM. Thus this is only 100% reliable if there is not global CTM scale to be
1572 // applied later (i.e. if this is going into a picture).
1573 if (devR.round().contains(fMCRec->fRasterClip.getBounds())) {
1574#if 0
1575 SkDebugf("ignored clipRect [%g %g %g %g]\n",
1576 rect.left(), rect.top(), rect.right(), rect.bottom());
1577#endif
1578 return;
1579 }
1580 }
1581#endif
1582
reed2ff1fce2014-12-11 07:07:37 -08001583 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001584 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1585 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001586}
1587
1588void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001589#ifdef SK_ENABLE_CLIP_QUICKREJECT
1590 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001591 if (fMCRec->fRasterClip.isEmpty()) {
reed2d1afab2016-06-29 14:33:11 -07001592 return;
reed@google.comda17f752012-08-16 18:27:05 +00001593 }
1594
reed@google.com3b3e8952012-08-16 20:53:31 +00001595 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001596 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001597 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001598
reed687fa1c2015-04-07 08:00:56 -07001599 fClipStack->clipEmpty();
reed2d1afab2016-06-29 14:33:11 -07001600 (void)fMCRec->fRasterClip.setEmpty();
1601 return;
reed@google.comda17f752012-08-16 18:27:05 +00001602 }
1603 }
1604#endif
1605
reed74467162016-06-30 08:15:35 -07001606 const bool isScaleTrans = fMCRec->fMatrix.isScaleTranslate();
reedc64eff52015-11-21 12:39:45 -08001607 SkRect devR;
reed74467162016-06-30 08:15:35 -07001608 if (isScaleTrans) {
reed6092b6e2016-07-10 11:45:34 -07001609 fMCRec->fMatrix.mapRectScaleTranslate(&devR, rect);
reedc64eff52015-11-21 12:39:45 -08001610 }
bsalomonac8cabd2015-11-20 18:53:07 -08001611
reed2d1afab2016-06-29 14:33:11 -07001612#ifndef SK_SUPPORT_PRECHECK_CLIPRECT
reedc64eff52015-11-21 12:39:45 -08001613 if (SkRegion::kIntersect_Op == op &&
1614 kHard_ClipEdgeStyle == edgeStyle
reed74467162016-06-30 08:15:35 -07001615 && isScaleTrans)
reedc64eff52015-11-21 12:39:45 -08001616 {
1617 if (devR.round().contains(fMCRec->fRasterClip.getBounds())) {
1618#if 0
1619 SkDebugf("------- ignored clipRect [%g %g %g %g]\n",
1620 rect.left(), rect.top(), rect.right(), rect.bottom());
1621#endif
1622 return;
1623 }
1624 }
reed2d1afab2016-06-29 14:33:11 -07001625#endif
reedc64eff52015-11-21 12:39:45 -08001626
1627 AutoValidateClip avc(this);
1628
1629 fDeviceCMDirty = true;
1630 fCachedLocalClipBoundsDirty = true;
1631
reed74467162016-06-30 08:15:35 -07001632 if (isScaleTrans) {
reedc64eff52015-11-21 12:39:45 -08001633 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1634 fClipStack->clipDevRect(devR, op, isAA);
senorblancoafc7cce2016-02-02 18:44:15 -08001635 fMCRec->fRasterClip.op(devR, this->getTopLayerBounds(), op, isAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001636 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001637 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001638 // and clip against that, since it can handle any matrix. However, to
1639 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1640 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001641 SkPath path;
1642
1643 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001644 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001645 }
1646}
1647
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001648void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001649 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001650 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001651 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001652 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1653 } else {
1654 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001655 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001656}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001657
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001658void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001659 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001660 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001661 AutoValidateClip avc(this);
1662
1663 fDeviceCMDirty = true;
1664 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001665 if (!fAllowSoftClip) {
1666 edgeStyle = kHard_ClipEdgeStyle;
1667 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001668
reed687fa1c2015-04-07 08:00:56 -07001669 fClipStack->clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001670
senorblancoafc7cce2016-02-02 18:44:15 -08001671 fMCRec->fRasterClip.op(transformedRRect, this->getTopLayerBounds(), op,
robertphillips125f19a2015-11-23 09:00:05 -08001672 kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001673 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001674 }
1675
1676 SkPath path;
1677 path.addRRect(rrect);
1678 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001679 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001680}
1681
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001682void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001683 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001684 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001685
1686 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1687 SkRect r;
1688 if (path.isRect(&r)) {
1689 this->onClipRect(r, op, edgeStyle);
1690 return;
1691 }
1692 SkRRect rrect;
1693 if (path.isOval(&r)) {
1694 rrect.setOval(r);
1695 this->onClipRRect(rrect, op, edgeStyle);
1696 return;
1697 }
1698 if (path.isRRect(&rrect)) {
1699 this->onClipRRect(rrect, op, edgeStyle);
1700 return;
1701 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001702 }
robertphillips39f05382015-11-24 09:30:12 -08001703
1704 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001705}
1706
1707void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001708#ifdef SK_ENABLE_CLIP_QUICKREJECT
1709 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001710 if (fMCRec->fRasterClip.isEmpty()) {
reed2d1afab2016-06-29 14:33:11 -07001711 return;
reed@google.comda17f752012-08-16 18:27:05 +00001712 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001713
reed@google.com3b3e8952012-08-16 20:53:31 +00001714 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001715 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001716 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001717
reed687fa1c2015-04-07 08:00:56 -07001718 fClipStack->clipEmpty();
reed2d1afab2016-06-29 14:33:11 -07001719 (void)fMCRec->fRasterClip.setEmpty();
1720 return;
reed@google.comda17f752012-08-16 18:27:05 +00001721 }
1722 }
1723#endif
1724
reed@google.com5c3d1472011-02-22 19:12:23 +00001725 AutoValidateClip avc(this);
1726
reed@android.com8a1c16f2008-12-17 15:59:43 +00001727 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001728 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001729 if (!fAllowSoftClip) {
1730 edgeStyle = kHard_ClipEdgeStyle;
1731 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001732
1733 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001734 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001735
reed@google.comfe701122011-11-08 19:41:23 +00001736 // Check if the transfomation, or the original path itself
1737 // made us empty. Note this can also happen if we contained NaN
1738 // values. computing the bounds detects this, and will set our
1739 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1740 if (devPath.getBounds().isEmpty()) {
1741 // resetting the path will remove any NaN or other wanky values
1742 // that might upset our scan converter.
1743 devPath.reset();
1744 }
1745
reed@google.com5c3d1472011-02-22 19:12:23 +00001746 // if we called path.swap() we could avoid a deep copy of this path
reed687fa1c2015-04-07 08:00:56 -07001747 fClipStack->clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001748
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001749 if (fAllowSimplifyClip) {
fmalita1a481fe2015-02-04 07:39:34 -08001750 bool clipIsAA = getClipStack()->asPath(&devPath);
1751 if (clipIsAA) {
1752 edgeStyle = kSoft_ClipEdgeStyle;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001753 }
fmalita1a481fe2015-02-04 07:39:34 -08001754
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001755 op = SkRegion::kReplace_Op;
1756 }
1757
senorblancoafc7cce2016-02-02 18:44:15 -08001758 fMCRec->fRasterClip.op(devPath, this->getTopLayerBounds(), op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001759}
1760
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001761void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed2ff1fce2014-12-11 07:07:37 -08001762 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001763 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001764}
1765
1766void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001767 AutoValidateClip avc(this);
1768
reed@android.com8a1c16f2008-12-17 15:59:43 +00001769 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001770 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001771
reed@google.com5c3d1472011-02-22 19:12:23 +00001772 // todo: signal fClipStack that we have a region, and therefore (I guess)
1773 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001774 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001775
reed1f836ee2014-07-07 07:49:34 -07001776 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001777}
1778
reed@google.com819c9212011-02-23 18:56:55 +00001779#ifdef SK_DEBUG
1780void SkCanvas::validateClip() const {
1781 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001782 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001783 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001784 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001785 return;
1786 }
1787
reed@google.com819c9212011-02-23 18:56:55 +00001788 SkIRect ir;
1789 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001790 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001791
reed687fa1c2015-04-07 08:00:56 -07001792 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001793 const SkClipStack::Element* element;
halcanary96fcdcc2015-08-27 07:41:13 -07001794 while ((element = iter.next()) != nullptr) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001795 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001796 case SkClipStack::Element::kRect_Type:
1797 element->getRect().round(&ir);
1798 tmpClip.op(ir, element->getOp());
1799 break;
1800 case SkClipStack::Element::kEmpty_Type:
1801 tmpClip.setEmpty();
1802 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001803 default: {
1804 SkPath path;
1805 element->asPath(&path);
senorblancoafc7cce2016-02-02 18:44:15 -08001806 tmpClip.op(path, this->getTopLayerBounds(), element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001807 break;
1808 }
reed@google.com819c9212011-02-23 18:56:55 +00001809 }
1810 }
reed@google.com819c9212011-02-23 18:56:55 +00001811}
1812#endif
1813
reed@google.com90c07ea2012-04-13 13:50:27 +00001814void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001815 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001816 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001817
halcanary96fcdcc2015-08-27 07:41:13 -07001818 while ((element = iter.next()) != nullptr) {
fmalitac3b589a2014-06-05 12:40:07 -07001819 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001820 }
1821}
1822
reed@google.com5c3d1472011-02-22 19:12:23 +00001823///////////////////////////////////////////////////////////////////////////////
1824
reed@google.com754de5f2014-02-24 19:38:20 +00001825bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001826 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001827}
1828
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001829bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001830 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001831}
1832
reed@google.com3b3e8952012-08-16 20:53:31 +00001833bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001834 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001835 return true;
1836
reed1f836ee2014-07-07 07:49:34 -07001837 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001838 return true;
1839 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001840
reed1f836ee2014-07-07 07:49:34 -07001841 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001842 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001843 fMCRec->fMatrix.mapRect(&dst, rect);
reedb07a94f2014-11-19 05:03:18 -08001844 return !SkIRect::Intersects(dst.roundOut(), fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001845 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001846 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001847
reed@android.coma380ae42009-07-21 01:17:02 +00001848 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001849 // TODO: should we use | instead, or compare all 4 at once?
1850 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001851 return true;
1852 }
reed@google.comc0784db2013-12-13 21:16:12 +00001853 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001854 return true;
1855 }
1856 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001857 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001858}
1859
reed@google.com3b3e8952012-08-16 20:53:31 +00001860bool SkCanvas::quickReject(const SkPath& path) const {
1861 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001862}
1863
reed@google.com3b3e8952012-08-16 20:53:31 +00001864bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001865 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001866 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001867 return false;
1868 }
1869
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001870 SkMatrix inverse;
1871 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001872 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001873 if (bounds) {
1874 bounds->setEmpty();
1875 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001876 return false;
1877 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001878
bsalomon49f085d2014-09-05 13:34:00 -07001879 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001880 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001881 // adjust it outwards in case we are antialiasing
1882 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001883
reed@google.com8f4d2302013-12-17 16:44:46 +00001884 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1885 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001886 inverse.mapRect(bounds, r);
1887 }
1888 return true;
1889}
1890
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001891bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001892 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001893 if (clip.isEmpty()) {
1894 if (bounds) {
1895 bounds->setEmpty();
1896 }
1897 return false;
1898 }
1899
bsalomon49f085d2014-09-05 13:34:00 -07001900 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001901 *bounds = clip.getBounds();
1902 }
1903 return true;
1904}
1905
reed@android.com8a1c16f2008-12-17 15:59:43 +00001906const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001907 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001908}
1909
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001910const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001911 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001912}
1913
robertphillips175dd9b2016-04-28 14:32:04 -07001914GrDrawContext* SkCanvas::internal_private_accessTopLayerDrawContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001915 SkBaseDevice* dev = this->getTopDevice();
robertphillips175dd9b2016-04-28 14:32:04 -07001916 return dev ? dev->accessDrawContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001917}
1918
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001919GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001920 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001921 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001922}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001923
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001924void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1925 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001926 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001927 if (outer.isEmpty()) {
1928 return;
1929 }
1930 if (inner.isEmpty()) {
1931 this->drawRRect(outer, paint);
1932 return;
1933 }
1934
1935 // We don't have this method (yet), but technically this is what we should
1936 // be able to assert...
1937 // SkASSERT(outer.contains(inner));
1938 //
1939 // For now at least check for containment of bounds
1940 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1941
1942 this->onDrawDRRect(outer, inner, paint);
1943}
1944
reed41af9662015-01-05 07:49:08 -08001945// These need to stop being virtual -- clients need to override the onDraw... versions
1946
1947void SkCanvas::drawPaint(const SkPaint& paint) {
1948 this->onDrawPaint(paint);
1949}
1950
1951void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1952 this->onDrawRect(r, paint);
1953}
1954
1955void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1956 this->onDrawOval(r, paint);
1957}
1958
1959void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1960 this->onDrawRRect(rrect, paint);
1961}
1962
1963void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1964 this->onDrawPoints(mode, count, pts, paint);
1965}
1966
1967void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
1968 const SkPoint texs[], const SkColor colors[], SkXfermode* xmode,
1969 const uint16_t indices[], int indexCount, const SkPaint& paint) {
1970 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
1971 indices, indexCount, paint);
1972}
1973
1974void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1975 this->onDrawPath(path, paint);
1976}
1977
reeda85d4d02015-05-06 12:56:48 -07001978void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001979 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001980 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001981}
1982
reede47829b2015-08-06 10:02:53 -07001983void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1984 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001985 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001986 if (dst.isEmpty() || src.isEmpty()) {
1987 return;
1988 }
1989 this->onDrawImageRect(image, &src, dst, paint, constraint);
1990}
reed41af9662015-01-05 07:49:08 -08001991
reed84984ef2015-07-17 07:09:43 -07001992void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1993 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001994 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001995 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001996}
1997
reede47829b2015-08-06 10:02:53 -07001998void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
1999 SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08002000 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002001 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
2002 constraint);
2003}
reede47829b2015-08-06 10:02:53 -07002004
reed4c21dc52015-06-25 12:32:03 -07002005void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2006 const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002007 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07002008 if (dst.isEmpty()) {
2009 return;
2010 }
2011 if (!SkNinePatchIter::Valid(image->width(), image->height(), center)) {
reede47829b2015-08-06 10:02:53 -07002012 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002013 }
2014 this->onDrawImageNine(image, center, dst, paint);
2015}
2016
reed41af9662015-01-05 07:49:08 -08002017void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07002018 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002019 return;
2020 }
reed41af9662015-01-05 07:49:08 -08002021 this->onDrawBitmap(bitmap, dx, dy, paint);
2022}
2023
reede47829b2015-08-06 10:02:53 -07002024void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07002025 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002026 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07002027 return;
2028 }
reede47829b2015-08-06 10:02:53 -07002029 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08002030}
2031
reed84984ef2015-07-17 07:09:43 -07002032void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2033 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002034 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002035}
2036
reede47829b2015-08-06 10:02:53 -07002037void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2038 SrcRectConstraint constraint) {
2039 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2040 constraint);
2041}
reede47829b2015-08-06 10:02:53 -07002042
reed41af9662015-01-05 07:49:08 -08002043void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2044 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07002045 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002046 return;
2047 }
reed4c21dc52015-06-25 12:32:03 -07002048 if (!SkNinePatchIter::Valid(bitmap.width(), bitmap.height(), center)) {
reeda5517e22015-07-14 10:54:12 -07002049 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002050 }
reed41af9662015-01-05 07:49:08 -08002051 this->onDrawBitmapNine(bitmap, center, dst, paint);
2052}
2053
reed71c3c762015-06-24 10:29:17 -07002054void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2055 const SkColor colors[], int count, SkXfermode::Mode mode,
2056 const SkRect* cull, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002057 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002058 if (count <= 0) {
2059 return;
2060 }
2061 SkASSERT(atlas);
2062 SkASSERT(xform);
2063 SkASSERT(tex);
2064 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
2065}
2066
reedf70b5312016-03-04 16:36:20 -08002067void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2068 if (key) {
2069 this->onDrawAnnotation(rect, key, value);
2070 }
2071}
2072
reede47829b2015-08-06 10:02:53 -07002073void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2074 const SkPaint* paint, SrcRectConstraint constraint) {
2075 if (src) {
2076 this->drawImageRect(image, *src, dst, paint, constraint);
2077 } else {
2078 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2079 dst, paint, constraint);
2080 }
2081}
2082void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2083 const SkPaint* paint, SrcRectConstraint constraint) {
2084 if (src) {
2085 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2086 } else {
2087 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2088 dst, paint, constraint);
2089 }
2090}
2091
tomhudsoncb3bd182016-05-18 07:24:16 -07002092void SkCanvas::temporary_internal_describeTopLayer(SkMatrix* matrix, SkIRect* clip_bounds) {
2093 SkIRect layer_bounds = this->getTopLayerBounds();
2094 if (matrix) {
2095 *matrix = this->getTotalMatrix();
2096 matrix->preTranslate(-layer_bounds.left(), -layer_bounds.top());
2097 }
2098 if (clip_bounds) {
2099 this->getClipDeviceBounds(clip_bounds);
2100 clip_bounds->offset(-layer_bounds.left(), -layer_bounds.top());
2101 }
2102}
2103
reed@android.com8a1c16f2008-12-17 15:59:43 +00002104//////////////////////////////////////////////////////////////////////////////
2105// These are the virtual drawing methods
2106//////////////////////////////////////////////////////////////////////////////
2107
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002108void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002109 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002110 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2111 }
2112}
2113
reed41af9662015-01-05 07:49:08 -08002114void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002115 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002116 this->internalDrawPaint(paint);
2117}
2118
2119void SkCanvas::internalDrawPaint(const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002120 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002121
2122 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002123 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002124 }
2125
reed@google.com4e2b3d32011-04-07 14:18:59 +00002126 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002127}
2128
reed41af9662015-01-05 07:49:08 -08002129void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2130 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002131 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002132 if ((long)count <= 0) {
2133 return;
2134 }
2135
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002136 SkRect r, storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002137 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002138 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002139 // special-case 2 points (common for drawing a single line)
2140 if (2 == count) {
2141 r.set(pts[0], pts[1]);
2142 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00002143 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002144 }
senorblanco87e066e2015-10-28 11:23:36 -07002145 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2146 return;
2147 }
2148 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002149 }
reed@google.coma584aed2012-05-16 14:06:02 +00002150
halcanary96fcdcc2015-08-27 07:41:13 -07002151 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002152
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002153 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002154
reed@android.com8a1c16f2008-12-17 15:59:43 +00002155 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002156 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002157 }
reed@google.com4b226022011-01-11 18:32:13 +00002158
reed@google.com4e2b3d32011-04-07 14:18:59 +00002159 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002160}
2161
reed41af9662015-01-05 07:49:08 -08002162void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002163 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002164 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002165 const SkRect* bounds = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002166 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08002167 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
2168 // To prevent accidental rejecting at this stage, we have to sort it before we check.
2169 SkRect tmp(r);
2170 tmp.sort();
2171
senorblanco87e066e2015-10-28 11:23:36 -07002172 if (this->quickReject(paint.computeFastBounds(tmp, &storage))) {
2173 return;
2174 }
2175 bounds = &r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002176 }
reed@google.com4b226022011-01-11 18:32:13 +00002177
reedc83a2972015-07-16 07:40:45 -07002178 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002179
2180 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002181 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002182 }
2183
reed@google.com4e2b3d32011-04-07 14:18:59 +00002184 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002185}
2186
reed41af9662015-01-05 07:49:08 -08002187void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002188 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002189 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002190 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002191 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002192 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2193 return;
2194 }
2195 bounds = &oval;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002196 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002197
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002198 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002199
2200 while (iter.next()) {
2201 iter.fDevice->drawOval(iter, oval, looper.paint());
2202 }
2203
2204 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002205}
2206
reed41af9662015-01-05 07:49:08 -08002207void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002208 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002209 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002210 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002211 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002212 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2213 return;
2214 }
2215 bounds = &rrect.getBounds();
reed@google.com4ed0fb72012-12-12 20:48:18 +00002216 }
2217
2218 if (rrect.isRect()) {
2219 // call the non-virtual version
2220 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002221 return;
2222 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002223 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002224 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2225 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002226 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002227
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002228 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002229
2230 while (iter.next()) {
2231 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2232 }
2233
2234 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002235}
2236
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002237void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2238 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002239 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002240 const SkRect* bounds = nullptr;
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002241 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002242 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2243 return;
2244 }
2245 bounds = &outer.getBounds();
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002246 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002247
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002248 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002249
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002250 while (iter.next()) {
2251 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2252 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002253
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002254 LOOPER_END
2255}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002256
reed41af9662015-01-05 07:49:08 -08002257void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002258 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002259 if (!path.isFinite()) {
2260 return;
2261 }
2262
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002263 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002264 const SkRect* bounds = nullptr;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002265 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002266 const SkRect& pathBounds = path.getBounds();
senorblanco87e066e2015-10-28 11:23:36 -07002267 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2268 return;
2269 }
2270 bounds = &pathBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002271 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002272
2273 const SkRect& r = path.getBounds();
2274 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002275 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002276 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002277 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002278 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002279 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002280
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002281 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002282
2283 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002284 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002285 }
2286
reed@google.com4e2b3d32011-04-07 14:18:59 +00002287 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002288}
2289
reed262a71b2015-12-05 13:07:27 -08002290bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002291 if (!paint.getImageFilter()) {
2292 return false;
2293 }
2294
2295 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002296 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002297 return false;
2298 }
2299
2300 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2301 // Once we can filter and the filter will return a result larger than itself, we should be
2302 // able to remove this constraint.
2303 // skbug.com/4526
2304 //
2305 SkPoint pt;
2306 ctm.mapXY(x, y, &pt);
2307 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2308 return ir.contains(fMCRec->fRasterClip.getBounds());
2309}
2310
reeda85d4d02015-05-06 12:56:48 -07002311void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002312 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002313 SkRect bounds = SkRect::MakeXYWH(x, y,
2314 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002315 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002316 SkRect tmp = bounds;
2317 if (paint) {
2318 paint->computeFastBounds(tmp, &tmp);
2319 }
2320 if (this->quickReject(tmp)) {
2321 return;
2322 }
reeda85d4d02015-05-06 12:56:48 -07002323 }
halcanary9d524f22016-03-29 09:03:52 -07002324
reeda85d4d02015-05-06 12:56:48 -07002325 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002326 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002327 paint = lazy.init();
2328 }
reed262a71b2015-12-05 13:07:27 -08002329
reed129ed1c2016-02-22 06:42:31 -08002330 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2331 *paint);
2332 if (drawAsSprite && paint->getImageFilter()) {
2333 SkBitmap bitmap;
2334 if (!as_IB(image)->asBitmapForImageFilters(&bitmap)) {
2335 drawAsSprite = false;
2336 } else{
2337 // Until imagefilters are updated, they cannot handle any src type but N32...
reeddabe5d32016-06-21 10:28:14 -07002338 if (bitmap.info().colorType() != kN32_SkColorType || bitmap.info().gammaCloseToSRGB()) {
reed129ed1c2016-02-22 06:42:31 -08002339 drawAsSprite = false;
2340 }
2341 }
2342 }
2343
reed262a71b2015-12-05 13:07:27 -08002344 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
2345
reeda85d4d02015-05-06 12:56:48 -07002346 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002347 const SkPaint& pnt = looper.paint();
2348 if (drawAsSprite && pnt.getImageFilter()) {
2349 SkBitmap bitmap;
2350 if (as_IB(image)->asBitmapForImageFilters(&bitmap)) {
2351 SkPoint pt;
2352 iter.fMatrix->mapXY(x, y, &pt);
robertphillips2302de92016-03-24 07:26:32 -07002353 iter.fDevice->drawSpriteWithFilter(iter, bitmap,
2354 SkScalarRoundToInt(pt.fX),
2355 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002356 }
2357 } else {
2358 iter.fDevice->drawImage(iter, image, x, y, pnt);
2359 }
reeda85d4d02015-05-06 12:56:48 -07002360 }
halcanary9d524f22016-03-29 09:03:52 -07002361
reeda85d4d02015-05-06 12:56:48 -07002362 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002363}
2364
reed41af9662015-01-05 07:49:08 -08002365void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002366 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002367 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
halcanary96fcdcc2015-08-27 07:41:13 -07002368 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002369 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002370 if (paint) {
2371 paint->computeFastBounds(dst, &storage);
2372 }
2373 if (this->quickReject(storage)) {
2374 return;
2375 }
reeda85d4d02015-05-06 12:56:48 -07002376 }
2377 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002378 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002379 paint = lazy.init();
2380 }
halcanary9d524f22016-03-29 09:03:52 -07002381
senorblancoc41e7e12015-12-07 12:51:30 -08002382 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002383 image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002384
reeda85d4d02015-05-06 12:56:48 -07002385 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002386 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002387 }
halcanary9d524f22016-03-29 09:03:52 -07002388
reeda85d4d02015-05-06 12:56:48 -07002389 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002390}
2391
reed41af9662015-01-05 07:49:08 -08002392void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002393 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002394 SkDEBUGCODE(bitmap.validate();)
2395
reed33366972015-10-08 09:22:02 -07002396 if (bitmap.drawsNothing()) {
2397 return;
2398 }
2399
2400 SkLazyPaint lazy;
2401 if (nullptr == paint) {
2402 paint = lazy.init();
2403 }
2404
2405 const SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2406
2407 SkRect storage;
2408 const SkRect* bounds = nullptr;
2409 if (paint->canComputeFastBounds()) {
2410 bitmap.getBounds(&storage);
2411 matrix.mapRect(&storage);
senorblanco87e066e2015-10-28 11:23:36 -07002412 SkRect tmp = storage;
2413 if (this->quickReject(paint->computeFastBounds(tmp, &tmp))) {
2414 return;
2415 }
2416 bounds = &storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002417 }
reed@google.com4b226022011-01-11 18:32:13 +00002418
reed129ed1c2016-02-22 06:42:31 -08002419 bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(), bitmap.height(),
2420 *paint);
2421 if (drawAsSprite && paint->getImageFilter()) {
2422 // Until imagefilters are updated, they cannot handle any src type but N32...
reeddabe5d32016-06-21 10:28:14 -07002423 if (bitmap.info().colorType() != kN32_SkColorType || bitmap.info().gammaCloseToSRGB()) {
reed129ed1c2016-02-22 06:42:31 -08002424 drawAsSprite = false;
2425 }
2426 }
2427
reed262a71b2015-12-05 13:07:27 -08002428 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds)
reed33366972015-10-08 09:22:02 -07002429
2430 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002431 const SkPaint& pnt = looper.paint();
2432 if (drawAsSprite && pnt.getImageFilter()) {
2433 SkPoint pt;
2434 iter.fMatrix->mapXY(x, y, &pt);
robertphillips2302de92016-03-24 07:26:32 -07002435 iter.fDevice->drawSpriteWithFilter(iter, bitmap,
2436 SkScalarRoundToInt(pt.fX),
2437 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002438 } else {
2439 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
2440 }
reed33366972015-10-08 09:22:02 -07002441 }
reed262a71b2015-12-05 13:07:27 -08002442
reed33366972015-10-08 09:22:02 -07002443 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002444}
2445
reed@google.com9987ec32011-09-07 11:57:52 +00002446// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002447void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002448 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002449 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002450 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002451 return;
2452 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002453
halcanary96fcdcc2015-08-27 07:41:13 -07002454 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002455 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002456 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2457 return;
2458 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002459 }
reed@google.com3d608122011-11-21 15:16:16 +00002460
reed@google.com33535f32012-09-25 15:37:50 +00002461 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002462 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002463 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002464 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002465
senorblancoc41e7e12015-12-07 12:51:30 -08002466 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002467 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002468
reed@google.com33535f32012-09-25 15:37:50 +00002469 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002470 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002471 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002472
reed@google.com33535f32012-09-25 15:37:50 +00002473 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002474}
2475
reed41af9662015-01-05 07:49:08 -08002476void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002477 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002478 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002479 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002480 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002481}
2482
reed4c21dc52015-06-25 12:32:03 -07002483void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2484 const SkPaint* paint) {
2485 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
halcanary9d524f22016-03-29 09:03:52 -07002486
halcanary96fcdcc2015-08-27 07:41:13 -07002487 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002488 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002489 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2490 return;
2491 }
reed@google.com3d608122011-11-21 15:16:16 +00002492 }
halcanary9d524f22016-03-29 09:03:52 -07002493
reed4c21dc52015-06-25 12:32:03 -07002494 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002495 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002496 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002497 }
halcanary9d524f22016-03-29 09:03:52 -07002498
senorblancoc41e7e12015-12-07 12:51:30 -08002499 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002500
reed4c21dc52015-06-25 12:32:03 -07002501 while (iter.next()) {
2502 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002503 }
halcanary9d524f22016-03-29 09:03:52 -07002504
reed4c21dc52015-06-25 12:32:03 -07002505 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002506}
2507
reed41af9662015-01-05 07:49:08 -08002508void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2509 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002510 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002511 SkDEBUGCODE(bitmap.validate();)
2512
halcanary96fcdcc2015-08-27 07:41:13 -07002513 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002514 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002515 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2516 return;
2517 }
reed4c21dc52015-06-25 12:32:03 -07002518 }
halcanary9d524f22016-03-29 09:03:52 -07002519
reed4c21dc52015-06-25 12:32:03 -07002520 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002521 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002522 paint = lazy.init();
2523 }
halcanary9d524f22016-03-29 09:03:52 -07002524
senorblancoc41e7e12015-12-07 12:51:30 -08002525 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002526
reed4c21dc52015-06-25 12:32:03 -07002527 while (iter.next()) {
2528 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2529 }
halcanary9d524f22016-03-29 09:03:52 -07002530
reed4c21dc52015-06-25 12:32:03 -07002531 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002532}
2533
reed@google.comf67e4cf2011-03-15 20:56:58 +00002534class SkDeviceFilteredPaint {
2535public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002536 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002537 uint32_t filteredFlags = device->filterTextFlags(paint);
2538 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002539 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002540 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002541 fPaint = newPaint;
2542 } else {
2543 fPaint = &paint;
2544 }
2545 }
2546
reed@google.comf67e4cf2011-03-15 20:56:58 +00002547 const SkPaint& paint() const { return *fPaint; }
2548
2549private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002550 const SkPaint* fPaint;
2551 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002552};
2553
bungeman@google.com52c748b2011-08-22 21:30:43 +00002554void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2555 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002556 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002557 draw.fDevice->drawRect(draw, r, paint);
2558 } else {
2559 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002560 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002561 draw.fDevice->drawRect(draw, r, p);
2562 }
2563}
2564
2565void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2566 const char text[], size_t byteLength,
2567 SkScalar x, SkScalar y) {
halcanary96fcdcc2015-08-27 07:41:13 -07002568 SkASSERT(byteLength == 0 || text != nullptr);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002569
2570 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07002571 if (text == nullptr || byteLength == 0 ||
reed1e7f5e72016-04-27 07:49:17 -07002572 draw.fRC->isEmpty() ||
halcanary96fcdcc2015-08-27 07:41:13 -07002573 (paint.getAlpha() == 0 && paint.getXfermode() == nullptr)) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002574 return;
2575 }
2576
2577 SkScalar width = 0;
2578 SkPoint start;
2579
2580 start.set(0, 0); // to avoid warning
2581 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2582 SkPaint::kStrikeThruText_Flag)) {
2583 width = paint.measureText(text, byteLength);
2584
2585 SkScalar offsetX = 0;
2586 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2587 offsetX = SkScalarHalf(width);
2588 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2589 offsetX = width;
2590 }
2591 start.set(x - offsetX, y);
2592 }
2593
2594 if (0 == width) {
2595 return;
2596 }
2597
2598 uint32_t flags = paint.getFlags();
2599
2600 if (flags & (SkPaint::kUnderlineText_Flag |
2601 SkPaint::kStrikeThruText_Flag)) {
2602 SkScalar textSize = paint.getTextSize();
2603 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2604 SkRect r;
2605
2606 r.fLeft = start.fX;
2607 r.fRight = start.fX + width;
2608
2609 if (flags & SkPaint::kUnderlineText_Flag) {
2610 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2611 start.fY);
2612 r.fTop = offset;
2613 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002614 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002615 }
2616 if (flags & SkPaint::kStrikeThruText_Flag) {
2617 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2618 start.fY);
2619 r.fTop = offset;
2620 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002621 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002622 }
2623 }
2624}
2625
reed@google.come0d9ce82014-04-23 04:00:17 +00002626void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2627 const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002628 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002629
2630 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002631 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002632 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002633 DrawTextDecorations(iter, dfp.paint(),
2634 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002635 }
2636
reed@google.com4e2b3d32011-04-07 14:18:59 +00002637 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002638}
2639
reed@google.come0d9ce82014-04-23 04:00:17 +00002640void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2641 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002642 SkPoint textOffset = SkPoint::Make(0, 0);
2643
halcanary96fcdcc2015-08-27 07:41:13 -07002644 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002645
reed@android.com8a1c16f2008-12-17 15:59:43 +00002646 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002647 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002648 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002649 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002650 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002651
reed@google.com4e2b3d32011-04-07 14:18:59 +00002652 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002653}
2654
reed@google.come0d9ce82014-04-23 04:00:17 +00002655void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2656 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002657
2658 SkPoint textOffset = SkPoint::Make(0, constY);
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, xpos, 1, 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::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2672 const SkMatrix* matrix, const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002673 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002674
reed@android.com8a1c16f2008-12-17 15:59:43 +00002675 while (iter.next()) {
2676 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002677 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002678 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002679
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002680 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002681}
2682
reed45561a02016-07-07 12:47:17 -07002683void SkCanvas::onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2684 const SkRect* cullRect, const SkPaint& paint) {
2685 if (cullRect && this->quickReject(*cullRect)) {
2686 return;
2687 }
2688
2689 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
2690
2691 while (iter.next()) {
2692 iter.fDevice->drawTextRSXform(iter, text, byteLength, xform, looper.paint());
2693 }
2694
2695 LOOPER_END
2696}
2697
fmalita00d5c2c2014-08-21 08:53:26 -07002698void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2699 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002700
fmalita85d5eb92015-03-04 11:20:12 -08002701 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002702 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002703 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002704 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002705 SkRect tmp;
2706 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2707 return;
2708 }
2709 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002710 }
2711
fmalita024f9962015-03-03 19:08:17 -08002712 // We cannot filter in the looper as we normally do, because the paint is
2713 // incomplete at this point (text-related attributes are embedded within blob run paints).
2714 SkDrawFilter* drawFilter = fMCRec->fFilter;
halcanary96fcdcc2015-08-27 07:41:13 -07002715 fMCRec->fFilter = nullptr;
fmalita024f9962015-03-03 19:08:17 -08002716
fmalita85d5eb92015-03-04 11:20:12 -08002717 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002718
fmalitaaa1b9122014-08-28 14:32:24 -07002719 while (iter.next()) {
2720 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002721 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002722 }
2723
fmalitaaa1b9122014-08-28 14:32:24 -07002724 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002725
2726 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002727}
2728
reed@google.come0d9ce82014-04-23 04:00:17 +00002729// These will become non-virtual, so they always call the (virtual) onDraw... method
2730void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2731 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002732 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002733 this->onDrawText(text, byteLength, x, y, paint);
2734}
2735void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2736 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002737 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002738 this->onDrawPosText(text, byteLength, pos, paint);
2739}
2740void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2741 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002742 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002743 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2744}
2745void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2746 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002747 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002748 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2749}
reed45561a02016-07-07 12:47:17 -07002750void SkCanvas::drawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2751 const SkRect* cullRect, const SkPaint& paint) {
2752 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextRSXform()");
2753 if (byteLength) {
2754 this->onDrawTextRSXform(text, byteLength, xform, cullRect, paint);
2755 }
2756}
fmalita00d5c2c2014-08-21 08:53:26 -07002757void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2758 const SkPaint& paint) {
reede3b38ce2016-01-08 09:18:44 -08002759 RETURN_ON_NULL(blob);
danakj9881d632014-11-26 12:41:06 -08002760 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
reede3b38ce2016-01-08 09:18:44 -08002761 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002762}
reed@google.come0d9ce82014-04-23 04:00:17 +00002763
reed41af9662015-01-05 07:49:08 -08002764void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2765 const SkPoint verts[], const SkPoint texs[],
2766 const SkColor colors[], SkXfermode* xmode,
2767 const uint16_t indices[], int indexCount,
2768 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002769 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
halcanary96fcdcc2015-08-27 07:41:13 -07002770 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
reed@google.com4b226022011-01-11 18:32:13 +00002771
reed@android.com8a1c16f2008-12-17 15:59:43 +00002772 while (iter.next()) {
2773 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002774 colors, xmode, indices, indexCount,
2775 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002776 }
reed@google.com4b226022011-01-11 18:32:13 +00002777
reed@google.com4e2b3d32011-04-07 14:18:59 +00002778 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002779}
2780
dandovb3c9d1c2014-08-12 08:34:29 -07002781void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2782 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002783 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
halcanary96fcdcc2015-08-27 07:41:13 -07002784 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002785 return;
2786 }
mtklein6cfa73a2014-08-13 13:33:49 -07002787
dandovecfff212014-08-04 10:02:00 -07002788 // Since a patch is always within the convex hull of the control points, we discard it when its
2789 // bounding rectangle is completely outside the current clip.
2790 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002791 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002792 if (this->quickReject(bounds)) {
2793 return;
2794 }
mtklein6cfa73a2014-08-13 13:33:49 -07002795
dandovb3c9d1c2014-08-12 08:34:29 -07002796 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2797}
2798
2799void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2800 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2801
halcanary96fcdcc2015-08-27 07:41:13 -07002802 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002803
dandovecfff212014-08-04 10:02:00 -07002804 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002805 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002806 }
mtklein6cfa73a2014-08-13 13:33:49 -07002807
dandovecfff212014-08-04 10:02:00 -07002808 LOOPER_END
2809}
2810
reeda8db7282015-07-07 10:22:31 -07002811void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
reede3b38ce2016-01-08 09:18:44 -08002812 RETURN_ON_NULL(dr);
2813 if (x || y) {
2814 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2815 this->onDrawDrawable(dr, &matrix);
2816 } else {
2817 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002818 }
2819}
2820
reeda8db7282015-07-07 10:22:31 -07002821void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reede3b38ce2016-01-08 09:18:44 -08002822 RETURN_ON_NULL(dr);
2823 if (matrix && matrix->isIdentity()) {
2824 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002825 }
reede3b38ce2016-01-08 09:18:44 -08002826 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002827}
2828
2829void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2830 SkRect bounds = dr->getBounds();
2831 if (matrix) {
2832 matrix->mapRect(&bounds);
2833 }
2834 if (this->quickReject(bounds)) {
2835 return;
2836 }
2837 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002838}
2839
reed71c3c762015-06-24 10:29:17 -07002840void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2841 const SkColor colors[], int count, SkXfermode::Mode mode,
2842 const SkRect* cull, const SkPaint* paint) {
2843 if (cull && this->quickReject(*cull)) {
2844 return;
2845 }
2846
2847 SkPaint pnt;
2848 if (paint) {
2849 pnt = *paint;
2850 }
halcanary9d524f22016-03-29 09:03:52 -07002851
halcanary96fcdcc2015-08-27 07:41:13 -07002852 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr)
reed71c3c762015-06-24 10:29:17 -07002853 while (iter.next()) {
2854 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, mode, pnt);
2855 }
2856 LOOPER_END
2857}
2858
reedf70b5312016-03-04 16:36:20 -08002859void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2860 SkASSERT(key);
2861
2862 SkPaint paint;
2863 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, nullptr)
2864 while (iter.next()) {
2865 iter.fDevice->drawAnnotation(iter, rect, key, value);
2866 }
2867 LOOPER_END
2868}
2869
reed@android.com8a1c16f2008-12-17 15:59:43 +00002870//////////////////////////////////////////////////////////////////////////////
2871// These methods are NOT virtual, and therefore must call back into virtual
2872// methods, rather than actually drawing themselves.
2873//////////////////////////////////////////////////////////////////////////////
2874
2875void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002876 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002877 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002878 SkPaint paint;
2879
2880 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002881 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002882 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002883 }
2884 this->drawPaint(paint);
2885}
2886
reed@android.com845fdac2009-06-23 03:01:32 +00002887void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002888 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002889 SkPaint paint;
2890
2891 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002892 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002893 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002894 }
2895 this->drawPaint(paint);
2896}
2897
2898void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002899 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002900 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002901
reed@android.com8a1c16f2008-12-17 15:59:43 +00002902 pt.set(x, y);
2903 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2904}
2905
2906void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002907 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002908 SkPoint pt;
2909 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002910
reed@android.com8a1c16f2008-12-17 15:59:43 +00002911 pt.set(x, y);
2912 paint.setColor(color);
2913 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2914}
2915
2916void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2917 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002918 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002919 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002920
reed@android.com8a1c16f2008-12-17 15:59:43 +00002921 pts[0].set(x0, y0);
2922 pts[1].set(x1, y1);
2923 this->drawPoints(kLines_PointMode, 2, pts, paint);
2924}
2925
2926void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2927 SkScalar right, SkScalar bottom,
2928 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002929 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002930 SkRect r;
2931
2932 r.set(left, top, right, bottom);
2933 this->drawRect(r, paint);
2934}
2935
2936void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2937 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002938 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002939 if (radius < 0) {
2940 radius = 0;
2941 }
2942
2943 SkRect r;
2944 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002945 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002946}
2947
2948void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2949 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002950 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002951 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002952 SkRRect rrect;
2953 rrect.setRectXY(r, rx, ry);
2954 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002955 } else {
2956 this->drawRect(r, paint);
2957 }
2958}
2959
reed@android.com8a1c16f2008-12-17 15:59:43 +00002960void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2961 SkScalar sweepAngle, bool useCenter,
2962 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002963 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002964 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2965 this->drawOval(oval, paint);
2966 } else {
2967 SkPath path;
2968 if (useCenter) {
2969 path.moveTo(oval.centerX(), oval.centerY());
2970 }
2971 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2972 if (useCenter) {
2973 path.close();
2974 }
2975 this->drawPath(path, paint);
2976 }
2977}
2978
2979void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2980 const SkPath& path, SkScalar hOffset,
2981 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002982 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002983 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002984
reed@android.com8a1c16f2008-12-17 15:59:43 +00002985 matrix.setTranslate(hOffset, vOffset);
2986 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2987}
2988
reed@android.comf76bacf2009-05-13 14:00:33 +00002989///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07002990
2991/**
2992 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2993 * against the playback cost of recursing into the subpicture to get at its actual ops.
2994 *
2995 * For now we pick a conservatively small value, though measurement (and other heuristics like
2996 * the type of ops contained) may justify changing this value.
2997 */
2998#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07002999
reedd5fa1a42014-08-09 11:08:05 -07003000void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08003001 RETURN_ON_NULL(picture);
3002
reed1c2c4412015-04-30 13:09:24 -07003003 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
reede3b38ce2016-01-08 09:18:44 -08003004 if (matrix && matrix->isIdentity()) {
3005 matrix = nullptr;
3006 }
3007 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
3008 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
3009 picture->playback(this);
3010 } else {
3011 this->onDrawPicture(picture, matrix, paint);
reedd5fa1a42014-08-09 11:08:05 -07003012 }
3013}
robertphillips9b14f262014-06-04 05:40:44 -07003014
reedd5fa1a42014-08-09 11:08:05 -07003015void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
3016 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07003017 if (!paint || paint->canComputeFastBounds()) {
3018 SkRect bounds = picture->cullRect();
3019 if (paint) {
3020 paint->computeFastBounds(bounds, &bounds);
3021 }
3022 if (matrix) {
3023 matrix->mapRect(&bounds);
3024 }
3025 if (this->quickReject(bounds)) {
3026 return;
3027 }
3028 }
3029
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00003030 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07003031 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00003032 // Canvas has to first give the device the opportunity to render
3033 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07003034 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00003035 return; // the device has rendered the entire picture
3036 }
3037 }
3038
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003039 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07003040 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003041}
3042
reed@android.com8a1c16f2008-12-17 15:59:43 +00003043///////////////////////////////////////////////////////////////////////////////
3044///////////////////////////////////////////////////////////////////////////////
3045
3046SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
bungeman99fe8222015-08-20 07:57:51 -07003047 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003048
3049 SkASSERT(canvas);
3050
3051 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
3052 fDone = !fImpl->next();
3053}
3054
3055SkCanvas::LayerIter::~LayerIter() {
3056 fImpl->~SkDrawIter();
3057}
3058
3059void SkCanvas::LayerIter::next() {
3060 fDone = !fImpl->next();
3061}
3062
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003063SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003064 return fImpl->getDevice();
3065}
3066
3067const SkMatrix& SkCanvas::LayerIter::matrix() const {
3068 return fImpl->getMatrix();
3069}
3070
3071const SkPaint& SkCanvas::LayerIter::paint() const {
3072 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003073 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003074 paint = &fDefaultPaint;
3075 }
3076 return *paint;
3077}
3078
reed1e7f5e72016-04-27 07:49:17 -07003079const SkRasterClip& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +00003080int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
3081int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003082
3083///////////////////////////////////////////////////////////////////////////////
3084
fmalitac3b589a2014-06-05 12:40:07 -07003085SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003086
3087///////////////////////////////////////////////////////////////////////////////
3088
3089static bool supported_for_raster_canvas(const SkImageInfo& info) {
3090 switch (info.alphaType()) {
3091 case kPremul_SkAlphaType:
3092 case kOpaque_SkAlphaType:
3093 break;
3094 default:
3095 return false;
3096 }
3097
3098 switch (info.colorType()) {
3099 case kAlpha_8_SkColorType:
3100 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00003101 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003102 break;
3103 default:
3104 return false;
3105 }
3106
3107 return true;
3108}
3109
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003110SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
3111 if (!supported_for_raster_canvas(info)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003112 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003113 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003114
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003115 SkBitmap bitmap;
3116 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003117 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003118 }
halcanary385fe4d2015-08-26 13:07:48 -07003119 return new SkCanvas(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003120}
reedd5fa1a42014-08-09 11:08:05 -07003121
3122///////////////////////////////////////////////////////////////////////////////
3123
3124SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003125 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07003126 : fCanvas(canvas)
3127 , fSaveCount(canvas->getSaveCount())
3128{
bsalomon49f085d2014-09-05 13:34:00 -07003129 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003130 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07003131 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003132 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07003133 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003134 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07003135 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003136 canvas->save();
3137 }
mtklein6cfa73a2014-08-13 13:33:49 -07003138
bsalomon49f085d2014-09-05 13:34:00 -07003139 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003140 canvas->concat(*matrix);
3141 }
3142}
3143
3144SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
3145 fCanvas->restoreToCount(fSaveCount);
3146}
reede8f30622016-03-23 18:59:25 -07003147
3148#ifdef SK_SUPPORT_LEGACY_NEW_SURFACE_API
3149SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
3150 return this->makeSurface(info, props).release();
3151}
3152#endif