blob: 1999f3109fb8c907509518dfe6fd6b8062b92ac7 [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"
bsalomon614d8f92016-07-13 15:42:40 -070044#include "SkGrPriv.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000045#endif
46
reede3b38ce2016-01-08 09:18:44 -080047#define RETURN_ON_NULL(ptr) do { if (nullptr == (ptr)) return; } while (0)
48
reed2d1afab2016-06-29 14:33:11 -070049//#define SK_SUPPORT_PRECHECK_CLIPRECT
50
reedc83a2972015-07-16 07:40:45 -070051/*
52 * Return true if the drawing this rect would hit every pixels in the canvas.
53 *
54 * Returns false if
55 * - rect does not contain the canvas' bounds
56 * - paint is not fill
57 * - paint would blur or otherwise change the coverage of the rect
58 */
59bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
60 ShaderOverrideOpacity overrideOpacity) const {
bungeman99fe8222015-08-20 07:57:51 -070061 static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
62 (int)kNone_ShaderOverrideOpacity,
63 "need_matching_enums0");
64 static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
65 (int)kOpaque_ShaderOverrideOpacity,
66 "need_matching_enums1");
67 static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
68 (int)kNotOpaque_ShaderOverrideOpacity,
69 "need_matching_enums2");
reedc83a2972015-07-16 07:40:45 -070070
71 const SkISize size = this->getBaseLayerSize();
72 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
73 if (!this->getClipStack()->quickContains(bounds)) {
74 return false;
75 }
76
77 if (rect) {
reed6092b6e2016-07-10 11:45:34 -070078 if (!this->getTotalMatrix().isScaleTranslate()) {
reedc83a2972015-07-16 07:40:45 -070079 return false; // conservative
80 }
reed6092b6e2016-07-10 11:45:34 -070081
82 SkRect devRect;
83 this->getTotalMatrix().mapRectScaleTranslate(&devRect, *rect);
84 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -070085 return false;
86 }
87 }
88
89 if (paint) {
90 SkPaint::Style paintStyle = paint->getStyle();
91 if (!(paintStyle == SkPaint::kFill_Style ||
92 paintStyle == SkPaint::kStrokeAndFill_Style)) {
93 return false;
94 }
95 if (paint->getMaskFilter() || paint->getLooper()
96 || paint->getPathEffect() || paint->getImageFilter()) {
97 return false; // conservative
98 }
99 }
100 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
101}
102
103///////////////////////////////////////////////////////////////////////////////////////////////////
104
reedd990e2f2014-12-22 11:58:30 -0800105static bool gIgnoreSaveLayerBounds;
106void SkCanvas::Internal_Private_SetIgnoreSaveLayerBounds(bool ignore) {
107 gIgnoreSaveLayerBounds = ignore;
108}
109bool SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds() {
110 return gIgnoreSaveLayerBounds;
111}
112
reed0acf1b42014-12-22 16:12:38 -0800113static bool gTreatSpriteAsBitmap;
114void SkCanvas::Internal_Private_SetTreatSpriteAsBitmap(bool spriteAsBitmap) {
115 gTreatSpriteAsBitmap = spriteAsBitmap;
116}
117bool SkCanvas::Internal_Private_GetTreatSpriteAsBitmap() {
118 return gTreatSpriteAsBitmap;
119}
120
reed@google.comda17f752012-08-16 18:27:05 +0000121// experimental for faster tiled drawing...
122//#define SK_ENABLE_CLIP_QUICKREJECT
reed@android.com8a1c16f2008-12-17 15:59:43 +0000123//#define SK_TRACE_SAVERESTORE
124
125#ifdef SK_TRACE_SAVERESTORE
126 static int gLayerCounter;
127 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
128 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
129
130 static int gRecCounter;
131 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
132 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
133
134 static int gCanvasCounter;
135 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
136 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
137#else
138 #define inc_layer()
139 #define dec_layer()
140 #define inc_rec()
141 #define dec_rec()
142 #define inc_canvas()
143 #define dec_canvas()
144#endif
145
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000146typedef SkTLazy<SkPaint> SkLazyPaint;
147
reedc83a2972015-07-16 07:40:45 -0700148void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000149 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700150 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
151 ? SkSurface::kDiscard_ContentChangeMode
152 : SkSurface::kRetain_ContentChangeMode);
153 }
154}
155
156void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
157 ShaderOverrideOpacity overrideOpacity) {
158 if (fSurfaceBase) {
159 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
160 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
161 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
162 // and therefore we don't care which mode we're in.
163 //
164 if (fSurfaceBase->outstandingImageSnapshot()) {
165 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
166 mode = SkSurface::kDiscard_ContentChangeMode;
167 }
168 }
169 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000170 }
171}
172
reed@android.com8a1c16f2008-12-17 15:59:43 +0000173///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000174
reed4a8126e2014-09-22 07:29:03 -0700175static uint32_t filter_paint_flags(const SkSurfaceProps& props, uint32_t flags) {
176 const uint32_t propFlags = props.flags();
177 if (propFlags & SkSurfaceProps::kDisallowDither_Flag) {
178 flags &= ~SkPaint::kDither_Flag;
179 }
180 if (propFlags & SkSurfaceProps::kDisallowAntiAlias_Flag) {
181 flags &= ~SkPaint::kAntiAlias_Flag;
182 }
183 return flags;
184}
185
186///////////////////////////////////////////////////////////////////////////////
187
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000188/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000189 The clip/matrix/proc are fields that reflect the top of the save/restore
190 stack. Whenever the canvas changes, it marks a dirty flag, and then before
191 these are used (assuming we're not on a layer) we rebuild these cache
192 values: they reflect the top of the save stack, but translated and clipped
193 by the device's XY offset and bitmap-bounds.
194*/
195struct DeviceCM {
196 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000197 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000198 SkRasterClip fClip;
reed@google.com6f8f2922011-03-04 22:27:10 +0000199 SkPaint* fPaint; // may be null (in the future)
reed61f501f2015-04-29 08:34:00 -0700200 const SkMatrix* fMatrix;
201 SkMatrix fMatrixStorage;
reed8c30a812016-04-20 16:36:51 -0700202 SkMatrix fStashedMatrix; // original CTM; used by imagefilter in saveLayer
reed@android.com8a1c16f2008-12-17 15:59:43 +0000203
reed96e657d2015-03-10 17:30:07 -0700204 DeviceCM(SkBaseDevice* device, const SkPaint* paint, SkCanvas* canvas,
reed7503d602016-07-15 14:23:29 -0700205 bool conservativeRasterClip, const SkMatrix& stashed)
halcanary96fcdcc2015-08-27 07:41:13 -0700206 : fNext(nullptr)
reedd9544982014-09-09 18:46:22 -0700207 , fClip(conservativeRasterClip)
reed8c30a812016-04-20 16:36:51 -0700208 , fStashedMatrix(stashed)
reedd9544982014-09-09 18:46:22 -0700209 {
halcanary96fcdcc2015-08-27 07:41:13 -0700210 if (nullptr != device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000212 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000213 }
reed@google.com4b226022011-01-11 18:32:13 +0000214 fDevice = device;
halcanary96fcdcc2015-08-27 07:41:13 -0700215 fPaint = paint ? new SkPaint(*paint) : nullptr;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000216 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000218 ~DeviceCM() {
bsalomon49f085d2014-09-05 13:34:00 -0700219 if (fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000220 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221 fDevice->unref();
222 }
halcanary385fe4d2015-08-26 13:07:48 -0700223 delete fPaint;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000224 }
reed@google.com4b226022011-01-11 18:32:13 +0000225
mtkleinfeaadee2015-04-08 11:25:48 -0700226 void reset(const SkIRect& bounds) {
227 SkASSERT(!fPaint);
228 SkASSERT(!fNext);
229 SkASSERT(fDevice);
230 fClip.setRect(bounds);
231 }
232
reed@google.com045e62d2011-10-24 12:19:46 +0000233 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
234 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000235 int x = fDevice->getOrigin().x();
236 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000237 int width = fDevice->width();
238 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000239
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240 if ((x | y) == 0) {
241 fMatrix = &totalMatrix;
242 fClip = totalClip;
243 } else {
244 fMatrixStorage = totalMatrix;
245 fMatrixStorage.postTranslate(SkIntToScalar(-x),
246 SkIntToScalar(-y));
247 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000248
reed@android.com8a1c16f2008-12-17 15:59:43 +0000249 totalClip.translate(-x, -y, &fClip);
250 }
251
reed@google.com045e62d2011-10-24 12:19:46 +0000252 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253
254 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000255
reed@android.com8a1c16f2008-12-17 15:59:43 +0000256 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000257 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000258 SkRegion::kDifference_Op);
259 }
reed@google.com4b226022011-01-11 18:32:13 +0000260
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000261 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
262
reed@android.com8a1c16f2008-12-17 15:59:43 +0000263#ifdef SK_DEBUG
264 if (!fClip.isEmpty()) {
265 SkIRect deviceR;
266 deviceR.set(0, 0, width, height);
267 SkASSERT(deviceR.contains(fClip.getBounds()));
268 }
269#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000270 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000271};
272
273/* This is the record we keep for each save/restore level in the stack.
274 Since a level optionally copies the matrix and/or stack, we have pointers
275 for these fields. If the value is copied for this level, the copy is
276 stored in the ...Storage field, and the pointer points to that. If the
277 value is not copied for this level, we ignore ...Storage, and just point
278 at the corresponding value in the previous level in the stack.
279*/
280class SkCanvas::MCRec {
281public:
reed1f836ee2014-07-07 07:49:34 -0700282 SkDrawFilter* fFilter; // the current filter (or null)
reedd9544982014-09-09 18:46:22 -0700283 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284 /* If there are any layers in the stack, this points to the top-most
285 one that is at or below this level in the stack (so we know what
286 bitmap/device to draw into from this level. This value is NOT
287 reference counted, since the real owner is either our fLayer field,
288 or a previous one in a lower level.)
289 */
reed2ff1fce2014-12-11 07:07:37 -0800290 DeviceCM* fTopLayer;
291 SkRasterClip fRasterClip;
292 SkMatrix fMatrix;
293 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000294
vjiaoblacke5de1302016-07-13 14:05:28 -0700295 // This is the current cumulative depth (aggregate of all done translateZ calls)
296 SkScalar fCurDrawDepth;
297
reedd9544982014-09-09 18:46:22 -0700298 MCRec(bool conservativeRasterClip) : fRasterClip(conservativeRasterClip) {
halcanary96fcdcc2015-08-27 07:41:13 -0700299 fFilter = nullptr;
300 fLayer = nullptr;
301 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800302 fMatrix.reset();
303 fDeferredSaveCount = 0;
vjiaoblacke5de1302016-07-13 14:05:28 -0700304 fCurDrawDepth = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700305
reedd9544982014-09-09 18:46:22 -0700306 // don't bother initializing fNext
307 inc_rec();
308 }
vjiaoblacke5de1302016-07-13 14:05:28 -0700309 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix),
310 fCurDrawDepth(prev.fCurDrawDepth) {
reedd9544982014-09-09 18:46:22 -0700311 fFilter = SkSafeRef(prev.fFilter);
halcanary96fcdcc2015-08-27 07:41:13 -0700312 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700313 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800314 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700315
reed@android.com8a1c16f2008-12-17 15:59:43 +0000316 // don't bother initializing fNext
317 inc_rec();
318 }
319 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000320 SkSafeUnref(fFilter);
halcanary385fe4d2015-08-26 13:07:48 -0700321 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000322 dec_rec();
323 }
mtkleinfeaadee2015-04-08 11:25:48 -0700324
325 void reset(const SkIRect& bounds) {
326 SkASSERT(fLayer);
327 SkASSERT(fDeferredSaveCount == 0);
328
329 fMatrix.reset();
330 fRasterClip.setRect(bounds);
331 fLayer->reset(bounds);
332 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000333};
334
335class SkDrawIter : public SkDraw {
336public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000337 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000338 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000339 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000340 canvas->updateDeviceCMCache();
341
reed687fa1c2015-04-07 08:00:56 -0700342 fClipStack = canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000343 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000344 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000345 }
reed@google.com4b226022011-01-11 18:32:13 +0000346
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347 bool next() {
348 // skip over recs with empty clips
349 if (fSkipEmptyClips) {
350 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
351 fCurrLayer = fCurrLayer->fNext;
352 }
353 }
354
reed@google.comf68c5e22012-02-24 16:38:58 +0000355 const DeviceCM* rec = fCurrLayer;
356 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000357
358 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000359 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000360 fDevice = rec->fDevice;
reed41e010c2015-06-09 12:16:53 -0700361 if (!fDevice->accessPixels(&fDst)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700362 fDst.reset(fDevice->imageInfo(), nullptr, 0);
reed41e010c2015-06-09 12:16:53 -0700363 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000364 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000365 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000366
367 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700368 // fCurrLayer may be nullptr now
reed@android.com199f1082009-06-10 02:12:47 +0000369
reed@android.com8a1c16f2008-12-17 15:59:43 +0000370 return true;
371 }
372 return false;
373 }
reed@google.com4b226022011-01-11 18:32:13 +0000374
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000375 SkBaseDevice* getDevice() const { return fDevice; }
reed1e7f5e72016-04-27 07:49:17 -0700376 const SkRasterClip& getClip() const { return *fRC; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000377 int getX() const { return fDevice->getOrigin().x(); }
378 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000379 const SkMatrix& getMatrix() const { return *fMatrix; }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000380 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000381
reed@android.com8a1c16f2008-12-17 15:59:43 +0000382private:
383 SkCanvas* fCanvas;
384 const DeviceCM* fCurrLayer;
385 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000386 SkBool8 fSkipEmptyClips;
387
388 typedef SkDraw INHERITED;
389};
390
391/////////////////////////////////////////////////////////////////////////////
392
reeddbc3cef2015-04-29 12:18:57 -0700393static SkPaint* set_if_needed(SkLazyPaint* lazy, const SkPaint& orig) {
394 return lazy->isValid() ? lazy->get() : lazy->set(orig);
395}
396
397/**
398 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700399 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700400 */
reedd053ce92016-03-22 10:17:23 -0700401static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700402 SkImageFilter* imgf = paint.getImageFilter();
403 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700404 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700405 }
406
reedd053ce92016-03-22 10:17:23 -0700407 SkColorFilter* imgCFPtr;
408 if (!imgf->asAColorFilter(&imgCFPtr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700409 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700410 }
reedd053ce92016-03-22 10:17:23 -0700411 sk_sp<SkColorFilter> imgCF(imgCFPtr);
reeddbc3cef2015-04-29 12:18:57 -0700412
413 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700414 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700415 // there is no existing paint colorfilter, so we can just return the imagefilter's
416 return imgCF;
417 }
418
419 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
420 // and we need to combine them into a single colorfilter.
reedd053ce92016-03-22 10:17:23 -0700421 return SkColorFilter::MakeComposeFilter(std::move(imgCF), sk_ref_sp(paintCF));
reeddbc3cef2015-04-29 12:18:57 -0700422}
423
senorblanco87e066e2015-10-28 11:23:36 -0700424/**
425 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
426 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
427 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
428 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
429 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
430 * conservative "effective" bounds based on the settings in the paint... with one exception. This
431 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
432 * deliberately ignored.
433 */
434static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
435 const SkRect& rawBounds,
436 SkRect* storage) {
437 SkPaint tmpUnfiltered(paint);
438 tmpUnfiltered.setImageFilter(nullptr);
439 if (tmpUnfiltered.canComputeFastBounds()) {
440 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
441 } else {
442 return rawBounds;
443 }
444}
445
reed@android.com8a1c16f2008-12-17 15:59:43 +0000446class AutoDrawLooper {
447public:
senorblanco87e066e2015-10-28 11:23:36 -0700448 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
449 // paint. It's used to determine the size of the offscreen layer for filters.
450 // If null, the clip will be used instead.
reed4a8126e2014-09-22 07:29:03 -0700451 AutoDrawLooper(SkCanvas* canvas, const SkSurfaceProps& props, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000452 bool skipLayerForImageFilter = false,
senorblanco87e066e2015-10-28 11:23:36 -0700453 const SkRect* rawBounds = nullptr) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000454 fCanvas = canvas;
fmalita53d9f1c2016-01-25 06:23:54 -0800455#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000456 fFilter = canvas->getDrawFilter();
fmalita77650002016-01-21 18:47:11 -0800457#else
458 fFilter = nullptr;
459#endif
reed4a8126e2014-09-22 07:29:03 -0700460 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000461 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700462 fTempLayerForImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000463 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000464
reedd053ce92016-03-22 10:17:23 -0700465 auto simplifiedCF = image_to_color_filter(fOrigPaint);
reeddbc3cef2015-04-29 12:18:57 -0700466 if (simplifiedCF) {
467 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reedd053ce92016-03-22 10:17:23 -0700468 paint->setColorFilter(std::move(simplifiedCF));
halcanary96fcdcc2015-08-27 07:41:13 -0700469 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700470 fPaint = paint;
471 }
472
473 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700474 /**
475 * We implement ImageFilters for a given draw by creating a layer, then applying the
476 * imagefilter to the pixels of that layer (its backing surface/image), and then
477 * we call restore() to xfer that layer to the main canvas.
478 *
479 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
480 * 2. Generate the src pixels:
481 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
482 * return (fPaint). We then draw the primitive (using srcover) into a cleared
483 * buffer/surface.
484 * 3. Restore the layer created in #1
485 * The imagefilter is passed the buffer/surface from the layer (now filled with the
486 * src pixels of the primitive). It returns a new "filtered" buffer, which we
487 * draw onto the previous layer using the xfermode from the original paint.
488 */
reed@google.com8926b162012-03-23 15:36:36 +0000489 SkPaint tmp;
reeddbc3cef2015-04-29 12:18:57 -0700490 tmp.setImageFilter(fPaint->getImageFilter());
reedcfb6bdf2016-03-29 11:32:50 -0700491 tmp.setXfermode(sk_ref_sp(fPaint->getXfermode()));
senorblanco87e066e2015-10-28 11:23:36 -0700492 SkRect storage;
493 if (rawBounds) {
494 // Make rawBounds include all paint outsets except for those due to image filters.
495 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
496 }
reedbfd5f172016-01-07 11:28:08 -0800497 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &tmp),
reed76033be2015-03-14 10:54:31 -0700498 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700499 fTempLayerForImageFilter = true;
500 // we remove the imagefilter/xfermode inside doNext()
reed@google.com8926b162012-03-23 15:36:36 +0000501 }
502
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000503 if (SkDrawLooper* looper = paint.getLooper()) {
504 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
505 looper->contextSize());
506 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000507 fIsSimple = false;
508 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700509 fLooperContext = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000510 // can we be marked as simple?
reed5c476fb2015-04-20 08:04:21 -0700511 fIsSimple = !fFilter && !fTempLayerForImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000512 }
piotaixrb5fae932014-09-24 13:03:30 -0700513
reed4a8126e2014-09-22 07:29:03 -0700514 uint32_t oldFlags = paint.getFlags();
515 fNewPaintFlags = filter_paint_flags(props, oldFlags);
516 if (fIsSimple && (fNewPaintFlags != oldFlags)) {
reeddbc3cef2015-04-29 12:18:57 -0700517 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700518 paint->setFlags(fNewPaintFlags);
519 fPaint = paint;
520 // if we're not simple, doNext() will take care of calling setFlags()
521 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000522 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000523
reed@android.com8a1c16f2008-12-17 15:59:43 +0000524 ~AutoDrawLooper() {
reed5c476fb2015-04-20 08:04:21 -0700525 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000526 fCanvas->internalRestore();
527 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000528 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000529 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000530
reed@google.com4e2b3d32011-04-07 14:18:59 +0000531 const SkPaint& paint() const {
532 SkASSERT(fPaint);
533 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000534 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000535
reed@google.com129ec222012-05-15 13:24:09 +0000536 bool next(SkDrawFilter::Type drawType) {
537 if (fDone) {
538 return false;
539 } else if (fIsSimple) {
540 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000541 return !fPaint->nothingToDraw();
542 } else {
543 return this->doNext(drawType);
544 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000545 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000546
reed@android.com8a1c16f2008-12-17 15:59:43 +0000547private:
reeddbc3cef2015-04-29 12:18:57 -0700548 SkLazyPaint fLazyPaintInit; // base paint storage in case we need to modify it
549 SkLazyPaint fLazyPaintPerLooper; // per-draw-looper storage, so the looper can modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000550 SkCanvas* fCanvas;
551 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000552 SkDrawFilter* fFilter;
553 const SkPaint* fPaint;
554 int fSaveCount;
reed4a8126e2014-09-22 07:29:03 -0700555 uint32_t fNewPaintFlags;
reed5c476fb2015-04-20 08:04:21 -0700556 bool fTempLayerForImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000557 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000558 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000559 SkDrawLooper::Context* fLooperContext;
560 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000561
562 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000563};
564
reed@google.com129ec222012-05-15 13:24:09 +0000565bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
halcanary96fcdcc2015-08-27 07:41:13 -0700566 fPaint = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000567 SkASSERT(!fIsSimple);
reed5c476fb2015-04-20 08:04:21 -0700568 SkASSERT(fLooperContext || fFilter || fTempLayerForImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000569
reeddbc3cef2015-04-29 12:18:57 -0700570 SkPaint* paint = fLazyPaintPerLooper.set(fLazyPaintInit.isValid() ?
571 *fLazyPaintInit.get() : fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700572 paint->setFlags(fNewPaintFlags);
reed@google.com129ec222012-05-15 13:24:09 +0000573
reed5c476fb2015-04-20 08:04:21 -0700574 if (fTempLayerForImageFilter) {
halcanary96fcdcc2015-08-27 07:41:13 -0700575 paint->setImageFilter(nullptr);
576 paint->setXfermode(nullptr);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000577 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000578
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000579 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000580 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000581 return false;
582 }
583 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000584 if (!fFilter->filter(paint, drawType)) {
585 fDone = true;
586 return false;
587 }
halcanary96fcdcc2015-08-27 07:41:13 -0700588 if (nullptr == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000589 // no looper means we only draw once
590 fDone = true;
591 }
592 }
593 fPaint = paint;
594
595 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000596 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000597 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000598 }
599
600 // call this after any possible paint modifiers
601 if (fPaint->nothingToDraw()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700602 fPaint = nullptr;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000603 return false;
604 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000605 return true;
606}
607
reed@android.com8a1c16f2008-12-17 15:59:43 +0000608////////// macros to place around the internal draw calls //////////////////
609
reed262a71b2015-12-05 13:07:27 -0800610#define LOOPER_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
611 this->predrawNotify(); \
612 AutoDrawLooper looper(this, fProps, paint, skipLayerForFilter, bounds); \
613 while (looper.next(SkDrawFilter::kBitmap_Type)) { \
614 SkDrawIter iter(this);
615
616
reed@google.com8926b162012-03-23 15:36:36 +0000617#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000618 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700619 AutoDrawLooper looper(this, fProps, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000620 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000621 SkDrawIter iter(this);
622
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000623#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000624 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700625 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000626 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000627 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000628
reedc83a2972015-07-16 07:40:45 -0700629#define LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, type, bounds, auxOpaque) \
630 this->predrawNotify(bounds, &paint, auxOpaque); \
631 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
632 while (looper.next(type)) { \
633 SkDrawIter iter(this);
634
reed@google.com4e2b3d32011-04-07 14:18:59 +0000635#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000636
637////////////////////////////////////////////////////////////////////////////
638
mtkleinfeaadee2015-04-08 11:25:48 -0700639void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
640 this->restoreToCount(1);
641 fCachedLocalClipBounds.setEmpty();
642 fCachedLocalClipBoundsDirty = true;
643 fClipStack->reset();
644 fMCRec->reset(bounds);
645
646 // We're peering through a lot of structs here. Only at this scope do we
647 // know that the device is an SkBitmapDevice (really an SkNoPixelsBitmapDevice).
648 static_cast<SkBitmapDevice*>(fMCRec->fLayer->fDevice)->setNewSize(bounds.size());
649}
650
reedd9544982014-09-09 18:46:22 -0700651SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
reed42b73eb2015-11-20 13:42:42 -0800652 if (device && device->forceConservativeRasterClip()) {
653 flags = InitFlags(flags | kConservativeRasterClip_InitFlag);
654 }
655 // Since init() is only called once by our constructors, it is safe to perform this
656 // const-cast.
657 *const_cast<bool*>(&fConservativeRasterClip) = SkToBool(flags & kConservativeRasterClip_InitFlag);
658
reed@google.comc0784db2013-12-13 21:16:12 +0000659 fCachedLocalClipBounds.setEmpty();
660 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000661 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000662 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700663 fDeviceCMDirty = true;
reed2ff1fce2014-12-11 07:07:37 -0800664 fSaveCount = 1;
halcanary96fcdcc2015-08-27 07:41:13 -0700665 fMetaData = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000666
halcanary385fe4d2015-08-26 13:07:48 -0700667 fClipStack.reset(new SkClipStack);
reed687fa1c2015-04-07 08:00:56 -0700668
reed@android.com8a1c16f2008-12-17 15:59:43 +0000669 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700670 new (fMCRec) MCRec(fConservativeRasterClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000671
reeda499f902015-05-01 09:34:31 -0700672 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
673 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
reed7503d602016-07-15 14:23:29 -0700674 new (fDeviceCMStorage) DeviceCM(nullptr, nullptr, nullptr, fConservativeRasterClip,
reed8c30a812016-04-20 16:36:51 -0700675 fMCRec->fMatrix);
reedb679ca82015-04-07 04:40:48 -0700676
reed@android.com8a1c16f2008-12-17 15:59:43 +0000677 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000678
halcanary96fcdcc2015-08-27 07:41:13 -0700679 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000680
reedf92c8662014-08-18 08:02:43 -0700681 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700682 // The root device and the canvas should always have the same pixel geometry
683 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reedf92c8662014-08-18 08:02:43 -0700684 device->onAttachToCanvas(this);
685 fMCRec->fLayer->fDevice = SkRef(device);
reed78e27682014-11-19 08:04:34 -0800686 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
reedf92c8662014-08-18 08:02:43 -0700687 }
688 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000689}
690
reed@google.comcde92112011-07-06 20:00:52 +0000691SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000692 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700693 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800694 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000695{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000696 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000697
halcanary96fcdcc2015-08-27 07:41:13 -0700698 this->init(nullptr, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000699}
700
reedd9544982014-09-09 18:46:22 -0700701static SkBitmap make_nopixels(int width, int height) {
702 SkBitmap bitmap;
703 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
704 return bitmap;
705}
706
707class SkNoPixelsBitmapDevice : public SkBitmapDevice {
708public:
robertphillipsfcf78292015-06-19 11:49:52 -0700709 SkNoPixelsBitmapDevice(const SkIRect& bounds, const SkSurfaceProps& surfaceProps)
710 : INHERITED(make_nopixels(bounds.width(), bounds.height()), surfaceProps)
reed78e27682014-11-19 08:04:34 -0800711 {
712 this->setOrigin(bounds.x(), bounds.y());
713 }
reedd9544982014-09-09 18:46:22 -0700714
715private:
piotaixrb5fae932014-09-24 13:03:30 -0700716
reedd9544982014-09-09 18:46:22 -0700717 typedef SkBitmapDevice INHERITED;
718};
719
reed96a857e2015-01-25 10:33:58 -0800720SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000721 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800722 , fProps(SkSurfacePropsCopyOrDefault(props))
reed42b73eb2015-11-20 13:42:42 -0800723 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000724{
725 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700726
halcanary385fe4d2015-08-26 13:07:48 -0700727 this->init(new SkNoPixelsBitmapDevice(SkIRect::MakeWH(width, height), fProps),
728 kDefault_InitFlags)->unref();
reedd9544982014-09-09 18:46:22 -0700729}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000730
reed78e27682014-11-19 08:04:34 -0800731SkCanvas::SkCanvas(const SkIRect& bounds, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700732 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700733 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800734 , fConservativeRasterClip(false)
reedd9544982014-09-09 18:46:22 -0700735{
736 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700737
halcanary385fe4d2015-08-26 13:07:48 -0700738 this->init(new SkNoPixelsBitmapDevice(bounds, fProps), flags)->unref();
reedd9544982014-09-09 18:46:22 -0700739}
740
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000741SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000742 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700743 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800744 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000745{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000746 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700747
reedd9544982014-09-09 18:46:22 -0700748 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000749}
750
robertphillipsfcf78292015-06-19 11:49:52 -0700751SkCanvas::SkCanvas(SkBaseDevice* device, InitFlags flags)
752 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700753 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800754 , fConservativeRasterClip(false)
robertphillipsfcf78292015-06-19 11:49:52 -0700755{
756 inc_canvas();
757
758 this->init(device, flags);
759}
760
reed4a8126e2014-09-22 07:29:03 -0700761SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700762 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700763 , fProps(props)
reed42b73eb2015-11-20 13:42:42 -0800764 , fConservativeRasterClip(false)
reed3716fd02014-09-21 09:39:55 -0700765{
766 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700767
halcanary385fe4d2015-08-26 13:07:48 -0700768 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700769 this->init(device, kDefault_InitFlags);
770}
reed29c857d2014-09-21 10:25:07 -0700771
reed4a8126e2014-09-22 07:29:03 -0700772SkCanvas::SkCanvas(const SkBitmap& bitmap)
773 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
774 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800775 , fConservativeRasterClip(false)
reed4a8126e2014-09-22 07:29:03 -0700776{
777 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700778
halcanary385fe4d2015-08-26 13:07:48 -0700779 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700780 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000781}
782
783SkCanvas::~SkCanvas() {
784 // free up the contents of our deque
785 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000786
reed@android.com8a1c16f2008-12-17 15:59:43 +0000787 this->internalRestore(); // restore the last, since we're going away
788
halcanary385fe4d2015-08-26 13:07:48 -0700789 delete fMetaData;
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000790
reed@android.com8a1c16f2008-12-17 15:59:43 +0000791 dec_canvas();
792}
793
fmalita53d9f1c2016-01-25 06:23:54 -0800794#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000795SkDrawFilter* SkCanvas::getDrawFilter() const {
796 return fMCRec->fFilter;
797}
798
799SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
reed51985e32015-04-11 08:04:56 -0700800 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000801 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
802 return filter;
803}
fmalita77650002016-01-21 18:47:11 -0800804#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000805
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000806SkMetaData& SkCanvas::getMetaData() {
807 // metadata users are rare, so we lazily allocate it. If that changes we
808 // can decide to just make it a field in the device (rather than a ptr)
halcanary96fcdcc2015-08-27 07:41:13 -0700809 if (nullptr == fMetaData) {
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000810 fMetaData = new SkMetaData;
811 }
812 return *fMetaData;
813}
814
reed@android.com8a1c16f2008-12-17 15:59:43 +0000815///////////////////////////////////////////////////////////////////////////////
816
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000817void SkCanvas::flush() {
reedea5a6512016-07-07 16:44:27 -0700818 this->onFlush();
819}
820
821void SkCanvas::onFlush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000822 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000823 if (device) {
824 device->flush();
825 }
826}
827
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000828SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000829 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000830 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
831}
832
senorblancoafc7cce2016-02-02 18:44:15 -0800833SkIRect SkCanvas::getTopLayerBounds() const {
834 SkBaseDevice* d = this->getTopDevice();
835 if (!d) {
836 return SkIRect::MakeEmpty();
837 }
838 return SkIRect::MakeXYWH(d->getOrigin().x(), d->getOrigin().y(), d->width(), d->height());
839}
840
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000841SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000842 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000843 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000844 SkASSERT(rec && rec->fLayer);
845 return rec->fLayer->fDevice;
846}
847
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000848SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000849 if (updateMatrixClip) {
850 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
851 }
reed@google.com9266fed2011-03-30 00:18:03 +0000852 return fMCRec->fTopLayer->fDevice;
853}
854
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000855bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
856 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
857 return false;
858 }
859
860 bool weAllocated = false;
halcanary96fcdcc2015-08-27 07:41:13 -0700861 if (nullptr == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700862 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000863 return false;
864 }
865 weAllocated = true;
866 }
867
reedcf01e312015-05-23 19:14:51 -0700868 SkAutoPixmapUnlock unlocker;
869 if (bitmap->requestLock(&unlocker)) {
870 const SkPixmap& pm = unlocker.pixmap();
871 if (this->readPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), x, y)) {
872 return true;
873 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000874 }
875
876 if (weAllocated) {
halcanary96fcdcc2015-08-27 07:41:13 -0700877 bitmap->setPixelRef(nullptr);
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000878 }
879 return false;
880}
reed@google.com51df9e32010-12-23 19:29:18 +0000881
bsalomon@google.comc6980972011-11-02 19:57:21 +0000882bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000883 SkIRect r = srcRect;
884 const SkISize size = this->getBaseLayerSize();
885 if (!r.intersect(0, 0, size.width(), size.height())) {
886 bitmap->reset();
887 return false;
888 }
889
reed84825042014-09-02 12:50:45 -0700890 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000891 // bitmap will already be reset.
892 return false;
893 }
894 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
895 bitmap->reset();
896 return false;
897 }
898 return true;
899}
900
reed96472de2014-12-10 09:53:42 -0800901bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000902 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000903 if (!device) {
904 return false;
905 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000906 const SkISize size = this->getBaseLayerSize();
mtkleinf0f14112014-12-12 08:46:25 -0800907
reed96472de2014-12-10 09:53:42 -0800908 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
909 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000910 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000911 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000912
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000913 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800914 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000915}
916
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000917bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
918 if (bitmap.getTexture()) {
919 return false;
920 }
reedcf01e312015-05-23 19:14:51 -0700921
922 SkAutoPixmapUnlock unlocker;
923 if (bitmap.requestLock(&unlocker)) {
924 const SkPixmap& pm = unlocker.pixmap();
925 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000926 }
927 return false;
928}
929
930bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
931 int x, int y) {
932 switch (origInfo.colorType()) {
933 case kUnknown_SkColorType:
934 case kIndex_8_SkColorType:
935 return false;
936 default:
937 break;
938 }
halcanary96fcdcc2015-08-27 07:41:13 -0700939 if (nullptr == pixels || rowBytes < origInfo.minRowBytes()) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000940 return false;
941 }
942
943 const SkISize size = this->getBaseLayerSize();
944 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
945 if (!target.intersect(0, 0, size.width(), size.height())) {
946 return false;
947 }
948
949 SkBaseDevice* device = this->getDevice();
950 if (!device) {
951 return false;
952 }
953
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000954 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700955 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000956
957 // if x or y are negative, then we have to adjust pixels
958 if (x > 0) {
959 x = 0;
960 }
961 if (y > 0) {
962 y = 0;
963 }
964 // here x,y are either 0 or negative
965 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
966
reed4af35f32014-06-27 17:47:49 -0700967 // Tell our owning surface to bump its generation ID
reedc83a2972015-07-16 07:40:45 -0700968 const bool completeOverwrite = info.dimensions() == size;
969 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700970
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000971 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000972 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000973}
reed@google.com51df9e32010-12-23 19:29:18 +0000974
junov@google.com4370aed2012-01-18 16:21:08 +0000975SkCanvas* SkCanvas::canvasForDrawIter() {
976 return this;
977}
978
reed@android.com8a1c16f2008-12-17 15:59:43 +0000979//////////////////////////////////////////////////////////////////////////////
980
reed@android.com8a1c16f2008-12-17 15:59:43 +0000981void SkCanvas::updateDeviceCMCache() {
982 if (fDeviceCMDirty) {
983 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700984 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000985 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000986
halcanary96fcdcc2015-08-27 07:41:13 -0700987 if (nullptr == layer->fNext) { // only one layer
988 layer->updateMC(totalMatrix, totalClip, *fClipStack, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000989 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000990 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000991 do {
reed687fa1c2015-04-07 08:00:56 -0700992 layer->updateMC(totalMatrix, clip, *fClipStack, &clip);
halcanary96fcdcc2015-08-27 07:41:13 -0700993 } while ((layer = layer->fNext) != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000994 }
995 fDeviceCMDirty = false;
996 }
997}
998
reed@android.com8a1c16f2008-12-17 15:59:43 +0000999///////////////////////////////////////////////////////////////////////////////
1000
reed2ff1fce2014-12-11 07:07:37 -08001001void SkCanvas::checkForDeferredSave() {
1002 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -08001003 this->doSave();
1004 }
1005}
1006
reedf0090cb2014-11-26 08:55:51 -08001007int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -08001008#ifdef SK_DEBUG
1009 int count = 0;
1010 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
1011 for (;;) {
1012 const MCRec* rec = (const MCRec*)iter.next();
1013 if (!rec) {
1014 break;
1015 }
1016 count += 1 + rec->fDeferredSaveCount;
1017 }
1018 SkASSERT(count == fSaveCount);
1019#endif
1020 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -08001021}
1022
1023int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -08001024 fSaveCount += 1;
1025 fMCRec->fDeferredSaveCount += 1;
1026 return this->getSaveCount() - 1; // return our prev value
1027}
1028
1029void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -08001030 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -07001031
1032 SkASSERT(fMCRec->fDeferredSaveCount > 0);
1033 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001034 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -08001035}
1036
1037void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -08001038 if (fMCRec->fDeferredSaveCount > 0) {
1039 SkASSERT(fSaveCount > 1);
1040 fSaveCount -= 1;
1041 fMCRec->fDeferredSaveCount -= 1;
1042 } else {
1043 // check for underflow
1044 if (fMCStack.count() > 1) {
1045 this->willRestore();
1046 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -07001047 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001048 this->internalRestore();
1049 this->didRestore();
1050 }
reedf0090cb2014-11-26 08:55:51 -08001051 }
1052}
1053
1054void SkCanvas::restoreToCount(int count) {
1055 // sanity check
1056 if (count < 1) {
1057 count = 1;
1058 }
mtkleinf0f14112014-12-12 08:46:25 -08001059
reedf0090cb2014-11-26 08:55:51 -08001060 int n = this->getSaveCount() - count;
1061 for (int i = 0; i < n; ++i) {
1062 this->restore();
1063 }
1064}
1065
reed2ff1fce2014-12-11 07:07:37 -08001066void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001067 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -07001068 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +00001069 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +00001070
reed687fa1c2015-04-07 08:00:56 -07001071 fClipStack->save();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001072}
1073
reed4960eee2015-12-18 07:09:18 -08001074bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
reed@google.comb93ba452014-03-10 19:47:58 +00001075#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001076 return !(saveLayerFlags & SkCanvas::kDontClipToLayer_PrivateSaveLayerFlag);
reed@google.comb93ba452014-03-10 19:47:58 +00001077#else
1078 return true;
1079#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001080}
1081
reed4960eee2015-12-18 07:09:18 -08001082bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -07001083 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001084 SkIRect clipBounds;
1085 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001086 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +00001087 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001088
reed96e657d2015-03-10 17:30:07 -07001089 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
1090
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001091 if (imageFilter) {
senorblancoe5e79842016-03-21 14:51:59 -07001092 clipBounds = imageFilter->filterBounds(clipBounds, ctm);
senorblancodb64af32015-12-09 10:11:43 -08001093 if (bounds && !imageFilter->canComputeFastBounds()) {
1094 bounds = nullptr;
1095 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001096 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001097 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -07001098 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001099 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +00001100
reed96e657d2015-03-10 17:30:07 -07001101 ctm.mapRect(&r, *bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001102 r.roundOut(&ir);
1103 // early exit if the layer's bounds are clipped out
1104 if (!ir.intersect(clipBounds)) {
reed4960eee2015-12-18 07:09:18 -08001105 if (BoundsAffectsClip(saveLayerFlags)) {
reed9b3aa542015-03-11 08:47:12 -07001106 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001107 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001108 }
junov@chromium.orga907ac32012-02-24 21:54:07 +00001109 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001110 }
1111 } else { // no user bounds, so just use the clip
1112 ir = clipBounds;
1113 }
reed180aec42015-03-11 10:39:04 -07001114 SkASSERT(!ir.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001115
reed4960eee2015-12-18 07:09:18 -08001116 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -07001117 // Simplify the current clips since they will be applied properly during restore()
reed9b3aa542015-03-11 08:47:12 -07001118 fCachedLocalClipBoundsDirty = true;
reed687fa1c2015-04-07 08:00:56 -07001119 fClipStack->clipDevRect(ir, SkRegion::kReplace_Op);
reed180aec42015-03-11 10:39:04 -07001120 fMCRec->fRasterClip.setRect(ir);
junov@chromium.orga907ac32012-02-24 21:54:07 +00001121 }
1122
1123 if (intersection) {
1124 *intersection = ir;
1125 }
1126 return true;
1127}
1128
reed4960eee2015-12-18 07:09:18 -08001129
reed4960eee2015-12-18 07:09:18 -08001130int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
1131 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001132}
1133
reed70ee31b2015-12-10 13:44:45 -08001134int SkCanvas::saveLayerPreserveLCDTextRequests(const SkRect* bounds, const SkPaint* paint) {
reed4960eee2015-12-18 07:09:18 -08001135 return this->saveLayer(SaveLayerRec(bounds, paint, kPreserveLCDText_SaveLayerFlag));
1136}
1137
1138int SkCanvas::saveLayer(const SaveLayerRec& origRec) {
1139 SaveLayerRec rec(origRec);
1140 if (gIgnoreSaveLayerBounds) {
1141 rec.fBounds = nullptr;
1142 }
1143 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
1144 fSaveCount += 1;
1145 this->internalSaveLayer(rec, strategy);
1146 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -08001147}
1148
reedbfd5f172016-01-07 11:28:08 -08001149static void draw_filter_into_device(SkBaseDevice* src, const SkImageFilter* filter,
1150 SkBaseDevice* dst, const SkMatrix& ctm) {
robertphillips7354a4b2015-12-16 05:08:27 -08001151
1152 SkBitmap srcBM;
1153
1154#if SK_SUPPORT_GPU
robertphillips175dd9b2016-04-28 14:32:04 -07001155 // TODO: remove this virtual usage of accessRenderTarget! It is preventing
1156 // removal of the virtual on SkBaseDevice.
robertphillips7354a4b2015-12-16 05:08:27 -08001157 GrRenderTarget* srcRT = src->accessRenderTarget();
1158 if (srcRT && !srcRT->asTexture() && dst->accessRenderTarget()) {
1159 // When both the src & the dst are on the gpu but the src doesn't have a texture,
1160 // we create a temporary texture for the draw.
1161 // TODO: we should actually only copy the portion of the source needed to apply the image
1162 // filter
1163 GrContext* context = srcRT->getContext();
bsalomon5ec26ae2016-02-25 08:33:02 -08001164 SkAutoTUnref<GrTexture> tex(context->textureProvider()->createTexture(srcRT->desc(),
1165 SkBudgeted::kYes));
robertphillips7354a4b2015-12-16 05:08:27 -08001166
1167 context->copySurface(tex, srcRT);
1168
1169 GrWrapTextureInBitmap(tex, src->width(), src->height(), src->isOpaque(), &srcBM);
1170 } else
1171#endif
1172 {
1173 srcBM = src->accessBitmap(false);
1174 }
1175
1176 SkCanvas c(dst);
1177
1178 SkPaint p;
robertphillips372177e2016-03-30 07:32:28 -07001179 p.setImageFilter(filter->makeWithLocalMatrix(ctm));
reedbfd5f172016-01-07 11:28:08 -08001180 const SkScalar x = SkIntToScalar(src->getOrigin().x());
1181 const SkScalar y = SkIntToScalar(src->getOrigin().y());
1182 c.drawBitmap(srcBM, x, y, &p);
robertphillips7354a4b2015-12-16 05:08:27 -08001183}
reed70ee31b2015-12-10 13:44:45 -08001184
reed129ed1c2016-02-22 06:42:31 -08001185static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, bool isOpaque,
1186 const SkPaint* paint) {
1187 // need to force L32 for now if we have an image filter. Once filters support other colortypes
1188 // e.g. sRGB or F16, we can remove this check
brianosman52ede1d2016-06-20 08:25:02 -07001189 // SRGBTODO: Can we remove this check now?
reed129ed1c2016-02-22 06:42:31 -08001190 const bool hasImageFilter = paint && paint->getImageFilter();
1191
1192 SkAlphaType alphaType = isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
1193 if ((prev.bytesPerPixel() < 4) || hasImageFilter) {
1194 // force to L32
1195 return SkImageInfo::MakeN32(w, h, alphaType);
1196 } else {
1197 // keep the same characteristics as the prev
brianosman52ede1d2016-06-20 08:25:02 -07001198 return SkImageInfo::Make(w, h, prev.colorType(), alphaType, sk_ref_sp(prev.colorSpace()));
reed129ed1c2016-02-22 06:42:31 -08001199 }
1200}
1201
reed4960eee2015-12-18 07:09:18 -08001202void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
1203 const SkRect* bounds = rec.fBounds;
1204 const SkPaint* paint = rec.fPaint;
1205 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1206
reed@google.comb93ba452014-03-10 19:47:58 +00001207#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001208 saveLayerFlags &= ~kDontClipToLayer_PrivateSaveLayerFlag;
reed@google.comb93ba452014-03-10 19:47:58 +00001209#endif
1210
reed8c30a812016-04-20 16:36:51 -07001211 SkLazyPaint lazyP;
1212 SkImageFilter* imageFilter = paint ? paint->getImageFilter() : NULL;
1213 SkMatrix stashedMatrix = fMCRec->fMatrix;
reed8c30a812016-04-20 16:36:51 -07001214 SkMatrix remainder;
1215 SkSize scale;
1216 /*
1217 * ImageFilters (so far) do not correctly handle matrices (CTM) that contain rotation/skew/etc.
1218 * but they do handle scaling. To accommodate this, we do the following:
1219 *
1220 * 1. Stash off the current CTM
1221 * 2. Decompose the CTM into SCALE and REMAINDER
1222 * 3. Wack the CTM to be just SCALE, and wrap the imagefilter with a MatrixImageFilter that
1223 * contains the REMAINDER
1224 * 4. Proceed as usual, allowing the client to draw into the layer (now with a scale-only CTM)
1225 * 5. During restore, we process the MatrixImageFilter, which applies REMAINDER to the output
1226 * of the original imagefilter, and draw that (via drawSprite)
1227 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1228 *
1229 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1230 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1231 */
reed96a04f32016-04-25 09:25:15 -07001232 if (imageFilter && !stashedMatrix.isScaleTranslate() && !imageFilter->canHandleComplexCTM() &&
reed8c30a812016-04-20 16:36:51 -07001233 stashedMatrix.decomposeScale(&scale, &remainder))
1234 {
1235 // We will restore the matrix (which we are overwriting here) in restore via fStashedMatrix
1236 this->internalSetMatrix(SkMatrix::MakeScale(scale.width(), scale.height()));
1237 SkPaint* p = lazyP.set(*paint);
1238 p->setImageFilter(SkImageFilter::MakeMatrixFilter(remainder,
1239 SkFilterQuality::kLow_SkFilterQuality,
1240 sk_ref_sp(imageFilter)));
1241 imageFilter = p->getImageFilter();
1242 paint = p;
1243 }
reed8c30a812016-04-20 16:36:51 -07001244
junov@chromium.orga907ac32012-02-24 21:54:07 +00001245 // do this before we create the layer. We don't call the public save() since
1246 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001247 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001248
1249 fDeviceCMDirty = true;
1250
1251 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001252 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
reed2ff1fce2014-12-11 07:07:37 -08001253 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001254 }
1255
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001256 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1257 // the clipRectBounds() call above?
1258 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001259 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001260 }
1261
reed4960eee2015-12-18 07:09:18 -08001262 bool isOpaque = SkToBool(saveLayerFlags & kIsOpaque_SaveLayerFlag);
reed8dc0ccb2015-03-20 06:32:52 -07001263 SkPixelGeometry geo = fProps.pixelGeometry();
1264 if (paint) {
reed76033be2015-03-14 10:54:31 -07001265 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001266 if (paint->getImageFilter() || paint->getColorFilter()) {
reed76033be2015-03-14 10:54:31 -07001267 isOpaque = false;
reed8dc0ccb2015-03-20 06:32:52 -07001268 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001269 }
1270 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001271
robertphillips5139e502016-07-19 05:10:40 -07001272 SkBaseDevice* priorDevice = this->getTopDevice();
1273 if (!priorDevice) {
reedb2db8982014-11-13 12:41:02 -08001274 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001275 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001276 }
reedb2db8982014-11-13 12:41:02 -08001277
robertphillips5139e502016-07-19 05:10:40 -07001278 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), isOpaque,
reed129ed1c2016-02-22 06:42:31 -08001279 paint);
1280
robertphillips5139e502016-07-19 05:10:40 -07001281 SkAutoTUnref<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001282 {
reed70ee31b2015-12-10 13:44:45 -08001283 const bool preserveLCDText = kOpaque_SkAlphaType == info.alphaType() ||
reed4960eee2015-12-18 07:09:18 -08001284 (saveLayerFlags & kPreserveLCDText_SaveLayerFlag);
reeddaa57bf2015-05-15 10:39:17 -07001285 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
reed70ee31b2015-12-10 13:44:45 -08001286 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
reedcd4051e2016-07-15 09:41:26 -07001287 preserveLCDText);
robertphillips5139e502016-07-19 05:10:40 -07001288 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1289 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001290 SkErrorInternals::SetError(kInternalError_SkError,
1291 "Unable to create device for layer.");
1292 return;
reed61f501f2015-04-29 08:34:00 -07001293 }
bungeman@google.come25c6842011-08-17 14:53:54 +00001294 }
robertphillips5139e502016-07-19 05:10:40 -07001295 newDevice->setOrigin(ir.fLeft, ir.fTop);
robertphillips7354a4b2015-12-16 05:08:27 -08001296
reedbfd5f172016-01-07 11:28:08 -08001297 if (rec.fBackdrop) {
robertphillips5139e502016-07-19 05:10:40 -07001298 draw_filter_into_device(priorDevice, rec.fBackdrop, newDevice, fMCRec->fMatrix);
robertphillips7354a4b2015-12-16 05:08:27 -08001299 }
1300
robertphillips5139e502016-07-19 05:10:40 -07001301 DeviceCM* layer = new DeviceCM(newDevice, paint, this, fConservativeRasterClip, stashedMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001302
1303 layer->fNext = fMCRec->fTopLayer;
1304 fMCRec->fLayer = layer;
1305 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reed@android.com8a1c16f2008-12-17 15:59:43 +00001306}
1307
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001308int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001309 if (0xFF == alpha) {
1310 return this->saveLayer(bounds, nullptr);
1311 } else {
1312 SkPaint tmpPaint;
1313 tmpPaint.setAlpha(alpha);
1314 return this->saveLayer(bounds, &tmpPaint);
1315 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001316}
1317
reed@android.com8a1c16f2008-12-17 15:59:43 +00001318void SkCanvas::internalRestore() {
1319 SkASSERT(fMCStack.count() != 0);
1320
1321 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001322 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001323
reed687fa1c2015-04-07 08:00:56 -07001324 fClipStack->restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001325
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001326 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001327 DeviceCM* layer = fMCRec->fLayer; // may be null
1328 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001329 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001330
1331 // now do the normal restore()
1332 fMCRec->~MCRec(); // balanced in save()
1333 fMCStack.pop_back();
1334 fMCRec = (MCRec*)fMCStack.back();
1335
1336 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1337 since if we're being recorded, we don't want to record this (the
1338 recorder will have already recorded the restore).
1339 */
bsalomon49f085d2014-09-05 13:34:00 -07001340 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001341 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001342 const SkIPoint& origin = layer->fDevice->getOrigin();
reed7503d602016-07-15 14:23:29 -07001343 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(), layer->fPaint);
reed8c30a812016-04-20 16:36:51 -07001344 // restore what we smashed in internalSaveLayer
1345 fMCRec->fMatrix = layer->fStashedMatrix;
reed@google.com8926b162012-03-23 15:36:36 +00001346 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001347 fDeviceCMDirty = true;
halcanary385fe4d2015-08-26 13:07:48 -07001348 delete layer;
reedb679ca82015-04-07 04:40:48 -07001349 } else {
1350 // we're at the root
reeda499f902015-05-01 09:34:31 -07001351 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001352 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001353 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001354 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001355 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001356}
1357
reede8f30622016-03-23 18:59:25 -07001358sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001359 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001360 props = &fProps;
1361 }
1362 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001363}
1364
reede8f30622016-03-23 18:59:25 -07001365sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001366 SkBaseDevice* dev = this->getDevice();
reede8f30622016-03-23 18:59:25 -07001367 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001368}
1369
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001370SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001371 return this->onImageInfo();
1372}
1373
1374SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001375 SkBaseDevice* dev = this->getDevice();
1376 if (dev) {
1377 return dev->imageInfo();
1378 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001379 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001380 }
1381}
1382
brianosman898235c2016-04-06 07:38:23 -07001383bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001384 return this->onGetProps(props);
1385}
1386
1387bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001388 SkBaseDevice* dev = this->getDevice();
1389 if (dev) {
1390 if (props) {
1391 *props = fProps;
1392 }
1393 return true;
1394 } else {
1395 return false;
1396 }
1397}
1398
reed6ceeebd2016-03-09 14:26:26 -08001399#ifdef SK_SUPPORT_LEGACY_PEEKPIXELS_PARMS
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001400const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
reed884e97c2015-05-26 11:31:54 -07001401 SkPixmap pmap;
reed6ceeebd2016-03-09 14:26:26 -08001402 if (this->peekPixels(&pmap)) {
1403 if (info) {
1404 *info = pmap.info();
1405 }
1406 if (rowBytes) {
1407 *rowBytes = pmap.rowBytes();
1408 }
1409 return pmap.addr();
reed884e97c2015-05-26 11:31:54 -07001410 }
reed6ceeebd2016-03-09 14:26:26 -08001411 return nullptr;
1412}
1413#endif
1414
1415bool SkCanvas::peekPixels(SkPixmap* pmap) {
1416 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001417}
1418
reed884e97c2015-05-26 11:31:54 -07001419bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001420 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001421 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001422}
1423
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001424void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001425 SkPixmap pmap;
1426 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001427 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001428 }
1429 if (info) {
1430 *info = pmap.info();
1431 }
1432 if (rowBytes) {
1433 *rowBytes = pmap.rowBytes();
1434 }
1435 if (origin) {
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001436 *origin = this->getTopDevice(false)->getOrigin();
1437 }
reed884e97c2015-05-26 11:31:54 -07001438 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001439}
1440
reed884e97c2015-05-26 11:31:54 -07001441bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001442 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001443 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001444}
1445
reed@android.com8a1c16f2008-12-17 15:59:43 +00001446/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001447
reed7503d602016-07-15 14:23:29 -07001448void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001449 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001450 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001451 paint = &tmp;
1452 }
reed@google.com4b226022011-01-11 18:32:13 +00001453
reed@google.com8926b162012-03-23 15:36:36 +00001454 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001455 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001456 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001457 paint = &looper.paint();
1458 SkImageFilter* filter = paint->getImageFilter();
1459 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
senorblancof35566e2016-04-18 10:32:02 -07001460 if (filter) {
robertphillips4418dba2016-03-07 12:45:14 -08001461 const SkBitmap& srcBM = srcDev->accessBitmap(false);
reed1eca1162016-04-25 12:29:38 -07001462 dstDev->drawSpriteWithFilter(iter, srcBM, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001463 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001464 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001465 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001466 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001467 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001468}
1469
reed32704672015-12-16 08:27:10 -08001470/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001471
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001472void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001473 SkMatrix m;
1474 m.setTranslate(dx, dy);
1475 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001476}
1477
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001478void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001479 SkMatrix m;
1480 m.setScale(sx, sy);
1481 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001482}
1483
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001484void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001485 SkMatrix m;
1486 m.setRotate(degrees);
1487 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001488}
1489
bungeman7438bfc2016-07-12 15:01:19 -07001490void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1491 SkMatrix m;
1492 m.setRotate(degrees, px, py);
1493 this->concat(m);
1494}
1495
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001496void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001497 SkMatrix m;
1498 m.setSkew(sx, sy);
1499 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001500}
1501
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001502void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001503 if (matrix.isIdentity()) {
1504 return;
1505 }
1506
reed2ff1fce2014-12-11 07:07:37 -08001507 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001508 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001509 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001510 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001511
1512 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001513}
1514
reed8c30a812016-04-20 16:36:51 -07001515void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001516 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001517 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001518 fMCRec->fMatrix = matrix;
reed8c30a812016-04-20 16:36:51 -07001519}
1520
1521void SkCanvas::setMatrix(const SkMatrix& matrix) {
1522 this->checkForDeferredSave();
1523 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001524 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001525}
1526
reed@android.com8a1c16f2008-12-17 15:59:43 +00001527void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001528 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001529}
1530
vjiaoblacke5de1302016-07-13 14:05:28 -07001531void SkCanvas::translateZ(SkScalar z) {
1532 this->checkForDeferredSave();
1533 this->fMCRec->fCurDrawDepth += z;
1534 this->didTranslateZ(z);
1535}
1536
1537SkScalar SkCanvas::getZ() const {
1538 return this->fMCRec->fCurDrawDepth;
1539}
1540
reed@android.com8a1c16f2008-12-17 15:59:43 +00001541//////////////////////////////////////////////////////////////////////////////
1542
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001543void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed2d1afab2016-06-29 14:33:11 -07001544 if (!fAllowSoftClip) {
1545 doAA = false;
1546 }
1547
1548#ifdef SK_SUPPORT_PRECHECK_CLIPRECT
1549 // Check if we can quick-accept the clip call (and do nothing)
1550 //
reed74467162016-06-30 08:15:35 -07001551 if (SkRegion::kIntersect_Op == op && !doAA && fMCRec->fMatrix.isScaleTranslate()) {
reed6092b6e2016-07-10 11:45:34 -07001552 SkRect devR;
1553 fMCRec->fMatrix.mapRectScaleTranslate(&devR, rect);
reed2d1afab2016-06-29 14:33:11 -07001554 // NOTE: this check is CTM specific, since we might round differently with a different
1555 // CTM. Thus this is only 100% reliable if there is not global CTM scale to be
1556 // applied later (i.e. if this is going into a picture).
1557 if (devR.round().contains(fMCRec->fRasterClip.getBounds())) {
1558#if 0
1559 SkDebugf("ignored clipRect [%g %g %g %g]\n",
1560 rect.left(), rect.top(), rect.right(), rect.bottom());
1561#endif
1562 return;
1563 }
1564 }
1565#endif
1566
reed2ff1fce2014-12-11 07:07:37 -08001567 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001568 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1569 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001570}
1571
1572void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001573#ifdef SK_ENABLE_CLIP_QUICKREJECT
1574 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001575 if (fMCRec->fRasterClip.isEmpty()) {
reed2d1afab2016-06-29 14:33:11 -07001576 return;
reed@google.comda17f752012-08-16 18:27:05 +00001577 }
1578
reed@google.com3b3e8952012-08-16 20:53:31 +00001579 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001580 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001581 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001582
reed687fa1c2015-04-07 08:00:56 -07001583 fClipStack->clipEmpty();
reed2d1afab2016-06-29 14:33:11 -07001584 (void)fMCRec->fRasterClip.setEmpty();
1585 return;
reed@google.comda17f752012-08-16 18:27:05 +00001586 }
1587 }
1588#endif
1589
reed74467162016-06-30 08:15:35 -07001590 const bool isScaleTrans = fMCRec->fMatrix.isScaleTranslate();
reedc64eff52015-11-21 12:39:45 -08001591 SkRect devR;
reed74467162016-06-30 08:15:35 -07001592 if (isScaleTrans) {
reed6092b6e2016-07-10 11:45:34 -07001593 fMCRec->fMatrix.mapRectScaleTranslate(&devR, rect);
reedc64eff52015-11-21 12:39:45 -08001594 }
bsalomonac8cabd2015-11-20 18:53:07 -08001595
reed2d1afab2016-06-29 14:33:11 -07001596#ifndef SK_SUPPORT_PRECHECK_CLIPRECT
reedc64eff52015-11-21 12:39:45 -08001597 if (SkRegion::kIntersect_Op == op &&
1598 kHard_ClipEdgeStyle == edgeStyle
reed74467162016-06-30 08:15:35 -07001599 && isScaleTrans)
reedc64eff52015-11-21 12:39:45 -08001600 {
1601 if (devR.round().contains(fMCRec->fRasterClip.getBounds())) {
1602#if 0
1603 SkDebugf("------- ignored clipRect [%g %g %g %g]\n",
1604 rect.left(), rect.top(), rect.right(), rect.bottom());
1605#endif
1606 return;
1607 }
1608 }
reed2d1afab2016-06-29 14:33:11 -07001609#endif
reedc64eff52015-11-21 12:39:45 -08001610
1611 AutoValidateClip avc(this);
1612
1613 fDeviceCMDirty = true;
1614 fCachedLocalClipBoundsDirty = true;
1615
reed74467162016-06-30 08:15:35 -07001616 if (isScaleTrans) {
reedc64eff52015-11-21 12:39:45 -08001617 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1618 fClipStack->clipDevRect(devR, op, isAA);
senorblancoafc7cce2016-02-02 18:44:15 -08001619 fMCRec->fRasterClip.op(devR, this->getTopLayerBounds(), op, isAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001620 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001621 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001622 // and clip against that, since it can handle any matrix. However, to
1623 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1624 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001625 SkPath path;
1626
1627 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001628 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001629 }
1630}
1631
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001632void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001633 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001634 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001635 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001636 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1637 } else {
1638 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001639 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001640}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001641
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001642void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001643 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001644 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001645 AutoValidateClip avc(this);
1646
1647 fDeviceCMDirty = true;
1648 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001649 if (!fAllowSoftClip) {
1650 edgeStyle = kHard_ClipEdgeStyle;
1651 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001652
reed687fa1c2015-04-07 08:00:56 -07001653 fClipStack->clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001654
senorblancoafc7cce2016-02-02 18:44:15 -08001655 fMCRec->fRasterClip.op(transformedRRect, this->getTopLayerBounds(), op,
robertphillips125f19a2015-11-23 09:00:05 -08001656 kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001657 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001658 }
1659
1660 SkPath path;
1661 path.addRRect(rrect);
1662 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001663 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001664}
1665
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001666void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001667 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001668 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001669
1670 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1671 SkRect r;
1672 if (path.isRect(&r)) {
1673 this->onClipRect(r, op, edgeStyle);
1674 return;
1675 }
1676 SkRRect rrect;
1677 if (path.isOval(&r)) {
1678 rrect.setOval(r);
1679 this->onClipRRect(rrect, op, edgeStyle);
1680 return;
1681 }
1682 if (path.isRRect(&rrect)) {
1683 this->onClipRRect(rrect, op, edgeStyle);
1684 return;
1685 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001686 }
robertphillips39f05382015-11-24 09:30:12 -08001687
1688 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001689}
1690
1691void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001692#ifdef SK_ENABLE_CLIP_QUICKREJECT
1693 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001694 if (fMCRec->fRasterClip.isEmpty()) {
reed2d1afab2016-06-29 14:33:11 -07001695 return;
reed@google.comda17f752012-08-16 18:27:05 +00001696 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001697
reed@google.com3b3e8952012-08-16 20:53:31 +00001698 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001699 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001700 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001701
reed687fa1c2015-04-07 08:00:56 -07001702 fClipStack->clipEmpty();
reed2d1afab2016-06-29 14:33:11 -07001703 (void)fMCRec->fRasterClip.setEmpty();
1704 return;
reed@google.comda17f752012-08-16 18:27:05 +00001705 }
1706 }
1707#endif
1708
reed@google.com5c3d1472011-02-22 19:12:23 +00001709 AutoValidateClip avc(this);
1710
reed@android.com8a1c16f2008-12-17 15:59:43 +00001711 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001712 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001713 if (!fAllowSoftClip) {
1714 edgeStyle = kHard_ClipEdgeStyle;
1715 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001716
1717 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001718 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001719
reed@google.comfe701122011-11-08 19:41:23 +00001720 // Check if the transfomation, or the original path itself
1721 // made us empty. Note this can also happen if we contained NaN
1722 // values. computing the bounds detects this, and will set our
1723 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1724 if (devPath.getBounds().isEmpty()) {
1725 // resetting the path will remove any NaN or other wanky values
1726 // that might upset our scan converter.
1727 devPath.reset();
1728 }
1729
reed@google.com5c3d1472011-02-22 19:12:23 +00001730 // if we called path.swap() we could avoid a deep copy of this path
reed687fa1c2015-04-07 08:00:56 -07001731 fClipStack->clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001732
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001733 if (fAllowSimplifyClip) {
fmalita1a481fe2015-02-04 07:39:34 -08001734 bool clipIsAA = getClipStack()->asPath(&devPath);
1735 if (clipIsAA) {
1736 edgeStyle = kSoft_ClipEdgeStyle;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001737 }
fmalita1a481fe2015-02-04 07:39:34 -08001738
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001739 op = SkRegion::kReplace_Op;
1740 }
1741
senorblancoafc7cce2016-02-02 18:44:15 -08001742 fMCRec->fRasterClip.op(devPath, this->getTopLayerBounds(), op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001743}
1744
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001745void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed2ff1fce2014-12-11 07:07:37 -08001746 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001747 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001748}
1749
1750void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001751 AutoValidateClip avc(this);
1752
reed@android.com8a1c16f2008-12-17 15:59:43 +00001753 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001754 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001755
reed@google.com5c3d1472011-02-22 19:12:23 +00001756 // todo: signal fClipStack that we have a region, and therefore (I guess)
1757 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001758 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001759
reed1f836ee2014-07-07 07:49:34 -07001760 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001761}
1762
reed@google.com819c9212011-02-23 18:56:55 +00001763#ifdef SK_DEBUG
1764void SkCanvas::validateClip() const {
1765 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001766 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001767 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001768 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001769 return;
1770 }
1771
reed@google.com819c9212011-02-23 18:56:55 +00001772 SkIRect ir;
1773 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001774 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001775
reed687fa1c2015-04-07 08:00:56 -07001776 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001777 const SkClipStack::Element* element;
halcanary96fcdcc2015-08-27 07:41:13 -07001778 while ((element = iter.next()) != nullptr) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001779 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001780 case SkClipStack::Element::kRect_Type:
1781 element->getRect().round(&ir);
1782 tmpClip.op(ir, element->getOp());
1783 break;
1784 case SkClipStack::Element::kEmpty_Type:
1785 tmpClip.setEmpty();
1786 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001787 default: {
1788 SkPath path;
1789 element->asPath(&path);
senorblancoafc7cce2016-02-02 18:44:15 -08001790 tmpClip.op(path, this->getTopLayerBounds(), element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001791 break;
1792 }
reed@google.com819c9212011-02-23 18:56:55 +00001793 }
1794 }
reed@google.com819c9212011-02-23 18:56:55 +00001795}
1796#endif
1797
reed@google.com90c07ea2012-04-13 13:50:27 +00001798void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001799 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001800 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001801
halcanary96fcdcc2015-08-27 07:41:13 -07001802 while ((element = iter.next()) != nullptr) {
fmalitac3b589a2014-06-05 12:40:07 -07001803 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001804 }
1805}
1806
reed@google.com5c3d1472011-02-22 19:12:23 +00001807///////////////////////////////////////////////////////////////////////////////
1808
reed@google.com754de5f2014-02-24 19:38:20 +00001809bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001810 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001811}
1812
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001813bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001814 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001815}
1816
reed@google.com3b3e8952012-08-16 20:53:31 +00001817bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001818 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001819 return true;
1820
reed1f836ee2014-07-07 07:49:34 -07001821 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001822 return true;
1823 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001824
reed1f836ee2014-07-07 07:49:34 -07001825 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001826 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001827 fMCRec->fMatrix.mapRect(&dst, rect);
reedb07a94f2014-11-19 05:03:18 -08001828 return !SkIRect::Intersects(dst.roundOut(), fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001829 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001830 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001831
reed@android.coma380ae42009-07-21 01:17:02 +00001832 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001833 // TODO: should we use | instead, or compare all 4 at once?
1834 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001835 return true;
1836 }
reed@google.comc0784db2013-12-13 21:16:12 +00001837 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001838 return true;
1839 }
1840 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001841 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001842}
1843
reed@google.com3b3e8952012-08-16 20:53:31 +00001844bool SkCanvas::quickReject(const SkPath& path) const {
1845 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001846}
1847
reed@google.com3b3e8952012-08-16 20:53:31 +00001848bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001849 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001850 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001851 return false;
1852 }
1853
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001854 SkMatrix inverse;
1855 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001856 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001857 if (bounds) {
1858 bounds->setEmpty();
1859 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001860 return false;
1861 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001862
bsalomon49f085d2014-09-05 13:34:00 -07001863 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001864 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001865 // adjust it outwards in case we are antialiasing
1866 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001867
reed@google.com8f4d2302013-12-17 16:44:46 +00001868 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1869 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001870 inverse.mapRect(bounds, r);
1871 }
1872 return true;
1873}
1874
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001875bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001876 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001877 if (clip.isEmpty()) {
1878 if (bounds) {
1879 bounds->setEmpty();
1880 }
1881 return false;
1882 }
1883
bsalomon49f085d2014-09-05 13:34:00 -07001884 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001885 *bounds = clip.getBounds();
1886 }
1887 return true;
1888}
1889
reed@android.com8a1c16f2008-12-17 15:59:43 +00001890const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001891 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001892}
1893
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001894const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001895 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001896}
1897
robertphillips175dd9b2016-04-28 14:32:04 -07001898GrDrawContext* SkCanvas::internal_private_accessTopLayerDrawContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001899 SkBaseDevice* dev = this->getTopDevice();
robertphillips175dd9b2016-04-28 14:32:04 -07001900 return dev ? dev->accessDrawContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001901}
1902
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001903GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001904 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001905 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001906}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001907
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001908void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1909 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001910 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001911 if (outer.isEmpty()) {
1912 return;
1913 }
1914 if (inner.isEmpty()) {
1915 this->drawRRect(outer, paint);
1916 return;
1917 }
1918
1919 // We don't have this method (yet), but technically this is what we should
1920 // be able to assert...
1921 // SkASSERT(outer.contains(inner));
1922 //
1923 // For now at least check for containment of bounds
1924 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1925
1926 this->onDrawDRRect(outer, inner, paint);
1927}
1928
reed41af9662015-01-05 07:49:08 -08001929// These need to stop being virtual -- clients need to override the onDraw... versions
1930
1931void SkCanvas::drawPaint(const SkPaint& paint) {
1932 this->onDrawPaint(paint);
1933}
1934
1935void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1936 this->onDrawRect(r, paint);
1937}
1938
1939void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1940 this->onDrawOval(r, paint);
1941}
1942
1943void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1944 this->onDrawRRect(rrect, paint);
1945}
1946
1947void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1948 this->onDrawPoints(mode, count, pts, paint);
1949}
1950
1951void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
1952 const SkPoint texs[], const SkColor colors[], SkXfermode* xmode,
1953 const uint16_t indices[], int indexCount, const SkPaint& paint) {
1954 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
1955 indices, indexCount, paint);
1956}
1957
1958void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1959 this->onDrawPath(path, paint);
1960}
1961
reeda85d4d02015-05-06 12:56:48 -07001962void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001963 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001964 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001965}
1966
reede47829b2015-08-06 10:02:53 -07001967void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1968 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001969 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001970 if (dst.isEmpty() || src.isEmpty()) {
1971 return;
1972 }
1973 this->onDrawImageRect(image, &src, dst, paint, constraint);
1974}
reed41af9662015-01-05 07:49:08 -08001975
reed84984ef2015-07-17 07:09:43 -07001976void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1977 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001978 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001979 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001980}
1981
reede47829b2015-08-06 10:02:53 -07001982void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
1983 SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001984 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001985 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
1986 constraint);
1987}
reede47829b2015-08-06 10:02:53 -07001988
reed4c21dc52015-06-25 12:32:03 -07001989void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1990 const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001991 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07001992 if (dst.isEmpty()) {
1993 return;
1994 }
1995 if (!SkNinePatchIter::Valid(image->width(), image->height(), center)) {
reede47829b2015-08-06 10:02:53 -07001996 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001997 }
1998 this->onDrawImageNine(image, center, dst, paint);
1999}
2000
reed41af9662015-01-05 07:49:08 -08002001void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07002002 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002003 return;
2004 }
reed41af9662015-01-05 07:49:08 -08002005 this->onDrawBitmap(bitmap, dx, dy, paint);
2006}
2007
reede47829b2015-08-06 10:02:53 -07002008void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07002009 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002010 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07002011 return;
2012 }
reede47829b2015-08-06 10:02:53 -07002013 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08002014}
2015
reed84984ef2015-07-17 07:09:43 -07002016void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2017 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002018 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002019}
2020
reede47829b2015-08-06 10:02:53 -07002021void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2022 SrcRectConstraint constraint) {
2023 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2024 constraint);
2025}
reede47829b2015-08-06 10:02:53 -07002026
reed41af9662015-01-05 07:49:08 -08002027void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2028 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07002029 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002030 return;
2031 }
reed4c21dc52015-06-25 12:32:03 -07002032 if (!SkNinePatchIter::Valid(bitmap.width(), bitmap.height(), center)) {
reeda5517e22015-07-14 10:54:12 -07002033 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002034 }
reed41af9662015-01-05 07:49:08 -08002035 this->onDrawBitmapNine(bitmap, center, dst, paint);
2036}
2037
reed71c3c762015-06-24 10:29:17 -07002038void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2039 const SkColor colors[], int count, SkXfermode::Mode mode,
2040 const SkRect* cull, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002041 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002042 if (count <= 0) {
2043 return;
2044 }
2045 SkASSERT(atlas);
2046 SkASSERT(xform);
2047 SkASSERT(tex);
2048 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
2049}
2050
reedf70b5312016-03-04 16:36:20 -08002051void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2052 if (key) {
2053 this->onDrawAnnotation(rect, key, value);
2054 }
2055}
2056
reede47829b2015-08-06 10:02:53 -07002057void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2058 const SkPaint* paint, SrcRectConstraint constraint) {
2059 if (src) {
2060 this->drawImageRect(image, *src, dst, paint, constraint);
2061 } else {
2062 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2063 dst, paint, constraint);
2064 }
2065}
2066void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2067 const SkPaint* paint, SrcRectConstraint constraint) {
2068 if (src) {
2069 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2070 } else {
2071 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2072 dst, paint, constraint);
2073 }
2074}
2075
tomhudsoncb3bd182016-05-18 07:24:16 -07002076void SkCanvas::temporary_internal_describeTopLayer(SkMatrix* matrix, SkIRect* clip_bounds) {
2077 SkIRect layer_bounds = this->getTopLayerBounds();
2078 if (matrix) {
2079 *matrix = this->getTotalMatrix();
2080 matrix->preTranslate(-layer_bounds.left(), -layer_bounds.top());
2081 }
2082 if (clip_bounds) {
2083 this->getClipDeviceBounds(clip_bounds);
2084 clip_bounds->offset(-layer_bounds.left(), -layer_bounds.top());
2085 }
2086}
2087
reed@android.com8a1c16f2008-12-17 15:59:43 +00002088//////////////////////////////////////////////////////////////////////////////
2089// These are the virtual drawing methods
2090//////////////////////////////////////////////////////////////////////////////
2091
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002092void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002093 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002094 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2095 }
2096}
2097
reed41af9662015-01-05 07:49:08 -08002098void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002099 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002100 this->internalDrawPaint(paint);
2101}
2102
2103void SkCanvas::internalDrawPaint(const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002104 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002105
2106 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002107 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002108 }
2109
reed@google.com4e2b3d32011-04-07 14:18:59 +00002110 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002111}
2112
reed41af9662015-01-05 07:49:08 -08002113void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2114 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002115 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002116 if ((long)count <= 0) {
2117 return;
2118 }
2119
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002120 SkRect r, storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002121 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002122 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002123 // special-case 2 points (common for drawing a single line)
2124 if (2 == count) {
2125 r.set(pts[0], pts[1]);
2126 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00002127 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002128 }
senorblanco87e066e2015-10-28 11:23:36 -07002129 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2130 return;
2131 }
2132 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002133 }
reed@google.coma584aed2012-05-16 14:06:02 +00002134
halcanary96fcdcc2015-08-27 07:41:13 -07002135 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002136
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002137 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002138
reed@android.com8a1c16f2008-12-17 15:59:43 +00002139 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002140 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002141 }
reed@google.com4b226022011-01-11 18:32:13 +00002142
reed@google.com4e2b3d32011-04-07 14:18:59 +00002143 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002144}
2145
reed41af9662015-01-05 07:49:08 -08002146void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002147 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002148 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002149 const SkRect* bounds = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002150 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08002151 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
2152 // To prevent accidental rejecting at this stage, we have to sort it before we check.
2153 SkRect tmp(r);
2154 tmp.sort();
2155
senorblanco87e066e2015-10-28 11:23:36 -07002156 if (this->quickReject(paint.computeFastBounds(tmp, &storage))) {
2157 return;
2158 }
2159 bounds = &r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002160 }
reed@google.com4b226022011-01-11 18:32:13 +00002161
reedc83a2972015-07-16 07:40:45 -07002162 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002163
2164 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002165 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002166 }
2167
reed@google.com4e2b3d32011-04-07 14:18:59 +00002168 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002169}
2170
reed41af9662015-01-05 07:49:08 -08002171void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002172 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002173 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002174 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002175 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002176 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2177 return;
2178 }
2179 bounds = &oval;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002180 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002181
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002182 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002183
2184 while (iter.next()) {
2185 iter.fDevice->drawOval(iter, oval, looper.paint());
2186 }
2187
2188 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002189}
2190
reed41af9662015-01-05 07:49:08 -08002191void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002192 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002193 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002194 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002195 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002196 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2197 return;
2198 }
2199 bounds = &rrect.getBounds();
reed@google.com4ed0fb72012-12-12 20:48:18 +00002200 }
2201
2202 if (rrect.isRect()) {
2203 // call the non-virtual version
2204 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002205 return;
2206 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002207 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002208 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2209 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002210 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002211
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002212 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002213
2214 while (iter.next()) {
2215 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2216 }
2217
2218 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002219}
2220
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002221void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2222 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002223 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002224 const SkRect* bounds = nullptr;
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002225 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002226 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2227 return;
2228 }
2229 bounds = &outer.getBounds();
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002230 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002231
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002232 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002233
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002234 while (iter.next()) {
2235 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2236 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002237
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002238 LOOPER_END
2239}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002240
reed41af9662015-01-05 07:49:08 -08002241void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002242 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002243 if (!path.isFinite()) {
2244 return;
2245 }
2246
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002247 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002248 const SkRect* bounds = nullptr;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002249 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002250 const SkRect& pathBounds = path.getBounds();
senorblanco87e066e2015-10-28 11:23:36 -07002251 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2252 return;
2253 }
2254 bounds = &pathBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002255 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002256
2257 const SkRect& r = path.getBounds();
2258 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002259 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002260 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002261 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002262 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002263 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002264
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002265 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002266
2267 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002268 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002269 }
2270
reed@google.com4e2b3d32011-04-07 14:18:59 +00002271 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002272}
2273
reed262a71b2015-12-05 13:07:27 -08002274bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002275 if (!paint.getImageFilter()) {
2276 return false;
2277 }
2278
2279 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002280 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002281 return false;
2282 }
2283
2284 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2285 // Once we can filter and the filter will return a result larger than itself, we should be
2286 // able to remove this constraint.
2287 // skbug.com/4526
2288 //
2289 SkPoint pt;
2290 ctm.mapXY(x, y, &pt);
2291 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2292 return ir.contains(fMCRec->fRasterClip.getBounds());
2293}
2294
reeda85d4d02015-05-06 12:56:48 -07002295void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002296 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002297 SkRect bounds = SkRect::MakeXYWH(x, y,
2298 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002299 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002300 SkRect tmp = bounds;
2301 if (paint) {
2302 paint->computeFastBounds(tmp, &tmp);
2303 }
2304 if (this->quickReject(tmp)) {
2305 return;
2306 }
reeda85d4d02015-05-06 12:56:48 -07002307 }
halcanary9d524f22016-03-29 09:03:52 -07002308
reeda85d4d02015-05-06 12:56:48 -07002309 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002310 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002311 paint = lazy.init();
2312 }
reed262a71b2015-12-05 13:07:27 -08002313
reed129ed1c2016-02-22 06:42:31 -08002314 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2315 *paint);
2316 if (drawAsSprite && paint->getImageFilter()) {
2317 SkBitmap bitmap;
2318 if (!as_IB(image)->asBitmapForImageFilters(&bitmap)) {
2319 drawAsSprite = false;
2320 } else{
2321 // Until imagefilters are updated, they cannot handle any src type but N32...
reeddabe5d32016-06-21 10:28:14 -07002322 if (bitmap.info().colorType() != kN32_SkColorType || bitmap.info().gammaCloseToSRGB()) {
reed129ed1c2016-02-22 06:42:31 -08002323 drawAsSprite = false;
2324 }
2325 }
2326 }
2327
reed262a71b2015-12-05 13:07:27 -08002328 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
2329
reeda85d4d02015-05-06 12:56:48 -07002330 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002331 const SkPaint& pnt = looper.paint();
2332 if (drawAsSprite && pnt.getImageFilter()) {
2333 SkBitmap bitmap;
2334 if (as_IB(image)->asBitmapForImageFilters(&bitmap)) {
2335 SkPoint pt;
2336 iter.fMatrix->mapXY(x, y, &pt);
robertphillips2302de92016-03-24 07:26:32 -07002337 iter.fDevice->drawSpriteWithFilter(iter, bitmap,
2338 SkScalarRoundToInt(pt.fX),
2339 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002340 }
2341 } else {
2342 iter.fDevice->drawImage(iter, image, x, y, pnt);
2343 }
reeda85d4d02015-05-06 12:56:48 -07002344 }
halcanary9d524f22016-03-29 09:03:52 -07002345
reeda85d4d02015-05-06 12:56:48 -07002346 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002347}
2348
reed41af9662015-01-05 07:49:08 -08002349void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002350 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002351 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
halcanary96fcdcc2015-08-27 07:41:13 -07002352 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002353 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002354 if (paint) {
2355 paint->computeFastBounds(dst, &storage);
2356 }
2357 if (this->quickReject(storage)) {
2358 return;
2359 }
reeda85d4d02015-05-06 12:56:48 -07002360 }
2361 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002362 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002363 paint = lazy.init();
2364 }
halcanary9d524f22016-03-29 09:03:52 -07002365
senorblancoc41e7e12015-12-07 12:51:30 -08002366 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002367 image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002368
reeda85d4d02015-05-06 12:56:48 -07002369 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002370 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002371 }
halcanary9d524f22016-03-29 09:03:52 -07002372
reeda85d4d02015-05-06 12:56:48 -07002373 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002374}
2375
reed41af9662015-01-05 07:49:08 -08002376void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002377 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002378 SkDEBUGCODE(bitmap.validate();)
2379
reed33366972015-10-08 09:22:02 -07002380 if (bitmap.drawsNothing()) {
2381 return;
2382 }
2383
2384 SkLazyPaint lazy;
2385 if (nullptr == paint) {
2386 paint = lazy.init();
2387 }
2388
2389 const SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2390
2391 SkRect storage;
2392 const SkRect* bounds = nullptr;
2393 if (paint->canComputeFastBounds()) {
2394 bitmap.getBounds(&storage);
2395 matrix.mapRect(&storage);
senorblanco87e066e2015-10-28 11:23:36 -07002396 SkRect tmp = storage;
2397 if (this->quickReject(paint->computeFastBounds(tmp, &tmp))) {
2398 return;
2399 }
2400 bounds = &storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002401 }
reed@google.com4b226022011-01-11 18:32:13 +00002402
reed129ed1c2016-02-22 06:42:31 -08002403 bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(), bitmap.height(),
2404 *paint);
2405 if (drawAsSprite && paint->getImageFilter()) {
2406 // Until imagefilters are updated, they cannot handle any src type but N32...
reeddabe5d32016-06-21 10:28:14 -07002407 if (bitmap.info().colorType() != kN32_SkColorType || bitmap.info().gammaCloseToSRGB()) {
reed129ed1c2016-02-22 06:42:31 -08002408 drawAsSprite = false;
2409 }
2410 }
2411
reed262a71b2015-12-05 13:07:27 -08002412 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds)
reed33366972015-10-08 09:22:02 -07002413
2414 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002415 const SkPaint& pnt = looper.paint();
2416 if (drawAsSprite && pnt.getImageFilter()) {
2417 SkPoint pt;
2418 iter.fMatrix->mapXY(x, y, &pt);
robertphillips2302de92016-03-24 07:26:32 -07002419 iter.fDevice->drawSpriteWithFilter(iter, bitmap,
2420 SkScalarRoundToInt(pt.fX),
2421 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002422 } else {
2423 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
2424 }
reed33366972015-10-08 09:22:02 -07002425 }
reed262a71b2015-12-05 13:07:27 -08002426
reed33366972015-10-08 09:22:02 -07002427 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002428}
2429
reed@google.com9987ec32011-09-07 11:57:52 +00002430// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002431void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002432 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002433 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002434 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002435 return;
2436 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002437
halcanary96fcdcc2015-08-27 07:41:13 -07002438 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002439 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002440 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2441 return;
2442 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002443 }
reed@google.com3d608122011-11-21 15:16:16 +00002444
reed@google.com33535f32012-09-25 15:37:50 +00002445 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002446 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002447 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002448 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002449
senorblancoc41e7e12015-12-07 12:51:30 -08002450 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002451 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002452
reed@google.com33535f32012-09-25 15:37:50 +00002453 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002454 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002455 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002456
reed@google.com33535f32012-09-25 15:37:50 +00002457 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002458}
2459
reed41af9662015-01-05 07:49:08 -08002460void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002461 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002462 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002463 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002464 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002465}
2466
reed4c21dc52015-06-25 12:32:03 -07002467void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2468 const SkPaint* paint) {
2469 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
halcanary9d524f22016-03-29 09:03:52 -07002470
halcanary96fcdcc2015-08-27 07:41:13 -07002471 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002472 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002473 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2474 return;
2475 }
reed@google.com3d608122011-11-21 15:16:16 +00002476 }
halcanary9d524f22016-03-29 09:03:52 -07002477
reed4c21dc52015-06-25 12:32:03 -07002478 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002479 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002480 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002481 }
halcanary9d524f22016-03-29 09:03:52 -07002482
senorblancoc41e7e12015-12-07 12:51:30 -08002483 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002484
reed4c21dc52015-06-25 12:32:03 -07002485 while (iter.next()) {
2486 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002487 }
halcanary9d524f22016-03-29 09:03:52 -07002488
reed4c21dc52015-06-25 12:32:03 -07002489 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002490}
2491
reed41af9662015-01-05 07:49:08 -08002492void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2493 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002494 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002495 SkDEBUGCODE(bitmap.validate();)
2496
halcanary96fcdcc2015-08-27 07:41:13 -07002497 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002498 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002499 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2500 return;
2501 }
reed4c21dc52015-06-25 12:32:03 -07002502 }
halcanary9d524f22016-03-29 09:03:52 -07002503
reed4c21dc52015-06-25 12:32:03 -07002504 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002505 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002506 paint = lazy.init();
2507 }
halcanary9d524f22016-03-29 09:03:52 -07002508
senorblancoc41e7e12015-12-07 12:51:30 -08002509 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002510
reed4c21dc52015-06-25 12:32:03 -07002511 while (iter.next()) {
2512 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2513 }
halcanary9d524f22016-03-29 09:03:52 -07002514
reed4c21dc52015-06-25 12:32:03 -07002515 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002516}
2517
reed@google.comf67e4cf2011-03-15 20:56:58 +00002518class SkDeviceFilteredPaint {
2519public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002520 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002521 uint32_t filteredFlags = device->filterTextFlags(paint);
2522 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002523 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002524 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002525 fPaint = newPaint;
2526 } else {
2527 fPaint = &paint;
2528 }
2529 }
2530
reed@google.comf67e4cf2011-03-15 20:56:58 +00002531 const SkPaint& paint() const { return *fPaint; }
2532
2533private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002534 const SkPaint* fPaint;
2535 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002536};
2537
bungeman@google.com52c748b2011-08-22 21:30:43 +00002538void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2539 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002540 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002541 draw.fDevice->drawRect(draw, r, paint);
2542 } else {
2543 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002544 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002545 draw.fDevice->drawRect(draw, r, p);
2546 }
2547}
2548
2549void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2550 const char text[], size_t byteLength,
2551 SkScalar x, SkScalar y) {
halcanary96fcdcc2015-08-27 07:41:13 -07002552 SkASSERT(byteLength == 0 || text != nullptr);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002553
2554 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07002555 if (text == nullptr || byteLength == 0 ||
reed1e7f5e72016-04-27 07:49:17 -07002556 draw.fRC->isEmpty() ||
halcanary96fcdcc2015-08-27 07:41:13 -07002557 (paint.getAlpha() == 0 && paint.getXfermode() == nullptr)) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002558 return;
2559 }
2560
2561 SkScalar width = 0;
2562 SkPoint start;
2563
2564 start.set(0, 0); // to avoid warning
2565 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2566 SkPaint::kStrikeThruText_Flag)) {
2567 width = paint.measureText(text, byteLength);
2568
2569 SkScalar offsetX = 0;
2570 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2571 offsetX = SkScalarHalf(width);
2572 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2573 offsetX = width;
2574 }
2575 start.set(x - offsetX, y);
2576 }
2577
2578 if (0 == width) {
2579 return;
2580 }
2581
2582 uint32_t flags = paint.getFlags();
2583
2584 if (flags & (SkPaint::kUnderlineText_Flag |
2585 SkPaint::kStrikeThruText_Flag)) {
2586 SkScalar textSize = paint.getTextSize();
2587 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2588 SkRect r;
2589
2590 r.fLeft = start.fX;
2591 r.fRight = start.fX + width;
2592
2593 if (flags & SkPaint::kUnderlineText_Flag) {
2594 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2595 start.fY);
2596 r.fTop = offset;
2597 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002598 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002599 }
2600 if (flags & SkPaint::kStrikeThruText_Flag) {
2601 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2602 start.fY);
2603 r.fTop = offset;
2604 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002605 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002606 }
2607 }
2608}
2609
reed@google.come0d9ce82014-04-23 04:00:17 +00002610void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2611 const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002612 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002613
2614 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002615 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002616 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002617 DrawTextDecorations(iter, dfp.paint(),
2618 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002619 }
2620
reed@google.com4e2b3d32011-04-07 14:18:59 +00002621 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002622}
2623
reed@google.come0d9ce82014-04-23 04:00:17 +00002624void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2625 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002626 SkPoint textOffset = SkPoint::Make(0, 0);
2627
halcanary96fcdcc2015-08-27 07:41:13 -07002628 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002629
reed@android.com8a1c16f2008-12-17 15:59:43 +00002630 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002631 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002632 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002633 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002634 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002635
reed@google.com4e2b3d32011-04-07 14:18:59 +00002636 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002637}
2638
reed@google.come0d9ce82014-04-23 04:00:17 +00002639void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2640 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002641
2642 SkPoint textOffset = SkPoint::Make(0, constY);
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, xpos, 1, 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::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2656 const SkMatrix* matrix, const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002657 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002658
reed@android.com8a1c16f2008-12-17 15:59:43 +00002659 while (iter.next()) {
2660 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002661 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002662 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002663
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002664 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002665}
2666
reed45561a02016-07-07 12:47:17 -07002667void SkCanvas::onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2668 const SkRect* cullRect, const SkPaint& paint) {
2669 if (cullRect && this->quickReject(*cullRect)) {
2670 return;
2671 }
2672
2673 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
2674
2675 while (iter.next()) {
2676 iter.fDevice->drawTextRSXform(iter, text, byteLength, xform, looper.paint());
2677 }
2678
2679 LOOPER_END
2680}
2681
fmalita00d5c2c2014-08-21 08:53:26 -07002682void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2683 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002684
fmalita85d5eb92015-03-04 11:20:12 -08002685 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002686 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002687 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002688 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002689 SkRect tmp;
2690 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2691 return;
2692 }
2693 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002694 }
2695
fmalita024f9962015-03-03 19:08:17 -08002696 // We cannot filter in the looper as we normally do, because the paint is
2697 // incomplete at this point (text-related attributes are embedded within blob run paints).
2698 SkDrawFilter* drawFilter = fMCRec->fFilter;
halcanary96fcdcc2015-08-27 07:41:13 -07002699 fMCRec->fFilter = nullptr;
fmalita024f9962015-03-03 19:08:17 -08002700
fmalita85d5eb92015-03-04 11:20:12 -08002701 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002702
fmalitaaa1b9122014-08-28 14:32:24 -07002703 while (iter.next()) {
2704 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002705 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002706 }
2707
fmalitaaa1b9122014-08-28 14:32:24 -07002708 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002709
2710 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002711}
2712
reed@google.come0d9ce82014-04-23 04:00:17 +00002713// These will become non-virtual, so they always call the (virtual) onDraw... method
2714void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2715 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002716 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002717 this->onDrawText(text, byteLength, x, y, paint);
2718}
2719void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2720 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002721 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002722 this->onDrawPosText(text, byteLength, pos, paint);
2723}
2724void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2725 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002726 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002727 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2728}
2729void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2730 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002731 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002732 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2733}
reed45561a02016-07-07 12:47:17 -07002734void SkCanvas::drawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2735 const SkRect* cullRect, const SkPaint& paint) {
2736 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextRSXform()");
2737 if (byteLength) {
2738 this->onDrawTextRSXform(text, byteLength, xform, cullRect, paint);
2739 }
2740}
fmalita00d5c2c2014-08-21 08:53:26 -07002741void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2742 const SkPaint& paint) {
reede3b38ce2016-01-08 09:18:44 -08002743 RETURN_ON_NULL(blob);
danakj9881d632014-11-26 12:41:06 -08002744 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
reede3b38ce2016-01-08 09:18:44 -08002745 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002746}
reed@google.come0d9ce82014-04-23 04:00:17 +00002747
reed41af9662015-01-05 07:49:08 -08002748void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2749 const SkPoint verts[], const SkPoint texs[],
2750 const SkColor colors[], SkXfermode* xmode,
2751 const uint16_t indices[], int indexCount,
2752 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002753 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
halcanary96fcdcc2015-08-27 07:41:13 -07002754 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
reed@google.com4b226022011-01-11 18:32:13 +00002755
reed@android.com8a1c16f2008-12-17 15:59:43 +00002756 while (iter.next()) {
2757 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002758 colors, xmode, indices, indexCount,
2759 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002760 }
reed@google.com4b226022011-01-11 18:32:13 +00002761
reed@google.com4e2b3d32011-04-07 14:18:59 +00002762 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002763}
2764
dandovb3c9d1c2014-08-12 08:34:29 -07002765void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2766 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002767 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
halcanary96fcdcc2015-08-27 07:41:13 -07002768 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002769 return;
2770 }
mtklein6cfa73a2014-08-13 13:33:49 -07002771
dandovecfff212014-08-04 10:02:00 -07002772 // Since a patch is always within the convex hull of the control points, we discard it when its
2773 // bounding rectangle is completely outside the current clip.
2774 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002775 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002776 if (this->quickReject(bounds)) {
2777 return;
2778 }
mtklein6cfa73a2014-08-13 13:33:49 -07002779
dandovb3c9d1c2014-08-12 08:34:29 -07002780 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2781}
2782
2783void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2784 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2785
halcanary96fcdcc2015-08-27 07:41:13 -07002786 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002787
dandovecfff212014-08-04 10:02:00 -07002788 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002789 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002790 }
mtklein6cfa73a2014-08-13 13:33:49 -07002791
dandovecfff212014-08-04 10:02:00 -07002792 LOOPER_END
2793}
2794
reeda8db7282015-07-07 10:22:31 -07002795void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
reede3b38ce2016-01-08 09:18:44 -08002796 RETURN_ON_NULL(dr);
2797 if (x || y) {
2798 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2799 this->onDrawDrawable(dr, &matrix);
2800 } else {
2801 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002802 }
2803}
2804
reeda8db7282015-07-07 10:22:31 -07002805void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reede3b38ce2016-01-08 09:18:44 -08002806 RETURN_ON_NULL(dr);
2807 if (matrix && matrix->isIdentity()) {
2808 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002809 }
reede3b38ce2016-01-08 09:18:44 -08002810 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002811}
2812
2813void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2814 SkRect bounds = dr->getBounds();
2815 if (matrix) {
2816 matrix->mapRect(&bounds);
2817 }
2818 if (this->quickReject(bounds)) {
2819 return;
2820 }
2821 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002822}
2823
reed71c3c762015-06-24 10:29:17 -07002824void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2825 const SkColor colors[], int count, SkXfermode::Mode mode,
2826 const SkRect* cull, const SkPaint* paint) {
2827 if (cull && this->quickReject(*cull)) {
2828 return;
2829 }
2830
2831 SkPaint pnt;
2832 if (paint) {
2833 pnt = *paint;
2834 }
halcanary9d524f22016-03-29 09:03:52 -07002835
halcanary96fcdcc2015-08-27 07:41:13 -07002836 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr)
reed71c3c762015-06-24 10:29:17 -07002837 while (iter.next()) {
2838 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, mode, pnt);
2839 }
2840 LOOPER_END
2841}
2842
reedf70b5312016-03-04 16:36:20 -08002843void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2844 SkASSERT(key);
2845
2846 SkPaint paint;
2847 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, nullptr)
2848 while (iter.next()) {
2849 iter.fDevice->drawAnnotation(iter, rect, key, value);
2850 }
2851 LOOPER_END
2852}
2853
reed@android.com8a1c16f2008-12-17 15:59:43 +00002854//////////////////////////////////////////////////////////////////////////////
2855// These methods are NOT virtual, and therefore must call back into virtual
2856// methods, rather than actually drawing themselves.
2857//////////////////////////////////////////////////////////////////////////////
2858
2859void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002860 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002861 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002862 SkPaint paint;
2863
2864 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002865 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002866 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002867 }
2868 this->drawPaint(paint);
2869}
2870
reed@android.com845fdac2009-06-23 03:01:32 +00002871void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002872 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002873 SkPaint paint;
2874
2875 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002876 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002877 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002878 }
2879 this->drawPaint(paint);
2880}
2881
2882void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002883 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002884 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002885
reed@android.com8a1c16f2008-12-17 15:59:43 +00002886 pt.set(x, y);
2887 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2888}
2889
2890void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002891 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002892 SkPoint pt;
2893 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002894
reed@android.com8a1c16f2008-12-17 15:59:43 +00002895 pt.set(x, y);
2896 paint.setColor(color);
2897 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2898}
2899
2900void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2901 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002902 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002903 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002904
reed@android.com8a1c16f2008-12-17 15:59:43 +00002905 pts[0].set(x0, y0);
2906 pts[1].set(x1, y1);
2907 this->drawPoints(kLines_PointMode, 2, pts, paint);
2908}
2909
2910void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2911 SkScalar right, SkScalar bottom,
2912 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002913 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002914 SkRect r;
2915
2916 r.set(left, top, right, bottom);
2917 this->drawRect(r, paint);
2918}
2919
2920void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2921 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002922 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002923 if (radius < 0) {
2924 radius = 0;
2925 }
2926
2927 SkRect r;
2928 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002929 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002930}
2931
2932void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2933 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002934 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002935 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002936 SkRRect rrect;
2937 rrect.setRectXY(r, rx, ry);
2938 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002939 } else {
2940 this->drawRect(r, paint);
2941 }
2942}
2943
reed@android.com8a1c16f2008-12-17 15:59:43 +00002944void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2945 SkScalar sweepAngle, bool useCenter,
2946 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002947 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002948 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2949 this->drawOval(oval, paint);
2950 } else {
2951 SkPath path;
2952 if (useCenter) {
2953 path.moveTo(oval.centerX(), oval.centerY());
2954 }
2955 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2956 if (useCenter) {
2957 path.close();
2958 }
2959 this->drawPath(path, paint);
2960 }
2961}
2962
2963void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2964 const SkPath& path, SkScalar hOffset,
2965 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002966 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002967 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002968
reed@android.com8a1c16f2008-12-17 15:59:43 +00002969 matrix.setTranslate(hOffset, vOffset);
2970 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2971}
2972
reed@android.comf76bacf2009-05-13 14:00:33 +00002973///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07002974
2975/**
2976 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2977 * against the playback cost of recursing into the subpicture to get at its actual ops.
2978 *
2979 * For now we pick a conservatively small value, though measurement (and other heuristics like
2980 * the type of ops contained) may justify changing this value.
2981 */
2982#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07002983
reedd5fa1a42014-08-09 11:08:05 -07002984void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002985 RETURN_ON_NULL(picture);
2986
reed1c2c4412015-04-30 13:09:24 -07002987 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
reede3b38ce2016-01-08 09:18:44 -08002988 if (matrix && matrix->isIdentity()) {
2989 matrix = nullptr;
2990 }
2991 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2992 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2993 picture->playback(this);
2994 } else {
2995 this->onDrawPicture(picture, matrix, paint);
reedd5fa1a42014-08-09 11:08:05 -07002996 }
2997}
robertphillips9b14f262014-06-04 05:40:44 -07002998
reedd5fa1a42014-08-09 11:08:05 -07002999void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
3000 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07003001 if (!paint || paint->canComputeFastBounds()) {
3002 SkRect bounds = picture->cullRect();
3003 if (paint) {
3004 paint->computeFastBounds(bounds, &bounds);
3005 }
3006 if (matrix) {
3007 matrix->mapRect(&bounds);
3008 }
3009 if (this->quickReject(bounds)) {
3010 return;
3011 }
3012 }
3013
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003014 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07003015 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003016}
3017
reed@android.com8a1c16f2008-12-17 15:59:43 +00003018///////////////////////////////////////////////////////////////////////////////
3019///////////////////////////////////////////////////////////////////////////////
3020
3021SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
bungeman99fe8222015-08-20 07:57:51 -07003022 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003023
3024 SkASSERT(canvas);
3025
3026 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
3027 fDone = !fImpl->next();
3028}
3029
3030SkCanvas::LayerIter::~LayerIter() {
3031 fImpl->~SkDrawIter();
3032}
3033
3034void SkCanvas::LayerIter::next() {
3035 fDone = !fImpl->next();
3036}
3037
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003038SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003039 return fImpl->getDevice();
3040}
3041
3042const SkMatrix& SkCanvas::LayerIter::matrix() const {
3043 return fImpl->getMatrix();
3044}
3045
3046const SkPaint& SkCanvas::LayerIter::paint() const {
3047 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003048 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003049 paint = &fDefaultPaint;
3050 }
3051 return *paint;
3052}
3053
reed1e7f5e72016-04-27 07:49:17 -07003054const SkRasterClip& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +00003055int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
3056int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003057
3058///////////////////////////////////////////////////////////////////////////////
3059
fmalitac3b589a2014-06-05 12:40:07 -07003060SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003061
3062///////////////////////////////////////////////////////////////////////////////
3063
3064static bool supported_for_raster_canvas(const SkImageInfo& info) {
3065 switch (info.alphaType()) {
3066 case kPremul_SkAlphaType:
3067 case kOpaque_SkAlphaType:
3068 break;
3069 default:
3070 return false;
3071 }
3072
3073 switch (info.colorType()) {
3074 case kAlpha_8_SkColorType:
3075 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00003076 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003077 break;
3078 default:
3079 return false;
3080 }
3081
3082 return true;
3083}
3084
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003085SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
3086 if (!supported_for_raster_canvas(info)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003087 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003088 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003089
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003090 SkBitmap bitmap;
3091 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003092 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003093 }
halcanary385fe4d2015-08-26 13:07:48 -07003094 return new SkCanvas(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003095}
reedd5fa1a42014-08-09 11:08:05 -07003096
3097///////////////////////////////////////////////////////////////////////////////
3098
3099SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003100 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07003101 : fCanvas(canvas)
3102 , fSaveCount(canvas->getSaveCount())
3103{
bsalomon49f085d2014-09-05 13:34:00 -07003104 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003105 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07003106 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003107 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07003108 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003109 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07003110 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003111 canvas->save();
3112 }
mtklein6cfa73a2014-08-13 13:33:49 -07003113
bsalomon49f085d2014-09-05 13:34:00 -07003114 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003115 canvas->concat(*matrix);
3116 }
3117}
3118
3119SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
3120 fCanvas->restoreToCount(fSaveCount);
3121}
reede8f30622016-03-23 18:59:25 -07003122
3123#ifdef SK_SUPPORT_LEGACY_NEW_SURFACE_API
3124SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
3125 return this->makeSurface(info, props).release();
3126}
3127#endif