blob: 60bda8d36a8786c0db64a45aa966f05d37db3c6c [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
reedb2db8982014-11-13 12:41:02 -08001272 SkBaseDevice* device = this->getTopDevice();
halcanary96fcdcc2015-08-27 07:41:13 -07001273 if (nullptr == device) {
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
reed129ed1c2016-02-22 06:42:31 -08001278 SkImageInfo info = make_layer_info(device->imageInfo(), ir.width(), ir.height(), isOpaque,
1279 paint);
1280
reed61f501f2015-04-29 08:34:00 -07001281 {
reed70ee31b2015-12-10 13:44:45 -08001282 const bool preserveLCDText = kOpaque_SkAlphaType == info.alphaType() ||
reed4960eee2015-12-18 07:09:18 -08001283 (saveLayerFlags & kPreserveLCDText_SaveLayerFlag);
reeddaa57bf2015-05-15 10:39:17 -07001284 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
reed70ee31b2015-12-10 13:44:45 -08001285 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
reedcd4051e2016-07-15 09:41:26 -07001286 preserveLCDText);
reed61f501f2015-04-29 08:34:00 -07001287 SkBaseDevice* newDev = device->onCreateDevice(createInfo, paint);
halcanary96fcdcc2015-08-27 07:41:13 -07001288 if (nullptr == newDev) {
reed7503d602016-07-15 14:23:29 -07001289 SkErrorInternals::SetError(kInternalError_SkError,
1290 "Unable to create device for layer.");
1291 return;
reed61f501f2015-04-29 08:34:00 -07001292 }
1293 device = newDev;
bungeman@google.come25c6842011-08-17 14:53:54 +00001294 }
reed@google.com6f8f2922011-03-04 22:27:10 +00001295 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips7354a4b2015-12-16 05:08:27 -08001296
reedbfd5f172016-01-07 11:28:08 -08001297 if (rec.fBackdrop) {
1298 draw_filter_into_device(fMCRec->fTopLayer->fDevice, rec.fBackdrop, device, fMCRec->fMatrix);
robertphillips7354a4b2015-12-16 05:08:27 -08001299 }
1300
reed7503d602016-07-15 14:23:29 -07001301 DeviceCM* layer = new DeviceCM(device, paint, this, fConservativeRasterClip, stashedMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001302 device->unref();
1303
1304 layer->fNext = fMCRec->fTopLayer;
1305 fMCRec->fLayer = layer;
1306 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reed@android.com8a1c16f2008-12-17 15:59:43 +00001307}
1308
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001309int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001310 if (0xFF == alpha) {
1311 return this->saveLayer(bounds, nullptr);
1312 } else {
1313 SkPaint tmpPaint;
1314 tmpPaint.setAlpha(alpha);
1315 return this->saveLayer(bounds, &tmpPaint);
1316 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001317}
1318
reed@android.com8a1c16f2008-12-17 15:59:43 +00001319void SkCanvas::internalRestore() {
1320 SkASSERT(fMCStack.count() != 0);
1321
1322 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001323 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001324
reed687fa1c2015-04-07 08:00:56 -07001325 fClipStack->restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001326
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001327 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001328 DeviceCM* layer = fMCRec->fLayer; // may be null
1329 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001330 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001331
1332 // now do the normal restore()
1333 fMCRec->~MCRec(); // balanced in save()
1334 fMCStack.pop_back();
1335 fMCRec = (MCRec*)fMCStack.back();
1336
1337 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1338 since if we're being recorded, we don't want to record this (the
1339 recorder will have already recorded the restore).
1340 */
bsalomon49f085d2014-09-05 13:34:00 -07001341 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001342 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001343 const SkIPoint& origin = layer->fDevice->getOrigin();
reed7503d602016-07-15 14:23:29 -07001344 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(), layer->fPaint);
reed8c30a812016-04-20 16:36:51 -07001345 // restore what we smashed in internalSaveLayer
1346 fMCRec->fMatrix = layer->fStashedMatrix;
reed@google.com8926b162012-03-23 15:36:36 +00001347 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001348 fDeviceCMDirty = true;
halcanary385fe4d2015-08-26 13:07:48 -07001349 delete layer;
reedb679ca82015-04-07 04:40:48 -07001350 } else {
1351 // we're at the root
reeda499f902015-05-01 09:34:31 -07001352 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001353 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001354 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001355 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001356 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001357}
1358
reede8f30622016-03-23 18:59:25 -07001359sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001360 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001361 props = &fProps;
1362 }
1363 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001364}
1365
reede8f30622016-03-23 18:59:25 -07001366sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001367 SkBaseDevice* dev = this->getDevice();
reede8f30622016-03-23 18:59:25 -07001368 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001369}
1370
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001371SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001372 return this->onImageInfo();
1373}
1374
1375SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001376 SkBaseDevice* dev = this->getDevice();
1377 if (dev) {
1378 return dev->imageInfo();
1379 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001380 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001381 }
1382}
1383
brianosman898235c2016-04-06 07:38:23 -07001384bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001385 return this->onGetProps(props);
1386}
1387
1388bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001389 SkBaseDevice* dev = this->getDevice();
1390 if (dev) {
1391 if (props) {
1392 *props = fProps;
1393 }
1394 return true;
1395 } else {
1396 return false;
1397 }
1398}
1399
reed6ceeebd2016-03-09 14:26:26 -08001400#ifdef SK_SUPPORT_LEGACY_PEEKPIXELS_PARMS
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001401const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
reed884e97c2015-05-26 11:31:54 -07001402 SkPixmap pmap;
reed6ceeebd2016-03-09 14:26:26 -08001403 if (this->peekPixels(&pmap)) {
1404 if (info) {
1405 *info = pmap.info();
1406 }
1407 if (rowBytes) {
1408 *rowBytes = pmap.rowBytes();
1409 }
1410 return pmap.addr();
reed884e97c2015-05-26 11:31:54 -07001411 }
reed6ceeebd2016-03-09 14:26:26 -08001412 return nullptr;
1413}
1414#endif
1415
1416bool SkCanvas::peekPixels(SkPixmap* pmap) {
1417 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001418}
1419
reed884e97c2015-05-26 11:31:54 -07001420bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001421 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001422 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001423}
1424
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001425void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001426 SkPixmap pmap;
1427 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001428 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001429 }
1430 if (info) {
1431 *info = pmap.info();
1432 }
1433 if (rowBytes) {
1434 *rowBytes = pmap.rowBytes();
1435 }
1436 if (origin) {
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001437 *origin = this->getTopDevice(false)->getOrigin();
1438 }
reed884e97c2015-05-26 11:31:54 -07001439 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001440}
1441
reed884e97c2015-05-26 11:31:54 -07001442bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001443 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001444 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001445}
1446
reed@android.com8a1c16f2008-12-17 15:59:43 +00001447/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001448
reed7503d602016-07-15 14:23:29 -07001449void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001450 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001451 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001452 paint = &tmp;
1453 }
reed@google.com4b226022011-01-11 18:32:13 +00001454
reed@google.com8926b162012-03-23 15:36:36 +00001455 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001456 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001457 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001458 paint = &looper.paint();
1459 SkImageFilter* filter = paint->getImageFilter();
1460 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
senorblancof35566e2016-04-18 10:32:02 -07001461 if (filter) {
robertphillips4418dba2016-03-07 12:45:14 -08001462 const SkBitmap& srcBM = srcDev->accessBitmap(false);
reed1eca1162016-04-25 12:29:38 -07001463 dstDev->drawSpriteWithFilter(iter, srcBM, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001464 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001465 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001466 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001467 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001468 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001469}
1470
reed32704672015-12-16 08:27:10 -08001471/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001472
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001473void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001474 SkMatrix m;
1475 m.setTranslate(dx, dy);
1476 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001477}
1478
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001479void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001480 SkMatrix m;
1481 m.setScale(sx, sy);
1482 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001483}
1484
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001485void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001486 SkMatrix m;
1487 m.setRotate(degrees);
1488 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001489}
1490
bungeman7438bfc2016-07-12 15:01:19 -07001491void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1492 SkMatrix m;
1493 m.setRotate(degrees, px, py);
1494 this->concat(m);
1495}
1496
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001497void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001498 SkMatrix m;
1499 m.setSkew(sx, sy);
1500 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001501}
1502
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001503void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001504 if (matrix.isIdentity()) {
1505 return;
1506 }
1507
reed2ff1fce2014-12-11 07:07:37 -08001508 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001509 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001510 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001511 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001512
1513 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001514}
1515
reed8c30a812016-04-20 16:36:51 -07001516void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001517 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001518 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001519 fMCRec->fMatrix = matrix;
reed8c30a812016-04-20 16:36:51 -07001520}
1521
1522void SkCanvas::setMatrix(const SkMatrix& matrix) {
1523 this->checkForDeferredSave();
1524 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001525 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001526}
1527
reed@android.com8a1c16f2008-12-17 15:59:43 +00001528void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001529 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001530}
1531
vjiaoblacke5de1302016-07-13 14:05:28 -07001532void SkCanvas::translateZ(SkScalar z) {
1533 this->checkForDeferredSave();
1534 this->fMCRec->fCurDrawDepth += z;
1535 this->didTranslateZ(z);
1536}
1537
1538SkScalar SkCanvas::getZ() const {
1539 return this->fMCRec->fCurDrawDepth;
1540}
1541
reed@android.com8a1c16f2008-12-17 15:59:43 +00001542//////////////////////////////////////////////////////////////////////////////
1543
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001544void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed2d1afab2016-06-29 14:33:11 -07001545 if (!fAllowSoftClip) {
1546 doAA = false;
1547 }
1548
1549#ifdef SK_SUPPORT_PRECHECK_CLIPRECT
1550 // Check if we can quick-accept the clip call (and do nothing)
1551 //
reed74467162016-06-30 08:15:35 -07001552 if (SkRegion::kIntersect_Op == op && !doAA && fMCRec->fMatrix.isScaleTranslate()) {
reed6092b6e2016-07-10 11:45:34 -07001553 SkRect devR;
1554 fMCRec->fMatrix.mapRectScaleTranslate(&devR, rect);
reed2d1afab2016-06-29 14:33:11 -07001555 // NOTE: this check is CTM specific, since we might round differently with a different
1556 // CTM. Thus this is only 100% reliable if there is not global CTM scale to be
1557 // applied later (i.e. if this is going into a picture).
1558 if (devR.round().contains(fMCRec->fRasterClip.getBounds())) {
1559#if 0
1560 SkDebugf("ignored clipRect [%g %g %g %g]\n",
1561 rect.left(), rect.top(), rect.right(), rect.bottom());
1562#endif
1563 return;
1564 }
1565 }
1566#endif
1567
reed2ff1fce2014-12-11 07:07:37 -08001568 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001569 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1570 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001571}
1572
1573void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001574#ifdef SK_ENABLE_CLIP_QUICKREJECT
1575 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001576 if (fMCRec->fRasterClip.isEmpty()) {
reed2d1afab2016-06-29 14:33:11 -07001577 return;
reed@google.comda17f752012-08-16 18:27:05 +00001578 }
1579
reed@google.com3b3e8952012-08-16 20:53:31 +00001580 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001581 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001582 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001583
reed687fa1c2015-04-07 08:00:56 -07001584 fClipStack->clipEmpty();
reed2d1afab2016-06-29 14:33:11 -07001585 (void)fMCRec->fRasterClip.setEmpty();
1586 return;
reed@google.comda17f752012-08-16 18:27:05 +00001587 }
1588 }
1589#endif
1590
reed74467162016-06-30 08:15:35 -07001591 const bool isScaleTrans = fMCRec->fMatrix.isScaleTranslate();
reedc64eff52015-11-21 12:39:45 -08001592 SkRect devR;
reed74467162016-06-30 08:15:35 -07001593 if (isScaleTrans) {
reed6092b6e2016-07-10 11:45:34 -07001594 fMCRec->fMatrix.mapRectScaleTranslate(&devR, rect);
reedc64eff52015-11-21 12:39:45 -08001595 }
bsalomonac8cabd2015-11-20 18:53:07 -08001596
reed2d1afab2016-06-29 14:33:11 -07001597#ifndef SK_SUPPORT_PRECHECK_CLIPRECT
reedc64eff52015-11-21 12:39:45 -08001598 if (SkRegion::kIntersect_Op == op &&
1599 kHard_ClipEdgeStyle == edgeStyle
reed74467162016-06-30 08:15:35 -07001600 && isScaleTrans)
reedc64eff52015-11-21 12:39:45 -08001601 {
1602 if (devR.round().contains(fMCRec->fRasterClip.getBounds())) {
1603#if 0
1604 SkDebugf("------- ignored clipRect [%g %g %g %g]\n",
1605 rect.left(), rect.top(), rect.right(), rect.bottom());
1606#endif
1607 return;
1608 }
1609 }
reed2d1afab2016-06-29 14:33:11 -07001610#endif
reedc64eff52015-11-21 12:39:45 -08001611
1612 AutoValidateClip avc(this);
1613
1614 fDeviceCMDirty = true;
1615 fCachedLocalClipBoundsDirty = true;
1616
reed74467162016-06-30 08:15:35 -07001617 if (isScaleTrans) {
reedc64eff52015-11-21 12:39:45 -08001618 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1619 fClipStack->clipDevRect(devR, op, isAA);
senorblancoafc7cce2016-02-02 18:44:15 -08001620 fMCRec->fRasterClip.op(devR, this->getTopLayerBounds(), op, isAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001621 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001622 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001623 // and clip against that, since it can handle any matrix. However, to
1624 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1625 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001626 SkPath path;
1627
1628 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001629 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001630 }
1631}
1632
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001633void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001634 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001635 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001636 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001637 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1638 } else {
1639 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001640 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001641}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001642
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001643void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001644 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001645 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001646 AutoValidateClip avc(this);
1647
1648 fDeviceCMDirty = true;
1649 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001650 if (!fAllowSoftClip) {
1651 edgeStyle = kHard_ClipEdgeStyle;
1652 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001653
reed687fa1c2015-04-07 08:00:56 -07001654 fClipStack->clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001655
senorblancoafc7cce2016-02-02 18:44:15 -08001656 fMCRec->fRasterClip.op(transformedRRect, this->getTopLayerBounds(), op,
robertphillips125f19a2015-11-23 09:00:05 -08001657 kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001658 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001659 }
1660
1661 SkPath path;
1662 path.addRRect(rrect);
1663 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001664 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001665}
1666
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001667void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001668 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001669 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001670
1671 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1672 SkRect r;
1673 if (path.isRect(&r)) {
1674 this->onClipRect(r, op, edgeStyle);
1675 return;
1676 }
1677 SkRRect rrect;
1678 if (path.isOval(&r)) {
1679 rrect.setOval(r);
1680 this->onClipRRect(rrect, op, edgeStyle);
1681 return;
1682 }
1683 if (path.isRRect(&rrect)) {
1684 this->onClipRRect(rrect, op, edgeStyle);
1685 return;
1686 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001687 }
robertphillips39f05382015-11-24 09:30:12 -08001688
1689 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001690}
1691
1692void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001693#ifdef SK_ENABLE_CLIP_QUICKREJECT
1694 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001695 if (fMCRec->fRasterClip.isEmpty()) {
reed2d1afab2016-06-29 14:33:11 -07001696 return;
reed@google.comda17f752012-08-16 18:27:05 +00001697 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001698
reed@google.com3b3e8952012-08-16 20:53:31 +00001699 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001700 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001701 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001702
reed687fa1c2015-04-07 08:00:56 -07001703 fClipStack->clipEmpty();
reed2d1afab2016-06-29 14:33:11 -07001704 (void)fMCRec->fRasterClip.setEmpty();
1705 return;
reed@google.comda17f752012-08-16 18:27:05 +00001706 }
1707 }
1708#endif
1709
reed@google.com5c3d1472011-02-22 19:12:23 +00001710 AutoValidateClip avc(this);
1711
reed@android.com8a1c16f2008-12-17 15:59:43 +00001712 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001713 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001714 if (!fAllowSoftClip) {
1715 edgeStyle = kHard_ClipEdgeStyle;
1716 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001717
1718 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001719 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001720
reed@google.comfe701122011-11-08 19:41:23 +00001721 // Check if the transfomation, or the original path itself
1722 // made us empty. Note this can also happen if we contained NaN
1723 // values. computing the bounds detects this, and will set our
1724 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1725 if (devPath.getBounds().isEmpty()) {
1726 // resetting the path will remove any NaN or other wanky values
1727 // that might upset our scan converter.
1728 devPath.reset();
1729 }
1730
reed@google.com5c3d1472011-02-22 19:12:23 +00001731 // if we called path.swap() we could avoid a deep copy of this path
reed687fa1c2015-04-07 08:00:56 -07001732 fClipStack->clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001733
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001734 if (fAllowSimplifyClip) {
fmalita1a481fe2015-02-04 07:39:34 -08001735 bool clipIsAA = getClipStack()->asPath(&devPath);
1736 if (clipIsAA) {
1737 edgeStyle = kSoft_ClipEdgeStyle;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001738 }
fmalita1a481fe2015-02-04 07:39:34 -08001739
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001740 op = SkRegion::kReplace_Op;
1741 }
1742
senorblancoafc7cce2016-02-02 18:44:15 -08001743 fMCRec->fRasterClip.op(devPath, this->getTopLayerBounds(), op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001744}
1745
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001746void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed2ff1fce2014-12-11 07:07:37 -08001747 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001748 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001749}
1750
1751void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001752 AutoValidateClip avc(this);
1753
reed@android.com8a1c16f2008-12-17 15:59:43 +00001754 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001755 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001756
reed@google.com5c3d1472011-02-22 19:12:23 +00001757 // todo: signal fClipStack that we have a region, and therefore (I guess)
1758 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001759 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001760
reed1f836ee2014-07-07 07:49:34 -07001761 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001762}
1763
reed@google.com819c9212011-02-23 18:56:55 +00001764#ifdef SK_DEBUG
1765void SkCanvas::validateClip() const {
1766 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001767 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001768 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001769 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001770 return;
1771 }
1772
reed@google.com819c9212011-02-23 18:56:55 +00001773 SkIRect ir;
1774 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001775 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001776
reed687fa1c2015-04-07 08:00:56 -07001777 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001778 const SkClipStack::Element* element;
halcanary96fcdcc2015-08-27 07:41:13 -07001779 while ((element = iter.next()) != nullptr) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001780 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001781 case SkClipStack::Element::kRect_Type:
1782 element->getRect().round(&ir);
1783 tmpClip.op(ir, element->getOp());
1784 break;
1785 case SkClipStack::Element::kEmpty_Type:
1786 tmpClip.setEmpty();
1787 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001788 default: {
1789 SkPath path;
1790 element->asPath(&path);
senorblancoafc7cce2016-02-02 18:44:15 -08001791 tmpClip.op(path, this->getTopLayerBounds(), element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001792 break;
1793 }
reed@google.com819c9212011-02-23 18:56:55 +00001794 }
1795 }
reed@google.com819c9212011-02-23 18:56:55 +00001796}
1797#endif
1798
reed@google.com90c07ea2012-04-13 13:50:27 +00001799void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001800 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001801 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001802
halcanary96fcdcc2015-08-27 07:41:13 -07001803 while ((element = iter.next()) != nullptr) {
fmalitac3b589a2014-06-05 12:40:07 -07001804 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001805 }
1806}
1807
reed@google.com5c3d1472011-02-22 19:12:23 +00001808///////////////////////////////////////////////////////////////////////////////
1809
reed@google.com754de5f2014-02-24 19:38:20 +00001810bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001811 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001812}
1813
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001814bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001815 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001816}
1817
reed@google.com3b3e8952012-08-16 20:53:31 +00001818bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001819 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001820 return true;
1821
reed1f836ee2014-07-07 07:49:34 -07001822 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001823 return true;
1824 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001825
reed1f836ee2014-07-07 07:49:34 -07001826 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001827 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001828 fMCRec->fMatrix.mapRect(&dst, rect);
reedb07a94f2014-11-19 05:03:18 -08001829 return !SkIRect::Intersects(dst.roundOut(), fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001830 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001831 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001832
reed@android.coma380ae42009-07-21 01:17:02 +00001833 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001834 // TODO: should we use | instead, or compare all 4 at once?
1835 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001836 return true;
1837 }
reed@google.comc0784db2013-12-13 21:16:12 +00001838 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001839 return true;
1840 }
1841 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001842 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001843}
1844
reed@google.com3b3e8952012-08-16 20:53:31 +00001845bool SkCanvas::quickReject(const SkPath& path) const {
1846 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001847}
1848
reed@google.com3b3e8952012-08-16 20:53:31 +00001849bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001850 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001851 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001852 return false;
1853 }
1854
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001855 SkMatrix inverse;
1856 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001857 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001858 if (bounds) {
1859 bounds->setEmpty();
1860 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001861 return false;
1862 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001863
bsalomon49f085d2014-09-05 13:34:00 -07001864 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001865 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001866 // adjust it outwards in case we are antialiasing
1867 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001868
reed@google.com8f4d2302013-12-17 16:44:46 +00001869 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1870 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001871 inverse.mapRect(bounds, r);
1872 }
1873 return true;
1874}
1875
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001876bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001877 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001878 if (clip.isEmpty()) {
1879 if (bounds) {
1880 bounds->setEmpty();
1881 }
1882 return false;
1883 }
1884
bsalomon49f085d2014-09-05 13:34:00 -07001885 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001886 *bounds = clip.getBounds();
1887 }
1888 return true;
1889}
1890
reed@android.com8a1c16f2008-12-17 15:59:43 +00001891const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001892 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001893}
1894
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001895const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001896 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001897}
1898
robertphillips175dd9b2016-04-28 14:32:04 -07001899GrDrawContext* SkCanvas::internal_private_accessTopLayerDrawContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001900 SkBaseDevice* dev = this->getTopDevice();
robertphillips175dd9b2016-04-28 14:32:04 -07001901 return dev ? dev->accessDrawContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001902}
1903
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001904GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001905 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001906 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001907}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001908
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001909void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1910 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001911 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001912 if (outer.isEmpty()) {
1913 return;
1914 }
1915 if (inner.isEmpty()) {
1916 this->drawRRect(outer, paint);
1917 return;
1918 }
1919
1920 // We don't have this method (yet), but technically this is what we should
1921 // be able to assert...
1922 // SkASSERT(outer.contains(inner));
1923 //
1924 // For now at least check for containment of bounds
1925 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1926
1927 this->onDrawDRRect(outer, inner, paint);
1928}
1929
reed41af9662015-01-05 07:49:08 -08001930// These need to stop being virtual -- clients need to override the onDraw... versions
1931
1932void SkCanvas::drawPaint(const SkPaint& paint) {
1933 this->onDrawPaint(paint);
1934}
1935
1936void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1937 this->onDrawRect(r, paint);
1938}
1939
1940void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1941 this->onDrawOval(r, paint);
1942}
1943
1944void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1945 this->onDrawRRect(rrect, paint);
1946}
1947
1948void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1949 this->onDrawPoints(mode, count, pts, paint);
1950}
1951
1952void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
1953 const SkPoint texs[], const SkColor colors[], SkXfermode* xmode,
1954 const uint16_t indices[], int indexCount, const SkPaint& paint) {
1955 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
1956 indices, indexCount, paint);
1957}
1958
1959void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1960 this->onDrawPath(path, paint);
1961}
1962
reeda85d4d02015-05-06 12:56:48 -07001963void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001964 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001965 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001966}
1967
reede47829b2015-08-06 10:02:53 -07001968void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1969 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001970 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001971 if (dst.isEmpty() || src.isEmpty()) {
1972 return;
1973 }
1974 this->onDrawImageRect(image, &src, dst, paint, constraint);
1975}
reed41af9662015-01-05 07:49:08 -08001976
reed84984ef2015-07-17 07:09:43 -07001977void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1978 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001979 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001980 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001981}
1982
reede47829b2015-08-06 10:02:53 -07001983void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
1984 SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001985 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001986 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
1987 constraint);
1988}
reede47829b2015-08-06 10:02:53 -07001989
reed4c21dc52015-06-25 12:32:03 -07001990void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1991 const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001992 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07001993 if (dst.isEmpty()) {
1994 return;
1995 }
1996 if (!SkNinePatchIter::Valid(image->width(), image->height(), center)) {
reede47829b2015-08-06 10:02:53 -07001997 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001998 }
1999 this->onDrawImageNine(image, center, dst, paint);
2000}
2001
reed41af9662015-01-05 07:49:08 -08002002void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07002003 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002004 return;
2005 }
reed41af9662015-01-05 07:49:08 -08002006 this->onDrawBitmap(bitmap, dx, dy, paint);
2007}
2008
reede47829b2015-08-06 10:02:53 -07002009void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07002010 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002011 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07002012 return;
2013 }
reede47829b2015-08-06 10:02:53 -07002014 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08002015}
2016
reed84984ef2015-07-17 07:09:43 -07002017void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2018 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002019 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002020}
2021
reede47829b2015-08-06 10:02:53 -07002022void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2023 SrcRectConstraint constraint) {
2024 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2025 constraint);
2026}
reede47829b2015-08-06 10:02:53 -07002027
reed41af9662015-01-05 07:49:08 -08002028void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2029 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07002030 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002031 return;
2032 }
reed4c21dc52015-06-25 12:32:03 -07002033 if (!SkNinePatchIter::Valid(bitmap.width(), bitmap.height(), center)) {
reeda5517e22015-07-14 10:54:12 -07002034 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002035 }
reed41af9662015-01-05 07:49:08 -08002036 this->onDrawBitmapNine(bitmap, center, dst, paint);
2037}
2038
reed71c3c762015-06-24 10:29:17 -07002039void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2040 const SkColor colors[], int count, SkXfermode::Mode mode,
2041 const SkRect* cull, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002042 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002043 if (count <= 0) {
2044 return;
2045 }
2046 SkASSERT(atlas);
2047 SkASSERT(xform);
2048 SkASSERT(tex);
2049 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
2050}
2051
reedf70b5312016-03-04 16:36:20 -08002052void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2053 if (key) {
2054 this->onDrawAnnotation(rect, key, value);
2055 }
2056}
2057
reede47829b2015-08-06 10:02:53 -07002058void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2059 const SkPaint* paint, SrcRectConstraint constraint) {
2060 if (src) {
2061 this->drawImageRect(image, *src, dst, paint, constraint);
2062 } else {
2063 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2064 dst, paint, constraint);
2065 }
2066}
2067void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2068 const SkPaint* paint, SrcRectConstraint constraint) {
2069 if (src) {
2070 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2071 } else {
2072 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2073 dst, paint, constraint);
2074 }
2075}
2076
tomhudsoncb3bd182016-05-18 07:24:16 -07002077void SkCanvas::temporary_internal_describeTopLayer(SkMatrix* matrix, SkIRect* clip_bounds) {
2078 SkIRect layer_bounds = this->getTopLayerBounds();
2079 if (matrix) {
2080 *matrix = this->getTotalMatrix();
2081 matrix->preTranslate(-layer_bounds.left(), -layer_bounds.top());
2082 }
2083 if (clip_bounds) {
2084 this->getClipDeviceBounds(clip_bounds);
2085 clip_bounds->offset(-layer_bounds.left(), -layer_bounds.top());
2086 }
2087}
2088
reed@android.com8a1c16f2008-12-17 15:59:43 +00002089//////////////////////////////////////////////////////////////////////////////
2090// These are the virtual drawing methods
2091//////////////////////////////////////////////////////////////////////////////
2092
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002093void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002094 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002095 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2096 }
2097}
2098
reed41af9662015-01-05 07:49:08 -08002099void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002100 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002101 this->internalDrawPaint(paint);
2102}
2103
2104void SkCanvas::internalDrawPaint(const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002105 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002106
2107 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002108 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002109 }
2110
reed@google.com4e2b3d32011-04-07 14:18:59 +00002111 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002112}
2113
reed41af9662015-01-05 07:49:08 -08002114void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2115 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002116 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002117 if ((long)count <= 0) {
2118 return;
2119 }
2120
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002121 SkRect r, storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002122 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002123 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002124 // special-case 2 points (common for drawing a single line)
2125 if (2 == count) {
2126 r.set(pts[0], pts[1]);
2127 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00002128 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002129 }
senorblanco87e066e2015-10-28 11:23:36 -07002130 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2131 return;
2132 }
2133 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002134 }
reed@google.coma584aed2012-05-16 14:06:02 +00002135
halcanary96fcdcc2015-08-27 07:41:13 -07002136 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002137
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002138 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002139
reed@android.com8a1c16f2008-12-17 15:59:43 +00002140 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002141 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002142 }
reed@google.com4b226022011-01-11 18:32:13 +00002143
reed@google.com4e2b3d32011-04-07 14:18:59 +00002144 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002145}
2146
reed41af9662015-01-05 07:49:08 -08002147void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002148 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002149 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002150 const SkRect* bounds = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002151 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08002152 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
2153 // To prevent accidental rejecting at this stage, we have to sort it before we check.
2154 SkRect tmp(r);
2155 tmp.sort();
2156
senorblanco87e066e2015-10-28 11:23:36 -07002157 if (this->quickReject(paint.computeFastBounds(tmp, &storage))) {
2158 return;
2159 }
2160 bounds = &r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002161 }
reed@google.com4b226022011-01-11 18:32:13 +00002162
reedc83a2972015-07-16 07:40:45 -07002163 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002164
2165 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002166 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002167 }
2168
reed@google.com4e2b3d32011-04-07 14:18:59 +00002169 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002170}
2171
reed41af9662015-01-05 07:49:08 -08002172void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002173 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002174 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002175 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002176 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002177 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2178 return;
2179 }
2180 bounds = &oval;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002181 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002182
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002183 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002184
2185 while (iter.next()) {
2186 iter.fDevice->drawOval(iter, oval, looper.paint());
2187 }
2188
2189 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002190}
2191
reed41af9662015-01-05 07:49:08 -08002192void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002193 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002194 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002195 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002196 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002197 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2198 return;
2199 }
2200 bounds = &rrect.getBounds();
reed@google.com4ed0fb72012-12-12 20:48:18 +00002201 }
2202
2203 if (rrect.isRect()) {
2204 // call the non-virtual version
2205 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002206 return;
2207 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002208 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002209 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2210 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002211 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002212
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002213 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002214
2215 while (iter.next()) {
2216 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2217 }
2218
2219 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002220}
2221
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002222void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2223 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002224 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002225 const SkRect* bounds = nullptr;
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002226 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002227 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2228 return;
2229 }
2230 bounds = &outer.getBounds();
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002231 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002232
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002233 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002234
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002235 while (iter.next()) {
2236 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2237 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002238
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002239 LOOPER_END
2240}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002241
reed41af9662015-01-05 07:49:08 -08002242void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002243 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002244 if (!path.isFinite()) {
2245 return;
2246 }
2247
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002248 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002249 const SkRect* bounds = nullptr;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002250 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002251 const SkRect& pathBounds = path.getBounds();
senorblanco87e066e2015-10-28 11:23:36 -07002252 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2253 return;
2254 }
2255 bounds = &pathBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002256 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002257
2258 const SkRect& r = path.getBounds();
2259 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002260 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002261 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002262 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002263 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002264 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002265
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002266 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002267
2268 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002269 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002270 }
2271
reed@google.com4e2b3d32011-04-07 14:18:59 +00002272 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002273}
2274
reed262a71b2015-12-05 13:07:27 -08002275bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002276 if (!paint.getImageFilter()) {
2277 return false;
2278 }
2279
2280 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002281 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002282 return false;
2283 }
2284
2285 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2286 // Once we can filter and the filter will return a result larger than itself, we should be
2287 // able to remove this constraint.
2288 // skbug.com/4526
2289 //
2290 SkPoint pt;
2291 ctm.mapXY(x, y, &pt);
2292 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2293 return ir.contains(fMCRec->fRasterClip.getBounds());
2294}
2295
reeda85d4d02015-05-06 12:56:48 -07002296void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002297 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002298 SkRect bounds = SkRect::MakeXYWH(x, y,
2299 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002300 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002301 SkRect tmp = bounds;
2302 if (paint) {
2303 paint->computeFastBounds(tmp, &tmp);
2304 }
2305 if (this->quickReject(tmp)) {
2306 return;
2307 }
reeda85d4d02015-05-06 12:56:48 -07002308 }
halcanary9d524f22016-03-29 09:03:52 -07002309
reeda85d4d02015-05-06 12:56:48 -07002310 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002311 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002312 paint = lazy.init();
2313 }
reed262a71b2015-12-05 13:07:27 -08002314
reed129ed1c2016-02-22 06:42:31 -08002315 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2316 *paint);
2317 if (drawAsSprite && paint->getImageFilter()) {
2318 SkBitmap bitmap;
2319 if (!as_IB(image)->asBitmapForImageFilters(&bitmap)) {
2320 drawAsSprite = false;
2321 } else{
2322 // Until imagefilters are updated, they cannot handle any src type but N32...
reeddabe5d32016-06-21 10:28:14 -07002323 if (bitmap.info().colorType() != kN32_SkColorType || bitmap.info().gammaCloseToSRGB()) {
reed129ed1c2016-02-22 06:42:31 -08002324 drawAsSprite = false;
2325 }
2326 }
2327 }
2328
reed262a71b2015-12-05 13:07:27 -08002329 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
2330
reeda85d4d02015-05-06 12:56:48 -07002331 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002332 const SkPaint& pnt = looper.paint();
2333 if (drawAsSprite && pnt.getImageFilter()) {
2334 SkBitmap bitmap;
2335 if (as_IB(image)->asBitmapForImageFilters(&bitmap)) {
2336 SkPoint pt;
2337 iter.fMatrix->mapXY(x, y, &pt);
robertphillips2302de92016-03-24 07:26:32 -07002338 iter.fDevice->drawSpriteWithFilter(iter, bitmap,
2339 SkScalarRoundToInt(pt.fX),
2340 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002341 }
2342 } else {
2343 iter.fDevice->drawImage(iter, image, x, y, pnt);
2344 }
reeda85d4d02015-05-06 12:56:48 -07002345 }
halcanary9d524f22016-03-29 09:03:52 -07002346
reeda85d4d02015-05-06 12:56:48 -07002347 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002348}
2349
reed41af9662015-01-05 07:49:08 -08002350void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002351 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002352 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
halcanary96fcdcc2015-08-27 07:41:13 -07002353 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002354 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002355 if (paint) {
2356 paint->computeFastBounds(dst, &storage);
2357 }
2358 if (this->quickReject(storage)) {
2359 return;
2360 }
reeda85d4d02015-05-06 12:56:48 -07002361 }
2362 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002363 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002364 paint = lazy.init();
2365 }
halcanary9d524f22016-03-29 09:03:52 -07002366
senorblancoc41e7e12015-12-07 12:51:30 -08002367 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002368 image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002369
reeda85d4d02015-05-06 12:56:48 -07002370 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002371 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002372 }
halcanary9d524f22016-03-29 09:03:52 -07002373
reeda85d4d02015-05-06 12:56:48 -07002374 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002375}
2376
reed41af9662015-01-05 07:49:08 -08002377void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002378 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002379 SkDEBUGCODE(bitmap.validate();)
2380
reed33366972015-10-08 09:22:02 -07002381 if (bitmap.drawsNothing()) {
2382 return;
2383 }
2384
2385 SkLazyPaint lazy;
2386 if (nullptr == paint) {
2387 paint = lazy.init();
2388 }
2389
2390 const SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2391
2392 SkRect storage;
2393 const SkRect* bounds = nullptr;
2394 if (paint->canComputeFastBounds()) {
2395 bitmap.getBounds(&storage);
2396 matrix.mapRect(&storage);
senorblanco87e066e2015-10-28 11:23:36 -07002397 SkRect tmp = storage;
2398 if (this->quickReject(paint->computeFastBounds(tmp, &tmp))) {
2399 return;
2400 }
2401 bounds = &storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002402 }
reed@google.com4b226022011-01-11 18:32:13 +00002403
reed129ed1c2016-02-22 06:42:31 -08002404 bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(), bitmap.height(),
2405 *paint);
2406 if (drawAsSprite && paint->getImageFilter()) {
2407 // Until imagefilters are updated, they cannot handle any src type but N32...
reeddabe5d32016-06-21 10:28:14 -07002408 if (bitmap.info().colorType() != kN32_SkColorType || bitmap.info().gammaCloseToSRGB()) {
reed129ed1c2016-02-22 06:42:31 -08002409 drawAsSprite = false;
2410 }
2411 }
2412
reed262a71b2015-12-05 13:07:27 -08002413 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds)
reed33366972015-10-08 09:22:02 -07002414
2415 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002416 const SkPaint& pnt = looper.paint();
2417 if (drawAsSprite && pnt.getImageFilter()) {
2418 SkPoint pt;
2419 iter.fMatrix->mapXY(x, y, &pt);
robertphillips2302de92016-03-24 07:26:32 -07002420 iter.fDevice->drawSpriteWithFilter(iter, bitmap,
2421 SkScalarRoundToInt(pt.fX),
2422 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002423 } else {
2424 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
2425 }
reed33366972015-10-08 09:22:02 -07002426 }
reed262a71b2015-12-05 13:07:27 -08002427
reed33366972015-10-08 09:22:02 -07002428 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002429}
2430
reed@google.com9987ec32011-09-07 11:57:52 +00002431// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002432void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002433 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002434 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002435 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002436 return;
2437 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002438
halcanary96fcdcc2015-08-27 07:41:13 -07002439 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002440 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002441 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2442 return;
2443 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002444 }
reed@google.com3d608122011-11-21 15:16:16 +00002445
reed@google.com33535f32012-09-25 15:37:50 +00002446 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002447 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002448 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002449 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002450
senorblancoc41e7e12015-12-07 12:51:30 -08002451 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002452 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002453
reed@google.com33535f32012-09-25 15:37:50 +00002454 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002455 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002456 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002457
reed@google.com33535f32012-09-25 15:37:50 +00002458 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002459}
2460
reed41af9662015-01-05 07:49:08 -08002461void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002462 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002463 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002464 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002465 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002466}
2467
reed4c21dc52015-06-25 12:32:03 -07002468void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2469 const SkPaint* paint) {
2470 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
halcanary9d524f22016-03-29 09:03:52 -07002471
halcanary96fcdcc2015-08-27 07:41:13 -07002472 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002473 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002474 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2475 return;
2476 }
reed@google.com3d608122011-11-21 15:16:16 +00002477 }
halcanary9d524f22016-03-29 09:03:52 -07002478
reed4c21dc52015-06-25 12:32:03 -07002479 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002480 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002481 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002482 }
halcanary9d524f22016-03-29 09:03:52 -07002483
senorblancoc41e7e12015-12-07 12:51:30 -08002484 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002485
reed4c21dc52015-06-25 12:32:03 -07002486 while (iter.next()) {
2487 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002488 }
halcanary9d524f22016-03-29 09:03:52 -07002489
reed4c21dc52015-06-25 12:32:03 -07002490 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002491}
2492
reed41af9662015-01-05 07:49:08 -08002493void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2494 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002495 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002496 SkDEBUGCODE(bitmap.validate();)
2497
halcanary96fcdcc2015-08-27 07:41:13 -07002498 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002499 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002500 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2501 return;
2502 }
reed4c21dc52015-06-25 12:32:03 -07002503 }
halcanary9d524f22016-03-29 09:03:52 -07002504
reed4c21dc52015-06-25 12:32:03 -07002505 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002506 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002507 paint = lazy.init();
2508 }
halcanary9d524f22016-03-29 09:03:52 -07002509
senorblancoc41e7e12015-12-07 12:51:30 -08002510 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002511
reed4c21dc52015-06-25 12:32:03 -07002512 while (iter.next()) {
2513 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2514 }
halcanary9d524f22016-03-29 09:03:52 -07002515
reed4c21dc52015-06-25 12:32:03 -07002516 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002517}
2518
reed@google.comf67e4cf2011-03-15 20:56:58 +00002519class SkDeviceFilteredPaint {
2520public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002521 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002522 uint32_t filteredFlags = device->filterTextFlags(paint);
2523 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002524 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002525 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002526 fPaint = newPaint;
2527 } else {
2528 fPaint = &paint;
2529 }
2530 }
2531
reed@google.comf67e4cf2011-03-15 20:56:58 +00002532 const SkPaint& paint() const { return *fPaint; }
2533
2534private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002535 const SkPaint* fPaint;
2536 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002537};
2538
bungeman@google.com52c748b2011-08-22 21:30:43 +00002539void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2540 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002541 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002542 draw.fDevice->drawRect(draw, r, paint);
2543 } else {
2544 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002545 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002546 draw.fDevice->drawRect(draw, r, p);
2547 }
2548}
2549
2550void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2551 const char text[], size_t byteLength,
2552 SkScalar x, SkScalar y) {
halcanary96fcdcc2015-08-27 07:41:13 -07002553 SkASSERT(byteLength == 0 || text != nullptr);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002554
2555 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07002556 if (text == nullptr || byteLength == 0 ||
reed1e7f5e72016-04-27 07:49:17 -07002557 draw.fRC->isEmpty() ||
halcanary96fcdcc2015-08-27 07:41:13 -07002558 (paint.getAlpha() == 0 && paint.getXfermode() == nullptr)) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002559 return;
2560 }
2561
2562 SkScalar width = 0;
2563 SkPoint start;
2564
2565 start.set(0, 0); // to avoid warning
2566 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2567 SkPaint::kStrikeThruText_Flag)) {
2568 width = paint.measureText(text, byteLength);
2569
2570 SkScalar offsetX = 0;
2571 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2572 offsetX = SkScalarHalf(width);
2573 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2574 offsetX = width;
2575 }
2576 start.set(x - offsetX, y);
2577 }
2578
2579 if (0 == width) {
2580 return;
2581 }
2582
2583 uint32_t flags = paint.getFlags();
2584
2585 if (flags & (SkPaint::kUnderlineText_Flag |
2586 SkPaint::kStrikeThruText_Flag)) {
2587 SkScalar textSize = paint.getTextSize();
2588 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2589 SkRect r;
2590
2591 r.fLeft = start.fX;
2592 r.fRight = start.fX + width;
2593
2594 if (flags & SkPaint::kUnderlineText_Flag) {
2595 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2596 start.fY);
2597 r.fTop = offset;
2598 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002599 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002600 }
2601 if (flags & SkPaint::kStrikeThruText_Flag) {
2602 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2603 start.fY);
2604 r.fTop = offset;
2605 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002606 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002607 }
2608 }
2609}
2610
reed@google.come0d9ce82014-04-23 04:00:17 +00002611void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2612 const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002613 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002614
2615 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002616 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002617 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002618 DrawTextDecorations(iter, dfp.paint(),
2619 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002620 }
2621
reed@google.com4e2b3d32011-04-07 14:18:59 +00002622 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002623}
2624
reed@google.come0d9ce82014-04-23 04:00:17 +00002625void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2626 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002627 SkPoint textOffset = SkPoint::Make(0, 0);
2628
halcanary96fcdcc2015-08-27 07:41:13 -07002629 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002630
reed@android.com8a1c16f2008-12-17 15:59:43 +00002631 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002632 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002633 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002634 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002635 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002636
reed@google.com4e2b3d32011-04-07 14:18:59 +00002637 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002638}
2639
reed@google.come0d9ce82014-04-23 04:00:17 +00002640void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2641 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002642
2643 SkPoint textOffset = SkPoint::Make(0, constY);
2644
halcanary96fcdcc2015-08-27 07:41:13 -07002645 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002646
reed@android.com8a1c16f2008-12-17 15:59:43 +00002647 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002648 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002649 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002650 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002651 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002652
reed@google.com4e2b3d32011-04-07 14:18:59 +00002653 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002654}
2655
reed@google.come0d9ce82014-04-23 04:00:17 +00002656void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2657 const SkMatrix* matrix, const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002658 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002659
reed@android.com8a1c16f2008-12-17 15:59:43 +00002660 while (iter.next()) {
2661 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002662 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002663 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002664
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002665 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002666}
2667
reed45561a02016-07-07 12:47:17 -07002668void SkCanvas::onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2669 const SkRect* cullRect, const SkPaint& paint) {
2670 if (cullRect && this->quickReject(*cullRect)) {
2671 return;
2672 }
2673
2674 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
2675
2676 while (iter.next()) {
2677 iter.fDevice->drawTextRSXform(iter, text, byteLength, xform, looper.paint());
2678 }
2679
2680 LOOPER_END
2681}
2682
fmalita00d5c2c2014-08-21 08:53:26 -07002683void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2684 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002685
fmalita85d5eb92015-03-04 11:20:12 -08002686 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002687 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002688 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002689 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002690 SkRect tmp;
2691 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2692 return;
2693 }
2694 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002695 }
2696
fmalita024f9962015-03-03 19:08:17 -08002697 // We cannot filter in the looper as we normally do, because the paint is
2698 // incomplete at this point (text-related attributes are embedded within blob run paints).
2699 SkDrawFilter* drawFilter = fMCRec->fFilter;
halcanary96fcdcc2015-08-27 07:41:13 -07002700 fMCRec->fFilter = nullptr;
fmalita024f9962015-03-03 19:08:17 -08002701
fmalita85d5eb92015-03-04 11:20:12 -08002702 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002703
fmalitaaa1b9122014-08-28 14:32:24 -07002704 while (iter.next()) {
2705 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002706 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002707 }
2708
fmalitaaa1b9122014-08-28 14:32:24 -07002709 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002710
2711 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002712}
2713
reed@google.come0d9ce82014-04-23 04:00:17 +00002714// These will become non-virtual, so they always call the (virtual) onDraw... method
2715void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2716 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002717 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002718 this->onDrawText(text, byteLength, x, y, paint);
2719}
2720void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2721 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002722 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002723 this->onDrawPosText(text, byteLength, pos, paint);
2724}
2725void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2726 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002727 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002728 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2729}
2730void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2731 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002732 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002733 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2734}
reed45561a02016-07-07 12:47:17 -07002735void SkCanvas::drawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2736 const SkRect* cullRect, const SkPaint& paint) {
2737 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextRSXform()");
2738 if (byteLength) {
2739 this->onDrawTextRSXform(text, byteLength, xform, cullRect, paint);
2740 }
2741}
fmalita00d5c2c2014-08-21 08:53:26 -07002742void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2743 const SkPaint& paint) {
reede3b38ce2016-01-08 09:18:44 -08002744 RETURN_ON_NULL(blob);
danakj9881d632014-11-26 12:41:06 -08002745 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
reede3b38ce2016-01-08 09:18:44 -08002746 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002747}
reed@google.come0d9ce82014-04-23 04:00:17 +00002748
reed41af9662015-01-05 07:49:08 -08002749void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2750 const SkPoint verts[], const SkPoint texs[],
2751 const SkColor colors[], SkXfermode* xmode,
2752 const uint16_t indices[], int indexCount,
2753 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002754 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
halcanary96fcdcc2015-08-27 07:41:13 -07002755 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
reed@google.com4b226022011-01-11 18:32:13 +00002756
reed@android.com8a1c16f2008-12-17 15:59:43 +00002757 while (iter.next()) {
2758 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002759 colors, xmode, indices, indexCount,
2760 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002761 }
reed@google.com4b226022011-01-11 18:32:13 +00002762
reed@google.com4e2b3d32011-04-07 14:18:59 +00002763 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002764}
2765
dandovb3c9d1c2014-08-12 08:34:29 -07002766void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2767 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002768 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
halcanary96fcdcc2015-08-27 07:41:13 -07002769 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002770 return;
2771 }
mtklein6cfa73a2014-08-13 13:33:49 -07002772
dandovecfff212014-08-04 10:02:00 -07002773 // Since a patch is always within the convex hull of the control points, we discard it when its
2774 // bounding rectangle is completely outside the current clip.
2775 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002776 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002777 if (this->quickReject(bounds)) {
2778 return;
2779 }
mtklein6cfa73a2014-08-13 13:33:49 -07002780
dandovb3c9d1c2014-08-12 08:34:29 -07002781 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2782}
2783
2784void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2785 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2786
halcanary96fcdcc2015-08-27 07:41:13 -07002787 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002788
dandovecfff212014-08-04 10:02:00 -07002789 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002790 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002791 }
mtklein6cfa73a2014-08-13 13:33:49 -07002792
dandovecfff212014-08-04 10:02:00 -07002793 LOOPER_END
2794}
2795
reeda8db7282015-07-07 10:22:31 -07002796void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
reede3b38ce2016-01-08 09:18:44 -08002797 RETURN_ON_NULL(dr);
2798 if (x || y) {
2799 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2800 this->onDrawDrawable(dr, &matrix);
2801 } else {
2802 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002803 }
2804}
2805
reeda8db7282015-07-07 10:22:31 -07002806void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reede3b38ce2016-01-08 09:18:44 -08002807 RETURN_ON_NULL(dr);
2808 if (matrix && matrix->isIdentity()) {
2809 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002810 }
reede3b38ce2016-01-08 09:18:44 -08002811 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002812}
2813
2814void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2815 SkRect bounds = dr->getBounds();
2816 if (matrix) {
2817 matrix->mapRect(&bounds);
2818 }
2819 if (this->quickReject(bounds)) {
2820 return;
2821 }
2822 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002823}
2824
reed71c3c762015-06-24 10:29:17 -07002825void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2826 const SkColor colors[], int count, SkXfermode::Mode mode,
2827 const SkRect* cull, const SkPaint* paint) {
2828 if (cull && this->quickReject(*cull)) {
2829 return;
2830 }
2831
2832 SkPaint pnt;
2833 if (paint) {
2834 pnt = *paint;
2835 }
halcanary9d524f22016-03-29 09:03:52 -07002836
halcanary96fcdcc2015-08-27 07:41:13 -07002837 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr)
reed71c3c762015-06-24 10:29:17 -07002838 while (iter.next()) {
2839 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, mode, pnt);
2840 }
2841 LOOPER_END
2842}
2843
reedf70b5312016-03-04 16:36:20 -08002844void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2845 SkASSERT(key);
2846
2847 SkPaint paint;
2848 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, nullptr)
2849 while (iter.next()) {
2850 iter.fDevice->drawAnnotation(iter, rect, key, value);
2851 }
2852 LOOPER_END
2853}
2854
reed@android.com8a1c16f2008-12-17 15:59:43 +00002855//////////////////////////////////////////////////////////////////////////////
2856// These methods are NOT virtual, and therefore must call back into virtual
2857// methods, rather than actually drawing themselves.
2858//////////////////////////////////////////////////////////////////////////////
2859
2860void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002861 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002862 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002863 SkPaint paint;
2864
2865 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002866 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002867 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002868 }
2869 this->drawPaint(paint);
2870}
2871
reed@android.com845fdac2009-06-23 03:01:32 +00002872void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002873 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002874 SkPaint paint;
2875
2876 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002877 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002878 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002879 }
2880 this->drawPaint(paint);
2881}
2882
2883void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002884 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002885 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002886
reed@android.com8a1c16f2008-12-17 15:59:43 +00002887 pt.set(x, y);
2888 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2889}
2890
2891void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002892 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002893 SkPoint pt;
2894 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002895
reed@android.com8a1c16f2008-12-17 15:59:43 +00002896 pt.set(x, y);
2897 paint.setColor(color);
2898 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2899}
2900
2901void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2902 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002903 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002904 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002905
reed@android.com8a1c16f2008-12-17 15:59:43 +00002906 pts[0].set(x0, y0);
2907 pts[1].set(x1, y1);
2908 this->drawPoints(kLines_PointMode, 2, pts, paint);
2909}
2910
2911void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2912 SkScalar right, SkScalar bottom,
2913 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002914 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002915 SkRect r;
2916
2917 r.set(left, top, right, bottom);
2918 this->drawRect(r, paint);
2919}
2920
2921void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2922 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002923 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002924 if (radius < 0) {
2925 radius = 0;
2926 }
2927
2928 SkRect r;
2929 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002930 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002931}
2932
2933void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2934 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002935 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002936 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002937 SkRRect rrect;
2938 rrect.setRectXY(r, rx, ry);
2939 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002940 } else {
2941 this->drawRect(r, paint);
2942 }
2943}
2944
reed@android.com8a1c16f2008-12-17 15:59:43 +00002945void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2946 SkScalar sweepAngle, bool useCenter,
2947 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002948 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002949 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2950 this->drawOval(oval, paint);
2951 } else {
2952 SkPath path;
2953 if (useCenter) {
2954 path.moveTo(oval.centerX(), oval.centerY());
2955 }
2956 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2957 if (useCenter) {
2958 path.close();
2959 }
2960 this->drawPath(path, paint);
2961 }
2962}
2963
2964void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2965 const SkPath& path, SkScalar hOffset,
2966 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002967 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002968 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002969
reed@android.com8a1c16f2008-12-17 15:59:43 +00002970 matrix.setTranslate(hOffset, vOffset);
2971 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2972}
2973
reed@android.comf76bacf2009-05-13 14:00:33 +00002974///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07002975
2976/**
2977 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2978 * against the playback cost of recursing into the subpicture to get at its actual ops.
2979 *
2980 * For now we pick a conservatively small value, though measurement (and other heuristics like
2981 * the type of ops contained) may justify changing this value.
2982 */
2983#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07002984
reedd5fa1a42014-08-09 11:08:05 -07002985void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002986 RETURN_ON_NULL(picture);
2987
reed1c2c4412015-04-30 13:09:24 -07002988 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
reede3b38ce2016-01-08 09:18:44 -08002989 if (matrix && matrix->isIdentity()) {
2990 matrix = nullptr;
2991 }
2992 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2993 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2994 picture->playback(this);
2995 } else {
2996 this->onDrawPicture(picture, matrix, paint);
reedd5fa1a42014-08-09 11:08:05 -07002997 }
2998}
robertphillips9b14f262014-06-04 05:40:44 -07002999
reedd5fa1a42014-08-09 11:08:05 -07003000void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
3001 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07003002 if (!paint || paint->canComputeFastBounds()) {
3003 SkRect bounds = picture->cullRect();
3004 if (paint) {
3005 paint->computeFastBounds(bounds, &bounds);
3006 }
3007 if (matrix) {
3008 matrix->mapRect(&bounds);
3009 }
3010 if (this->quickReject(bounds)) {
3011 return;
3012 }
3013 }
3014
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003015 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07003016 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003017}
3018
reed@android.com8a1c16f2008-12-17 15:59:43 +00003019///////////////////////////////////////////////////////////////////////////////
3020///////////////////////////////////////////////////////////////////////////////
3021
3022SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
bungeman99fe8222015-08-20 07:57:51 -07003023 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003024
3025 SkASSERT(canvas);
3026
3027 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
3028 fDone = !fImpl->next();
3029}
3030
3031SkCanvas::LayerIter::~LayerIter() {
3032 fImpl->~SkDrawIter();
3033}
3034
3035void SkCanvas::LayerIter::next() {
3036 fDone = !fImpl->next();
3037}
3038
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003039SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003040 return fImpl->getDevice();
3041}
3042
3043const SkMatrix& SkCanvas::LayerIter::matrix() const {
3044 return fImpl->getMatrix();
3045}
3046
3047const SkPaint& SkCanvas::LayerIter::paint() const {
3048 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003049 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003050 paint = &fDefaultPaint;
3051 }
3052 return *paint;
3053}
3054
reed1e7f5e72016-04-27 07:49:17 -07003055const SkRasterClip& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +00003056int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
3057int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003058
3059///////////////////////////////////////////////////////////////////////////////
3060
fmalitac3b589a2014-06-05 12:40:07 -07003061SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003062
3063///////////////////////////////////////////////////////////////////////////////
3064
3065static bool supported_for_raster_canvas(const SkImageInfo& info) {
3066 switch (info.alphaType()) {
3067 case kPremul_SkAlphaType:
3068 case kOpaque_SkAlphaType:
3069 break;
3070 default:
3071 return false;
3072 }
3073
3074 switch (info.colorType()) {
3075 case kAlpha_8_SkColorType:
3076 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00003077 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003078 break;
3079 default:
3080 return false;
3081 }
3082
3083 return true;
3084}
3085
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003086SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
3087 if (!supported_for_raster_canvas(info)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003088 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003089 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003090
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003091 SkBitmap bitmap;
3092 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003093 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003094 }
halcanary385fe4d2015-08-26 13:07:48 -07003095 return new SkCanvas(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003096}
reedd5fa1a42014-08-09 11:08:05 -07003097
3098///////////////////////////////////////////////////////////////////////////////
3099
3100SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003101 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07003102 : fCanvas(canvas)
3103 , fSaveCount(canvas->getSaveCount())
3104{
bsalomon49f085d2014-09-05 13:34:00 -07003105 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003106 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07003107 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003108 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07003109 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003110 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07003111 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003112 canvas->save();
3113 }
mtklein6cfa73a2014-08-13 13:33:49 -07003114
bsalomon49f085d2014-09-05 13:34:00 -07003115 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003116 canvas->concat(*matrix);
3117 }
3118}
3119
3120SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
3121 fCanvas->restoreToCount(fSaveCount);
3122}
reede8f30622016-03-23 18:59:25 -07003123
3124#ifdef SK_SUPPORT_LEGACY_NEW_SURFACE_API
3125SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
3126 return this->makeSurface(info, props).release();
3127}
3128#endif