blob: 1a5c4c4d112964041b2dab85064935629085e9c1 [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00003 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00004 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00006 */
7
bungemand3ebb482015-08-05 13:57:49 -07008#include "SkBitmapDevice.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +00009#include "SkCanvas.h"
reedd5fa1a42014-08-09 11:08:05 -070010#include "SkCanvasPriv.h"
bungemand3ebb482015-08-05 13:57:49 -070011#include "SkClipStack.h"
reeddbc3cef2015-04-29 12:18:57 -070012#include "SkColorFilter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013#include "SkDraw.h"
reed3cb38402015-02-06 08:36:15 -080014#include "SkDrawable.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000015#include "SkDrawFilter.h"
16#include "SkDrawLooper.h"
joshualitt5f5a8d72015-02-25 14:09:45 -080017#include "SkErrorInternals.h"
piotaixrb5fae932014-09-24 13:03:30 -070018#include "SkImage.h"
reed262a71b2015-12-05 13:07:27 -080019#include "SkImage_Base.h"
senorblanco900c3672016-04-27 11:31:23 -070020#include "SkImageFilter.h"
21#include "SkImageFilterCache.h"
reed262a71b2015-12-05 13:07:27 -080022#include "SkMatrixUtils.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000023#include "SkMetaData.h"
reed4c21dc52015-06-25 12:32:03 -070024#include "SkNinePatchIter.h"
reedc83a2972015-07-16 07:40:45 -070025#include "SkPaintPriv.h"
dandovb3c9d1c2014-08-12 08:34:29 -070026#include "SkPatchUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000027#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000028#include "SkRasterClip.h"
reed96472de2014-12-10 09:53:42 -080029#include "SkReadPixelsRec.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000030#include "SkRRect.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000031#include "SkSmallAllocator.h"
robertphillips4418dba2016-03-07 12:45:14 -080032#include "SkSpecialImage.h"
reed@google.com97af1a62012-08-28 12:19:02 +000033#include "SkSurface_Base.h"
fmalita7ba7aa72014-08-29 09:46:36 -070034#include "SkTextBlob.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000035#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000036#include "SkTLazy.h"
danakj8f757f52014-11-04 11:48:43 -080037#include "SkTraceEvent.h"
bungemand3ebb482015-08-05 13:57:49 -070038
39#include <new>
reed@android.com8a1c16f2008-12-17 15:59:43 +000040
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000041#if SK_SUPPORT_GPU
robertphillips7354a4b2015-12-16 05:08:27 -080042#include "GrContext.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000043#include "GrRenderTarget.h"
robertphillips7354a4b2015-12-16 05:08:27 -080044#include "SkGr.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000045#endif
46
reede3b38ce2016-01-08 09:18:44 -080047#define RETURN_ON_NULL(ptr) do { if (nullptr == (ptr)) return; } while (0)
48
reedc83a2972015-07-16 07:40:45 -070049/*
50 * Return true if the drawing this rect would hit every pixels in the canvas.
51 *
52 * Returns false if
53 * - rect does not contain the canvas' bounds
54 * - paint is not fill
55 * - paint would blur or otherwise change the coverage of the rect
56 */
57bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
58 ShaderOverrideOpacity overrideOpacity) const {
bungeman99fe8222015-08-20 07:57:51 -070059 static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
60 (int)kNone_ShaderOverrideOpacity,
61 "need_matching_enums0");
62 static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
63 (int)kOpaque_ShaderOverrideOpacity,
64 "need_matching_enums1");
65 static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
66 (int)kNotOpaque_ShaderOverrideOpacity,
67 "need_matching_enums2");
reedc83a2972015-07-16 07:40:45 -070068
69 const SkISize size = this->getBaseLayerSize();
70 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
71 if (!this->getClipStack()->quickContains(bounds)) {
72 return false;
73 }
74
75 if (rect) {
76 if (!this->getTotalMatrix().rectStaysRect()) {
77 return false; // conservative
78 }
79
80 SkRect devRect;
81 this->getTotalMatrix().mapRect(&devRect, *rect);
fmalita8c0144c2015-07-22 05:56:16 -070082 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -070083 return false;
84 }
85 }
86
87 if (paint) {
88 SkPaint::Style paintStyle = paint->getStyle();
89 if (!(paintStyle == SkPaint::kFill_Style ||
90 paintStyle == SkPaint::kStrokeAndFill_Style)) {
91 return false;
92 }
93 if (paint->getMaskFilter() || paint->getLooper()
94 || paint->getPathEffect() || paint->getImageFilter()) {
95 return false; // conservative
96 }
97 }
98 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
99}
100
101///////////////////////////////////////////////////////////////////////////////////////////////////
102
reedd990e2f2014-12-22 11:58:30 -0800103static bool gIgnoreSaveLayerBounds;
104void SkCanvas::Internal_Private_SetIgnoreSaveLayerBounds(bool ignore) {
105 gIgnoreSaveLayerBounds = ignore;
106}
107bool SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds() {
108 return gIgnoreSaveLayerBounds;
109}
110
reed0acf1b42014-12-22 16:12:38 -0800111static bool gTreatSpriteAsBitmap;
112void SkCanvas::Internal_Private_SetTreatSpriteAsBitmap(bool spriteAsBitmap) {
113 gTreatSpriteAsBitmap = spriteAsBitmap;
114}
115bool SkCanvas::Internal_Private_GetTreatSpriteAsBitmap() {
116 return gTreatSpriteAsBitmap;
117}
118
reed@google.comda17f752012-08-16 18:27:05 +0000119// experimental for faster tiled drawing...
120//#define SK_ENABLE_CLIP_QUICKREJECT
reed@android.com8a1c16f2008-12-17 15:59:43 +0000121//#define SK_TRACE_SAVERESTORE
122
123#ifdef SK_TRACE_SAVERESTORE
124 static int gLayerCounter;
125 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
126 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
127
128 static int gRecCounter;
129 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
130 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
131
132 static int gCanvasCounter;
133 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
134 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
135#else
136 #define inc_layer()
137 #define dec_layer()
138 #define inc_rec()
139 #define dec_rec()
140 #define inc_canvas()
141 #define dec_canvas()
142#endif
143
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000144typedef SkTLazy<SkPaint> SkLazyPaint;
145
reedc83a2972015-07-16 07:40:45 -0700146void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000147 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700148 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
149 ? SkSurface::kDiscard_ContentChangeMode
150 : SkSurface::kRetain_ContentChangeMode);
151 }
152}
153
154void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
155 ShaderOverrideOpacity overrideOpacity) {
156 if (fSurfaceBase) {
157 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
158 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
159 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
160 // and therefore we don't care which mode we're in.
161 //
162 if (fSurfaceBase->outstandingImageSnapshot()) {
163 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
164 mode = SkSurface::kDiscard_ContentChangeMode;
165 }
166 }
167 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000168 }
169}
170
reed@android.com8a1c16f2008-12-17 15:59:43 +0000171///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000172
reed4a8126e2014-09-22 07:29:03 -0700173static uint32_t filter_paint_flags(const SkSurfaceProps& props, uint32_t flags) {
174 const uint32_t propFlags = props.flags();
175 if (propFlags & SkSurfaceProps::kDisallowDither_Flag) {
176 flags &= ~SkPaint::kDither_Flag;
177 }
178 if (propFlags & SkSurfaceProps::kDisallowAntiAlias_Flag) {
179 flags &= ~SkPaint::kAntiAlias_Flag;
180 }
181 return flags;
182}
183
184///////////////////////////////////////////////////////////////////////////////
185
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000186/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000187 The clip/matrix/proc are fields that reflect the top of the save/restore
188 stack. Whenever the canvas changes, it marks a dirty flag, and then before
189 these are used (assuming we're not on a layer) we rebuild these cache
190 values: they reflect the top of the save stack, but translated and clipped
191 by the device's XY offset and bitmap-bounds.
192*/
193struct DeviceCM {
194 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000195 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000196 SkRasterClip fClip;
reed@google.com6f8f2922011-03-04 22:27:10 +0000197 SkPaint* fPaint; // may be null (in the future)
reed61f501f2015-04-29 08:34:00 -0700198 const SkMatrix* fMatrix;
199 SkMatrix fMatrixStorage;
reed8c30a812016-04-20 16:36:51 -0700200 SkMatrix fStashedMatrix; // original CTM; used by imagefilter in saveLayer
reed61f501f2015-04-29 08:34:00 -0700201 const bool fDeviceIsBitmapDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000202
reed96e657d2015-03-10 17:30:07 -0700203 DeviceCM(SkBaseDevice* device, const SkPaint* paint, SkCanvas* canvas,
reed8c30a812016-04-20 16:36:51 -0700204 bool conservativeRasterClip, bool deviceIsBitmapDevice, const SkMatrix& stashed)
halcanary96fcdcc2015-08-27 07:41:13 -0700205 : fNext(nullptr)
reedd9544982014-09-09 18:46:22 -0700206 , fClip(conservativeRasterClip)
reed8c30a812016-04-20 16:36:51 -0700207 , fStashedMatrix(stashed)
reed61f501f2015-04-29 08:34:00 -0700208 , fDeviceIsBitmapDevice(deviceIsBitmapDevice)
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
reedd9544982014-09-09 18:46:22 -0700295 MCRec(bool conservativeRasterClip) : fRasterClip(conservativeRasterClip) {
halcanary96fcdcc2015-08-27 07:41:13 -0700296 fFilter = nullptr;
297 fLayer = nullptr;
298 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800299 fMatrix.reset();
300 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700301
reedd9544982014-09-09 18:46:22 -0700302 // don't bother initializing fNext
303 inc_rec();
304 }
reed2ff1fce2014-12-11 07:07:37 -0800305 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
reedd9544982014-09-09 18:46:22 -0700306 fFilter = SkSafeRef(prev.fFilter);
halcanary96fcdcc2015-08-27 07:41:13 -0700307 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700308 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800309 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700310
reed@android.com8a1c16f2008-12-17 15:59:43 +0000311 // don't bother initializing fNext
312 inc_rec();
313 }
314 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000315 SkSafeUnref(fFilter);
halcanary385fe4d2015-08-26 13:07:48 -0700316 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000317 dec_rec();
318 }
mtkleinfeaadee2015-04-08 11:25:48 -0700319
320 void reset(const SkIRect& bounds) {
321 SkASSERT(fLayer);
322 SkASSERT(fDeferredSaveCount == 0);
323
324 fMatrix.reset();
325 fRasterClip.setRect(bounds);
326 fLayer->reset(bounds);
327 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000328};
329
330class SkDrawIter : public SkDraw {
331public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000332 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000333 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000334 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000335 canvas->updateDeviceCMCache();
336
reed687fa1c2015-04-07 08:00:56 -0700337 fClipStack = canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000338 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000339 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000340 }
reed@google.com4b226022011-01-11 18:32:13 +0000341
reed@android.com8a1c16f2008-12-17 15:59:43 +0000342 bool next() {
343 // skip over recs with empty clips
344 if (fSkipEmptyClips) {
345 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
346 fCurrLayer = fCurrLayer->fNext;
347 }
348 }
349
reed@google.comf68c5e22012-02-24 16:38:58 +0000350 const DeviceCM* rec = fCurrLayer;
351 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000352
353 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000354 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000355 fDevice = rec->fDevice;
reed41e010c2015-06-09 12:16:53 -0700356 if (!fDevice->accessPixels(&fDst)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700357 fDst.reset(fDevice->imageInfo(), nullptr, 0);
reed41e010c2015-06-09 12:16:53 -0700358 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000359 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000360 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000361
362 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700363 // fCurrLayer may be nullptr now
reed@android.com199f1082009-06-10 02:12:47 +0000364
reed@android.com8a1c16f2008-12-17 15:59:43 +0000365 return true;
366 }
367 return false;
368 }
reed@google.com4b226022011-01-11 18:32:13 +0000369
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000370 SkBaseDevice* getDevice() const { return fDevice; }
reed1e7f5e72016-04-27 07:49:17 -0700371 const SkRasterClip& getClip() const { return *fRC; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000372 int getX() const { return fDevice->getOrigin().x(); }
373 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000374 const SkMatrix& getMatrix() const { return *fMatrix; }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000375 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000376
reed@android.com8a1c16f2008-12-17 15:59:43 +0000377private:
378 SkCanvas* fCanvas;
379 const DeviceCM* fCurrLayer;
380 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000381 SkBool8 fSkipEmptyClips;
382
383 typedef SkDraw INHERITED;
384};
385
386/////////////////////////////////////////////////////////////////////////////
387
reeddbc3cef2015-04-29 12:18:57 -0700388static SkPaint* set_if_needed(SkLazyPaint* lazy, const SkPaint& orig) {
389 return lazy->isValid() ? lazy->get() : lazy->set(orig);
390}
391
392/**
393 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700394 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700395 */
reedd053ce92016-03-22 10:17:23 -0700396static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700397 SkImageFilter* imgf = paint.getImageFilter();
398 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700399 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700400 }
401
reedd053ce92016-03-22 10:17:23 -0700402 SkColorFilter* imgCFPtr;
403 if (!imgf->asAColorFilter(&imgCFPtr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700404 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700405 }
reedd053ce92016-03-22 10:17:23 -0700406 sk_sp<SkColorFilter> imgCF(imgCFPtr);
reeddbc3cef2015-04-29 12:18:57 -0700407
408 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700409 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700410 // there is no existing paint colorfilter, so we can just return the imagefilter's
411 return imgCF;
412 }
413
414 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
415 // and we need to combine them into a single colorfilter.
reedd053ce92016-03-22 10:17:23 -0700416 return SkColorFilter::MakeComposeFilter(std::move(imgCF), sk_ref_sp(paintCF));
reeddbc3cef2015-04-29 12:18:57 -0700417}
418
senorblanco87e066e2015-10-28 11:23:36 -0700419/**
420 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
421 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
422 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
423 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
424 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
425 * conservative "effective" bounds based on the settings in the paint... with one exception. This
426 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
427 * deliberately ignored.
428 */
429static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
430 const SkRect& rawBounds,
431 SkRect* storage) {
432 SkPaint tmpUnfiltered(paint);
433 tmpUnfiltered.setImageFilter(nullptr);
434 if (tmpUnfiltered.canComputeFastBounds()) {
435 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
436 } else {
437 return rawBounds;
438 }
439}
440
reed@android.com8a1c16f2008-12-17 15:59:43 +0000441class AutoDrawLooper {
442public:
senorblanco87e066e2015-10-28 11:23:36 -0700443 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
444 // paint. It's used to determine the size of the offscreen layer for filters.
445 // If null, the clip will be used instead.
reed4a8126e2014-09-22 07:29:03 -0700446 AutoDrawLooper(SkCanvas* canvas, const SkSurfaceProps& props, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000447 bool skipLayerForImageFilter = false,
senorblanco87e066e2015-10-28 11:23:36 -0700448 const SkRect* rawBounds = nullptr) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000449 fCanvas = canvas;
fmalita53d9f1c2016-01-25 06:23:54 -0800450#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000451 fFilter = canvas->getDrawFilter();
fmalita77650002016-01-21 18:47:11 -0800452#else
453 fFilter = nullptr;
454#endif
reed4a8126e2014-09-22 07:29:03 -0700455 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000456 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700457 fTempLayerForImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000458 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000459
reedd053ce92016-03-22 10:17:23 -0700460 auto simplifiedCF = image_to_color_filter(fOrigPaint);
reeddbc3cef2015-04-29 12:18:57 -0700461 if (simplifiedCF) {
462 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reedd053ce92016-03-22 10:17:23 -0700463 paint->setColorFilter(std::move(simplifiedCF));
halcanary96fcdcc2015-08-27 07:41:13 -0700464 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700465 fPaint = paint;
466 }
467
468 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700469 /**
470 * We implement ImageFilters for a given draw by creating a layer, then applying the
471 * imagefilter to the pixels of that layer (its backing surface/image), and then
472 * we call restore() to xfer that layer to the main canvas.
473 *
474 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
475 * 2. Generate the src pixels:
476 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
477 * return (fPaint). We then draw the primitive (using srcover) into a cleared
478 * buffer/surface.
479 * 3. Restore the layer created in #1
480 * The imagefilter is passed the buffer/surface from the layer (now filled with the
481 * src pixels of the primitive). It returns a new "filtered" buffer, which we
482 * draw onto the previous layer using the xfermode from the original paint.
483 */
reed@google.com8926b162012-03-23 15:36:36 +0000484 SkPaint tmp;
reeddbc3cef2015-04-29 12:18:57 -0700485 tmp.setImageFilter(fPaint->getImageFilter());
reedcfb6bdf2016-03-29 11:32:50 -0700486 tmp.setXfermode(sk_ref_sp(fPaint->getXfermode()));
senorblanco87e066e2015-10-28 11:23:36 -0700487 SkRect storage;
488 if (rawBounds) {
489 // Make rawBounds include all paint outsets except for those due to image filters.
490 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
491 }
reedbfd5f172016-01-07 11:28:08 -0800492 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &tmp),
reed76033be2015-03-14 10:54:31 -0700493 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700494 fTempLayerForImageFilter = true;
495 // we remove the imagefilter/xfermode inside doNext()
reed@google.com8926b162012-03-23 15:36:36 +0000496 }
497
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000498 if (SkDrawLooper* looper = paint.getLooper()) {
499 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
500 looper->contextSize());
501 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000502 fIsSimple = false;
503 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700504 fLooperContext = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000505 // can we be marked as simple?
reed5c476fb2015-04-20 08:04:21 -0700506 fIsSimple = !fFilter && !fTempLayerForImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000507 }
piotaixrb5fae932014-09-24 13:03:30 -0700508
reed4a8126e2014-09-22 07:29:03 -0700509 uint32_t oldFlags = paint.getFlags();
510 fNewPaintFlags = filter_paint_flags(props, oldFlags);
511 if (fIsSimple && (fNewPaintFlags != oldFlags)) {
reeddbc3cef2015-04-29 12:18:57 -0700512 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700513 paint->setFlags(fNewPaintFlags);
514 fPaint = paint;
515 // if we're not simple, doNext() will take care of calling setFlags()
516 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000517 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000518
reed@android.com8a1c16f2008-12-17 15:59:43 +0000519 ~AutoDrawLooper() {
reed5c476fb2015-04-20 08:04:21 -0700520 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000521 fCanvas->internalRestore();
522 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000523 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000524 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000525
reed@google.com4e2b3d32011-04-07 14:18:59 +0000526 const SkPaint& paint() const {
527 SkASSERT(fPaint);
528 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000529 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000530
reed@google.com129ec222012-05-15 13:24:09 +0000531 bool next(SkDrawFilter::Type drawType) {
532 if (fDone) {
533 return false;
534 } else if (fIsSimple) {
535 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000536 return !fPaint->nothingToDraw();
537 } else {
538 return this->doNext(drawType);
539 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000540 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000541
reed@android.com8a1c16f2008-12-17 15:59:43 +0000542private:
reeddbc3cef2015-04-29 12:18:57 -0700543 SkLazyPaint fLazyPaintInit; // base paint storage in case we need to modify it
544 SkLazyPaint fLazyPaintPerLooper; // per-draw-looper storage, so the looper can modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000545 SkCanvas* fCanvas;
546 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000547 SkDrawFilter* fFilter;
548 const SkPaint* fPaint;
549 int fSaveCount;
reed4a8126e2014-09-22 07:29:03 -0700550 uint32_t fNewPaintFlags;
reed5c476fb2015-04-20 08:04:21 -0700551 bool fTempLayerForImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000552 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000553 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000554 SkDrawLooper::Context* fLooperContext;
555 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000556
557 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000558};
559
reed@google.com129ec222012-05-15 13:24:09 +0000560bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
halcanary96fcdcc2015-08-27 07:41:13 -0700561 fPaint = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000562 SkASSERT(!fIsSimple);
reed5c476fb2015-04-20 08:04:21 -0700563 SkASSERT(fLooperContext || fFilter || fTempLayerForImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000564
reeddbc3cef2015-04-29 12:18:57 -0700565 SkPaint* paint = fLazyPaintPerLooper.set(fLazyPaintInit.isValid() ?
566 *fLazyPaintInit.get() : fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700567 paint->setFlags(fNewPaintFlags);
reed@google.com129ec222012-05-15 13:24:09 +0000568
reed5c476fb2015-04-20 08:04:21 -0700569 if (fTempLayerForImageFilter) {
halcanary96fcdcc2015-08-27 07:41:13 -0700570 paint->setImageFilter(nullptr);
571 paint->setXfermode(nullptr);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000572 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000573
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000574 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000575 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000576 return false;
577 }
578 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000579 if (!fFilter->filter(paint, drawType)) {
580 fDone = true;
581 return false;
582 }
halcanary96fcdcc2015-08-27 07:41:13 -0700583 if (nullptr == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000584 // no looper means we only draw once
585 fDone = true;
586 }
587 }
588 fPaint = paint;
589
590 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000591 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000592 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000593 }
594
595 // call this after any possible paint modifiers
596 if (fPaint->nothingToDraw()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700597 fPaint = nullptr;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000598 return false;
599 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000600 return true;
601}
602
reed@android.com8a1c16f2008-12-17 15:59:43 +0000603////////// macros to place around the internal draw calls //////////////////
604
reed262a71b2015-12-05 13:07:27 -0800605#define LOOPER_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
606 this->predrawNotify(); \
607 AutoDrawLooper looper(this, fProps, paint, skipLayerForFilter, bounds); \
608 while (looper.next(SkDrawFilter::kBitmap_Type)) { \
609 SkDrawIter iter(this);
610
611
reed@google.com8926b162012-03-23 15:36:36 +0000612#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000613 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700614 AutoDrawLooper looper(this, fProps, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000615 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000616 SkDrawIter iter(this);
617
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000618#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000619 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700620 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000621 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000622 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000623
reedc83a2972015-07-16 07:40:45 -0700624#define LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, type, bounds, auxOpaque) \
625 this->predrawNotify(bounds, &paint, auxOpaque); \
626 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
627 while (looper.next(type)) { \
628 SkDrawIter iter(this);
629
reed@google.com4e2b3d32011-04-07 14:18:59 +0000630#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000631
632////////////////////////////////////////////////////////////////////////////
633
mtkleinfeaadee2015-04-08 11:25:48 -0700634void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
635 this->restoreToCount(1);
636 fCachedLocalClipBounds.setEmpty();
637 fCachedLocalClipBoundsDirty = true;
638 fClipStack->reset();
639 fMCRec->reset(bounds);
640
641 // We're peering through a lot of structs here. Only at this scope do we
642 // know that the device is an SkBitmapDevice (really an SkNoPixelsBitmapDevice).
643 static_cast<SkBitmapDevice*>(fMCRec->fLayer->fDevice)->setNewSize(bounds.size());
644}
645
reedd9544982014-09-09 18:46:22 -0700646SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
reed42b73eb2015-11-20 13:42:42 -0800647 if (device && device->forceConservativeRasterClip()) {
648 flags = InitFlags(flags | kConservativeRasterClip_InitFlag);
649 }
650 // Since init() is only called once by our constructors, it is safe to perform this
651 // const-cast.
652 *const_cast<bool*>(&fConservativeRasterClip) = SkToBool(flags & kConservativeRasterClip_InitFlag);
653
reed@google.comc0784db2013-12-13 21:16:12 +0000654 fCachedLocalClipBounds.setEmpty();
655 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000656 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000657 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700658 fDeviceCMDirty = true;
reed2ff1fce2014-12-11 07:07:37 -0800659 fSaveCount = 1;
halcanary96fcdcc2015-08-27 07:41:13 -0700660 fMetaData = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000661
halcanary385fe4d2015-08-26 13:07:48 -0700662 fClipStack.reset(new SkClipStack);
reed687fa1c2015-04-07 08:00:56 -0700663
reed@android.com8a1c16f2008-12-17 15:59:43 +0000664 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700665 new (fMCRec) MCRec(fConservativeRasterClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000666
reeda499f902015-05-01 09:34:31 -0700667 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
668 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
reed8c30a812016-04-20 16:36:51 -0700669 new (fDeviceCMStorage) DeviceCM(nullptr, nullptr, nullptr, fConservativeRasterClip, false,
670 fMCRec->fMatrix);
reedb679ca82015-04-07 04:40:48 -0700671
reed@android.com8a1c16f2008-12-17 15:59:43 +0000672 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000673
halcanary96fcdcc2015-08-27 07:41:13 -0700674 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000675
reedf92c8662014-08-18 08:02:43 -0700676 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700677 // The root device and the canvas should always have the same pixel geometry
678 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reedf92c8662014-08-18 08:02:43 -0700679 device->onAttachToCanvas(this);
680 fMCRec->fLayer->fDevice = SkRef(device);
reed78e27682014-11-19 08:04:34 -0800681 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
reedf92c8662014-08-18 08:02:43 -0700682 }
683 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000684}
685
reed@google.comcde92112011-07-06 20:00:52 +0000686SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000687 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700688 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800689 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000690{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000691 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000692
halcanary96fcdcc2015-08-27 07:41:13 -0700693 this->init(nullptr, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000694}
695
reedd9544982014-09-09 18:46:22 -0700696static SkBitmap make_nopixels(int width, int height) {
697 SkBitmap bitmap;
698 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
699 return bitmap;
700}
701
702class SkNoPixelsBitmapDevice : public SkBitmapDevice {
703public:
robertphillipsfcf78292015-06-19 11:49:52 -0700704 SkNoPixelsBitmapDevice(const SkIRect& bounds, const SkSurfaceProps& surfaceProps)
705 : INHERITED(make_nopixels(bounds.width(), bounds.height()), surfaceProps)
reed78e27682014-11-19 08:04:34 -0800706 {
707 this->setOrigin(bounds.x(), bounds.y());
708 }
reedd9544982014-09-09 18:46:22 -0700709
710private:
piotaixrb5fae932014-09-24 13:03:30 -0700711
reedd9544982014-09-09 18:46:22 -0700712 typedef SkBitmapDevice INHERITED;
713};
714
reed96a857e2015-01-25 10:33:58 -0800715SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000716 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800717 , fProps(SkSurfacePropsCopyOrDefault(props))
reed42b73eb2015-11-20 13:42:42 -0800718 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000719{
720 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700721
halcanary385fe4d2015-08-26 13:07:48 -0700722 this->init(new SkNoPixelsBitmapDevice(SkIRect::MakeWH(width, height), fProps),
723 kDefault_InitFlags)->unref();
reedd9544982014-09-09 18:46:22 -0700724}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000725
reed78e27682014-11-19 08:04:34 -0800726SkCanvas::SkCanvas(const SkIRect& bounds, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700727 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700728 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800729 , fConservativeRasterClip(false)
reedd9544982014-09-09 18:46:22 -0700730{
731 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700732
halcanary385fe4d2015-08-26 13:07:48 -0700733 this->init(new SkNoPixelsBitmapDevice(bounds, fProps), flags)->unref();
reedd9544982014-09-09 18:46:22 -0700734}
735
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000736SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000737 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700738 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800739 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000740{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000741 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700742
reedd9544982014-09-09 18:46:22 -0700743 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000744}
745
robertphillipsfcf78292015-06-19 11:49:52 -0700746SkCanvas::SkCanvas(SkBaseDevice* device, InitFlags flags)
747 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700748 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800749 , fConservativeRasterClip(false)
robertphillipsfcf78292015-06-19 11:49:52 -0700750{
751 inc_canvas();
752
753 this->init(device, flags);
754}
755
reed4a8126e2014-09-22 07:29:03 -0700756SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700757 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700758 , fProps(props)
reed42b73eb2015-11-20 13:42:42 -0800759 , fConservativeRasterClip(false)
reed3716fd02014-09-21 09:39:55 -0700760{
761 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700762
halcanary385fe4d2015-08-26 13:07:48 -0700763 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700764 this->init(device, kDefault_InitFlags);
765}
reed29c857d2014-09-21 10:25:07 -0700766
reed4a8126e2014-09-22 07:29:03 -0700767SkCanvas::SkCanvas(const SkBitmap& bitmap)
768 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
769 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800770 , fConservativeRasterClip(false)
reed4a8126e2014-09-22 07:29:03 -0700771{
772 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700773
halcanary385fe4d2015-08-26 13:07:48 -0700774 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700775 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000776}
777
778SkCanvas::~SkCanvas() {
779 // free up the contents of our deque
780 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000781
reed@android.com8a1c16f2008-12-17 15:59:43 +0000782 this->internalRestore(); // restore the last, since we're going away
783
halcanary385fe4d2015-08-26 13:07:48 -0700784 delete fMetaData;
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000785
reed@android.com8a1c16f2008-12-17 15:59:43 +0000786 dec_canvas();
787}
788
fmalita53d9f1c2016-01-25 06:23:54 -0800789#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000790SkDrawFilter* SkCanvas::getDrawFilter() const {
791 return fMCRec->fFilter;
792}
793
794SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
reed51985e32015-04-11 08:04:56 -0700795 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000796 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
797 return filter;
798}
fmalita77650002016-01-21 18:47:11 -0800799#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000800
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000801SkMetaData& SkCanvas::getMetaData() {
802 // metadata users are rare, so we lazily allocate it. If that changes we
803 // can decide to just make it a field in the device (rather than a ptr)
halcanary96fcdcc2015-08-27 07:41:13 -0700804 if (nullptr == fMetaData) {
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000805 fMetaData = new SkMetaData;
806 }
807 return *fMetaData;
808}
809
reed@android.com8a1c16f2008-12-17 15:59:43 +0000810///////////////////////////////////////////////////////////////////////////////
811
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000812void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000813 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000814 if (device) {
815 device->flush();
816 }
817}
818
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000819SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000820 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000821 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
822}
823
senorblancoafc7cce2016-02-02 18:44:15 -0800824SkIRect SkCanvas::getTopLayerBounds() const {
825 SkBaseDevice* d = this->getTopDevice();
826 if (!d) {
827 return SkIRect::MakeEmpty();
828 }
829 return SkIRect::MakeXYWH(d->getOrigin().x(), d->getOrigin().y(), d->width(), d->height());
830}
831
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000832SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000833 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000834 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000835 SkASSERT(rec && rec->fLayer);
836 return rec->fLayer->fDevice;
837}
838
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000839SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000840 if (updateMatrixClip) {
841 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
842 }
reed@google.com9266fed2011-03-30 00:18:03 +0000843 return fMCRec->fTopLayer->fDevice;
844}
845
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000846bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
847 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
848 return false;
849 }
850
851 bool weAllocated = false;
halcanary96fcdcc2015-08-27 07:41:13 -0700852 if (nullptr == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700853 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000854 return false;
855 }
856 weAllocated = true;
857 }
858
reedcf01e312015-05-23 19:14:51 -0700859 SkAutoPixmapUnlock unlocker;
860 if (bitmap->requestLock(&unlocker)) {
861 const SkPixmap& pm = unlocker.pixmap();
862 if (this->readPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), x, y)) {
863 return true;
864 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000865 }
866
867 if (weAllocated) {
halcanary96fcdcc2015-08-27 07:41:13 -0700868 bitmap->setPixelRef(nullptr);
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000869 }
870 return false;
871}
reed@google.com51df9e32010-12-23 19:29:18 +0000872
bsalomon@google.comc6980972011-11-02 19:57:21 +0000873bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000874 SkIRect r = srcRect;
875 const SkISize size = this->getBaseLayerSize();
876 if (!r.intersect(0, 0, size.width(), size.height())) {
877 bitmap->reset();
878 return false;
879 }
880
reed84825042014-09-02 12:50:45 -0700881 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000882 // bitmap will already be reset.
883 return false;
884 }
885 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
886 bitmap->reset();
887 return false;
888 }
889 return true;
890}
891
reed96472de2014-12-10 09:53:42 -0800892bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000893 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000894 if (!device) {
895 return false;
896 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000897 const SkISize size = this->getBaseLayerSize();
mtkleinf0f14112014-12-12 08:46:25 -0800898
reed96472de2014-12-10 09:53:42 -0800899 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
900 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000901 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000902 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000903
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000904 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800905 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000906}
907
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000908bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
909 if (bitmap.getTexture()) {
910 return false;
911 }
reedcf01e312015-05-23 19:14:51 -0700912
913 SkAutoPixmapUnlock unlocker;
914 if (bitmap.requestLock(&unlocker)) {
915 const SkPixmap& pm = unlocker.pixmap();
916 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000917 }
918 return false;
919}
920
921bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
922 int x, int y) {
923 switch (origInfo.colorType()) {
924 case kUnknown_SkColorType:
925 case kIndex_8_SkColorType:
926 return false;
927 default:
928 break;
929 }
halcanary96fcdcc2015-08-27 07:41:13 -0700930 if (nullptr == pixels || rowBytes < origInfo.minRowBytes()) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000931 return false;
932 }
933
934 const SkISize size = this->getBaseLayerSize();
935 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
936 if (!target.intersect(0, 0, size.width(), size.height())) {
937 return false;
938 }
939
940 SkBaseDevice* device = this->getDevice();
941 if (!device) {
942 return false;
943 }
944
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000945 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700946 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000947
948 // if x or y are negative, then we have to adjust pixels
949 if (x > 0) {
950 x = 0;
951 }
952 if (y > 0) {
953 y = 0;
954 }
955 // here x,y are either 0 or negative
956 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
957
reed4af35f32014-06-27 17:47:49 -0700958 // Tell our owning surface to bump its generation ID
reedc83a2972015-07-16 07:40:45 -0700959 const bool completeOverwrite = info.dimensions() == size;
960 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700961
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000962 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000963 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000964}
reed@google.com51df9e32010-12-23 19:29:18 +0000965
junov@google.com4370aed2012-01-18 16:21:08 +0000966SkCanvas* SkCanvas::canvasForDrawIter() {
967 return this;
968}
969
reed@android.com8a1c16f2008-12-17 15:59:43 +0000970//////////////////////////////////////////////////////////////////////////////
971
reed@android.com8a1c16f2008-12-17 15:59:43 +0000972void SkCanvas::updateDeviceCMCache() {
973 if (fDeviceCMDirty) {
974 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700975 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000976 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000977
halcanary96fcdcc2015-08-27 07:41:13 -0700978 if (nullptr == layer->fNext) { // only one layer
979 layer->updateMC(totalMatrix, totalClip, *fClipStack, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000980 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000981 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000982 do {
reed687fa1c2015-04-07 08:00:56 -0700983 layer->updateMC(totalMatrix, clip, *fClipStack, &clip);
halcanary96fcdcc2015-08-27 07:41:13 -0700984 } while ((layer = layer->fNext) != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000985 }
986 fDeviceCMDirty = false;
987 }
988}
989
reed@android.com8a1c16f2008-12-17 15:59:43 +0000990///////////////////////////////////////////////////////////////////////////////
991
reed2ff1fce2014-12-11 07:07:37 -0800992void SkCanvas::checkForDeferredSave() {
993 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800994 this->doSave();
995 }
996}
997
reedf0090cb2014-11-26 08:55:51 -0800998int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800999#ifdef SK_DEBUG
1000 int count = 0;
1001 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
1002 for (;;) {
1003 const MCRec* rec = (const MCRec*)iter.next();
1004 if (!rec) {
1005 break;
1006 }
1007 count += 1 + rec->fDeferredSaveCount;
1008 }
1009 SkASSERT(count == fSaveCount);
1010#endif
1011 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -08001012}
1013
1014int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -08001015 fSaveCount += 1;
1016 fMCRec->fDeferredSaveCount += 1;
1017 return this->getSaveCount() - 1; // return our prev value
1018}
1019
1020void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -08001021 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -07001022
1023 SkASSERT(fMCRec->fDeferredSaveCount > 0);
1024 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001025 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -08001026}
1027
1028void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -08001029 if (fMCRec->fDeferredSaveCount > 0) {
1030 SkASSERT(fSaveCount > 1);
1031 fSaveCount -= 1;
1032 fMCRec->fDeferredSaveCount -= 1;
1033 } else {
1034 // check for underflow
1035 if (fMCStack.count() > 1) {
1036 this->willRestore();
1037 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -07001038 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001039 this->internalRestore();
1040 this->didRestore();
1041 }
reedf0090cb2014-11-26 08:55:51 -08001042 }
1043}
1044
1045void SkCanvas::restoreToCount(int count) {
1046 // sanity check
1047 if (count < 1) {
1048 count = 1;
1049 }
mtkleinf0f14112014-12-12 08:46:25 -08001050
reedf0090cb2014-11-26 08:55:51 -08001051 int n = this->getSaveCount() - count;
1052 for (int i = 0; i < n; ++i) {
1053 this->restore();
1054 }
1055}
1056
reed2ff1fce2014-12-11 07:07:37 -08001057void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001058 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -07001059 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +00001060 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +00001061
reed687fa1c2015-04-07 08:00:56 -07001062 fClipStack->save();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001063}
1064
reed4960eee2015-12-18 07:09:18 -08001065bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
reed@google.comb93ba452014-03-10 19:47:58 +00001066#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001067 return !(saveLayerFlags & SkCanvas::kDontClipToLayer_PrivateSaveLayerFlag);
reed@google.comb93ba452014-03-10 19:47:58 +00001068#else
1069 return true;
1070#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001071}
1072
reed4960eee2015-12-18 07:09:18 -08001073bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -07001074 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001075 SkIRect clipBounds;
1076 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001077 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +00001078 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001079
reed96e657d2015-03-10 17:30:07 -07001080 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
1081
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001082 if (imageFilter) {
senorblancoe5e79842016-03-21 14:51:59 -07001083 clipBounds = imageFilter->filterBounds(clipBounds, ctm);
senorblancodb64af32015-12-09 10:11:43 -08001084 if (bounds && !imageFilter->canComputeFastBounds()) {
1085 bounds = nullptr;
1086 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001087 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001088 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -07001089 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001090 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +00001091
reed96e657d2015-03-10 17:30:07 -07001092 ctm.mapRect(&r, *bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001093 r.roundOut(&ir);
1094 // early exit if the layer's bounds are clipped out
1095 if (!ir.intersect(clipBounds)) {
reed4960eee2015-12-18 07:09:18 -08001096 if (BoundsAffectsClip(saveLayerFlags)) {
reed9b3aa542015-03-11 08:47:12 -07001097 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001098 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001099 }
junov@chromium.orga907ac32012-02-24 21:54:07 +00001100 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001101 }
1102 } else { // no user bounds, so just use the clip
1103 ir = clipBounds;
1104 }
reed180aec42015-03-11 10:39:04 -07001105 SkASSERT(!ir.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001106
reed4960eee2015-12-18 07:09:18 -08001107 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -07001108 // Simplify the current clips since they will be applied properly during restore()
reed9b3aa542015-03-11 08:47:12 -07001109 fCachedLocalClipBoundsDirty = true;
reed687fa1c2015-04-07 08:00:56 -07001110 fClipStack->clipDevRect(ir, SkRegion::kReplace_Op);
reed180aec42015-03-11 10:39:04 -07001111 fMCRec->fRasterClip.setRect(ir);
junov@chromium.orga907ac32012-02-24 21:54:07 +00001112 }
1113
1114 if (intersection) {
1115 *intersection = ir;
1116 }
1117 return true;
1118}
1119
reed4960eee2015-12-18 07:09:18 -08001120
reed4960eee2015-12-18 07:09:18 -08001121int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
1122 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001123}
1124
reed70ee31b2015-12-10 13:44:45 -08001125int SkCanvas::saveLayerPreserveLCDTextRequests(const SkRect* bounds, const SkPaint* paint) {
reed4960eee2015-12-18 07:09:18 -08001126 return this->saveLayer(SaveLayerRec(bounds, paint, kPreserveLCDText_SaveLayerFlag));
1127}
1128
1129int SkCanvas::saveLayer(const SaveLayerRec& origRec) {
1130 SaveLayerRec rec(origRec);
1131 if (gIgnoreSaveLayerBounds) {
1132 rec.fBounds = nullptr;
1133 }
1134 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
1135 fSaveCount += 1;
1136 this->internalSaveLayer(rec, strategy);
1137 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -08001138}
1139
reedbfd5f172016-01-07 11:28:08 -08001140static void draw_filter_into_device(SkBaseDevice* src, const SkImageFilter* filter,
1141 SkBaseDevice* dst, const SkMatrix& ctm) {
robertphillips7354a4b2015-12-16 05:08:27 -08001142
1143 SkBitmap srcBM;
1144
1145#if SK_SUPPORT_GPU
robertphillips175dd9b2016-04-28 14:32:04 -07001146 // TODO: remove this virtual usage of accessRenderTarget! It is preventing
1147 // removal of the virtual on SkBaseDevice.
robertphillips7354a4b2015-12-16 05:08:27 -08001148 GrRenderTarget* srcRT = src->accessRenderTarget();
1149 if (srcRT && !srcRT->asTexture() && dst->accessRenderTarget()) {
1150 // When both the src & the dst are on the gpu but the src doesn't have a texture,
1151 // we create a temporary texture for the draw.
1152 // TODO: we should actually only copy the portion of the source needed to apply the image
1153 // filter
1154 GrContext* context = srcRT->getContext();
bsalomon5ec26ae2016-02-25 08:33:02 -08001155 SkAutoTUnref<GrTexture> tex(context->textureProvider()->createTexture(srcRT->desc(),
1156 SkBudgeted::kYes));
robertphillips7354a4b2015-12-16 05:08:27 -08001157
1158 context->copySurface(tex, srcRT);
1159
1160 GrWrapTextureInBitmap(tex, src->width(), src->height(), src->isOpaque(), &srcBM);
1161 } else
1162#endif
1163 {
1164 srcBM = src->accessBitmap(false);
1165 }
1166
1167 SkCanvas c(dst);
1168
1169 SkPaint p;
robertphillips372177e2016-03-30 07:32:28 -07001170 p.setImageFilter(filter->makeWithLocalMatrix(ctm));
reedbfd5f172016-01-07 11:28:08 -08001171 const SkScalar x = SkIntToScalar(src->getOrigin().x());
1172 const SkScalar y = SkIntToScalar(src->getOrigin().y());
1173 c.drawBitmap(srcBM, x, y, &p);
robertphillips7354a4b2015-12-16 05:08:27 -08001174}
reed70ee31b2015-12-10 13:44:45 -08001175
reed129ed1c2016-02-22 06:42:31 -08001176static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, bool isOpaque,
1177 const SkPaint* paint) {
1178 // need to force L32 for now if we have an image filter. Once filters support other colortypes
1179 // e.g. sRGB or F16, we can remove this check
brianosman52ede1d2016-06-20 08:25:02 -07001180 // SRGBTODO: Can we remove this check now?
reed129ed1c2016-02-22 06:42:31 -08001181 const bool hasImageFilter = paint && paint->getImageFilter();
1182
1183 SkAlphaType alphaType = isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
1184 if ((prev.bytesPerPixel() < 4) || hasImageFilter) {
1185 // force to L32
1186 return SkImageInfo::MakeN32(w, h, alphaType);
1187 } else {
1188 // keep the same characteristics as the prev
brianosman52ede1d2016-06-20 08:25:02 -07001189 return SkImageInfo::Make(w, h, prev.colorType(), alphaType, sk_ref_sp(prev.colorSpace()));
reed129ed1c2016-02-22 06:42:31 -08001190 }
1191}
1192
reed4960eee2015-12-18 07:09:18 -08001193void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
1194 const SkRect* bounds = rec.fBounds;
1195 const SkPaint* paint = rec.fPaint;
1196 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1197
reed@google.comb93ba452014-03-10 19:47:58 +00001198#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001199 saveLayerFlags &= ~kDontClipToLayer_PrivateSaveLayerFlag;
reed@google.comb93ba452014-03-10 19:47:58 +00001200#endif
1201
reed8c30a812016-04-20 16:36:51 -07001202 SkLazyPaint lazyP;
1203 SkImageFilter* imageFilter = paint ? paint->getImageFilter() : NULL;
1204 SkMatrix stashedMatrix = fMCRec->fMatrix;
reed8c30a812016-04-20 16:36:51 -07001205 SkMatrix remainder;
1206 SkSize scale;
1207 /*
1208 * ImageFilters (so far) do not correctly handle matrices (CTM) that contain rotation/skew/etc.
1209 * but they do handle scaling. To accommodate this, we do the following:
1210 *
1211 * 1. Stash off the current CTM
1212 * 2. Decompose the CTM into SCALE and REMAINDER
1213 * 3. Wack the CTM to be just SCALE, and wrap the imagefilter with a MatrixImageFilter that
1214 * contains the REMAINDER
1215 * 4. Proceed as usual, allowing the client to draw into the layer (now with a scale-only CTM)
1216 * 5. During restore, we process the MatrixImageFilter, which applies REMAINDER to the output
1217 * of the original imagefilter, and draw that (via drawSprite)
1218 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1219 *
1220 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1221 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1222 */
reed96a04f32016-04-25 09:25:15 -07001223 if (imageFilter && !stashedMatrix.isScaleTranslate() && !imageFilter->canHandleComplexCTM() &&
reed8c30a812016-04-20 16:36:51 -07001224 stashedMatrix.decomposeScale(&scale, &remainder))
1225 {
1226 // We will restore the matrix (which we are overwriting here) in restore via fStashedMatrix
1227 this->internalSetMatrix(SkMatrix::MakeScale(scale.width(), scale.height()));
1228 SkPaint* p = lazyP.set(*paint);
1229 p->setImageFilter(SkImageFilter::MakeMatrixFilter(remainder,
1230 SkFilterQuality::kLow_SkFilterQuality,
1231 sk_ref_sp(imageFilter)));
1232 imageFilter = p->getImageFilter();
1233 paint = p;
1234 }
reed8c30a812016-04-20 16:36:51 -07001235
junov@chromium.orga907ac32012-02-24 21:54:07 +00001236 // do this before we create the layer. We don't call the public save() since
1237 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001238 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001239
1240 fDeviceCMDirty = true;
1241
1242 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001243 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
reed2ff1fce2014-12-11 07:07:37 -08001244 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001245 }
1246
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001247 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1248 // the clipRectBounds() call above?
1249 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001250 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001251 }
1252
reed4960eee2015-12-18 07:09:18 -08001253 bool isOpaque = SkToBool(saveLayerFlags & kIsOpaque_SaveLayerFlag);
reed8dc0ccb2015-03-20 06:32:52 -07001254 SkPixelGeometry geo = fProps.pixelGeometry();
1255 if (paint) {
reed76033be2015-03-14 10:54:31 -07001256 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001257 if (paint->getImageFilter() || paint->getColorFilter()) {
reed76033be2015-03-14 10:54:31 -07001258 isOpaque = false;
reed8dc0ccb2015-03-20 06:32:52 -07001259 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001260 }
1261 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001262
reedb2db8982014-11-13 12:41:02 -08001263 SkBaseDevice* device = this->getTopDevice();
halcanary96fcdcc2015-08-27 07:41:13 -07001264 if (nullptr == device) {
reedb2db8982014-11-13 12:41:02 -08001265 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001266 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001267 }
reedb2db8982014-11-13 12:41:02 -08001268
reed129ed1c2016-02-22 06:42:31 -08001269 SkImageInfo info = make_layer_info(device->imageInfo(), ir.width(), ir.height(), isOpaque,
1270 paint);
1271
reed61f501f2015-04-29 08:34:00 -07001272 bool forceSpriteOnRestore = false;
1273 {
reed70ee31b2015-12-10 13:44:45 -08001274 const bool preserveLCDText = kOpaque_SkAlphaType == info.alphaType() ||
reed4960eee2015-12-18 07:09:18 -08001275 (saveLayerFlags & kPreserveLCDText_SaveLayerFlag);
reeddaa57bf2015-05-15 10:39:17 -07001276 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
reed70ee31b2015-12-10 13:44:45 -08001277 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
1278 preserveLCDText, false);
reed61f501f2015-04-29 08:34:00 -07001279 SkBaseDevice* newDev = device->onCreateDevice(createInfo, paint);
halcanary96fcdcc2015-08-27 07:41:13 -07001280 if (nullptr == newDev) {
reed61f501f2015-04-29 08:34:00 -07001281 // If onCreateDevice didn't succeed, try raster (e.g. PDF couldn't handle the paint)
robertphillips9a53fd72015-06-22 09:46:59 -07001282 const SkSurfaceProps surfaceProps(fProps.flags(), createInfo.fPixelGeometry);
1283 newDev = SkBitmapDevice::Create(createInfo.fInfo, surfaceProps);
halcanary96fcdcc2015-08-27 07:41:13 -07001284 if (nullptr == newDev) {
reed61f501f2015-04-29 08:34:00 -07001285 SkErrorInternals::SetError(kInternalError_SkError,
1286 "Unable to create device for layer.");
1287 return;
1288 }
1289 forceSpriteOnRestore = true;
1290 }
1291 device = newDev;
bungeman@google.come25c6842011-08-17 14:53:54 +00001292 }
reed@google.com6f8f2922011-03-04 22:27:10 +00001293 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips7354a4b2015-12-16 05:08:27 -08001294
reedbfd5f172016-01-07 11:28:08 -08001295 if (rec.fBackdrop) {
1296 draw_filter_into_device(fMCRec->fTopLayer->fDevice, rec.fBackdrop, device, fMCRec->fMatrix);
robertphillips7354a4b2015-12-16 05:08:27 -08001297 }
1298
reed8c30a812016-04-20 16:36:51 -07001299 DeviceCM* layer = new DeviceCM(device, paint, this, fConservativeRasterClip,
1300 forceSpriteOnRestore, stashedMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001301 device->unref();
1302
1303 layer->fNext = fMCRec->fTopLayer;
1304 fMCRec->fLayer = layer;
1305 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reed@android.com8a1c16f2008-12-17 15:59:43 +00001306}
1307
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001308int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001309 if (0xFF == alpha) {
1310 return this->saveLayer(bounds, nullptr);
1311 } else {
1312 SkPaint tmpPaint;
1313 tmpPaint.setAlpha(alpha);
1314 return this->saveLayer(bounds, &tmpPaint);
1315 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001316}
1317
reed@android.com8a1c16f2008-12-17 15:59:43 +00001318void SkCanvas::internalRestore() {
1319 SkASSERT(fMCStack.count() != 0);
1320
1321 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001322 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001323
reed687fa1c2015-04-07 08:00:56 -07001324 fClipStack->restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001325
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001326 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001327 DeviceCM* layer = fMCRec->fLayer; // may be null
1328 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001329 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001330
1331 // now do the normal restore()
1332 fMCRec->~MCRec(); // balanced in save()
1333 fMCStack.pop_back();
1334 fMCRec = (MCRec*)fMCStack.back();
1335
1336 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1337 since if we're being recorded, we don't want to record this (the
1338 recorder will have already recorded the restore).
1339 */
bsalomon49f085d2014-09-05 13:34:00 -07001340 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001341 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001342 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001343 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
reed61f501f2015-04-29 08:34:00 -07001344 layer->fPaint, layer->fDeviceIsBitmapDevice);
reed8c30a812016-04-20 16:36:51 -07001345 // restore what we smashed in internalSaveLayer
1346 fMCRec->fMatrix = layer->fStashedMatrix;
reed@google.com8926b162012-03-23 15:36:36 +00001347 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001348 fDeviceCMDirty = true;
halcanary385fe4d2015-08-26 13:07:48 -07001349 delete layer;
reedb679ca82015-04-07 04:40:48 -07001350 } else {
1351 // we're at the root
reeda499f902015-05-01 09:34:31 -07001352 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001353 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001354 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001355 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001356 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001357}
1358
reede8f30622016-03-23 18:59:25 -07001359sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001360 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001361 props = &fProps;
1362 }
1363 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001364}
1365
reede8f30622016-03-23 18:59:25 -07001366sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001367 SkBaseDevice* dev = this->getDevice();
reede8f30622016-03-23 18:59:25 -07001368 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001369}
1370
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001371SkImageInfo SkCanvas::imageInfo() const {
1372 SkBaseDevice* dev = this->getDevice();
1373 if (dev) {
1374 return dev->imageInfo();
1375 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001376 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001377 }
1378}
1379
brianosman898235c2016-04-06 07:38:23 -07001380bool SkCanvas::getProps(SkSurfaceProps* props) const {
1381 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
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001441void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed61f501f2015-04-29 08:34:00 -07001442 const SkPaint* paint, bool deviceIsBitmapDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001443 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001444 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001445 paint = &tmp;
1446 }
reed@google.com4b226022011-01-11 18:32:13 +00001447
reed@google.com8926b162012-03-23 15:36:36 +00001448 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
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) {
robertphillips4418dba2016-03-07 12:45:14 -08001455 const SkBitmap& srcBM = srcDev->accessBitmap(false);
reed1eca1162016-04-25 12:29:38 -07001456 dstDev->drawSpriteWithFilter(iter, srcBM, pos.x(), pos.y(), *paint);
reed61f501f2015-04-29 08:34:00 -07001457 } else if (deviceIsBitmapDevice) {
reed9572a102015-05-26 19:22:17 -07001458 const SkBitmap& src = static_cast<SkBitmapDevice*>(srcDev)->fBitmap;
reed61f501f2015-04-29 08:34:00 -07001459 dstDev->drawSprite(iter, src, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001460 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001461 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001462 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001463 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001464 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001465}
1466
reed32704672015-12-16 08:27:10 -08001467/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001468
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001469void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001470 SkMatrix m;
1471 m.setTranslate(dx, dy);
1472 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001473}
1474
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001475void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001476 SkMatrix m;
1477 m.setScale(sx, sy);
1478 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001479}
1480
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001481void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001482 SkMatrix m;
1483 m.setRotate(degrees);
1484 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001485}
1486
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001487void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001488 SkMatrix m;
1489 m.setSkew(sx, sy);
1490 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001491}
1492
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001493void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001494 if (matrix.isIdentity()) {
1495 return;
1496 }
1497
reed2ff1fce2014-12-11 07:07:37 -08001498 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001499 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001500 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001501 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001502
1503 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001504}
1505
reed8c30a812016-04-20 16:36:51 -07001506void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001507 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001508 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001509 fMCRec->fMatrix = matrix;
reed8c30a812016-04-20 16:36:51 -07001510}
1511
1512void SkCanvas::setMatrix(const SkMatrix& matrix) {
1513 this->checkForDeferredSave();
1514 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001515 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001516}
1517
reed@android.com8a1c16f2008-12-17 15:59:43 +00001518void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001519 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001520}
1521
1522//////////////////////////////////////////////////////////////////////////////
1523
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001524void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001525 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001526 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1527 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001528}
1529
1530void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001531#ifdef SK_ENABLE_CLIP_QUICKREJECT
1532 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001533 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001534 return false;
1535 }
1536
reed@google.com3b3e8952012-08-16 20:53:31 +00001537 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001538 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001539 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001540
reed687fa1c2015-04-07 08:00:56 -07001541 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001542 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001543 }
1544 }
1545#endif
1546
bsalomonac8cabd2015-11-20 18:53:07 -08001547 if (!fAllowSoftClip) {
1548 edgeStyle = kHard_ClipEdgeStyle;
1549 }
reed90ba0952015-11-20 13:42:47 -08001550
reedc64eff52015-11-21 12:39:45 -08001551 const bool rectStaysRect = fMCRec->fMatrix.rectStaysRect();
1552 SkRect devR;
1553 if (rectStaysRect) {
1554 fMCRec->fMatrix.mapRect(&devR, rect);
1555 }
bsalomonac8cabd2015-11-20 18:53:07 -08001556
reedc64eff52015-11-21 12:39:45 -08001557 // Check if we can quick-accept the clip call (and do nothing)
1558 //
1559 // TODO: investigate if a (conservative) version of this could be done in ::clipRect,
1560 // so that subclasses (like PictureRecording) didn't see unnecessary clips, which in turn
1561 // might allow lazy save/restores to eliminate entire save/restore blocks.
1562 //
1563 if (SkRegion::kIntersect_Op == op &&
1564 kHard_ClipEdgeStyle == edgeStyle
1565 && rectStaysRect)
1566 {
1567 if (devR.round().contains(fMCRec->fRasterClip.getBounds())) {
1568#if 0
1569 SkDebugf("------- ignored clipRect [%g %g %g %g]\n",
1570 rect.left(), rect.top(), rect.right(), rect.bottom());
1571#endif
1572 return;
1573 }
1574 }
1575
1576 AutoValidateClip avc(this);
1577
1578 fDeviceCMDirty = true;
1579 fCachedLocalClipBoundsDirty = true;
1580
1581 if (rectStaysRect) {
1582 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1583 fClipStack->clipDevRect(devR, op, isAA);
senorblancoafc7cce2016-02-02 18:44:15 -08001584 fMCRec->fRasterClip.op(devR, this->getTopLayerBounds(), op, isAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001585 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001586 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001587 // and clip against that, since it can handle any matrix. However, to
1588 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1589 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001590 SkPath path;
1591
1592 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001593 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001594 }
1595}
1596
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001597void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001598 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001599 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001600 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001601 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1602 } else {
1603 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001604 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001605}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001606
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001607void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001608 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001609 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001610 AutoValidateClip avc(this);
1611
1612 fDeviceCMDirty = true;
1613 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001614 if (!fAllowSoftClip) {
1615 edgeStyle = kHard_ClipEdgeStyle;
1616 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001617
reed687fa1c2015-04-07 08:00:56 -07001618 fClipStack->clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001619
senorblancoafc7cce2016-02-02 18:44:15 -08001620 fMCRec->fRasterClip.op(transformedRRect, this->getTopLayerBounds(), op,
robertphillips125f19a2015-11-23 09:00:05 -08001621 kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001622 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001623 }
1624
1625 SkPath path;
1626 path.addRRect(rrect);
1627 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001628 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001629}
1630
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001631void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001632 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001633 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001634
1635 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1636 SkRect r;
1637 if (path.isRect(&r)) {
1638 this->onClipRect(r, op, edgeStyle);
1639 return;
1640 }
1641 SkRRect rrect;
1642 if (path.isOval(&r)) {
1643 rrect.setOval(r);
1644 this->onClipRRect(rrect, op, edgeStyle);
1645 return;
1646 }
1647 if (path.isRRect(&rrect)) {
1648 this->onClipRRect(rrect, op, edgeStyle);
1649 return;
1650 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001651 }
robertphillips39f05382015-11-24 09:30:12 -08001652
1653 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001654}
1655
1656void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001657#ifdef SK_ENABLE_CLIP_QUICKREJECT
1658 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001659 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001660 return false;
1661 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001662
reed@google.com3b3e8952012-08-16 20:53:31 +00001663 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001664 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001665 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001666
reed687fa1c2015-04-07 08:00:56 -07001667 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001668 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001669 }
1670 }
1671#endif
1672
reed@google.com5c3d1472011-02-22 19:12:23 +00001673 AutoValidateClip avc(this);
1674
reed@android.com8a1c16f2008-12-17 15:59:43 +00001675 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001676 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001677 if (!fAllowSoftClip) {
1678 edgeStyle = kHard_ClipEdgeStyle;
1679 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001680
1681 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001682 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001683
reed@google.comfe701122011-11-08 19:41:23 +00001684 // Check if the transfomation, or the original path itself
1685 // made us empty. Note this can also happen if we contained NaN
1686 // values. computing the bounds detects this, and will set our
1687 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1688 if (devPath.getBounds().isEmpty()) {
1689 // resetting the path will remove any NaN or other wanky values
1690 // that might upset our scan converter.
1691 devPath.reset();
1692 }
1693
reed@google.com5c3d1472011-02-22 19:12:23 +00001694 // if we called path.swap() we could avoid a deep copy of this path
reed687fa1c2015-04-07 08:00:56 -07001695 fClipStack->clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001696
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001697 if (fAllowSimplifyClip) {
fmalita1a481fe2015-02-04 07:39:34 -08001698 bool clipIsAA = getClipStack()->asPath(&devPath);
1699 if (clipIsAA) {
1700 edgeStyle = kSoft_ClipEdgeStyle;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001701 }
fmalita1a481fe2015-02-04 07:39:34 -08001702
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001703 op = SkRegion::kReplace_Op;
1704 }
1705
senorblancoafc7cce2016-02-02 18:44:15 -08001706 fMCRec->fRasterClip.op(devPath, this->getTopLayerBounds(), op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001707}
1708
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001709void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed2ff1fce2014-12-11 07:07:37 -08001710 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001711 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001712}
1713
1714void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001715 AutoValidateClip avc(this);
1716
reed@android.com8a1c16f2008-12-17 15:59:43 +00001717 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001718 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001719
reed@google.com5c3d1472011-02-22 19:12:23 +00001720 // todo: signal fClipStack that we have a region, and therefore (I guess)
1721 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001722 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001723
reed1f836ee2014-07-07 07:49:34 -07001724 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001725}
1726
reed@google.com819c9212011-02-23 18:56:55 +00001727#ifdef SK_DEBUG
1728void SkCanvas::validateClip() const {
1729 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001730 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001731 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001732 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001733 return;
1734 }
1735
reed@google.com819c9212011-02-23 18:56:55 +00001736 SkIRect ir;
1737 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001738 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001739
reed687fa1c2015-04-07 08:00:56 -07001740 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001741 const SkClipStack::Element* element;
halcanary96fcdcc2015-08-27 07:41:13 -07001742 while ((element = iter.next()) != nullptr) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001743 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001744 case SkClipStack::Element::kRect_Type:
1745 element->getRect().round(&ir);
1746 tmpClip.op(ir, element->getOp());
1747 break;
1748 case SkClipStack::Element::kEmpty_Type:
1749 tmpClip.setEmpty();
1750 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001751 default: {
1752 SkPath path;
1753 element->asPath(&path);
senorblancoafc7cce2016-02-02 18:44:15 -08001754 tmpClip.op(path, this->getTopLayerBounds(), element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001755 break;
1756 }
reed@google.com819c9212011-02-23 18:56:55 +00001757 }
1758 }
reed@google.com819c9212011-02-23 18:56:55 +00001759}
1760#endif
1761
reed@google.com90c07ea2012-04-13 13:50:27 +00001762void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001763 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001764 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001765
halcanary96fcdcc2015-08-27 07:41:13 -07001766 while ((element = iter.next()) != nullptr) {
fmalitac3b589a2014-06-05 12:40:07 -07001767 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001768 }
1769}
1770
reed@google.com5c3d1472011-02-22 19:12:23 +00001771///////////////////////////////////////////////////////////////////////////////
1772
reed@google.com754de5f2014-02-24 19:38:20 +00001773bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001774 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001775}
1776
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001777bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001778 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001779}
1780
reed@google.com3b3e8952012-08-16 20:53:31 +00001781bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001782 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001783 return true;
1784
reed1f836ee2014-07-07 07:49:34 -07001785 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001786 return true;
1787 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001788
reed1f836ee2014-07-07 07:49:34 -07001789 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001790 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001791 fMCRec->fMatrix.mapRect(&dst, rect);
reedb07a94f2014-11-19 05:03:18 -08001792 return !SkIRect::Intersects(dst.roundOut(), fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001793 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001794 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001795
reed@android.coma380ae42009-07-21 01:17:02 +00001796 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001797 // TODO: should we use | instead, or compare all 4 at once?
1798 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001799 return true;
1800 }
reed@google.comc0784db2013-12-13 21:16:12 +00001801 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001802 return true;
1803 }
1804 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001805 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001806}
1807
reed@google.com3b3e8952012-08-16 20:53:31 +00001808bool SkCanvas::quickReject(const SkPath& path) const {
1809 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001810}
1811
reed@google.com3b3e8952012-08-16 20:53:31 +00001812bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001813 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001814 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001815 return false;
1816 }
1817
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001818 SkMatrix inverse;
1819 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001820 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001821 if (bounds) {
1822 bounds->setEmpty();
1823 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001824 return false;
1825 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001826
bsalomon49f085d2014-09-05 13:34:00 -07001827 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001828 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001829 // adjust it outwards in case we are antialiasing
1830 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001831
reed@google.com8f4d2302013-12-17 16:44:46 +00001832 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1833 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001834 inverse.mapRect(bounds, r);
1835 }
1836 return true;
1837}
1838
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001839bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001840 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001841 if (clip.isEmpty()) {
1842 if (bounds) {
1843 bounds->setEmpty();
1844 }
1845 return false;
1846 }
1847
bsalomon49f085d2014-09-05 13:34:00 -07001848 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001849 *bounds = clip.getBounds();
1850 }
1851 return true;
1852}
1853
reed@android.com8a1c16f2008-12-17 15:59:43 +00001854const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001855 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001856}
1857
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001858const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001859 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001860}
1861
robertphillips175dd9b2016-04-28 14:32:04 -07001862GrDrawContext* SkCanvas::internal_private_accessTopLayerDrawContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001863 SkBaseDevice* dev = this->getTopDevice();
robertphillips175dd9b2016-04-28 14:32:04 -07001864 return dev ? dev->accessDrawContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001865}
1866
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001867GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001868 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001869 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001870}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001871
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001872void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1873 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001874 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001875 if (outer.isEmpty()) {
1876 return;
1877 }
1878 if (inner.isEmpty()) {
1879 this->drawRRect(outer, paint);
1880 return;
1881 }
1882
1883 // We don't have this method (yet), but technically this is what we should
1884 // be able to assert...
1885 // SkASSERT(outer.contains(inner));
1886 //
1887 // For now at least check for containment of bounds
1888 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1889
1890 this->onDrawDRRect(outer, inner, paint);
1891}
1892
reed41af9662015-01-05 07:49:08 -08001893// These need to stop being virtual -- clients need to override the onDraw... versions
1894
1895void SkCanvas::drawPaint(const SkPaint& paint) {
1896 this->onDrawPaint(paint);
1897}
1898
1899void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1900 this->onDrawRect(r, paint);
1901}
1902
1903void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1904 this->onDrawOval(r, paint);
1905}
1906
1907void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1908 this->onDrawRRect(rrect, paint);
1909}
1910
1911void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1912 this->onDrawPoints(mode, count, pts, paint);
1913}
1914
1915void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
1916 const SkPoint texs[], const SkColor colors[], SkXfermode* xmode,
1917 const uint16_t indices[], int indexCount, const SkPaint& paint) {
1918 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
1919 indices, indexCount, paint);
1920}
1921
1922void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1923 this->onDrawPath(path, paint);
1924}
1925
reeda85d4d02015-05-06 12:56:48 -07001926void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001927 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001928 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001929}
1930
reede47829b2015-08-06 10:02:53 -07001931void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1932 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001933 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001934 if (dst.isEmpty() || src.isEmpty()) {
1935 return;
1936 }
1937 this->onDrawImageRect(image, &src, dst, paint, constraint);
1938}
reed41af9662015-01-05 07:49:08 -08001939
reed84984ef2015-07-17 07:09:43 -07001940void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1941 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001942 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001943 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001944}
1945
reede47829b2015-08-06 10:02:53 -07001946void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
1947 SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001948 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001949 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
1950 constraint);
1951}
reede47829b2015-08-06 10:02:53 -07001952
reed4c21dc52015-06-25 12:32:03 -07001953void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1954 const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001955 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07001956 if (dst.isEmpty()) {
1957 return;
1958 }
1959 if (!SkNinePatchIter::Valid(image->width(), image->height(), center)) {
reede47829b2015-08-06 10:02:53 -07001960 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001961 }
1962 this->onDrawImageNine(image, center, dst, paint);
1963}
1964
reed41af9662015-01-05 07:49:08 -08001965void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001966 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001967 return;
1968 }
reed41af9662015-01-05 07:49:08 -08001969 this->onDrawBitmap(bitmap, dx, dy, paint);
1970}
1971
reede47829b2015-08-06 10:02:53 -07001972void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07001973 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001974 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07001975 return;
1976 }
reede47829b2015-08-06 10:02:53 -07001977 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08001978}
1979
reed84984ef2015-07-17 07:09:43 -07001980void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
1981 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001982 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001983}
1984
reede47829b2015-08-06 10:02:53 -07001985void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
1986 SrcRectConstraint constraint) {
1987 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
1988 constraint);
1989}
reede47829b2015-08-06 10:02:53 -07001990
reed41af9662015-01-05 07:49:08 -08001991void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
1992 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001993 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001994 return;
1995 }
reed4c21dc52015-06-25 12:32:03 -07001996 if (!SkNinePatchIter::Valid(bitmap.width(), bitmap.height(), center)) {
reeda5517e22015-07-14 10:54:12 -07001997 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001998 }
reed41af9662015-01-05 07:49:08 -08001999 this->onDrawBitmapNine(bitmap, center, dst, paint);
2000}
2001
reed71c3c762015-06-24 10:29:17 -07002002void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2003 const SkColor colors[], int count, SkXfermode::Mode mode,
2004 const SkRect* cull, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002005 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002006 if (count <= 0) {
2007 return;
2008 }
2009 SkASSERT(atlas);
2010 SkASSERT(xform);
2011 SkASSERT(tex);
2012 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
2013}
2014
reedf70b5312016-03-04 16:36:20 -08002015void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2016 if (key) {
2017 this->onDrawAnnotation(rect, key, value);
2018 }
2019}
2020
reede47829b2015-08-06 10:02:53 -07002021void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2022 const SkPaint* paint, SrcRectConstraint constraint) {
2023 if (src) {
2024 this->drawImageRect(image, *src, dst, paint, constraint);
2025 } else {
2026 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2027 dst, paint, constraint);
2028 }
2029}
2030void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2031 const SkPaint* paint, SrcRectConstraint constraint) {
2032 if (src) {
2033 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2034 } else {
2035 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2036 dst, paint, constraint);
2037 }
2038}
2039
tomhudsoncb3bd182016-05-18 07:24:16 -07002040void SkCanvas::temporary_internal_describeTopLayer(SkMatrix* matrix, SkIRect* clip_bounds) {
2041 SkIRect layer_bounds = this->getTopLayerBounds();
2042 if (matrix) {
2043 *matrix = this->getTotalMatrix();
2044 matrix->preTranslate(-layer_bounds.left(), -layer_bounds.top());
2045 }
2046 if (clip_bounds) {
2047 this->getClipDeviceBounds(clip_bounds);
2048 clip_bounds->offset(-layer_bounds.left(), -layer_bounds.top());
2049 }
2050}
2051
reed@android.com8a1c16f2008-12-17 15:59:43 +00002052//////////////////////////////////////////////////////////////////////////////
2053// These are the virtual drawing methods
2054//////////////////////////////////////////////////////////////////////////////
2055
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002056void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002057 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002058 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2059 }
2060}
2061
reed41af9662015-01-05 07:49:08 -08002062void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002063 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002064 this->internalDrawPaint(paint);
2065}
2066
2067void SkCanvas::internalDrawPaint(const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002068 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002069
2070 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002071 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002072 }
2073
reed@google.com4e2b3d32011-04-07 14:18:59 +00002074 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002075}
2076
reed41af9662015-01-05 07:49:08 -08002077void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2078 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002079 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002080 if ((long)count <= 0) {
2081 return;
2082 }
2083
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002084 SkRect r, storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002085 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002086 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002087 // special-case 2 points (common for drawing a single line)
2088 if (2 == count) {
2089 r.set(pts[0], pts[1]);
2090 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00002091 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002092 }
senorblanco87e066e2015-10-28 11:23:36 -07002093 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2094 return;
2095 }
2096 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002097 }
reed@google.coma584aed2012-05-16 14:06:02 +00002098
halcanary96fcdcc2015-08-27 07:41:13 -07002099 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002100
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002101 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002102
reed@android.com8a1c16f2008-12-17 15:59:43 +00002103 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002104 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002105 }
reed@google.com4b226022011-01-11 18:32:13 +00002106
reed@google.com4e2b3d32011-04-07 14:18:59 +00002107 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002108}
2109
reed41af9662015-01-05 07:49:08 -08002110void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002111 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002112 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002113 const SkRect* bounds = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002114 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08002115 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
2116 // To prevent accidental rejecting at this stage, we have to sort it before we check.
2117 SkRect tmp(r);
2118 tmp.sort();
2119
senorblanco87e066e2015-10-28 11:23:36 -07002120 if (this->quickReject(paint.computeFastBounds(tmp, &storage))) {
2121 return;
2122 }
2123 bounds = &r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002124 }
reed@google.com4b226022011-01-11 18:32:13 +00002125
reedc83a2972015-07-16 07:40:45 -07002126 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002127
2128 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002129 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002130 }
2131
reed@google.com4e2b3d32011-04-07 14:18:59 +00002132 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002133}
2134
reed41af9662015-01-05 07:49:08 -08002135void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002136 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002137 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002138 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002139 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002140 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2141 return;
2142 }
2143 bounds = &oval;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002144 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002145
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002146 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002147
2148 while (iter.next()) {
2149 iter.fDevice->drawOval(iter, oval, looper.paint());
2150 }
2151
2152 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002153}
2154
reed41af9662015-01-05 07:49:08 -08002155void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002156 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002157 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002158 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002159 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002160 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2161 return;
2162 }
2163 bounds = &rrect.getBounds();
reed@google.com4ed0fb72012-12-12 20:48:18 +00002164 }
2165
2166 if (rrect.isRect()) {
2167 // call the non-virtual version
2168 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002169 return;
2170 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002171 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002172 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2173 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002174 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002175
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002176 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002177
2178 while (iter.next()) {
2179 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2180 }
2181
2182 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002183}
2184
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002185void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2186 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002187 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002188 const SkRect* bounds = nullptr;
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002189 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002190 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2191 return;
2192 }
2193 bounds = &outer.getBounds();
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002194 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002195
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002196 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002197
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002198 while (iter.next()) {
2199 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2200 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002201
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002202 LOOPER_END
2203}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002204
reed41af9662015-01-05 07:49:08 -08002205void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002206 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002207 if (!path.isFinite()) {
2208 return;
2209 }
2210
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002211 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002212 const SkRect* bounds = nullptr;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002213 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002214 const SkRect& pathBounds = path.getBounds();
senorblanco87e066e2015-10-28 11:23:36 -07002215 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2216 return;
2217 }
2218 bounds = &pathBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002219 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002220
2221 const SkRect& r = path.getBounds();
2222 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002223 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002224 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002225 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002226 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002227 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002228
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002229 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002230
2231 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002232 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002233 }
2234
reed@google.com4e2b3d32011-04-07 14:18:59 +00002235 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002236}
2237
reed262a71b2015-12-05 13:07:27 -08002238bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002239 if (!paint.getImageFilter()) {
2240 return false;
2241 }
2242
2243 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002244 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002245 return false;
2246 }
2247
2248 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2249 // Once we can filter and the filter will return a result larger than itself, we should be
2250 // able to remove this constraint.
2251 // skbug.com/4526
2252 //
2253 SkPoint pt;
2254 ctm.mapXY(x, y, &pt);
2255 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2256 return ir.contains(fMCRec->fRasterClip.getBounds());
2257}
2258
reeda85d4d02015-05-06 12:56:48 -07002259void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002260 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002261 SkRect bounds = SkRect::MakeXYWH(x, y,
2262 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002263 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002264 SkRect tmp = bounds;
2265 if (paint) {
2266 paint->computeFastBounds(tmp, &tmp);
2267 }
2268 if (this->quickReject(tmp)) {
2269 return;
2270 }
reeda85d4d02015-05-06 12:56:48 -07002271 }
halcanary9d524f22016-03-29 09:03:52 -07002272
reeda85d4d02015-05-06 12:56:48 -07002273 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002274 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002275 paint = lazy.init();
2276 }
reed262a71b2015-12-05 13:07:27 -08002277
reed129ed1c2016-02-22 06:42:31 -08002278 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2279 *paint);
2280 if (drawAsSprite && paint->getImageFilter()) {
2281 SkBitmap bitmap;
2282 if (!as_IB(image)->asBitmapForImageFilters(&bitmap)) {
2283 drawAsSprite = false;
2284 } else{
2285 // Until imagefilters are updated, they cannot handle any src type but N32...
2286 if (bitmap.info().colorType() != kN32_SkColorType || bitmap.info().isSRGB()) {
2287 drawAsSprite = false;
2288 }
2289 }
2290 }
2291
reed262a71b2015-12-05 13:07:27 -08002292 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
2293
reeda85d4d02015-05-06 12:56:48 -07002294 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002295 const SkPaint& pnt = looper.paint();
2296 if (drawAsSprite && pnt.getImageFilter()) {
2297 SkBitmap bitmap;
2298 if (as_IB(image)->asBitmapForImageFilters(&bitmap)) {
2299 SkPoint pt;
2300 iter.fMatrix->mapXY(x, y, &pt);
robertphillips2302de92016-03-24 07:26:32 -07002301 iter.fDevice->drawSpriteWithFilter(iter, bitmap,
2302 SkScalarRoundToInt(pt.fX),
2303 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002304 }
2305 } else {
2306 iter.fDevice->drawImage(iter, image, x, y, pnt);
2307 }
reeda85d4d02015-05-06 12:56:48 -07002308 }
halcanary9d524f22016-03-29 09:03:52 -07002309
reeda85d4d02015-05-06 12:56:48 -07002310 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002311}
2312
reed41af9662015-01-05 07:49:08 -08002313void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002314 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002315 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
halcanary96fcdcc2015-08-27 07:41:13 -07002316 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002317 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002318 if (paint) {
2319 paint->computeFastBounds(dst, &storage);
2320 }
2321 if (this->quickReject(storage)) {
2322 return;
2323 }
reeda85d4d02015-05-06 12:56:48 -07002324 }
2325 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002326 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002327 paint = lazy.init();
2328 }
halcanary9d524f22016-03-29 09:03:52 -07002329
senorblancoc41e7e12015-12-07 12:51:30 -08002330 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002331 image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002332
reeda85d4d02015-05-06 12:56:48 -07002333 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002334 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002335 }
halcanary9d524f22016-03-29 09:03:52 -07002336
reeda85d4d02015-05-06 12:56:48 -07002337 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002338}
2339
reed41af9662015-01-05 07:49:08 -08002340void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002341 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002342 SkDEBUGCODE(bitmap.validate();)
2343
reed33366972015-10-08 09:22:02 -07002344 if (bitmap.drawsNothing()) {
2345 return;
2346 }
2347
2348 SkLazyPaint lazy;
2349 if (nullptr == paint) {
2350 paint = lazy.init();
2351 }
2352
2353 const SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2354
2355 SkRect storage;
2356 const SkRect* bounds = nullptr;
2357 if (paint->canComputeFastBounds()) {
2358 bitmap.getBounds(&storage);
2359 matrix.mapRect(&storage);
senorblanco87e066e2015-10-28 11:23:36 -07002360 SkRect tmp = storage;
2361 if (this->quickReject(paint->computeFastBounds(tmp, &tmp))) {
2362 return;
2363 }
2364 bounds = &storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002365 }
reed@google.com4b226022011-01-11 18:32:13 +00002366
reed129ed1c2016-02-22 06:42:31 -08002367 bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(), bitmap.height(),
2368 *paint);
2369 if (drawAsSprite && paint->getImageFilter()) {
2370 // Until imagefilters are updated, they cannot handle any src type but N32...
2371 if (bitmap.info().colorType() != kN32_SkColorType || bitmap.info().isSRGB()) {
2372 drawAsSprite = false;
2373 }
2374 }
2375
reed262a71b2015-12-05 13:07:27 -08002376 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds)
reed33366972015-10-08 09:22:02 -07002377
2378 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002379 const SkPaint& pnt = looper.paint();
2380 if (drawAsSprite && pnt.getImageFilter()) {
2381 SkPoint pt;
2382 iter.fMatrix->mapXY(x, y, &pt);
robertphillips2302de92016-03-24 07:26:32 -07002383 iter.fDevice->drawSpriteWithFilter(iter, bitmap,
2384 SkScalarRoundToInt(pt.fX),
2385 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002386 } else {
2387 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
2388 }
reed33366972015-10-08 09:22:02 -07002389 }
reed262a71b2015-12-05 13:07:27 -08002390
reed33366972015-10-08 09:22:02 -07002391 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002392}
2393
reed@google.com9987ec32011-09-07 11:57:52 +00002394// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002395void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002396 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002397 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002398 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002399 return;
2400 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002401
halcanary96fcdcc2015-08-27 07:41:13 -07002402 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002403 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002404 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2405 return;
2406 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002407 }
reed@google.com3d608122011-11-21 15:16:16 +00002408
reed@google.com33535f32012-09-25 15:37:50 +00002409 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002410 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002411 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002412 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002413
senorblancoc41e7e12015-12-07 12:51:30 -08002414 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002415 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002416
reed@google.com33535f32012-09-25 15:37:50 +00002417 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002418 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002419 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002420
reed@google.com33535f32012-09-25 15:37:50 +00002421 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002422}
2423
reed41af9662015-01-05 07:49:08 -08002424void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002425 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002426 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002427 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002428 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002429}
2430
reed4c21dc52015-06-25 12:32:03 -07002431void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2432 const SkPaint* paint) {
2433 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
halcanary9d524f22016-03-29 09:03:52 -07002434
halcanary96fcdcc2015-08-27 07:41:13 -07002435 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002436 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002437 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2438 return;
2439 }
reed@google.com3d608122011-11-21 15:16:16 +00002440 }
halcanary9d524f22016-03-29 09:03:52 -07002441
reed4c21dc52015-06-25 12:32:03 -07002442 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002443 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002444 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002445 }
halcanary9d524f22016-03-29 09:03:52 -07002446
senorblancoc41e7e12015-12-07 12:51:30 -08002447 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002448
reed4c21dc52015-06-25 12:32:03 -07002449 while (iter.next()) {
2450 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002451 }
halcanary9d524f22016-03-29 09:03:52 -07002452
reed4c21dc52015-06-25 12:32:03 -07002453 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002454}
2455
reed41af9662015-01-05 07:49:08 -08002456void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2457 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002458 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002459 SkDEBUGCODE(bitmap.validate();)
2460
halcanary96fcdcc2015-08-27 07:41:13 -07002461 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002462 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002463 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2464 return;
2465 }
reed4c21dc52015-06-25 12:32:03 -07002466 }
halcanary9d524f22016-03-29 09:03:52 -07002467
reed4c21dc52015-06-25 12:32:03 -07002468 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002469 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002470 paint = lazy.init();
2471 }
halcanary9d524f22016-03-29 09:03:52 -07002472
senorblancoc41e7e12015-12-07 12:51:30 -08002473 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002474
reed4c21dc52015-06-25 12:32:03 -07002475 while (iter.next()) {
2476 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2477 }
halcanary9d524f22016-03-29 09:03:52 -07002478
reed4c21dc52015-06-25 12:32:03 -07002479 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002480}
2481
reed@google.comf67e4cf2011-03-15 20:56:58 +00002482class SkDeviceFilteredPaint {
2483public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002484 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002485 uint32_t filteredFlags = device->filterTextFlags(paint);
2486 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002487 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002488 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002489 fPaint = newPaint;
2490 } else {
2491 fPaint = &paint;
2492 }
2493 }
2494
reed@google.comf67e4cf2011-03-15 20:56:58 +00002495 const SkPaint& paint() const { return *fPaint; }
2496
2497private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002498 const SkPaint* fPaint;
2499 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002500};
2501
bungeman@google.com52c748b2011-08-22 21:30:43 +00002502void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2503 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002504 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002505 draw.fDevice->drawRect(draw, r, paint);
2506 } else {
2507 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002508 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002509 draw.fDevice->drawRect(draw, r, p);
2510 }
2511}
2512
2513void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2514 const char text[], size_t byteLength,
2515 SkScalar x, SkScalar y) {
halcanary96fcdcc2015-08-27 07:41:13 -07002516 SkASSERT(byteLength == 0 || text != nullptr);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002517
2518 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07002519 if (text == nullptr || byteLength == 0 ||
reed1e7f5e72016-04-27 07:49:17 -07002520 draw.fRC->isEmpty() ||
halcanary96fcdcc2015-08-27 07:41:13 -07002521 (paint.getAlpha() == 0 && paint.getXfermode() == nullptr)) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002522 return;
2523 }
2524
2525 SkScalar width = 0;
2526 SkPoint start;
2527
2528 start.set(0, 0); // to avoid warning
2529 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2530 SkPaint::kStrikeThruText_Flag)) {
2531 width = paint.measureText(text, byteLength);
2532
2533 SkScalar offsetX = 0;
2534 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2535 offsetX = SkScalarHalf(width);
2536 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2537 offsetX = width;
2538 }
2539 start.set(x - offsetX, y);
2540 }
2541
2542 if (0 == width) {
2543 return;
2544 }
2545
2546 uint32_t flags = paint.getFlags();
2547
2548 if (flags & (SkPaint::kUnderlineText_Flag |
2549 SkPaint::kStrikeThruText_Flag)) {
2550 SkScalar textSize = paint.getTextSize();
2551 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2552 SkRect r;
2553
2554 r.fLeft = start.fX;
2555 r.fRight = start.fX + width;
2556
2557 if (flags & SkPaint::kUnderlineText_Flag) {
2558 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2559 start.fY);
2560 r.fTop = offset;
2561 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002562 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002563 }
2564 if (flags & SkPaint::kStrikeThruText_Flag) {
2565 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2566 start.fY);
2567 r.fTop = offset;
2568 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002569 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002570 }
2571 }
2572}
2573
reed@google.come0d9ce82014-04-23 04:00:17 +00002574void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2575 const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002576 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002577
2578 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002579 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002580 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002581 DrawTextDecorations(iter, dfp.paint(),
2582 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002583 }
2584
reed@google.com4e2b3d32011-04-07 14:18:59 +00002585 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002586}
2587
reed@google.come0d9ce82014-04-23 04:00:17 +00002588void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2589 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002590 SkPoint textOffset = SkPoint::Make(0, 0);
2591
halcanary96fcdcc2015-08-27 07:41:13 -07002592 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002593
reed@android.com8a1c16f2008-12-17 15:59:43 +00002594 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002595 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002596 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002597 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002598 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002599
reed@google.com4e2b3d32011-04-07 14:18:59 +00002600 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002601}
2602
reed@google.come0d9ce82014-04-23 04:00:17 +00002603void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2604 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002605
2606 SkPoint textOffset = SkPoint::Make(0, constY);
2607
halcanary96fcdcc2015-08-27 07:41:13 -07002608 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002609
reed@android.com8a1c16f2008-12-17 15:59:43 +00002610 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002611 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002612 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002613 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002614 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002615
reed@google.com4e2b3d32011-04-07 14:18:59 +00002616 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002617}
2618
reed@google.come0d9ce82014-04-23 04:00:17 +00002619void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2620 const SkMatrix* matrix, const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002621 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002622
reed@android.com8a1c16f2008-12-17 15:59:43 +00002623 while (iter.next()) {
2624 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002625 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002626 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002627
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002628 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002629}
2630
fmalita00d5c2c2014-08-21 08:53:26 -07002631void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2632 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002633
fmalita85d5eb92015-03-04 11:20:12 -08002634 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002635 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002636 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002637 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002638 SkRect tmp;
2639 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2640 return;
2641 }
2642 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002643 }
2644
fmalita024f9962015-03-03 19:08:17 -08002645 // We cannot filter in the looper as we normally do, because the paint is
2646 // incomplete at this point (text-related attributes are embedded within blob run paints).
2647 SkDrawFilter* drawFilter = fMCRec->fFilter;
halcanary96fcdcc2015-08-27 07:41:13 -07002648 fMCRec->fFilter = nullptr;
fmalita024f9962015-03-03 19:08:17 -08002649
fmalita85d5eb92015-03-04 11:20:12 -08002650 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002651
fmalitaaa1b9122014-08-28 14:32:24 -07002652 while (iter.next()) {
2653 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002654 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002655 }
2656
fmalitaaa1b9122014-08-28 14:32:24 -07002657 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002658
2659 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002660}
2661
reed@google.come0d9ce82014-04-23 04:00:17 +00002662// These will become non-virtual, so they always call the (virtual) onDraw... method
2663void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2664 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002665 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002666 this->onDrawText(text, byteLength, x, y, paint);
2667}
2668void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2669 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002670 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002671 this->onDrawPosText(text, byteLength, pos, paint);
2672}
2673void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2674 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002675 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002676 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2677}
2678void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2679 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002680 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002681 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2682}
fmalita00d5c2c2014-08-21 08:53:26 -07002683void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2684 const SkPaint& paint) {
reede3b38ce2016-01-08 09:18:44 -08002685 RETURN_ON_NULL(blob);
danakj9881d632014-11-26 12:41:06 -08002686 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
reede3b38ce2016-01-08 09:18:44 -08002687 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002688}
reed@google.come0d9ce82014-04-23 04:00:17 +00002689
reed41af9662015-01-05 07:49:08 -08002690void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2691 const SkPoint verts[], const SkPoint texs[],
2692 const SkColor colors[], SkXfermode* xmode,
2693 const uint16_t indices[], int indexCount,
2694 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002695 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
halcanary96fcdcc2015-08-27 07:41:13 -07002696 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
reed@google.com4b226022011-01-11 18:32:13 +00002697
reed@android.com8a1c16f2008-12-17 15:59:43 +00002698 while (iter.next()) {
2699 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002700 colors, xmode, indices, indexCount,
2701 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002702 }
reed@google.com4b226022011-01-11 18:32:13 +00002703
reed@google.com4e2b3d32011-04-07 14:18:59 +00002704 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002705}
2706
dandovb3c9d1c2014-08-12 08:34:29 -07002707void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2708 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002709 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
halcanary96fcdcc2015-08-27 07:41:13 -07002710 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002711 return;
2712 }
mtklein6cfa73a2014-08-13 13:33:49 -07002713
dandovecfff212014-08-04 10:02:00 -07002714 // Since a patch is always within the convex hull of the control points, we discard it when its
2715 // bounding rectangle is completely outside the current clip.
2716 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002717 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002718 if (this->quickReject(bounds)) {
2719 return;
2720 }
mtklein6cfa73a2014-08-13 13:33:49 -07002721
dandovb3c9d1c2014-08-12 08:34:29 -07002722 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2723}
2724
2725void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2726 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2727
halcanary96fcdcc2015-08-27 07:41:13 -07002728 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002729
dandovecfff212014-08-04 10:02:00 -07002730 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002731 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002732 }
mtklein6cfa73a2014-08-13 13:33:49 -07002733
dandovecfff212014-08-04 10:02:00 -07002734 LOOPER_END
2735}
2736
reeda8db7282015-07-07 10:22:31 -07002737void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
reede3b38ce2016-01-08 09:18:44 -08002738 RETURN_ON_NULL(dr);
2739 if (x || y) {
2740 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2741 this->onDrawDrawable(dr, &matrix);
2742 } else {
2743 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002744 }
2745}
2746
reeda8db7282015-07-07 10:22:31 -07002747void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reede3b38ce2016-01-08 09:18:44 -08002748 RETURN_ON_NULL(dr);
2749 if (matrix && matrix->isIdentity()) {
2750 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002751 }
reede3b38ce2016-01-08 09:18:44 -08002752 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002753}
2754
2755void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2756 SkRect bounds = dr->getBounds();
2757 if (matrix) {
2758 matrix->mapRect(&bounds);
2759 }
2760 if (this->quickReject(bounds)) {
2761 return;
2762 }
2763 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002764}
2765
reed71c3c762015-06-24 10:29:17 -07002766void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2767 const SkColor colors[], int count, SkXfermode::Mode mode,
2768 const SkRect* cull, const SkPaint* paint) {
2769 if (cull && this->quickReject(*cull)) {
2770 return;
2771 }
2772
2773 SkPaint pnt;
2774 if (paint) {
2775 pnt = *paint;
2776 }
halcanary9d524f22016-03-29 09:03:52 -07002777
halcanary96fcdcc2015-08-27 07:41:13 -07002778 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr)
reed71c3c762015-06-24 10:29:17 -07002779 while (iter.next()) {
2780 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, mode, pnt);
2781 }
2782 LOOPER_END
2783}
2784
reedf70b5312016-03-04 16:36:20 -08002785void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2786 SkASSERT(key);
2787
2788 SkPaint paint;
2789 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, nullptr)
2790 while (iter.next()) {
2791 iter.fDevice->drawAnnotation(iter, rect, key, value);
2792 }
2793 LOOPER_END
2794}
2795
reed@android.com8a1c16f2008-12-17 15:59:43 +00002796//////////////////////////////////////////////////////////////////////////////
2797// These methods are NOT virtual, and therefore must call back into virtual
2798// methods, rather than actually drawing themselves.
2799//////////////////////////////////////////////////////////////////////////////
2800
2801void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002802 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002803 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002804 SkPaint paint;
2805
2806 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002807 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002808 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002809 }
2810 this->drawPaint(paint);
2811}
2812
reed@android.com845fdac2009-06-23 03:01:32 +00002813void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002814 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002815 SkPaint paint;
2816
2817 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002818 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002819 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002820 }
2821 this->drawPaint(paint);
2822}
2823
2824void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002825 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002826 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002827
reed@android.com8a1c16f2008-12-17 15:59:43 +00002828 pt.set(x, y);
2829 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2830}
2831
2832void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002833 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002834 SkPoint pt;
2835 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002836
reed@android.com8a1c16f2008-12-17 15:59:43 +00002837 pt.set(x, y);
2838 paint.setColor(color);
2839 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2840}
2841
2842void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2843 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002844 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002845 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002846
reed@android.com8a1c16f2008-12-17 15:59:43 +00002847 pts[0].set(x0, y0);
2848 pts[1].set(x1, y1);
2849 this->drawPoints(kLines_PointMode, 2, pts, paint);
2850}
2851
2852void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2853 SkScalar right, SkScalar bottom,
2854 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002855 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002856 SkRect r;
2857
2858 r.set(left, top, right, bottom);
2859 this->drawRect(r, paint);
2860}
2861
2862void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2863 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002864 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002865 if (radius < 0) {
2866 radius = 0;
2867 }
2868
2869 SkRect r;
2870 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002871 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002872}
2873
2874void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2875 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002876 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002877 if (rx > 0 && ry > 0) {
2878 if (paint.canComputeFastBounds()) {
2879 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002880 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002881 return;
2882 }
2883 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002884 SkRRect rrect;
2885 rrect.setRectXY(r, rx, ry);
2886 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002887 } else {
2888 this->drawRect(r, paint);
2889 }
2890}
2891
reed@android.com8a1c16f2008-12-17 15:59:43 +00002892void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2893 SkScalar sweepAngle, bool useCenter,
2894 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002895 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002896 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2897 this->drawOval(oval, paint);
2898 } else {
2899 SkPath path;
2900 if (useCenter) {
2901 path.moveTo(oval.centerX(), oval.centerY());
2902 }
2903 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2904 if (useCenter) {
2905 path.close();
2906 }
2907 this->drawPath(path, paint);
2908 }
2909}
2910
2911void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2912 const SkPath& path, SkScalar hOffset,
2913 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002914 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002915 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002916
reed@android.com8a1c16f2008-12-17 15:59:43 +00002917 matrix.setTranslate(hOffset, vOffset);
2918 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2919}
2920
reed@android.comf76bacf2009-05-13 14:00:33 +00002921///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07002922
2923/**
2924 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2925 * against the playback cost of recursing into the subpicture to get at its actual ops.
2926 *
2927 * For now we pick a conservatively small value, though measurement (and other heuristics like
2928 * the type of ops contained) may justify changing this value.
2929 */
2930#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07002931
reedd5fa1a42014-08-09 11:08:05 -07002932void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002933 RETURN_ON_NULL(picture);
2934
reed1c2c4412015-04-30 13:09:24 -07002935 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
reede3b38ce2016-01-08 09:18:44 -08002936 if (matrix && matrix->isIdentity()) {
2937 matrix = nullptr;
2938 }
2939 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2940 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2941 picture->playback(this);
2942 } else {
2943 this->onDrawPicture(picture, matrix, paint);
reedd5fa1a42014-08-09 11:08:05 -07002944 }
2945}
robertphillips9b14f262014-06-04 05:40:44 -07002946
reedd5fa1a42014-08-09 11:08:05 -07002947void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2948 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07002949 if (!paint || paint->canComputeFastBounds()) {
2950 SkRect bounds = picture->cullRect();
2951 if (paint) {
2952 paint->computeFastBounds(bounds, &bounds);
2953 }
2954 if (matrix) {
2955 matrix->mapRect(&bounds);
2956 }
2957 if (this->quickReject(bounds)) {
2958 return;
2959 }
2960 }
2961
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002962 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002963 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002964 // Canvas has to first give the device the opportunity to render
2965 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002966 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002967 return; // the device has rendered the entire picture
2968 }
2969 }
2970
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002971 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07002972 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002973}
2974
reed@android.com8a1c16f2008-12-17 15:59:43 +00002975///////////////////////////////////////////////////////////////////////////////
2976///////////////////////////////////////////////////////////////////////////////
2977
2978SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
bungeman99fe8222015-08-20 07:57:51 -07002979 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002980
2981 SkASSERT(canvas);
2982
2983 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2984 fDone = !fImpl->next();
2985}
2986
2987SkCanvas::LayerIter::~LayerIter() {
2988 fImpl->~SkDrawIter();
2989}
2990
2991void SkCanvas::LayerIter::next() {
2992 fDone = !fImpl->next();
2993}
2994
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002995SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002996 return fImpl->getDevice();
2997}
2998
2999const SkMatrix& SkCanvas::LayerIter::matrix() const {
3000 return fImpl->getMatrix();
3001}
3002
3003const SkPaint& SkCanvas::LayerIter::paint() const {
3004 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003005 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003006 paint = &fDefaultPaint;
3007 }
3008 return *paint;
3009}
3010
reed1e7f5e72016-04-27 07:49:17 -07003011const SkRasterClip& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +00003012int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
3013int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003014
3015///////////////////////////////////////////////////////////////////////////////
3016
fmalitac3b589a2014-06-05 12:40:07 -07003017SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003018
3019///////////////////////////////////////////////////////////////////////////////
3020
3021static bool supported_for_raster_canvas(const SkImageInfo& info) {
3022 switch (info.alphaType()) {
3023 case kPremul_SkAlphaType:
3024 case kOpaque_SkAlphaType:
3025 break;
3026 default:
3027 return false;
3028 }
3029
3030 switch (info.colorType()) {
3031 case kAlpha_8_SkColorType:
3032 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00003033 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003034 break;
3035 default:
3036 return false;
3037 }
3038
3039 return true;
3040}
3041
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003042SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
3043 if (!supported_for_raster_canvas(info)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003044 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003045 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003046
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003047 SkBitmap bitmap;
3048 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003049 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003050 }
halcanary385fe4d2015-08-26 13:07:48 -07003051 return new SkCanvas(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003052}
reedd5fa1a42014-08-09 11:08:05 -07003053
3054///////////////////////////////////////////////////////////////////////////////
3055
3056SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003057 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07003058 : fCanvas(canvas)
3059 , fSaveCount(canvas->getSaveCount())
3060{
bsalomon49f085d2014-09-05 13:34:00 -07003061 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003062 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07003063 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003064 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07003065 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003066 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07003067 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003068 canvas->save();
3069 }
mtklein6cfa73a2014-08-13 13:33:49 -07003070
bsalomon49f085d2014-09-05 13:34:00 -07003071 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003072 canvas->concat(*matrix);
3073 }
3074}
3075
3076SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
3077 fCanvas->restoreToCount(fSaveCount);
3078}
reede8f30622016-03-23 18:59:25 -07003079
3080#ifdef SK_SUPPORT_LEGACY_NEW_SURFACE_API
3081SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
3082 return this->makeSurface(info, props).release();
3083}
3084#endif