blob: 6c4cd272502aca824c05f77ffe762032b85fc0b4 [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"
20#include "SkMatrixUtils.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000021#include "SkMetaData.h"
reed4c21dc52015-06-25 12:32:03 -070022#include "SkNinePatchIter.h"
reedc83a2972015-07-16 07:40:45 -070023#include "SkPaintPriv.h"
dandovb3c9d1c2014-08-12 08:34:29 -070024#include "SkPatchUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000025#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000026#include "SkRasterClip.h"
reed96472de2014-12-10 09:53:42 -080027#include "SkReadPixelsRec.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000028#include "SkRRect.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000029#include "SkSmallAllocator.h"
robertphillips4418dba2016-03-07 12:45:14 -080030#include "SkSpecialImage.h"
reed@google.com97af1a62012-08-28 12:19:02 +000031#include "SkSurface_Base.h"
fmalita7ba7aa72014-08-29 09:46:36 -070032#include "SkTextBlob.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000033#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000034#include "SkTLazy.h"
danakj8f757f52014-11-04 11:48:43 -080035#include "SkTraceEvent.h"
bungemand3ebb482015-08-05 13:57:49 -070036
37#include <new>
reed@android.com8a1c16f2008-12-17 15:59:43 +000038
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000039#if SK_SUPPORT_GPU
robertphillips7354a4b2015-12-16 05:08:27 -080040#include "GrContext.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000041#include "GrRenderTarget.h"
robertphillips7354a4b2015-12-16 05:08:27 -080042#include "SkGr.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000043#endif
44
reede3b38ce2016-01-08 09:18:44 -080045#define RETURN_ON_NULL(ptr) do { if (nullptr == (ptr)) return; } while (0)
46
reedc83a2972015-07-16 07:40:45 -070047/*
48 * Return true if the drawing this rect would hit every pixels in the canvas.
49 *
50 * Returns false if
51 * - rect does not contain the canvas' bounds
52 * - paint is not fill
53 * - paint would blur or otherwise change the coverage of the rect
54 */
55bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
56 ShaderOverrideOpacity overrideOpacity) const {
bungeman99fe8222015-08-20 07:57:51 -070057 static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
58 (int)kNone_ShaderOverrideOpacity,
59 "need_matching_enums0");
60 static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
61 (int)kOpaque_ShaderOverrideOpacity,
62 "need_matching_enums1");
63 static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
64 (int)kNotOpaque_ShaderOverrideOpacity,
65 "need_matching_enums2");
reedc83a2972015-07-16 07:40:45 -070066
67 const SkISize size = this->getBaseLayerSize();
68 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
69 if (!this->getClipStack()->quickContains(bounds)) {
70 return false;
71 }
72
73 if (rect) {
74 if (!this->getTotalMatrix().rectStaysRect()) {
75 return false; // conservative
76 }
77
78 SkRect devRect;
79 this->getTotalMatrix().mapRect(&devRect, *rect);
fmalita8c0144c2015-07-22 05:56:16 -070080 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -070081 return false;
82 }
83 }
84
85 if (paint) {
86 SkPaint::Style paintStyle = paint->getStyle();
87 if (!(paintStyle == SkPaint::kFill_Style ||
88 paintStyle == SkPaint::kStrokeAndFill_Style)) {
89 return false;
90 }
91 if (paint->getMaskFilter() || paint->getLooper()
92 || paint->getPathEffect() || paint->getImageFilter()) {
93 return false; // conservative
94 }
95 }
96 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
97}
98
99///////////////////////////////////////////////////////////////////////////////////////////////////
100
reedd990e2f2014-12-22 11:58:30 -0800101static bool gIgnoreSaveLayerBounds;
102void SkCanvas::Internal_Private_SetIgnoreSaveLayerBounds(bool ignore) {
103 gIgnoreSaveLayerBounds = ignore;
104}
105bool SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds() {
106 return gIgnoreSaveLayerBounds;
107}
108
reed0acf1b42014-12-22 16:12:38 -0800109static bool gTreatSpriteAsBitmap;
110void SkCanvas::Internal_Private_SetTreatSpriteAsBitmap(bool spriteAsBitmap) {
111 gTreatSpriteAsBitmap = spriteAsBitmap;
112}
113bool SkCanvas::Internal_Private_GetTreatSpriteAsBitmap() {
114 return gTreatSpriteAsBitmap;
115}
116
reed@google.comda17f752012-08-16 18:27:05 +0000117// experimental for faster tiled drawing...
118//#define SK_ENABLE_CLIP_QUICKREJECT
reed@android.com8a1c16f2008-12-17 15:59:43 +0000119//#define SK_TRACE_SAVERESTORE
120
121#ifdef SK_TRACE_SAVERESTORE
122 static int gLayerCounter;
123 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
124 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
125
126 static int gRecCounter;
127 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
128 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
129
130 static int gCanvasCounter;
131 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
132 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
133#else
134 #define inc_layer()
135 #define dec_layer()
136 #define inc_rec()
137 #define dec_rec()
138 #define inc_canvas()
139 #define dec_canvas()
140#endif
141
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000142typedef SkTLazy<SkPaint> SkLazyPaint;
143
reedc83a2972015-07-16 07:40:45 -0700144void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000145 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700146 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
147 ? SkSurface::kDiscard_ContentChangeMode
148 : SkSurface::kRetain_ContentChangeMode);
149 }
150}
151
152void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
153 ShaderOverrideOpacity overrideOpacity) {
154 if (fSurfaceBase) {
155 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
156 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
157 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
158 // and therefore we don't care which mode we're in.
159 //
160 if (fSurfaceBase->outstandingImageSnapshot()) {
161 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
162 mode = SkSurface::kDiscard_ContentChangeMode;
163 }
164 }
165 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000166 }
167}
168
reed@android.com8a1c16f2008-12-17 15:59:43 +0000169///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000170
reed4a8126e2014-09-22 07:29:03 -0700171static uint32_t filter_paint_flags(const SkSurfaceProps& props, uint32_t flags) {
172 const uint32_t propFlags = props.flags();
173 if (propFlags & SkSurfaceProps::kDisallowDither_Flag) {
174 flags &= ~SkPaint::kDither_Flag;
175 }
176 if (propFlags & SkSurfaceProps::kDisallowAntiAlias_Flag) {
177 flags &= ~SkPaint::kAntiAlias_Flag;
178 }
179 return flags;
180}
181
182///////////////////////////////////////////////////////////////////////////////
183
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000184/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000185 The clip/matrix/proc are fields that reflect the top of the save/restore
186 stack. Whenever the canvas changes, it marks a dirty flag, and then before
187 these are used (assuming we're not on a layer) we rebuild these cache
188 values: they reflect the top of the save stack, but translated and clipped
189 by the device's XY offset and bitmap-bounds.
190*/
191struct DeviceCM {
192 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000193 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000194 SkRasterClip fClip;
reed@google.com6f8f2922011-03-04 22:27:10 +0000195 SkPaint* fPaint; // may be null (in the future)
reed61f501f2015-04-29 08:34:00 -0700196 const SkMatrix* fMatrix;
197 SkMatrix fMatrixStorage;
reed8c30a812016-04-20 16:36:51 -0700198 SkMatrix fStashedMatrix; // original CTM; used by imagefilter in saveLayer
reed61f501f2015-04-29 08:34:00 -0700199 const bool fDeviceIsBitmapDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000200
reed96e657d2015-03-10 17:30:07 -0700201 DeviceCM(SkBaseDevice* device, const SkPaint* paint, SkCanvas* canvas,
reed8c30a812016-04-20 16:36:51 -0700202 bool conservativeRasterClip, bool deviceIsBitmapDevice, const SkMatrix& stashed)
halcanary96fcdcc2015-08-27 07:41:13 -0700203 : fNext(nullptr)
reedd9544982014-09-09 18:46:22 -0700204 , fClip(conservativeRasterClip)
reed8c30a812016-04-20 16:36:51 -0700205 , fStashedMatrix(stashed)
reed61f501f2015-04-29 08:34:00 -0700206 , fDeviceIsBitmapDevice(deviceIsBitmapDevice)
reedd9544982014-09-09 18:46:22 -0700207 {
halcanary96fcdcc2015-08-27 07:41:13 -0700208 if (nullptr != device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000209 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000210 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211 }
reed@google.com4b226022011-01-11 18:32:13 +0000212 fDevice = device;
halcanary96fcdcc2015-08-27 07:41:13 -0700213 fPaint = paint ? new SkPaint(*paint) : nullptr;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000214 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000216 ~DeviceCM() {
bsalomon49f085d2014-09-05 13:34:00 -0700217 if (fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000218 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000219 fDevice->unref();
220 }
halcanary385fe4d2015-08-26 13:07:48 -0700221 delete fPaint;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000222 }
reed@google.com4b226022011-01-11 18:32:13 +0000223
mtkleinfeaadee2015-04-08 11:25:48 -0700224 void reset(const SkIRect& bounds) {
225 SkASSERT(!fPaint);
226 SkASSERT(!fNext);
227 SkASSERT(fDevice);
228 fClip.setRect(bounds);
229 }
230
reed@google.com045e62d2011-10-24 12:19:46 +0000231 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
232 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000233 int x = fDevice->getOrigin().x();
234 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000235 int width = fDevice->width();
236 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000237
reed@android.com8a1c16f2008-12-17 15:59:43 +0000238 if ((x | y) == 0) {
239 fMatrix = &totalMatrix;
240 fClip = totalClip;
241 } else {
242 fMatrixStorage = totalMatrix;
243 fMatrixStorage.postTranslate(SkIntToScalar(-x),
244 SkIntToScalar(-y));
245 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000246
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247 totalClip.translate(-x, -y, &fClip);
248 }
249
reed@google.com045e62d2011-10-24 12:19:46 +0000250 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000251
252 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000253
reed@android.com8a1c16f2008-12-17 15:59:43 +0000254 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000255 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000256 SkRegion::kDifference_Op);
257 }
reed@google.com4b226022011-01-11 18:32:13 +0000258
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000259 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
260
reed@android.com8a1c16f2008-12-17 15:59:43 +0000261#ifdef SK_DEBUG
262 if (!fClip.isEmpty()) {
263 SkIRect deviceR;
264 deviceR.set(0, 0, width, height);
265 SkASSERT(deviceR.contains(fClip.getBounds()));
266 }
267#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000268 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269};
270
271/* This is the record we keep for each save/restore level in the stack.
272 Since a level optionally copies the matrix and/or stack, we have pointers
273 for these fields. If the value is copied for this level, the copy is
274 stored in the ...Storage field, and the pointer points to that. If the
275 value is not copied for this level, we ignore ...Storage, and just point
276 at the corresponding value in the previous level in the stack.
277*/
278class SkCanvas::MCRec {
279public:
reed1f836ee2014-07-07 07:49:34 -0700280 SkDrawFilter* fFilter; // the current filter (or null)
reedd9544982014-09-09 18:46:22 -0700281 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000282 /* If there are any layers in the stack, this points to the top-most
283 one that is at or below this level in the stack (so we know what
284 bitmap/device to draw into from this level. This value is NOT
285 reference counted, since the real owner is either our fLayer field,
286 or a previous one in a lower level.)
287 */
reed2ff1fce2014-12-11 07:07:37 -0800288 DeviceCM* fTopLayer;
289 SkRasterClip fRasterClip;
290 SkMatrix fMatrix;
291 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000292
reedd9544982014-09-09 18:46:22 -0700293 MCRec(bool conservativeRasterClip) : fRasterClip(conservativeRasterClip) {
halcanary96fcdcc2015-08-27 07:41:13 -0700294 fFilter = nullptr;
295 fLayer = nullptr;
296 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800297 fMatrix.reset();
298 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700299
reedd9544982014-09-09 18:46:22 -0700300 // don't bother initializing fNext
301 inc_rec();
302 }
reed2ff1fce2014-12-11 07:07:37 -0800303 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
reedd9544982014-09-09 18:46:22 -0700304 fFilter = SkSafeRef(prev.fFilter);
halcanary96fcdcc2015-08-27 07:41:13 -0700305 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700306 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800307 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700308
reed@android.com8a1c16f2008-12-17 15:59:43 +0000309 // don't bother initializing fNext
310 inc_rec();
311 }
312 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000313 SkSafeUnref(fFilter);
halcanary385fe4d2015-08-26 13:07:48 -0700314 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000315 dec_rec();
316 }
mtkleinfeaadee2015-04-08 11:25:48 -0700317
318 void reset(const SkIRect& bounds) {
319 SkASSERT(fLayer);
320 SkASSERT(fDeferredSaveCount == 0);
321
322 fMatrix.reset();
323 fRasterClip.setRect(bounds);
324 fLayer->reset(bounds);
325 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000326};
327
328class SkDrawIter : public SkDraw {
329public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000330 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000331 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000332 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000333 canvas->updateDeviceCMCache();
334
reed687fa1c2015-04-07 08:00:56 -0700335 fClipStack = canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000336 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000337 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000338 }
reed@google.com4b226022011-01-11 18:32:13 +0000339
reed@android.com8a1c16f2008-12-17 15:59:43 +0000340 bool next() {
341 // skip over recs with empty clips
342 if (fSkipEmptyClips) {
343 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
344 fCurrLayer = fCurrLayer->fNext;
345 }
346 }
347
reed@google.comf68c5e22012-02-24 16:38:58 +0000348 const DeviceCM* rec = fCurrLayer;
349 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000350
351 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000352 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000353 fDevice = rec->fDevice;
reed41e010c2015-06-09 12:16:53 -0700354 if (!fDevice->accessPixels(&fDst)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700355 fDst.reset(fDevice->imageInfo(), nullptr, 0);
reed41e010c2015-06-09 12:16:53 -0700356 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000357 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000358 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000359
360 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700361 // fCurrLayer may be nullptr now
reed@android.com199f1082009-06-10 02:12:47 +0000362
reed@android.com8a1c16f2008-12-17 15:59:43 +0000363 return true;
364 }
365 return false;
366 }
reed@google.com4b226022011-01-11 18:32:13 +0000367
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000368 SkBaseDevice* getDevice() const { return fDevice; }
reed1e7f5e72016-04-27 07:49:17 -0700369 const SkRasterClip& getClip() const { return *fRC; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000370 int getX() const { return fDevice->getOrigin().x(); }
371 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000372 const SkMatrix& getMatrix() const { return *fMatrix; }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000373 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000374
reed@android.com8a1c16f2008-12-17 15:59:43 +0000375private:
376 SkCanvas* fCanvas;
377 const DeviceCM* fCurrLayer;
378 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000379 SkBool8 fSkipEmptyClips;
380
381 typedef SkDraw INHERITED;
382};
383
384/////////////////////////////////////////////////////////////////////////////
385
reeddbc3cef2015-04-29 12:18:57 -0700386static SkPaint* set_if_needed(SkLazyPaint* lazy, const SkPaint& orig) {
387 return lazy->isValid() ? lazy->get() : lazy->set(orig);
388}
389
390/**
391 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700392 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700393 */
reedd053ce92016-03-22 10:17:23 -0700394static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700395 SkImageFilter* imgf = paint.getImageFilter();
396 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700397 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700398 }
399
reedd053ce92016-03-22 10:17:23 -0700400 SkColorFilter* imgCFPtr;
401 if (!imgf->asAColorFilter(&imgCFPtr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700402 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700403 }
reedd053ce92016-03-22 10:17:23 -0700404 sk_sp<SkColorFilter> imgCF(imgCFPtr);
reeddbc3cef2015-04-29 12:18:57 -0700405
406 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700407 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700408 // there is no existing paint colorfilter, so we can just return the imagefilter's
409 return imgCF;
410 }
411
412 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
413 // and we need to combine them into a single colorfilter.
reedd053ce92016-03-22 10:17:23 -0700414 return SkColorFilter::MakeComposeFilter(std::move(imgCF), sk_ref_sp(paintCF));
reeddbc3cef2015-04-29 12:18:57 -0700415}
416
senorblanco87e066e2015-10-28 11:23:36 -0700417/**
418 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
419 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
420 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
421 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
422 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
423 * conservative "effective" bounds based on the settings in the paint... with one exception. This
424 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
425 * deliberately ignored.
426 */
427static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
428 const SkRect& rawBounds,
429 SkRect* storage) {
430 SkPaint tmpUnfiltered(paint);
431 tmpUnfiltered.setImageFilter(nullptr);
432 if (tmpUnfiltered.canComputeFastBounds()) {
433 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
434 } else {
435 return rawBounds;
436 }
437}
438
reed@android.com8a1c16f2008-12-17 15:59:43 +0000439class AutoDrawLooper {
440public:
senorblanco87e066e2015-10-28 11:23:36 -0700441 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
442 // paint. It's used to determine the size of the offscreen layer for filters.
443 // If null, the clip will be used instead.
reed4a8126e2014-09-22 07:29:03 -0700444 AutoDrawLooper(SkCanvas* canvas, const SkSurfaceProps& props, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000445 bool skipLayerForImageFilter = false,
senorblanco87e066e2015-10-28 11:23:36 -0700446 const SkRect* rawBounds = nullptr) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000447 fCanvas = canvas;
fmalita53d9f1c2016-01-25 06:23:54 -0800448#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000449 fFilter = canvas->getDrawFilter();
fmalita77650002016-01-21 18:47:11 -0800450#else
451 fFilter = nullptr;
452#endif
reed4a8126e2014-09-22 07:29:03 -0700453 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000454 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700455 fTempLayerForImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000456 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000457
reedd053ce92016-03-22 10:17:23 -0700458 auto simplifiedCF = image_to_color_filter(fOrigPaint);
reeddbc3cef2015-04-29 12:18:57 -0700459 if (simplifiedCF) {
460 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reedd053ce92016-03-22 10:17:23 -0700461 paint->setColorFilter(std::move(simplifiedCF));
halcanary96fcdcc2015-08-27 07:41:13 -0700462 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700463 fPaint = paint;
464 }
465
466 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700467 /**
468 * We implement ImageFilters for a given draw by creating a layer, then applying the
469 * imagefilter to the pixels of that layer (its backing surface/image), and then
470 * we call restore() to xfer that layer to the main canvas.
471 *
472 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
473 * 2. Generate the src pixels:
474 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
475 * return (fPaint). We then draw the primitive (using srcover) into a cleared
476 * buffer/surface.
477 * 3. Restore the layer created in #1
478 * The imagefilter is passed the buffer/surface from the layer (now filled with the
479 * src pixels of the primitive). It returns a new "filtered" buffer, which we
480 * draw onto the previous layer using the xfermode from the original paint.
481 */
reed@google.com8926b162012-03-23 15:36:36 +0000482 SkPaint tmp;
reeddbc3cef2015-04-29 12:18:57 -0700483 tmp.setImageFilter(fPaint->getImageFilter());
reedcfb6bdf2016-03-29 11:32:50 -0700484 tmp.setXfermode(sk_ref_sp(fPaint->getXfermode()));
senorblanco87e066e2015-10-28 11:23:36 -0700485 SkRect storage;
486 if (rawBounds) {
487 // Make rawBounds include all paint outsets except for those due to image filters.
488 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
489 }
reedbfd5f172016-01-07 11:28:08 -0800490 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &tmp),
reed76033be2015-03-14 10:54:31 -0700491 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700492 fTempLayerForImageFilter = true;
493 // we remove the imagefilter/xfermode inside doNext()
reed@google.com8926b162012-03-23 15:36:36 +0000494 }
495
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000496 if (SkDrawLooper* looper = paint.getLooper()) {
497 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
498 looper->contextSize());
499 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000500 fIsSimple = false;
501 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700502 fLooperContext = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000503 // can we be marked as simple?
reed5c476fb2015-04-20 08:04:21 -0700504 fIsSimple = !fFilter && !fTempLayerForImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000505 }
piotaixrb5fae932014-09-24 13:03:30 -0700506
reed4a8126e2014-09-22 07:29:03 -0700507 uint32_t oldFlags = paint.getFlags();
508 fNewPaintFlags = filter_paint_flags(props, oldFlags);
509 if (fIsSimple && (fNewPaintFlags != oldFlags)) {
reeddbc3cef2015-04-29 12:18:57 -0700510 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700511 paint->setFlags(fNewPaintFlags);
512 fPaint = paint;
513 // if we're not simple, doNext() will take care of calling setFlags()
514 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000515 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000516
reed@android.com8a1c16f2008-12-17 15:59:43 +0000517 ~AutoDrawLooper() {
reed5c476fb2015-04-20 08:04:21 -0700518 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000519 fCanvas->internalRestore();
520 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000521 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000522 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000523
reed@google.com4e2b3d32011-04-07 14:18:59 +0000524 const SkPaint& paint() const {
525 SkASSERT(fPaint);
526 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000527 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000528
reed@google.com129ec222012-05-15 13:24:09 +0000529 bool next(SkDrawFilter::Type drawType) {
530 if (fDone) {
531 return false;
532 } else if (fIsSimple) {
533 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000534 return !fPaint->nothingToDraw();
535 } else {
536 return this->doNext(drawType);
537 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000538 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000539
reed@android.com8a1c16f2008-12-17 15:59:43 +0000540private:
reeddbc3cef2015-04-29 12:18:57 -0700541 SkLazyPaint fLazyPaintInit; // base paint storage in case we need to modify it
542 SkLazyPaint fLazyPaintPerLooper; // per-draw-looper storage, so the looper can modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000543 SkCanvas* fCanvas;
544 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000545 SkDrawFilter* fFilter;
546 const SkPaint* fPaint;
547 int fSaveCount;
reed4a8126e2014-09-22 07:29:03 -0700548 uint32_t fNewPaintFlags;
reed5c476fb2015-04-20 08:04:21 -0700549 bool fTempLayerForImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000550 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000551 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000552 SkDrawLooper::Context* fLooperContext;
553 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000554
555 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000556};
557
reed@google.com129ec222012-05-15 13:24:09 +0000558bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
halcanary96fcdcc2015-08-27 07:41:13 -0700559 fPaint = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000560 SkASSERT(!fIsSimple);
reed5c476fb2015-04-20 08:04:21 -0700561 SkASSERT(fLooperContext || fFilter || fTempLayerForImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000562
reeddbc3cef2015-04-29 12:18:57 -0700563 SkPaint* paint = fLazyPaintPerLooper.set(fLazyPaintInit.isValid() ?
564 *fLazyPaintInit.get() : fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700565 paint->setFlags(fNewPaintFlags);
reed@google.com129ec222012-05-15 13:24:09 +0000566
reed5c476fb2015-04-20 08:04:21 -0700567 if (fTempLayerForImageFilter) {
halcanary96fcdcc2015-08-27 07:41:13 -0700568 paint->setImageFilter(nullptr);
569 paint->setXfermode(nullptr);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000570 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000571
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000572 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000573 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000574 return false;
575 }
576 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000577 if (!fFilter->filter(paint, drawType)) {
578 fDone = true;
579 return false;
580 }
halcanary96fcdcc2015-08-27 07:41:13 -0700581 if (nullptr == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000582 // no looper means we only draw once
583 fDone = true;
584 }
585 }
586 fPaint = paint;
587
588 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000589 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000590 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000591 }
592
593 // call this after any possible paint modifiers
594 if (fPaint->nothingToDraw()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700595 fPaint = nullptr;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000596 return false;
597 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000598 return true;
599}
600
reed@android.com8a1c16f2008-12-17 15:59:43 +0000601////////// macros to place around the internal draw calls //////////////////
602
reed262a71b2015-12-05 13:07:27 -0800603#define LOOPER_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
604 this->predrawNotify(); \
605 AutoDrawLooper looper(this, fProps, paint, skipLayerForFilter, bounds); \
606 while (looper.next(SkDrawFilter::kBitmap_Type)) { \
607 SkDrawIter iter(this);
608
609
reed@google.com8926b162012-03-23 15:36:36 +0000610#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000611 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700612 AutoDrawLooper looper(this, fProps, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000613 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000614 SkDrawIter iter(this);
615
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000616#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000617 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700618 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000619 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000620 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000621
reedc83a2972015-07-16 07:40:45 -0700622#define LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, type, bounds, auxOpaque) \
623 this->predrawNotify(bounds, &paint, auxOpaque); \
624 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
625 while (looper.next(type)) { \
626 SkDrawIter iter(this);
627
reed@google.com4e2b3d32011-04-07 14:18:59 +0000628#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000629
630////////////////////////////////////////////////////////////////////////////
631
mtkleinfeaadee2015-04-08 11:25:48 -0700632void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
633 this->restoreToCount(1);
634 fCachedLocalClipBounds.setEmpty();
635 fCachedLocalClipBoundsDirty = true;
636 fClipStack->reset();
637 fMCRec->reset(bounds);
638
639 // We're peering through a lot of structs here. Only at this scope do we
640 // know that the device is an SkBitmapDevice (really an SkNoPixelsBitmapDevice).
641 static_cast<SkBitmapDevice*>(fMCRec->fLayer->fDevice)->setNewSize(bounds.size());
642}
643
reedd9544982014-09-09 18:46:22 -0700644SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
reed42b73eb2015-11-20 13:42:42 -0800645 if (device && device->forceConservativeRasterClip()) {
646 flags = InitFlags(flags | kConservativeRasterClip_InitFlag);
647 }
648 // Since init() is only called once by our constructors, it is safe to perform this
649 // const-cast.
650 *const_cast<bool*>(&fConservativeRasterClip) = SkToBool(flags & kConservativeRasterClip_InitFlag);
651
reed@google.comc0784db2013-12-13 21:16:12 +0000652 fCachedLocalClipBounds.setEmpty();
653 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000654 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000655 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700656 fDeviceCMDirty = true;
reed2ff1fce2014-12-11 07:07:37 -0800657 fSaveCount = 1;
halcanary96fcdcc2015-08-27 07:41:13 -0700658 fMetaData = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000659
halcanary385fe4d2015-08-26 13:07:48 -0700660 fClipStack.reset(new SkClipStack);
reed687fa1c2015-04-07 08:00:56 -0700661
reed@android.com8a1c16f2008-12-17 15:59:43 +0000662 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700663 new (fMCRec) MCRec(fConservativeRasterClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000664
reeda499f902015-05-01 09:34:31 -0700665 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
666 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
reed8c30a812016-04-20 16:36:51 -0700667 new (fDeviceCMStorage) DeviceCM(nullptr, nullptr, nullptr, fConservativeRasterClip, false,
668 fMCRec->fMatrix);
reedb679ca82015-04-07 04:40:48 -0700669
reed@android.com8a1c16f2008-12-17 15:59:43 +0000670 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000671
halcanary96fcdcc2015-08-27 07:41:13 -0700672 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000673
reedf92c8662014-08-18 08:02:43 -0700674 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700675 // The root device and the canvas should always have the same pixel geometry
676 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reedf92c8662014-08-18 08:02:43 -0700677 device->onAttachToCanvas(this);
678 fMCRec->fLayer->fDevice = SkRef(device);
reed78e27682014-11-19 08:04:34 -0800679 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
reedf92c8662014-08-18 08:02:43 -0700680 }
681 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000682}
683
reed@google.comcde92112011-07-06 20:00:52 +0000684SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000685 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700686 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800687 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000688{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000689 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000690
halcanary96fcdcc2015-08-27 07:41:13 -0700691 this->init(nullptr, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000692}
693
reedd9544982014-09-09 18:46:22 -0700694static SkBitmap make_nopixels(int width, int height) {
695 SkBitmap bitmap;
696 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
697 return bitmap;
698}
699
700class SkNoPixelsBitmapDevice : public SkBitmapDevice {
701public:
robertphillipsfcf78292015-06-19 11:49:52 -0700702 SkNoPixelsBitmapDevice(const SkIRect& bounds, const SkSurfaceProps& surfaceProps)
703 : INHERITED(make_nopixels(bounds.width(), bounds.height()), surfaceProps)
reed78e27682014-11-19 08:04:34 -0800704 {
705 this->setOrigin(bounds.x(), bounds.y());
706 }
reedd9544982014-09-09 18:46:22 -0700707
708private:
piotaixrb5fae932014-09-24 13:03:30 -0700709
reedd9544982014-09-09 18:46:22 -0700710 typedef SkBitmapDevice INHERITED;
711};
712
reed96a857e2015-01-25 10:33:58 -0800713SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000714 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800715 , fProps(SkSurfacePropsCopyOrDefault(props))
reed42b73eb2015-11-20 13:42:42 -0800716 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000717{
718 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700719
halcanary385fe4d2015-08-26 13:07:48 -0700720 this->init(new SkNoPixelsBitmapDevice(SkIRect::MakeWH(width, height), fProps),
721 kDefault_InitFlags)->unref();
reedd9544982014-09-09 18:46:22 -0700722}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000723
reed78e27682014-11-19 08:04:34 -0800724SkCanvas::SkCanvas(const SkIRect& bounds, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700725 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700726 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800727 , fConservativeRasterClip(false)
reedd9544982014-09-09 18:46:22 -0700728{
729 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700730
halcanary385fe4d2015-08-26 13:07:48 -0700731 this->init(new SkNoPixelsBitmapDevice(bounds, fProps), flags)->unref();
reedd9544982014-09-09 18:46:22 -0700732}
733
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000734SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000735 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700736 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800737 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000738{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000739 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700740
reedd9544982014-09-09 18:46:22 -0700741 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000742}
743
robertphillipsfcf78292015-06-19 11:49:52 -0700744SkCanvas::SkCanvas(SkBaseDevice* device, InitFlags flags)
745 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700746 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800747 , fConservativeRasterClip(false)
robertphillipsfcf78292015-06-19 11:49:52 -0700748{
749 inc_canvas();
750
751 this->init(device, flags);
752}
753
reed4a8126e2014-09-22 07:29:03 -0700754SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700755 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700756 , fProps(props)
reed42b73eb2015-11-20 13:42:42 -0800757 , fConservativeRasterClip(false)
reed3716fd02014-09-21 09:39:55 -0700758{
759 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700760
halcanary385fe4d2015-08-26 13:07:48 -0700761 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700762 this->init(device, kDefault_InitFlags);
763}
reed29c857d2014-09-21 10:25:07 -0700764
reed4a8126e2014-09-22 07:29:03 -0700765SkCanvas::SkCanvas(const SkBitmap& bitmap)
766 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
767 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800768 , fConservativeRasterClip(false)
reed4a8126e2014-09-22 07:29:03 -0700769{
770 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700771
halcanary385fe4d2015-08-26 13:07:48 -0700772 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700773 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000774}
775
776SkCanvas::~SkCanvas() {
777 // free up the contents of our deque
778 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000779
reed@android.com8a1c16f2008-12-17 15:59:43 +0000780 this->internalRestore(); // restore the last, since we're going away
781
halcanary385fe4d2015-08-26 13:07:48 -0700782 delete fMetaData;
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000783
reed@android.com8a1c16f2008-12-17 15:59:43 +0000784 dec_canvas();
785}
786
fmalita53d9f1c2016-01-25 06:23:54 -0800787#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000788SkDrawFilter* SkCanvas::getDrawFilter() const {
789 return fMCRec->fFilter;
790}
791
792SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
reed51985e32015-04-11 08:04:56 -0700793 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000794 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
795 return filter;
796}
fmalita77650002016-01-21 18:47:11 -0800797#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000798
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000799SkMetaData& SkCanvas::getMetaData() {
800 // metadata users are rare, so we lazily allocate it. If that changes we
801 // can decide to just make it a field in the device (rather than a ptr)
halcanary96fcdcc2015-08-27 07:41:13 -0700802 if (nullptr == fMetaData) {
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000803 fMetaData = new SkMetaData;
804 }
805 return *fMetaData;
806}
807
reed@android.com8a1c16f2008-12-17 15:59:43 +0000808///////////////////////////////////////////////////////////////////////////////
809
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000810void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000811 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000812 if (device) {
813 device->flush();
814 }
815}
816
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000817SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000818 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000819 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
820}
821
senorblancoafc7cce2016-02-02 18:44:15 -0800822SkIRect SkCanvas::getTopLayerBounds() const {
823 SkBaseDevice* d = this->getTopDevice();
824 if (!d) {
825 return SkIRect::MakeEmpty();
826 }
827 return SkIRect::MakeXYWH(d->getOrigin().x(), d->getOrigin().y(), d->width(), d->height());
828}
829
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000830SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000831 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000832 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000833 SkASSERT(rec && rec->fLayer);
834 return rec->fLayer->fDevice;
835}
836
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000837SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000838 if (updateMatrixClip) {
839 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
840 }
reed@google.com9266fed2011-03-30 00:18:03 +0000841 return fMCRec->fTopLayer->fDevice;
842}
843
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000844bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
845 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
846 return false;
847 }
848
849 bool weAllocated = false;
halcanary96fcdcc2015-08-27 07:41:13 -0700850 if (nullptr == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700851 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000852 return false;
853 }
854 weAllocated = true;
855 }
856
reedcf01e312015-05-23 19:14:51 -0700857 SkAutoPixmapUnlock unlocker;
858 if (bitmap->requestLock(&unlocker)) {
859 const SkPixmap& pm = unlocker.pixmap();
860 if (this->readPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), x, y)) {
861 return true;
862 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000863 }
864
865 if (weAllocated) {
halcanary96fcdcc2015-08-27 07:41:13 -0700866 bitmap->setPixelRef(nullptr);
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000867 }
868 return false;
869}
reed@google.com51df9e32010-12-23 19:29:18 +0000870
bsalomon@google.comc6980972011-11-02 19:57:21 +0000871bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000872 SkIRect r = srcRect;
873 const SkISize size = this->getBaseLayerSize();
874 if (!r.intersect(0, 0, size.width(), size.height())) {
875 bitmap->reset();
876 return false;
877 }
878
reed84825042014-09-02 12:50:45 -0700879 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000880 // bitmap will already be reset.
881 return false;
882 }
883 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
884 bitmap->reset();
885 return false;
886 }
887 return true;
888}
889
reed96472de2014-12-10 09:53:42 -0800890bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000891 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000892 if (!device) {
893 return false;
894 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000895 const SkISize size = this->getBaseLayerSize();
mtkleinf0f14112014-12-12 08:46:25 -0800896
reed96472de2014-12-10 09:53:42 -0800897 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
898 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000899 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000900 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000901
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000902 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800903 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000904}
905
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000906bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
907 if (bitmap.getTexture()) {
908 return false;
909 }
reedcf01e312015-05-23 19:14:51 -0700910
911 SkAutoPixmapUnlock unlocker;
912 if (bitmap.requestLock(&unlocker)) {
913 const SkPixmap& pm = unlocker.pixmap();
914 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000915 }
916 return false;
917}
918
919bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
920 int x, int y) {
921 switch (origInfo.colorType()) {
922 case kUnknown_SkColorType:
923 case kIndex_8_SkColorType:
924 return false;
925 default:
926 break;
927 }
halcanary96fcdcc2015-08-27 07:41:13 -0700928 if (nullptr == pixels || rowBytes < origInfo.minRowBytes()) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000929 return false;
930 }
931
932 const SkISize size = this->getBaseLayerSize();
933 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
934 if (!target.intersect(0, 0, size.width(), size.height())) {
935 return false;
936 }
937
938 SkBaseDevice* device = this->getDevice();
939 if (!device) {
940 return false;
941 }
942
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000943 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700944 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000945
946 // if x or y are negative, then we have to adjust pixels
947 if (x > 0) {
948 x = 0;
949 }
950 if (y > 0) {
951 y = 0;
952 }
953 // here x,y are either 0 or negative
954 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
955
reed4af35f32014-06-27 17:47:49 -0700956 // Tell our owning surface to bump its generation ID
reedc83a2972015-07-16 07:40:45 -0700957 const bool completeOverwrite = info.dimensions() == size;
958 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700959
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000960 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000961 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000962}
reed@google.com51df9e32010-12-23 19:29:18 +0000963
junov@google.com4370aed2012-01-18 16:21:08 +0000964SkCanvas* SkCanvas::canvasForDrawIter() {
965 return this;
966}
967
reed@android.com8a1c16f2008-12-17 15:59:43 +0000968//////////////////////////////////////////////////////////////////////////////
969
reed@android.com8a1c16f2008-12-17 15:59:43 +0000970void SkCanvas::updateDeviceCMCache() {
971 if (fDeviceCMDirty) {
972 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700973 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000974 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000975
halcanary96fcdcc2015-08-27 07:41:13 -0700976 if (nullptr == layer->fNext) { // only one layer
977 layer->updateMC(totalMatrix, totalClip, *fClipStack, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000978 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000979 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000980 do {
reed687fa1c2015-04-07 08:00:56 -0700981 layer->updateMC(totalMatrix, clip, *fClipStack, &clip);
halcanary96fcdcc2015-08-27 07:41:13 -0700982 } while ((layer = layer->fNext) != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000983 }
984 fDeviceCMDirty = false;
985 }
986}
987
reed@android.com8a1c16f2008-12-17 15:59:43 +0000988///////////////////////////////////////////////////////////////////////////////
989
reed2ff1fce2014-12-11 07:07:37 -0800990void SkCanvas::checkForDeferredSave() {
991 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800992 this->doSave();
993 }
994}
995
reedf0090cb2014-11-26 08:55:51 -0800996int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800997#ifdef SK_DEBUG
998 int count = 0;
999 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
1000 for (;;) {
1001 const MCRec* rec = (const MCRec*)iter.next();
1002 if (!rec) {
1003 break;
1004 }
1005 count += 1 + rec->fDeferredSaveCount;
1006 }
1007 SkASSERT(count == fSaveCount);
1008#endif
1009 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -08001010}
1011
1012int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -08001013 fSaveCount += 1;
1014 fMCRec->fDeferredSaveCount += 1;
1015 return this->getSaveCount() - 1; // return our prev value
1016}
1017
1018void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -08001019 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -07001020
1021 SkASSERT(fMCRec->fDeferredSaveCount > 0);
1022 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001023 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -08001024}
1025
1026void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -08001027 if (fMCRec->fDeferredSaveCount > 0) {
1028 SkASSERT(fSaveCount > 1);
1029 fSaveCount -= 1;
1030 fMCRec->fDeferredSaveCount -= 1;
1031 } else {
1032 // check for underflow
1033 if (fMCStack.count() > 1) {
1034 this->willRestore();
1035 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -07001036 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001037 this->internalRestore();
1038 this->didRestore();
1039 }
reedf0090cb2014-11-26 08:55:51 -08001040 }
1041}
1042
1043void SkCanvas::restoreToCount(int count) {
1044 // sanity check
1045 if (count < 1) {
1046 count = 1;
1047 }
mtkleinf0f14112014-12-12 08:46:25 -08001048
reedf0090cb2014-11-26 08:55:51 -08001049 int n = this->getSaveCount() - count;
1050 for (int i = 0; i < n; ++i) {
1051 this->restore();
1052 }
1053}
1054
reed2ff1fce2014-12-11 07:07:37 -08001055void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001056 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -07001057 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +00001058 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +00001059
reed687fa1c2015-04-07 08:00:56 -07001060 fClipStack->save();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001061}
1062
reed4960eee2015-12-18 07:09:18 -08001063bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
reed@google.comb93ba452014-03-10 19:47:58 +00001064#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001065 return !(saveLayerFlags & SkCanvas::kDontClipToLayer_PrivateSaveLayerFlag);
reed@google.comb93ba452014-03-10 19:47:58 +00001066#else
1067 return true;
1068#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001069}
1070
reed4960eee2015-12-18 07:09:18 -08001071bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -07001072 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001073 SkIRect clipBounds;
1074 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001075 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +00001076 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001077
reed96e657d2015-03-10 17:30:07 -07001078 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
1079
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001080 if (imageFilter) {
senorblancoe5e79842016-03-21 14:51:59 -07001081 clipBounds = imageFilter->filterBounds(clipBounds, ctm);
senorblancodb64af32015-12-09 10:11:43 -08001082 if (bounds && !imageFilter->canComputeFastBounds()) {
1083 bounds = nullptr;
1084 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001085 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001086 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -07001087 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001088 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +00001089
reed96e657d2015-03-10 17:30:07 -07001090 ctm.mapRect(&r, *bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001091 r.roundOut(&ir);
1092 // early exit if the layer's bounds are clipped out
1093 if (!ir.intersect(clipBounds)) {
reed4960eee2015-12-18 07:09:18 -08001094 if (BoundsAffectsClip(saveLayerFlags)) {
reed9b3aa542015-03-11 08:47:12 -07001095 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001096 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001097 }
junov@chromium.orga907ac32012-02-24 21:54:07 +00001098 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001099 }
1100 } else { // no user bounds, so just use the clip
1101 ir = clipBounds;
1102 }
reed180aec42015-03-11 10:39:04 -07001103 SkASSERT(!ir.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001104
reed4960eee2015-12-18 07:09:18 -08001105 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -07001106 // Simplify the current clips since they will be applied properly during restore()
reed9b3aa542015-03-11 08:47:12 -07001107 fCachedLocalClipBoundsDirty = true;
reed687fa1c2015-04-07 08:00:56 -07001108 fClipStack->clipDevRect(ir, SkRegion::kReplace_Op);
reed180aec42015-03-11 10:39:04 -07001109 fMCRec->fRasterClip.setRect(ir);
junov@chromium.orga907ac32012-02-24 21:54:07 +00001110 }
1111
1112 if (intersection) {
1113 *intersection = ir;
1114 }
1115 return true;
1116}
1117
reed4960eee2015-12-18 07:09:18 -08001118
reed4960eee2015-12-18 07:09:18 -08001119int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
1120 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001121}
1122
reed70ee31b2015-12-10 13:44:45 -08001123int SkCanvas::saveLayerPreserveLCDTextRequests(const SkRect* bounds, const SkPaint* paint) {
reed4960eee2015-12-18 07:09:18 -08001124 return this->saveLayer(SaveLayerRec(bounds, paint, kPreserveLCDText_SaveLayerFlag));
1125}
1126
1127int SkCanvas::saveLayer(const SaveLayerRec& origRec) {
1128 SaveLayerRec rec(origRec);
1129 if (gIgnoreSaveLayerBounds) {
1130 rec.fBounds = nullptr;
1131 }
1132 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
1133 fSaveCount += 1;
1134 this->internalSaveLayer(rec, strategy);
1135 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -08001136}
1137
reedbfd5f172016-01-07 11:28:08 -08001138static void draw_filter_into_device(SkBaseDevice* src, const SkImageFilter* filter,
1139 SkBaseDevice* dst, const SkMatrix& ctm) {
robertphillips7354a4b2015-12-16 05:08:27 -08001140
1141 SkBitmap srcBM;
1142
1143#if SK_SUPPORT_GPU
1144 GrRenderTarget* srcRT = src->accessRenderTarget();
1145 if (srcRT && !srcRT->asTexture() && dst->accessRenderTarget()) {
1146 // When both the src & the dst are on the gpu but the src doesn't have a texture,
1147 // we create a temporary texture for the draw.
1148 // TODO: we should actually only copy the portion of the source needed to apply the image
1149 // filter
1150 GrContext* context = srcRT->getContext();
bsalomon5ec26ae2016-02-25 08:33:02 -08001151 SkAutoTUnref<GrTexture> tex(context->textureProvider()->createTexture(srcRT->desc(),
1152 SkBudgeted::kYes));
robertphillips7354a4b2015-12-16 05:08:27 -08001153
1154 context->copySurface(tex, srcRT);
1155
1156 GrWrapTextureInBitmap(tex, src->width(), src->height(), src->isOpaque(), &srcBM);
1157 } else
1158#endif
1159 {
1160 srcBM = src->accessBitmap(false);
1161 }
1162
1163 SkCanvas c(dst);
1164
1165 SkPaint p;
robertphillips372177e2016-03-30 07:32:28 -07001166 p.setImageFilter(filter->makeWithLocalMatrix(ctm));
reedbfd5f172016-01-07 11:28:08 -08001167 const SkScalar x = SkIntToScalar(src->getOrigin().x());
1168 const SkScalar y = SkIntToScalar(src->getOrigin().y());
1169 c.drawBitmap(srcBM, x, y, &p);
robertphillips7354a4b2015-12-16 05:08:27 -08001170}
reed70ee31b2015-12-10 13:44:45 -08001171
reed129ed1c2016-02-22 06:42:31 -08001172static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, bool isOpaque,
1173 const SkPaint* paint) {
1174 // need to force L32 for now if we have an image filter. Once filters support other colortypes
1175 // e.g. sRGB or F16, we can remove this check
1176 const bool hasImageFilter = paint && paint->getImageFilter();
1177
1178 SkAlphaType alphaType = isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
1179 if ((prev.bytesPerPixel() < 4) || hasImageFilter) {
1180 // force to L32
1181 return SkImageInfo::MakeN32(w, h, alphaType);
1182 } else {
1183 // keep the same characteristics as the prev
1184 return SkImageInfo::Make(w, h, prev.colorType(), alphaType, prev.profileType());
1185 }
1186}
1187
reed4960eee2015-12-18 07:09:18 -08001188void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
1189 const SkRect* bounds = rec.fBounds;
1190 const SkPaint* paint = rec.fPaint;
1191 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1192
reed@google.comb93ba452014-03-10 19:47:58 +00001193#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001194 saveLayerFlags &= ~kDontClipToLayer_PrivateSaveLayerFlag;
reed@google.comb93ba452014-03-10 19:47:58 +00001195#endif
1196
reed8c30a812016-04-20 16:36:51 -07001197 SkLazyPaint lazyP;
1198 SkImageFilter* imageFilter = paint ? paint->getImageFilter() : NULL;
1199 SkMatrix stashedMatrix = fMCRec->fMatrix;
1200#ifndef SK_SUPPORT_LEGACY_IMAGEFILTER_CTM
1201 SkMatrix remainder;
1202 SkSize scale;
1203 /*
1204 * ImageFilters (so far) do not correctly handle matrices (CTM) that contain rotation/skew/etc.
1205 * but they do handle scaling. To accommodate this, we do the following:
1206 *
1207 * 1. Stash off the current CTM
1208 * 2. Decompose the CTM into SCALE and REMAINDER
1209 * 3. Wack the CTM to be just SCALE, and wrap the imagefilter with a MatrixImageFilter that
1210 * contains the REMAINDER
1211 * 4. Proceed as usual, allowing the client to draw into the layer (now with a scale-only CTM)
1212 * 5. During restore, we process the MatrixImageFilter, which applies REMAINDER to the output
1213 * of the original imagefilter, and draw that (via drawSprite)
1214 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1215 *
1216 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1217 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1218 */
reed96a04f32016-04-25 09:25:15 -07001219 if (imageFilter && !stashedMatrix.isScaleTranslate() && !imageFilter->canHandleComplexCTM() &&
reed8c30a812016-04-20 16:36:51 -07001220 stashedMatrix.decomposeScale(&scale, &remainder))
1221 {
1222 // We will restore the matrix (which we are overwriting here) in restore via fStashedMatrix
1223 this->internalSetMatrix(SkMatrix::MakeScale(scale.width(), scale.height()));
1224 SkPaint* p = lazyP.set(*paint);
1225 p->setImageFilter(SkImageFilter::MakeMatrixFilter(remainder,
1226 SkFilterQuality::kLow_SkFilterQuality,
1227 sk_ref_sp(imageFilter)));
1228 imageFilter = p->getImageFilter();
1229 paint = p;
1230 }
1231#endif
1232
junov@chromium.orga907ac32012-02-24 21:54:07 +00001233 // do this before we create the layer. We don't call the public save() since
1234 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001235 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001236
1237 fDeviceCMDirty = true;
1238
1239 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001240 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
reed2ff1fce2014-12-11 07:07:37 -08001241 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001242 }
1243
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001244 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1245 // the clipRectBounds() call above?
1246 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001247 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001248 }
1249
reed4960eee2015-12-18 07:09:18 -08001250 bool isOpaque = SkToBool(saveLayerFlags & kIsOpaque_SaveLayerFlag);
reed8dc0ccb2015-03-20 06:32:52 -07001251 SkPixelGeometry geo = fProps.pixelGeometry();
1252 if (paint) {
reed76033be2015-03-14 10:54:31 -07001253 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001254 if (paint->getImageFilter() || paint->getColorFilter()) {
reed76033be2015-03-14 10:54:31 -07001255 isOpaque = false;
reed8dc0ccb2015-03-20 06:32:52 -07001256 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001257 }
1258 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001259
reedb2db8982014-11-13 12:41:02 -08001260 SkBaseDevice* device = this->getTopDevice();
halcanary96fcdcc2015-08-27 07:41:13 -07001261 if (nullptr == device) {
reedb2db8982014-11-13 12:41:02 -08001262 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001263 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001264 }
reedb2db8982014-11-13 12:41:02 -08001265
reed129ed1c2016-02-22 06:42:31 -08001266 SkImageInfo info = make_layer_info(device->imageInfo(), ir.width(), ir.height(), isOpaque,
1267 paint);
1268
reed61f501f2015-04-29 08:34:00 -07001269 bool forceSpriteOnRestore = false;
1270 {
reed70ee31b2015-12-10 13:44:45 -08001271 const bool preserveLCDText = kOpaque_SkAlphaType == info.alphaType() ||
reed4960eee2015-12-18 07:09:18 -08001272 (saveLayerFlags & kPreserveLCDText_SaveLayerFlag);
reeddaa57bf2015-05-15 10:39:17 -07001273 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
reed70ee31b2015-12-10 13:44:45 -08001274 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
1275 preserveLCDText, false);
reed61f501f2015-04-29 08:34:00 -07001276 SkBaseDevice* newDev = device->onCreateDevice(createInfo, paint);
halcanary96fcdcc2015-08-27 07:41:13 -07001277 if (nullptr == newDev) {
reed61f501f2015-04-29 08:34:00 -07001278 // If onCreateDevice didn't succeed, try raster (e.g. PDF couldn't handle the paint)
robertphillips9a53fd72015-06-22 09:46:59 -07001279 const SkSurfaceProps surfaceProps(fProps.flags(), createInfo.fPixelGeometry);
1280 newDev = SkBitmapDevice::Create(createInfo.fInfo, surfaceProps);
halcanary96fcdcc2015-08-27 07:41:13 -07001281 if (nullptr == newDev) {
reed61f501f2015-04-29 08:34:00 -07001282 SkErrorInternals::SetError(kInternalError_SkError,
1283 "Unable to create device for layer.");
1284 return;
1285 }
1286 forceSpriteOnRestore = true;
1287 }
1288 device = newDev;
bungeman@google.come25c6842011-08-17 14:53:54 +00001289 }
reed@google.com6f8f2922011-03-04 22:27:10 +00001290 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips7354a4b2015-12-16 05:08:27 -08001291
reedbfd5f172016-01-07 11:28:08 -08001292 if (rec.fBackdrop) {
1293 draw_filter_into_device(fMCRec->fTopLayer->fDevice, rec.fBackdrop, device, fMCRec->fMatrix);
robertphillips7354a4b2015-12-16 05:08:27 -08001294 }
1295
reed8c30a812016-04-20 16:36:51 -07001296 DeviceCM* layer = new DeviceCM(device, paint, this, fConservativeRasterClip,
1297 forceSpriteOnRestore, stashedMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001298 device->unref();
1299
1300 layer->fNext = fMCRec->fTopLayer;
1301 fMCRec->fLayer = layer;
1302 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reed@android.com8a1c16f2008-12-17 15:59:43 +00001303}
1304
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001305int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001306 if (0xFF == alpha) {
1307 return this->saveLayer(bounds, nullptr);
1308 } else {
1309 SkPaint tmpPaint;
1310 tmpPaint.setAlpha(alpha);
1311 return this->saveLayer(bounds, &tmpPaint);
1312 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001313}
1314
reed@android.com8a1c16f2008-12-17 15:59:43 +00001315void SkCanvas::internalRestore() {
1316 SkASSERT(fMCStack.count() != 0);
1317
1318 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001319 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001320
reed687fa1c2015-04-07 08:00:56 -07001321 fClipStack->restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001322
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001323 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001324 DeviceCM* layer = fMCRec->fLayer; // may be null
1325 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001326 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001327
1328 // now do the normal restore()
1329 fMCRec->~MCRec(); // balanced in save()
1330 fMCStack.pop_back();
1331 fMCRec = (MCRec*)fMCStack.back();
1332
1333 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1334 since if we're being recorded, we don't want to record this (the
1335 recorder will have already recorded the restore).
1336 */
bsalomon49f085d2014-09-05 13:34:00 -07001337 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001338 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001339 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001340 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
reed61f501f2015-04-29 08:34:00 -07001341 layer->fPaint, layer->fDeviceIsBitmapDevice);
reed8c30a812016-04-20 16:36:51 -07001342 // restore what we smashed in internalSaveLayer
1343 fMCRec->fMatrix = layer->fStashedMatrix;
reed@google.com8926b162012-03-23 15:36:36 +00001344 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001345 fDeviceCMDirty = true;
halcanary385fe4d2015-08-26 13:07:48 -07001346 delete layer;
reedb679ca82015-04-07 04:40:48 -07001347 } else {
1348 // we're at the root
reeda499f902015-05-01 09:34:31 -07001349 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001350 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001351 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001352 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001353 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001354}
1355
reede8f30622016-03-23 18:59:25 -07001356sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001357 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001358 props = &fProps;
1359 }
1360 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001361}
1362
reede8f30622016-03-23 18:59:25 -07001363sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001364 SkBaseDevice* dev = this->getDevice();
reede8f30622016-03-23 18:59:25 -07001365 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001366}
1367
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001368SkImageInfo SkCanvas::imageInfo() const {
1369 SkBaseDevice* dev = this->getDevice();
1370 if (dev) {
1371 return dev->imageInfo();
1372 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001373 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001374 }
1375}
1376
brianosman898235c2016-04-06 07:38:23 -07001377bool SkCanvas::getProps(SkSurfaceProps* props) const {
1378 SkBaseDevice* dev = this->getDevice();
1379 if (dev) {
1380 if (props) {
1381 *props = fProps;
1382 }
1383 return true;
1384 } else {
1385 return false;
1386 }
1387}
1388
reed6ceeebd2016-03-09 14:26:26 -08001389#ifdef SK_SUPPORT_LEGACY_PEEKPIXELS_PARMS
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001390const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
reed884e97c2015-05-26 11:31:54 -07001391 SkPixmap pmap;
reed6ceeebd2016-03-09 14:26:26 -08001392 if (this->peekPixels(&pmap)) {
1393 if (info) {
1394 *info = pmap.info();
1395 }
1396 if (rowBytes) {
1397 *rowBytes = pmap.rowBytes();
1398 }
1399 return pmap.addr();
reed884e97c2015-05-26 11:31:54 -07001400 }
reed6ceeebd2016-03-09 14:26:26 -08001401 return nullptr;
1402}
1403#endif
1404
1405bool SkCanvas::peekPixels(SkPixmap* pmap) {
1406 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001407}
1408
reed884e97c2015-05-26 11:31:54 -07001409bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001410 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001411 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001412}
1413
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001414void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001415 SkPixmap pmap;
1416 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001417 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001418 }
1419 if (info) {
1420 *info = pmap.info();
1421 }
1422 if (rowBytes) {
1423 *rowBytes = pmap.rowBytes();
1424 }
1425 if (origin) {
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001426 *origin = this->getTopDevice(false)->getOrigin();
1427 }
reed884e97c2015-05-26 11:31:54 -07001428 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001429}
1430
reed884e97c2015-05-26 11:31:54 -07001431bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001432 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001433 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001434}
1435
reed@android.com8a1c16f2008-12-17 15:59:43 +00001436/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001437
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001438void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed61f501f2015-04-29 08:34:00 -07001439 const SkPaint* paint, bool deviceIsBitmapDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001440 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001441 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001442 paint = &tmp;
1443 }
reed@google.com4b226022011-01-11 18:32:13 +00001444
reed@google.com8926b162012-03-23 15:36:36 +00001445 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001446 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001447 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001448 paint = &looper.paint();
1449 SkImageFilter* filter = paint->getImageFilter();
1450 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
senorblancof35566e2016-04-18 10:32:02 -07001451 if (filter) {
robertphillips4418dba2016-03-07 12:45:14 -08001452 const SkBitmap& srcBM = srcDev->accessBitmap(false);
reed1eca1162016-04-25 12:29:38 -07001453 dstDev->drawSpriteWithFilter(iter, srcBM, pos.x(), pos.y(), *paint);
reed61f501f2015-04-29 08:34:00 -07001454 } else if (deviceIsBitmapDevice) {
reed9572a102015-05-26 19:22:17 -07001455 const SkBitmap& src = static_cast<SkBitmapDevice*>(srcDev)->fBitmap;
reed61f501f2015-04-29 08:34:00 -07001456 dstDev->drawSprite(iter, src, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001457 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001458 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001459 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001460 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001461 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001462}
1463
reed32704672015-12-16 08:27:10 -08001464/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001465
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001466void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001467 SkMatrix m;
1468 m.setTranslate(dx, dy);
1469 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001470}
1471
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001472void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001473 SkMatrix m;
1474 m.setScale(sx, sy);
1475 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001476}
1477
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001478void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001479 SkMatrix m;
1480 m.setRotate(degrees);
1481 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001482}
1483
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001484void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001485 SkMatrix m;
1486 m.setSkew(sx, sy);
1487 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001488}
1489
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001490void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001491 if (matrix.isIdentity()) {
1492 return;
1493 }
1494
reed2ff1fce2014-12-11 07:07:37 -08001495 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001496 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001497 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001498 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001499
1500 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001501}
1502
reed8c30a812016-04-20 16:36:51 -07001503void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001504 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001505 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001506 fMCRec->fMatrix = matrix;
reed8c30a812016-04-20 16:36:51 -07001507}
1508
1509void SkCanvas::setMatrix(const SkMatrix& matrix) {
1510 this->checkForDeferredSave();
1511 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001512 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001513}
1514
reed@android.com8a1c16f2008-12-17 15:59:43 +00001515void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001516 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001517}
1518
1519//////////////////////////////////////////////////////////////////////////////
1520
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001521void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001522 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001523 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1524 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001525}
1526
1527void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001528#ifdef SK_ENABLE_CLIP_QUICKREJECT
1529 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001530 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001531 return false;
1532 }
1533
reed@google.com3b3e8952012-08-16 20:53:31 +00001534 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001535 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001536 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001537
reed687fa1c2015-04-07 08:00:56 -07001538 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001539 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001540 }
1541 }
1542#endif
1543
bsalomonac8cabd2015-11-20 18:53:07 -08001544 if (!fAllowSoftClip) {
1545 edgeStyle = kHard_ClipEdgeStyle;
1546 }
reed90ba0952015-11-20 13:42:47 -08001547
reedc64eff52015-11-21 12:39:45 -08001548 const bool rectStaysRect = fMCRec->fMatrix.rectStaysRect();
1549 SkRect devR;
1550 if (rectStaysRect) {
1551 fMCRec->fMatrix.mapRect(&devR, rect);
1552 }
bsalomonac8cabd2015-11-20 18:53:07 -08001553
reedc64eff52015-11-21 12:39:45 -08001554 // Check if we can quick-accept the clip call (and do nothing)
1555 //
1556 // TODO: investigate if a (conservative) version of this could be done in ::clipRect,
1557 // so that subclasses (like PictureRecording) didn't see unnecessary clips, which in turn
1558 // might allow lazy save/restores to eliminate entire save/restore blocks.
1559 //
1560 if (SkRegion::kIntersect_Op == op &&
1561 kHard_ClipEdgeStyle == edgeStyle
1562 && rectStaysRect)
1563 {
1564 if (devR.round().contains(fMCRec->fRasterClip.getBounds())) {
1565#if 0
1566 SkDebugf("------- ignored clipRect [%g %g %g %g]\n",
1567 rect.left(), rect.top(), rect.right(), rect.bottom());
1568#endif
1569 return;
1570 }
1571 }
1572
1573 AutoValidateClip avc(this);
1574
1575 fDeviceCMDirty = true;
1576 fCachedLocalClipBoundsDirty = true;
1577
1578 if (rectStaysRect) {
1579 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1580 fClipStack->clipDevRect(devR, op, isAA);
senorblancoafc7cce2016-02-02 18:44:15 -08001581 fMCRec->fRasterClip.op(devR, this->getTopLayerBounds(), op, isAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001582 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001583 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001584 // and clip against that, since it can handle any matrix. However, to
1585 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1586 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001587 SkPath path;
1588
1589 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001590 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001591 }
1592}
1593
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001594void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001595 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001596 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001597 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001598 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1599 } else {
1600 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001601 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001602}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001603
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001604void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001605 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001606 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001607 AutoValidateClip avc(this);
1608
1609 fDeviceCMDirty = true;
1610 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001611 if (!fAllowSoftClip) {
1612 edgeStyle = kHard_ClipEdgeStyle;
1613 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001614
reed687fa1c2015-04-07 08:00:56 -07001615 fClipStack->clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001616
senorblancoafc7cce2016-02-02 18:44:15 -08001617 fMCRec->fRasterClip.op(transformedRRect, this->getTopLayerBounds(), op,
robertphillips125f19a2015-11-23 09:00:05 -08001618 kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001619 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001620 }
1621
1622 SkPath path;
1623 path.addRRect(rrect);
1624 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001625 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001626}
1627
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001628void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001629 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001630 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001631
1632 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1633 SkRect r;
1634 if (path.isRect(&r)) {
1635 this->onClipRect(r, op, edgeStyle);
1636 return;
1637 }
1638 SkRRect rrect;
1639 if (path.isOval(&r)) {
1640 rrect.setOval(r);
1641 this->onClipRRect(rrect, op, edgeStyle);
1642 return;
1643 }
1644 if (path.isRRect(&rrect)) {
1645 this->onClipRRect(rrect, op, edgeStyle);
1646 return;
1647 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001648 }
robertphillips39f05382015-11-24 09:30:12 -08001649
1650 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001651}
1652
1653void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001654#ifdef SK_ENABLE_CLIP_QUICKREJECT
1655 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001656 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001657 return false;
1658 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001659
reed@google.com3b3e8952012-08-16 20:53:31 +00001660 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001661 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001662 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001663
reed687fa1c2015-04-07 08:00:56 -07001664 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001665 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001666 }
1667 }
1668#endif
1669
reed@google.com5c3d1472011-02-22 19:12:23 +00001670 AutoValidateClip avc(this);
1671
reed@android.com8a1c16f2008-12-17 15:59:43 +00001672 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001673 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001674 if (!fAllowSoftClip) {
1675 edgeStyle = kHard_ClipEdgeStyle;
1676 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001677
1678 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001679 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001680
reed@google.comfe701122011-11-08 19:41:23 +00001681 // Check if the transfomation, or the original path itself
1682 // made us empty. Note this can also happen if we contained NaN
1683 // values. computing the bounds detects this, and will set our
1684 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1685 if (devPath.getBounds().isEmpty()) {
1686 // resetting the path will remove any NaN or other wanky values
1687 // that might upset our scan converter.
1688 devPath.reset();
1689 }
1690
reed@google.com5c3d1472011-02-22 19:12:23 +00001691 // if we called path.swap() we could avoid a deep copy of this path
reed687fa1c2015-04-07 08:00:56 -07001692 fClipStack->clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001693
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001694 if (fAllowSimplifyClip) {
fmalita1a481fe2015-02-04 07:39:34 -08001695 bool clipIsAA = getClipStack()->asPath(&devPath);
1696 if (clipIsAA) {
1697 edgeStyle = kSoft_ClipEdgeStyle;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001698 }
fmalita1a481fe2015-02-04 07:39:34 -08001699
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001700 op = SkRegion::kReplace_Op;
1701 }
1702
senorblancoafc7cce2016-02-02 18:44:15 -08001703 fMCRec->fRasterClip.op(devPath, this->getTopLayerBounds(), op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001704}
1705
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001706void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed2ff1fce2014-12-11 07:07:37 -08001707 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001708 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001709}
1710
1711void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001712 AutoValidateClip avc(this);
1713
reed@android.com8a1c16f2008-12-17 15:59:43 +00001714 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001715 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001716
reed@google.com5c3d1472011-02-22 19:12:23 +00001717 // todo: signal fClipStack that we have a region, and therefore (I guess)
1718 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001719 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001720
reed1f836ee2014-07-07 07:49:34 -07001721 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001722}
1723
reed@google.com819c9212011-02-23 18:56:55 +00001724#ifdef SK_DEBUG
1725void SkCanvas::validateClip() const {
1726 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001727 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001728 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001729 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001730 return;
1731 }
1732
reed@google.com819c9212011-02-23 18:56:55 +00001733 SkIRect ir;
1734 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001735 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001736
reed687fa1c2015-04-07 08:00:56 -07001737 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001738 const SkClipStack::Element* element;
halcanary96fcdcc2015-08-27 07:41:13 -07001739 while ((element = iter.next()) != nullptr) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001740 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001741 case SkClipStack::Element::kRect_Type:
1742 element->getRect().round(&ir);
1743 tmpClip.op(ir, element->getOp());
1744 break;
1745 case SkClipStack::Element::kEmpty_Type:
1746 tmpClip.setEmpty();
1747 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001748 default: {
1749 SkPath path;
1750 element->asPath(&path);
senorblancoafc7cce2016-02-02 18:44:15 -08001751 tmpClip.op(path, this->getTopLayerBounds(), element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001752 break;
1753 }
reed@google.com819c9212011-02-23 18:56:55 +00001754 }
1755 }
reed@google.com819c9212011-02-23 18:56:55 +00001756}
1757#endif
1758
reed@google.com90c07ea2012-04-13 13:50:27 +00001759void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001760 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001761 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001762
halcanary96fcdcc2015-08-27 07:41:13 -07001763 while ((element = iter.next()) != nullptr) {
fmalitac3b589a2014-06-05 12:40:07 -07001764 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001765 }
1766}
1767
reed@google.com5c3d1472011-02-22 19:12:23 +00001768///////////////////////////////////////////////////////////////////////////////
1769
reed@google.com754de5f2014-02-24 19:38:20 +00001770bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001771 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001772}
1773
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001774bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001775 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001776}
1777
reed@google.com3b3e8952012-08-16 20:53:31 +00001778bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001779 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001780 return true;
1781
reed1f836ee2014-07-07 07:49:34 -07001782 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001783 return true;
1784 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001785
reed1f836ee2014-07-07 07:49:34 -07001786 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001787 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001788 fMCRec->fMatrix.mapRect(&dst, rect);
reedb07a94f2014-11-19 05:03:18 -08001789 return !SkIRect::Intersects(dst.roundOut(), fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001790 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001791 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001792
reed@android.coma380ae42009-07-21 01:17:02 +00001793 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001794 // TODO: should we use | instead, or compare all 4 at once?
1795 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001796 return true;
1797 }
reed@google.comc0784db2013-12-13 21:16:12 +00001798 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001799 return true;
1800 }
1801 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001802 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001803}
1804
reed@google.com3b3e8952012-08-16 20:53:31 +00001805bool SkCanvas::quickReject(const SkPath& path) const {
1806 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001807}
1808
reed@google.com3b3e8952012-08-16 20:53:31 +00001809bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001810 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001811 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001812 return false;
1813 }
1814
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001815 SkMatrix inverse;
1816 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001817 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001818 if (bounds) {
1819 bounds->setEmpty();
1820 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001821 return false;
1822 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001823
bsalomon49f085d2014-09-05 13:34:00 -07001824 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001825 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001826 // adjust it outwards in case we are antialiasing
1827 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001828
reed@google.com8f4d2302013-12-17 16:44:46 +00001829 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1830 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001831 inverse.mapRect(bounds, r);
1832 }
1833 return true;
1834}
1835
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001836bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001837 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001838 if (clip.isEmpty()) {
1839 if (bounds) {
1840 bounds->setEmpty();
1841 }
1842 return false;
1843 }
1844
bsalomon49f085d2014-09-05 13:34:00 -07001845 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001846 *bounds = clip.getBounds();
1847 }
1848 return true;
1849}
1850
reed@android.com8a1c16f2008-12-17 15:59:43 +00001851const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001852 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001853}
1854
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001855const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001856 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001857}
1858
reed@google.com9c135db2014-03-12 18:28:35 +00001859GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1860 SkBaseDevice* dev = this->getTopDevice();
halcanary96fcdcc2015-08-27 07:41:13 -07001861 return dev ? dev->accessRenderTarget() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001862}
1863
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001864GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001865 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001866 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001867}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001868
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001869void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1870 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001871 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001872 if (outer.isEmpty()) {
1873 return;
1874 }
1875 if (inner.isEmpty()) {
1876 this->drawRRect(outer, paint);
1877 return;
1878 }
1879
1880 // We don't have this method (yet), but technically this is what we should
1881 // be able to assert...
1882 // SkASSERT(outer.contains(inner));
1883 //
1884 // For now at least check for containment of bounds
1885 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1886
1887 this->onDrawDRRect(outer, inner, paint);
1888}
1889
reed41af9662015-01-05 07:49:08 -08001890// These need to stop being virtual -- clients need to override the onDraw... versions
1891
1892void SkCanvas::drawPaint(const SkPaint& paint) {
1893 this->onDrawPaint(paint);
1894}
1895
1896void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1897 this->onDrawRect(r, paint);
1898}
1899
1900void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1901 this->onDrawOval(r, paint);
1902}
1903
1904void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1905 this->onDrawRRect(rrect, paint);
1906}
1907
1908void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1909 this->onDrawPoints(mode, count, pts, paint);
1910}
1911
1912void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
1913 const SkPoint texs[], const SkColor colors[], SkXfermode* xmode,
1914 const uint16_t indices[], int indexCount, const SkPaint& paint) {
1915 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
1916 indices, indexCount, paint);
1917}
1918
1919void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1920 this->onDrawPath(path, paint);
1921}
1922
reeda85d4d02015-05-06 12:56:48 -07001923void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001924 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001925 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001926}
1927
reede47829b2015-08-06 10:02:53 -07001928void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1929 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001930 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001931 if (dst.isEmpty() || src.isEmpty()) {
1932 return;
1933 }
1934 this->onDrawImageRect(image, &src, dst, paint, constraint);
1935}
reed41af9662015-01-05 07:49:08 -08001936
reed84984ef2015-07-17 07:09:43 -07001937void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1938 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001939 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001940 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001941}
1942
reede47829b2015-08-06 10:02:53 -07001943void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
1944 SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001945 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001946 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
1947 constraint);
1948}
reede47829b2015-08-06 10:02:53 -07001949
reed4c21dc52015-06-25 12:32:03 -07001950void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1951 const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001952 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07001953 if (dst.isEmpty()) {
1954 return;
1955 }
1956 if (!SkNinePatchIter::Valid(image->width(), image->height(), center)) {
reede47829b2015-08-06 10:02:53 -07001957 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001958 }
1959 this->onDrawImageNine(image, center, dst, paint);
1960}
1961
reed41af9662015-01-05 07:49:08 -08001962void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001963 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001964 return;
1965 }
reed41af9662015-01-05 07:49:08 -08001966 this->onDrawBitmap(bitmap, dx, dy, paint);
1967}
1968
reede47829b2015-08-06 10:02:53 -07001969void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07001970 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001971 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07001972 return;
1973 }
reede47829b2015-08-06 10:02:53 -07001974 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08001975}
1976
reed84984ef2015-07-17 07:09:43 -07001977void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
1978 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001979 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001980}
1981
reede47829b2015-08-06 10:02:53 -07001982void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
1983 SrcRectConstraint constraint) {
1984 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
1985 constraint);
1986}
reede47829b2015-08-06 10:02:53 -07001987
reed41af9662015-01-05 07:49:08 -08001988void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
1989 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001990 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001991 return;
1992 }
reed4c21dc52015-06-25 12:32:03 -07001993 if (!SkNinePatchIter::Valid(bitmap.width(), bitmap.height(), center)) {
reeda5517e22015-07-14 10:54:12 -07001994 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001995 }
reed41af9662015-01-05 07:49:08 -08001996 this->onDrawBitmapNine(bitmap, center, dst, paint);
1997}
1998
reed71c3c762015-06-24 10:29:17 -07001999void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2000 const SkColor colors[], int count, SkXfermode::Mode mode,
2001 const SkRect* cull, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002002 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002003 if (count <= 0) {
2004 return;
2005 }
2006 SkASSERT(atlas);
2007 SkASSERT(xform);
2008 SkASSERT(tex);
2009 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
2010}
2011
reedf70b5312016-03-04 16:36:20 -08002012void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2013 if (key) {
2014 this->onDrawAnnotation(rect, key, value);
2015 }
2016}
2017
reede47829b2015-08-06 10:02:53 -07002018void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2019 const SkPaint* paint, SrcRectConstraint constraint) {
2020 if (src) {
2021 this->drawImageRect(image, *src, dst, paint, constraint);
2022 } else {
2023 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2024 dst, paint, constraint);
2025 }
2026}
2027void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2028 const SkPaint* paint, SrcRectConstraint constraint) {
2029 if (src) {
2030 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2031 } else {
2032 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2033 dst, paint, constraint);
2034 }
2035}
2036
reed@android.com8a1c16f2008-12-17 15:59:43 +00002037//////////////////////////////////////////////////////////////////////////////
2038// These are the virtual drawing methods
2039//////////////////////////////////////////////////////////////////////////////
2040
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002041void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002042 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002043 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2044 }
2045}
2046
reed41af9662015-01-05 07:49:08 -08002047void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002048 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002049 this->internalDrawPaint(paint);
2050}
2051
2052void SkCanvas::internalDrawPaint(const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002053 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002054
2055 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002056 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002057 }
2058
reed@google.com4e2b3d32011-04-07 14:18:59 +00002059 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002060}
2061
reed41af9662015-01-05 07:49:08 -08002062void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2063 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002064 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002065 if ((long)count <= 0) {
2066 return;
2067 }
2068
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002069 SkRect r, storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002070 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002071 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002072 // special-case 2 points (common for drawing a single line)
2073 if (2 == count) {
2074 r.set(pts[0], pts[1]);
2075 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00002076 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002077 }
senorblanco87e066e2015-10-28 11:23:36 -07002078 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2079 return;
2080 }
2081 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002082 }
reed@google.coma584aed2012-05-16 14:06:02 +00002083
halcanary96fcdcc2015-08-27 07:41:13 -07002084 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002085
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002086 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002087
reed@android.com8a1c16f2008-12-17 15:59:43 +00002088 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002089 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002090 }
reed@google.com4b226022011-01-11 18:32:13 +00002091
reed@google.com4e2b3d32011-04-07 14:18:59 +00002092 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002093}
2094
reed41af9662015-01-05 07:49:08 -08002095void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002096 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002097 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002098 const SkRect* bounds = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002099 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08002100 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
2101 // To prevent accidental rejecting at this stage, we have to sort it before we check.
2102 SkRect tmp(r);
2103 tmp.sort();
2104
senorblanco87e066e2015-10-28 11:23:36 -07002105 if (this->quickReject(paint.computeFastBounds(tmp, &storage))) {
2106 return;
2107 }
2108 bounds = &r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002109 }
reed@google.com4b226022011-01-11 18:32:13 +00002110
reedc83a2972015-07-16 07:40:45 -07002111 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002112
2113 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002114 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002115 }
2116
reed@google.com4e2b3d32011-04-07 14:18:59 +00002117 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002118}
2119
reed41af9662015-01-05 07:49:08 -08002120void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002121 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002122 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002123 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002124 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002125 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2126 return;
2127 }
2128 bounds = &oval;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002129 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002130
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002131 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002132
2133 while (iter.next()) {
2134 iter.fDevice->drawOval(iter, oval, looper.paint());
2135 }
2136
2137 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002138}
2139
reed41af9662015-01-05 07:49:08 -08002140void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002141 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002142 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002143 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002144 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002145 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2146 return;
2147 }
2148 bounds = &rrect.getBounds();
reed@google.com4ed0fb72012-12-12 20:48:18 +00002149 }
2150
2151 if (rrect.isRect()) {
2152 // call the non-virtual version
2153 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002154 return;
2155 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002156 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002157 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2158 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002159 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002160
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002161 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002162
2163 while (iter.next()) {
2164 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2165 }
2166
2167 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002168}
2169
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002170void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2171 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002172 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002173 const SkRect* bounds = nullptr;
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002174 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002175 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2176 return;
2177 }
2178 bounds = &outer.getBounds();
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002179 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002180
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002181 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002182
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002183 while (iter.next()) {
2184 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2185 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002186
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002187 LOOPER_END
2188}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002189
reed41af9662015-01-05 07:49:08 -08002190void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002191 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002192 if (!path.isFinite()) {
2193 return;
2194 }
2195
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002196 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002197 const SkRect* bounds = nullptr;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002198 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002199 const SkRect& pathBounds = path.getBounds();
senorblanco87e066e2015-10-28 11:23:36 -07002200 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2201 return;
2202 }
2203 bounds = &pathBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002204 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002205
2206 const SkRect& r = path.getBounds();
2207 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002208 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002209 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002210 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002211 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002212 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002213
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002214 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002215
2216 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002217 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002218 }
2219
reed@google.com4e2b3d32011-04-07 14:18:59 +00002220 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002221}
2222
reed262a71b2015-12-05 13:07:27 -08002223bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002224 if (!paint.getImageFilter()) {
2225 return false;
2226 }
2227
2228 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002229 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002230 return false;
2231 }
2232
2233 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2234 // Once we can filter and the filter will return a result larger than itself, we should be
2235 // able to remove this constraint.
2236 // skbug.com/4526
2237 //
2238 SkPoint pt;
2239 ctm.mapXY(x, y, &pt);
2240 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2241 return ir.contains(fMCRec->fRasterClip.getBounds());
2242}
2243
reeda85d4d02015-05-06 12:56:48 -07002244void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002245 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002246 SkRect bounds = SkRect::MakeXYWH(x, y,
2247 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002248 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002249 SkRect tmp = bounds;
2250 if (paint) {
2251 paint->computeFastBounds(tmp, &tmp);
2252 }
2253 if (this->quickReject(tmp)) {
2254 return;
2255 }
reeda85d4d02015-05-06 12:56:48 -07002256 }
halcanary9d524f22016-03-29 09:03:52 -07002257
reeda85d4d02015-05-06 12:56:48 -07002258 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002259 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002260 paint = lazy.init();
2261 }
reed262a71b2015-12-05 13:07:27 -08002262
reed129ed1c2016-02-22 06:42:31 -08002263 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2264 *paint);
2265 if (drawAsSprite && paint->getImageFilter()) {
2266 SkBitmap bitmap;
2267 if (!as_IB(image)->asBitmapForImageFilters(&bitmap)) {
2268 drawAsSprite = false;
2269 } else{
2270 // Until imagefilters are updated, they cannot handle any src type but N32...
2271 if (bitmap.info().colorType() != kN32_SkColorType || bitmap.info().isSRGB()) {
2272 drawAsSprite = false;
2273 }
2274 }
2275 }
2276
reed262a71b2015-12-05 13:07:27 -08002277 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
2278
reeda85d4d02015-05-06 12:56:48 -07002279 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002280 const SkPaint& pnt = looper.paint();
2281 if (drawAsSprite && pnt.getImageFilter()) {
2282 SkBitmap bitmap;
2283 if (as_IB(image)->asBitmapForImageFilters(&bitmap)) {
2284 SkPoint pt;
2285 iter.fMatrix->mapXY(x, y, &pt);
robertphillips2302de92016-03-24 07:26:32 -07002286 iter.fDevice->drawSpriteWithFilter(iter, bitmap,
2287 SkScalarRoundToInt(pt.fX),
2288 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002289 }
2290 } else {
2291 iter.fDevice->drawImage(iter, image, x, y, pnt);
2292 }
reeda85d4d02015-05-06 12:56:48 -07002293 }
halcanary9d524f22016-03-29 09:03:52 -07002294
reeda85d4d02015-05-06 12:56:48 -07002295 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002296}
2297
reed41af9662015-01-05 07:49:08 -08002298void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002299 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002300 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
halcanary96fcdcc2015-08-27 07:41:13 -07002301 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002302 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002303 if (paint) {
2304 paint->computeFastBounds(dst, &storage);
2305 }
2306 if (this->quickReject(storage)) {
2307 return;
2308 }
reeda85d4d02015-05-06 12:56:48 -07002309 }
2310 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002311 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002312 paint = lazy.init();
2313 }
halcanary9d524f22016-03-29 09:03:52 -07002314
senorblancoc41e7e12015-12-07 12:51:30 -08002315 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002316 image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002317
reeda85d4d02015-05-06 12:56:48 -07002318 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002319 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002320 }
halcanary9d524f22016-03-29 09:03:52 -07002321
reeda85d4d02015-05-06 12:56:48 -07002322 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002323}
2324
reed41af9662015-01-05 07:49:08 -08002325void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002326 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002327 SkDEBUGCODE(bitmap.validate();)
2328
reed33366972015-10-08 09:22:02 -07002329 if (bitmap.drawsNothing()) {
2330 return;
2331 }
2332
2333 SkLazyPaint lazy;
2334 if (nullptr == paint) {
2335 paint = lazy.init();
2336 }
2337
2338 const SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2339
2340 SkRect storage;
2341 const SkRect* bounds = nullptr;
2342 if (paint->canComputeFastBounds()) {
2343 bitmap.getBounds(&storage);
2344 matrix.mapRect(&storage);
senorblanco87e066e2015-10-28 11:23:36 -07002345 SkRect tmp = storage;
2346 if (this->quickReject(paint->computeFastBounds(tmp, &tmp))) {
2347 return;
2348 }
2349 bounds = &storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002350 }
reed@google.com4b226022011-01-11 18:32:13 +00002351
reed129ed1c2016-02-22 06:42:31 -08002352 bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(), bitmap.height(),
2353 *paint);
2354 if (drawAsSprite && paint->getImageFilter()) {
2355 // Until imagefilters are updated, they cannot handle any src type but N32...
2356 if (bitmap.info().colorType() != kN32_SkColorType || bitmap.info().isSRGB()) {
2357 drawAsSprite = false;
2358 }
2359 }
2360
reed262a71b2015-12-05 13:07:27 -08002361 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds)
reed33366972015-10-08 09:22:02 -07002362
2363 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002364 const SkPaint& pnt = looper.paint();
2365 if (drawAsSprite && pnt.getImageFilter()) {
2366 SkPoint pt;
2367 iter.fMatrix->mapXY(x, y, &pt);
robertphillips2302de92016-03-24 07:26:32 -07002368 iter.fDevice->drawSpriteWithFilter(iter, bitmap,
2369 SkScalarRoundToInt(pt.fX),
2370 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002371 } else {
2372 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
2373 }
reed33366972015-10-08 09:22:02 -07002374 }
reed262a71b2015-12-05 13:07:27 -08002375
reed33366972015-10-08 09:22:02 -07002376 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002377}
2378
reed@google.com9987ec32011-09-07 11:57:52 +00002379// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002380void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002381 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002382 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002383 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002384 return;
2385 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002386
halcanary96fcdcc2015-08-27 07:41:13 -07002387 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002388 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002389 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2390 return;
2391 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002392 }
reed@google.com3d608122011-11-21 15:16:16 +00002393
reed@google.com33535f32012-09-25 15:37:50 +00002394 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002395 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002396 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002397 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002398
senorblancoc41e7e12015-12-07 12:51:30 -08002399 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002400 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002401
reed@google.com33535f32012-09-25 15:37:50 +00002402 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002403 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002404 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002405
reed@google.com33535f32012-09-25 15:37:50 +00002406 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002407}
2408
reed41af9662015-01-05 07:49:08 -08002409void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002410 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002411 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002412 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002413 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002414}
2415
reed4c21dc52015-06-25 12:32:03 -07002416void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2417 const SkPaint* paint) {
2418 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
halcanary9d524f22016-03-29 09:03:52 -07002419
halcanary96fcdcc2015-08-27 07:41:13 -07002420 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002421 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002422 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2423 return;
2424 }
reed@google.com3d608122011-11-21 15:16:16 +00002425 }
halcanary9d524f22016-03-29 09:03:52 -07002426
reed4c21dc52015-06-25 12:32:03 -07002427 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002428 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002429 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002430 }
halcanary9d524f22016-03-29 09:03:52 -07002431
senorblancoc41e7e12015-12-07 12:51:30 -08002432 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002433
reed4c21dc52015-06-25 12:32:03 -07002434 while (iter.next()) {
2435 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002436 }
halcanary9d524f22016-03-29 09:03:52 -07002437
reed4c21dc52015-06-25 12:32:03 -07002438 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002439}
2440
reed41af9662015-01-05 07:49:08 -08002441void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2442 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002443 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002444 SkDEBUGCODE(bitmap.validate();)
2445
halcanary96fcdcc2015-08-27 07:41:13 -07002446 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002447 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002448 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2449 return;
2450 }
reed4c21dc52015-06-25 12:32:03 -07002451 }
halcanary9d524f22016-03-29 09:03:52 -07002452
reed4c21dc52015-06-25 12:32:03 -07002453 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002454 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002455 paint = lazy.init();
2456 }
halcanary9d524f22016-03-29 09:03:52 -07002457
senorblancoc41e7e12015-12-07 12:51:30 -08002458 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002459
reed4c21dc52015-06-25 12:32:03 -07002460 while (iter.next()) {
2461 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2462 }
halcanary9d524f22016-03-29 09:03:52 -07002463
reed4c21dc52015-06-25 12:32:03 -07002464 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002465}
2466
reed@google.comf67e4cf2011-03-15 20:56:58 +00002467class SkDeviceFilteredPaint {
2468public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002469 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002470 uint32_t filteredFlags = device->filterTextFlags(paint);
2471 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002472 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002473 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002474 fPaint = newPaint;
2475 } else {
2476 fPaint = &paint;
2477 }
2478 }
2479
reed@google.comf67e4cf2011-03-15 20:56:58 +00002480 const SkPaint& paint() const { return *fPaint; }
2481
2482private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002483 const SkPaint* fPaint;
2484 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002485};
2486
bungeman@google.com52c748b2011-08-22 21:30:43 +00002487void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2488 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002489 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002490 draw.fDevice->drawRect(draw, r, paint);
2491 } else {
2492 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002493 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002494 draw.fDevice->drawRect(draw, r, p);
2495 }
2496}
2497
2498void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2499 const char text[], size_t byteLength,
2500 SkScalar x, SkScalar y) {
halcanary96fcdcc2015-08-27 07:41:13 -07002501 SkASSERT(byteLength == 0 || text != nullptr);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002502
2503 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07002504 if (text == nullptr || byteLength == 0 ||
reed1e7f5e72016-04-27 07:49:17 -07002505 draw.fRC->isEmpty() ||
halcanary96fcdcc2015-08-27 07:41:13 -07002506 (paint.getAlpha() == 0 && paint.getXfermode() == nullptr)) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002507 return;
2508 }
2509
2510 SkScalar width = 0;
2511 SkPoint start;
2512
2513 start.set(0, 0); // to avoid warning
2514 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2515 SkPaint::kStrikeThruText_Flag)) {
2516 width = paint.measureText(text, byteLength);
2517
2518 SkScalar offsetX = 0;
2519 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2520 offsetX = SkScalarHalf(width);
2521 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2522 offsetX = width;
2523 }
2524 start.set(x - offsetX, y);
2525 }
2526
2527 if (0 == width) {
2528 return;
2529 }
2530
2531 uint32_t flags = paint.getFlags();
2532
2533 if (flags & (SkPaint::kUnderlineText_Flag |
2534 SkPaint::kStrikeThruText_Flag)) {
2535 SkScalar textSize = paint.getTextSize();
2536 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2537 SkRect r;
2538
2539 r.fLeft = start.fX;
2540 r.fRight = start.fX + width;
2541
2542 if (flags & SkPaint::kUnderlineText_Flag) {
2543 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2544 start.fY);
2545 r.fTop = offset;
2546 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002547 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002548 }
2549 if (flags & SkPaint::kStrikeThruText_Flag) {
2550 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2551 start.fY);
2552 r.fTop = offset;
2553 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002554 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002555 }
2556 }
2557}
2558
reed@google.come0d9ce82014-04-23 04:00:17 +00002559void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2560 const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002561 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002562
2563 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002564 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002565 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002566 DrawTextDecorations(iter, dfp.paint(),
2567 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002568 }
2569
reed@google.com4e2b3d32011-04-07 14:18:59 +00002570 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002571}
2572
reed@google.come0d9ce82014-04-23 04:00:17 +00002573void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2574 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002575 SkPoint textOffset = SkPoint::Make(0, 0);
2576
halcanary96fcdcc2015-08-27 07:41:13 -07002577 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002578
reed@android.com8a1c16f2008-12-17 15:59:43 +00002579 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002580 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002581 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002582 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002583 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002584
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::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2589 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002590
2591 SkPoint textOffset = SkPoint::Make(0, constY);
2592
halcanary96fcdcc2015-08-27 07:41:13 -07002593 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002594
reed@android.com8a1c16f2008-12-17 15:59:43 +00002595 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002596 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002597 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002598 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002599 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002600
reed@google.com4e2b3d32011-04-07 14:18:59 +00002601 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002602}
2603
reed@google.come0d9ce82014-04-23 04:00:17 +00002604void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2605 const SkMatrix* matrix, const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002606 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002607
reed@android.com8a1c16f2008-12-17 15:59:43 +00002608 while (iter.next()) {
2609 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002610 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002611 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002612
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002613 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002614}
2615
fmalita00d5c2c2014-08-21 08:53:26 -07002616void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2617 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002618
fmalita85d5eb92015-03-04 11:20:12 -08002619 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002620 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002621 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002622 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002623 SkRect tmp;
2624 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2625 return;
2626 }
2627 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002628 }
2629
fmalita024f9962015-03-03 19:08:17 -08002630 // We cannot filter in the looper as we normally do, because the paint is
2631 // incomplete at this point (text-related attributes are embedded within blob run paints).
2632 SkDrawFilter* drawFilter = fMCRec->fFilter;
halcanary96fcdcc2015-08-27 07:41:13 -07002633 fMCRec->fFilter = nullptr;
fmalita024f9962015-03-03 19:08:17 -08002634
fmalita85d5eb92015-03-04 11:20:12 -08002635 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002636
fmalitaaa1b9122014-08-28 14:32:24 -07002637 while (iter.next()) {
2638 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002639 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002640 }
2641
fmalitaaa1b9122014-08-28 14:32:24 -07002642 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002643
2644 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002645}
2646
reed@google.come0d9ce82014-04-23 04:00:17 +00002647// These will become non-virtual, so they always call the (virtual) onDraw... method
2648void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2649 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002650 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002651 this->onDrawText(text, byteLength, x, y, paint);
2652}
2653void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2654 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002655 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002656 this->onDrawPosText(text, byteLength, pos, paint);
2657}
2658void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2659 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002660 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002661 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2662}
2663void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2664 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002665 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002666 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2667}
fmalita00d5c2c2014-08-21 08:53:26 -07002668void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2669 const SkPaint& paint) {
reede3b38ce2016-01-08 09:18:44 -08002670 RETURN_ON_NULL(blob);
danakj9881d632014-11-26 12:41:06 -08002671 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
reede3b38ce2016-01-08 09:18:44 -08002672 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002673}
reed@google.come0d9ce82014-04-23 04:00:17 +00002674
reed41af9662015-01-05 07:49:08 -08002675void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2676 const SkPoint verts[], const SkPoint texs[],
2677 const SkColor colors[], SkXfermode* xmode,
2678 const uint16_t indices[], int indexCount,
2679 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002680 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
halcanary96fcdcc2015-08-27 07:41:13 -07002681 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
reed@google.com4b226022011-01-11 18:32:13 +00002682
reed@android.com8a1c16f2008-12-17 15:59:43 +00002683 while (iter.next()) {
2684 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002685 colors, xmode, indices, indexCount,
2686 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002687 }
reed@google.com4b226022011-01-11 18:32:13 +00002688
reed@google.com4e2b3d32011-04-07 14:18:59 +00002689 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002690}
2691
dandovb3c9d1c2014-08-12 08:34:29 -07002692void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2693 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002694 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
halcanary96fcdcc2015-08-27 07:41:13 -07002695 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002696 return;
2697 }
mtklein6cfa73a2014-08-13 13:33:49 -07002698
dandovecfff212014-08-04 10:02:00 -07002699 // Since a patch is always within the convex hull of the control points, we discard it when its
2700 // bounding rectangle is completely outside the current clip.
2701 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002702 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002703 if (this->quickReject(bounds)) {
2704 return;
2705 }
mtklein6cfa73a2014-08-13 13:33:49 -07002706
dandovb3c9d1c2014-08-12 08:34:29 -07002707 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2708}
2709
2710void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2711 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2712
halcanary96fcdcc2015-08-27 07:41:13 -07002713 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002714
dandovecfff212014-08-04 10:02:00 -07002715 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002716 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002717 }
mtklein6cfa73a2014-08-13 13:33:49 -07002718
dandovecfff212014-08-04 10:02:00 -07002719 LOOPER_END
2720}
2721
reeda8db7282015-07-07 10:22:31 -07002722void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
reede3b38ce2016-01-08 09:18:44 -08002723 RETURN_ON_NULL(dr);
2724 if (x || y) {
2725 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2726 this->onDrawDrawable(dr, &matrix);
2727 } else {
2728 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002729 }
2730}
2731
reeda8db7282015-07-07 10:22:31 -07002732void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reede3b38ce2016-01-08 09:18:44 -08002733 RETURN_ON_NULL(dr);
2734 if (matrix && matrix->isIdentity()) {
2735 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002736 }
reede3b38ce2016-01-08 09:18:44 -08002737 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002738}
2739
2740void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2741 SkRect bounds = dr->getBounds();
2742 if (matrix) {
2743 matrix->mapRect(&bounds);
2744 }
2745 if (this->quickReject(bounds)) {
2746 return;
2747 }
2748 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002749}
2750
reed71c3c762015-06-24 10:29:17 -07002751void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2752 const SkColor colors[], int count, SkXfermode::Mode mode,
2753 const SkRect* cull, const SkPaint* paint) {
2754 if (cull && this->quickReject(*cull)) {
2755 return;
2756 }
2757
2758 SkPaint pnt;
2759 if (paint) {
2760 pnt = *paint;
2761 }
halcanary9d524f22016-03-29 09:03:52 -07002762
halcanary96fcdcc2015-08-27 07:41:13 -07002763 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr)
reed71c3c762015-06-24 10:29:17 -07002764 while (iter.next()) {
2765 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, mode, pnt);
2766 }
2767 LOOPER_END
2768}
2769
reedf70b5312016-03-04 16:36:20 -08002770void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2771 SkASSERT(key);
2772
2773 SkPaint paint;
2774 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, nullptr)
2775 while (iter.next()) {
2776 iter.fDevice->drawAnnotation(iter, rect, key, value);
2777 }
2778 LOOPER_END
2779}
2780
reed@android.com8a1c16f2008-12-17 15:59:43 +00002781//////////////////////////////////////////////////////////////////////////////
2782// These methods are NOT virtual, and therefore must call back into virtual
2783// methods, rather than actually drawing themselves.
2784//////////////////////////////////////////////////////////////////////////////
2785
2786void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002787 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002788 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002789 SkPaint paint;
2790
2791 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002792 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002793 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002794 }
2795 this->drawPaint(paint);
2796}
2797
reed@android.com845fdac2009-06-23 03:01:32 +00002798void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002799 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002800 SkPaint paint;
2801
2802 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002803 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002804 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002805 }
2806 this->drawPaint(paint);
2807}
2808
2809void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002810 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002811 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002812
reed@android.com8a1c16f2008-12-17 15:59:43 +00002813 pt.set(x, y);
2814 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2815}
2816
2817void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002818 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002819 SkPoint pt;
2820 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002821
reed@android.com8a1c16f2008-12-17 15:59:43 +00002822 pt.set(x, y);
2823 paint.setColor(color);
2824 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2825}
2826
2827void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2828 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002829 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002830 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002831
reed@android.com8a1c16f2008-12-17 15:59:43 +00002832 pts[0].set(x0, y0);
2833 pts[1].set(x1, y1);
2834 this->drawPoints(kLines_PointMode, 2, pts, paint);
2835}
2836
2837void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2838 SkScalar right, SkScalar bottom,
2839 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002840 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002841 SkRect r;
2842
2843 r.set(left, top, right, bottom);
2844 this->drawRect(r, paint);
2845}
2846
2847void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2848 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002849 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002850 if (radius < 0) {
2851 radius = 0;
2852 }
2853
2854 SkRect r;
2855 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002856 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002857}
2858
2859void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2860 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002861 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002862 if (rx > 0 && ry > 0) {
2863 if (paint.canComputeFastBounds()) {
2864 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002865 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002866 return;
2867 }
2868 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002869 SkRRect rrect;
2870 rrect.setRectXY(r, rx, ry);
2871 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002872 } else {
2873 this->drawRect(r, paint);
2874 }
2875}
2876
reed@android.com8a1c16f2008-12-17 15:59:43 +00002877void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2878 SkScalar sweepAngle, bool useCenter,
2879 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002880 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002881 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2882 this->drawOval(oval, paint);
2883 } else {
2884 SkPath path;
2885 if (useCenter) {
2886 path.moveTo(oval.centerX(), oval.centerY());
2887 }
2888 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2889 if (useCenter) {
2890 path.close();
2891 }
2892 this->drawPath(path, paint);
2893 }
2894}
2895
2896void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2897 const SkPath& path, SkScalar hOffset,
2898 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002899 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002900 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002901
reed@android.com8a1c16f2008-12-17 15:59:43 +00002902 matrix.setTranslate(hOffset, vOffset);
2903 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2904}
2905
reed@android.comf76bacf2009-05-13 14:00:33 +00002906///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07002907
2908/**
2909 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2910 * against the playback cost of recursing into the subpicture to get at its actual ops.
2911 *
2912 * For now we pick a conservatively small value, though measurement (and other heuristics like
2913 * the type of ops contained) may justify changing this value.
2914 */
2915#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07002916
reedd5fa1a42014-08-09 11:08:05 -07002917void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002918 RETURN_ON_NULL(picture);
2919
reed1c2c4412015-04-30 13:09:24 -07002920 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
reede3b38ce2016-01-08 09:18:44 -08002921 if (matrix && matrix->isIdentity()) {
2922 matrix = nullptr;
2923 }
2924 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2925 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2926 picture->playback(this);
2927 } else {
2928 this->onDrawPicture(picture, matrix, paint);
reedd5fa1a42014-08-09 11:08:05 -07002929 }
2930}
robertphillips9b14f262014-06-04 05:40:44 -07002931
reedd5fa1a42014-08-09 11:08:05 -07002932void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2933 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07002934 if (!paint || paint->canComputeFastBounds()) {
2935 SkRect bounds = picture->cullRect();
2936 if (paint) {
2937 paint->computeFastBounds(bounds, &bounds);
2938 }
2939 if (matrix) {
2940 matrix->mapRect(&bounds);
2941 }
2942 if (this->quickReject(bounds)) {
2943 return;
2944 }
2945 }
2946
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002947 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002948 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002949 // Canvas has to first give the device the opportunity to render
2950 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002951 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002952 return; // the device has rendered the entire picture
2953 }
2954 }
2955
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002956 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07002957 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002958}
2959
reed@android.com8a1c16f2008-12-17 15:59:43 +00002960///////////////////////////////////////////////////////////////////////////////
2961///////////////////////////////////////////////////////////////////////////////
2962
2963SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
bungeman99fe8222015-08-20 07:57:51 -07002964 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002965
2966 SkASSERT(canvas);
2967
2968 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2969 fDone = !fImpl->next();
2970}
2971
2972SkCanvas::LayerIter::~LayerIter() {
2973 fImpl->~SkDrawIter();
2974}
2975
2976void SkCanvas::LayerIter::next() {
2977 fDone = !fImpl->next();
2978}
2979
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002980SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002981 return fImpl->getDevice();
2982}
2983
2984const SkMatrix& SkCanvas::LayerIter::matrix() const {
2985 return fImpl->getMatrix();
2986}
2987
2988const SkPaint& SkCanvas::LayerIter::paint() const {
2989 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07002990 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002991 paint = &fDefaultPaint;
2992 }
2993 return *paint;
2994}
2995
reed1e7f5e72016-04-27 07:49:17 -07002996const SkRasterClip& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002997int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2998int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002999
3000///////////////////////////////////////////////////////////////////////////////
3001
fmalitac3b589a2014-06-05 12:40:07 -07003002SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003003
3004///////////////////////////////////////////////////////////////////////////////
3005
3006static bool supported_for_raster_canvas(const SkImageInfo& info) {
3007 switch (info.alphaType()) {
3008 case kPremul_SkAlphaType:
3009 case kOpaque_SkAlphaType:
3010 break;
3011 default:
3012 return false;
3013 }
3014
3015 switch (info.colorType()) {
3016 case kAlpha_8_SkColorType:
3017 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00003018 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003019 break;
3020 default:
3021 return false;
3022 }
3023
3024 return true;
3025}
3026
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003027SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
3028 if (!supported_for_raster_canvas(info)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003029 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003030 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003031
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003032 SkBitmap bitmap;
3033 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003034 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003035 }
halcanary385fe4d2015-08-26 13:07:48 -07003036 return new SkCanvas(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003037}
reedd5fa1a42014-08-09 11:08:05 -07003038
3039///////////////////////////////////////////////////////////////////////////////
3040
3041SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003042 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07003043 : fCanvas(canvas)
3044 , fSaveCount(canvas->getSaveCount())
3045{
bsalomon49f085d2014-09-05 13:34:00 -07003046 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003047 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07003048 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003049 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07003050 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003051 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07003052 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003053 canvas->save();
3054 }
mtklein6cfa73a2014-08-13 13:33:49 -07003055
bsalomon49f085d2014-09-05 13:34:00 -07003056 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003057 canvas->concat(*matrix);
3058 }
3059}
3060
3061SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
3062 fCanvas->restoreToCount(fSaveCount);
3063}
reede8f30622016-03-23 18:59:25 -07003064
3065#ifdef SK_SUPPORT_LEGACY_NEW_SURFACE_API
3066SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
3067 return this->makeSurface(info, props).release();
3068}
3069#endif