blob: 8f28ea4b7742097ed7f289e26f592e7ba7cf5dbc [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"
bungemand3ebb482015-08-05 13:57:49 -070013#include "SkDevice.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000014#include "SkDraw.h"
reed3cb38402015-02-06 08:36:15 -080015#include "SkDrawable.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000016#include "SkDrawFilter.h"
17#include "SkDrawLooper.h"
joshualitt5f5a8d72015-02-25 14:09:45 -080018#include "SkErrorInternals.h"
piotaixrb5fae932014-09-24 13:03:30 -070019#include "SkImage.h"
reed262a71b2015-12-05 13:07:27 -080020#include "SkImage_Base.h"
21#include "SkMatrixUtils.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000022#include "SkMetaData.h"
reed4c21dc52015-06-25 12:32:03 -070023#include "SkNinePatchIter.h"
reedc83a2972015-07-16 07:40:45 -070024#include "SkPaintPriv.h"
dandovb3c9d1c2014-08-12 08:34:29 -070025#include "SkPatchUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000026#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000027#include "SkRasterClip.h"
reed96472de2014-12-10 09:53:42 -080028#include "SkReadPixelsRec.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000029#include "SkRRect.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000030#include "SkSmallAllocator.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;
198 const bool fDeviceIsBitmapDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199
reed96e657d2015-03-10 17:30:07 -0700200 DeviceCM(SkBaseDevice* device, const SkPaint* paint, SkCanvas* canvas,
reed86a17e72015-05-14 12:25:22 -0700201 bool conservativeRasterClip, bool deviceIsBitmapDevice)
halcanary96fcdcc2015-08-27 07:41:13 -0700202 : fNext(nullptr)
reedd9544982014-09-09 18:46:22 -0700203 , fClip(conservativeRasterClip)
reed61f501f2015-04-29 08:34:00 -0700204 , fDeviceIsBitmapDevice(deviceIsBitmapDevice)
reedd9544982014-09-09 18:46:22 -0700205 {
halcanary96fcdcc2015-08-27 07:41:13 -0700206 if (nullptr != device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000207 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000208 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000209 }
reed@google.com4b226022011-01-11 18:32:13 +0000210 fDevice = device;
halcanary96fcdcc2015-08-27 07:41:13 -0700211 fPaint = paint ? new SkPaint(*paint) : nullptr;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000212 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000213
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000214 ~DeviceCM() {
bsalomon49f085d2014-09-05 13:34:00 -0700215 if (fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000216 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217 fDevice->unref();
218 }
halcanary385fe4d2015-08-26 13:07:48 -0700219 delete fPaint;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000220 }
reed@google.com4b226022011-01-11 18:32:13 +0000221
mtkleinfeaadee2015-04-08 11:25:48 -0700222 void reset(const SkIRect& bounds) {
223 SkASSERT(!fPaint);
224 SkASSERT(!fNext);
225 SkASSERT(fDevice);
226 fClip.setRect(bounds);
227 }
228
reed@google.com045e62d2011-10-24 12:19:46 +0000229 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
230 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000231 int x = fDevice->getOrigin().x();
232 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000233 int width = fDevice->width();
234 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000235
reed@android.com8a1c16f2008-12-17 15:59:43 +0000236 if ((x | y) == 0) {
237 fMatrix = &totalMatrix;
238 fClip = totalClip;
239 } else {
240 fMatrixStorage = totalMatrix;
241 fMatrixStorage.postTranslate(SkIntToScalar(-x),
242 SkIntToScalar(-y));
243 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000244
reed@android.com8a1c16f2008-12-17 15:59:43 +0000245 totalClip.translate(-x, -y, &fClip);
246 }
247
reed@google.com045e62d2011-10-24 12:19:46 +0000248 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000249
250 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000251
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000253 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000254 SkRegion::kDifference_Op);
255 }
reed@google.com4b226022011-01-11 18:32:13 +0000256
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000257 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
258
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259#ifdef SK_DEBUG
260 if (!fClip.isEmpty()) {
261 SkIRect deviceR;
262 deviceR.set(0, 0, width, height);
263 SkASSERT(deviceR.contains(fClip.getBounds()));
264 }
265#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000266 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000267};
268
269/* This is the record we keep for each save/restore level in the stack.
270 Since a level optionally copies the matrix and/or stack, we have pointers
271 for these fields. If the value is copied for this level, the copy is
272 stored in the ...Storage field, and the pointer points to that. If the
273 value is not copied for this level, we ignore ...Storage, and just point
274 at the corresponding value in the previous level in the stack.
275*/
276class SkCanvas::MCRec {
277public:
reed1f836ee2014-07-07 07:49:34 -0700278 SkDrawFilter* fFilter; // the current filter (or null)
reedd9544982014-09-09 18:46:22 -0700279 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000280 /* If there are any layers in the stack, this points to the top-most
281 one that is at or below this level in the stack (so we know what
282 bitmap/device to draw into from this level. This value is NOT
283 reference counted, since the real owner is either our fLayer field,
284 or a previous one in a lower level.)
285 */
reed2ff1fce2014-12-11 07:07:37 -0800286 DeviceCM* fTopLayer;
287 SkRasterClip fRasterClip;
288 SkMatrix fMatrix;
289 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000290
reedd9544982014-09-09 18:46:22 -0700291 MCRec(bool conservativeRasterClip) : fRasterClip(conservativeRasterClip) {
halcanary96fcdcc2015-08-27 07:41:13 -0700292 fFilter = nullptr;
293 fLayer = nullptr;
294 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800295 fMatrix.reset();
296 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700297
reedd9544982014-09-09 18:46:22 -0700298 // don't bother initializing fNext
299 inc_rec();
300 }
reed2ff1fce2014-12-11 07:07:37 -0800301 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
reedd9544982014-09-09 18:46:22 -0700302 fFilter = SkSafeRef(prev.fFilter);
halcanary96fcdcc2015-08-27 07:41:13 -0700303 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700304 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800305 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700306
reed@android.com8a1c16f2008-12-17 15:59:43 +0000307 // don't bother initializing fNext
308 inc_rec();
309 }
310 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000311 SkSafeUnref(fFilter);
halcanary385fe4d2015-08-26 13:07:48 -0700312 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000313 dec_rec();
314 }
mtkleinfeaadee2015-04-08 11:25:48 -0700315
316 void reset(const SkIRect& bounds) {
317 SkASSERT(fLayer);
318 SkASSERT(fDeferredSaveCount == 0);
319
320 fMatrix.reset();
321 fRasterClip.setRect(bounds);
322 fLayer->reset(bounds);
323 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000324};
325
326class SkDrawIter : public SkDraw {
327public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000328 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000329 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000330 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000331 canvas->updateDeviceCMCache();
332
reed687fa1c2015-04-07 08:00:56 -0700333 fClipStack = canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000334 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000335 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000336 }
reed@google.com4b226022011-01-11 18:32:13 +0000337
reed@android.com8a1c16f2008-12-17 15:59:43 +0000338 bool next() {
339 // skip over recs with empty clips
340 if (fSkipEmptyClips) {
341 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
342 fCurrLayer = fCurrLayer->fNext;
343 }
344 }
345
reed@google.comf68c5e22012-02-24 16:38:58 +0000346 const DeviceCM* rec = fCurrLayer;
347 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000348
349 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000350 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
351 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000352 fDevice = rec->fDevice;
reed41e010c2015-06-09 12:16:53 -0700353 if (!fDevice->accessPixels(&fDst)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700354 fDst.reset(fDevice->imageInfo(), nullptr, 0);
reed41e010c2015-06-09 12:16:53 -0700355 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000356 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000357 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000358
359 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700360 // fCurrLayer may be nullptr now
reed@android.com199f1082009-06-10 02:12:47 +0000361
reed@android.com8a1c16f2008-12-17 15:59:43 +0000362 return true;
363 }
364 return false;
365 }
reed@google.com4b226022011-01-11 18:32:13 +0000366
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000367 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000368 int getX() const { return fDevice->getOrigin().x(); }
369 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000370 const SkMatrix& getMatrix() const { return *fMatrix; }
371 const SkRegion& getClip() const { return *fClip; }
372 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000373
reed@android.com8a1c16f2008-12-17 15:59:43 +0000374private:
375 SkCanvas* fCanvas;
376 const DeviceCM* fCurrLayer;
377 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000378 SkBool8 fSkipEmptyClips;
379
380 typedef SkDraw INHERITED;
381};
382
383/////////////////////////////////////////////////////////////////////////////
384
reeddbc3cef2015-04-29 12:18:57 -0700385static SkPaint* set_if_needed(SkLazyPaint* lazy, const SkPaint& orig) {
386 return lazy->isValid() ? lazy->get() : lazy->set(orig);
387}
388
389/**
390 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700391 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700392 */
393static SkColorFilter* image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700394 SkImageFilter* imgf = paint.getImageFilter();
395 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700396 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700397 }
398
399 SkColorFilter* imgCF;
400 if (!imgf->asAColorFilter(&imgCF)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700401 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700402 }
403
404 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700405 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700406 // there is no existing paint colorfilter, so we can just return the imagefilter's
407 return imgCF;
408 }
409
410 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
411 // and we need to combine them into a single colorfilter.
412 SkAutoTUnref<SkColorFilter> autoImgCF(imgCF);
413 return SkColorFilter::CreateComposeFilter(imgCF, paintCF);
reeddbc3cef2015-04-29 12:18:57 -0700414}
415
senorblanco87e066e2015-10-28 11:23:36 -0700416/**
417 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
418 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
419 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
420 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
421 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
422 * conservative "effective" bounds based on the settings in the paint... with one exception. This
423 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
424 * deliberately ignored.
425 */
426static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
427 const SkRect& rawBounds,
428 SkRect* storage) {
429 SkPaint tmpUnfiltered(paint);
430 tmpUnfiltered.setImageFilter(nullptr);
431 if (tmpUnfiltered.canComputeFastBounds()) {
432 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
433 } else {
434 return rawBounds;
435 }
436}
437
reed@android.com8a1c16f2008-12-17 15:59:43 +0000438class AutoDrawLooper {
439public:
senorblanco87e066e2015-10-28 11:23:36 -0700440 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
441 // paint. It's used to determine the size of the offscreen layer for filters.
442 // If null, the clip will be used instead.
reed4a8126e2014-09-22 07:29:03 -0700443 AutoDrawLooper(SkCanvas* canvas, const SkSurfaceProps& props, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000444 bool skipLayerForImageFilter = false,
senorblanco87e066e2015-10-28 11:23:36 -0700445 const SkRect* rawBounds = nullptr) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000446 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000447 fFilter = canvas->getDrawFilter();
reed4a8126e2014-09-22 07:29:03 -0700448 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000449 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700450 fTempLayerForImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000451 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000452
reeddbc3cef2015-04-29 12:18:57 -0700453 SkColorFilter* simplifiedCF = image_to_color_filter(fOrigPaint);
454 if (simplifiedCF) {
455 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
456 paint->setColorFilter(simplifiedCF)->unref();
halcanary96fcdcc2015-08-27 07:41:13 -0700457 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700458 fPaint = paint;
459 }
460
461 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700462 /**
463 * We implement ImageFilters for a given draw by creating a layer, then applying the
464 * imagefilter to the pixels of that layer (its backing surface/image), and then
465 * we call restore() to xfer that layer to the main canvas.
466 *
467 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
468 * 2. Generate the src pixels:
469 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
470 * return (fPaint). We then draw the primitive (using srcover) into a cleared
471 * buffer/surface.
472 * 3. Restore the layer created in #1
473 * The imagefilter is passed the buffer/surface from the layer (now filled with the
474 * src pixels of the primitive). It returns a new "filtered" buffer, which we
475 * draw onto the previous layer using the xfermode from the original paint.
476 */
reed@google.com8926b162012-03-23 15:36:36 +0000477 SkPaint tmp;
reeddbc3cef2015-04-29 12:18:57 -0700478 tmp.setImageFilter(fPaint->getImageFilter());
479 tmp.setXfermode(fPaint->getXfermode());
senorblanco87e066e2015-10-28 11:23:36 -0700480 SkRect storage;
481 if (rawBounds) {
482 // Make rawBounds include all paint outsets except for those due to image filters.
483 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
484 }
reedbfd5f172016-01-07 11:28:08 -0800485 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &tmp),
reed76033be2015-03-14 10:54:31 -0700486 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700487 fTempLayerForImageFilter = true;
488 // we remove the imagefilter/xfermode inside doNext()
reed@google.com8926b162012-03-23 15:36:36 +0000489 }
490
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000491 if (SkDrawLooper* looper = paint.getLooper()) {
492 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
493 looper->contextSize());
494 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000495 fIsSimple = false;
496 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700497 fLooperContext = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000498 // can we be marked as simple?
reed5c476fb2015-04-20 08:04:21 -0700499 fIsSimple = !fFilter && !fTempLayerForImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000500 }
piotaixrb5fae932014-09-24 13:03:30 -0700501
reed4a8126e2014-09-22 07:29:03 -0700502 uint32_t oldFlags = paint.getFlags();
503 fNewPaintFlags = filter_paint_flags(props, oldFlags);
504 if (fIsSimple && (fNewPaintFlags != oldFlags)) {
reeddbc3cef2015-04-29 12:18:57 -0700505 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700506 paint->setFlags(fNewPaintFlags);
507 fPaint = paint;
508 // if we're not simple, doNext() will take care of calling setFlags()
509 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000510 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000511
reed@android.com8a1c16f2008-12-17 15:59:43 +0000512 ~AutoDrawLooper() {
reed5c476fb2015-04-20 08:04:21 -0700513 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000514 fCanvas->internalRestore();
515 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000516 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000517 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000518
reed@google.com4e2b3d32011-04-07 14:18:59 +0000519 const SkPaint& paint() const {
520 SkASSERT(fPaint);
521 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000522 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000523
reed@google.com129ec222012-05-15 13:24:09 +0000524 bool next(SkDrawFilter::Type drawType) {
525 if (fDone) {
526 return false;
527 } else if (fIsSimple) {
528 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000529 return !fPaint->nothingToDraw();
530 } else {
531 return this->doNext(drawType);
532 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000533 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000534
reed@android.com8a1c16f2008-12-17 15:59:43 +0000535private:
reeddbc3cef2015-04-29 12:18:57 -0700536 SkLazyPaint fLazyPaintInit; // base paint storage in case we need to modify it
537 SkLazyPaint fLazyPaintPerLooper; // per-draw-looper storage, so the looper can modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000538 SkCanvas* fCanvas;
539 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000540 SkDrawFilter* fFilter;
541 const SkPaint* fPaint;
542 int fSaveCount;
reed4a8126e2014-09-22 07:29:03 -0700543 uint32_t fNewPaintFlags;
reed5c476fb2015-04-20 08:04:21 -0700544 bool fTempLayerForImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000545 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000546 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000547 SkDrawLooper::Context* fLooperContext;
548 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000549
550 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000551};
552
reed@google.com129ec222012-05-15 13:24:09 +0000553bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
halcanary96fcdcc2015-08-27 07:41:13 -0700554 fPaint = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000555 SkASSERT(!fIsSimple);
reed5c476fb2015-04-20 08:04:21 -0700556 SkASSERT(fLooperContext || fFilter || fTempLayerForImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000557
reeddbc3cef2015-04-29 12:18:57 -0700558 SkPaint* paint = fLazyPaintPerLooper.set(fLazyPaintInit.isValid() ?
559 *fLazyPaintInit.get() : fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700560 paint->setFlags(fNewPaintFlags);
reed@google.com129ec222012-05-15 13:24:09 +0000561
reed5c476fb2015-04-20 08:04:21 -0700562 if (fTempLayerForImageFilter) {
halcanary96fcdcc2015-08-27 07:41:13 -0700563 paint->setImageFilter(nullptr);
564 paint->setXfermode(nullptr);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000565 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000566
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000567 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000568 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000569 return false;
570 }
571 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000572 if (!fFilter->filter(paint, drawType)) {
573 fDone = true;
574 return false;
575 }
halcanary96fcdcc2015-08-27 07:41:13 -0700576 if (nullptr == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000577 // no looper means we only draw once
578 fDone = true;
579 }
580 }
581 fPaint = paint;
582
583 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000584 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000585 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000586 }
587
588 // call this after any possible paint modifiers
589 if (fPaint->nothingToDraw()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700590 fPaint = nullptr;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000591 return false;
592 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000593 return true;
594}
595
reed@android.com8a1c16f2008-12-17 15:59:43 +0000596////////// macros to place around the internal draw calls //////////////////
597
reed262a71b2015-12-05 13:07:27 -0800598#define LOOPER_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
599 this->predrawNotify(); \
600 AutoDrawLooper looper(this, fProps, paint, skipLayerForFilter, bounds); \
601 while (looper.next(SkDrawFilter::kBitmap_Type)) { \
602 SkDrawIter iter(this);
603
604
reed@google.com8926b162012-03-23 15:36:36 +0000605#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000606 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700607 AutoDrawLooper looper(this, fProps, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000608 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000609 SkDrawIter iter(this);
610
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000611#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000612 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700613 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000614 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000615 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000616
reedc83a2972015-07-16 07:40:45 -0700617#define LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, type, bounds, auxOpaque) \
618 this->predrawNotify(bounds, &paint, auxOpaque); \
619 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
620 while (looper.next(type)) { \
621 SkDrawIter iter(this);
622
reed@google.com4e2b3d32011-04-07 14:18:59 +0000623#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000624
625////////////////////////////////////////////////////////////////////////////
626
mtkleinfeaadee2015-04-08 11:25:48 -0700627void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
628 this->restoreToCount(1);
629 fCachedLocalClipBounds.setEmpty();
630 fCachedLocalClipBoundsDirty = true;
631 fClipStack->reset();
632 fMCRec->reset(bounds);
633
634 // We're peering through a lot of structs here. Only at this scope do we
635 // know that the device is an SkBitmapDevice (really an SkNoPixelsBitmapDevice).
636 static_cast<SkBitmapDevice*>(fMCRec->fLayer->fDevice)->setNewSize(bounds.size());
637}
638
reedd9544982014-09-09 18:46:22 -0700639SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
reed42b73eb2015-11-20 13:42:42 -0800640 if (device && device->forceConservativeRasterClip()) {
641 flags = InitFlags(flags | kConservativeRasterClip_InitFlag);
642 }
643 // Since init() is only called once by our constructors, it is safe to perform this
644 // const-cast.
645 *const_cast<bool*>(&fConservativeRasterClip) = SkToBool(flags & kConservativeRasterClip_InitFlag);
646
reed@google.comc0784db2013-12-13 21:16:12 +0000647 fCachedLocalClipBounds.setEmpty();
648 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000649 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000650 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700651 fDeviceCMDirty = true;
reed2ff1fce2014-12-11 07:07:37 -0800652 fSaveCount = 1;
halcanary96fcdcc2015-08-27 07:41:13 -0700653 fMetaData = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000654
halcanary385fe4d2015-08-26 13:07:48 -0700655 fClipStack.reset(new SkClipStack);
reed687fa1c2015-04-07 08:00:56 -0700656
reed@android.com8a1c16f2008-12-17 15:59:43 +0000657 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700658 new (fMCRec) MCRec(fConservativeRasterClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000659
reeda499f902015-05-01 09:34:31 -0700660 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
661 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
halcanary96fcdcc2015-08-27 07:41:13 -0700662 new (fDeviceCMStorage) DeviceCM(nullptr, nullptr, nullptr, fConservativeRasterClip, false);
reedb679ca82015-04-07 04:40:48 -0700663
reed@android.com8a1c16f2008-12-17 15:59:43 +0000664 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000665
halcanary96fcdcc2015-08-27 07:41:13 -0700666 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000667
reedf92c8662014-08-18 08:02:43 -0700668 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700669 // The root device and the canvas should always have the same pixel geometry
670 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reedf92c8662014-08-18 08:02:43 -0700671 device->onAttachToCanvas(this);
672 fMCRec->fLayer->fDevice = SkRef(device);
reed78e27682014-11-19 08:04:34 -0800673 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
reedf92c8662014-08-18 08:02:43 -0700674 }
675 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000676}
677
reed@google.comcde92112011-07-06 20:00:52 +0000678SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000679 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700680 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800681 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000682{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000683 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000684
halcanary96fcdcc2015-08-27 07:41:13 -0700685 this->init(nullptr, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000686}
687
reedd9544982014-09-09 18:46:22 -0700688static SkBitmap make_nopixels(int width, int height) {
689 SkBitmap bitmap;
690 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
691 return bitmap;
692}
693
694class SkNoPixelsBitmapDevice : public SkBitmapDevice {
695public:
robertphillipsfcf78292015-06-19 11:49:52 -0700696 SkNoPixelsBitmapDevice(const SkIRect& bounds, const SkSurfaceProps& surfaceProps)
697 : INHERITED(make_nopixels(bounds.width(), bounds.height()), surfaceProps)
reed78e27682014-11-19 08:04:34 -0800698 {
699 this->setOrigin(bounds.x(), bounds.y());
700 }
reedd9544982014-09-09 18:46:22 -0700701
702private:
piotaixrb5fae932014-09-24 13:03:30 -0700703
reedd9544982014-09-09 18:46:22 -0700704 typedef SkBitmapDevice INHERITED;
705};
706
reed96a857e2015-01-25 10:33:58 -0800707SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000708 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800709 , fProps(SkSurfacePropsCopyOrDefault(props))
reed42b73eb2015-11-20 13:42:42 -0800710 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000711{
712 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700713
halcanary385fe4d2015-08-26 13:07:48 -0700714 this->init(new SkNoPixelsBitmapDevice(SkIRect::MakeWH(width, height), fProps),
715 kDefault_InitFlags)->unref();
reedd9544982014-09-09 18:46:22 -0700716}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000717
reed78e27682014-11-19 08:04:34 -0800718SkCanvas::SkCanvas(const SkIRect& bounds, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700719 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700720 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800721 , fConservativeRasterClip(false)
reedd9544982014-09-09 18:46:22 -0700722{
723 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700724
halcanary385fe4d2015-08-26 13:07:48 -0700725 this->init(new SkNoPixelsBitmapDevice(bounds, fProps), flags)->unref();
reedd9544982014-09-09 18:46:22 -0700726}
727
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000728SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000729 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700730 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800731 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000732{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000733 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700734
reedd9544982014-09-09 18:46:22 -0700735 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000736}
737
robertphillipsfcf78292015-06-19 11:49:52 -0700738SkCanvas::SkCanvas(SkBaseDevice* device, InitFlags flags)
739 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700740 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800741 , fConservativeRasterClip(false)
robertphillipsfcf78292015-06-19 11:49:52 -0700742{
743 inc_canvas();
744
745 this->init(device, flags);
746}
747
reed4a8126e2014-09-22 07:29:03 -0700748SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700749 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700750 , fProps(props)
reed42b73eb2015-11-20 13:42:42 -0800751 , fConservativeRasterClip(false)
reed3716fd02014-09-21 09:39:55 -0700752{
753 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700754
halcanary385fe4d2015-08-26 13:07:48 -0700755 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700756 this->init(device, kDefault_InitFlags);
757}
reed29c857d2014-09-21 10:25:07 -0700758
reed4a8126e2014-09-22 07:29:03 -0700759SkCanvas::SkCanvas(const SkBitmap& bitmap)
760 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
761 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800762 , fConservativeRasterClip(false)
reed4a8126e2014-09-22 07:29:03 -0700763{
764 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700765
halcanary385fe4d2015-08-26 13:07:48 -0700766 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700767 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000768}
769
770SkCanvas::~SkCanvas() {
771 // free up the contents of our deque
772 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000773
reed@android.com8a1c16f2008-12-17 15:59:43 +0000774 this->internalRestore(); // restore the last, since we're going away
775
halcanary385fe4d2015-08-26 13:07:48 -0700776 delete fMetaData;
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000777
reed@android.com8a1c16f2008-12-17 15:59:43 +0000778 dec_canvas();
779}
780
reed@android.com8a1c16f2008-12-17 15:59:43 +0000781SkDrawFilter* SkCanvas::getDrawFilter() const {
782 return fMCRec->fFilter;
783}
784
785SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
reed51985e32015-04-11 08:04:56 -0700786 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000787 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
788 return filter;
789}
790
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000791SkMetaData& SkCanvas::getMetaData() {
792 // metadata users are rare, so we lazily allocate it. If that changes we
793 // can decide to just make it a field in the device (rather than a ptr)
halcanary96fcdcc2015-08-27 07:41:13 -0700794 if (nullptr == fMetaData) {
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000795 fMetaData = new SkMetaData;
796 }
797 return *fMetaData;
798}
799
reed@android.com8a1c16f2008-12-17 15:59:43 +0000800///////////////////////////////////////////////////////////////////////////////
801
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000802void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000803 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000804 if (device) {
805 device->flush();
806 }
807}
808
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000809SkISize SkCanvas::getTopLayerSize() const {
810 SkBaseDevice* d = this->getTopDevice();
811 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
812}
813
814SkIPoint SkCanvas::getTopLayerOrigin() const {
815 SkBaseDevice* d = this->getTopDevice();
816 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
817}
818
819SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000820 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000821 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
822}
823
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000824SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000825 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000826 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000827 SkASSERT(rec && rec->fLayer);
828 return rec->fLayer->fDevice;
829}
830
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000831SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000832 if (updateMatrixClip) {
833 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
834 }
reed@google.com9266fed2011-03-30 00:18:03 +0000835 return fMCRec->fTopLayer->fDevice;
836}
837
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000838bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
839 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
840 return false;
841 }
842
843 bool weAllocated = false;
halcanary96fcdcc2015-08-27 07:41:13 -0700844 if (nullptr == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700845 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000846 return false;
847 }
848 weAllocated = true;
849 }
850
reedcf01e312015-05-23 19:14:51 -0700851 SkAutoPixmapUnlock unlocker;
852 if (bitmap->requestLock(&unlocker)) {
853 const SkPixmap& pm = unlocker.pixmap();
854 if (this->readPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), x, y)) {
855 return true;
856 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000857 }
858
859 if (weAllocated) {
halcanary96fcdcc2015-08-27 07:41:13 -0700860 bitmap->setPixelRef(nullptr);
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000861 }
862 return false;
863}
reed@google.com51df9e32010-12-23 19:29:18 +0000864
bsalomon@google.comc6980972011-11-02 19:57:21 +0000865bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000866 SkIRect r = srcRect;
867 const SkISize size = this->getBaseLayerSize();
868 if (!r.intersect(0, 0, size.width(), size.height())) {
869 bitmap->reset();
870 return false;
871 }
872
reed84825042014-09-02 12:50:45 -0700873 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000874 // bitmap will already be reset.
875 return false;
876 }
877 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
878 bitmap->reset();
879 return false;
880 }
881 return true;
882}
883
reed96472de2014-12-10 09:53:42 -0800884bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000885 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000886 if (!device) {
887 return false;
888 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000889 const SkISize size = this->getBaseLayerSize();
mtkleinf0f14112014-12-12 08:46:25 -0800890
reed96472de2014-12-10 09:53:42 -0800891 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
892 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000893 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000894 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000895
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000896 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800897 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000898}
899
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000900bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
901 if (bitmap.getTexture()) {
902 return false;
903 }
reedcf01e312015-05-23 19:14:51 -0700904
905 SkAutoPixmapUnlock unlocker;
906 if (bitmap.requestLock(&unlocker)) {
907 const SkPixmap& pm = unlocker.pixmap();
908 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000909 }
910 return false;
911}
912
913bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
914 int x, int y) {
915 switch (origInfo.colorType()) {
916 case kUnknown_SkColorType:
917 case kIndex_8_SkColorType:
918 return false;
919 default:
920 break;
921 }
halcanary96fcdcc2015-08-27 07:41:13 -0700922 if (nullptr == pixels || rowBytes < origInfo.minRowBytes()) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000923 return false;
924 }
925
926 const SkISize size = this->getBaseLayerSize();
927 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
928 if (!target.intersect(0, 0, size.width(), size.height())) {
929 return false;
930 }
931
932 SkBaseDevice* device = this->getDevice();
933 if (!device) {
934 return false;
935 }
936
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000937 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700938 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000939
940 // if x or y are negative, then we have to adjust pixels
941 if (x > 0) {
942 x = 0;
943 }
944 if (y > 0) {
945 y = 0;
946 }
947 // here x,y are either 0 or negative
948 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
949
reed4af35f32014-06-27 17:47:49 -0700950 // Tell our owning surface to bump its generation ID
reedc83a2972015-07-16 07:40:45 -0700951 const bool completeOverwrite = info.dimensions() == size;
952 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700953
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000954 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000955 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000956}
reed@google.com51df9e32010-12-23 19:29:18 +0000957
junov@google.com4370aed2012-01-18 16:21:08 +0000958SkCanvas* SkCanvas::canvasForDrawIter() {
959 return this;
960}
961
reed@android.com8a1c16f2008-12-17 15:59:43 +0000962//////////////////////////////////////////////////////////////////////////////
963
reed@android.com8a1c16f2008-12-17 15:59:43 +0000964void SkCanvas::updateDeviceCMCache() {
965 if (fDeviceCMDirty) {
966 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700967 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000968 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000969
halcanary96fcdcc2015-08-27 07:41:13 -0700970 if (nullptr == layer->fNext) { // only one layer
971 layer->updateMC(totalMatrix, totalClip, *fClipStack, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000972 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000973 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000974 do {
reed687fa1c2015-04-07 08:00:56 -0700975 layer->updateMC(totalMatrix, clip, *fClipStack, &clip);
halcanary96fcdcc2015-08-27 07:41:13 -0700976 } while ((layer = layer->fNext) != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000977 }
978 fDeviceCMDirty = false;
979 }
980}
981
reed@android.com8a1c16f2008-12-17 15:59:43 +0000982///////////////////////////////////////////////////////////////////////////////
983
reed2ff1fce2014-12-11 07:07:37 -0800984void SkCanvas::checkForDeferredSave() {
985 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800986 this->doSave();
987 }
988}
989
reedf0090cb2014-11-26 08:55:51 -0800990int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800991#ifdef SK_DEBUG
992 int count = 0;
993 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
994 for (;;) {
995 const MCRec* rec = (const MCRec*)iter.next();
996 if (!rec) {
997 break;
998 }
999 count += 1 + rec->fDeferredSaveCount;
1000 }
1001 SkASSERT(count == fSaveCount);
1002#endif
1003 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -08001004}
1005
1006int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -08001007 fSaveCount += 1;
1008 fMCRec->fDeferredSaveCount += 1;
1009 return this->getSaveCount() - 1; // return our prev value
1010}
1011
1012void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -08001013 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -07001014
1015 SkASSERT(fMCRec->fDeferredSaveCount > 0);
1016 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001017 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -08001018}
1019
1020void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -08001021 if (fMCRec->fDeferredSaveCount > 0) {
1022 SkASSERT(fSaveCount > 1);
1023 fSaveCount -= 1;
1024 fMCRec->fDeferredSaveCount -= 1;
1025 } else {
1026 // check for underflow
1027 if (fMCStack.count() > 1) {
1028 this->willRestore();
1029 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -07001030 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001031 this->internalRestore();
1032 this->didRestore();
1033 }
reedf0090cb2014-11-26 08:55:51 -08001034 }
1035}
1036
1037void SkCanvas::restoreToCount(int count) {
1038 // sanity check
1039 if (count < 1) {
1040 count = 1;
1041 }
mtkleinf0f14112014-12-12 08:46:25 -08001042
reedf0090cb2014-11-26 08:55:51 -08001043 int n = this->getSaveCount() - count;
1044 for (int i = 0; i < n; ++i) {
1045 this->restore();
1046 }
1047}
1048
reed2ff1fce2014-12-11 07:07:37 -08001049void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001050 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -07001051 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +00001052 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +00001053
reed687fa1c2015-04-07 08:00:56 -07001054 fClipStack->save();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001055}
1056
reed4960eee2015-12-18 07:09:18 -08001057bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
reed@google.comb93ba452014-03-10 19:47:58 +00001058#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001059 return !(saveLayerFlags & SkCanvas::kDontClipToLayer_PrivateSaveLayerFlag);
reed@google.comb93ba452014-03-10 19:47:58 +00001060#else
1061 return true;
1062#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001063}
1064
reed4960eee2015-12-18 07:09:18 -08001065bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -07001066 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001067 SkIRect clipBounds;
1068 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001069 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +00001070 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001071
reed96e657d2015-03-10 17:30:07 -07001072 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
1073
senorblanco87e066e2015-10-28 11:23:36 -07001074// This is a temporary hack, until individual filters can do their own
1075// bloating, when this will be removed.
1076#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
1077 SkRect storage;
1078#endif
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001079 if (imageFilter) {
reed96e657d2015-03-10 17:30:07 -07001080 imageFilter->filterBounds(clipBounds, ctm, &clipBounds);
senorblanco87e066e2015-10-28 11:23:36 -07001081#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
1082 if (bounds && imageFilter->canComputeFastBounds()) {
1083 imageFilter->computeFastBounds(*bounds, &storage);
1084 bounds = &storage;
1085 } else {
1086 bounds = nullptr;
1087 }
senorblancodb64af32015-12-09 10:11:43 -08001088#else
1089 if (bounds && !imageFilter->canComputeFastBounds()) {
1090 bounds = nullptr;
1091 }
senorblanco87e066e2015-10-28 11:23:36 -07001092#endif
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001093 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001094 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -07001095 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001096 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +00001097
reed96e657d2015-03-10 17:30:07 -07001098 ctm.mapRect(&r, *bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001099 r.roundOut(&ir);
1100 // early exit if the layer's bounds are clipped out
1101 if (!ir.intersect(clipBounds)) {
reed4960eee2015-12-18 07:09:18 -08001102 if (BoundsAffectsClip(saveLayerFlags)) {
reed9b3aa542015-03-11 08:47:12 -07001103 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001104 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001105 }
junov@chromium.orga907ac32012-02-24 21:54:07 +00001106 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001107 }
1108 } else { // no user bounds, so just use the clip
1109 ir = clipBounds;
1110 }
reed180aec42015-03-11 10:39:04 -07001111 SkASSERT(!ir.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001112
reed4960eee2015-12-18 07:09:18 -08001113 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -07001114 // Simplify the current clips since they will be applied properly during restore()
reed9b3aa542015-03-11 08:47:12 -07001115 fCachedLocalClipBoundsDirty = true;
reed687fa1c2015-04-07 08:00:56 -07001116 fClipStack->clipDevRect(ir, SkRegion::kReplace_Op);
reed180aec42015-03-11 10:39:04 -07001117 fMCRec->fRasterClip.setRect(ir);
junov@chromium.orga907ac32012-02-24 21:54:07 +00001118 }
1119
1120 if (intersection) {
1121 *intersection = ir;
1122 }
1123 return true;
1124}
1125
reedbada1882015-12-21 13:09:44 -08001126#ifdef SK_SUPPORT_LEGACY_SAVEFLAGS
reed4960eee2015-12-18 07:09:18 -08001127uint32_t SkCanvas::SaveFlagsToSaveLayerFlags(SaveFlags flags) {
1128 uint32_t layerFlags = 0;
1129
1130 if (0 == (flags & kClipToLayer_SaveFlag)) {
1131 layerFlags |= kDontClipToLayer_PrivateSaveLayerFlag;
reedd990e2f2014-12-22 11:58:30 -08001132 }
reed4960eee2015-12-18 07:09:18 -08001133 if (0 == (flags & kHasAlphaLayer_SaveFlag)) {
1134 layerFlags |= kIsOpaque_SaveLayerFlag;
1135 }
1136 return layerFlags;
1137}
scroggoffe031e2016-01-04 07:16:32 -08001138
1139uint32_t SkCanvas::SaveLayerFlagsToSaveFlags(SaveLayerFlags layerFlags) {
1140 uint32_t saveFlags = 0;
1141
1142 if (0 == (layerFlags & kDontClipToLayer_PrivateSaveLayerFlag)) {
1143 saveFlags |= kClipToLayer_SaveFlag;
1144 }
1145 if (0 == (layerFlags & kIsOpaque_SaveLayerFlag)) {
1146 saveFlags |= kHasAlphaLayer_SaveFlag;
1147 }
1148 return saveFlags;
1149}
reedbada1882015-12-21 13:09:44 -08001150#endif
reed4960eee2015-12-18 07:09:18 -08001151
reed4960eee2015-12-18 07:09:18 -08001152int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
1153 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001154}
1155
reedbada1882015-12-21 13:09:44 -08001156#ifdef SK_SUPPORT_LEGACY_SAVEFLAGS
reed2ff1fce2014-12-11 07:07:37 -08001157int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags) {
reed4960eee2015-12-18 07:09:18 -08001158 return this->saveLayer(SaveLayerRec(bounds, paint, SaveFlagsToSaveLayerFlags(flags)));
reed@google.com8926b162012-03-23 15:36:36 +00001159}
reedbada1882015-12-21 13:09:44 -08001160#endif
reed@google.com8926b162012-03-23 15:36:36 +00001161
reed70ee31b2015-12-10 13:44:45 -08001162int SkCanvas::saveLayerPreserveLCDTextRequests(const SkRect* bounds, const SkPaint* paint) {
reed4960eee2015-12-18 07:09:18 -08001163 return this->saveLayer(SaveLayerRec(bounds, paint, kPreserveLCDText_SaveLayerFlag));
1164}
1165
1166int SkCanvas::saveLayer(const SaveLayerRec& origRec) {
1167 SaveLayerRec rec(origRec);
1168 if (gIgnoreSaveLayerBounds) {
1169 rec.fBounds = nullptr;
1170 }
1171 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
1172 fSaveCount += 1;
1173 this->internalSaveLayer(rec, strategy);
1174 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -08001175}
1176
reedbfd5f172016-01-07 11:28:08 -08001177static void draw_filter_into_device(SkBaseDevice* src, const SkImageFilter* filter,
1178 SkBaseDevice* dst, const SkMatrix& ctm) {
robertphillips7354a4b2015-12-16 05:08:27 -08001179
1180 SkBitmap srcBM;
1181
1182#if SK_SUPPORT_GPU
1183 GrRenderTarget* srcRT = src->accessRenderTarget();
1184 if (srcRT && !srcRT->asTexture() && dst->accessRenderTarget()) {
1185 // When both the src & the dst are on the gpu but the src doesn't have a texture,
1186 // we create a temporary texture for the draw.
1187 // TODO: we should actually only copy the portion of the source needed to apply the image
1188 // filter
1189 GrContext* context = srcRT->getContext();
1190 SkAutoTUnref<GrTexture> tex(context->textureProvider()->createTexture(srcRT->desc(), true));
1191
1192 context->copySurface(tex, srcRT);
1193
1194 GrWrapTextureInBitmap(tex, src->width(), src->height(), src->isOpaque(), &srcBM);
1195 } else
1196#endif
1197 {
1198 srcBM = src->accessBitmap(false);
1199 }
1200
1201 SkCanvas c(dst);
1202
reedbfd5f172016-01-07 11:28:08 -08001203 SkAutoTUnref<SkImageFilter> localF(filter->newWithLocalMatrix(ctm));
robertphillips7354a4b2015-12-16 05:08:27 -08001204 SkPaint p;
reedbfd5f172016-01-07 11:28:08 -08001205 p.setImageFilter(localF);
1206 const SkScalar x = SkIntToScalar(src->getOrigin().x());
1207 const SkScalar y = SkIntToScalar(src->getOrigin().y());
1208 c.drawBitmap(srcBM, x, y, &p);
robertphillips7354a4b2015-12-16 05:08:27 -08001209}
reed70ee31b2015-12-10 13:44:45 -08001210
reed4960eee2015-12-18 07:09:18 -08001211void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
1212 const SkRect* bounds = rec.fBounds;
1213 const SkPaint* paint = rec.fPaint;
1214 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1215
reed@google.comb93ba452014-03-10 19:47:58 +00001216#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001217 saveLayerFlags &= ~kDontClipToLayer_PrivateSaveLayerFlag;
reed@google.comb93ba452014-03-10 19:47:58 +00001218#endif
1219
junov@chromium.orga907ac32012-02-24 21:54:07 +00001220 // do this before we create the layer. We don't call the public save() since
1221 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001222 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001223
1224 fDeviceCMDirty = true;
1225
1226 SkIRect ir;
reed4960eee2015-12-18 07:09:18 -08001227 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, paint ? paint->getImageFilter() : nullptr)) {
reed2ff1fce2014-12-11 07:07:37 -08001228 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001229 }
1230
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001231 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1232 // the clipRectBounds() call above?
1233 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001234 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001235 }
1236
reed4960eee2015-12-18 07:09:18 -08001237 bool isOpaque = SkToBool(saveLayerFlags & kIsOpaque_SaveLayerFlag);
reed8dc0ccb2015-03-20 06:32:52 -07001238 SkPixelGeometry geo = fProps.pixelGeometry();
1239 if (paint) {
reed76033be2015-03-14 10:54:31 -07001240 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001241 if (paint->getImageFilter() || paint->getColorFilter()) {
reed76033be2015-03-14 10:54:31 -07001242 isOpaque = false;
reed8dc0ccb2015-03-20 06:32:52 -07001243 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001244 }
1245 }
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001246 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
1247 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001248
reedb2db8982014-11-13 12:41:02 -08001249 SkBaseDevice* device = this->getTopDevice();
halcanary96fcdcc2015-08-27 07:41:13 -07001250 if (nullptr == device) {
reedb2db8982014-11-13 12:41:02 -08001251 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001252 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001253 }
reedb2db8982014-11-13 12:41:02 -08001254
reed61f501f2015-04-29 08:34:00 -07001255 bool forceSpriteOnRestore = false;
1256 {
reed70ee31b2015-12-10 13:44:45 -08001257 const bool preserveLCDText = kOpaque_SkAlphaType == info.alphaType() ||
reed4960eee2015-12-18 07:09:18 -08001258 (saveLayerFlags & kPreserveLCDText_SaveLayerFlag);
reeddaa57bf2015-05-15 10:39:17 -07001259 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
reed70ee31b2015-12-10 13:44:45 -08001260 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
1261 preserveLCDText, false);
reed61f501f2015-04-29 08:34:00 -07001262 SkBaseDevice* newDev = device->onCreateDevice(createInfo, paint);
halcanary96fcdcc2015-08-27 07:41:13 -07001263 if (nullptr == newDev) {
reed61f501f2015-04-29 08:34:00 -07001264 // If onCreateDevice didn't succeed, try raster (e.g. PDF couldn't handle the paint)
robertphillips9a53fd72015-06-22 09:46:59 -07001265 const SkSurfaceProps surfaceProps(fProps.flags(), createInfo.fPixelGeometry);
1266 newDev = SkBitmapDevice::Create(createInfo.fInfo, surfaceProps);
halcanary96fcdcc2015-08-27 07:41:13 -07001267 if (nullptr == newDev) {
reed61f501f2015-04-29 08:34:00 -07001268 SkErrorInternals::SetError(kInternalError_SkError,
1269 "Unable to create device for layer.");
1270 return;
1271 }
1272 forceSpriteOnRestore = true;
1273 }
1274 device = newDev;
bungeman@google.come25c6842011-08-17 14:53:54 +00001275 }
reed@google.com6f8f2922011-03-04 22:27:10 +00001276 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips7354a4b2015-12-16 05:08:27 -08001277
reedbfd5f172016-01-07 11:28:08 -08001278 if (rec.fBackdrop) {
1279 draw_filter_into_device(fMCRec->fTopLayer->fDevice, rec.fBackdrop, device, fMCRec->fMatrix);
robertphillips7354a4b2015-12-16 05:08:27 -08001280 }
1281
halcanary385fe4d2015-08-26 13:07:48 -07001282 DeviceCM* layer =
1283 new DeviceCM(device, paint, this, fConservativeRasterClip, forceSpriteOnRestore);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001284 device->unref();
1285
1286 layer->fNext = fMCRec->fTopLayer;
1287 fMCRec->fLayer = layer;
1288 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reed@android.com8a1c16f2008-12-17 15:59:43 +00001289}
1290
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001291int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001292 if (0xFF == alpha) {
1293 return this->saveLayer(bounds, nullptr);
1294 } else {
1295 SkPaint tmpPaint;
1296 tmpPaint.setAlpha(alpha);
1297 return this->saveLayer(bounds, &tmpPaint);
1298 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001299}
1300
reedbada1882015-12-21 13:09:44 -08001301#ifdef SK_SUPPORT_LEGACY_SAVEFLAGS
reed@android.com8a1c16f2008-12-17 15:59:43 +00001302int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
1303 SaveFlags flags) {
1304 if (0xFF == alpha) {
halcanary96fcdcc2015-08-27 07:41:13 -07001305 return this->saveLayer(bounds, nullptr, flags);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001306 } else {
1307 SkPaint tmpPaint;
1308 tmpPaint.setAlpha(alpha);
1309 return this->saveLayer(bounds, &tmpPaint, flags);
1310 }
1311}
reedbada1882015-12-21 13:09:44 -08001312#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001313
reed@android.com8a1c16f2008-12-17 15:59:43 +00001314void SkCanvas::internalRestore() {
1315 SkASSERT(fMCStack.count() != 0);
1316
1317 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001318 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001319
reed687fa1c2015-04-07 08:00:56 -07001320 fClipStack->restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001321
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001322 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001323 DeviceCM* layer = fMCRec->fLayer; // may be null
1324 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001325 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001326
1327 // now do the normal restore()
1328 fMCRec->~MCRec(); // balanced in save()
1329 fMCStack.pop_back();
1330 fMCRec = (MCRec*)fMCStack.back();
1331
1332 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1333 since if we're being recorded, we don't want to record this (the
1334 recorder will have already recorded the restore).
1335 */
bsalomon49f085d2014-09-05 13:34:00 -07001336 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001337 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001338 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001339 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
reed61f501f2015-04-29 08:34:00 -07001340 layer->fPaint, layer->fDeviceIsBitmapDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001341 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001342 fDeviceCMDirty = true;
halcanary385fe4d2015-08-26 13:07:48 -07001343 delete layer;
reedb679ca82015-04-07 04:40:48 -07001344 } else {
1345 // we're at the root
reeda499f902015-05-01 09:34:31 -07001346 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001347 layer->~DeviceCM();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001348 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001349 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001350}
1351
reed4a8126e2014-09-22 07:29:03 -07001352SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001353 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001354 props = &fProps;
1355 }
1356 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001357}
1358
reed4a8126e2014-09-22 07:29:03 -07001359SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001360 SkBaseDevice* dev = this->getDevice();
halcanary96fcdcc2015-08-27 07:41:13 -07001361 return dev ? dev->newSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001362}
1363
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001364SkImageInfo SkCanvas::imageInfo() const {
1365 SkBaseDevice* dev = this->getDevice();
1366 if (dev) {
1367 return dev->imageInfo();
1368 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001369 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001370 }
1371}
1372
1373const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
reed884e97c2015-05-26 11:31:54 -07001374 SkPixmap pmap;
1375 if (!this->onPeekPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001376 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001377 }
1378 if (info) {
1379 *info = pmap.info();
1380 }
1381 if (rowBytes) {
1382 *rowBytes = pmap.rowBytes();
1383 }
1384 return pmap.addr();
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001385}
1386
reed884e97c2015-05-26 11:31:54 -07001387bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001388 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001389 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001390}
1391
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001392void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001393 SkPixmap pmap;
1394 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001395 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001396 }
1397 if (info) {
1398 *info = pmap.info();
1399 }
1400 if (rowBytes) {
1401 *rowBytes = pmap.rowBytes();
1402 }
1403 if (origin) {
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001404 *origin = this->getTopDevice(false)->getOrigin();
1405 }
reed884e97c2015-05-26 11:31:54 -07001406 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001407}
1408
reed884e97c2015-05-26 11:31:54 -07001409bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001410 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001411 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001412}
1413
reed@android.com8a1c16f2008-12-17 15:59:43 +00001414/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001415
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001416void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed61f501f2015-04-29 08:34:00 -07001417 const SkPaint* paint, bool deviceIsBitmapDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001418 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001419 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001420 paint = &tmp;
1421 }
reed@google.com4b226022011-01-11 18:32:13 +00001422
reed@google.com8926b162012-03-23 15:36:36 +00001423 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001424 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001425 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001426 paint = &looper.paint();
1427 SkImageFilter* filter = paint->getImageFilter();
1428 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001429 if (filter && !dstDev->canHandleImageFilter(filter)) {
reed88d064d2015-10-12 11:30:02 -07001430 SkImageFilter::DeviceProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001431 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001432 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001433 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001434 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001435 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblancodb64af32015-12-09 10:11:43 -08001436#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001437 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblancodb64af32015-12-09 10:11:43 -08001438#else
1439 SkIRect clipBounds = iter.fClip->getBounds().makeOffset(-pos.x(), -pos.y());
1440#endif
senorblancobe129b22014-08-08 07:14:35 -07001441 SkAutoTUnref<SkImageFilter::Cache> cache(dstDev->getImageFilterCache());
reedc9b5f8b2015-10-22 13:20:20 -07001442 SkImageFilter::Context ctx(matrix, clipBounds, cache.get(),
1443 SkImageFilter::kApprox_SizeConstraint);
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001444 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001445 SkPaint tmpUnfiltered(*paint);
halcanary96fcdcc2015-08-27 07:41:13 -07001446 tmpUnfiltered.setImageFilter(nullptr);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001447 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1448 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001449 }
reed61f501f2015-04-29 08:34:00 -07001450 } else if (deviceIsBitmapDevice) {
reed9572a102015-05-26 19:22:17 -07001451 const SkBitmap& src = static_cast<SkBitmapDevice*>(srcDev)->fBitmap;
reed61f501f2015-04-29 08:34:00 -07001452 dstDev->drawSprite(iter, src, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001453 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001454 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001455 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001456 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001457 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001458}
1459
reed32704672015-12-16 08:27:10 -08001460/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001461
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001462void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001463 SkMatrix m;
1464 m.setTranslate(dx, dy);
1465 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001466}
1467
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001468void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001469 SkMatrix m;
1470 m.setScale(sx, sy);
1471 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001472}
1473
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001474void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001475 SkMatrix m;
1476 m.setRotate(degrees);
1477 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001478}
1479
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001480void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001481 SkMatrix m;
1482 m.setSkew(sx, sy);
1483 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001484}
1485
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001486void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001487 if (matrix.isIdentity()) {
1488 return;
1489 }
1490
reed2ff1fce2014-12-11 07:07:37 -08001491 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001492 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001493 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001494 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001495
1496 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001497}
1498
reed86a17e72015-05-14 12:25:22 -07001499void SkCanvas::setMatrix(const SkMatrix& matrix) {
1500 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001501 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001502 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001503 fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001504 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001505}
1506
reed@android.com8a1c16f2008-12-17 15:59:43 +00001507void SkCanvas::resetMatrix() {
1508 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001509
reed@android.com8a1c16f2008-12-17 15:59:43 +00001510 matrix.reset();
1511 this->setMatrix(matrix);
1512}
1513
1514//////////////////////////////////////////////////////////////////////////////
1515
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001516void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001517 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001518 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1519 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001520}
1521
1522void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001523#ifdef SK_ENABLE_CLIP_QUICKREJECT
1524 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001525 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001526 return false;
1527 }
1528
reed@google.com3b3e8952012-08-16 20:53:31 +00001529 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001530 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001531 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001532
reed687fa1c2015-04-07 08:00:56 -07001533 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001534 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001535 }
1536 }
1537#endif
1538
bsalomonac8cabd2015-11-20 18:53:07 -08001539 if (!fAllowSoftClip) {
1540 edgeStyle = kHard_ClipEdgeStyle;
1541 }
reed90ba0952015-11-20 13:42:47 -08001542
reedc64eff52015-11-21 12:39:45 -08001543 const bool rectStaysRect = fMCRec->fMatrix.rectStaysRect();
1544 SkRect devR;
1545 if (rectStaysRect) {
1546 fMCRec->fMatrix.mapRect(&devR, rect);
1547 }
bsalomonac8cabd2015-11-20 18:53:07 -08001548
reedc64eff52015-11-21 12:39:45 -08001549 // Check if we can quick-accept the clip call (and do nothing)
1550 //
1551 // TODO: investigate if a (conservative) version of this could be done in ::clipRect,
1552 // so that subclasses (like PictureRecording) didn't see unnecessary clips, which in turn
1553 // might allow lazy save/restores to eliminate entire save/restore blocks.
1554 //
1555 if (SkRegion::kIntersect_Op == op &&
1556 kHard_ClipEdgeStyle == edgeStyle
1557 && rectStaysRect)
1558 {
1559 if (devR.round().contains(fMCRec->fRasterClip.getBounds())) {
1560#if 0
1561 SkDebugf("------- ignored clipRect [%g %g %g %g]\n",
1562 rect.left(), rect.top(), rect.right(), rect.bottom());
1563#endif
1564 return;
1565 }
1566 }
1567
1568 AutoValidateClip avc(this);
1569
1570 fDeviceCMDirty = true;
1571 fCachedLocalClipBoundsDirty = true;
1572
1573 if (rectStaysRect) {
1574 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1575 fClipStack->clipDevRect(devR, op, isAA);
1576 fMCRec->fRasterClip.op(devR, this->getBaseLayerSize(), op, isAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001577 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001578 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001579 // and clip against that, since it can handle any matrix. However, to
1580 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1581 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001582 SkPath path;
1583
1584 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001585 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001586 }
1587}
1588
reed73e714e2014-09-04 09:02:23 -07001589static void rasterclip_path(SkRasterClip* rc, const SkCanvas* canvas, const SkPath& devPath,
1590 SkRegion::Op op, bool doAA) {
reedd64c9482014-09-05 17:37:38 -07001591 rc->op(devPath, canvas->getBaseLayerSize(), op, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001592}
1593
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001594void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001595 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001596 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001597 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001598 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1599 } else {
1600 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001601 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001602}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001603
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001604void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001605 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001606 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001607 AutoValidateClip avc(this);
1608
1609 fDeviceCMDirty = true;
1610 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001611 if (!fAllowSoftClip) {
1612 edgeStyle = kHard_ClipEdgeStyle;
1613 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001614
reed687fa1c2015-04-07 08:00:56 -07001615 fClipStack->clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001616
robertphillips125f19a2015-11-23 09:00:05 -08001617 fMCRec->fRasterClip.op(transformedRRect, this->getBaseLayerSize(), op,
1618 kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001619 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001620 }
1621
1622 SkPath path;
1623 path.addRRect(rrect);
1624 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001625 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001626}
1627
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001628void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001629 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001630 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001631
1632 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1633 SkRect r;
1634 if (path.isRect(&r)) {
1635 this->onClipRect(r, op, edgeStyle);
1636 return;
1637 }
1638 SkRRect rrect;
1639 if (path.isOval(&r)) {
1640 rrect.setOval(r);
1641 this->onClipRRect(rrect, op, edgeStyle);
1642 return;
1643 }
1644 if (path.isRRect(&rrect)) {
1645 this->onClipRRect(rrect, op, edgeStyle);
1646 return;
1647 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001648 }
robertphillips39f05382015-11-24 09:30:12 -08001649
1650 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001651}
1652
1653void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001654#ifdef SK_ENABLE_CLIP_QUICKREJECT
1655 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001656 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001657 return false;
1658 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001659
reed@google.com3b3e8952012-08-16 20:53:31 +00001660 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001661 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001662 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001663
reed687fa1c2015-04-07 08:00:56 -07001664 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001665 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001666 }
1667 }
1668#endif
1669
reed@google.com5c3d1472011-02-22 19:12:23 +00001670 AutoValidateClip avc(this);
1671
reed@android.com8a1c16f2008-12-17 15:59:43 +00001672 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001673 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001674 if (!fAllowSoftClip) {
1675 edgeStyle = kHard_ClipEdgeStyle;
1676 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001677
1678 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001679 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001680
reed@google.comfe701122011-11-08 19:41:23 +00001681 // Check if the transfomation, or the original path itself
1682 // made us empty. Note this can also happen if we contained NaN
1683 // values. computing the bounds detects this, and will set our
1684 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1685 if (devPath.getBounds().isEmpty()) {
1686 // resetting the path will remove any NaN or other wanky values
1687 // that might upset our scan converter.
1688 devPath.reset();
1689 }
1690
reed@google.com5c3d1472011-02-22 19:12:23 +00001691 // if we called path.swap() we could avoid a deep copy of this path
reed687fa1c2015-04-07 08:00:56 -07001692 fClipStack->clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001693
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001694 if (fAllowSimplifyClip) {
fmalita1a481fe2015-02-04 07:39:34 -08001695 bool clipIsAA = getClipStack()->asPath(&devPath);
1696 if (clipIsAA) {
1697 edgeStyle = kSoft_ClipEdgeStyle;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001698 }
fmalita1a481fe2015-02-04 07:39:34 -08001699
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001700 op = SkRegion::kReplace_Op;
1701 }
1702
reed73e714e2014-09-04 09:02:23 -07001703 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001704}
1705
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001706void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed2ff1fce2014-12-11 07:07:37 -08001707 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001708 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001709}
1710
1711void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001712 AutoValidateClip avc(this);
1713
reed@android.com8a1c16f2008-12-17 15:59:43 +00001714 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001715 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001716
reed@google.com5c3d1472011-02-22 19:12:23 +00001717 // todo: signal fClipStack that we have a region, and therefore (I guess)
1718 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001719 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001720
reed1f836ee2014-07-07 07:49:34 -07001721 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001722}
1723
reed@google.com819c9212011-02-23 18:56:55 +00001724#ifdef SK_DEBUG
1725void SkCanvas::validateClip() const {
1726 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001727 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001728 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001729 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001730 return;
1731 }
1732
reed@google.com819c9212011-02-23 18:56:55 +00001733 SkIRect ir;
1734 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001735 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001736
reed687fa1c2015-04-07 08:00:56 -07001737 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001738 const SkClipStack::Element* element;
halcanary96fcdcc2015-08-27 07:41:13 -07001739 while ((element = iter.next()) != nullptr) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001740 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001741 case SkClipStack::Element::kRect_Type:
1742 element->getRect().round(&ir);
1743 tmpClip.op(ir, element->getOp());
1744 break;
1745 case SkClipStack::Element::kEmpty_Type:
1746 tmpClip.setEmpty();
1747 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001748 default: {
1749 SkPath path;
1750 element->asPath(&path);
reed73e714e2014-09-04 09:02:23 -07001751 rasterclip_path(&tmpClip, this, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001752 break;
1753 }
reed@google.com819c9212011-02-23 18:56:55 +00001754 }
1755 }
reed@google.com819c9212011-02-23 18:56:55 +00001756}
1757#endif
1758
reed@google.com90c07ea2012-04-13 13:50:27 +00001759void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001760 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001761 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001762
halcanary96fcdcc2015-08-27 07:41:13 -07001763 while ((element = iter.next()) != nullptr) {
fmalitac3b589a2014-06-05 12:40:07 -07001764 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001765 }
1766}
1767
reed@google.com5c3d1472011-02-22 19:12:23 +00001768///////////////////////////////////////////////////////////////////////////////
1769
reed@google.com754de5f2014-02-24 19:38:20 +00001770bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001771 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001772}
1773
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001774bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001775 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001776}
1777
reed@google.com3b3e8952012-08-16 20:53:31 +00001778bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001779 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001780 return true;
1781
reed1f836ee2014-07-07 07:49:34 -07001782 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001783 return true;
1784 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001785
reed1f836ee2014-07-07 07:49:34 -07001786 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001787 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001788 fMCRec->fMatrix.mapRect(&dst, rect);
reedb07a94f2014-11-19 05:03:18 -08001789 return !SkIRect::Intersects(dst.roundOut(), fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001790 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001791 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001792
reed@android.coma380ae42009-07-21 01:17:02 +00001793 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001794 // TODO: should we use | instead, or compare all 4 at once?
1795 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001796 return true;
1797 }
reed@google.comc0784db2013-12-13 21:16:12 +00001798 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001799 return true;
1800 }
1801 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001802 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001803}
1804
reed@google.com3b3e8952012-08-16 20:53:31 +00001805bool SkCanvas::quickReject(const SkPath& path) const {
1806 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001807}
1808
reed@google.com3b3e8952012-08-16 20:53:31 +00001809bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001810 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001811 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001812 return false;
1813 }
1814
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001815 SkMatrix inverse;
1816 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001817 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001818 if (bounds) {
1819 bounds->setEmpty();
1820 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001821 return false;
1822 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001823
bsalomon49f085d2014-09-05 13:34:00 -07001824 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001825 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001826 // adjust it outwards in case we are antialiasing
1827 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001828
reed@google.com8f4d2302013-12-17 16:44:46 +00001829 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1830 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001831 inverse.mapRect(bounds, r);
1832 }
1833 return true;
1834}
1835
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001836bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001837 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001838 if (clip.isEmpty()) {
1839 if (bounds) {
1840 bounds->setEmpty();
1841 }
1842 return false;
1843 }
1844
bsalomon49f085d2014-09-05 13:34:00 -07001845 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001846 *bounds = clip.getBounds();
1847 }
1848 return true;
1849}
1850
reed@android.com8a1c16f2008-12-17 15:59:43 +00001851const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001852 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001853}
1854
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001855const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001856 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001857}
1858
reed@google.com9c135db2014-03-12 18:28:35 +00001859GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1860 SkBaseDevice* dev = this->getTopDevice();
halcanary96fcdcc2015-08-27 07:41:13 -07001861 return dev ? dev->accessRenderTarget() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001862}
1863
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001864GrContext* SkCanvas::getGrContext() {
1865#if SK_SUPPORT_GPU
1866 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07001867 if (device) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001868 GrRenderTarget* renderTarget = device->accessRenderTarget();
bsalomon49f085d2014-09-05 13:34:00 -07001869 if (renderTarget) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001870 return renderTarget->getContext();
1871 }
1872 }
1873#endif
1874
halcanary96fcdcc2015-08-27 07:41:13 -07001875 return nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001876
1877}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001878
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001879void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1880 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001881 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001882 if (outer.isEmpty()) {
1883 return;
1884 }
1885 if (inner.isEmpty()) {
1886 this->drawRRect(outer, paint);
1887 return;
1888 }
1889
1890 // We don't have this method (yet), but technically this is what we should
1891 // be able to assert...
1892 // SkASSERT(outer.contains(inner));
1893 //
1894 // For now at least check for containment of bounds
1895 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1896
1897 this->onDrawDRRect(outer, inner, paint);
1898}
1899
reed41af9662015-01-05 07:49:08 -08001900// These need to stop being virtual -- clients need to override the onDraw... versions
1901
1902void SkCanvas::drawPaint(const SkPaint& paint) {
1903 this->onDrawPaint(paint);
1904}
1905
1906void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1907 this->onDrawRect(r, paint);
1908}
1909
1910void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1911 this->onDrawOval(r, paint);
1912}
1913
1914void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1915 this->onDrawRRect(rrect, paint);
1916}
1917
1918void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1919 this->onDrawPoints(mode, count, pts, paint);
1920}
1921
1922void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
1923 const SkPoint texs[], const SkColor colors[], SkXfermode* xmode,
1924 const uint16_t indices[], int indexCount, const SkPaint& paint) {
1925 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
1926 indices, indexCount, paint);
1927}
1928
1929void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1930 this->onDrawPath(path, paint);
1931}
1932
reeda85d4d02015-05-06 12:56:48 -07001933void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001934 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001935 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001936}
1937
reede47829b2015-08-06 10:02:53 -07001938void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, 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 if (dst.isEmpty() || src.isEmpty()) {
1942 return;
1943 }
1944 this->onDrawImageRect(image, &src, dst, paint, constraint);
1945}
reed41af9662015-01-05 07:49:08 -08001946
reed84984ef2015-07-17 07:09:43 -07001947void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1948 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001949 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001950 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001951}
1952
reede47829b2015-08-06 10:02:53 -07001953void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
1954 SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001955 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001956 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
1957 constraint);
1958}
reede47829b2015-08-06 10:02:53 -07001959
reed4c21dc52015-06-25 12:32:03 -07001960void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1961 const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001962 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07001963 if (dst.isEmpty()) {
1964 return;
1965 }
1966 if (!SkNinePatchIter::Valid(image->width(), image->height(), center)) {
reede47829b2015-08-06 10:02:53 -07001967 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001968 }
1969 this->onDrawImageNine(image, center, dst, paint);
1970}
1971
reed41af9662015-01-05 07:49:08 -08001972void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001973 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001974 return;
1975 }
reed41af9662015-01-05 07:49:08 -08001976 this->onDrawBitmap(bitmap, dx, dy, paint);
1977}
1978
reede47829b2015-08-06 10:02:53 -07001979void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07001980 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001981 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07001982 return;
1983 }
reede47829b2015-08-06 10:02:53 -07001984 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08001985}
1986
reed84984ef2015-07-17 07:09:43 -07001987void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
1988 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001989 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001990}
1991
reede47829b2015-08-06 10:02:53 -07001992void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
1993 SrcRectConstraint constraint) {
1994 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
1995 constraint);
1996}
reede47829b2015-08-06 10:02:53 -07001997
reed41af9662015-01-05 07:49:08 -08001998void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
1999 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07002000 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002001 return;
2002 }
reed4c21dc52015-06-25 12:32:03 -07002003 if (!SkNinePatchIter::Valid(bitmap.width(), bitmap.height(), center)) {
reeda5517e22015-07-14 10:54:12 -07002004 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002005 }
reed41af9662015-01-05 07:49:08 -08002006 this->onDrawBitmapNine(bitmap, center, dst, paint);
2007}
2008
reed71c3c762015-06-24 10:29:17 -07002009void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2010 const SkColor colors[], int count, SkXfermode::Mode mode,
2011 const SkRect* cull, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002012 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002013 if (count <= 0) {
2014 return;
2015 }
2016 SkASSERT(atlas);
2017 SkASSERT(xform);
2018 SkASSERT(tex);
2019 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
2020}
2021
reede47829b2015-08-06 10:02:53 -07002022void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2023 const SkPaint* paint, SrcRectConstraint constraint) {
2024 if (src) {
2025 this->drawImageRect(image, *src, dst, paint, constraint);
2026 } else {
2027 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2028 dst, paint, constraint);
2029 }
2030}
2031void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2032 const SkPaint* paint, SrcRectConstraint constraint) {
2033 if (src) {
2034 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2035 } else {
2036 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2037 dst, paint, constraint);
2038 }
2039}
2040
reed@android.com8a1c16f2008-12-17 15:59:43 +00002041//////////////////////////////////////////////////////////////////////////////
2042// These are the virtual drawing methods
2043//////////////////////////////////////////////////////////////////////////////
2044
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002045void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002046 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002047 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2048 }
2049}
2050
reed41af9662015-01-05 07:49:08 -08002051void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002052 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002053 this->internalDrawPaint(paint);
2054}
2055
2056void SkCanvas::internalDrawPaint(const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002057 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002058
2059 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002060 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002061 }
2062
reed@google.com4e2b3d32011-04-07 14:18:59 +00002063 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002064}
2065
reed41af9662015-01-05 07:49:08 -08002066void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2067 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002068 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002069 if ((long)count <= 0) {
2070 return;
2071 }
2072
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002073 SkRect r, storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002074 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002075 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002076 // special-case 2 points (common for drawing a single line)
2077 if (2 == count) {
2078 r.set(pts[0], pts[1]);
2079 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00002080 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002081 }
senorblanco87e066e2015-10-28 11:23:36 -07002082 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2083 return;
2084 }
2085 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002086 }
reed@google.coma584aed2012-05-16 14:06:02 +00002087
halcanary96fcdcc2015-08-27 07:41:13 -07002088 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002089
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002090 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002091
reed@android.com8a1c16f2008-12-17 15:59:43 +00002092 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002093 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002094 }
reed@google.com4b226022011-01-11 18:32:13 +00002095
reed@google.com4e2b3d32011-04-07 14:18:59 +00002096 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002097}
2098
reed41af9662015-01-05 07:49:08 -08002099void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002100 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002101 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002102 const SkRect* bounds = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002103 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08002104 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
2105 // To prevent accidental rejecting at this stage, we have to sort it before we check.
2106 SkRect tmp(r);
2107 tmp.sort();
2108
senorblanco87e066e2015-10-28 11:23:36 -07002109 if (this->quickReject(paint.computeFastBounds(tmp, &storage))) {
2110 return;
2111 }
2112 bounds = &r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002113 }
reed@google.com4b226022011-01-11 18:32:13 +00002114
reedc83a2972015-07-16 07:40:45 -07002115 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002116
2117 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002118 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002119 }
2120
reed@google.com4e2b3d32011-04-07 14:18:59 +00002121 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002122}
2123
reed41af9662015-01-05 07:49:08 -08002124void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002125 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002126 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002127 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002128 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002129 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2130 return;
2131 }
2132 bounds = &oval;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002133 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002134
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002135 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002136
2137 while (iter.next()) {
2138 iter.fDevice->drawOval(iter, oval, looper.paint());
2139 }
2140
2141 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002142}
2143
reed41af9662015-01-05 07:49:08 -08002144void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002145 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002146 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002147 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002148 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002149 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2150 return;
2151 }
2152 bounds = &rrect.getBounds();
reed@google.com4ed0fb72012-12-12 20:48:18 +00002153 }
2154
2155 if (rrect.isRect()) {
2156 // call the non-virtual version
2157 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002158 return;
2159 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002160 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002161 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2162 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002163 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002164
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002165 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002166
2167 while (iter.next()) {
2168 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2169 }
2170
2171 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002172}
2173
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002174void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2175 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002176 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002177 const SkRect* bounds = nullptr;
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002178 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002179 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2180 return;
2181 }
2182 bounds = &outer.getBounds();
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002183 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002184
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002185 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002186
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002187 while (iter.next()) {
2188 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2189 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002190
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002191 LOOPER_END
2192}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002193
reed41af9662015-01-05 07:49:08 -08002194void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002195 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002196 if (!path.isFinite()) {
2197 return;
2198 }
2199
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002200 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002201 const SkRect* bounds = nullptr;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002202 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002203 const SkRect& pathBounds = path.getBounds();
senorblanco87e066e2015-10-28 11:23:36 -07002204 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2205 return;
2206 }
2207 bounds = &pathBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002208 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002209
2210 const SkRect& r = path.getBounds();
2211 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002212 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002213 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002214 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002215 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002216 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002217
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002218 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002219
2220 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002221 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002222 }
2223
reed@google.com4e2b3d32011-04-07 14:18:59 +00002224 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002225}
2226
reed262a71b2015-12-05 13:07:27 -08002227bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002228 if (!paint.getImageFilter()) {
2229 return false;
2230 }
2231
2232 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002233 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002234 return false;
2235 }
2236
2237 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2238 // Once we can filter and the filter will return a result larger than itself, we should be
2239 // able to remove this constraint.
2240 // skbug.com/4526
2241 //
2242 SkPoint pt;
2243 ctm.mapXY(x, y, &pt);
2244 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2245 return ir.contains(fMCRec->fRasterClip.getBounds());
2246}
2247
reeda85d4d02015-05-06 12:56:48 -07002248void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002249 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002250 SkRect bounds = SkRect::MakeXYWH(x, y,
2251 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002252 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002253 SkRect tmp = bounds;
2254 if (paint) {
2255 paint->computeFastBounds(tmp, &tmp);
2256 }
2257 if (this->quickReject(tmp)) {
2258 return;
2259 }
reeda85d4d02015-05-06 12:56:48 -07002260 }
2261
2262 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002263 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002264 paint = lazy.init();
2265 }
reed262a71b2015-12-05 13:07:27 -08002266
2267 const bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2268 *paint);
2269 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
2270
reeda85d4d02015-05-06 12:56:48 -07002271 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002272 const SkPaint& pnt = looper.paint();
2273 if (drawAsSprite && pnt.getImageFilter()) {
2274 SkBitmap bitmap;
2275 if (as_IB(image)->asBitmapForImageFilters(&bitmap)) {
2276 SkPoint pt;
2277 iter.fMatrix->mapXY(x, y, &pt);
2278 iter.fDevice->drawBitmapAsSprite(iter, bitmap,
2279 SkScalarRoundToInt(pt.fX),
2280 SkScalarRoundToInt(pt.fY), pnt);
2281 }
2282 } else {
2283 iter.fDevice->drawImage(iter, image, x, y, pnt);
2284 }
reeda85d4d02015-05-06 12:56:48 -07002285 }
2286
2287 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002288}
2289
reed41af9662015-01-05 07:49:08 -08002290void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002291 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002292 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
halcanary96fcdcc2015-08-27 07:41:13 -07002293 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002294 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002295 if (paint) {
2296 paint->computeFastBounds(dst, &storage);
2297 }
2298 if (this->quickReject(storage)) {
2299 return;
2300 }
reeda85d4d02015-05-06 12:56:48 -07002301 }
2302 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002303 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002304 paint = lazy.init();
2305 }
2306
senorblancoc41e7e12015-12-07 12:51:30 -08002307 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002308 image->isOpaque())
reeda85d4d02015-05-06 12:56:48 -07002309
2310 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002311 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002312 }
2313
2314 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002315}
2316
reed41af9662015-01-05 07:49:08 -08002317void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002318 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002319 SkDEBUGCODE(bitmap.validate();)
2320
reed33366972015-10-08 09:22:02 -07002321 if (bitmap.drawsNothing()) {
2322 return;
2323 }
2324
2325 SkLazyPaint lazy;
2326 if (nullptr == paint) {
2327 paint = lazy.init();
2328 }
2329
2330 const SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2331
2332 SkRect storage;
2333 const SkRect* bounds = nullptr;
2334 if (paint->canComputeFastBounds()) {
2335 bitmap.getBounds(&storage);
2336 matrix.mapRect(&storage);
senorblanco87e066e2015-10-28 11:23:36 -07002337 SkRect tmp = storage;
2338 if (this->quickReject(paint->computeFastBounds(tmp, &tmp))) {
2339 return;
2340 }
2341 bounds = &storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002342 }
reed@google.com4b226022011-01-11 18:32:13 +00002343
reed262a71b2015-12-05 13:07:27 -08002344 const bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(),
2345 bitmap.height(), *paint);
2346 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds)
reed33366972015-10-08 09:22:02 -07002347
2348 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002349 const SkPaint& pnt = looper.paint();
2350 if (drawAsSprite && pnt.getImageFilter()) {
2351 SkPoint pt;
2352 iter.fMatrix->mapXY(x, y, &pt);
2353 iter.fDevice->drawBitmapAsSprite(iter, bitmap,
2354 SkScalarRoundToInt(pt.fX),
2355 SkScalarRoundToInt(pt.fY), pnt);
2356 } else {
2357 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
2358 }
reed33366972015-10-08 09:22:02 -07002359 }
reed262a71b2015-12-05 13:07:27 -08002360
reed33366972015-10-08 09:22:02 -07002361 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002362}
2363
reed@google.com9987ec32011-09-07 11:57:52 +00002364// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002365void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002366 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002367 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002368 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002369 return;
2370 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002371
halcanary96fcdcc2015-08-27 07:41:13 -07002372 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002373 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002374 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2375 return;
2376 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002377 }
reed@google.com3d608122011-11-21 15:16:16 +00002378
reed@google.com33535f32012-09-25 15:37:50 +00002379 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002380 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002381 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002382 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002383
senorblancoc41e7e12015-12-07 12:51:30 -08002384 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002385 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002386
reed@google.com33535f32012-09-25 15:37:50 +00002387 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002388 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002389 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002390
reed@google.com33535f32012-09-25 15:37:50 +00002391 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002392}
2393
reed41af9662015-01-05 07:49:08 -08002394void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002395 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002396 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002397 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002398 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002399}
2400
reed4c21dc52015-06-25 12:32:03 -07002401void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2402 const SkPaint* paint) {
2403 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
2404
halcanary96fcdcc2015-08-27 07:41:13 -07002405 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002406 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002407 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2408 return;
2409 }
reed@google.com3d608122011-11-21 15:16:16 +00002410 }
reed4c21dc52015-06-25 12:32:03 -07002411
2412 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002413 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002414 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002415 }
reed4c21dc52015-06-25 12:32:03 -07002416
senorblancoc41e7e12015-12-07 12:51:30 -08002417 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
reed4c21dc52015-06-25 12:32:03 -07002418
2419 while (iter.next()) {
2420 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002421 }
reed4c21dc52015-06-25 12:32:03 -07002422
2423 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002424}
2425
reed41af9662015-01-05 07:49:08 -08002426void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2427 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002428 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002429 SkDEBUGCODE(bitmap.validate();)
2430
halcanary96fcdcc2015-08-27 07:41:13 -07002431 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002432 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002433 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2434 return;
2435 }
reed4c21dc52015-06-25 12:32:03 -07002436 }
2437
2438 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002439 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002440 paint = lazy.init();
2441 }
2442
senorblancoc41e7e12015-12-07 12:51:30 -08002443 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
reed4c21dc52015-06-25 12:32:03 -07002444
2445 while (iter.next()) {
2446 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2447 }
2448
2449 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002450}
2451
reed@google.comf67e4cf2011-03-15 20:56:58 +00002452class SkDeviceFilteredPaint {
2453public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002454 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002455 uint32_t filteredFlags = device->filterTextFlags(paint);
2456 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002457 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002458 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002459 fPaint = newPaint;
2460 } else {
2461 fPaint = &paint;
2462 }
2463 }
2464
reed@google.comf67e4cf2011-03-15 20:56:58 +00002465 const SkPaint& paint() const { return *fPaint; }
2466
2467private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002468 const SkPaint* fPaint;
2469 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002470};
2471
bungeman@google.com52c748b2011-08-22 21:30:43 +00002472void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2473 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002474 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002475 draw.fDevice->drawRect(draw, r, paint);
2476 } else {
2477 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002478 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002479 draw.fDevice->drawRect(draw, r, p);
2480 }
2481}
2482
2483void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2484 const char text[], size_t byteLength,
2485 SkScalar x, SkScalar y) {
halcanary96fcdcc2015-08-27 07:41:13 -07002486 SkASSERT(byteLength == 0 || text != nullptr);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002487
2488 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07002489 if (text == nullptr || byteLength == 0 ||
bungeman@google.com52c748b2011-08-22 21:30:43 +00002490 draw.fClip->isEmpty() ||
halcanary96fcdcc2015-08-27 07:41:13 -07002491 (paint.getAlpha() == 0 && paint.getXfermode() == nullptr)) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002492 return;
2493 }
2494
2495 SkScalar width = 0;
2496 SkPoint start;
2497
2498 start.set(0, 0); // to avoid warning
2499 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2500 SkPaint::kStrikeThruText_Flag)) {
2501 width = paint.measureText(text, byteLength);
2502
2503 SkScalar offsetX = 0;
2504 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2505 offsetX = SkScalarHalf(width);
2506 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2507 offsetX = width;
2508 }
2509 start.set(x - offsetX, y);
2510 }
2511
2512 if (0 == width) {
2513 return;
2514 }
2515
2516 uint32_t flags = paint.getFlags();
2517
2518 if (flags & (SkPaint::kUnderlineText_Flag |
2519 SkPaint::kStrikeThruText_Flag)) {
2520 SkScalar textSize = paint.getTextSize();
2521 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2522 SkRect r;
2523
2524 r.fLeft = start.fX;
2525 r.fRight = start.fX + width;
2526
2527 if (flags & SkPaint::kUnderlineText_Flag) {
2528 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2529 start.fY);
2530 r.fTop = offset;
2531 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002532 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002533 }
2534 if (flags & SkPaint::kStrikeThruText_Flag) {
2535 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2536 start.fY);
2537 r.fTop = offset;
2538 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002539 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002540 }
2541 }
2542}
2543
reed@google.come0d9ce82014-04-23 04:00:17 +00002544void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2545 const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002546 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002547
2548 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002549 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002550 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002551 DrawTextDecorations(iter, dfp.paint(),
2552 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002553 }
2554
reed@google.com4e2b3d32011-04-07 14:18:59 +00002555 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002556}
2557
reed@google.come0d9ce82014-04-23 04:00:17 +00002558void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2559 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002560 SkPoint textOffset = SkPoint::Make(0, 0);
2561
halcanary96fcdcc2015-08-27 07:41:13 -07002562 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002563
reed@android.com8a1c16f2008-12-17 15:59:43 +00002564 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002565 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002566 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002567 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002568 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002569
reed@google.com4e2b3d32011-04-07 14:18:59 +00002570 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002571}
2572
reed@google.come0d9ce82014-04-23 04:00:17 +00002573void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2574 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002575
2576 SkPoint textOffset = SkPoint::Make(0, constY);
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, xpos, 1, 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::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2590 const SkMatrix* matrix, const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002591 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002592
reed@android.com8a1c16f2008-12-17 15:59:43 +00002593 while (iter.next()) {
2594 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002595 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002596 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002597
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002598 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002599}
2600
fmalita00d5c2c2014-08-21 08:53:26 -07002601void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2602 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002603
fmalita85d5eb92015-03-04 11:20:12 -08002604 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002605 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002606 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002607 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002608 SkRect tmp;
2609 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2610 return;
2611 }
2612 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002613 }
2614
fmalita024f9962015-03-03 19:08:17 -08002615 // We cannot filter in the looper as we normally do, because the paint is
2616 // incomplete at this point (text-related attributes are embedded within blob run paints).
2617 SkDrawFilter* drawFilter = fMCRec->fFilter;
halcanary96fcdcc2015-08-27 07:41:13 -07002618 fMCRec->fFilter = nullptr;
fmalita024f9962015-03-03 19:08:17 -08002619
fmalita85d5eb92015-03-04 11:20:12 -08002620 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002621
fmalitaaa1b9122014-08-28 14:32:24 -07002622 while (iter.next()) {
2623 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002624 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002625 }
2626
fmalitaaa1b9122014-08-28 14:32:24 -07002627 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002628
2629 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002630}
2631
reed@google.come0d9ce82014-04-23 04:00:17 +00002632// These will become non-virtual, so they always call the (virtual) onDraw... method
2633void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2634 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002635 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002636 this->onDrawText(text, byteLength, x, y, paint);
2637}
2638void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2639 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002640 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002641 this->onDrawPosText(text, byteLength, pos, paint);
2642}
2643void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2644 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002645 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002646 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2647}
2648void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2649 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002650 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002651 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2652}
fmalita00d5c2c2014-08-21 08:53:26 -07002653void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2654 const SkPaint& paint) {
reede3b38ce2016-01-08 09:18:44 -08002655 RETURN_ON_NULL(blob);
danakj9881d632014-11-26 12:41:06 -08002656 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
reede3b38ce2016-01-08 09:18:44 -08002657 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002658}
reed@google.come0d9ce82014-04-23 04:00:17 +00002659
reed41af9662015-01-05 07:49:08 -08002660void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2661 const SkPoint verts[], const SkPoint texs[],
2662 const SkColor colors[], SkXfermode* xmode,
2663 const uint16_t indices[], int indexCount,
2664 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002665 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
halcanary96fcdcc2015-08-27 07:41:13 -07002666 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
reed@google.com4b226022011-01-11 18:32:13 +00002667
reed@android.com8a1c16f2008-12-17 15:59:43 +00002668 while (iter.next()) {
2669 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002670 colors, xmode, indices, indexCount,
2671 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002672 }
reed@google.com4b226022011-01-11 18:32:13 +00002673
reed@google.com4e2b3d32011-04-07 14:18:59 +00002674 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002675}
2676
dandovb3c9d1c2014-08-12 08:34:29 -07002677void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2678 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002679 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
halcanary96fcdcc2015-08-27 07:41:13 -07002680 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002681 return;
2682 }
mtklein6cfa73a2014-08-13 13:33:49 -07002683
dandovecfff212014-08-04 10:02:00 -07002684 // Since a patch is always within the convex hull of the control points, we discard it when its
2685 // bounding rectangle is completely outside the current clip.
2686 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002687 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002688 if (this->quickReject(bounds)) {
2689 return;
2690 }
mtklein6cfa73a2014-08-13 13:33:49 -07002691
dandovb3c9d1c2014-08-12 08:34:29 -07002692 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2693}
2694
2695void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2696 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2697
halcanary96fcdcc2015-08-27 07:41:13 -07002698 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002699
dandovecfff212014-08-04 10:02:00 -07002700 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002701 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002702 }
mtklein6cfa73a2014-08-13 13:33:49 -07002703
dandovecfff212014-08-04 10:02:00 -07002704 LOOPER_END
2705}
2706
reeda8db7282015-07-07 10:22:31 -07002707void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
reede3b38ce2016-01-08 09:18:44 -08002708 RETURN_ON_NULL(dr);
2709 if (x || y) {
2710 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2711 this->onDrawDrawable(dr, &matrix);
2712 } else {
2713 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002714 }
2715}
2716
reeda8db7282015-07-07 10:22:31 -07002717void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reede3b38ce2016-01-08 09:18:44 -08002718 RETURN_ON_NULL(dr);
2719 if (matrix && matrix->isIdentity()) {
2720 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002721 }
reede3b38ce2016-01-08 09:18:44 -08002722 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002723}
2724
2725void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2726 SkRect bounds = dr->getBounds();
2727 if (matrix) {
2728 matrix->mapRect(&bounds);
2729 }
2730 if (this->quickReject(bounds)) {
2731 return;
2732 }
2733 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002734}
2735
reed71c3c762015-06-24 10:29:17 -07002736void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2737 const SkColor colors[], int count, SkXfermode::Mode mode,
2738 const SkRect* cull, const SkPaint* paint) {
2739 if (cull && this->quickReject(*cull)) {
2740 return;
2741 }
2742
2743 SkPaint pnt;
2744 if (paint) {
2745 pnt = *paint;
2746 }
2747
halcanary96fcdcc2015-08-27 07:41:13 -07002748 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr)
reed71c3c762015-06-24 10:29:17 -07002749 while (iter.next()) {
2750 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, mode, pnt);
2751 }
2752 LOOPER_END
2753}
2754
reed@android.com8a1c16f2008-12-17 15:59:43 +00002755//////////////////////////////////////////////////////////////////////////////
2756// These methods are NOT virtual, and therefore must call back into virtual
2757// methods, rather than actually drawing themselves.
2758//////////////////////////////////////////////////////////////////////////////
2759
2760void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002761 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002762 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002763 SkPaint paint;
2764
2765 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002766 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002767 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002768 }
2769 this->drawPaint(paint);
2770}
2771
reed@android.com845fdac2009-06-23 03:01:32 +00002772void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002773 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002774 SkPaint paint;
2775
2776 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002777 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002778 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002779 }
2780 this->drawPaint(paint);
2781}
2782
2783void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002784 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002785 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002786
reed@android.com8a1c16f2008-12-17 15:59:43 +00002787 pt.set(x, y);
2788 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2789}
2790
2791void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002792 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002793 SkPoint pt;
2794 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002795
reed@android.com8a1c16f2008-12-17 15:59:43 +00002796 pt.set(x, y);
2797 paint.setColor(color);
2798 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2799}
2800
2801void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2802 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002803 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002804 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002805
reed@android.com8a1c16f2008-12-17 15:59:43 +00002806 pts[0].set(x0, y0);
2807 pts[1].set(x1, y1);
2808 this->drawPoints(kLines_PointMode, 2, pts, paint);
2809}
2810
2811void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2812 SkScalar right, SkScalar bottom,
2813 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002814 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002815 SkRect r;
2816
2817 r.set(left, top, right, bottom);
2818 this->drawRect(r, paint);
2819}
2820
2821void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2822 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002823 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002824 if (radius < 0) {
2825 radius = 0;
2826 }
2827
2828 SkRect r;
2829 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002830 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002831}
2832
2833void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2834 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002835 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002836 if (rx > 0 && ry > 0) {
2837 if (paint.canComputeFastBounds()) {
2838 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002839 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002840 return;
2841 }
2842 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002843 SkRRect rrect;
2844 rrect.setRectXY(r, rx, ry);
2845 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002846 } else {
2847 this->drawRect(r, paint);
2848 }
2849}
2850
reed@android.com8a1c16f2008-12-17 15:59:43 +00002851void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2852 SkScalar sweepAngle, bool useCenter,
2853 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002854 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002855 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2856 this->drawOval(oval, paint);
2857 } else {
2858 SkPath path;
2859 if (useCenter) {
2860 path.moveTo(oval.centerX(), oval.centerY());
2861 }
2862 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2863 if (useCenter) {
2864 path.close();
2865 }
2866 this->drawPath(path, paint);
2867 }
2868}
2869
2870void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2871 const SkPath& path, SkScalar hOffset,
2872 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002873 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002874 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002875
reed@android.com8a1c16f2008-12-17 15:59:43 +00002876 matrix.setTranslate(hOffset, vOffset);
2877 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2878}
2879
reed@android.comf76bacf2009-05-13 14:00:33 +00002880///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07002881
2882/**
2883 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2884 * against the playback cost of recursing into the subpicture to get at its actual ops.
2885 *
2886 * For now we pick a conservatively small value, though measurement (and other heuristics like
2887 * the type of ops contained) may justify changing this value.
2888 */
2889#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07002890
reedd5fa1a42014-08-09 11:08:05 -07002891void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002892 RETURN_ON_NULL(picture);
2893
reed1c2c4412015-04-30 13:09:24 -07002894 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
reede3b38ce2016-01-08 09:18:44 -08002895 if (matrix && matrix->isIdentity()) {
2896 matrix = nullptr;
2897 }
2898 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2899 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2900 picture->playback(this);
2901 } else {
2902 this->onDrawPicture(picture, matrix, paint);
reedd5fa1a42014-08-09 11:08:05 -07002903 }
2904}
robertphillips9b14f262014-06-04 05:40:44 -07002905
reedd5fa1a42014-08-09 11:08:05 -07002906void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2907 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07002908 if (!paint || paint->canComputeFastBounds()) {
2909 SkRect bounds = picture->cullRect();
2910 if (paint) {
2911 paint->computeFastBounds(bounds, &bounds);
2912 }
2913 if (matrix) {
2914 matrix->mapRect(&bounds);
2915 }
2916 if (this->quickReject(bounds)) {
2917 return;
2918 }
2919 }
2920
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002921 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002922 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002923 // Canvas has to first give the device the opportunity to render
2924 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002925 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002926 return; // the device has rendered the entire picture
2927 }
2928 }
2929
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002930 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07002931 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002932}
2933
reed@android.com8a1c16f2008-12-17 15:59:43 +00002934///////////////////////////////////////////////////////////////////////////////
2935///////////////////////////////////////////////////////////////////////////////
2936
2937SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
bungeman99fe8222015-08-20 07:57:51 -07002938 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002939
2940 SkASSERT(canvas);
2941
2942 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2943 fDone = !fImpl->next();
2944}
2945
2946SkCanvas::LayerIter::~LayerIter() {
2947 fImpl->~SkDrawIter();
2948}
2949
2950void SkCanvas::LayerIter::next() {
2951 fDone = !fImpl->next();
2952}
2953
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002954SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002955 return fImpl->getDevice();
2956}
2957
2958const SkMatrix& SkCanvas::LayerIter::matrix() const {
2959 return fImpl->getMatrix();
2960}
2961
2962const SkPaint& SkCanvas::LayerIter::paint() const {
2963 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07002964 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002965 paint = &fDefaultPaint;
2966 }
2967 return *paint;
2968}
2969
2970const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2971int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2972int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002973
2974///////////////////////////////////////////////////////////////////////////////
2975
fmalitac3b589a2014-06-05 12:40:07 -07002976SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002977
2978///////////////////////////////////////////////////////////////////////////////
2979
2980static bool supported_for_raster_canvas(const SkImageInfo& info) {
2981 switch (info.alphaType()) {
2982 case kPremul_SkAlphaType:
2983 case kOpaque_SkAlphaType:
2984 break;
2985 default:
2986 return false;
2987 }
2988
2989 switch (info.colorType()) {
2990 case kAlpha_8_SkColorType:
2991 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002992 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002993 break;
2994 default:
2995 return false;
2996 }
2997
2998 return true;
2999}
3000
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003001SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
3002 if (!supported_for_raster_canvas(info)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003003 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003004 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003005
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003006 SkBitmap bitmap;
3007 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003008 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003009 }
halcanary385fe4d2015-08-26 13:07:48 -07003010 return new SkCanvas(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003011}
reedd5fa1a42014-08-09 11:08:05 -07003012
3013///////////////////////////////////////////////////////////////////////////////
3014
3015SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003016 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07003017 : fCanvas(canvas)
3018 , fSaveCount(canvas->getSaveCount())
3019{
bsalomon49f085d2014-09-05 13:34:00 -07003020 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003021 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07003022 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003023 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07003024 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003025 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07003026 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003027 canvas->save();
3028 }
mtklein6cfa73a2014-08-13 13:33:49 -07003029
bsalomon49f085d2014-09-05 13:34:00 -07003030 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003031 canvas->concat(*matrix);
3032 }
3033}
3034
3035SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
3036 fCanvas->restoreToCount(fSaveCount);
3037}