blob: 050253fa68c4e85dcca7cf44976b2384168f36a3 [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
reeda2217ef2016-07-20 06:04:34 -07001149void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
1150 SkBaseDevice* dst, const SkMatrix& ctm,
1151 const SkClipStack* clipStack) {
1152 SkDraw draw;
1153 SkRasterClip rc;
1154 rc.setRect(SkIRect::MakeWH(dst->width(), dst->height()));
1155 if (!dst->accessPixels(&draw.fDst)) {
1156 draw.fDst.reset(dst->imageInfo(), nullptr, 0);
robertphillips7354a4b2015-12-16 05:08:27 -08001157 }
reeda2217ef2016-07-20 06:04:34 -07001158 draw.fMatrix = &SkMatrix::I();
1159 draw.fRC = &rc;
1160 draw.fClipStack = clipStack;
1161 draw.fDevice = dst;
robertphillips7354a4b2015-12-16 05:08:27 -08001162
1163 SkPaint p;
robertphillips372177e2016-03-30 07:32:28 -07001164 p.setImageFilter(filter->makeWithLocalMatrix(ctm));
reeda2217ef2016-07-20 06:04:34 -07001165
1166 int x = src->getOrigin().x() - dst->getOrigin().x();
1167 int y = src->getOrigin().y() - dst->getOrigin().y();
1168 auto special = src->snapSpecial();
1169 if (special) {
1170 dst->drawSpecial(draw, special.get(), x, y, p);
1171 }
robertphillips7354a4b2015-12-16 05:08:27 -08001172}
reed70ee31b2015-12-10 13:44:45 -08001173
reed129ed1c2016-02-22 06:42:31 -08001174static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, bool isOpaque,
1175 const SkPaint* paint) {
1176 // need to force L32 for now if we have an image filter. Once filters support other colortypes
1177 // e.g. sRGB or F16, we can remove this check
brianosman52ede1d2016-06-20 08:25:02 -07001178 // SRGBTODO: Can we remove this check now?
reed129ed1c2016-02-22 06:42:31 -08001179 const bool hasImageFilter = paint && paint->getImageFilter();
1180
1181 SkAlphaType alphaType = isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
1182 if ((prev.bytesPerPixel() < 4) || hasImageFilter) {
1183 // force to L32
1184 return SkImageInfo::MakeN32(w, h, alphaType);
1185 } else {
1186 // keep the same characteristics as the prev
brianosman52ede1d2016-06-20 08:25:02 -07001187 return SkImageInfo::Make(w, h, prev.colorType(), alphaType, sk_ref_sp(prev.colorSpace()));
reed129ed1c2016-02-22 06:42:31 -08001188 }
1189}
1190
reed4960eee2015-12-18 07:09:18 -08001191void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
1192 const SkRect* bounds = rec.fBounds;
1193 const SkPaint* paint = rec.fPaint;
1194 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1195
reed@google.comb93ba452014-03-10 19:47:58 +00001196#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001197 saveLayerFlags &= ~kDontClipToLayer_PrivateSaveLayerFlag;
reed@google.comb93ba452014-03-10 19:47:58 +00001198#endif
1199
reed8c30a812016-04-20 16:36:51 -07001200 SkLazyPaint lazyP;
1201 SkImageFilter* imageFilter = paint ? paint->getImageFilter() : NULL;
1202 SkMatrix stashedMatrix = fMCRec->fMatrix;
reed8c30a812016-04-20 16:36:51 -07001203 SkMatrix remainder;
1204 SkSize scale;
1205 /*
1206 * ImageFilters (so far) do not correctly handle matrices (CTM) that contain rotation/skew/etc.
1207 * but they do handle scaling. To accommodate this, we do the following:
1208 *
1209 * 1. Stash off the current CTM
1210 * 2. Decompose the CTM into SCALE and REMAINDER
1211 * 3. Wack the CTM to be just SCALE, and wrap the imagefilter with a MatrixImageFilter that
1212 * contains the REMAINDER
1213 * 4. Proceed as usual, allowing the client to draw into the layer (now with a scale-only CTM)
1214 * 5. During restore, we process the MatrixImageFilter, which applies REMAINDER to the output
1215 * of the original imagefilter, and draw that (via drawSprite)
1216 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1217 *
1218 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1219 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1220 */
reed96a04f32016-04-25 09:25:15 -07001221 if (imageFilter && !stashedMatrix.isScaleTranslate() && !imageFilter->canHandleComplexCTM() &&
reed8c30a812016-04-20 16:36:51 -07001222 stashedMatrix.decomposeScale(&scale, &remainder))
1223 {
1224 // We will restore the matrix (which we are overwriting here) in restore via fStashedMatrix
1225 this->internalSetMatrix(SkMatrix::MakeScale(scale.width(), scale.height()));
1226 SkPaint* p = lazyP.set(*paint);
1227 p->setImageFilter(SkImageFilter::MakeMatrixFilter(remainder,
1228 SkFilterQuality::kLow_SkFilterQuality,
1229 sk_ref_sp(imageFilter)));
1230 imageFilter = p->getImageFilter();
1231 paint = p;
1232 }
reed8c30a812016-04-20 16:36:51 -07001233
junov@chromium.orga907ac32012-02-24 21:54:07 +00001234 // do this before we create the layer. We don't call the public save() since
1235 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001236 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001237
1238 fDeviceCMDirty = true;
1239
1240 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001241 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
reed2ff1fce2014-12-11 07:07:37 -08001242 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001243 }
1244
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001245 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1246 // the clipRectBounds() call above?
1247 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001248 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001249 }
1250
reed4960eee2015-12-18 07:09:18 -08001251 bool isOpaque = SkToBool(saveLayerFlags & kIsOpaque_SaveLayerFlag);
reed8dc0ccb2015-03-20 06:32:52 -07001252 SkPixelGeometry geo = fProps.pixelGeometry();
1253 if (paint) {
reed76033be2015-03-14 10:54:31 -07001254 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001255 if (paint->getImageFilter() || paint->getColorFilter()) {
reed76033be2015-03-14 10:54:31 -07001256 isOpaque = false;
reed8dc0ccb2015-03-20 06:32:52 -07001257 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001258 }
1259 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001260
robertphillips5139e502016-07-19 05:10:40 -07001261 SkBaseDevice* priorDevice = this->getTopDevice();
reeda2217ef2016-07-20 06:04:34 -07001262 if (nullptr == priorDevice) {
reedb2db8982014-11-13 12:41:02 -08001263 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001264 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001265 }
reedb2db8982014-11-13 12:41:02 -08001266
robertphillips5139e502016-07-19 05:10:40 -07001267 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), isOpaque,
reed129ed1c2016-02-22 06:42:31 -08001268 paint);
1269
robertphillips5139e502016-07-19 05:10:40 -07001270 SkAutoTUnref<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001271 {
reed70ee31b2015-12-10 13:44:45 -08001272 const bool preserveLCDText = kOpaque_SkAlphaType == info.alphaType() ||
reed4960eee2015-12-18 07:09:18 -08001273 (saveLayerFlags & kPreserveLCDText_SaveLayerFlag);
reeddaa57bf2015-05-15 10:39:17 -07001274 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
reed70ee31b2015-12-10 13:44:45 -08001275 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
reedcd4051e2016-07-15 09:41:26 -07001276 preserveLCDText);
robertphillips5139e502016-07-19 05:10:40 -07001277 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1278 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001279 SkErrorInternals::SetError(kInternalError_SkError,
1280 "Unable to create device for layer.");
1281 return;
reed61f501f2015-04-29 08:34:00 -07001282 }
bungeman@google.come25c6842011-08-17 14:53:54 +00001283 }
robertphillips5139e502016-07-19 05:10:40 -07001284 newDevice->setOrigin(ir.fLeft, ir.fTop);
robertphillips7354a4b2015-12-16 05:08:27 -08001285
robertphillips5139e502016-07-19 05:10:40 -07001286 DeviceCM* layer = new DeviceCM(newDevice, paint, this, fConservativeRasterClip, stashedMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001287
1288 layer->fNext = fMCRec->fTopLayer;
1289 fMCRec->fLayer = layer;
1290 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001291
1292 if (rec.fBackdrop) {
1293 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice,
1294 fMCRec->fMatrix, this->getClipStack());
1295 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001296}
1297
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001298int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001299 if (0xFF == alpha) {
1300 return this->saveLayer(bounds, nullptr);
1301 } else {
1302 SkPaint tmpPaint;
1303 tmpPaint.setAlpha(alpha);
1304 return this->saveLayer(bounds, &tmpPaint);
1305 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001306}
1307
reed@android.com8a1c16f2008-12-17 15:59:43 +00001308void SkCanvas::internalRestore() {
1309 SkASSERT(fMCStack.count() != 0);
1310
1311 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001312 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001313
reed687fa1c2015-04-07 08:00:56 -07001314 fClipStack->restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001315
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001316 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001317 DeviceCM* layer = fMCRec->fLayer; // may be null
1318 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001319 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001320
1321 // now do the normal restore()
1322 fMCRec->~MCRec(); // balanced in save()
1323 fMCStack.pop_back();
1324 fMCRec = (MCRec*)fMCStack.back();
1325
1326 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1327 since if we're being recorded, we don't want to record this (the
1328 recorder will have already recorded the restore).
1329 */
bsalomon49f085d2014-09-05 13:34:00 -07001330 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001331 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001332 const SkIPoint& origin = layer->fDevice->getOrigin();
reed7503d602016-07-15 14:23:29 -07001333 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(), layer->fPaint);
reed8c30a812016-04-20 16:36:51 -07001334 // restore what we smashed in internalSaveLayer
1335 fMCRec->fMatrix = layer->fStashedMatrix;
reed@google.com8926b162012-03-23 15:36:36 +00001336 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001337 fDeviceCMDirty = true;
halcanary385fe4d2015-08-26 13:07:48 -07001338 delete layer;
reedb679ca82015-04-07 04:40:48 -07001339 } else {
1340 // we're at the root
reeda499f902015-05-01 09:34:31 -07001341 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001342 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001343 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001344 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001345 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001346}
1347
reede8f30622016-03-23 18:59:25 -07001348sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001349 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001350 props = &fProps;
1351 }
1352 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001353}
1354
reede8f30622016-03-23 18:59:25 -07001355sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001356 SkBaseDevice* dev = this->getDevice();
reede8f30622016-03-23 18:59:25 -07001357 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001358}
1359
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001360SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001361 return this->onImageInfo();
1362}
1363
1364SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001365 SkBaseDevice* dev = this->getDevice();
1366 if (dev) {
1367 return dev->imageInfo();
1368 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001369 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001370 }
1371}
1372
brianosman898235c2016-04-06 07:38:23 -07001373bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001374 return this->onGetProps(props);
1375}
1376
1377bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001378 SkBaseDevice* dev = this->getDevice();
1379 if (dev) {
1380 if (props) {
1381 *props = fProps;
1382 }
1383 return true;
1384 } else {
1385 return false;
1386 }
1387}
1388
reed6ceeebd2016-03-09 14:26:26 -08001389#ifdef SK_SUPPORT_LEGACY_PEEKPIXELS_PARMS
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001390const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
reed884e97c2015-05-26 11:31:54 -07001391 SkPixmap pmap;
reed6ceeebd2016-03-09 14:26:26 -08001392 if (this->peekPixels(&pmap)) {
1393 if (info) {
1394 *info = pmap.info();
1395 }
1396 if (rowBytes) {
1397 *rowBytes = pmap.rowBytes();
1398 }
1399 return pmap.addr();
reed884e97c2015-05-26 11:31:54 -07001400 }
reed6ceeebd2016-03-09 14:26:26 -08001401 return nullptr;
1402}
1403#endif
1404
1405bool SkCanvas::peekPixels(SkPixmap* pmap) {
1406 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001407}
1408
reed884e97c2015-05-26 11:31:54 -07001409bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001410 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001411 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001412}
1413
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001414void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001415 SkPixmap pmap;
1416 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001417 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001418 }
1419 if (info) {
1420 *info = pmap.info();
1421 }
1422 if (rowBytes) {
1423 *rowBytes = pmap.rowBytes();
1424 }
1425 if (origin) {
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001426 *origin = this->getTopDevice(false)->getOrigin();
1427 }
reed884e97c2015-05-26 11:31:54 -07001428 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001429}
1430
reed884e97c2015-05-26 11:31:54 -07001431bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001432 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001433 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001434}
1435
reed@android.com8a1c16f2008-12-17 15:59:43 +00001436/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001437
reed7503d602016-07-15 14:23:29 -07001438void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001439 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001440 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001441 paint = &tmp;
1442 }
reed@google.com4b226022011-01-11 18:32:13 +00001443
reed@google.com8926b162012-03-23 15:36:36 +00001444 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reeda2217ef2016-07-20 06:04:34 -07001445
reed@android.com8a1c16f2008-12-17 15:59:43 +00001446 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001447 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001448 paint = &looper.paint();
1449 SkImageFilter* filter = paint->getImageFilter();
1450 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
senorblancof35566e2016-04-18 10:32:02 -07001451 if (filter) {
reeda2217ef2016-07-20 06:04:34 -07001452 dstDev->drawSpecial(iter, srcDev->snapSpecial().get(), pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001453 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001454 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001455 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001456 }
reeda2217ef2016-07-20 06:04:34 -07001457
reed@google.com4e2b3d32011-04-07 14:18:59 +00001458 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001459}
1460
reed32704672015-12-16 08:27:10 -08001461/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001462
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001463void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001464 SkMatrix m;
1465 m.setTranslate(dx, dy);
1466 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001467}
1468
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001469void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001470 SkMatrix m;
1471 m.setScale(sx, sy);
1472 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001473}
1474
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001475void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001476 SkMatrix m;
1477 m.setRotate(degrees);
1478 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001479}
1480
bungeman7438bfc2016-07-12 15:01:19 -07001481void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1482 SkMatrix m;
1483 m.setRotate(degrees, px, py);
1484 this->concat(m);
1485}
1486
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001487void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001488 SkMatrix m;
1489 m.setSkew(sx, sy);
1490 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001491}
1492
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001493void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001494 if (matrix.isIdentity()) {
1495 return;
1496 }
1497
reed2ff1fce2014-12-11 07:07:37 -08001498 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001499 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001500 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001501 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001502
1503 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001504}
1505
reed8c30a812016-04-20 16:36:51 -07001506void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001507 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001508 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001509 fMCRec->fMatrix = matrix;
reed8c30a812016-04-20 16:36:51 -07001510}
1511
1512void SkCanvas::setMatrix(const SkMatrix& matrix) {
1513 this->checkForDeferredSave();
1514 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001515 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001516}
1517
reed@android.com8a1c16f2008-12-17 15:59:43 +00001518void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001519 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001520}
1521
vjiaoblacke5de1302016-07-13 14:05:28 -07001522void SkCanvas::translateZ(SkScalar z) {
1523 this->checkForDeferredSave();
1524 this->fMCRec->fCurDrawDepth += z;
1525 this->didTranslateZ(z);
1526}
1527
1528SkScalar SkCanvas::getZ() const {
1529 return this->fMCRec->fCurDrawDepth;
1530}
1531
reed@android.com8a1c16f2008-12-17 15:59:43 +00001532//////////////////////////////////////////////////////////////////////////////
1533
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001534void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed2d1afab2016-06-29 14:33:11 -07001535 if (!fAllowSoftClip) {
1536 doAA = false;
1537 }
1538
1539#ifdef SK_SUPPORT_PRECHECK_CLIPRECT
1540 // Check if we can quick-accept the clip call (and do nothing)
1541 //
reed74467162016-06-30 08:15:35 -07001542 if (SkRegion::kIntersect_Op == op && !doAA && fMCRec->fMatrix.isScaleTranslate()) {
reed6092b6e2016-07-10 11:45:34 -07001543 SkRect devR;
1544 fMCRec->fMatrix.mapRectScaleTranslate(&devR, rect);
reed2d1afab2016-06-29 14:33:11 -07001545 // NOTE: this check is CTM specific, since we might round differently with a different
1546 // CTM. Thus this is only 100% reliable if there is not global CTM scale to be
1547 // applied later (i.e. if this is going into a picture).
1548 if (devR.round().contains(fMCRec->fRasterClip.getBounds())) {
1549#if 0
1550 SkDebugf("ignored clipRect [%g %g %g %g]\n",
1551 rect.left(), rect.top(), rect.right(), rect.bottom());
1552#endif
1553 return;
1554 }
1555 }
1556#endif
1557
reed2ff1fce2014-12-11 07:07:37 -08001558 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001559 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1560 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001561}
1562
1563void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001564#ifdef SK_ENABLE_CLIP_QUICKREJECT
1565 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001566 if (fMCRec->fRasterClip.isEmpty()) {
reed2d1afab2016-06-29 14:33:11 -07001567 return;
reed@google.comda17f752012-08-16 18:27:05 +00001568 }
1569
reed@google.com3b3e8952012-08-16 20:53:31 +00001570 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001571 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001572 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001573
reed687fa1c2015-04-07 08:00:56 -07001574 fClipStack->clipEmpty();
reed2d1afab2016-06-29 14:33:11 -07001575 (void)fMCRec->fRasterClip.setEmpty();
1576 return;
reed@google.comda17f752012-08-16 18:27:05 +00001577 }
1578 }
1579#endif
1580
reed74467162016-06-30 08:15:35 -07001581 const bool isScaleTrans = fMCRec->fMatrix.isScaleTranslate();
reedc64eff52015-11-21 12:39:45 -08001582 SkRect devR;
reed74467162016-06-30 08:15:35 -07001583 if (isScaleTrans) {
reed6092b6e2016-07-10 11:45:34 -07001584 fMCRec->fMatrix.mapRectScaleTranslate(&devR, rect);
reedc64eff52015-11-21 12:39:45 -08001585 }
bsalomonac8cabd2015-11-20 18:53:07 -08001586
reed2d1afab2016-06-29 14:33:11 -07001587#ifndef SK_SUPPORT_PRECHECK_CLIPRECT
reedc64eff52015-11-21 12:39:45 -08001588 if (SkRegion::kIntersect_Op == op &&
1589 kHard_ClipEdgeStyle == edgeStyle
reed74467162016-06-30 08:15:35 -07001590 && isScaleTrans)
reedc64eff52015-11-21 12:39:45 -08001591 {
1592 if (devR.round().contains(fMCRec->fRasterClip.getBounds())) {
1593#if 0
1594 SkDebugf("------- ignored clipRect [%g %g %g %g]\n",
1595 rect.left(), rect.top(), rect.right(), rect.bottom());
1596#endif
1597 return;
1598 }
1599 }
reed2d1afab2016-06-29 14:33:11 -07001600#endif
reedc64eff52015-11-21 12:39:45 -08001601
1602 AutoValidateClip avc(this);
1603
1604 fDeviceCMDirty = true;
1605 fCachedLocalClipBoundsDirty = true;
1606
reed74467162016-06-30 08:15:35 -07001607 if (isScaleTrans) {
reedc64eff52015-11-21 12:39:45 -08001608 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1609 fClipStack->clipDevRect(devR, op, isAA);
senorblancoafc7cce2016-02-02 18:44:15 -08001610 fMCRec->fRasterClip.op(devR, this->getTopLayerBounds(), op, isAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001611 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001612 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001613 // and clip against that, since it can handle any matrix. However, to
1614 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1615 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001616 SkPath path;
1617
1618 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001619 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001620 }
1621}
1622
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001623void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001624 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001625 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001626 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001627 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1628 } else {
1629 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001630 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001631}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001632
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001633void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001634 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001635 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001636 AutoValidateClip avc(this);
1637
1638 fDeviceCMDirty = true;
1639 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001640 if (!fAllowSoftClip) {
1641 edgeStyle = kHard_ClipEdgeStyle;
1642 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001643
reed687fa1c2015-04-07 08:00:56 -07001644 fClipStack->clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001645
senorblancoafc7cce2016-02-02 18:44:15 -08001646 fMCRec->fRasterClip.op(transformedRRect, this->getTopLayerBounds(), op,
robertphillips125f19a2015-11-23 09:00:05 -08001647 kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001648 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001649 }
1650
1651 SkPath path;
1652 path.addRRect(rrect);
1653 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001654 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001655}
1656
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001657void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001658 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001659 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001660
1661 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1662 SkRect r;
1663 if (path.isRect(&r)) {
1664 this->onClipRect(r, op, edgeStyle);
1665 return;
1666 }
1667 SkRRect rrect;
1668 if (path.isOval(&r)) {
1669 rrect.setOval(r);
1670 this->onClipRRect(rrect, op, edgeStyle);
1671 return;
1672 }
1673 if (path.isRRect(&rrect)) {
1674 this->onClipRRect(rrect, op, edgeStyle);
1675 return;
1676 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001677 }
robertphillips39f05382015-11-24 09:30:12 -08001678
1679 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001680}
1681
1682void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001683#ifdef SK_ENABLE_CLIP_QUICKREJECT
1684 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001685 if (fMCRec->fRasterClip.isEmpty()) {
reed2d1afab2016-06-29 14:33:11 -07001686 return;
reed@google.comda17f752012-08-16 18:27:05 +00001687 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001688
reed@google.com3b3e8952012-08-16 20:53:31 +00001689 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001690 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001691 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001692
reed687fa1c2015-04-07 08:00:56 -07001693 fClipStack->clipEmpty();
reed2d1afab2016-06-29 14:33:11 -07001694 (void)fMCRec->fRasterClip.setEmpty();
1695 return;
reed@google.comda17f752012-08-16 18:27:05 +00001696 }
1697 }
1698#endif
1699
reed@google.com5c3d1472011-02-22 19:12:23 +00001700 AutoValidateClip avc(this);
1701
reed@android.com8a1c16f2008-12-17 15:59:43 +00001702 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001703 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001704 if (!fAllowSoftClip) {
1705 edgeStyle = kHard_ClipEdgeStyle;
1706 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001707
1708 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001709 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001710
reed@google.comfe701122011-11-08 19:41:23 +00001711 // Check if the transfomation, or the original path itself
1712 // made us empty. Note this can also happen if we contained NaN
1713 // values. computing the bounds detects this, and will set our
1714 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1715 if (devPath.getBounds().isEmpty()) {
1716 // resetting the path will remove any NaN or other wanky values
1717 // that might upset our scan converter.
1718 devPath.reset();
1719 }
1720
reed@google.com5c3d1472011-02-22 19:12:23 +00001721 // if we called path.swap() we could avoid a deep copy of this path
reed687fa1c2015-04-07 08:00:56 -07001722 fClipStack->clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001723
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001724 if (fAllowSimplifyClip) {
fmalita1a481fe2015-02-04 07:39:34 -08001725 bool clipIsAA = getClipStack()->asPath(&devPath);
1726 if (clipIsAA) {
1727 edgeStyle = kSoft_ClipEdgeStyle;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001728 }
fmalita1a481fe2015-02-04 07:39:34 -08001729
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001730 op = SkRegion::kReplace_Op;
1731 }
1732
senorblancoafc7cce2016-02-02 18:44:15 -08001733 fMCRec->fRasterClip.op(devPath, this->getTopLayerBounds(), op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001734}
1735
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001736void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed2ff1fce2014-12-11 07:07:37 -08001737 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001738 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001739}
1740
1741void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001742 AutoValidateClip avc(this);
1743
reed@android.com8a1c16f2008-12-17 15:59:43 +00001744 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001745 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001746
reed@google.com5c3d1472011-02-22 19:12:23 +00001747 // todo: signal fClipStack that we have a region, and therefore (I guess)
1748 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001749 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001750
reed1f836ee2014-07-07 07:49:34 -07001751 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001752}
1753
reed@google.com819c9212011-02-23 18:56:55 +00001754#ifdef SK_DEBUG
1755void SkCanvas::validateClip() const {
1756 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001757 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001758 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001759 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001760 return;
1761 }
1762
reed@google.com819c9212011-02-23 18:56:55 +00001763 SkIRect ir;
1764 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001765 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001766
reed687fa1c2015-04-07 08:00:56 -07001767 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001768 const SkClipStack::Element* element;
halcanary96fcdcc2015-08-27 07:41:13 -07001769 while ((element = iter.next()) != nullptr) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001770 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001771 case SkClipStack::Element::kRect_Type:
1772 element->getRect().round(&ir);
1773 tmpClip.op(ir, element->getOp());
1774 break;
1775 case SkClipStack::Element::kEmpty_Type:
1776 tmpClip.setEmpty();
1777 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001778 default: {
1779 SkPath path;
1780 element->asPath(&path);
senorblancoafc7cce2016-02-02 18:44:15 -08001781 tmpClip.op(path, this->getTopLayerBounds(), element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001782 break;
1783 }
reed@google.com819c9212011-02-23 18:56:55 +00001784 }
1785 }
reed@google.com819c9212011-02-23 18:56:55 +00001786}
1787#endif
1788
reed@google.com90c07ea2012-04-13 13:50:27 +00001789void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001790 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001791 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001792
halcanary96fcdcc2015-08-27 07:41:13 -07001793 while ((element = iter.next()) != nullptr) {
fmalitac3b589a2014-06-05 12:40:07 -07001794 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001795 }
1796}
1797
reed@google.com5c3d1472011-02-22 19:12:23 +00001798///////////////////////////////////////////////////////////////////////////////
1799
reed@google.com754de5f2014-02-24 19:38:20 +00001800bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001801 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001802}
1803
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001804bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001805 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001806}
1807
reed@google.com3b3e8952012-08-16 20:53:31 +00001808bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001809 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001810 return true;
1811
reed1f836ee2014-07-07 07:49:34 -07001812 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001813 return true;
1814 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001815
reed1f836ee2014-07-07 07:49:34 -07001816 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001817 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001818 fMCRec->fMatrix.mapRect(&dst, rect);
reedb07a94f2014-11-19 05:03:18 -08001819 return !SkIRect::Intersects(dst.roundOut(), fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001820 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001821 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001822
reed@android.coma380ae42009-07-21 01:17:02 +00001823 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001824 // TODO: should we use | instead, or compare all 4 at once?
1825 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001826 return true;
1827 }
reed@google.comc0784db2013-12-13 21:16:12 +00001828 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001829 return true;
1830 }
1831 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001832 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001833}
1834
reed@google.com3b3e8952012-08-16 20:53:31 +00001835bool SkCanvas::quickReject(const SkPath& path) const {
1836 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001837}
1838
reed@google.com3b3e8952012-08-16 20:53:31 +00001839bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001840 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001841 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001842 return false;
1843 }
1844
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001845 SkMatrix inverse;
1846 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001847 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001848 if (bounds) {
1849 bounds->setEmpty();
1850 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001851 return false;
1852 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001853
bsalomon49f085d2014-09-05 13:34:00 -07001854 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001855 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001856 // adjust it outwards in case we are antialiasing
1857 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001858
reed@google.com8f4d2302013-12-17 16:44:46 +00001859 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1860 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001861 inverse.mapRect(bounds, r);
1862 }
1863 return true;
1864}
1865
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001866bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001867 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001868 if (clip.isEmpty()) {
1869 if (bounds) {
1870 bounds->setEmpty();
1871 }
1872 return false;
1873 }
1874
bsalomon49f085d2014-09-05 13:34:00 -07001875 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001876 *bounds = clip.getBounds();
1877 }
1878 return true;
1879}
1880
reed@android.com8a1c16f2008-12-17 15:59:43 +00001881const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001882 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001883}
1884
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001885const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001886 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001887}
1888
robertphillips175dd9b2016-04-28 14:32:04 -07001889GrDrawContext* SkCanvas::internal_private_accessTopLayerDrawContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001890 SkBaseDevice* dev = this->getTopDevice();
robertphillips175dd9b2016-04-28 14:32:04 -07001891 return dev ? dev->accessDrawContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001892}
1893
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001894GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001895 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001896 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001897}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001898
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001899void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1900 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001901 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001902 if (outer.isEmpty()) {
1903 return;
1904 }
1905 if (inner.isEmpty()) {
1906 this->drawRRect(outer, paint);
1907 return;
1908 }
1909
1910 // We don't have this method (yet), but technically this is what we should
1911 // be able to assert...
1912 // SkASSERT(outer.contains(inner));
1913 //
1914 // For now at least check for containment of bounds
1915 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1916
1917 this->onDrawDRRect(outer, inner, paint);
1918}
1919
reed41af9662015-01-05 07:49:08 -08001920// These need to stop being virtual -- clients need to override the onDraw... versions
1921
1922void SkCanvas::drawPaint(const SkPaint& paint) {
1923 this->onDrawPaint(paint);
1924}
1925
1926void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1927 this->onDrawRect(r, paint);
1928}
1929
1930void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1931 this->onDrawOval(r, paint);
1932}
1933
1934void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1935 this->onDrawRRect(rrect, paint);
1936}
1937
1938void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1939 this->onDrawPoints(mode, count, pts, paint);
1940}
1941
1942void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
1943 const SkPoint texs[], const SkColor colors[], SkXfermode* xmode,
1944 const uint16_t indices[], int indexCount, const SkPaint& paint) {
1945 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
1946 indices, indexCount, paint);
1947}
1948
1949void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1950 this->onDrawPath(path, paint);
1951}
1952
reeda85d4d02015-05-06 12:56:48 -07001953void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001954 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001955 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001956}
1957
reede47829b2015-08-06 10:02:53 -07001958void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1959 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001960 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001961 if (dst.isEmpty() || src.isEmpty()) {
1962 return;
1963 }
1964 this->onDrawImageRect(image, &src, dst, paint, constraint);
1965}
reed41af9662015-01-05 07:49:08 -08001966
reed84984ef2015-07-17 07:09:43 -07001967void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1968 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001969 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001970 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001971}
1972
reede47829b2015-08-06 10:02:53 -07001973void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
1974 SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001975 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001976 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
1977 constraint);
1978}
reede47829b2015-08-06 10:02:53 -07001979
reed4c21dc52015-06-25 12:32:03 -07001980void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1981 const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001982 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07001983 if (dst.isEmpty()) {
1984 return;
1985 }
1986 if (!SkNinePatchIter::Valid(image->width(), image->height(), center)) {
reede47829b2015-08-06 10:02:53 -07001987 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001988 }
1989 this->onDrawImageNine(image, center, dst, paint);
1990}
1991
reed41af9662015-01-05 07:49:08 -08001992void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001993 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001994 return;
1995 }
reed41af9662015-01-05 07:49:08 -08001996 this->onDrawBitmap(bitmap, dx, dy, paint);
1997}
1998
reede47829b2015-08-06 10:02:53 -07001999void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07002000 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002001 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07002002 return;
2003 }
reede47829b2015-08-06 10:02:53 -07002004 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08002005}
2006
reed84984ef2015-07-17 07:09:43 -07002007void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2008 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002009 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002010}
2011
reede47829b2015-08-06 10:02:53 -07002012void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2013 SrcRectConstraint constraint) {
2014 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2015 constraint);
2016}
reede47829b2015-08-06 10:02:53 -07002017
reed41af9662015-01-05 07:49:08 -08002018void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2019 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07002020 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002021 return;
2022 }
reed4c21dc52015-06-25 12:32:03 -07002023 if (!SkNinePatchIter::Valid(bitmap.width(), bitmap.height(), center)) {
reeda5517e22015-07-14 10:54:12 -07002024 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002025 }
reed41af9662015-01-05 07:49:08 -08002026 this->onDrawBitmapNine(bitmap, center, dst, paint);
2027}
2028
reed71c3c762015-06-24 10:29:17 -07002029void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2030 const SkColor colors[], int count, SkXfermode::Mode mode,
2031 const SkRect* cull, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002032 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002033 if (count <= 0) {
2034 return;
2035 }
2036 SkASSERT(atlas);
2037 SkASSERT(xform);
2038 SkASSERT(tex);
2039 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
2040}
2041
reedf70b5312016-03-04 16:36:20 -08002042void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2043 if (key) {
2044 this->onDrawAnnotation(rect, key, value);
2045 }
2046}
2047
reede47829b2015-08-06 10:02:53 -07002048void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2049 const SkPaint* paint, SrcRectConstraint constraint) {
2050 if (src) {
2051 this->drawImageRect(image, *src, dst, paint, constraint);
2052 } else {
2053 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2054 dst, paint, constraint);
2055 }
2056}
2057void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2058 const SkPaint* paint, SrcRectConstraint constraint) {
2059 if (src) {
2060 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2061 } else {
2062 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2063 dst, paint, constraint);
2064 }
2065}
2066
tomhudsoncb3bd182016-05-18 07:24:16 -07002067void SkCanvas::temporary_internal_describeTopLayer(SkMatrix* matrix, SkIRect* clip_bounds) {
2068 SkIRect layer_bounds = this->getTopLayerBounds();
2069 if (matrix) {
2070 *matrix = this->getTotalMatrix();
2071 matrix->preTranslate(-layer_bounds.left(), -layer_bounds.top());
2072 }
2073 if (clip_bounds) {
2074 this->getClipDeviceBounds(clip_bounds);
2075 clip_bounds->offset(-layer_bounds.left(), -layer_bounds.top());
2076 }
2077}
2078
reed@android.com8a1c16f2008-12-17 15:59:43 +00002079//////////////////////////////////////////////////////////////////////////////
2080// These are the virtual drawing methods
2081//////////////////////////////////////////////////////////////////////////////
2082
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002083void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002084 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002085 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2086 }
2087}
2088
reed41af9662015-01-05 07:49:08 -08002089void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002090 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002091 this->internalDrawPaint(paint);
2092}
2093
2094void SkCanvas::internalDrawPaint(const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002095 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002096
2097 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002098 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002099 }
2100
reed@google.com4e2b3d32011-04-07 14:18:59 +00002101 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002102}
2103
reed41af9662015-01-05 07:49:08 -08002104void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2105 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002106 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002107 if ((long)count <= 0) {
2108 return;
2109 }
2110
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002111 SkRect r, storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002112 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002113 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002114 // special-case 2 points (common for drawing a single line)
2115 if (2 == count) {
2116 r.set(pts[0], pts[1]);
2117 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00002118 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002119 }
senorblanco87e066e2015-10-28 11:23:36 -07002120 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2121 return;
2122 }
2123 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002124 }
reed@google.coma584aed2012-05-16 14:06:02 +00002125
halcanary96fcdcc2015-08-27 07:41:13 -07002126 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002127
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002128 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002129
reed@android.com8a1c16f2008-12-17 15:59:43 +00002130 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002131 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002132 }
reed@google.com4b226022011-01-11 18:32:13 +00002133
reed@google.com4e2b3d32011-04-07 14:18:59 +00002134 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002135}
2136
reed41af9662015-01-05 07:49:08 -08002137void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002138 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002139 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002140 const SkRect* bounds = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002141 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08002142 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
2143 // To prevent accidental rejecting at this stage, we have to sort it before we check.
2144 SkRect tmp(r);
2145 tmp.sort();
2146
senorblanco87e066e2015-10-28 11:23:36 -07002147 if (this->quickReject(paint.computeFastBounds(tmp, &storage))) {
2148 return;
2149 }
2150 bounds = &r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002151 }
reed@google.com4b226022011-01-11 18:32:13 +00002152
reedc83a2972015-07-16 07:40:45 -07002153 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002154
2155 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002156 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002157 }
2158
reed@google.com4e2b3d32011-04-07 14:18:59 +00002159 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002160}
2161
reed41af9662015-01-05 07:49:08 -08002162void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002163 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002164 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002165 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002166 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002167 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2168 return;
2169 }
2170 bounds = &oval;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002171 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002172
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002173 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002174
2175 while (iter.next()) {
2176 iter.fDevice->drawOval(iter, oval, looper.paint());
2177 }
2178
2179 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002180}
2181
reed41af9662015-01-05 07:49:08 -08002182void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002183 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002184 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002185 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002186 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002187 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2188 return;
2189 }
2190 bounds = &rrect.getBounds();
reed@google.com4ed0fb72012-12-12 20:48:18 +00002191 }
2192
2193 if (rrect.isRect()) {
2194 // call the non-virtual version
2195 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002196 return;
2197 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002198 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002199 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2200 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002201 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002202
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002203 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002204
2205 while (iter.next()) {
2206 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2207 }
2208
2209 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002210}
2211
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002212void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2213 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002214 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002215 const SkRect* bounds = nullptr;
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002216 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002217 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2218 return;
2219 }
2220 bounds = &outer.getBounds();
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002221 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002222
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002223 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002224
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002225 while (iter.next()) {
2226 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2227 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002228
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002229 LOOPER_END
2230}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002231
reed41af9662015-01-05 07:49:08 -08002232void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002233 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002234 if (!path.isFinite()) {
2235 return;
2236 }
2237
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002238 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002239 const SkRect* bounds = nullptr;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002240 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002241 const SkRect& pathBounds = path.getBounds();
senorblanco87e066e2015-10-28 11:23:36 -07002242 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2243 return;
2244 }
2245 bounds = &pathBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002246 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002247
2248 const SkRect& r = path.getBounds();
2249 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002250 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002251 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002252 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002253 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002254 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002255
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002256 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002257
2258 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002259 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002260 }
2261
reed@google.com4e2b3d32011-04-07 14:18:59 +00002262 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002263}
2264
reed262a71b2015-12-05 13:07:27 -08002265bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002266 if (!paint.getImageFilter()) {
2267 return false;
2268 }
2269
2270 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002271 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002272 return false;
2273 }
2274
2275 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2276 // Once we can filter and the filter will return a result larger than itself, we should be
2277 // able to remove this constraint.
2278 // skbug.com/4526
2279 //
2280 SkPoint pt;
2281 ctm.mapXY(x, y, &pt);
2282 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2283 return ir.contains(fMCRec->fRasterClip.getBounds());
2284}
2285
reeda85d4d02015-05-06 12:56:48 -07002286void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002287 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002288 SkRect bounds = SkRect::MakeXYWH(x, y,
2289 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002290 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002291 SkRect tmp = bounds;
2292 if (paint) {
2293 paint->computeFastBounds(tmp, &tmp);
2294 }
2295 if (this->quickReject(tmp)) {
2296 return;
2297 }
reeda85d4d02015-05-06 12:56:48 -07002298 }
halcanary9d524f22016-03-29 09:03:52 -07002299
reeda85d4d02015-05-06 12:56:48 -07002300 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002301 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002302 paint = lazy.init();
2303 }
reed262a71b2015-12-05 13:07:27 -08002304
reeda2217ef2016-07-20 06:04:34 -07002305 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002306 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2307 *paint);
2308 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002309 special = this->getDevice()->makeSpecial(image);
2310 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002311 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002312 }
2313 }
2314
reed262a71b2015-12-05 13:07:27 -08002315 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
2316
reeda85d4d02015-05-06 12:56:48 -07002317 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002318 const SkPaint& pnt = looper.paint();
reeda2217ef2016-07-20 06:04:34 -07002319 if (special) {
2320 SkPoint pt;
2321 iter.fMatrix->mapXY(x, y, &pt);
2322 iter.fDevice->drawSpecial(iter, special.get(),
2323 SkScalarRoundToInt(pt.fX),
2324 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002325 } else {
2326 iter.fDevice->drawImage(iter, image, x, y, pnt);
2327 }
reeda85d4d02015-05-06 12:56:48 -07002328 }
halcanary9d524f22016-03-29 09:03:52 -07002329
reeda85d4d02015-05-06 12:56:48 -07002330 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002331}
2332
reed41af9662015-01-05 07:49:08 -08002333void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002334 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002335 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
halcanary96fcdcc2015-08-27 07:41:13 -07002336 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002337 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002338 if (paint) {
2339 paint->computeFastBounds(dst, &storage);
2340 }
2341 if (this->quickReject(storage)) {
2342 return;
2343 }
reeda85d4d02015-05-06 12:56:48 -07002344 }
2345 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002346 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002347 paint = lazy.init();
2348 }
halcanary9d524f22016-03-29 09:03:52 -07002349
senorblancoc41e7e12015-12-07 12:51:30 -08002350 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002351 image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002352
reeda85d4d02015-05-06 12:56:48 -07002353 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002354 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002355 }
halcanary9d524f22016-03-29 09:03:52 -07002356
reeda85d4d02015-05-06 12:56:48 -07002357 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002358}
2359
reed41af9662015-01-05 07:49:08 -08002360void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002361 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002362 SkDEBUGCODE(bitmap.validate();)
2363
reed33366972015-10-08 09:22:02 -07002364 if (bitmap.drawsNothing()) {
2365 return;
2366 }
2367
2368 SkLazyPaint lazy;
2369 if (nullptr == paint) {
2370 paint = lazy.init();
2371 }
2372
2373 const SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2374
2375 SkRect storage;
2376 const SkRect* bounds = nullptr;
2377 if (paint->canComputeFastBounds()) {
2378 bitmap.getBounds(&storage);
2379 matrix.mapRect(&storage);
senorblanco87e066e2015-10-28 11:23:36 -07002380 SkRect tmp = storage;
2381 if (this->quickReject(paint->computeFastBounds(tmp, &tmp))) {
2382 return;
2383 }
2384 bounds = &storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002385 }
reed@google.com4b226022011-01-11 18:32:13 +00002386
reeda2217ef2016-07-20 06:04:34 -07002387 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002388 bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(), bitmap.height(),
2389 *paint);
2390 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002391 special = this->getDevice()->makeSpecial(bitmap);
2392 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002393 drawAsSprite = false;
2394 }
2395 }
2396
reed262a71b2015-12-05 13:07:27 -08002397 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds)
reed33366972015-10-08 09:22:02 -07002398
2399 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002400 const SkPaint& pnt = looper.paint();
reeda2217ef2016-07-20 06:04:34 -07002401 if (special) {
reed262a71b2015-12-05 13:07:27 -08002402 SkPoint pt;
2403 iter.fMatrix->mapXY(x, y, &pt);
reeda2217ef2016-07-20 06:04:34 -07002404 iter.fDevice->drawSpecial(iter, special.get(),
2405 SkScalarRoundToInt(pt.fX),
2406 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002407 } else {
2408 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
2409 }
reed33366972015-10-08 09:22:02 -07002410 }
reeda2217ef2016-07-20 06:04:34 -07002411
reed33366972015-10-08 09:22:02 -07002412 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002413}
2414
reed@google.com9987ec32011-09-07 11:57:52 +00002415// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002416void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002417 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002418 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002419 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002420 return;
2421 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002422
halcanary96fcdcc2015-08-27 07:41:13 -07002423 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002424 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002425 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2426 return;
2427 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002428 }
reed@google.com3d608122011-11-21 15:16:16 +00002429
reed@google.com33535f32012-09-25 15:37:50 +00002430 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002431 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002432 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002433 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002434
senorblancoc41e7e12015-12-07 12:51:30 -08002435 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002436 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002437
reed@google.com33535f32012-09-25 15:37:50 +00002438 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002439 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002440 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002441
reed@google.com33535f32012-09-25 15:37:50 +00002442 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002443}
2444
reed41af9662015-01-05 07:49:08 -08002445void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002446 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002447 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002448 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002449 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002450}
2451
reed4c21dc52015-06-25 12:32:03 -07002452void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2453 const SkPaint* paint) {
2454 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
halcanary9d524f22016-03-29 09:03:52 -07002455
halcanary96fcdcc2015-08-27 07:41:13 -07002456 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002457 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002458 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2459 return;
2460 }
reed@google.com3d608122011-11-21 15:16:16 +00002461 }
halcanary9d524f22016-03-29 09:03:52 -07002462
reed4c21dc52015-06-25 12:32:03 -07002463 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002464 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002465 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002466 }
halcanary9d524f22016-03-29 09:03:52 -07002467
senorblancoc41e7e12015-12-07 12:51:30 -08002468 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002469
reed4c21dc52015-06-25 12:32:03 -07002470 while (iter.next()) {
2471 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002472 }
halcanary9d524f22016-03-29 09:03:52 -07002473
reed4c21dc52015-06-25 12:32:03 -07002474 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002475}
2476
reed41af9662015-01-05 07:49:08 -08002477void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2478 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002479 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002480 SkDEBUGCODE(bitmap.validate();)
2481
halcanary96fcdcc2015-08-27 07:41:13 -07002482 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002483 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002484 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2485 return;
2486 }
reed4c21dc52015-06-25 12:32:03 -07002487 }
halcanary9d524f22016-03-29 09:03:52 -07002488
reed4c21dc52015-06-25 12:32:03 -07002489 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002490 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002491 paint = lazy.init();
2492 }
halcanary9d524f22016-03-29 09:03:52 -07002493
senorblancoc41e7e12015-12-07 12:51:30 -08002494 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002495
reed4c21dc52015-06-25 12:32:03 -07002496 while (iter.next()) {
2497 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2498 }
halcanary9d524f22016-03-29 09:03:52 -07002499
reed4c21dc52015-06-25 12:32:03 -07002500 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002501}
2502
reed@google.comf67e4cf2011-03-15 20:56:58 +00002503class SkDeviceFilteredPaint {
2504public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002505 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002506 uint32_t filteredFlags = device->filterTextFlags(paint);
2507 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002508 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002509 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002510 fPaint = newPaint;
2511 } else {
2512 fPaint = &paint;
2513 }
2514 }
2515
reed@google.comf67e4cf2011-03-15 20:56:58 +00002516 const SkPaint& paint() const { return *fPaint; }
2517
2518private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002519 const SkPaint* fPaint;
2520 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002521};
2522
bungeman@google.com52c748b2011-08-22 21:30:43 +00002523void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2524 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002525 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002526 draw.fDevice->drawRect(draw, r, paint);
2527 } else {
2528 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002529 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002530 draw.fDevice->drawRect(draw, r, p);
2531 }
2532}
2533
2534void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2535 const char text[], size_t byteLength,
2536 SkScalar x, SkScalar y) {
halcanary96fcdcc2015-08-27 07:41:13 -07002537 SkASSERT(byteLength == 0 || text != nullptr);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002538
2539 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07002540 if (text == nullptr || byteLength == 0 ||
reed1e7f5e72016-04-27 07:49:17 -07002541 draw.fRC->isEmpty() ||
halcanary96fcdcc2015-08-27 07:41:13 -07002542 (paint.getAlpha() == 0 && paint.getXfermode() == nullptr)) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002543 return;
2544 }
2545
2546 SkScalar width = 0;
2547 SkPoint start;
2548
2549 start.set(0, 0); // to avoid warning
2550 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2551 SkPaint::kStrikeThruText_Flag)) {
2552 width = paint.measureText(text, byteLength);
2553
2554 SkScalar offsetX = 0;
2555 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2556 offsetX = SkScalarHalf(width);
2557 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2558 offsetX = width;
2559 }
2560 start.set(x - offsetX, y);
2561 }
2562
2563 if (0 == width) {
2564 return;
2565 }
2566
2567 uint32_t flags = paint.getFlags();
2568
2569 if (flags & (SkPaint::kUnderlineText_Flag |
2570 SkPaint::kStrikeThruText_Flag)) {
2571 SkScalar textSize = paint.getTextSize();
2572 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2573 SkRect r;
2574
2575 r.fLeft = start.fX;
2576 r.fRight = start.fX + width;
2577
2578 if (flags & SkPaint::kUnderlineText_Flag) {
2579 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2580 start.fY);
2581 r.fTop = offset;
2582 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002583 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002584 }
2585 if (flags & SkPaint::kStrikeThruText_Flag) {
2586 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2587 start.fY);
2588 r.fTop = offset;
2589 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002590 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002591 }
2592 }
2593}
2594
reed@google.come0d9ce82014-04-23 04:00:17 +00002595void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2596 const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002597 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002598
2599 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002600 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002601 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002602 DrawTextDecorations(iter, dfp.paint(),
2603 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002604 }
2605
reed@google.com4e2b3d32011-04-07 14:18:59 +00002606 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002607}
2608
reed@google.come0d9ce82014-04-23 04:00:17 +00002609void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2610 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002611 SkPoint textOffset = SkPoint::Make(0, 0);
2612
halcanary96fcdcc2015-08-27 07:41:13 -07002613 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002614
reed@android.com8a1c16f2008-12-17 15:59:43 +00002615 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002616 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002617 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002618 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002619 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002620
reed@google.com4e2b3d32011-04-07 14:18:59 +00002621 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002622}
2623
reed@google.come0d9ce82014-04-23 04:00:17 +00002624void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2625 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002626
2627 SkPoint textOffset = SkPoint::Make(0, constY);
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, xpos, 1, 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::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2641 const SkMatrix* matrix, const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002642 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002643
reed@android.com8a1c16f2008-12-17 15:59:43 +00002644 while (iter.next()) {
2645 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002646 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002647 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002648
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002649 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002650}
2651
reed45561a02016-07-07 12:47:17 -07002652void SkCanvas::onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2653 const SkRect* cullRect, const SkPaint& paint) {
2654 if (cullRect && this->quickReject(*cullRect)) {
2655 return;
2656 }
2657
2658 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
2659
2660 while (iter.next()) {
2661 iter.fDevice->drawTextRSXform(iter, text, byteLength, xform, looper.paint());
2662 }
2663
2664 LOOPER_END
2665}
2666
fmalita00d5c2c2014-08-21 08:53:26 -07002667void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2668 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002669
fmalita85d5eb92015-03-04 11:20:12 -08002670 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002671 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002672 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002673 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002674 SkRect tmp;
2675 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2676 return;
2677 }
2678 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002679 }
2680
fmalita024f9962015-03-03 19:08:17 -08002681 // We cannot filter in the looper as we normally do, because the paint is
2682 // incomplete at this point (text-related attributes are embedded within blob run paints).
2683 SkDrawFilter* drawFilter = fMCRec->fFilter;
halcanary96fcdcc2015-08-27 07:41:13 -07002684 fMCRec->fFilter = nullptr;
fmalita024f9962015-03-03 19:08:17 -08002685
fmalita85d5eb92015-03-04 11:20:12 -08002686 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002687
fmalitaaa1b9122014-08-28 14:32:24 -07002688 while (iter.next()) {
2689 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002690 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002691 }
2692
fmalitaaa1b9122014-08-28 14:32:24 -07002693 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002694
2695 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002696}
2697
reed@google.come0d9ce82014-04-23 04:00:17 +00002698// These will become non-virtual, so they always call the (virtual) onDraw... method
2699void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2700 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002701 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002702 this->onDrawText(text, byteLength, x, y, paint);
2703}
2704void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2705 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002706 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002707 this->onDrawPosText(text, byteLength, pos, paint);
2708}
2709void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2710 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002711 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002712 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2713}
2714void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2715 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002716 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002717 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2718}
reed45561a02016-07-07 12:47:17 -07002719void SkCanvas::drawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2720 const SkRect* cullRect, const SkPaint& paint) {
2721 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextRSXform()");
2722 if (byteLength) {
2723 this->onDrawTextRSXform(text, byteLength, xform, cullRect, paint);
2724 }
2725}
fmalita00d5c2c2014-08-21 08:53:26 -07002726void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2727 const SkPaint& paint) {
reede3b38ce2016-01-08 09:18:44 -08002728 RETURN_ON_NULL(blob);
danakj9881d632014-11-26 12:41:06 -08002729 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
reede3b38ce2016-01-08 09:18:44 -08002730 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002731}
reed@google.come0d9ce82014-04-23 04:00:17 +00002732
reed41af9662015-01-05 07:49:08 -08002733void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2734 const SkPoint verts[], const SkPoint texs[],
2735 const SkColor colors[], SkXfermode* xmode,
2736 const uint16_t indices[], int indexCount,
2737 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002738 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
halcanary96fcdcc2015-08-27 07:41:13 -07002739 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
reed@google.com4b226022011-01-11 18:32:13 +00002740
reed@android.com8a1c16f2008-12-17 15:59:43 +00002741 while (iter.next()) {
2742 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002743 colors, xmode, indices, indexCount,
2744 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002745 }
reed@google.com4b226022011-01-11 18:32:13 +00002746
reed@google.com4e2b3d32011-04-07 14:18:59 +00002747 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002748}
2749
dandovb3c9d1c2014-08-12 08:34:29 -07002750void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2751 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002752 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
halcanary96fcdcc2015-08-27 07:41:13 -07002753 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002754 return;
2755 }
mtklein6cfa73a2014-08-13 13:33:49 -07002756
dandovecfff212014-08-04 10:02:00 -07002757 // Since a patch is always within the convex hull of the control points, we discard it when its
2758 // bounding rectangle is completely outside the current clip.
2759 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002760 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002761 if (this->quickReject(bounds)) {
2762 return;
2763 }
mtklein6cfa73a2014-08-13 13:33:49 -07002764
dandovb3c9d1c2014-08-12 08:34:29 -07002765 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2766}
2767
2768void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2769 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2770
halcanary96fcdcc2015-08-27 07:41:13 -07002771 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002772
dandovecfff212014-08-04 10:02:00 -07002773 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002774 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002775 }
mtklein6cfa73a2014-08-13 13:33:49 -07002776
dandovecfff212014-08-04 10:02:00 -07002777 LOOPER_END
2778}
2779
reeda8db7282015-07-07 10:22:31 -07002780void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
reede3b38ce2016-01-08 09:18:44 -08002781 RETURN_ON_NULL(dr);
2782 if (x || y) {
2783 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2784 this->onDrawDrawable(dr, &matrix);
2785 } else {
2786 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002787 }
2788}
2789
reeda8db7282015-07-07 10:22:31 -07002790void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reede3b38ce2016-01-08 09:18:44 -08002791 RETURN_ON_NULL(dr);
2792 if (matrix && matrix->isIdentity()) {
2793 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002794 }
reede3b38ce2016-01-08 09:18:44 -08002795 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002796}
2797
2798void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2799 SkRect bounds = dr->getBounds();
2800 if (matrix) {
2801 matrix->mapRect(&bounds);
2802 }
2803 if (this->quickReject(bounds)) {
2804 return;
2805 }
2806 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002807}
2808
reed71c3c762015-06-24 10:29:17 -07002809void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2810 const SkColor colors[], int count, SkXfermode::Mode mode,
2811 const SkRect* cull, const SkPaint* paint) {
2812 if (cull && this->quickReject(*cull)) {
2813 return;
2814 }
2815
2816 SkPaint pnt;
2817 if (paint) {
2818 pnt = *paint;
2819 }
halcanary9d524f22016-03-29 09:03:52 -07002820
halcanary96fcdcc2015-08-27 07:41:13 -07002821 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr)
reed71c3c762015-06-24 10:29:17 -07002822 while (iter.next()) {
2823 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, mode, pnt);
2824 }
2825 LOOPER_END
2826}
2827
reedf70b5312016-03-04 16:36:20 -08002828void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2829 SkASSERT(key);
2830
2831 SkPaint paint;
2832 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, nullptr)
2833 while (iter.next()) {
2834 iter.fDevice->drawAnnotation(iter, rect, key, value);
2835 }
2836 LOOPER_END
2837}
2838
reed@android.com8a1c16f2008-12-17 15:59:43 +00002839//////////////////////////////////////////////////////////////////////////////
2840// These methods are NOT virtual, and therefore must call back into virtual
2841// methods, rather than actually drawing themselves.
2842//////////////////////////////////////////////////////////////////////////////
2843
2844void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002845 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002846 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002847 SkPaint paint;
2848
2849 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002850 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002851 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002852 }
2853 this->drawPaint(paint);
2854}
2855
reed@android.com845fdac2009-06-23 03:01:32 +00002856void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002857 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002858 SkPaint paint;
2859
2860 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002861 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002862 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002863 }
2864 this->drawPaint(paint);
2865}
2866
2867void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002868 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002869 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002870
reed@android.com8a1c16f2008-12-17 15:59:43 +00002871 pt.set(x, y);
2872 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2873}
2874
2875void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002876 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002877 SkPoint pt;
2878 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002879
reed@android.com8a1c16f2008-12-17 15:59:43 +00002880 pt.set(x, y);
2881 paint.setColor(color);
2882 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2883}
2884
2885void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2886 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002887 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002888 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002889
reed@android.com8a1c16f2008-12-17 15:59:43 +00002890 pts[0].set(x0, y0);
2891 pts[1].set(x1, y1);
2892 this->drawPoints(kLines_PointMode, 2, pts, paint);
2893}
2894
2895void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2896 SkScalar right, SkScalar bottom,
2897 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002898 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002899 SkRect r;
2900
2901 r.set(left, top, right, bottom);
2902 this->drawRect(r, paint);
2903}
2904
2905void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2906 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002907 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002908 if (radius < 0) {
2909 radius = 0;
2910 }
2911
2912 SkRect r;
2913 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002914 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002915}
2916
2917void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2918 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002919 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002920 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002921 SkRRect rrect;
2922 rrect.setRectXY(r, rx, ry);
2923 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002924 } else {
2925 this->drawRect(r, paint);
2926 }
2927}
2928
reed@android.com8a1c16f2008-12-17 15:59:43 +00002929void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2930 SkScalar sweepAngle, bool useCenter,
2931 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002932 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002933 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2934 this->drawOval(oval, paint);
2935 } else {
2936 SkPath path;
2937 if (useCenter) {
2938 path.moveTo(oval.centerX(), oval.centerY());
2939 }
2940 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2941 if (useCenter) {
2942 path.close();
2943 }
2944 this->drawPath(path, paint);
2945 }
2946}
2947
2948void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2949 const SkPath& path, SkScalar hOffset,
2950 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002951 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002952 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002953
reed@android.com8a1c16f2008-12-17 15:59:43 +00002954 matrix.setTranslate(hOffset, vOffset);
2955 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2956}
2957
reed@android.comf76bacf2009-05-13 14:00:33 +00002958///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07002959
2960/**
2961 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2962 * against the playback cost of recursing into the subpicture to get at its actual ops.
2963 *
2964 * For now we pick a conservatively small value, though measurement (and other heuristics like
2965 * the type of ops contained) may justify changing this value.
2966 */
2967#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07002968
reedd5fa1a42014-08-09 11:08:05 -07002969void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002970 RETURN_ON_NULL(picture);
2971
reed1c2c4412015-04-30 13:09:24 -07002972 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
reede3b38ce2016-01-08 09:18:44 -08002973 if (matrix && matrix->isIdentity()) {
2974 matrix = nullptr;
2975 }
2976 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2977 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2978 picture->playback(this);
2979 } else {
2980 this->onDrawPicture(picture, matrix, paint);
reedd5fa1a42014-08-09 11:08:05 -07002981 }
2982}
robertphillips9b14f262014-06-04 05:40:44 -07002983
reedd5fa1a42014-08-09 11:08:05 -07002984void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2985 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07002986 if (!paint || paint->canComputeFastBounds()) {
2987 SkRect bounds = picture->cullRect();
2988 if (paint) {
2989 paint->computeFastBounds(bounds, &bounds);
2990 }
2991 if (matrix) {
2992 matrix->mapRect(&bounds);
2993 }
2994 if (this->quickReject(bounds)) {
2995 return;
2996 }
2997 }
2998
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002999 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07003000 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003001}
3002
reed@android.com8a1c16f2008-12-17 15:59:43 +00003003///////////////////////////////////////////////////////////////////////////////
3004///////////////////////////////////////////////////////////////////////////////
3005
3006SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
bungeman99fe8222015-08-20 07:57:51 -07003007 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003008
3009 SkASSERT(canvas);
3010
3011 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
3012 fDone = !fImpl->next();
3013}
3014
3015SkCanvas::LayerIter::~LayerIter() {
3016 fImpl->~SkDrawIter();
3017}
3018
3019void SkCanvas::LayerIter::next() {
3020 fDone = !fImpl->next();
3021}
3022
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003023SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003024 return fImpl->getDevice();
3025}
3026
3027const SkMatrix& SkCanvas::LayerIter::matrix() const {
3028 return fImpl->getMatrix();
3029}
3030
3031const SkPaint& SkCanvas::LayerIter::paint() const {
3032 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003033 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003034 paint = &fDefaultPaint;
3035 }
3036 return *paint;
3037}
3038
reed1e7f5e72016-04-27 07:49:17 -07003039const SkRasterClip& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +00003040int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
3041int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003042
3043///////////////////////////////////////////////////////////////////////////////
3044
fmalitac3b589a2014-06-05 12:40:07 -07003045SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003046
3047///////////////////////////////////////////////////////////////////////////////
3048
3049static bool supported_for_raster_canvas(const SkImageInfo& info) {
3050 switch (info.alphaType()) {
3051 case kPremul_SkAlphaType:
3052 case kOpaque_SkAlphaType:
3053 break;
3054 default:
3055 return false;
3056 }
3057
3058 switch (info.colorType()) {
3059 case kAlpha_8_SkColorType:
3060 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00003061 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003062 break;
3063 default:
3064 return false;
3065 }
3066
3067 return true;
3068}
3069
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003070SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
3071 if (!supported_for_raster_canvas(info)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003072 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003073 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003074
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003075 SkBitmap bitmap;
3076 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003077 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003078 }
halcanary385fe4d2015-08-26 13:07:48 -07003079 return new SkCanvas(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003080}
reedd5fa1a42014-08-09 11:08:05 -07003081
3082///////////////////////////////////////////////////////////////////////////////
3083
3084SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003085 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07003086 : fCanvas(canvas)
3087 , fSaveCount(canvas->getSaveCount())
3088{
bsalomon49f085d2014-09-05 13:34:00 -07003089 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003090 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07003091 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003092 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07003093 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003094 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07003095 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003096 canvas->save();
3097 }
mtklein6cfa73a2014-08-13 13:33:49 -07003098
bsalomon49f085d2014-09-05 13:34:00 -07003099 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003100 canvas->concat(*matrix);
3101 }
3102}
3103
3104SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
3105 fCanvas->restoreToCount(fSaveCount);
3106}
reede8f30622016-03-23 18:59:25 -07003107
3108#ifdef SK_SUPPORT_LEGACY_NEW_SURFACE_API
3109SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
3110 return this->makeSurface(info, props).release();
3111}
3112#endif