blob: 905849254c7fcf5ad6689fe9354cc326ff395992 [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 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
353 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000354 fDevice = rec->fDevice;
reed41e010c2015-06-09 12:16:53 -0700355 if (!fDevice->accessPixels(&fDst)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700356 fDst.reset(fDevice->imageInfo(), nullptr, 0);
reed41e010c2015-06-09 12:16:53 -0700357 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000358 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000359 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000360
361 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700362 // fCurrLayer may be nullptr now
reed@android.com199f1082009-06-10 02:12:47 +0000363
reed@android.com8a1c16f2008-12-17 15:59:43 +0000364 return true;
365 }
366 return false;
367 }
reed@google.com4b226022011-01-11 18:32:13 +0000368
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000369 SkBaseDevice* getDevice() const { return fDevice; }
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; }
373 const SkRegion& getClip() const { return *fClip; }
374 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000375
reed@android.com8a1c16f2008-12-17 15:59:43 +0000376private:
377 SkCanvas* fCanvas;
378 const DeviceCM* fCurrLayer;
379 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000380 SkBool8 fSkipEmptyClips;
381
382 typedef SkDraw INHERITED;
383};
384
385/////////////////////////////////////////////////////////////////////////////
386
reeddbc3cef2015-04-29 12:18:57 -0700387static SkPaint* set_if_needed(SkLazyPaint* lazy, const SkPaint& orig) {
388 return lazy->isValid() ? lazy->get() : lazy->set(orig);
389}
390
391/**
392 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700393 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700394 */
reedd053ce92016-03-22 10:17:23 -0700395static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700396 SkImageFilter* imgf = paint.getImageFilter();
397 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700398 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700399 }
400
reedd053ce92016-03-22 10:17:23 -0700401 SkColorFilter* imgCFPtr;
402 if (!imgf->asAColorFilter(&imgCFPtr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700403 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700404 }
reedd053ce92016-03-22 10:17:23 -0700405 sk_sp<SkColorFilter> imgCF(imgCFPtr);
reeddbc3cef2015-04-29 12:18:57 -0700406
407 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700408 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700409 // there is no existing paint colorfilter, so we can just return the imagefilter's
410 return imgCF;
411 }
412
413 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
414 // and we need to combine them into a single colorfilter.
reedd053ce92016-03-22 10:17:23 -0700415 return SkColorFilter::MakeComposeFilter(std::move(imgCF), sk_ref_sp(paintCF));
reeddbc3cef2015-04-29 12:18:57 -0700416}
417
senorblanco87e066e2015-10-28 11:23:36 -0700418/**
419 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
420 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
421 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
422 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
423 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
424 * conservative "effective" bounds based on the settings in the paint... with one exception. This
425 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
426 * deliberately ignored.
427 */
428static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
429 const SkRect& rawBounds,
430 SkRect* storage) {
431 SkPaint tmpUnfiltered(paint);
432 tmpUnfiltered.setImageFilter(nullptr);
433 if (tmpUnfiltered.canComputeFastBounds()) {
434 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
435 } else {
436 return rawBounds;
437 }
438}
439
reed@android.com8a1c16f2008-12-17 15:59:43 +0000440class AutoDrawLooper {
441public:
senorblanco87e066e2015-10-28 11:23:36 -0700442 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
443 // paint. It's used to determine the size of the offscreen layer for filters.
444 // If null, the clip will be used instead.
reed4a8126e2014-09-22 07:29:03 -0700445 AutoDrawLooper(SkCanvas* canvas, const SkSurfaceProps& props, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000446 bool skipLayerForImageFilter = false,
senorblanco87e066e2015-10-28 11:23:36 -0700447 const SkRect* rawBounds = nullptr) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000448 fCanvas = canvas;
fmalita53d9f1c2016-01-25 06:23:54 -0800449#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450 fFilter = canvas->getDrawFilter();
fmalita77650002016-01-21 18:47:11 -0800451#else
452 fFilter = nullptr;
453#endif
reed4a8126e2014-09-22 07:29:03 -0700454 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000455 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700456 fTempLayerForImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000457 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000458
reedd053ce92016-03-22 10:17:23 -0700459 auto simplifiedCF = image_to_color_filter(fOrigPaint);
reeddbc3cef2015-04-29 12:18:57 -0700460 if (simplifiedCF) {
461 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reedd053ce92016-03-22 10:17:23 -0700462 paint->setColorFilter(std::move(simplifiedCF));
halcanary96fcdcc2015-08-27 07:41:13 -0700463 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700464 fPaint = paint;
465 }
466
467 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700468 /**
469 * We implement ImageFilters for a given draw by creating a layer, then applying the
470 * imagefilter to the pixels of that layer (its backing surface/image), and then
471 * we call restore() to xfer that layer to the main canvas.
472 *
473 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
474 * 2. Generate the src pixels:
475 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
476 * return (fPaint). We then draw the primitive (using srcover) into a cleared
477 * buffer/surface.
478 * 3. Restore the layer created in #1
479 * The imagefilter is passed the buffer/surface from the layer (now filled with the
480 * src pixels of the primitive). It returns a new "filtered" buffer, which we
481 * draw onto the previous layer using the xfermode from the original paint.
482 */
reed@google.com8926b162012-03-23 15:36:36 +0000483 SkPaint tmp;
reeddbc3cef2015-04-29 12:18:57 -0700484 tmp.setImageFilter(fPaint->getImageFilter());
reedcfb6bdf2016-03-29 11:32:50 -0700485 tmp.setXfermode(sk_ref_sp(fPaint->getXfermode()));
senorblanco87e066e2015-10-28 11:23:36 -0700486 SkRect storage;
487 if (rawBounds) {
488 // Make rawBounds include all paint outsets except for those due to image filters.
489 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
490 }
reedbfd5f172016-01-07 11:28:08 -0800491 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &tmp),
reed76033be2015-03-14 10:54:31 -0700492 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700493 fTempLayerForImageFilter = true;
494 // we remove the imagefilter/xfermode inside doNext()
reed@google.com8926b162012-03-23 15:36:36 +0000495 }
496
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000497 if (SkDrawLooper* looper = paint.getLooper()) {
498 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
499 looper->contextSize());
500 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000501 fIsSimple = false;
502 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700503 fLooperContext = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000504 // can we be marked as simple?
reed5c476fb2015-04-20 08:04:21 -0700505 fIsSimple = !fFilter && !fTempLayerForImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000506 }
piotaixrb5fae932014-09-24 13:03:30 -0700507
reed4a8126e2014-09-22 07:29:03 -0700508 uint32_t oldFlags = paint.getFlags();
509 fNewPaintFlags = filter_paint_flags(props, oldFlags);
510 if (fIsSimple && (fNewPaintFlags != oldFlags)) {
reeddbc3cef2015-04-29 12:18:57 -0700511 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700512 paint->setFlags(fNewPaintFlags);
513 fPaint = paint;
514 // if we're not simple, doNext() will take care of calling setFlags()
515 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000516 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000517
reed@android.com8a1c16f2008-12-17 15:59:43 +0000518 ~AutoDrawLooper() {
reed5c476fb2015-04-20 08:04:21 -0700519 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000520 fCanvas->internalRestore();
521 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000522 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000523 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000524
reed@google.com4e2b3d32011-04-07 14:18:59 +0000525 const SkPaint& paint() const {
526 SkASSERT(fPaint);
527 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000528 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000529
reed@google.com129ec222012-05-15 13:24:09 +0000530 bool next(SkDrawFilter::Type drawType) {
531 if (fDone) {
532 return false;
533 } else if (fIsSimple) {
534 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000535 return !fPaint->nothingToDraw();
536 } else {
537 return this->doNext(drawType);
538 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000539 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000540
reed@android.com8a1c16f2008-12-17 15:59:43 +0000541private:
reeddbc3cef2015-04-29 12:18:57 -0700542 SkLazyPaint fLazyPaintInit; // base paint storage in case we need to modify it
543 SkLazyPaint fLazyPaintPerLooper; // per-draw-looper storage, so the looper can modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000544 SkCanvas* fCanvas;
545 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000546 SkDrawFilter* fFilter;
547 const SkPaint* fPaint;
548 int fSaveCount;
reed4a8126e2014-09-22 07:29:03 -0700549 uint32_t fNewPaintFlags;
reed5c476fb2015-04-20 08:04:21 -0700550 bool fTempLayerForImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000551 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000552 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000553 SkDrawLooper::Context* fLooperContext;
554 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000555
556 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000557};
558
reed@google.com129ec222012-05-15 13:24:09 +0000559bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
halcanary96fcdcc2015-08-27 07:41:13 -0700560 fPaint = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000561 SkASSERT(!fIsSimple);
reed5c476fb2015-04-20 08:04:21 -0700562 SkASSERT(fLooperContext || fFilter || fTempLayerForImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000563
reeddbc3cef2015-04-29 12:18:57 -0700564 SkPaint* paint = fLazyPaintPerLooper.set(fLazyPaintInit.isValid() ?
565 *fLazyPaintInit.get() : fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700566 paint->setFlags(fNewPaintFlags);
reed@google.com129ec222012-05-15 13:24:09 +0000567
reed5c476fb2015-04-20 08:04:21 -0700568 if (fTempLayerForImageFilter) {
halcanary96fcdcc2015-08-27 07:41:13 -0700569 paint->setImageFilter(nullptr);
570 paint->setXfermode(nullptr);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000571 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000572
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000573 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000574 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000575 return false;
576 }
577 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000578 if (!fFilter->filter(paint, drawType)) {
579 fDone = true;
580 return false;
581 }
halcanary96fcdcc2015-08-27 07:41:13 -0700582 if (nullptr == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000583 // no looper means we only draw once
584 fDone = true;
585 }
586 }
587 fPaint = paint;
588
589 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000590 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000591 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000592 }
593
594 // call this after any possible paint modifiers
595 if (fPaint->nothingToDraw()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700596 fPaint = nullptr;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000597 return false;
598 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000599 return true;
600}
601
reed@android.com8a1c16f2008-12-17 15:59:43 +0000602////////// macros to place around the internal draw calls //////////////////
603
reed262a71b2015-12-05 13:07:27 -0800604#define LOOPER_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
605 this->predrawNotify(); \
606 AutoDrawLooper looper(this, fProps, paint, skipLayerForFilter, bounds); \
607 while (looper.next(SkDrawFilter::kBitmap_Type)) { \
608 SkDrawIter iter(this);
609
610
reed@google.com8926b162012-03-23 15:36:36 +0000611#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000612 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700613 AutoDrawLooper looper(this, fProps, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000614 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000615 SkDrawIter iter(this);
616
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000617#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000618 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700619 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000620 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000621 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000622
reedc83a2972015-07-16 07:40:45 -0700623#define LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, type, bounds, auxOpaque) \
624 this->predrawNotify(bounds, &paint, auxOpaque); \
625 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
626 while (looper.next(type)) { \
627 SkDrawIter iter(this);
628
reed@google.com4e2b3d32011-04-07 14:18:59 +0000629#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000630
631////////////////////////////////////////////////////////////////////////////
632
mtkleinfeaadee2015-04-08 11:25:48 -0700633void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
634 this->restoreToCount(1);
635 fCachedLocalClipBounds.setEmpty();
636 fCachedLocalClipBoundsDirty = true;
637 fClipStack->reset();
638 fMCRec->reset(bounds);
639
640 // We're peering through a lot of structs here. Only at this scope do we
641 // know that the device is an SkBitmapDevice (really an SkNoPixelsBitmapDevice).
642 static_cast<SkBitmapDevice*>(fMCRec->fLayer->fDevice)->setNewSize(bounds.size());
643}
644
reedd9544982014-09-09 18:46:22 -0700645SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
reed42b73eb2015-11-20 13:42:42 -0800646 if (device && device->forceConservativeRasterClip()) {
647 flags = InitFlags(flags | kConservativeRasterClip_InitFlag);
648 }
649 // Since init() is only called once by our constructors, it is safe to perform this
650 // const-cast.
651 *const_cast<bool*>(&fConservativeRasterClip) = SkToBool(flags & kConservativeRasterClip_InitFlag);
652
reed@google.comc0784db2013-12-13 21:16:12 +0000653 fCachedLocalClipBounds.setEmpty();
654 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000655 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000656 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700657 fDeviceCMDirty = true;
reed2ff1fce2014-12-11 07:07:37 -0800658 fSaveCount = 1;
halcanary96fcdcc2015-08-27 07:41:13 -0700659 fMetaData = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000660
halcanary385fe4d2015-08-26 13:07:48 -0700661 fClipStack.reset(new SkClipStack);
reed687fa1c2015-04-07 08:00:56 -0700662
reed@android.com8a1c16f2008-12-17 15:59:43 +0000663 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700664 new (fMCRec) MCRec(fConservativeRasterClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000665
reeda499f902015-05-01 09:34:31 -0700666 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
667 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
reed8c30a812016-04-20 16:36:51 -0700668 new (fDeviceCMStorage) DeviceCM(nullptr, nullptr, nullptr, fConservativeRasterClip, false,
669 fMCRec->fMatrix);
reedb679ca82015-04-07 04:40:48 -0700670
reed@android.com8a1c16f2008-12-17 15:59:43 +0000671 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000672
halcanary96fcdcc2015-08-27 07:41:13 -0700673 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000674
reedf92c8662014-08-18 08:02:43 -0700675 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700676 // The root device and the canvas should always have the same pixel geometry
677 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reedf92c8662014-08-18 08:02:43 -0700678 device->onAttachToCanvas(this);
679 fMCRec->fLayer->fDevice = SkRef(device);
reed78e27682014-11-19 08:04:34 -0800680 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
reedf92c8662014-08-18 08:02:43 -0700681 }
682 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000683}
684
reed@google.comcde92112011-07-06 20:00:52 +0000685SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000686 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700687 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800688 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000689{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000690 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000691
halcanary96fcdcc2015-08-27 07:41:13 -0700692 this->init(nullptr, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000693}
694
reedd9544982014-09-09 18:46:22 -0700695static SkBitmap make_nopixels(int width, int height) {
696 SkBitmap bitmap;
697 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
698 return bitmap;
699}
700
701class SkNoPixelsBitmapDevice : public SkBitmapDevice {
702public:
robertphillipsfcf78292015-06-19 11:49:52 -0700703 SkNoPixelsBitmapDevice(const SkIRect& bounds, const SkSurfaceProps& surfaceProps)
704 : INHERITED(make_nopixels(bounds.width(), bounds.height()), surfaceProps)
reed78e27682014-11-19 08:04:34 -0800705 {
706 this->setOrigin(bounds.x(), bounds.y());
707 }
reedd9544982014-09-09 18:46:22 -0700708
709private:
piotaixrb5fae932014-09-24 13:03:30 -0700710
reedd9544982014-09-09 18:46:22 -0700711 typedef SkBitmapDevice INHERITED;
712};
713
reed96a857e2015-01-25 10:33:58 -0800714SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000715 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800716 , fProps(SkSurfacePropsCopyOrDefault(props))
reed42b73eb2015-11-20 13:42:42 -0800717 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000718{
719 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700720
halcanary385fe4d2015-08-26 13:07:48 -0700721 this->init(new SkNoPixelsBitmapDevice(SkIRect::MakeWH(width, height), fProps),
722 kDefault_InitFlags)->unref();
reedd9544982014-09-09 18:46:22 -0700723}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000724
reed78e27682014-11-19 08:04:34 -0800725SkCanvas::SkCanvas(const SkIRect& bounds, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700726 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700727 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800728 , fConservativeRasterClip(false)
reedd9544982014-09-09 18:46:22 -0700729{
730 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700731
halcanary385fe4d2015-08-26 13:07:48 -0700732 this->init(new SkNoPixelsBitmapDevice(bounds, fProps), flags)->unref();
reedd9544982014-09-09 18:46:22 -0700733}
734
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000735SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000736 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700737 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800738 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000739{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000740 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700741
reedd9544982014-09-09 18:46:22 -0700742 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000743}
744
robertphillipsfcf78292015-06-19 11:49:52 -0700745SkCanvas::SkCanvas(SkBaseDevice* device, InitFlags flags)
746 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700747 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800748 , fConservativeRasterClip(false)
robertphillipsfcf78292015-06-19 11:49:52 -0700749{
750 inc_canvas();
751
752 this->init(device, flags);
753}
754
reed4a8126e2014-09-22 07:29:03 -0700755SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700756 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700757 , fProps(props)
reed42b73eb2015-11-20 13:42:42 -0800758 , fConservativeRasterClip(false)
reed3716fd02014-09-21 09:39:55 -0700759{
760 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700761
halcanary385fe4d2015-08-26 13:07:48 -0700762 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700763 this->init(device, kDefault_InitFlags);
764}
reed29c857d2014-09-21 10:25:07 -0700765
reed4a8126e2014-09-22 07:29:03 -0700766SkCanvas::SkCanvas(const SkBitmap& bitmap)
767 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
768 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800769 , fConservativeRasterClip(false)
reed4a8126e2014-09-22 07:29:03 -0700770{
771 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700772
halcanary385fe4d2015-08-26 13:07:48 -0700773 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700774 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000775}
776
777SkCanvas::~SkCanvas() {
778 // free up the contents of our deque
779 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000780
reed@android.com8a1c16f2008-12-17 15:59:43 +0000781 this->internalRestore(); // restore the last, since we're going away
782
halcanary385fe4d2015-08-26 13:07:48 -0700783 delete fMetaData;
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000784
reed@android.com8a1c16f2008-12-17 15:59:43 +0000785 dec_canvas();
786}
787
fmalita53d9f1c2016-01-25 06:23:54 -0800788#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000789SkDrawFilter* SkCanvas::getDrawFilter() const {
790 return fMCRec->fFilter;
791}
792
793SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
reed51985e32015-04-11 08:04:56 -0700794 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000795 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
796 return filter;
797}
fmalita77650002016-01-21 18:47:11 -0800798#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000799
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000800SkMetaData& SkCanvas::getMetaData() {
801 // metadata users are rare, so we lazily allocate it. If that changes we
802 // can decide to just make it a field in the device (rather than a ptr)
halcanary96fcdcc2015-08-27 07:41:13 -0700803 if (nullptr == fMetaData) {
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000804 fMetaData = new SkMetaData;
805 }
806 return *fMetaData;
807}
808
reed@android.com8a1c16f2008-12-17 15:59:43 +0000809///////////////////////////////////////////////////////////////////////////////
810
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000811void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000812 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000813 if (device) {
814 device->flush();
815 }
816}
817
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000818SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000819 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000820 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
821}
822
senorblancoafc7cce2016-02-02 18:44:15 -0800823SkIRect SkCanvas::getTopLayerBounds() const {
824 SkBaseDevice* d = this->getTopDevice();
825 if (!d) {
826 return SkIRect::MakeEmpty();
827 }
828 return SkIRect::MakeXYWH(d->getOrigin().x(), d->getOrigin().y(), d->width(), d->height());
829}
830
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000831SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000832 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000833 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000834 SkASSERT(rec && rec->fLayer);
835 return rec->fLayer->fDevice;
836}
837
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000838SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000839 if (updateMatrixClip) {
840 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
841 }
reed@google.com9266fed2011-03-30 00:18:03 +0000842 return fMCRec->fTopLayer->fDevice;
843}
844
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000845bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
846 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
847 return false;
848 }
849
850 bool weAllocated = false;
halcanary96fcdcc2015-08-27 07:41:13 -0700851 if (nullptr == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700852 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000853 return false;
854 }
855 weAllocated = true;
856 }
857
reedcf01e312015-05-23 19:14:51 -0700858 SkAutoPixmapUnlock unlocker;
859 if (bitmap->requestLock(&unlocker)) {
860 const SkPixmap& pm = unlocker.pixmap();
861 if (this->readPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), x, y)) {
862 return true;
863 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000864 }
865
866 if (weAllocated) {
halcanary96fcdcc2015-08-27 07:41:13 -0700867 bitmap->setPixelRef(nullptr);
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000868 }
869 return false;
870}
reed@google.com51df9e32010-12-23 19:29:18 +0000871
bsalomon@google.comc6980972011-11-02 19:57:21 +0000872bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000873 SkIRect r = srcRect;
874 const SkISize size = this->getBaseLayerSize();
875 if (!r.intersect(0, 0, size.width(), size.height())) {
876 bitmap->reset();
877 return false;
878 }
879
reed84825042014-09-02 12:50:45 -0700880 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000881 // bitmap will already be reset.
882 return false;
883 }
884 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
885 bitmap->reset();
886 return false;
887 }
888 return true;
889}
890
reed96472de2014-12-10 09:53:42 -0800891bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000892 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000893 if (!device) {
894 return false;
895 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000896 const SkISize size = this->getBaseLayerSize();
mtkleinf0f14112014-12-12 08:46:25 -0800897
reed96472de2014-12-10 09:53:42 -0800898 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
899 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000900 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000901 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000902
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000903 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800904 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000905}
906
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000907bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
908 if (bitmap.getTexture()) {
909 return false;
910 }
reedcf01e312015-05-23 19:14:51 -0700911
912 SkAutoPixmapUnlock unlocker;
913 if (bitmap.requestLock(&unlocker)) {
914 const SkPixmap& pm = unlocker.pixmap();
915 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000916 }
917 return false;
918}
919
920bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
921 int x, int y) {
922 switch (origInfo.colorType()) {
923 case kUnknown_SkColorType:
924 case kIndex_8_SkColorType:
925 return false;
926 default:
927 break;
928 }
halcanary96fcdcc2015-08-27 07:41:13 -0700929 if (nullptr == pixels || rowBytes < origInfo.minRowBytes()) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000930 return false;
931 }
932
933 const SkISize size = this->getBaseLayerSize();
934 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
935 if (!target.intersect(0, 0, size.width(), size.height())) {
936 return false;
937 }
938
939 SkBaseDevice* device = this->getDevice();
940 if (!device) {
941 return false;
942 }
943
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000944 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700945 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000946
947 // if x or y are negative, then we have to adjust pixels
948 if (x > 0) {
949 x = 0;
950 }
951 if (y > 0) {
952 y = 0;
953 }
954 // here x,y are either 0 or negative
955 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
956
reed4af35f32014-06-27 17:47:49 -0700957 // Tell our owning surface to bump its generation ID
reedc83a2972015-07-16 07:40:45 -0700958 const bool completeOverwrite = info.dimensions() == size;
959 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700960
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000961 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000962 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000963}
reed@google.com51df9e32010-12-23 19:29:18 +0000964
junov@google.com4370aed2012-01-18 16:21:08 +0000965SkCanvas* SkCanvas::canvasForDrawIter() {
966 return this;
967}
968
reed@android.com8a1c16f2008-12-17 15:59:43 +0000969//////////////////////////////////////////////////////////////////////////////
970
reed@android.com8a1c16f2008-12-17 15:59:43 +0000971void SkCanvas::updateDeviceCMCache() {
972 if (fDeviceCMDirty) {
973 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700974 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000975 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000976
halcanary96fcdcc2015-08-27 07:41:13 -0700977 if (nullptr == layer->fNext) { // only one layer
978 layer->updateMC(totalMatrix, totalClip, *fClipStack, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000979 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000980 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000981 do {
reed687fa1c2015-04-07 08:00:56 -0700982 layer->updateMC(totalMatrix, clip, *fClipStack, &clip);
halcanary96fcdcc2015-08-27 07:41:13 -0700983 } while ((layer = layer->fNext) != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000984 }
985 fDeviceCMDirty = false;
986 }
987}
988
reed@android.com8a1c16f2008-12-17 15:59:43 +0000989///////////////////////////////////////////////////////////////////////////////
990
reed2ff1fce2014-12-11 07:07:37 -0800991void SkCanvas::checkForDeferredSave() {
992 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800993 this->doSave();
994 }
995}
996
reedf0090cb2014-11-26 08:55:51 -0800997int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800998#ifdef SK_DEBUG
999 int count = 0;
1000 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
1001 for (;;) {
1002 const MCRec* rec = (const MCRec*)iter.next();
1003 if (!rec) {
1004 break;
1005 }
1006 count += 1 + rec->fDeferredSaveCount;
1007 }
1008 SkASSERT(count == fSaveCount);
1009#endif
1010 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -08001011}
1012
1013int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -08001014 fSaveCount += 1;
1015 fMCRec->fDeferredSaveCount += 1;
1016 return this->getSaveCount() - 1; // return our prev value
1017}
1018
1019void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -08001020 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -07001021
1022 SkASSERT(fMCRec->fDeferredSaveCount > 0);
1023 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001024 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -08001025}
1026
1027void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -08001028 if (fMCRec->fDeferredSaveCount > 0) {
1029 SkASSERT(fSaveCount > 1);
1030 fSaveCount -= 1;
1031 fMCRec->fDeferredSaveCount -= 1;
1032 } else {
1033 // check for underflow
1034 if (fMCStack.count() > 1) {
1035 this->willRestore();
1036 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -07001037 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001038 this->internalRestore();
1039 this->didRestore();
1040 }
reedf0090cb2014-11-26 08:55:51 -08001041 }
1042}
1043
1044void SkCanvas::restoreToCount(int count) {
1045 // sanity check
1046 if (count < 1) {
1047 count = 1;
1048 }
mtkleinf0f14112014-12-12 08:46:25 -08001049
reedf0090cb2014-11-26 08:55:51 -08001050 int n = this->getSaveCount() - count;
1051 for (int i = 0; i < n; ++i) {
1052 this->restore();
1053 }
1054}
1055
reed2ff1fce2014-12-11 07:07:37 -08001056void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001057 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -07001058 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +00001059 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +00001060
reed687fa1c2015-04-07 08:00:56 -07001061 fClipStack->save();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001062}
1063
reed4960eee2015-12-18 07:09:18 -08001064bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
reed@google.comb93ba452014-03-10 19:47:58 +00001065#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001066 return !(saveLayerFlags & SkCanvas::kDontClipToLayer_PrivateSaveLayerFlag);
reed@google.comb93ba452014-03-10 19:47:58 +00001067#else
1068 return true;
1069#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001070}
1071
reed4960eee2015-12-18 07:09:18 -08001072bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -07001073 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001074 SkIRect clipBounds;
1075 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001076 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +00001077 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001078
reed96e657d2015-03-10 17:30:07 -07001079 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
1080
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001081 if (imageFilter) {
senorblancoe5e79842016-03-21 14:51:59 -07001082 clipBounds = imageFilter->filterBounds(clipBounds, ctm);
senorblancodb64af32015-12-09 10:11:43 -08001083 if (bounds && !imageFilter->canComputeFastBounds()) {
1084 bounds = nullptr;
1085 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001086 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001087 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -07001088 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001089 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +00001090
reed96e657d2015-03-10 17:30:07 -07001091 ctm.mapRect(&r, *bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001092 r.roundOut(&ir);
1093 // early exit if the layer's bounds are clipped out
1094 if (!ir.intersect(clipBounds)) {
reed4960eee2015-12-18 07:09:18 -08001095 if (BoundsAffectsClip(saveLayerFlags)) {
reed9b3aa542015-03-11 08:47:12 -07001096 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001097 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001098 }
junov@chromium.orga907ac32012-02-24 21:54:07 +00001099 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001100 }
1101 } else { // no user bounds, so just use the clip
1102 ir = clipBounds;
1103 }
reed180aec42015-03-11 10:39:04 -07001104 SkASSERT(!ir.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001105
reed4960eee2015-12-18 07:09:18 -08001106 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -07001107 // Simplify the current clips since they will be applied properly during restore()
reed9b3aa542015-03-11 08:47:12 -07001108 fCachedLocalClipBoundsDirty = true;
reed687fa1c2015-04-07 08:00:56 -07001109 fClipStack->clipDevRect(ir, SkRegion::kReplace_Op);
reed180aec42015-03-11 10:39:04 -07001110 fMCRec->fRasterClip.setRect(ir);
junov@chromium.orga907ac32012-02-24 21:54:07 +00001111 }
1112
1113 if (intersection) {
1114 *intersection = ir;
1115 }
1116 return true;
1117}
1118
reed4960eee2015-12-18 07:09:18 -08001119
reed4960eee2015-12-18 07:09:18 -08001120int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
1121 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001122}
1123
reed70ee31b2015-12-10 13:44:45 -08001124int SkCanvas::saveLayerPreserveLCDTextRequests(const SkRect* bounds, const SkPaint* paint) {
reed4960eee2015-12-18 07:09:18 -08001125 return this->saveLayer(SaveLayerRec(bounds, paint, kPreserveLCDText_SaveLayerFlag));
1126}
1127
1128int SkCanvas::saveLayer(const SaveLayerRec& origRec) {
1129 SaveLayerRec rec(origRec);
1130 if (gIgnoreSaveLayerBounds) {
1131 rec.fBounds = nullptr;
1132 }
1133 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
1134 fSaveCount += 1;
1135 this->internalSaveLayer(rec, strategy);
1136 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -08001137}
1138
reedbfd5f172016-01-07 11:28:08 -08001139static void draw_filter_into_device(SkBaseDevice* src, const SkImageFilter* filter,
1140 SkBaseDevice* dst, const SkMatrix& ctm) {
robertphillips7354a4b2015-12-16 05:08:27 -08001141
1142 SkBitmap srcBM;
1143
1144#if SK_SUPPORT_GPU
1145 GrRenderTarget* srcRT = src->accessRenderTarget();
1146 if (srcRT && !srcRT->asTexture() && dst->accessRenderTarget()) {
1147 // When both the src & the dst are on the gpu but the src doesn't have a texture,
1148 // we create a temporary texture for the draw.
1149 // TODO: we should actually only copy the portion of the source needed to apply the image
1150 // filter
1151 GrContext* context = srcRT->getContext();
bsalomon5ec26ae2016-02-25 08:33:02 -08001152 SkAutoTUnref<GrTexture> tex(context->textureProvider()->createTexture(srcRT->desc(),
1153 SkBudgeted::kYes));
robertphillips7354a4b2015-12-16 05:08:27 -08001154
1155 context->copySurface(tex, srcRT);
1156
1157 GrWrapTextureInBitmap(tex, src->width(), src->height(), src->isOpaque(), &srcBM);
1158 } else
1159#endif
1160 {
1161 srcBM = src->accessBitmap(false);
1162 }
1163
1164 SkCanvas c(dst);
1165
1166 SkPaint p;
robertphillips372177e2016-03-30 07:32:28 -07001167 p.setImageFilter(filter->makeWithLocalMatrix(ctm));
reedbfd5f172016-01-07 11:28:08 -08001168 const SkScalar x = SkIntToScalar(src->getOrigin().x());
1169 const SkScalar y = SkIntToScalar(src->getOrigin().y());
1170 c.drawBitmap(srcBM, x, y, &p);
robertphillips7354a4b2015-12-16 05:08:27 -08001171}
reed70ee31b2015-12-10 13:44:45 -08001172
reed129ed1c2016-02-22 06:42:31 -08001173static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, bool isOpaque,
1174 const SkPaint* paint) {
1175 // need to force L32 for now if we have an image filter. Once filters support other colortypes
1176 // e.g. sRGB or F16, we can remove this check
1177 const bool hasImageFilter = paint && paint->getImageFilter();
1178
1179 SkAlphaType alphaType = isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
1180 if ((prev.bytesPerPixel() < 4) || hasImageFilter) {
1181 // force to L32
1182 return SkImageInfo::MakeN32(w, h, alphaType);
1183 } else {
1184 // keep the same characteristics as the prev
1185 return SkImageInfo::Make(w, h, prev.colorType(), alphaType, prev.profileType());
1186 }
1187}
1188
reed4960eee2015-12-18 07:09:18 -08001189void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
1190 const SkRect* bounds = rec.fBounds;
1191 const SkPaint* paint = rec.fPaint;
1192 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1193
reed@google.comb93ba452014-03-10 19:47:58 +00001194#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001195 saveLayerFlags &= ~kDontClipToLayer_PrivateSaveLayerFlag;
reed@google.comb93ba452014-03-10 19:47:58 +00001196#endif
1197
reed8c30a812016-04-20 16:36:51 -07001198 SkLazyPaint lazyP;
1199 SkImageFilter* imageFilter = paint ? paint->getImageFilter() : NULL;
1200 SkMatrix stashedMatrix = fMCRec->fMatrix;
1201#ifndef SK_SUPPORT_LEGACY_IMAGEFILTER_CTM
1202 SkMatrix remainder;
1203 SkSize scale;
1204 /*
1205 * ImageFilters (so far) do not correctly handle matrices (CTM) that contain rotation/skew/etc.
1206 * but they do handle scaling. To accommodate this, we do the following:
1207 *
1208 * 1. Stash off the current CTM
1209 * 2. Decompose the CTM into SCALE and REMAINDER
1210 * 3. Wack the CTM to be just SCALE, and wrap the imagefilter with a MatrixImageFilter that
1211 * contains the REMAINDER
1212 * 4. Proceed as usual, allowing the client to draw into the layer (now with a scale-only CTM)
1213 * 5. During restore, we process the MatrixImageFilter, which applies REMAINDER to the output
1214 * of the original imagefilter, and draw that (via drawSprite)
1215 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1216 *
1217 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1218 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1219 */
reed96a04f32016-04-25 09:25:15 -07001220 if (imageFilter && !stashedMatrix.isScaleTranslate() && !imageFilter->canHandleComplexCTM() &&
reed8c30a812016-04-20 16:36:51 -07001221 stashedMatrix.decomposeScale(&scale, &remainder))
1222 {
1223 // We will restore the matrix (which we are overwriting here) in restore via fStashedMatrix
1224 this->internalSetMatrix(SkMatrix::MakeScale(scale.width(), scale.height()));
1225 SkPaint* p = lazyP.set(*paint);
1226 p->setImageFilter(SkImageFilter::MakeMatrixFilter(remainder,
1227 SkFilterQuality::kLow_SkFilterQuality,
1228 sk_ref_sp(imageFilter)));
1229 imageFilter = p->getImageFilter();
1230 paint = p;
1231 }
1232#endif
1233
junov@chromium.orga907ac32012-02-24 21:54:07 +00001234 // do this before we create the layer. We don't call the public save() since
1235 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001236 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001237
1238 fDeviceCMDirty = true;
1239
1240 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001241 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
reed2ff1fce2014-12-11 07:07:37 -08001242 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001243 }
1244
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001245 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1246 // the clipRectBounds() call above?
1247 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001248 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001249 }
1250
reed4960eee2015-12-18 07:09:18 -08001251 bool isOpaque = SkToBool(saveLayerFlags & kIsOpaque_SaveLayerFlag);
reed8dc0ccb2015-03-20 06:32:52 -07001252 SkPixelGeometry geo = fProps.pixelGeometry();
1253 if (paint) {
reed76033be2015-03-14 10:54:31 -07001254 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001255 if (paint->getImageFilter() || paint->getColorFilter()) {
reed76033be2015-03-14 10:54:31 -07001256 isOpaque = false;
reed8dc0ccb2015-03-20 06:32:52 -07001257 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001258 }
1259 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001260
reedb2db8982014-11-13 12:41:02 -08001261 SkBaseDevice* device = this->getTopDevice();
halcanary96fcdcc2015-08-27 07:41:13 -07001262 if (nullptr == device) {
reedb2db8982014-11-13 12:41:02 -08001263 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001264 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001265 }
reedb2db8982014-11-13 12:41:02 -08001266
reed129ed1c2016-02-22 06:42:31 -08001267 SkImageInfo info = make_layer_info(device->imageInfo(), ir.width(), ir.height(), isOpaque,
1268 paint);
1269
reed61f501f2015-04-29 08:34:00 -07001270 bool forceSpriteOnRestore = false;
1271 {
reed70ee31b2015-12-10 13:44:45 -08001272 const bool preserveLCDText = kOpaque_SkAlphaType == info.alphaType() ||
reed4960eee2015-12-18 07:09:18 -08001273 (saveLayerFlags & kPreserveLCDText_SaveLayerFlag);
reeddaa57bf2015-05-15 10:39:17 -07001274 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
reed70ee31b2015-12-10 13:44:45 -08001275 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
1276 preserveLCDText, false);
reed61f501f2015-04-29 08:34:00 -07001277 SkBaseDevice* newDev = device->onCreateDevice(createInfo, paint);
halcanary96fcdcc2015-08-27 07:41:13 -07001278 if (nullptr == newDev) {
reed61f501f2015-04-29 08:34:00 -07001279 // If onCreateDevice didn't succeed, try raster (e.g. PDF couldn't handle the paint)
robertphillips9a53fd72015-06-22 09:46:59 -07001280 const SkSurfaceProps surfaceProps(fProps.flags(), createInfo.fPixelGeometry);
1281 newDev = SkBitmapDevice::Create(createInfo.fInfo, surfaceProps);
halcanary96fcdcc2015-08-27 07:41:13 -07001282 if (nullptr == newDev) {
reed61f501f2015-04-29 08:34:00 -07001283 SkErrorInternals::SetError(kInternalError_SkError,
1284 "Unable to create device for layer.");
1285 return;
1286 }
1287 forceSpriteOnRestore = true;
1288 }
1289 device = newDev;
bungeman@google.come25c6842011-08-17 14:53:54 +00001290 }
reed@google.com6f8f2922011-03-04 22:27:10 +00001291 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips7354a4b2015-12-16 05:08:27 -08001292
reedbfd5f172016-01-07 11:28:08 -08001293 if (rec.fBackdrop) {
1294 draw_filter_into_device(fMCRec->fTopLayer->fDevice, rec.fBackdrop, device, fMCRec->fMatrix);
robertphillips7354a4b2015-12-16 05:08:27 -08001295 }
1296
reed8c30a812016-04-20 16:36:51 -07001297 DeviceCM* layer = new DeviceCM(device, paint, this, fConservativeRasterClip,
1298 forceSpriteOnRestore, stashedMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001299 device->unref();
1300
1301 layer->fNext = fMCRec->fTopLayer;
1302 fMCRec->fLayer = layer;
1303 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reed@android.com8a1c16f2008-12-17 15:59:43 +00001304}
1305
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001306int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001307 if (0xFF == alpha) {
1308 return this->saveLayer(bounds, nullptr);
1309 } else {
1310 SkPaint tmpPaint;
1311 tmpPaint.setAlpha(alpha);
1312 return this->saveLayer(bounds, &tmpPaint);
1313 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001314}
1315
reed@android.com8a1c16f2008-12-17 15:59:43 +00001316void SkCanvas::internalRestore() {
1317 SkASSERT(fMCStack.count() != 0);
1318
1319 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001320 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001321
reed687fa1c2015-04-07 08:00:56 -07001322 fClipStack->restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001323
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001324 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001325 DeviceCM* layer = fMCRec->fLayer; // may be null
1326 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001327 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001328
1329 // now do the normal restore()
1330 fMCRec->~MCRec(); // balanced in save()
1331 fMCStack.pop_back();
1332 fMCRec = (MCRec*)fMCStack.back();
1333
1334 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1335 since if we're being recorded, we don't want to record this (the
1336 recorder will have already recorded the restore).
1337 */
bsalomon49f085d2014-09-05 13:34:00 -07001338 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001339 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001340 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001341 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
reed61f501f2015-04-29 08:34:00 -07001342 layer->fPaint, layer->fDeviceIsBitmapDevice);
reed8c30a812016-04-20 16:36:51 -07001343 // restore what we smashed in internalSaveLayer
1344 fMCRec->fMatrix = layer->fStashedMatrix;
reed@google.com8926b162012-03-23 15:36:36 +00001345 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001346 fDeviceCMDirty = true;
halcanary385fe4d2015-08-26 13:07:48 -07001347 delete layer;
reedb679ca82015-04-07 04:40:48 -07001348 } else {
1349 // we're at the root
reeda499f902015-05-01 09:34:31 -07001350 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001351 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001352 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001353 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001354 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001355}
1356
reede8f30622016-03-23 18:59:25 -07001357sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001358 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001359 props = &fProps;
1360 }
1361 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001362}
1363
reede8f30622016-03-23 18:59:25 -07001364sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001365 SkBaseDevice* dev = this->getDevice();
reede8f30622016-03-23 18:59:25 -07001366 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001367}
1368
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001369SkImageInfo SkCanvas::imageInfo() const {
1370 SkBaseDevice* dev = this->getDevice();
1371 if (dev) {
1372 return dev->imageInfo();
1373 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001374 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001375 }
1376}
1377
brianosman898235c2016-04-06 07:38:23 -07001378bool SkCanvas::getProps(SkSurfaceProps* props) const {
1379 SkBaseDevice* dev = this->getDevice();
1380 if (dev) {
1381 if (props) {
1382 *props = fProps;
1383 }
1384 return true;
1385 } else {
1386 return false;
1387 }
1388}
1389
reed6ceeebd2016-03-09 14:26:26 -08001390#ifdef SK_SUPPORT_LEGACY_PEEKPIXELS_PARMS
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001391const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
reed884e97c2015-05-26 11:31:54 -07001392 SkPixmap pmap;
reed6ceeebd2016-03-09 14:26:26 -08001393 if (this->peekPixels(&pmap)) {
1394 if (info) {
1395 *info = pmap.info();
1396 }
1397 if (rowBytes) {
1398 *rowBytes = pmap.rowBytes();
1399 }
1400 return pmap.addr();
reed884e97c2015-05-26 11:31:54 -07001401 }
reed6ceeebd2016-03-09 14:26:26 -08001402 return nullptr;
1403}
1404#endif
1405
1406bool SkCanvas::peekPixels(SkPixmap* pmap) {
1407 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001408}
1409
reed884e97c2015-05-26 11:31:54 -07001410bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001411 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001412 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001413}
1414
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001415void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001416 SkPixmap pmap;
1417 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001418 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001419 }
1420 if (info) {
1421 *info = pmap.info();
1422 }
1423 if (rowBytes) {
1424 *rowBytes = pmap.rowBytes();
1425 }
1426 if (origin) {
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001427 *origin = this->getTopDevice(false)->getOrigin();
1428 }
reed884e97c2015-05-26 11:31:54 -07001429 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001430}
1431
reed884e97c2015-05-26 11:31:54 -07001432bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001433 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001434 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001435}
1436
reed@android.com8a1c16f2008-12-17 15:59:43 +00001437/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001438
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001439void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed61f501f2015-04-29 08:34:00 -07001440 const SkPaint* paint, bool deviceIsBitmapDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001441 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001442 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001443 paint = &tmp;
1444 }
reed@google.com4b226022011-01-11 18:32:13 +00001445
reed@google.com8926b162012-03-23 15:36:36 +00001446 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001447 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001448 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001449 paint = &looper.paint();
1450 SkImageFilter* filter = paint->getImageFilter();
1451 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
senorblancof35566e2016-04-18 10:32:02 -07001452 if (filter) {
robertphillips4418dba2016-03-07 12:45:14 -08001453 const SkBitmap& srcBM = srcDev->accessBitmap(false);
reed1eca1162016-04-25 12:29:38 -07001454 dstDev->drawSpriteWithFilter(iter, srcBM, pos.x(), pos.y(), *paint);
reed61f501f2015-04-29 08:34:00 -07001455 } else if (deviceIsBitmapDevice) {
reed9572a102015-05-26 19:22:17 -07001456 const SkBitmap& src = static_cast<SkBitmapDevice*>(srcDev)->fBitmap;
reed61f501f2015-04-29 08:34:00 -07001457 dstDev->drawSprite(iter, src, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001458 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001459 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001460 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001461 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001462 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001463}
1464
reed32704672015-12-16 08:27:10 -08001465/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001466
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001467void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001468 SkMatrix m;
1469 m.setTranslate(dx, dy);
1470 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001471}
1472
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001473void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001474 SkMatrix m;
1475 m.setScale(sx, sy);
1476 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001477}
1478
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001479void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001480 SkMatrix m;
1481 m.setRotate(degrees);
1482 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001483}
1484
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001485void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001486 SkMatrix m;
1487 m.setSkew(sx, sy);
1488 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001489}
1490
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001491void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001492 if (matrix.isIdentity()) {
1493 return;
1494 }
1495
reed2ff1fce2014-12-11 07:07:37 -08001496 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001497 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001498 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001499 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001500
1501 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001502}
1503
reed8c30a812016-04-20 16:36:51 -07001504void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001505 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001506 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001507 fMCRec->fMatrix = matrix;
reed8c30a812016-04-20 16:36:51 -07001508}
1509
1510void SkCanvas::setMatrix(const SkMatrix& matrix) {
1511 this->checkForDeferredSave();
1512 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001513 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001514}
1515
reed@android.com8a1c16f2008-12-17 15:59:43 +00001516void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001517 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001518}
1519
1520//////////////////////////////////////////////////////////////////////////////
1521
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001522void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001523 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001524 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1525 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001526}
1527
1528void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001529#ifdef SK_ENABLE_CLIP_QUICKREJECT
1530 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001531 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001532 return false;
1533 }
1534
reed@google.com3b3e8952012-08-16 20:53:31 +00001535 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001536 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001537 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001538
reed687fa1c2015-04-07 08:00:56 -07001539 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001540 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001541 }
1542 }
1543#endif
1544
bsalomonac8cabd2015-11-20 18:53:07 -08001545 if (!fAllowSoftClip) {
1546 edgeStyle = kHard_ClipEdgeStyle;
1547 }
reed90ba0952015-11-20 13:42:47 -08001548
reedc64eff52015-11-21 12:39:45 -08001549 const bool rectStaysRect = fMCRec->fMatrix.rectStaysRect();
1550 SkRect devR;
1551 if (rectStaysRect) {
1552 fMCRec->fMatrix.mapRect(&devR, rect);
1553 }
bsalomonac8cabd2015-11-20 18:53:07 -08001554
reedc64eff52015-11-21 12:39:45 -08001555 // Check if we can quick-accept the clip call (and do nothing)
1556 //
1557 // TODO: investigate if a (conservative) version of this could be done in ::clipRect,
1558 // so that subclasses (like PictureRecording) didn't see unnecessary clips, which in turn
1559 // might allow lazy save/restores to eliminate entire save/restore blocks.
1560 //
1561 if (SkRegion::kIntersect_Op == op &&
1562 kHard_ClipEdgeStyle == edgeStyle
1563 && rectStaysRect)
1564 {
1565 if (devR.round().contains(fMCRec->fRasterClip.getBounds())) {
1566#if 0
1567 SkDebugf("------- ignored clipRect [%g %g %g %g]\n",
1568 rect.left(), rect.top(), rect.right(), rect.bottom());
1569#endif
1570 return;
1571 }
1572 }
1573
1574 AutoValidateClip avc(this);
1575
1576 fDeviceCMDirty = true;
1577 fCachedLocalClipBoundsDirty = true;
1578
1579 if (rectStaysRect) {
1580 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1581 fClipStack->clipDevRect(devR, op, isAA);
senorblancoafc7cce2016-02-02 18:44:15 -08001582 fMCRec->fRasterClip.op(devR, this->getTopLayerBounds(), op, isAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001583 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001584 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001585 // and clip against that, since it can handle any matrix. However, to
1586 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1587 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001588 SkPath path;
1589
1590 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001591 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001592 }
1593}
1594
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001595void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001596 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001597 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001598 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001599 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1600 } else {
1601 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001602 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001603}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001604
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001605void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001606 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001607 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001608 AutoValidateClip avc(this);
1609
1610 fDeviceCMDirty = true;
1611 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001612 if (!fAllowSoftClip) {
1613 edgeStyle = kHard_ClipEdgeStyle;
1614 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001615
reed687fa1c2015-04-07 08:00:56 -07001616 fClipStack->clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001617
senorblancoafc7cce2016-02-02 18:44:15 -08001618 fMCRec->fRasterClip.op(transformedRRect, this->getTopLayerBounds(), op,
robertphillips125f19a2015-11-23 09:00:05 -08001619 kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001620 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001621 }
1622
1623 SkPath path;
1624 path.addRRect(rrect);
1625 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001626 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001627}
1628
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001629void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001630 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001631 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001632
1633 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1634 SkRect r;
1635 if (path.isRect(&r)) {
1636 this->onClipRect(r, op, edgeStyle);
1637 return;
1638 }
1639 SkRRect rrect;
1640 if (path.isOval(&r)) {
1641 rrect.setOval(r);
1642 this->onClipRRect(rrect, op, edgeStyle);
1643 return;
1644 }
1645 if (path.isRRect(&rrect)) {
1646 this->onClipRRect(rrect, op, edgeStyle);
1647 return;
1648 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001649 }
robertphillips39f05382015-11-24 09:30:12 -08001650
1651 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001652}
1653
1654void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001655#ifdef SK_ENABLE_CLIP_QUICKREJECT
1656 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001657 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001658 return false;
1659 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001660
reed@google.com3b3e8952012-08-16 20:53:31 +00001661 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001662 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001663 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001664
reed687fa1c2015-04-07 08:00:56 -07001665 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001666 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001667 }
1668 }
1669#endif
1670
reed@google.com5c3d1472011-02-22 19:12:23 +00001671 AutoValidateClip avc(this);
1672
reed@android.com8a1c16f2008-12-17 15:59:43 +00001673 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001674 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001675 if (!fAllowSoftClip) {
1676 edgeStyle = kHard_ClipEdgeStyle;
1677 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001678
1679 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001680 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001681
reed@google.comfe701122011-11-08 19:41:23 +00001682 // Check if the transfomation, or the original path itself
1683 // made us empty. Note this can also happen if we contained NaN
1684 // values. computing the bounds detects this, and will set our
1685 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1686 if (devPath.getBounds().isEmpty()) {
1687 // resetting the path will remove any NaN or other wanky values
1688 // that might upset our scan converter.
1689 devPath.reset();
1690 }
1691
reed@google.com5c3d1472011-02-22 19:12:23 +00001692 // if we called path.swap() we could avoid a deep copy of this path
reed687fa1c2015-04-07 08:00:56 -07001693 fClipStack->clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001694
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001695 if (fAllowSimplifyClip) {
fmalita1a481fe2015-02-04 07:39:34 -08001696 bool clipIsAA = getClipStack()->asPath(&devPath);
1697 if (clipIsAA) {
1698 edgeStyle = kSoft_ClipEdgeStyle;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001699 }
fmalita1a481fe2015-02-04 07:39:34 -08001700
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001701 op = SkRegion::kReplace_Op;
1702 }
1703
senorblancoafc7cce2016-02-02 18:44:15 -08001704 fMCRec->fRasterClip.op(devPath, this->getTopLayerBounds(), op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001705}
1706
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001707void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed2ff1fce2014-12-11 07:07:37 -08001708 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001709 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001710}
1711
1712void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001713 AutoValidateClip avc(this);
1714
reed@android.com8a1c16f2008-12-17 15:59:43 +00001715 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001716 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001717
reed@google.com5c3d1472011-02-22 19:12:23 +00001718 // todo: signal fClipStack that we have a region, and therefore (I guess)
1719 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001720 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001721
reed1f836ee2014-07-07 07:49:34 -07001722 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001723}
1724
reed@google.com819c9212011-02-23 18:56:55 +00001725#ifdef SK_DEBUG
1726void SkCanvas::validateClip() const {
1727 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001728 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001729 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001730 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001731 return;
1732 }
1733
reed@google.com819c9212011-02-23 18:56:55 +00001734 SkIRect ir;
1735 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001736 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001737
reed687fa1c2015-04-07 08:00:56 -07001738 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001739 const SkClipStack::Element* element;
halcanary96fcdcc2015-08-27 07:41:13 -07001740 while ((element = iter.next()) != nullptr) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001741 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001742 case SkClipStack::Element::kRect_Type:
1743 element->getRect().round(&ir);
1744 tmpClip.op(ir, element->getOp());
1745 break;
1746 case SkClipStack::Element::kEmpty_Type:
1747 tmpClip.setEmpty();
1748 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001749 default: {
1750 SkPath path;
1751 element->asPath(&path);
senorblancoafc7cce2016-02-02 18:44:15 -08001752 tmpClip.op(path, this->getTopLayerBounds(), element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001753 break;
1754 }
reed@google.com819c9212011-02-23 18:56:55 +00001755 }
1756 }
reed@google.com819c9212011-02-23 18:56:55 +00001757}
1758#endif
1759
reed@google.com90c07ea2012-04-13 13:50:27 +00001760void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001761 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001762 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001763
halcanary96fcdcc2015-08-27 07:41:13 -07001764 while ((element = iter.next()) != nullptr) {
fmalitac3b589a2014-06-05 12:40:07 -07001765 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001766 }
1767}
1768
reed@google.com5c3d1472011-02-22 19:12:23 +00001769///////////////////////////////////////////////////////////////////////////////
1770
reed@google.com754de5f2014-02-24 19:38:20 +00001771bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001772 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001773}
1774
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001775bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001776 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001777}
1778
reed@google.com3b3e8952012-08-16 20:53:31 +00001779bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001780 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001781 return true;
1782
reed1f836ee2014-07-07 07:49:34 -07001783 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001784 return true;
1785 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001786
reed1f836ee2014-07-07 07:49:34 -07001787 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001788 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001789 fMCRec->fMatrix.mapRect(&dst, rect);
reedb07a94f2014-11-19 05:03:18 -08001790 return !SkIRect::Intersects(dst.roundOut(), fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001791 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001792 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001793
reed@android.coma380ae42009-07-21 01:17:02 +00001794 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001795 // TODO: should we use | instead, or compare all 4 at once?
1796 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001797 return true;
1798 }
reed@google.comc0784db2013-12-13 21:16:12 +00001799 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001800 return true;
1801 }
1802 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001803 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001804}
1805
reed@google.com3b3e8952012-08-16 20:53:31 +00001806bool SkCanvas::quickReject(const SkPath& path) const {
1807 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001808}
1809
reed@google.com3b3e8952012-08-16 20:53:31 +00001810bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001811 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001812 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001813 return false;
1814 }
1815
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001816 SkMatrix inverse;
1817 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001818 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001819 if (bounds) {
1820 bounds->setEmpty();
1821 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001822 return false;
1823 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001824
bsalomon49f085d2014-09-05 13:34:00 -07001825 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001826 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001827 // adjust it outwards in case we are antialiasing
1828 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001829
reed@google.com8f4d2302013-12-17 16:44:46 +00001830 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1831 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001832 inverse.mapRect(bounds, r);
1833 }
1834 return true;
1835}
1836
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001837bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001838 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001839 if (clip.isEmpty()) {
1840 if (bounds) {
1841 bounds->setEmpty();
1842 }
1843 return false;
1844 }
1845
bsalomon49f085d2014-09-05 13:34:00 -07001846 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001847 *bounds = clip.getBounds();
1848 }
1849 return true;
1850}
1851
reed@android.com8a1c16f2008-12-17 15:59:43 +00001852const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001853 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001854}
1855
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001856const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001857 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001858}
1859
reed@google.com9c135db2014-03-12 18:28:35 +00001860GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1861 SkBaseDevice* dev = this->getTopDevice();
halcanary96fcdcc2015-08-27 07:41:13 -07001862 return dev ? dev->accessRenderTarget() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001863}
1864
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001865GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001866 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001867 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001868}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001869
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001870void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1871 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001872 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001873 if (outer.isEmpty()) {
1874 return;
1875 }
1876 if (inner.isEmpty()) {
1877 this->drawRRect(outer, paint);
1878 return;
1879 }
1880
1881 // We don't have this method (yet), but technically this is what we should
1882 // be able to assert...
1883 // SkASSERT(outer.contains(inner));
1884 //
1885 // For now at least check for containment of bounds
1886 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1887
1888 this->onDrawDRRect(outer, inner, paint);
1889}
1890
reed41af9662015-01-05 07:49:08 -08001891// These need to stop being virtual -- clients need to override the onDraw... versions
1892
1893void SkCanvas::drawPaint(const SkPaint& paint) {
1894 this->onDrawPaint(paint);
1895}
1896
1897void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1898 this->onDrawRect(r, paint);
1899}
1900
1901void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1902 this->onDrawOval(r, paint);
1903}
1904
1905void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1906 this->onDrawRRect(rrect, paint);
1907}
1908
1909void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1910 this->onDrawPoints(mode, count, pts, paint);
1911}
1912
1913void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
1914 const SkPoint texs[], const SkColor colors[], SkXfermode* xmode,
1915 const uint16_t indices[], int indexCount, const SkPaint& paint) {
1916 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
1917 indices, indexCount, paint);
1918}
1919
1920void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1921 this->onDrawPath(path, paint);
1922}
1923
reeda85d4d02015-05-06 12:56:48 -07001924void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001925 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001926 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001927}
1928
reede47829b2015-08-06 10:02:53 -07001929void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1930 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001931 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001932 if (dst.isEmpty() || src.isEmpty()) {
1933 return;
1934 }
1935 this->onDrawImageRect(image, &src, dst, paint, constraint);
1936}
reed41af9662015-01-05 07:49:08 -08001937
reed84984ef2015-07-17 07:09:43 -07001938void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1939 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001940 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001941 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001942}
1943
reede47829b2015-08-06 10:02:53 -07001944void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
1945 SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001946 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001947 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
1948 constraint);
1949}
reede47829b2015-08-06 10:02:53 -07001950
reed4c21dc52015-06-25 12:32:03 -07001951void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1952 const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001953 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07001954 if (dst.isEmpty()) {
1955 return;
1956 }
1957 if (!SkNinePatchIter::Valid(image->width(), image->height(), center)) {
reede47829b2015-08-06 10:02:53 -07001958 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001959 }
1960 this->onDrawImageNine(image, center, dst, paint);
1961}
1962
reed41af9662015-01-05 07:49:08 -08001963void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001964 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001965 return;
1966 }
reed41af9662015-01-05 07:49:08 -08001967 this->onDrawBitmap(bitmap, dx, dy, paint);
1968}
1969
reede47829b2015-08-06 10:02:53 -07001970void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07001971 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001972 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07001973 return;
1974 }
reede47829b2015-08-06 10:02:53 -07001975 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08001976}
1977
reed84984ef2015-07-17 07:09:43 -07001978void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
1979 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001980 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001981}
1982
reede47829b2015-08-06 10:02:53 -07001983void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
1984 SrcRectConstraint constraint) {
1985 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
1986 constraint);
1987}
reede47829b2015-08-06 10:02:53 -07001988
reed41af9662015-01-05 07:49:08 -08001989void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
1990 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001991 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001992 return;
1993 }
reed4c21dc52015-06-25 12:32:03 -07001994 if (!SkNinePatchIter::Valid(bitmap.width(), bitmap.height(), center)) {
reeda5517e22015-07-14 10:54:12 -07001995 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001996 }
reed41af9662015-01-05 07:49:08 -08001997 this->onDrawBitmapNine(bitmap, center, dst, paint);
1998}
1999
reed71c3c762015-06-24 10:29:17 -07002000void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2001 const SkColor colors[], int count, SkXfermode::Mode mode,
2002 const SkRect* cull, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002003 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002004 if (count <= 0) {
2005 return;
2006 }
2007 SkASSERT(atlas);
2008 SkASSERT(xform);
2009 SkASSERT(tex);
2010 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
2011}
2012
reedf70b5312016-03-04 16:36:20 -08002013void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2014 if (key) {
2015 this->onDrawAnnotation(rect, key, value);
2016 }
2017}
2018
reede47829b2015-08-06 10:02:53 -07002019void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2020 const SkPaint* paint, SrcRectConstraint constraint) {
2021 if (src) {
2022 this->drawImageRect(image, *src, dst, paint, constraint);
2023 } else {
2024 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2025 dst, paint, constraint);
2026 }
2027}
2028void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2029 const SkPaint* paint, SrcRectConstraint constraint) {
2030 if (src) {
2031 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2032 } else {
2033 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2034 dst, paint, constraint);
2035 }
2036}
2037
reed@android.com8a1c16f2008-12-17 15:59:43 +00002038//////////////////////////////////////////////////////////////////////////////
2039// These are the virtual drawing methods
2040//////////////////////////////////////////////////////////////////////////////
2041
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002042void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002043 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002044 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2045 }
2046}
2047
reed41af9662015-01-05 07:49:08 -08002048void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002049 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002050 this->internalDrawPaint(paint);
2051}
2052
2053void SkCanvas::internalDrawPaint(const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002054 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002055
2056 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002057 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002058 }
2059
reed@google.com4e2b3d32011-04-07 14:18:59 +00002060 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002061}
2062
reed41af9662015-01-05 07:49:08 -08002063void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2064 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002065 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002066 if ((long)count <= 0) {
2067 return;
2068 }
2069
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002070 SkRect r, storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002071 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002072 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002073 // special-case 2 points (common for drawing a single line)
2074 if (2 == count) {
2075 r.set(pts[0], pts[1]);
2076 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00002077 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002078 }
senorblanco87e066e2015-10-28 11:23:36 -07002079 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2080 return;
2081 }
2082 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002083 }
reed@google.coma584aed2012-05-16 14:06:02 +00002084
halcanary96fcdcc2015-08-27 07:41:13 -07002085 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002086
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002087 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002088
reed@android.com8a1c16f2008-12-17 15:59:43 +00002089 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002090 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002091 }
reed@google.com4b226022011-01-11 18:32:13 +00002092
reed@google.com4e2b3d32011-04-07 14:18:59 +00002093 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002094}
2095
reed41af9662015-01-05 07:49:08 -08002096void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002097 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002098 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002099 const SkRect* bounds = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002100 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08002101 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
2102 // To prevent accidental rejecting at this stage, we have to sort it before we check.
2103 SkRect tmp(r);
2104 tmp.sort();
2105
senorblanco87e066e2015-10-28 11:23:36 -07002106 if (this->quickReject(paint.computeFastBounds(tmp, &storage))) {
2107 return;
2108 }
2109 bounds = &r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002110 }
reed@google.com4b226022011-01-11 18:32:13 +00002111
reedc83a2972015-07-16 07:40:45 -07002112 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002113
2114 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002115 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002116 }
2117
reed@google.com4e2b3d32011-04-07 14:18:59 +00002118 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002119}
2120
reed41af9662015-01-05 07:49:08 -08002121void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002122 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002123 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002124 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002125 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002126 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2127 return;
2128 }
2129 bounds = &oval;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002130 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002131
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002132 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002133
2134 while (iter.next()) {
2135 iter.fDevice->drawOval(iter, oval, looper.paint());
2136 }
2137
2138 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002139}
2140
reed41af9662015-01-05 07:49:08 -08002141void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002142 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002143 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002144 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002145 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002146 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2147 return;
2148 }
2149 bounds = &rrect.getBounds();
reed@google.com4ed0fb72012-12-12 20:48:18 +00002150 }
2151
2152 if (rrect.isRect()) {
2153 // call the non-virtual version
2154 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002155 return;
2156 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002157 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002158 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2159 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002160 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002161
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002162 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002163
2164 while (iter.next()) {
2165 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2166 }
2167
2168 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002169}
2170
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002171void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2172 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002173 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002174 const SkRect* bounds = nullptr;
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002175 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002176 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2177 return;
2178 }
2179 bounds = &outer.getBounds();
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002180 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002181
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002182 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002183
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002184 while (iter.next()) {
2185 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2186 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002187
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002188 LOOPER_END
2189}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002190
reed41af9662015-01-05 07:49:08 -08002191void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002192 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002193 if (!path.isFinite()) {
2194 return;
2195 }
2196
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002197 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002198 const SkRect* bounds = nullptr;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002199 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002200 const SkRect& pathBounds = path.getBounds();
senorblanco87e066e2015-10-28 11:23:36 -07002201 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2202 return;
2203 }
2204 bounds = &pathBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002205 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002206
2207 const SkRect& r = path.getBounds();
2208 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002209 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002210 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002211 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002212 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002213 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002214
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002215 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002216
2217 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002218 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002219 }
2220
reed@google.com4e2b3d32011-04-07 14:18:59 +00002221 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002222}
2223
reed262a71b2015-12-05 13:07:27 -08002224bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002225 if (!paint.getImageFilter()) {
2226 return false;
2227 }
2228
2229 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002230 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002231 return false;
2232 }
2233
2234 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2235 // Once we can filter and the filter will return a result larger than itself, we should be
2236 // able to remove this constraint.
2237 // skbug.com/4526
2238 //
2239 SkPoint pt;
2240 ctm.mapXY(x, y, &pt);
2241 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2242 return ir.contains(fMCRec->fRasterClip.getBounds());
2243}
2244
reeda85d4d02015-05-06 12:56:48 -07002245void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002246 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002247 SkRect bounds = SkRect::MakeXYWH(x, y,
2248 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002249 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002250 SkRect tmp = bounds;
2251 if (paint) {
2252 paint->computeFastBounds(tmp, &tmp);
2253 }
2254 if (this->quickReject(tmp)) {
2255 return;
2256 }
reeda85d4d02015-05-06 12:56:48 -07002257 }
halcanary9d524f22016-03-29 09:03:52 -07002258
reeda85d4d02015-05-06 12:56:48 -07002259 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002260 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002261 paint = lazy.init();
2262 }
reed262a71b2015-12-05 13:07:27 -08002263
reed129ed1c2016-02-22 06:42:31 -08002264 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2265 *paint);
2266 if (drawAsSprite && paint->getImageFilter()) {
2267 SkBitmap bitmap;
2268 if (!as_IB(image)->asBitmapForImageFilters(&bitmap)) {
2269 drawAsSprite = false;
2270 } else{
2271 // Until imagefilters are updated, they cannot handle any src type but N32...
2272 if (bitmap.info().colorType() != kN32_SkColorType || bitmap.info().isSRGB()) {
2273 drawAsSprite = false;
2274 }
2275 }
2276 }
2277
reed262a71b2015-12-05 13:07:27 -08002278 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
2279
reeda85d4d02015-05-06 12:56:48 -07002280 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002281 const SkPaint& pnt = looper.paint();
2282 if (drawAsSprite && pnt.getImageFilter()) {
2283 SkBitmap bitmap;
2284 if (as_IB(image)->asBitmapForImageFilters(&bitmap)) {
2285 SkPoint pt;
2286 iter.fMatrix->mapXY(x, y, &pt);
robertphillips2302de92016-03-24 07:26:32 -07002287 iter.fDevice->drawSpriteWithFilter(iter, bitmap,
2288 SkScalarRoundToInt(pt.fX),
2289 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002290 }
2291 } else {
2292 iter.fDevice->drawImage(iter, image, x, y, pnt);
2293 }
reeda85d4d02015-05-06 12:56:48 -07002294 }
halcanary9d524f22016-03-29 09:03:52 -07002295
reeda85d4d02015-05-06 12:56:48 -07002296 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002297}
2298
reed41af9662015-01-05 07:49:08 -08002299void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002300 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002301 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
halcanary96fcdcc2015-08-27 07:41:13 -07002302 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002303 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002304 if (paint) {
2305 paint->computeFastBounds(dst, &storage);
2306 }
2307 if (this->quickReject(storage)) {
2308 return;
2309 }
reeda85d4d02015-05-06 12:56:48 -07002310 }
2311 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002312 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002313 paint = lazy.init();
2314 }
halcanary9d524f22016-03-29 09:03:52 -07002315
senorblancoc41e7e12015-12-07 12:51:30 -08002316 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002317 image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002318
reeda85d4d02015-05-06 12:56:48 -07002319 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002320 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002321 }
halcanary9d524f22016-03-29 09:03:52 -07002322
reeda85d4d02015-05-06 12:56:48 -07002323 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002324}
2325
reed41af9662015-01-05 07:49:08 -08002326void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002327 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002328 SkDEBUGCODE(bitmap.validate();)
2329
reed33366972015-10-08 09:22:02 -07002330 if (bitmap.drawsNothing()) {
2331 return;
2332 }
2333
2334 SkLazyPaint lazy;
2335 if (nullptr == paint) {
2336 paint = lazy.init();
2337 }
2338
2339 const SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2340
2341 SkRect storage;
2342 const SkRect* bounds = nullptr;
2343 if (paint->canComputeFastBounds()) {
2344 bitmap.getBounds(&storage);
2345 matrix.mapRect(&storage);
senorblanco87e066e2015-10-28 11:23:36 -07002346 SkRect tmp = storage;
2347 if (this->quickReject(paint->computeFastBounds(tmp, &tmp))) {
2348 return;
2349 }
2350 bounds = &storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002351 }
reed@google.com4b226022011-01-11 18:32:13 +00002352
reed129ed1c2016-02-22 06:42:31 -08002353 bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(), bitmap.height(),
2354 *paint);
2355 if (drawAsSprite && paint->getImageFilter()) {
2356 // Until imagefilters are updated, they cannot handle any src type but N32...
2357 if (bitmap.info().colorType() != kN32_SkColorType || bitmap.info().isSRGB()) {
2358 drawAsSprite = false;
2359 }
2360 }
2361
reed262a71b2015-12-05 13:07:27 -08002362 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds)
reed33366972015-10-08 09:22:02 -07002363
2364 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002365 const SkPaint& pnt = looper.paint();
2366 if (drawAsSprite && pnt.getImageFilter()) {
2367 SkPoint pt;
2368 iter.fMatrix->mapXY(x, y, &pt);
robertphillips2302de92016-03-24 07:26:32 -07002369 iter.fDevice->drawSpriteWithFilter(iter, bitmap,
2370 SkScalarRoundToInt(pt.fX),
2371 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002372 } else {
2373 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
2374 }
reed33366972015-10-08 09:22:02 -07002375 }
reed262a71b2015-12-05 13:07:27 -08002376
reed33366972015-10-08 09:22:02 -07002377 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002378}
2379
reed@google.com9987ec32011-09-07 11:57:52 +00002380// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002381void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002382 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002383 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002384 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002385 return;
2386 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002387
halcanary96fcdcc2015-08-27 07:41:13 -07002388 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002389 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002390 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2391 return;
2392 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002393 }
reed@google.com3d608122011-11-21 15:16:16 +00002394
reed@google.com33535f32012-09-25 15:37:50 +00002395 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002396 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002397 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002398 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002399
senorblancoc41e7e12015-12-07 12:51:30 -08002400 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002401 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002402
reed@google.com33535f32012-09-25 15:37:50 +00002403 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002404 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002405 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002406
reed@google.com33535f32012-09-25 15:37:50 +00002407 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002408}
2409
reed41af9662015-01-05 07:49:08 -08002410void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002411 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002412 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002413 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002414 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002415}
2416
reed4c21dc52015-06-25 12:32:03 -07002417void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2418 const SkPaint* paint) {
2419 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
halcanary9d524f22016-03-29 09:03:52 -07002420
halcanary96fcdcc2015-08-27 07:41:13 -07002421 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002422 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002423 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2424 return;
2425 }
reed@google.com3d608122011-11-21 15:16:16 +00002426 }
halcanary9d524f22016-03-29 09:03:52 -07002427
reed4c21dc52015-06-25 12:32:03 -07002428 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002429 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002430 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002431 }
halcanary9d524f22016-03-29 09:03:52 -07002432
senorblancoc41e7e12015-12-07 12:51:30 -08002433 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002434
reed4c21dc52015-06-25 12:32:03 -07002435 while (iter.next()) {
2436 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002437 }
halcanary9d524f22016-03-29 09:03:52 -07002438
reed4c21dc52015-06-25 12:32:03 -07002439 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002440}
2441
reed41af9662015-01-05 07:49:08 -08002442void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2443 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002444 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002445 SkDEBUGCODE(bitmap.validate();)
2446
halcanary96fcdcc2015-08-27 07:41:13 -07002447 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002448 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002449 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2450 return;
2451 }
reed4c21dc52015-06-25 12:32:03 -07002452 }
halcanary9d524f22016-03-29 09:03:52 -07002453
reed4c21dc52015-06-25 12:32:03 -07002454 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002455 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002456 paint = lazy.init();
2457 }
halcanary9d524f22016-03-29 09:03:52 -07002458
senorblancoc41e7e12015-12-07 12:51:30 -08002459 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002460
reed4c21dc52015-06-25 12:32:03 -07002461 while (iter.next()) {
2462 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2463 }
halcanary9d524f22016-03-29 09:03:52 -07002464
reed4c21dc52015-06-25 12:32:03 -07002465 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002466}
2467
reed@google.comf67e4cf2011-03-15 20:56:58 +00002468class SkDeviceFilteredPaint {
2469public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002470 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002471 uint32_t filteredFlags = device->filterTextFlags(paint);
2472 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002473 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002474 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002475 fPaint = newPaint;
2476 } else {
2477 fPaint = &paint;
2478 }
2479 }
2480
reed@google.comf67e4cf2011-03-15 20:56:58 +00002481 const SkPaint& paint() const { return *fPaint; }
2482
2483private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002484 const SkPaint* fPaint;
2485 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002486};
2487
bungeman@google.com52c748b2011-08-22 21:30:43 +00002488void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2489 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002490 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002491 draw.fDevice->drawRect(draw, r, paint);
2492 } else {
2493 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002494 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002495 draw.fDevice->drawRect(draw, r, p);
2496 }
2497}
2498
2499void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2500 const char text[], size_t byteLength,
2501 SkScalar x, SkScalar y) {
halcanary96fcdcc2015-08-27 07:41:13 -07002502 SkASSERT(byteLength == 0 || text != nullptr);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002503
2504 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07002505 if (text == nullptr || byteLength == 0 ||
bungeman@google.com52c748b2011-08-22 21:30:43 +00002506 draw.fClip->isEmpty() ||
halcanary96fcdcc2015-08-27 07:41:13 -07002507 (paint.getAlpha() == 0 && paint.getXfermode() == nullptr)) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002508 return;
2509 }
2510
2511 SkScalar width = 0;
2512 SkPoint start;
2513
2514 start.set(0, 0); // to avoid warning
2515 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2516 SkPaint::kStrikeThruText_Flag)) {
2517 width = paint.measureText(text, byteLength);
2518
2519 SkScalar offsetX = 0;
2520 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2521 offsetX = SkScalarHalf(width);
2522 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2523 offsetX = width;
2524 }
2525 start.set(x - offsetX, y);
2526 }
2527
2528 if (0 == width) {
2529 return;
2530 }
2531
2532 uint32_t flags = paint.getFlags();
2533
2534 if (flags & (SkPaint::kUnderlineText_Flag |
2535 SkPaint::kStrikeThruText_Flag)) {
2536 SkScalar textSize = paint.getTextSize();
2537 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2538 SkRect r;
2539
2540 r.fLeft = start.fX;
2541 r.fRight = start.fX + width;
2542
2543 if (flags & SkPaint::kUnderlineText_Flag) {
2544 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2545 start.fY);
2546 r.fTop = offset;
2547 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002548 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002549 }
2550 if (flags & SkPaint::kStrikeThruText_Flag) {
2551 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2552 start.fY);
2553 r.fTop = offset;
2554 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002555 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002556 }
2557 }
2558}
2559
reed@google.come0d9ce82014-04-23 04:00:17 +00002560void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2561 const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002562 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002563
2564 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002565 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002566 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002567 DrawTextDecorations(iter, dfp.paint(),
2568 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002569 }
2570
reed@google.com4e2b3d32011-04-07 14:18:59 +00002571 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002572}
2573
reed@google.come0d9ce82014-04-23 04:00:17 +00002574void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2575 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002576 SkPoint textOffset = SkPoint::Make(0, 0);
2577
halcanary96fcdcc2015-08-27 07:41:13 -07002578 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002579
reed@android.com8a1c16f2008-12-17 15:59:43 +00002580 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002581 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002582 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002583 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002584 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002585
reed@google.com4e2b3d32011-04-07 14:18:59 +00002586 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002587}
2588
reed@google.come0d9ce82014-04-23 04:00:17 +00002589void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2590 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002591
2592 SkPoint textOffset = SkPoint::Make(0, constY);
2593
halcanary96fcdcc2015-08-27 07:41:13 -07002594 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002595
reed@android.com8a1c16f2008-12-17 15:59:43 +00002596 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002597 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002598 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002599 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002600 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002601
reed@google.com4e2b3d32011-04-07 14:18:59 +00002602 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002603}
2604
reed@google.come0d9ce82014-04-23 04:00:17 +00002605void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2606 const SkMatrix* matrix, const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002607 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002608
reed@android.com8a1c16f2008-12-17 15:59:43 +00002609 while (iter.next()) {
2610 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002611 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002612 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002613
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002614 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002615}
2616
fmalita00d5c2c2014-08-21 08:53:26 -07002617void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2618 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002619
fmalita85d5eb92015-03-04 11:20:12 -08002620 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002621 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002622 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002623 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002624 SkRect tmp;
2625 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2626 return;
2627 }
2628 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002629 }
2630
fmalita024f9962015-03-03 19:08:17 -08002631 // We cannot filter in the looper as we normally do, because the paint is
2632 // incomplete at this point (text-related attributes are embedded within blob run paints).
2633 SkDrawFilter* drawFilter = fMCRec->fFilter;
halcanary96fcdcc2015-08-27 07:41:13 -07002634 fMCRec->fFilter = nullptr;
fmalita024f9962015-03-03 19:08:17 -08002635
fmalita85d5eb92015-03-04 11:20:12 -08002636 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002637
fmalitaaa1b9122014-08-28 14:32:24 -07002638 while (iter.next()) {
2639 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002640 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002641 }
2642
fmalitaaa1b9122014-08-28 14:32:24 -07002643 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002644
2645 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002646}
2647
reed@google.come0d9ce82014-04-23 04:00:17 +00002648// These will become non-virtual, so they always call the (virtual) onDraw... method
2649void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2650 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002651 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002652 this->onDrawText(text, byteLength, x, y, paint);
2653}
2654void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2655 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002656 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002657 this->onDrawPosText(text, byteLength, pos, paint);
2658}
2659void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2660 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002661 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002662 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2663}
2664void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2665 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002666 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002667 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2668}
fmalita00d5c2c2014-08-21 08:53:26 -07002669void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2670 const SkPaint& paint) {
reede3b38ce2016-01-08 09:18:44 -08002671 RETURN_ON_NULL(blob);
danakj9881d632014-11-26 12:41:06 -08002672 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
reede3b38ce2016-01-08 09:18:44 -08002673 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002674}
reed@google.come0d9ce82014-04-23 04:00:17 +00002675
reed41af9662015-01-05 07:49:08 -08002676void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2677 const SkPoint verts[], const SkPoint texs[],
2678 const SkColor colors[], SkXfermode* xmode,
2679 const uint16_t indices[], int indexCount,
2680 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002681 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
halcanary96fcdcc2015-08-27 07:41:13 -07002682 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
reed@google.com4b226022011-01-11 18:32:13 +00002683
reed@android.com8a1c16f2008-12-17 15:59:43 +00002684 while (iter.next()) {
2685 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002686 colors, xmode, indices, indexCount,
2687 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002688 }
reed@google.com4b226022011-01-11 18:32:13 +00002689
reed@google.com4e2b3d32011-04-07 14:18:59 +00002690 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002691}
2692
dandovb3c9d1c2014-08-12 08:34:29 -07002693void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2694 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002695 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
halcanary96fcdcc2015-08-27 07:41:13 -07002696 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002697 return;
2698 }
mtklein6cfa73a2014-08-13 13:33:49 -07002699
dandovecfff212014-08-04 10:02:00 -07002700 // Since a patch is always within the convex hull of the control points, we discard it when its
2701 // bounding rectangle is completely outside the current clip.
2702 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002703 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002704 if (this->quickReject(bounds)) {
2705 return;
2706 }
mtklein6cfa73a2014-08-13 13:33:49 -07002707
dandovb3c9d1c2014-08-12 08:34:29 -07002708 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2709}
2710
2711void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2712 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2713
halcanary96fcdcc2015-08-27 07:41:13 -07002714 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002715
dandovecfff212014-08-04 10:02:00 -07002716 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002717 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002718 }
mtklein6cfa73a2014-08-13 13:33:49 -07002719
dandovecfff212014-08-04 10:02:00 -07002720 LOOPER_END
2721}
2722
reeda8db7282015-07-07 10:22:31 -07002723void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
reede3b38ce2016-01-08 09:18:44 -08002724 RETURN_ON_NULL(dr);
2725 if (x || y) {
2726 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2727 this->onDrawDrawable(dr, &matrix);
2728 } else {
2729 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002730 }
2731}
2732
reeda8db7282015-07-07 10:22:31 -07002733void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reede3b38ce2016-01-08 09:18:44 -08002734 RETURN_ON_NULL(dr);
2735 if (matrix && matrix->isIdentity()) {
2736 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002737 }
reede3b38ce2016-01-08 09:18:44 -08002738 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002739}
2740
2741void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2742 SkRect bounds = dr->getBounds();
2743 if (matrix) {
2744 matrix->mapRect(&bounds);
2745 }
2746 if (this->quickReject(bounds)) {
2747 return;
2748 }
2749 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002750}
2751
reed71c3c762015-06-24 10:29:17 -07002752void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2753 const SkColor colors[], int count, SkXfermode::Mode mode,
2754 const SkRect* cull, const SkPaint* paint) {
2755 if (cull && this->quickReject(*cull)) {
2756 return;
2757 }
2758
2759 SkPaint pnt;
2760 if (paint) {
2761 pnt = *paint;
2762 }
halcanary9d524f22016-03-29 09:03:52 -07002763
halcanary96fcdcc2015-08-27 07:41:13 -07002764 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr)
reed71c3c762015-06-24 10:29:17 -07002765 while (iter.next()) {
2766 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, mode, pnt);
2767 }
2768 LOOPER_END
2769}
2770
reedf70b5312016-03-04 16:36:20 -08002771void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2772 SkASSERT(key);
2773
2774 SkPaint paint;
2775 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, nullptr)
2776 while (iter.next()) {
2777 iter.fDevice->drawAnnotation(iter, rect, key, value);
2778 }
2779 LOOPER_END
2780}
2781
reed@android.com8a1c16f2008-12-17 15:59:43 +00002782//////////////////////////////////////////////////////////////////////////////
2783// These methods are NOT virtual, and therefore must call back into virtual
2784// methods, rather than actually drawing themselves.
2785//////////////////////////////////////////////////////////////////////////////
2786
2787void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002788 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002789 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002790 SkPaint paint;
2791
2792 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002793 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002794 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002795 }
2796 this->drawPaint(paint);
2797}
2798
reed@android.com845fdac2009-06-23 03:01:32 +00002799void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002800 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002801 SkPaint paint;
2802
2803 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002804 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002805 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002806 }
2807 this->drawPaint(paint);
2808}
2809
2810void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002811 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002812 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002813
reed@android.com8a1c16f2008-12-17 15:59:43 +00002814 pt.set(x, y);
2815 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2816}
2817
2818void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002819 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002820 SkPoint pt;
2821 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002822
reed@android.com8a1c16f2008-12-17 15:59:43 +00002823 pt.set(x, y);
2824 paint.setColor(color);
2825 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2826}
2827
2828void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2829 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002830 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002831 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002832
reed@android.com8a1c16f2008-12-17 15:59:43 +00002833 pts[0].set(x0, y0);
2834 pts[1].set(x1, y1);
2835 this->drawPoints(kLines_PointMode, 2, pts, paint);
2836}
2837
2838void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2839 SkScalar right, SkScalar bottom,
2840 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002841 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002842 SkRect r;
2843
2844 r.set(left, top, right, bottom);
2845 this->drawRect(r, paint);
2846}
2847
2848void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2849 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002850 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002851 if (radius < 0) {
2852 radius = 0;
2853 }
2854
2855 SkRect r;
2856 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002857 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002858}
2859
2860void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2861 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002862 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002863 if (rx > 0 && ry > 0) {
2864 if (paint.canComputeFastBounds()) {
2865 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002866 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002867 return;
2868 }
2869 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002870 SkRRect rrect;
2871 rrect.setRectXY(r, rx, ry);
2872 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002873 } else {
2874 this->drawRect(r, paint);
2875 }
2876}
2877
reed@android.com8a1c16f2008-12-17 15:59:43 +00002878void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2879 SkScalar sweepAngle, bool useCenter,
2880 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002881 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002882 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2883 this->drawOval(oval, paint);
2884 } else {
2885 SkPath path;
2886 if (useCenter) {
2887 path.moveTo(oval.centerX(), oval.centerY());
2888 }
2889 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2890 if (useCenter) {
2891 path.close();
2892 }
2893 this->drawPath(path, paint);
2894 }
2895}
2896
2897void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2898 const SkPath& path, SkScalar hOffset,
2899 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002900 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002901 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002902
reed@android.com8a1c16f2008-12-17 15:59:43 +00002903 matrix.setTranslate(hOffset, vOffset);
2904 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2905}
2906
reed@android.comf76bacf2009-05-13 14:00:33 +00002907///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07002908
2909/**
2910 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2911 * against the playback cost of recursing into the subpicture to get at its actual ops.
2912 *
2913 * For now we pick a conservatively small value, though measurement (and other heuristics like
2914 * the type of ops contained) may justify changing this value.
2915 */
2916#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07002917
reedd5fa1a42014-08-09 11:08:05 -07002918void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002919 RETURN_ON_NULL(picture);
2920
reed1c2c4412015-04-30 13:09:24 -07002921 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
reede3b38ce2016-01-08 09:18:44 -08002922 if (matrix && matrix->isIdentity()) {
2923 matrix = nullptr;
2924 }
2925 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2926 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2927 picture->playback(this);
2928 } else {
2929 this->onDrawPicture(picture, matrix, paint);
reedd5fa1a42014-08-09 11:08:05 -07002930 }
2931}
robertphillips9b14f262014-06-04 05:40:44 -07002932
reedd5fa1a42014-08-09 11:08:05 -07002933void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2934 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07002935 if (!paint || paint->canComputeFastBounds()) {
2936 SkRect bounds = picture->cullRect();
2937 if (paint) {
2938 paint->computeFastBounds(bounds, &bounds);
2939 }
2940 if (matrix) {
2941 matrix->mapRect(&bounds);
2942 }
2943 if (this->quickReject(bounds)) {
2944 return;
2945 }
2946 }
2947
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002948 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002949 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002950 // Canvas has to first give the device the opportunity to render
2951 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002952 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002953 return; // the device has rendered the entire picture
2954 }
2955 }
2956
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002957 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07002958 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002959}
2960
reed@android.com8a1c16f2008-12-17 15:59:43 +00002961///////////////////////////////////////////////////////////////////////////////
2962///////////////////////////////////////////////////////////////////////////////
2963
2964SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
bungeman99fe8222015-08-20 07:57:51 -07002965 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002966
2967 SkASSERT(canvas);
2968
2969 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2970 fDone = !fImpl->next();
2971}
2972
2973SkCanvas::LayerIter::~LayerIter() {
2974 fImpl->~SkDrawIter();
2975}
2976
2977void SkCanvas::LayerIter::next() {
2978 fDone = !fImpl->next();
2979}
2980
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002981SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002982 return fImpl->getDevice();
2983}
2984
2985const SkMatrix& SkCanvas::LayerIter::matrix() const {
2986 return fImpl->getMatrix();
2987}
2988
2989const SkPaint& SkCanvas::LayerIter::paint() const {
2990 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07002991 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002992 paint = &fDefaultPaint;
2993 }
2994 return *paint;
2995}
2996
2997const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2998int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2999int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003000
3001///////////////////////////////////////////////////////////////////////////////
3002
fmalitac3b589a2014-06-05 12:40:07 -07003003SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003004
3005///////////////////////////////////////////////////////////////////////////////
3006
3007static bool supported_for_raster_canvas(const SkImageInfo& info) {
3008 switch (info.alphaType()) {
3009 case kPremul_SkAlphaType:
3010 case kOpaque_SkAlphaType:
3011 break;
3012 default:
3013 return false;
3014 }
3015
3016 switch (info.colorType()) {
3017 case kAlpha_8_SkColorType:
3018 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00003019 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003020 break;
3021 default:
3022 return false;
3023 }
3024
3025 return true;
3026}
3027
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003028SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
3029 if (!supported_for_raster_canvas(info)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003030 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003031 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003032
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003033 SkBitmap bitmap;
3034 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003035 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003036 }
halcanary385fe4d2015-08-26 13:07:48 -07003037 return new SkCanvas(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003038}
reedd5fa1a42014-08-09 11:08:05 -07003039
3040///////////////////////////////////////////////////////////////////////////////
3041
3042SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003043 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07003044 : fCanvas(canvas)
3045 , fSaveCount(canvas->getSaveCount())
3046{
bsalomon49f085d2014-09-05 13:34:00 -07003047 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003048 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07003049 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003050 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07003051 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003052 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07003053 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003054 canvas->save();
3055 }
mtklein6cfa73a2014-08-13 13:33:49 -07003056
bsalomon49f085d2014-09-05 13:34:00 -07003057 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003058 canvas->concat(*matrix);
3059 }
3060}
3061
3062SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
3063 fCanvas->restoreToCount(fSaveCount);
3064}
reede8f30622016-03-23 18:59:25 -07003065
3066#ifdef SK_SUPPORT_LEGACY_NEW_SURFACE_API
3067SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
3068 return this->makeSurface(info, props).release();
3069}
3070#endif