blob: 2cfe8ae2da8b92e65a12b177eb7ef27dea7268fc [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;
vjiaoblack95302da2016-07-21 10:25:54 -0700666#ifdef SK_EXPERIMENTAL_SHADOWING
667 fLights = nullptr;
668#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000669
halcanary385fe4d2015-08-26 13:07:48 -0700670 fClipStack.reset(new SkClipStack);
reed687fa1c2015-04-07 08:00:56 -0700671
reed@android.com8a1c16f2008-12-17 15:59:43 +0000672 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700673 new (fMCRec) MCRec(fConservativeRasterClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000674
reeda499f902015-05-01 09:34:31 -0700675 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
676 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
reed7503d602016-07-15 14:23:29 -0700677 new (fDeviceCMStorage) DeviceCM(nullptr, nullptr, nullptr, fConservativeRasterClip,
reed8c30a812016-04-20 16:36:51 -0700678 fMCRec->fMatrix);
reedb679ca82015-04-07 04:40:48 -0700679
reed@android.com8a1c16f2008-12-17 15:59:43 +0000680 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000681
halcanary96fcdcc2015-08-27 07:41:13 -0700682 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000683
reedf92c8662014-08-18 08:02:43 -0700684 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700685 // The root device and the canvas should always have the same pixel geometry
686 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reedf92c8662014-08-18 08:02:43 -0700687 device->onAttachToCanvas(this);
688 fMCRec->fLayer->fDevice = SkRef(device);
reed78e27682014-11-19 08:04:34 -0800689 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
reedf92c8662014-08-18 08:02:43 -0700690 }
691 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000692}
693
reed@google.comcde92112011-07-06 20:00:52 +0000694SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000695 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700696 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800697 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000698{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000699 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000700
halcanary96fcdcc2015-08-27 07:41:13 -0700701 this->init(nullptr, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000702}
703
reedd9544982014-09-09 18:46:22 -0700704static SkBitmap make_nopixels(int width, int height) {
705 SkBitmap bitmap;
706 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
707 return bitmap;
708}
709
710class SkNoPixelsBitmapDevice : public SkBitmapDevice {
711public:
robertphillipsfcf78292015-06-19 11:49:52 -0700712 SkNoPixelsBitmapDevice(const SkIRect& bounds, const SkSurfaceProps& surfaceProps)
713 : INHERITED(make_nopixels(bounds.width(), bounds.height()), surfaceProps)
reed78e27682014-11-19 08:04:34 -0800714 {
715 this->setOrigin(bounds.x(), bounds.y());
716 }
reedd9544982014-09-09 18:46:22 -0700717
718private:
piotaixrb5fae932014-09-24 13:03:30 -0700719
reedd9544982014-09-09 18:46:22 -0700720 typedef SkBitmapDevice INHERITED;
721};
722
reed96a857e2015-01-25 10:33:58 -0800723SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000724 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800725 , fProps(SkSurfacePropsCopyOrDefault(props))
reed42b73eb2015-11-20 13:42:42 -0800726 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000727{
728 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700729
halcanary385fe4d2015-08-26 13:07:48 -0700730 this->init(new SkNoPixelsBitmapDevice(SkIRect::MakeWH(width, height), fProps),
731 kDefault_InitFlags)->unref();
reedd9544982014-09-09 18:46:22 -0700732}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000733
reed78e27682014-11-19 08:04:34 -0800734SkCanvas::SkCanvas(const SkIRect& bounds, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700735 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700736 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800737 , fConservativeRasterClip(false)
reedd9544982014-09-09 18:46:22 -0700738{
739 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700740
halcanary385fe4d2015-08-26 13:07:48 -0700741 this->init(new SkNoPixelsBitmapDevice(bounds, fProps), flags)->unref();
reedd9544982014-09-09 18:46:22 -0700742}
743
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000744SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000745 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700746 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800747 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000748{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000749 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700750
reedd9544982014-09-09 18:46:22 -0700751 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000752}
753
robertphillipsfcf78292015-06-19 11:49:52 -0700754SkCanvas::SkCanvas(SkBaseDevice* device, InitFlags flags)
755 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700756 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800757 , fConservativeRasterClip(false)
robertphillipsfcf78292015-06-19 11:49:52 -0700758{
759 inc_canvas();
760
761 this->init(device, flags);
762}
763
reed4a8126e2014-09-22 07:29:03 -0700764SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700765 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700766 , fProps(props)
reed42b73eb2015-11-20 13:42:42 -0800767 , fConservativeRasterClip(false)
reed3716fd02014-09-21 09:39:55 -0700768{
769 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700770
halcanary385fe4d2015-08-26 13:07:48 -0700771 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700772 this->init(device, kDefault_InitFlags);
773}
reed29c857d2014-09-21 10:25:07 -0700774
reed4a8126e2014-09-22 07:29:03 -0700775SkCanvas::SkCanvas(const SkBitmap& bitmap)
776 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
777 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800778 , fConservativeRasterClip(false)
reed4a8126e2014-09-22 07:29:03 -0700779{
780 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700781
halcanary385fe4d2015-08-26 13:07:48 -0700782 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700783 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000784}
785
786SkCanvas::~SkCanvas() {
787 // free up the contents of our deque
788 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000789
reed@android.com8a1c16f2008-12-17 15:59:43 +0000790 this->internalRestore(); // restore the last, since we're going away
791
halcanary385fe4d2015-08-26 13:07:48 -0700792 delete fMetaData;
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000793
reed@android.com8a1c16f2008-12-17 15:59:43 +0000794 dec_canvas();
795}
796
fmalita53d9f1c2016-01-25 06:23:54 -0800797#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000798SkDrawFilter* SkCanvas::getDrawFilter() const {
799 return fMCRec->fFilter;
800}
801
802SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
reed51985e32015-04-11 08:04:56 -0700803 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000804 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
805 return filter;
806}
fmalita77650002016-01-21 18:47:11 -0800807#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000808
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000809SkMetaData& SkCanvas::getMetaData() {
810 // metadata users are rare, so we lazily allocate it. If that changes we
811 // can decide to just make it a field in the device (rather than a ptr)
halcanary96fcdcc2015-08-27 07:41:13 -0700812 if (nullptr == fMetaData) {
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000813 fMetaData = new SkMetaData;
814 }
815 return *fMetaData;
816}
817
reed@android.com8a1c16f2008-12-17 15:59:43 +0000818///////////////////////////////////////////////////////////////////////////////
819
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000820void SkCanvas::flush() {
reedea5a6512016-07-07 16:44:27 -0700821 this->onFlush();
822}
823
824void SkCanvas::onFlush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000825 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000826 if (device) {
827 device->flush();
828 }
829}
830
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000831SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000832 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000833 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
834}
835
senorblancoafc7cce2016-02-02 18:44:15 -0800836SkIRect SkCanvas::getTopLayerBounds() const {
837 SkBaseDevice* d = this->getTopDevice();
838 if (!d) {
839 return SkIRect::MakeEmpty();
840 }
841 return SkIRect::MakeXYWH(d->getOrigin().x(), d->getOrigin().y(), d->width(), d->height());
842}
843
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000844SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000845 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000846 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000847 SkASSERT(rec && rec->fLayer);
848 return rec->fLayer->fDevice;
849}
850
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000851SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000852 if (updateMatrixClip) {
853 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
854 }
reed@google.com9266fed2011-03-30 00:18:03 +0000855 return fMCRec->fTopLayer->fDevice;
856}
857
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000858bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
859 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
860 return false;
861 }
862
863 bool weAllocated = false;
halcanary96fcdcc2015-08-27 07:41:13 -0700864 if (nullptr == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700865 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000866 return false;
867 }
868 weAllocated = true;
869 }
870
reedcf01e312015-05-23 19:14:51 -0700871 SkAutoPixmapUnlock unlocker;
872 if (bitmap->requestLock(&unlocker)) {
873 const SkPixmap& pm = unlocker.pixmap();
874 if (this->readPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), x, y)) {
875 return true;
876 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000877 }
878
879 if (weAllocated) {
halcanary96fcdcc2015-08-27 07:41:13 -0700880 bitmap->setPixelRef(nullptr);
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000881 }
882 return false;
883}
reed@google.com51df9e32010-12-23 19:29:18 +0000884
bsalomon@google.comc6980972011-11-02 19:57:21 +0000885bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000886 SkIRect r = srcRect;
887 const SkISize size = this->getBaseLayerSize();
888 if (!r.intersect(0, 0, size.width(), size.height())) {
889 bitmap->reset();
890 return false;
891 }
892
reed84825042014-09-02 12:50:45 -0700893 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000894 // bitmap will already be reset.
895 return false;
896 }
897 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
898 bitmap->reset();
899 return false;
900 }
901 return true;
902}
903
reed96472de2014-12-10 09:53:42 -0800904bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000905 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000906 if (!device) {
907 return false;
908 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000909 const SkISize size = this->getBaseLayerSize();
mtkleinf0f14112014-12-12 08:46:25 -0800910
reed96472de2014-12-10 09:53:42 -0800911 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
912 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000913 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000914 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000915
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000916 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800917 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000918}
919
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000920bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
921 if (bitmap.getTexture()) {
922 return false;
923 }
reedcf01e312015-05-23 19:14:51 -0700924
925 SkAutoPixmapUnlock unlocker;
926 if (bitmap.requestLock(&unlocker)) {
927 const SkPixmap& pm = unlocker.pixmap();
928 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000929 }
930 return false;
931}
932
933bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
934 int x, int y) {
935 switch (origInfo.colorType()) {
936 case kUnknown_SkColorType:
937 case kIndex_8_SkColorType:
938 return false;
939 default:
940 break;
941 }
halcanary96fcdcc2015-08-27 07:41:13 -0700942 if (nullptr == pixels || rowBytes < origInfo.minRowBytes()) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000943 return false;
944 }
945
946 const SkISize size = this->getBaseLayerSize();
947 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
948 if (!target.intersect(0, 0, size.width(), size.height())) {
949 return false;
950 }
951
952 SkBaseDevice* device = this->getDevice();
953 if (!device) {
954 return false;
955 }
956
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000957 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700958 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000959
960 // if x or y are negative, then we have to adjust pixels
961 if (x > 0) {
962 x = 0;
963 }
964 if (y > 0) {
965 y = 0;
966 }
967 // here x,y are either 0 or negative
968 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
969
reed4af35f32014-06-27 17:47:49 -0700970 // Tell our owning surface to bump its generation ID
reedc83a2972015-07-16 07:40:45 -0700971 const bool completeOverwrite = info.dimensions() == size;
972 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700973
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000974 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000975 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000976}
reed@google.com51df9e32010-12-23 19:29:18 +0000977
junov@google.com4370aed2012-01-18 16:21:08 +0000978SkCanvas* SkCanvas::canvasForDrawIter() {
979 return this;
980}
981
reed@android.com8a1c16f2008-12-17 15:59:43 +0000982//////////////////////////////////////////////////////////////////////////////
983
reed@android.com8a1c16f2008-12-17 15:59:43 +0000984void SkCanvas::updateDeviceCMCache() {
985 if (fDeviceCMDirty) {
986 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700987 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000988 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000989
halcanary96fcdcc2015-08-27 07:41:13 -0700990 if (nullptr == layer->fNext) { // only one layer
991 layer->updateMC(totalMatrix, totalClip, *fClipStack, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000992 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000993 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000994 do {
reed687fa1c2015-04-07 08:00:56 -0700995 layer->updateMC(totalMatrix, clip, *fClipStack, &clip);
halcanary96fcdcc2015-08-27 07:41:13 -0700996 } while ((layer = layer->fNext) != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000997 }
998 fDeviceCMDirty = false;
999 }
1000}
1001
reed@android.com8a1c16f2008-12-17 15:59:43 +00001002///////////////////////////////////////////////////////////////////////////////
1003
reed2ff1fce2014-12-11 07:07:37 -08001004void SkCanvas::checkForDeferredSave() {
1005 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -08001006 this->doSave();
1007 }
1008}
1009
reedf0090cb2014-11-26 08:55:51 -08001010int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -08001011#ifdef SK_DEBUG
1012 int count = 0;
1013 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
1014 for (;;) {
1015 const MCRec* rec = (const MCRec*)iter.next();
1016 if (!rec) {
1017 break;
1018 }
1019 count += 1 + rec->fDeferredSaveCount;
1020 }
1021 SkASSERT(count == fSaveCount);
1022#endif
1023 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -08001024}
1025
1026int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -08001027 fSaveCount += 1;
1028 fMCRec->fDeferredSaveCount += 1;
1029 return this->getSaveCount() - 1; // return our prev value
1030}
1031
1032void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -08001033 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -07001034
1035 SkASSERT(fMCRec->fDeferredSaveCount > 0);
1036 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001037 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -08001038}
1039
1040void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -08001041 if (fMCRec->fDeferredSaveCount > 0) {
1042 SkASSERT(fSaveCount > 1);
1043 fSaveCount -= 1;
1044 fMCRec->fDeferredSaveCount -= 1;
1045 } else {
1046 // check for underflow
1047 if (fMCStack.count() > 1) {
1048 this->willRestore();
1049 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -07001050 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001051 this->internalRestore();
1052 this->didRestore();
1053 }
reedf0090cb2014-11-26 08:55:51 -08001054 }
1055}
1056
1057void SkCanvas::restoreToCount(int count) {
1058 // sanity check
1059 if (count < 1) {
1060 count = 1;
1061 }
mtkleinf0f14112014-12-12 08:46:25 -08001062
reedf0090cb2014-11-26 08:55:51 -08001063 int n = this->getSaveCount() - count;
1064 for (int i = 0; i < n; ++i) {
1065 this->restore();
1066 }
1067}
1068
reed2ff1fce2014-12-11 07:07:37 -08001069void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001070 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -07001071 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +00001072 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +00001073
reed687fa1c2015-04-07 08:00:56 -07001074 fClipStack->save();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001075}
1076
reed4960eee2015-12-18 07:09:18 -08001077bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
reed@google.comb93ba452014-03-10 19:47:58 +00001078#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001079 return !(saveLayerFlags & SkCanvas::kDontClipToLayer_PrivateSaveLayerFlag);
reed@google.comb93ba452014-03-10 19:47:58 +00001080#else
1081 return true;
1082#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001083}
1084
reed4960eee2015-12-18 07:09:18 -08001085bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -07001086 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001087 SkIRect clipBounds;
1088 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001089 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +00001090 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001091
reed96e657d2015-03-10 17:30:07 -07001092 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
1093
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001094 if (imageFilter) {
senorblancoe5e79842016-03-21 14:51:59 -07001095 clipBounds = imageFilter->filterBounds(clipBounds, ctm);
senorblancodb64af32015-12-09 10:11:43 -08001096 if (bounds && !imageFilter->canComputeFastBounds()) {
1097 bounds = nullptr;
1098 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001099 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001100 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -07001101 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001102 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +00001103
reed96e657d2015-03-10 17:30:07 -07001104 ctm.mapRect(&r, *bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001105 r.roundOut(&ir);
1106 // early exit if the layer's bounds are clipped out
1107 if (!ir.intersect(clipBounds)) {
reed4960eee2015-12-18 07:09:18 -08001108 if (BoundsAffectsClip(saveLayerFlags)) {
reed9b3aa542015-03-11 08:47:12 -07001109 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001110 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001111 }
junov@chromium.orga907ac32012-02-24 21:54:07 +00001112 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001113 }
1114 } else { // no user bounds, so just use the clip
1115 ir = clipBounds;
1116 }
reed180aec42015-03-11 10:39:04 -07001117 SkASSERT(!ir.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001118
reed4960eee2015-12-18 07:09:18 -08001119 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -07001120 // Simplify the current clips since they will be applied properly during restore()
reed9b3aa542015-03-11 08:47:12 -07001121 fCachedLocalClipBoundsDirty = true;
reed687fa1c2015-04-07 08:00:56 -07001122 fClipStack->clipDevRect(ir, SkRegion::kReplace_Op);
reed180aec42015-03-11 10:39:04 -07001123 fMCRec->fRasterClip.setRect(ir);
junov@chromium.orga907ac32012-02-24 21:54:07 +00001124 }
1125
1126 if (intersection) {
1127 *intersection = ir;
1128 }
1129 return true;
1130}
1131
reed4960eee2015-12-18 07:09:18 -08001132
reed4960eee2015-12-18 07:09:18 -08001133int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
1134 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001135}
1136
reed70ee31b2015-12-10 13:44:45 -08001137int SkCanvas::saveLayerPreserveLCDTextRequests(const SkRect* bounds, const SkPaint* paint) {
reed4960eee2015-12-18 07:09:18 -08001138 return this->saveLayer(SaveLayerRec(bounds, paint, kPreserveLCDText_SaveLayerFlag));
1139}
1140
1141int SkCanvas::saveLayer(const SaveLayerRec& origRec) {
1142 SaveLayerRec rec(origRec);
1143 if (gIgnoreSaveLayerBounds) {
1144 rec.fBounds = nullptr;
1145 }
1146 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
1147 fSaveCount += 1;
1148 this->internalSaveLayer(rec, strategy);
1149 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -08001150}
1151
reeda2217ef2016-07-20 06:04:34 -07001152void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
1153 SkBaseDevice* dst, const SkMatrix& ctm,
1154 const SkClipStack* clipStack) {
1155 SkDraw draw;
1156 SkRasterClip rc;
1157 rc.setRect(SkIRect::MakeWH(dst->width(), dst->height()));
1158 if (!dst->accessPixels(&draw.fDst)) {
1159 draw.fDst.reset(dst->imageInfo(), nullptr, 0);
robertphillips7354a4b2015-12-16 05:08:27 -08001160 }
reeda2217ef2016-07-20 06:04:34 -07001161 draw.fMatrix = &SkMatrix::I();
1162 draw.fRC = &rc;
1163 draw.fClipStack = clipStack;
1164 draw.fDevice = dst;
robertphillips7354a4b2015-12-16 05:08:27 -08001165
1166 SkPaint p;
robertphillips372177e2016-03-30 07:32:28 -07001167 p.setImageFilter(filter->makeWithLocalMatrix(ctm));
reeda2217ef2016-07-20 06:04:34 -07001168
1169 int x = src->getOrigin().x() - dst->getOrigin().x();
1170 int y = src->getOrigin().y() - dst->getOrigin().y();
1171 auto special = src->snapSpecial();
1172 if (special) {
1173 dst->drawSpecial(draw, special.get(), x, y, p);
1174 }
robertphillips7354a4b2015-12-16 05:08:27 -08001175}
reed70ee31b2015-12-10 13:44:45 -08001176
reed129ed1c2016-02-22 06:42:31 -08001177static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, bool isOpaque,
1178 const SkPaint* paint) {
1179 // need to force L32 for now if we have an image filter. Once filters support other colortypes
1180 // e.g. sRGB or F16, we can remove this check
brianosman52ede1d2016-06-20 08:25:02 -07001181 // SRGBTODO: Can we remove this check now?
reed129ed1c2016-02-22 06:42:31 -08001182 const bool hasImageFilter = paint && paint->getImageFilter();
1183
1184 SkAlphaType alphaType = isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
1185 if ((prev.bytesPerPixel() < 4) || hasImageFilter) {
1186 // force to L32
1187 return SkImageInfo::MakeN32(w, h, alphaType);
1188 } else {
1189 // keep the same characteristics as the prev
brianosman52ede1d2016-06-20 08:25:02 -07001190 return SkImageInfo::Make(w, h, prev.colorType(), alphaType, sk_ref_sp(prev.colorSpace()));
reed129ed1c2016-02-22 06:42:31 -08001191 }
1192}
1193
reed4960eee2015-12-18 07:09:18 -08001194void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
1195 const SkRect* bounds = rec.fBounds;
1196 const SkPaint* paint = rec.fPaint;
1197 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1198
reed@google.comb93ba452014-03-10 19:47:58 +00001199#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001200 saveLayerFlags &= ~kDontClipToLayer_PrivateSaveLayerFlag;
reed@google.comb93ba452014-03-10 19:47:58 +00001201#endif
1202
reed8c30a812016-04-20 16:36:51 -07001203 SkLazyPaint lazyP;
1204 SkImageFilter* imageFilter = paint ? paint->getImageFilter() : NULL;
1205 SkMatrix stashedMatrix = fMCRec->fMatrix;
reed8c30a812016-04-20 16:36:51 -07001206 SkMatrix remainder;
1207 SkSize scale;
1208 /*
1209 * ImageFilters (so far) do not correctly handle matrices (CTM) that contain rotation/skew/etc.
1210 * but they do handle scaling. To accommodate this, we do the following:
1211 *
1212 * 1. Stash off the current CTM
1213 * 2. Decompose the CTM into SCALE and REMAINDER
1214 * 3. Wack the CTM to be just SCALE, and wrap the imagefilter with a MatrixImageFilter that
1215 * contains the REMAINDER
1216 * 4. Proceed as usual, allowing the client to draw into the layer (now with a scale-only CTM)
1217 * 5. During restore, we process the MatrixImageFilter, which applies REMAINDER to the output
1218 * of the original imagefilter, and draw that (via drawSprite)
1219 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1220 *
1221 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1222 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1223 */
reed96a04f32016-04-25 09:25:15 -07001224 if (imageFilter && !stashedMatrix.isScaleTranslate() && !imageFilter->canHandleComplexCTM() &&
reed8c30a812016-04-20 16:36:51 -07001225 stashedMatrix.decomposeScale(&scale, &remainder))
1226 {
1227 // We will restore the matrix (which we are overwriting here) in restore via fStashedMatrix
1228 this->internalSetMatrix(SkMatrix::MakeScale(scale.width(), scale.height()));
1229 SkPaint* p = lazyP.set(*paint);
1230 p->setImageFilter(SkImageFilter::MakeMatrixFilter(remainder,
1231 SkFilterQuality::kLow_SkFilterQuality,
1232 sk_ref_sp(imageFilter)));
1233 imageFilter = p->getImageFilter();
1234 paint = p;
1235 }
reed8c30a812016-04-20 16:36:51 -07001236
junov@chromium.orga907ac32012-02-24 21:54:07 +00001237 // do this before we create the layer. We don't call the public save() since
1238 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001239 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001240
1241 fDeviceCMDirty = true;
1242
1243 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001244 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
reed2ff1fce2014-12-11 07:07:37 -08001245 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001246 }
1247
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001248 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1249 // the clipRectBounds() call above?
1250 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001251 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001252 }
1253
reed4960eee2015-12-18 07:09:18 -08001254 bool isOpaque = SkToBool(saveLayerFlags & kIsOpaque_SaveLayerFlag);
reed8dc0ccb2015-03-20 06:32:52 -07001255 SkPixelGeometry geo = fProps.pixelGeometry();
1256 if (paint) {
reed76033be2015-03-14 10:54:31 -07001257 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001258 if (paint->getImageFilter() || paint->getColorFilter()) {
reed76033be2015-03-14 10:54:31 -07001259 isOpaque = false;
reed8dc0ccb2015-03-20 06:32:52 -07001260 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001261 }
1262 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001263
robertphillips5139e502016-07-19 05:10:40 -07001264 SkBaseDevice* priorDevice = this->getTopDevice();
reeda2217ef2016-07-20 06:04:34 -07001265 if (nullptr == priorDevice) {
reedb2db8982014-11-13 12:41:02 -08001266 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001267 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001268 }
reedb2db8982014-11-13 12:41:02 -08001269
robertphillips5139e502016-07-19 05:10:40 -07001270 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), isOpaque,
reed129ed1c2016-02-22 06:42:31 -08001271 paint);
1272
robertphillips5139e502016-07-19 05:10:40 -07001273 SkAutoTUnref<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001274 {
reed70ee31b2015-12-10 13:44:45 -08001275 const bool preserveLCDText = kOpaque_SkAlphaType == info.alphaType() ||
reed4960eee2015-12-18 07:09:18 -08001276 (saveLayerFlags & kPreserveLCDText_SaveLayerFlag);
reeddaa57bf2015-05-15 10:39:17 -07001277 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
reed70ee31b2015-12-10 13:44:45 -08001278 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
reedcd4051e2016-07-15 09:41:26 -07001279 preserveLCDText);
robertphillips5139e502016-07-19 05:10:40 -07001280 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1281 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001282 SkErrorInternals::SetError(kInternalError_SkError,
1283 "Unable to create device for layer.");
1284 return;
reed61f501f2015-04-29 08:34:00 -07001285 }
bungeman@google.come25c6842011-08-17 14:53:54 +00001286 }
robertphillips5139e502016-07-19 05:10:40 -07001287 newDevice->setOrigin(ir.fLeft, ir.fTop);
robertphillips7354a4b2015-12-16 05:08:27 -08001288
robertphillips5139e502016-07-19 05:10:40 -07001289 DeviceCM* layer = new DeviceCM(newDevice, paint, this, fConservativeRasterClip, stashedMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001290
1291 layer->fNext = fMCRec->fTopLayer;
1292 fMCRec->fLayer = layer;
1293 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001294
1295 if (rec.fBackdrop) {
1296 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice,
1297 fMCRec->fMatrix, this->getClipStack());
1298 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001299}
1300
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001301int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001302 if (0xFF == alpha) {
1303 return this->saveLayer(bounds, nullptr);
1304 } else {
1305 SkPaint tmpPaint;
1306 tmpPaint.setAlpha(alpha);
1307 return this->saveLayer(bounds, &tmpPaint);
1308 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001309}
1310
reed@android.com8a1c16f2008-12-17 15:59:43 +00001311void SkCanvas::internalRestore() {
1312 SkASSERT(fMCStack.count() != 0);
1313
1314 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001315 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001316
reed687fa1c2015-04-07 08:00:56 -07001317 fClipStack->restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001318
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001319 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001320 DeviceCM* layer = fMCRec->fLayer; // may be null
1321 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001322 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001323
1324 // now do the normal restore()
1325 fMCRec->~MCRec(); // balanced in save()
1326 fMCStack.pop_back();
1327 fMCRec = (MCRec*)fMCStack.back();
1328
1329 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1330 since if we're being recorded, we don't want to record this (the
1331 recorder will have already recorded the restore).
1332 */
bsalomon49f085d2014-09-05 13:34:00 -07001333 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001334 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001335 const SkIPoint& origin = layer->fDevice->getOrigin();
reed7503d602016-07-15 14:23:29 -07001336 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(), layer->fPaint);
reed8c30a812016-04-20 16:36:51 -07001337 // restore what we smashed in internalSaveLayer
1338 fMCRec->fMatrix = layer->fStashedMatrix;
reed@google.com8926b162012-03-23 15:36:36 +00001339 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001340 fDeviceCMDirty = true;
halcanary385fe4d2015-08-26 13:07:48 -07001341 delete layer;
reedb679ca82015-04-07 04:40:48 -07001342 } else {
1343 // we're at the root
reeda499f902015-05-01 09:34:31 -07001344 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001345 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001346 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001347 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001348 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001349}
1350
reede8f30622016-03-23 18:59:25 -07001351sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001352 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001353 props = &fProps;
1354 }
1355 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001356}
1357
reede8f30622016-03-23 18:59:25 -07001358sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001359 SkBaseDevice* dev = this->getDevice();
reede8f30622016-03-23 18:59:25 -07001360 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001361}
1362
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001363SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001364 return this->onImageInfo();
1365}
1366
1367SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001368 SkBaseDevice* dev = this->getDevice();
1369 if (dev) {
1370 return dev->imageInfo();
1371 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001372 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001373 }
1374}
1375
brianosman898235c2016-04-06 07:38:23 -07001376bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001377 return this->onGetProps(props);
1378}
1379
1380bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001381 SkBaseDevice* dev = this->getDevice();
1382 if (dev) {
1383 if (props) {
1384 *props = fProps;
1385 }
1386 return true;
1387 } else {
1388 return false;
1389 }
1390}
1391
reed6ceeebd2016-03-09 14:26:26 -08001392#ifdef SK_SUPPORT_LEGACY_PEEKPIXELS_PARMS
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001393const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
reed884e97c2015-05-26 11:31:54 -07001394 SkPixmap pmap;
reed6ceeebd2016-03-09 14:26:26 -08001395 if (this->peekPixels(&pmap)) {
1396 if (info) {
1397 *info = pmap.info();
1398 }
1399 if (rowBytes) {
1400 *rowBytes = pmap.rowBytes();
1401 }
1402 return pmap.addr();
reed884e97c2015-05-26 11:31:54 -07001403 }
reed6ceeebd2016-03-09 14:26:26 -08001404 return nullptr;
1405}
1406#endif
1407
1408bool SkCanvas::peekPixels(SkPixmap* pmap) {
1409 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001410}
1411
reed884e97c2015-05-26 11:31:54 -07001412bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001413 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001414 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001415}
1416
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001417void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001418 SkPixmap pmap;
1419 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001420 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001421 }
1422 if (info) {
1423 *info = pmap.info();
1424 }
1425 if (rowBytes) {
1426 *rowBytes = pmap.rowBytes();
1427 }
1428 if (origin) {
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001429 *origin = this->getTopDevice(false)->getOrigin();
1430 }
reed884e97c2015-05-26 11:31:54 -07001431 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001432}
1433
reed884e97c2015-05-26 11:31:54 -07001434bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001435 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001436 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001437}
1438
reed@android.com8a1c16f2008-12-17 15:59:43 +00001439/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001440
reed7503d602016-07-15 14:23:29 -07001441void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001442 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001443 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001444 paint = &tmp;
1445 }
reed@google.com4b226022011-01-11 18:32:13 +00001446
reed@google.com8926b162012-03-23 15:36:36 +00001447 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reeda2217ef2016-07-20 06:04:34 -07001448
reed@android.com8a1c16f2008-12-17 15:59:43 +00001449 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001450 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001451 paint = &looper.paint();
1452 SkImageFilter* filter = paint->getImageFilter();
1453 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
senorblancof35566e2016-04-18 10:32:02 -07001454 if (filter) {
reeda2217ef2016-07-20 06:04:34 -07001455 dstDev->drawSpecial(iter, srcDev->snapSpecial().get(), pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001456 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001457 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001458 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001459 }
reeda2217ef2016-07-20 06:04:34 -07001460
reed@google.com4e2b3d32011-04-07 14:18:59 +00001461 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001462}
1463
reed32704672015-12-16 08:27:10 -08001464/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001465
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001466void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001467 SkMatrix m;
1468 m.setTranslate(dx, dy);
1469 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001470}
1471
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001472void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001473 SkMatrix m;
1474 m.setScale(sx, sy);
1475 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001476}
1477
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001478void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001479 SkMatrix m;
1480 m.setRotate(degrees);
1481 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001482}
1483
bungeman7438bfc2016-07-12 15:01:19 -07001484void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1485 SkMatrix m;
1486 m.setRotate(degrees, px, py);
1487 this->concat(m);
1488}
1489
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001490void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001491 SkMatrix m;
1492 m.setSkew(sx, sy);
1493 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001494}
1495
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001496void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001497 if (matrix.isIdentity()) {
1498 return;
1499 }
1500
reed2ff1fce2014-12-11 07:07:37 -08001501 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001502 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001503 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001504 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001505
1506 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001507}
1508
reed8c30a812016-04-20 16:36:51 -07001509void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001510 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001511 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001512 fMCRec->fMatrix = matrix;
reed8c30a812016-04-20 16:36:51 -07001513}
1514
1515void SkCanvas::setMatrix(const SkMatrix& matrix) {
1516 this->checkForDeferredSave();
1517 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001518 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001519}
1520
reed@android.com8a1c16f2008-12-17 15:59:43 +00001521void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001522 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001523}
1524
vjiaoblack95302da2016-07-21 10:25:54 -07001525#ifdef SK_EXPERIMENTAL_SHADOWING
vjiaoblacke5de1302016-07-13 14:05:28 -07001526void SkCanvas::translateZ(SkScalar z) {
1527 this->checkForDeferredSave();
1528 this->fMCRec->fCurDrawDepth += z;
1529 this->didTranslateZ(z);
1530}
1531
1532SkScalar SkCanvas::getZ() const {
1533 return this->fMCRec->fCurDrawDepth;
1534}
1535
vjiaoblack95302da2016-07-21 10:25:54 -07001536void SkCanvas::setLights(sk_sp<SkLights> lights) {
1537 this->fLights = lights;
1538}
1539
1540sk_sp<SkLights> SkCanvas::getLights() const {
1541 return this->fLights;
1542}
1543#endif
1544
reed@android.com8a1c16f2008-12-17 15:59:43 +00001545//////////////////////////////////////////////////////////////////////////////
1546
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001547void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed2d1afab2016-06-29 14:33:11 -07001548 if (!fAllowSoftClip) {
1549 doAA = false;
1550 }
1551
1552#ifdef SK_SUPPORT_PRECHECK_CLIPRECT
1553 // Check if we can quick-accept the clip call (and do nothing)
1554 //
reed74467162016-06-30 08:15:35 -07001555 if (SkRegion::kIntersect_Op == op && !doAA && fMCRec->fMatrix.isScaleTranslate()) {
reed6092b6e2016-07-10 11:45:34 -07001556 SkRect devR;
1557 fMCRec->fMatrix.mapRectScaleTranslate(&devR, rect);
reed2d1afab2016-06-29 14:33:11 -07001558 // NOTE: this check is CTM specific, since we might round differently with a different
1559 // CTM. Thus this is only 100% reliable if there is not global CTM scale to be
1560 // applied later (i.e. if this is going into a picture).
1561 if (devR.round().contains(fMCRec->fRasterClip.getBounds())) {
1562#if 0
1563 SkDebugf("ignored clipRect [%g %g %g %g]\n",
1564 rect.left(), rect.top(), rect.right(), rect.bottom());
1565#endif
1566 return;
1567 }
1568 }
1569#endif
1570
reed2ff1fce2014-12-11 07:07:37 -08001571 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001572 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1573 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001574}
1575
1576void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001577#ifdef SK_ENABLE_CLIP_QUICKREJECT
1578 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001579 if (fMCRec->fRasterClip.isEmpty()) {
reed2d1afab2016-06-29 14:33:11 -07001580 return;
reed@google.comda17f752012-08-16 18:27:05 +00001581 }
1582
reed@google.com3b3e8952012-08-16 20:53:31 +00001583 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001584 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001585 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001586
reed687fa1c2015-04-07 08:00:56 -07001587 fClipStack->clipEmpty();
reed2d1afab2016-06-29 14:33:11 -07001588 (void)fMCRec->fRasterClip.setEmpty();
1589 return;
reed@google.comda17f752012-08-16 18:27:05 +00001590 }
1591 }
1592#endif
1593
reed74467162016-06-30 08:15:35 -07001594 const bool isScaleTrans = fMCRec->fMatrix.isScaleTranslate();
reedc64eff52015-11-21 12:39:45 -08001595 SkRect devR;
reed74467162016-06-30 08:15:35 -07001596 if (isScaleTrans) {
reed6092b6e2016-07-10 11:45:34 -07001597 fMCRec->fMatrix.mapRectScaleTranslate(&devR, rect);
reedc64eff52015-11-21 12:39:45 -08001598 }
bsalomonac8cabd2015-11-20 18:53:07 -08001599
reed2d1afab2016-06-29 14:33:11 -07001600#ifndef SK_SUPPORT_PRECHECK_CLIPRECT
reedc64eff52015-11-21 12:39:45 -08001601 if (SkRegion::kIntersect_Op == op &&
1602 kHard_ClipEdgeStyle == edgeStyle
reed74467162016-06-30 08:15:35 -07001603 && isScaleTrans)
reedc64eff52015-11-21 12:39:45 -08001604 {
1605 if (devR.round().contains(fMCRec->fRasterClip.getBounds())) {
1606#if 0
1607 SkDebugf("------- ignored clipRect [%g %g %g %g]\n",
1608 rect.left(), rect.top(), rect.right(), rect.bottom());
1609#endif
1610 return;
1611 }
1612 }
reed2d1afab2016-06-29 14:33:11 -07001613#endif
reedc64eff52015-11-21 12:39:45 -08001614
1615 AutoValidateClip avc(this);
1616
1617 fDeviceCMDirty = true;
1618 fCachedLocalClipBoundsDirty = true;
1619
reed74467162016-06-30 08:15:35 -07001620 if (isScaleTrans) {
reedc64eff52015-11-21 12:39:45 -08001621 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1622 fClipStack->clipDevRect(devR, op, isAA);
senorblancoafc7cce2016-02-02 18:44:15 -08001623 fMCRec->fRasterClip.op(devR, this->getTopLayerBounds(), op, isAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001624 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001625 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001626 // and clip against that, since it can handle any matrix. However, to
1627 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1628 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001629 SkPath path;
1630
1631 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001632 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001633 }
1634}
1635
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001636void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001637 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001638 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001639 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001640 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1641 } else {
1642 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001643 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001644}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001645
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001646void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001647 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001648 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001649 AutoValidateClip avc(this);
1650
1651 fDeviceCMDirty = true;
1652 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001653 if (!fAllowSoftClip) {
1654 edgeStyle = kHard_ClipEdgeStyle;
1655 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001656
reed687fa1c2015-04-07 08:00:56 -07001657 fClipStack->clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001658
senorblancoafc7cce2016-02-02 18:44:15 -08001659 fMCRec->fRasterClip.op(transformedRRect, this->getTopLayerBounds(), op,
robertphillips125f19a2015-11-23 09:00:05 -08001660 kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001661 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001662 }
1663
1664 SkPath path;
1665 path.addRRect(rrect);
1666 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001667 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001668}
1669
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001670void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001671 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001672 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001673
1674 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1675 SkRect r;
1676 if (path.isRect(&r)) {
1677 this->onClipRect(r, op, edgeStyle);
1678 return;
1679 }
1680 SkRRect rrect;
1681 if (path.isOval(&r)) {
1682 rrect.setOval(r);
1683 this->onClipRRect(rrect, op, edgeStyle);
1684 return;
1685 }
1686 if (path.isRRect(&rrect)) {
1687 this->onClipRRect(rrect, op, edgeStyle);
1688 return;
1689 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001690 }
robertphillips39f05382015-11-24 09:30:12 -08001691
1692 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001693}
1694
1695void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001696#ifdef SK_ENABLE_CLIP_QUICKREJECT
1697 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001698 if (fMCRec->fRasterClip.isEmpty()) {
reed2d1afab2016-06-29 14:33:11 -07001699 return;
reed@google.comda17f752012-08-16 18:27:05 +00001700 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001701
reed@google.com3b3e8952012-08-16 20:53:31 +00001702 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001703 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001704 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001705
reed687fa1c2015-04-07 08:00:56 -07001706 fClipStack->clipEmpty();
reed2d1afab2016-06-29 14:33:11 -07001707 (void)fMCRec->fRasterClip.setEmpty();
1708 return;
reed@google.comda17f752012-08-16 18:27:05 +00001709 }
1710 }
1711#endif
1712
reed@google.com5c3d1472011-02-22 19:12:23 +00001713 AutoValidateClip avc(this);
1714
reed@android.com8a1c16f2008-12-17 15:59:43 +00001715 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001716 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001717 if (!fAllowSoftClip) {
1718 edgeStyle = kHard_ClipEdgeStyle;
1719 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001720
1721 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001722 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001723
reed@google.comfe701122011-11-08 19:41:23 +00001724 // Check if the transfomation, or the original path itself
1725 // made us empty. Note this can also happen if we contained NaN
1726 // values. computing the bounds detects this, and will set our
1727 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1728 if (devPath.getBounds().isEmpty()) {
1729 // resetting the path will remove any NaN or other wanky values
1730 // that might upset our scan converter.
1731 devPath.reset();
1732 }
1733
reed@google.com5c3d1472011-02-22 19:12:23 +00001734 // if we called path.swap() we could avoid a deep copy of this path
reed687fa1c2015-04-07 08:00:56 -07001735 fClipStack->clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001736
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001737 if (fAllowSimplifyClip) {
fmalita1a481fe2015-02-04 07:39:34 -08001738 bool clipIsAA = getClipStack()->asPath(&devPath);
1739 if (clipIsAA) {
1740 edgeStyle = kSoft_ClipEdgeStyle;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001741 }
fmalita1a481fe2015-02-04 07:39:34 -08001742
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001743 op = SkRegion::kReplace_Op;
1744 }
1745
senorblancoafc7cce2016-02-02 18:44:15 -08001746 fMCRec->fRasterClip.op(devPath, this->getTopLayerBounds(), op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001747}
1748
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001749void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed2ff1fce2014-12-11 07:07:37 -08001750 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001751 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001752}
1753
1754void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001755 AutoValidateClip avc(this);
1756
reed@android.com8a1c16f2008-12-17 15:59:43 +00001757 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001758 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001759
reed@google.com5c3d1472011-02-22 19:12:23 +00001760 // todo: signal fClipStack that we have a region, and therefore (I guess)
1761 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001762 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001763
reed1f836ee2014-07-07 07:49:34 -07001764 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001765}
1766
reed@google.com819c9212011-02-23 18:56:55 +00001767#ifdef SK_DEBUG
1768void SkCanvas::validateClip() const {
1769 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001770 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001771 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001772 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001773 return;
1774 }
1775
reed@google.com819c9212011-02-23 18:56:55 +00001776 SkIRect ir;
1777 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001778 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001779
reed687fa1c2015-04-07 08:00:56 -07001780 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001781 const SkClipStack::Element* element;
halcanary96fcdcc2015-08-27 07:41:13 -07001782 while ((element = iter.next()) != nullptr) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001783 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001784 case SkClipStack::Element::kRect_Type:
1785 element->getRect().round(&ir);
1786 tmpClip.op(ir, element->getOp());
1787 break;
1788 case SkClipStack::Element::kEmpty_Type:
1789 tmpClip.setEmpty();
1790 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001791 default: {
1792 SkPath path;
1793 element->asPath(&path);
senorblancoafc7cce2016-02-02 18:44:15 -08001794 tmpClip.op(path, this->getTopLayerBounds(), element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001795 break;
1796 }
reed@google.com819c9212011-02-23 18:56:55 +00001797 }
1798 }
reed@google.com819c9212011-02-23 18:56:55 +00001799}
1800#endif
1801
reed@google.com90c07ea2012-04-13 13:50:27 +00001802void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001803 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001804 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001805
halcanary96fcdcc2015-08-27 07:41:13 -07001806 while ((element = iter.next()) != nullptr) {
fmalitac3b589a2014-06-05 12:40:07 -07001807 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001808 }
1809}
1810
reed@google.com5c3d1472011-02-22 19:12:23 +00001811///////////////////////////////////////////////////////////////////////////////
1812
reed@google.com754de5f2014-02-24 19:38:20 +00001813bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001814 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001815}
1816
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001817bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001818 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001819}
1820
reed@google.com3b3e8952012-08-16 20:53:31 +00001821bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001822 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001823 return true;
1824
reed1f836ee2014-07-07 07:49:34 -07001825 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001826 return true;
1827 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001828
reed1f836ee2014-07-07 07:49:34 -07001829 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001830 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001831 fMCRec->fMatrix.mapRect(&dst, rect);
reedb07a94f2014-11-19 05:03:18 -08001832 return !SkIRect::Intersects(dst.roundOut(), fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001833 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001834 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001835
reed@android.coma380ae42009-07-21 01:17:02 +00001836 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001837 // TODO: should we use | instead, or compare all 4 at once?
1838 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001839 return true;
1840 }
reed@google.comc0784db2013-12-13 21:16:12 +00001841 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001842 return true;
1843 }
1844 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001845 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001846}
1847
reed@google.com3b3e8952012-08-16 20:53:31 +00001848bool SkCanvas::quickReject(const SkPath& path) const {
1849 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001850}
1851
reed@google.com3b3e8952012-08-16 20:53:31 +00001852bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001853 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001854 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001855 return false;
1856 }
1857
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001858 SkMatrix inverse;
1859 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001860 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001861 if (bounds) {
1862 bounds->setEmpty();
1863 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001864 return false;
1865 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001866
bsalomon49f085d2014-09-05 13:34:00 -07001867 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001868 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001869 // adjust it outwards in case we are antialiasing
1870 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001871
reed@google.com8f4d2302013-12-17 16:44:46 +00001872 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1873 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001874 inverse.mapRect(bounds, r);
1875 }
1876 return true;
1877}
1878
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001879bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001880 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001881 if (clip.isEmpty()) {
1882 if (bounds) {
1883 bounds->setEmpty();
1884 }
1885 return false;
1886 }
1887
bsalomon49f085d2014-09-05 13:34:00 -07001888 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001889 *bounds = clip.getBounds();
1890 }
1891 return true;
1892}
1893
reed@android.com8a1c16f2008-12-17 15:59:43 +00001894const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001895 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001896}
1897
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001898const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001899 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001900}
1901
robertphillips175dd9b2016-04-28 14:32:04 -07001902GrDrawContext* SkCanvas::internal_private_accessTopLayerDrawContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001903 SkBaseDevice* dev = this->getTopDevice();
robertphillips175dd9b2016-04-28 14:32:04 -07001904 return dev ? dev->accessDrawContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001905}
1906
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001907GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001908 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001909 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001910}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001911
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001912void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1913 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001914 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001915 if (outer.isEmpty()) {
1916 return;
1917 }
1918 if (inner.isEmpty()) {
1919 this->drawRRect(outer, paint);
1920 return;
1921 }
1922
1923 // We don't have this method (yet), but technically this is what we should
1924 // be able to assert...
1925 // SkASSERT(outer.contains(inner));
1926 //
1927 // For now at least check for containment of bounds
1928 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1929
1930 this->onDrawDRRect(outer, inner, paint);
1931}
1932
reed41af9662015-01-05 07:49:08 -08001933// These need to stop being virtual -- clients need to override the onDraw... versions
1934
1935void SkCanvas::drawPaint(const SkPaint& paint) {
1936 this->onDrawPaint(paint);
1937}
1938
1939void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1940 this->onDrawRect(r, paint);
1941}
1942
1943void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1944 this->onDrawOval(r, paint);
1945}
1946
1947void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1948 this->onDrawRRect(rrect, paint);
1949}
1950
1951void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1952 this->onDrawPoints(mode, count, pts, paint);
1953}
1954
1955void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
1956 const SkPoint texs[], const SkColor colors[], SkXfermode* xmode,
1957 const uint16_t indices[], int indexCount, const SkPaint& paint) {
1958 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
1959 indices, indexCount, paint);
1960}
1961
1962void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1963 this->onDrawPath(path, paint);
1964}
1965
reeda85d4d02015-05-06 12:56:48 -07001966void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001967 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001968 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001969}
1970
reede47829b2015-08-06 10:02:53 -07001971void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1972 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001973 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001974 if (dst.isEmpty() || src.isEmpty()) {
1975 return;
1976 }
1977 this->onDrawImageRect(image, &src, dst, paint, constraint);
1978}
reed41af9662015-01-05 07:49:08 -08001979
reed84984ef2015-07-17 07:09:43 -07001980void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1981 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001982 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001983 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001984}
1985
reede47829b2015-08-06 10:02:53 -07001986void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
1987 SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001988 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001989 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
1990 constraint);
1991}
reede47829b2015-08-06 10:02:53 -07001992
reed4c21dc52015-06-25 12:32:03 -07001993void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1994 const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001995 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07001996 if (dst.isEmpty()) {
1997 return;
1998 }
1999 if (!SkNinePatchIter::Valid(image->width(), image->height(), center)) {
reede47829b2015-08-06 10:02:53 -07002000 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002001 }
2002 this->onDrawImageNine(image, center, dst, paint);
2003}
2004
reed41af9662015-01-05 07:49:08 -08002005void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07002006 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002007 return;
2008 }
reed41af9662015-01-05 07:49:08 -08002009 this->onDrawBitmap(bitmap, dx, dy, paint);
2010}
2011
reede47829b2015-08-06 10:02:53 -07002012void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07002013 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002014 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07002015 return;
2016 }
reede47829b2015-08-06 10:02:53 -07002017 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08002018}
2019
reed84984ef2015-07-17 07:09:43 -07002020void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2021 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002022 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002023}
2024
reede47829b2015-08-06 10:02:53 -07002025void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2026 SrcRectConstraint constraint) {
2027 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2028 constraint);
2029}
reede47829b2015-08-06 10:02:53 -07002030
reed41af9662015-01-05 07:49:08 -08002031void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2032 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07002033 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002034 return;
2035 }
reed4c21dc52015-06-25 12:32:03 -07002036 if (!SkNinePatchIter::Valid(bitmap.width(), bitmap.height(), center)) {
reeda5517e22015-07-14 10:54:12 -07002037 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002038 }
reed41af9662015-01-05 07:49:08 -08002039 this->onDrawBitmapNine(bitmap, center, dst, paint);
2040}
2041
reed71c3c762015-06-24 10:29:17 -07002042void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2043 const SkColor colors[], int count, SkXfermode::Mode mode,
2044 const SkRect* cull, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002045 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002046 if (count <= 0) {
2047 return;
2048 }
2049 SkASSERT(atlas);
2050 SkASSERT(xform);
2051 SkASSERT(tex);
2052 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
2053}
2054
reedf70b5312016-03-04 16:36:20 -08002055void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2056 if (key) {
2057 this->onDrawAnnotation(rect, key, value);
2058 }
2059}
2060
reede47829b2015-08-06 10:02:53 -07002061void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2062 const SkPaint* paint, SrcRectConstraint constraint) {
2063 if (src) {
2064 this->drawImageRect(image, *src, dst, paint, constraint);
2065 } else {
2066 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2067 dst, paint, constraint);
2068 }
2069}
2070void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2071 const SkPaint* paint, SrcRectConstraint constraint) {
2072 if (src) {
2073 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2074 } else {
2075 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2076 dst, paint, constraint);
2077 }
2078}
2079
tomhudsoncb3bd182016-05-18 07:24:16 -07002080void SkCanvas::temporary_internal_describeTopLayer(SkMatrix* matrix, SkIRect* clip_bounds) {
2081 SkIRect layer_bounds = this->getTopLayerBounds();
2082 if (matrix) {
2083 *matrix = this->getTotalMatrix();
2084 matrix->preTranslate(-layer_bounds.left(), -layer_bounds.top());
2085 }
2086 if (clip_bounds) {
2087 this->getClipDeviceBounds(clip_bounds);
2088 clip_bounds->offset(-layer_bounds.left(), -layer_bounds.top());
2089 }
2090}
2091
reed@android.com8a1c16f2008-12-17 15:59:43 +00002092//////////////////////////////////////////////////////////////////////////////
2093// These are the virtual drawing methods
2094//////////////////////////////////////////////////////////////////////////////
2095
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002096void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002097 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002098 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2099 }
2100}
2101
reed41af9662015-01-05 07:49:08 -08002102void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002103 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002104 this->internalDrawPaint(paint);
2105}
2106
2107void SkCanvas::internalDrawPaint(const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002108 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002109
2110 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002111 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002112 }
2113
reed@google.com4e2b3d32011-04-07 14:18:59 +00002114 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002115}
2116
reed41af9662015-01-05 07:49:08 -08002117void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2118 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002119 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002120 if ((long)count <= 0) {
2121 return;
2122 }
2123
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002124 SkRect r, storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002125 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002126 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002127 // special-case 2 points (common for drawing a single line)
2128 if (2 == count) {
2129 r.set(pts[0], pts[1]);
2130 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00002131 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002132 }
senorblanco87e066e2015-10-28 11:23:36 -07002133 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2134 return;
2135 }
2136 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002137 }
reed@google.coma584aed2012-05-16 14:06:02 +00002138
halcanary96fcdcc2015-08-27 07:41:13 -07002139 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002140
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002141 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002142
reed@android.com8a1c16f2008-12-17 15:59:43 +00002143 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002144 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002145 }
reed@google.com4b226022011-01-11 18:32:13 +00002146
reed@google.com4e2b3d32011-04-07 14:18:59 +00002147 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002148}
2149
reed41af9662015-01-05 07:49:08 -08002150void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002151 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002152 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002153 const SkRect* bounds = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002154 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08002155 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
2156 // To prevent accidental rejecting at this stage, we have to sort it before we check.
2157 SkRect tmp(r);
2158 tmp.sort();
2159
senorblanco87e066e2015-10-28 11:23:36 -07002160 if (this->quickReject(paint.computeFastBounds(tmp, &storage))) {
2161 return;
2162 }
2163 bounds = &r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002164 }
reed@google.com4b226022011-01-11 18:32:13 +00002165
reedc83a2972015-07-16 07:40:45 -07002166 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002167
2168 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002169 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002170 }
2171
reed@google.com4e2b3d32011-04-07 14:18:59 +00002172 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002173}
2174
reed41af9662015-01-05 07:49:08 -08002175void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002176 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002177 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002178 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002179 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002180 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2181 return;
2182 }
2183 bounds = &oval;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002184 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002185
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002186 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002187
2188 while (iter.next()) {
2189 iter.fDevice->drawOval(iter, oval, looper.paint());
2190 }
2191
2192 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002193}
2194
reed41af9662015-01-05 07:49:08 -08002195void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002196 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002197 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002198 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002199 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002200 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2201 return;
2202 }
2203 bounds = &rrect.getBounds();
reed@google.com4ed0fb72012-12-12 20:48:18 +00002204 }
2205
2206 if (rrect.isRect()) {
2207 // call the non-virtual version
2208 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002209 return;
2210 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002211 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002212 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2213 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002214 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002215
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002216 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002217
2218 while (iter.next()) {
2219 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2220 }
2221
2222 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002223}
2224
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002225void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2226 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002227 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002228 const SkRect* bounds = nullptr;
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002229 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002230 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2231 return;
2232 }
2233 bounds = &outer.getBounds();
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002234 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002235
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002236 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002237
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002238 while (iter.next()) {
2239 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2240 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002241
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002242 LOOPER_END
2243}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002244
reed41af9662015-01-05 07:49:08 -08002245void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002246 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002247 if (!path.isFinite()) {
2248 return;
2249 }
2250
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002251 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002252 const SkRect* bounds = nullptr;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002253 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002254 const SkRect& pathBounds = path.getBounds();
senorblanco87e066e2015-10-28 11:23:36 -07002255 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2256 return;
2257 }
2258 bounds = &pathBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002259 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002260
2261 const SkRect& r = path.getBounds();
2262 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002263 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002264 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002265 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002266 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002267 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002268
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002269 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002270
2271 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002272 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002273 }
2274
reed@google.com4e2b3d32011-04-07 14:18:59 +00002275 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002276}
2277
reed262a71b2015-12-05 13:07:27 -08002278bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002279 if (!paint.getImageFilter()) {
2280 return false;
2281 }
2282
2283 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002284 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002285 return false;
2286 }
2287
2288 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2289 // Once we can filter and the filter will return a result larger than itself, we should be
2290 // able to remove this constraint.
2291 // skbug.com/4526
2292 //
2293 SkPoint pt;
2294 ctm.mapXY(x, y, &pt);
2295 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2296 return ir.contains(fMCRec->fRasterClip.getBounds());
2297}
2298
reeda85d4d02015-05-06 12:56:48 -07002299void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002300 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002301 SkRect bounds = SkRect::MakeXYWH(x, y,
2302 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002303 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002304 SkRect tmp = bounds;
2305 if (paint) {
2306 paint->computeFastBounds(tmp, &tmp);
2307 }
2308 if (this->quickReject(tmp)) {
2309 return;
2310 }
reeda85d4d02015-05-06 12:56:48 -07002311 }
halcanary9d524f22016-03-29 09:03:52 -07002312
reeda85d4d02015-05-06 12:56:48 -07002313 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002314 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002315 paint = lazy.init();
2316 }
reed262a71b2015-12-05 13:07:27 -08002317
reeda2217ef2016-07-20 06:04:34 -07002318 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002319 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2320 *paint);
2321 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002322 special = this->getDevice()->makeSpecial(image);
2323 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002324 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002325 }
2326 }
2327
reed262a71b2015-12-05 13:07:27 -08002328 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
2329
reeda85d4d02015-05-06 12:56:48 -07002330 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002331 const SkPaint& pnt = looper.paint();
reeda2217ef2016-07-20 06:04:34 -07002332 if (special) {
2333 SkPoint pt;
2334 iter.fMatrix->mapXY(x, y, &pt);
2335 iter.fDevice->drawSpecial(iter, special.get(),
2336 SkScalarRoundToInt(pt.fX),
2337 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002338 } else {
2339 iter.fDevice->drawImage(iter, image, x, y, pnt);
2340 }
reeda85d4d02015-05-06 12:56:48 -07002341 }
halcanary9d524f22016-03-29 09:03:52 -07002342
reeda85d4d02015-05-06 12:56:48 -07002343 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002344}
2345
reed41af9662015-01-05 07:49:08 -08002346void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002347 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002348 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
halcanary96fcdcc2015-08-27 07:41:13 -07002349 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002350 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002351 if (paint) {
2352 paint->computeFastBounds(dst, &storage);
2353 }
2354 if (this->quickReject(storage)) {
2355 return;
2356 }
reeda85d4d02015-05-06 12:56:48 -07002357 }
2358 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002359 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002360 paint = lazy.init();
2361 }
halcanary9d524f22016-03-29 09:03:52 -07002362
senorblancoc41e7e12015-12-07 12:51:30 -08002363 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002364 image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002365
reeda85d4d02015-05-06 12:56:48 -07002366 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002367 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002368 }
halcanary9d524f22016-03-29 09:03:52 -07002369
reeda85d4d02015-05-06 12:56:48 -07002370 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002371}
2372
reed41af9662015-01-05 07:49:08 -08002373void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002374 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002375 SkDEBUGCODE(bitmap.validate();)
2376
reed33366972015-10-08 09:22:02 -07002377 if (bitmap.drawsNothing()) {
2378 return;
2379 }
2380
2381 SkLazyPaint lazy;
2382 if (nullptr == paint) {
2383 paint = lazy.init();
2384 }
2385
2386 const SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2387
2388 SkRect storage;
2389 const SkRect* bounds = nullptr;
2390 if (paint->canComputeFastBounds()) {
2391 bitmap.getBounds(&storage);
2392 matrix.mapRect(&storage);
senorblanco87e066e2015-10-28 11:23:36 -07002393 SkRect tmp = storage;
2394 if (this->quickReject(paint->computeFastBounds(tmp, &tmp))) {
2395 return;
2396 }
2397 bounds = &storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002398 }
reed@google.com4b226022011-01-11 18:32:13 +00002399
reeda2217ef2016-07-20 06:04:34 -07002400 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002401 bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(), bitmap.height(),
2402 *paint);
2403 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002404 special = this->getDevice()->makeSpecial(bitmap);
2405 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002406 drawAsSprite = false;
2407 }
2408 }
2409
reed262a71b2015-12-05 13:07:27 -08002410 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds)
reed33366972015-10-08 09:22:02 -07002411
2412 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002413 const SkPaint& pnt = looper.paint();
reeda2217ef2016-07-20 06:04:34 -07002414 if (special) {
reed262a71b2015-12-05 13:07:27 -08002415 SkPoint pt;
2416 iter.fMatrix->mapXY(x, y, &pt);
reeda2217ef2016-07-20 06:04:34 -07002417 iter.fDevice->drawSpecial(iter, special.get(),
2418 SkScalarRoundToInt(pt.fX),
2419 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002420 } else {
2421 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
2422 }
reed33366972015-10-08 09:22:02 -07002423 }
reeda2217ef2016-07-20 06:04:34 -07002424
reed33366972015-10-08 09:22:02 -07002425 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002426}
2427
reed@google.com9987ec32011-09-07 11:57:52 +00002428// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002429void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002430 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002431 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002432 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002433 return;
2434 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002435
halcanary96fcdcc2015-08-27 07:41:13 -07002436 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002437 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002438 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2439 return;
2440 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002441 }
reed@google.com3d608122011-11-21 15:16:16 +00002442
reed@google.com33535f32012-09-25 15:37:50 +00002443 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002444 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002445 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002446 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002447
senorblancoc41e7e12015-12-07 12:51:30 -08002448 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002449 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002450
reed@google.com33535f32012-09-25 15:37:50 +00002451 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002452 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002453 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002454
reed@google.com33535f32012-09-25 15:37:50 +00002455 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002456}
2457
reed41af9662015-01-05 07:49:08 -08002458void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002459 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002460 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002461 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002462 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002463}
2464
reed4c21dc52015-06-25 12:32:03 -07002465void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2466 const SkPaint* paint) {
2467 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
halcanary9d524f22016-03-29 09:03:52 -07002468
halcanary96fcdcc2015-08-27 07:41:13 -07002469 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002470 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002471 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2472 return;
2473 }
reed@google.com3d608122011-11-21 15:16:16 +00002474 }
halcanary9d524f22016-03-29 09:03:52 -07002475
reed4c21dc52015-06-25 12:32:03 -07002476 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002477 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002478 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002479 }
halcanary9d524f22016-03-29 09:03:52 -07002480
senorblancoc41e7e12015-12-07 12:51:30 -08002481 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002482
reed4c21dc52015-06-25 12:32:03 -07002483 while (iter.next()) {
2484 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002485 }
halcanary9d524f22016-03-29 09:03:52 -07002486
reed4c21dc52015-06-25 12:32:03 -07002487 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002488}
2489
reed41af9662015-01-05 07:49:08 -08002490void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2491 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002492 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002493 SkDEBUGCODE(bitmap.validate();)
2494
halcanary96fcdcc2015-08-27 07:41:13 -07002495 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002496 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002497 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2498 return;
2499 }
reed4c21dc52015-06-25 12:32:03 -07002500 }
halcanary9d524f22016-03-29 09:03:52 -07002501
reed4c21dc52015-06-25 12:32:03 -07002502 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002503 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002504 paint = lazy.init();
2505 }
halcanary9d524f22016-03-29 09:03:52 -07002506
senorblancoc41e7e12015-12-07 12:51:30 -08002507 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002508
reed4c21dc52015-06-25 12:32:03 -07002509 while (iter.next()) {
2510 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2511 }
halcanary9d524f22016-03-29 09:03:52 -07002512
reed4c21dc52015-06-25 12:32:03 -07002513 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002514}
2515
reed@google.comf67e4cf2011-03-15 20:56:58 +00002516class SkDeviceFilteredPaint {
2517public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002518 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002519 uint32_t filteredFlags = device->filterTextFlags(paint);
2520 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002521 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002522 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002523 fPaint = newPaint;
2524 } else {
2525 fPaint = &paint;
2526 }
2527 }
2528
reed@google.comf67e4cf2011-03-15 20:56:58 +00002529 const SkPaint& paint() const { return *fPaint; }
2530
2531private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002532 const SkPaint* fPaint;
2533 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002534};
2535
bungeman@google.com52c748b2011-08-22 21:30:43 +00002536void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2537 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002538 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002539 draw.fDevice->drawRect(draw, r, paint);
2540 } else {
2541 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002542 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002543 draw.fDevice->drawRect(draw, r, p);
2544 }
2545}
2546
2547void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2548 const char text[], size_t byteLength,
2549 SkScalar x, SkScalar y) {
halcanary96fcdcc2015-08-27 07:41:13 -07002550 SkASSERT(byteLength == 0 || text != nullptr);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002551
2552 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07002553 if (text == nullptr || byteLength == 0 ||
reed1e7f5e72016-04-27 07:49:17 -07002554 draw.fRC->isEmpty() ||
halcanary96fcdcc2015-08-27 07:41:13 -07002555 (paint.getAlpha() == 0 && paint.getXfermode() == nullptr)) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002556 return;
2557 }
2558
2559 SkScalar width = 0;
2560 SkPoint start;
2561
2562 start.set(0, 0); // to avoid warning
2563 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2564 SkPaint::kStrikeThruText_Flag)) {
2565 width = paint.measureText(text, byteLength);
2566
2567 SkScalar offsetX = 0;
2568 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2569 offsetX = SkScalarHalf(width);
2570 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2571 offsetX = width;
2572 }
2573 start.set(x - offsetX, y);
2574 }
2575
2576 if (0 == width) {
2577 return;
2578 }
2579
2580 uint32_t flags = paint.getFlags();
2581
2582 if (flags & (SkPaint::kUnderlineText_Flag |
2583 SkPaint::kStrikeThruText_Flag)) {
2584 SkScalar textSize = paint.getTextSize();
2585 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2586 SkRect r;
2587
2588 r.fLeft = start.fX;
2589 r.fRight = start.fX + width;
2590
2591 if (flags & SkPaint::kUnderlineText_Flag) {
2592 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2593 start.fY);
2594 r.fTop = offset;
2595 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002596 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002597 }
2598 if (flags & SkPaint::kStrikeThruText_Flag) {
2599 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2600 start.fY);
2601 r.fTop = offset;
2602 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002603 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002604 }
2605 }
2606}
2607
reed@google.come0d9ce82014-04-23 04:00:17 +00002608void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2609 const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002610 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002611
2612 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002613 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002614 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002615 DrawTextDecorations(iter, dfp.paint(),
2616 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002617 }
2618
reed@google.com4e2b3d32011-04-07 14:18:59 +00002619 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002620}
2621
reed@google.come0d9ce82014-04-23 04:00:17 +00002622void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2623 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002624 SkPoint textOffset = SkPoint::Make(0, 0);
2625
halcanary96fcdcc2015-08-27 07:41:13 -07002626 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002627
reed@android.com8a1c16f2008-12-17 15:59:43 +00002628 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002629 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002630 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002631 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002632 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002633
reed@google.com4e2b3d32011-04-07 14:18:59 +00002634 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002635}
2636
reed@google.come0d9ce82014-04-23 04:00:17 +00002637void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2638 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002639
2640 SkPoint textOffset = SkPoint::Make(0, constY);
2641
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()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002645 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002646 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002647 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002648 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002649
reed@google.com4e2b3d32011-04-07 14:18:59 +00002650 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002651}
2652
reed@google.come0d9ce82014-04-23 04:00:17 +00002653void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2654 const SkMatrix* matrix, const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002655 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002656
reed@android.com8a1c16f2008-12-17 15:59:43 +00002657 while (iter.next()) {
2658 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002659 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002660 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002661
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002662 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002663}
2664
reed45561a02016-07-07 12:47:17 -07002665void SkCanvas::onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2666 const SkRect* cullRect, const SkPaint& paint) {
2667 if (cullRect && this->quickReject(*cullRect)) {
2668 return;
2669 }
2670
2671 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
2672
2673 while (iter.next()) {
2674 iter.fDevice->drawTextRSXform(iter, text, byteLength, xform, looper.paint());
2675 }
2676
2677 LOOPER_END
2678}
2679
fmalita00d5c2c2014-08-21 08:53:26 -07002680void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2681 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002682
fmalita85d5eb92015-03-04 11:20:12 -08002683 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002684 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002685 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002686 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002687 SkRect tmp;
2688 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2689 return;
2690 }
2691 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002692 }
2693
fmalita024f9962015-03-03 19:08:17 -08002694 // We cannot filter in the looper as we normally do, because the paint is
2695 // incomplete at this point (text-related attributes are embedded within blob run paints).
2696 SkDrawFilter* drawFilter = fMCRec->fFilter;
halcanary96fcdcc2015-08-27 07:41:13 -07002697 fMCRec->fFilter = nullptr;
fmalita024f9962015-03-03 19:08:17 -08002698
fmalita85d5eb92015-03-04 11:20:12 -08002699 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002700
fmalitaaa1b9122014-08-28 14:32:24 -07002701 while (iter.next()) {
2702 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002703 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002704 }
2705
fmalitaaa1b9122014-08-28 14:32:24 -07002706 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002707
2708 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002709}
2710
reed@google.come0d9ce82014-04-23 04:00:17 +00002711// These will become non-virtual, so they always call the (virtual) onDraw... method
2712void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2713 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002714 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002715 this->onDrawText(text, byteLength, x, y, paint);
2716}
2717void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2718 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002719 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002720 this->onDrawPosText(text, byteLength, pos, paint);
2721}
2722void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2723 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002724 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002725 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2726}
2727void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2728 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002729 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002730 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2731}
reed45561a02016-07-07 12:47:17 -07002732void SkCanvas::drawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2733 const SkRect* cullRect, const SkPaint& paint) {
2734 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextRSXform()");
2735 if (byteLength) {
2736 this->onDrawTextRSXform(text, byteLength, xform, cullRect, paint);
2737 }
2738}
fmalita00d5c2c2014-08-21 08:53:26 -07002739void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2740 const SkPaint& paint) {
reede3b38ce2016-01-08 09:18:44 -08002741 RETURN_ON_NULL(blob);
danakj9881d632014-11-26 12:41:06 -08002742 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
reede3b38ce2016-01-08 09:18:44 -08002743 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002744}
reed@google.come0d9ce82014-04-23 04:00:17 +00002745
reed41af9662015-01-05 07:49:08 -08002746void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2747 const SkPoint verts[], const SkPoint texs[],
2748 const SkColor colors[], SkXfermode* xmode,
2749 const uint16_t indices[], int indexCount,
2750 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002751 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
halcanary96fcdcc2015-08-27 07:41:13 -07002752 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
reed@google.com4b226022011-01-11 18:32:13 +00002753
reed@android.com8a1c16f2008-12-17 15:59:43 +00002754 while (iter.next()) {
2755 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002756 colors, xmode, indices, indexCount,
2757 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002758 }
reed@google.com4b226022011-01-11 18:32:13 +00002759
reed@google.com4e2b3d32011-04-07 14:18:59 +00002760 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002761}
2762
dandovb3c9d1c2014-08-12 08:34:29 -07002763void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2764 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002765 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
halcanary96fcdcc2015-08-27 07:41:13 -07002766 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002767 return;
2768 }
mtklein6cfa73a2014-08-13 13:33:49 -07002769
dandovecfff212014-08-04 10:02:00 -07002770 // Since a patch is always within the convex hull of the control points, we discard it when its
2771 // bounding rectangle is completely outside the current clip.
2772 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002773 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002774 if (this->quickReject(bounds)) {
2775 return;
2776 }
mtklein6cfa73a2014-08-13 13:33:49 -07002777
dandovb3c9d1c2014-08-12 08:34:29 -07002778 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2779}
2780
2781void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2782 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2783
halcanary96fcdcc2015-08-27 07:41:13 -07002784 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002785
dandovecfff212014-08-04 10:02:00 -07002786 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002787 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002788 }
mtklein6cfa73a2014-08-13 13:33:49 -07002789
dandovecfff212014-08-04 10:02:00 -07002790 LOOPER_END
2791}
2792
reeda8db7282015-07-07 10:22:31 -07002793void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
reede3b38ce2016-01-08 09:18:44 -08002794 RETURN_ON_NULL(dr);
2795 if (x || y) {
2796 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2797 this->onDrawDrawable(dr, &matrix);
2798 } else {
2799 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002800 }
2801}
2802
reeda8db7282015-07-07 10:22:31 -07002803void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reede3b38ce2016-01-08 09:18:44 -08002804 RETURN_ON_NULL(dr);
2805 if (matrix && matrix->isIdentity()) {
2806 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002807 }
reede3b38ce2016-01-08 09:18:44 -08002808 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002809}
2810
2811void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2812 SkRect bounds = dr->getBounds();
2813 if (matrix) {
2814 matrix->mapRect(&bounds);
2815 }
2816 if (this->quickReject(bounds)) {
2817 return;
2818 }
2819 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002820}
2821
reed71c3c762015-06-24 10:29:17 -07002822void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2823 const SkColor colors[], int count, SkXfermode::Mode mode,
2824 const SkRect* cull, const SkPaint* paint) {
2825 if (cull && this->quickReject(*cull)) {
2826 return;
2827 }
2828
2829 SkPaint pnt;
2830 if (paint) {
2831 pnt = *paint;
2832 }
halcanary9d524f22016-03-29 09:03:52 -07002833
halcanary96fcdcc2015-08-27 07:41:13 -07002834 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr)
reed71c3c762015-06-24 10:29:17 -07002835 while (iter.next()) {
2836 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, mode, pnt);
2837 }
2838 LOOPER_END
2839}
2840
reedf70b5312016-03-04 16:36:20 -08002841void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2842 SkASSERT(key);
2843
2844 SkPaint paint;
2845 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, nullptr)
2846 while (iter.next()) {
2847 iter.fDevice->drawAnnotation(iter, rect, key, value);
2848 }
2849 LOOPER_END
2850}
2851
reed@android.com8a1c16f2008-12-17 15:59:43 +00002852//////////////////////////////////////////////////////////////////////////////
2853// These methods are NOT virtual, and therefore must call back into virtual
2854// methods, rather than actually drawing themselves.
2855//////////////////////////////////////////////////////////////////////////////
2856
2857void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002858 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002859 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002860 SkPaint paint;
2861
2862 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002863 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002864 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002865 }
2866 this->drawPaint(paint);
2867}
2868
reed@android.com845fdac2009-06-23 03:01:32 +00002869void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002870 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002871 SkPaint paint;
2872
2873 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002874 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002875 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002876 }
2877 this->drawPaint(paint);
2878}
2879
2880void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002881 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002882 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002883
reed@android.com8a1c16f2008-12-17 15:59:43 +00002884 pt.set(x, y);
2885 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2886}
2887
2888void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002889 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002890 SkPoint pt;
2891 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002892
reed@android.com8a1c16f2008-12-17 15:59:43 +00002893 pt.set(x, y);
2894 paint.setColor(color);
2895 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2896}
2897
2898void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2899 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002900 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002901 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002902
reed@android.com8a1c16f2008-12-17 15:59:43 +00002903 pts[0].set(x0, y0);
2904 pts[1].set(x1, y1);
2905 this->drawPoints(kLines_PointMode, 2, pts, paint);
2906}
2907
2908void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2909 SkScalar right, SkScalar bottom,
2910 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002911 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002912 SkRect r;
2913
2914 r.set(left, top, right, bottom);
2915 this->drawRect(r, paint);
2916}
2917
2918void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2919 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002920 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002921 if (radius < 0) {
2922 radius = 0;
2923 }
2924
2925 SkRect r;
2926 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002927 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002928}
2929
2930void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2931 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002932 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002933 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002934 SkRRect rrect;
2935 rrect.setRectXY(r, rx, ry);
2936 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002937 } else {
2938 this->drawRect(r, paint);
2939 }
2940}
2941
reed@android.com8a1c16f2008-12-17 15:59:43 +00002942void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2943 SkScalar sweepAngle, bool useCenter,
2944 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002945 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002946 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2947 this->drawOval(oval, paint);
2948 } else {
2949 SkPath path;
2950 if (useCenter) {
2951 path.moveTo(oval.centerX(), oval.centerY());
2952 }
2953 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2954 if (useCenter) {
2955 path.close();
2956 }
2957 this->drawPath(path, paint);
2958 }
2959}
2960
2961void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2962 const SkPath& path, SkScalar hOffset,
2963 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002964 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002965 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002966
reed@android.com8a1c16f2008-12-17 15:59:43 +00002967 matrix.setTranslate(hOffset, vOffset);
2968 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2969}
2970
reed@android.comf76bacf2009-05-13 14:00:33 +00002971///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07002972
2973/**
2974 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2975 * against the playback cost of recursing into the subpicture to get at its actual ops.
2976 *
2977 * For now we pick a conservatively small value, though measurement (and other heuristics like
2978 * the type of ops contained) may justify changing this value.
2979 */
2980#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07002981
reedd5fa1a42014-08-09 11:08:05 -07002982void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002983 RETURN_ON_NULL(picture);
2984
reed1c2c4412015-04-30 13:09:24 -07002985 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
reede3b38ce2016-01-08 09:18:44 -08002986 if (matrix && matrix->isIdentity()) {
2987 matrix = nullptr;
2988 }
2989 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2990 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2991 picture->playback(this);
2992 } else {
2993 this->onDrawPicture(picture, matrix, paint);
reedd5fa1a42014-08-09 11:08:05 -07002994 }
2995}
robertphillips9b14f262014-06-04 05:40:44 -07002996
reedd5fa1a42014-08-09 11:08:05 -07002997void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2998 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07002999 if (!paint || paint->canComputeFastBounds()) {
3000 SkRect bounds = picture->cullRect();
3001 if (paint) {
3002 paint->computeFastBounds(bounds, &bounds);
3003 }
3004 if (matrix) {
3005 matrix->mapRect(&bounds);
3006 }
3007 if (this->quickReject(bounds)) {
3008 return;
3009 }
3010 }
3011
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003012 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07003013 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003014}
3015
vjiaoblack95302da2016-07-21 10:25:54 -07003016#ifdef SK_EXPERIMENTAL_SHADOWING
3017void SkCanvas::drawShadowedPicture(const SkPicture* picture,
3018 const SkMatrix* matrix,
3019 const SkPaint* paint) {
3020 RETURN_ON_NULL(picture);
3021
3022 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawShadowedPicture()");
3023
3024 this->onDrawShadowedPicture(picture, matrix, paint);
3025}
3026
3027void SkCanvas::onDrawShadowedPicture(const SkPicture* picture,
3028 const SkMatrix* matrix,
3029 const SkPaint* paint) {
3030 this->onDrawPicture(picture, matrix, paint);
3031}
3032#endif
3033
reed@android.com8a1c16f2008-12-17 15:59:43 +00003034///////////////////////////////////////////////////////////////////////////////
3035///////////////////////////////////////////////////////////////////////////////
3036
3037SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
bungeman99fe8222015-08-20 07:57:51 -07003038 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003039
3040 SkASSERT(canvas);
3041
3042 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
3043 fDone = !fImpl->next();
3044}
3045
3046SkCanvas::LayerIter::~LayerIter() {
3047 fImpl->~SkDrawIter();
3048}
3049
3050void SkCanvas::LayerIter::next() {
3051 fDone = !fImpl->next();
3052}
3053
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003054SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003055 return fImpl->getDevice();
3056}
3057
3058const SkMatrix& SkCanvas::LayerIter::matrix() const {
3059 return fImpl->getMatrix();
3060}
3061
3062const SkPaint& SkCanvas::LayerIter::paint() const {
3063 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003064 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003065 paint = &fDefaultPaint;
3066 }
3067 return *paint;
3068}
3069
reed1e7f5e72016-04-27 07:49:17 -07003070const SkRasterClip& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +00003071int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
3072int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003073
3074///////////////////////////////////////////////////////////////////////////////
3075
fmalitac3b589a2014-06-05 12:40:07 -07003076SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003077
3078///////////////////////////////////////////////////////////////////////////////
3079
3080static bool supported_for_raster_canvas(const SkImageInfo& info) {
3081 switch (info.alphaType()) {
3082 case kPremul_SkAlphaType:
3083 case kOpaque_SkAlphaType:
3084 break;
3085 default:
3086 return false;
3087 }
3088
3089 switch (info.colorType()) {
3090 case kAlpha_8_SkColorType:
3091 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00003092 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003093 break;
3094 default:
3095 return false;
3096 }
3097
3098 return true;
3099}
3100
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003101SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
3102 if (!supported_for_raster_canvas(info)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003103 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003104 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003105
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003106 SkBitmap bitmap;
3107 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003108 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003109 }
halcanary385fe4d2015-08-26 13:07:48 -07003110 return new SkCanvas(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003111}
reedd5fa1a42014-08-09 11:08:05 -07003112
3113///////////////////////////////////////////////////////////////////////////////
3114
3115SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003116 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07003117 : fCanvas(canvas)
3118 , fSaveCount(canvas->getSaveCount())
3119{
bsalomon49f085d2014-09-05 13:34:00 -07003120 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003121 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07003122 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003123 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07003124 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003125 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07003126 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003127 canvas->save();
3128 }
mtklein6cfa73a2014-08-13 13:33:49 -07003129
bsalomon49f085d2014-09-05 13:34:00 -07003130 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003131 canvas->concat(*matrix);
3132 }
3133}
3134
3135SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
3136 fCanvas->restoreToCount(fSaveCount);
3137}
reede8f30622016-03-23 18:59:25 -07003138
3139#ifdef SK_SUPPORT_LEGACY_NEW_SURFACE_API
3140SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
3141 return this->makeSurface(info, props).release();
3142}
3143#endif