blob: 82ceba4fa9f45978c1440c4420e40f0cbecae97a [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
reedc83a2972015-07-16 07:40:45 -070045/*
46 * Return true if the drawing this rect would hit every pixels in the canvas.
47 *
48 * Returns false if
49 * - rect does not contain the canvas' bounds
50 * - paint is not fill
51 * - paint would blur or otherwise change the coverage of the rect
52 */
53bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
54 ShaderOverrideOpacity overrideOpacity) const {
bungeman99fe8222015-08-20 07:57:51 -070055 static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
56 (int)kNone_ShaderOverrideOpacity,
57 "need_matching_enums0");
58 static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
59 (int)kOpaque_ShaderOverrideOpacity,
60 "need_matching_enums1");
61 static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
62 (int)kNotOpaque_ShaderOverrideOpacity,
63 "need_matching_enums2");
reedc83a2972015-07-16 07:40:45 -070064
65 const SkISize size = this->getBaseLayerSize();
66 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
67 if (!this->getClipStack()->quickContains(bounds)) {
68 return false;
69 }
70
71 if (rect) {
72 if (!this->getTotalMatrix().rectStaysRect()) {
73 return false; // conservative
74 }
75
76 SkRect devRect;
77 this->getTotalMatrix().mapRect(&devRect, *rect);
fmalita8c0144c2015-07-22 05:56:16 -070078 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -070079 return false;
80 }
81 }
82
83 if (paint) {
84 SkPaint::Style paintStyle = paint->getStyle();
85 if (!(paintStyle == SkPaint::kFill_Style ||
86 paintStyle == SkPaint::kStrokeAndFill_Style)) {
87 return false;
88 }
89 if (paint->getMaskFilter() || paint->getLooper()
90 || paint->getPathEffect() || paint->getImageFilter()) {
91 return false; // conservative
92 }
93 }
94 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
95}
96
97///////////////////////////////////////////////////////////////////////////////////////////////////
98
reedd990e2f2014-12-22 11:58:30 -080099static bool gIgnoreSaveLayerBounds;
100void SkCanvas::Internal_Private_SetIgnoreSaveLayerBounds(bool ignore) {
101 gIgnoreSaveLayerBounds = ignore;
102}
103bool SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds() {
104 return gIgnoreSaveLayerBounds;
105}
106
reed0acf1b42014-12-22 16:12:38 -0800107static bool gTreatSpriteAsBitmap;
108void SkCanvas::Internal_Private_SetTreatSpriteAsBitmap(bool spriteAsBitmap) {
109 gTreatSpriteAsBitmap = spriteAsBitmap;
110}
111bool SkCanvas::Internal_Private_GetTreatSpriteAsBitmap() {
112 return gTreatSpriteAsBitmap;
113}
114
reed@google.comda17f752012-08-16 18:27:05 +0000115// experimental for faster tiled drawing...
116//#define SK_ENABLE_CLIP_QUICKREJECT
reed@android.com8a1c16f2008-12-17 15:59:43 +0000117//#define SK_TRACE_SAVERESTORE
118
119#ifdef SK_TRACE_SAVERESTORE
120 static int gLayerCounter;
121 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
122 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
123
124 static int gRecCounter;
125 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
126 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
127
128 static int gCanvasCounter;
129 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
130 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
131#else
132 #define inc_layer()
133 #define dec_layer()
134 #define inc_rec()
135 #define dec_rec()
136 #define inc_canvas()
137 #define dec_canvas()
138#endif
139
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000140typedef SkTLazy<SkPaint> SkLazyPaint;
141
reedc83a2972015-07-16 07:40:45 -0700142void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000143 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700144 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
145 ? SkSurface::kDiscard_ContentChangeMode
146 : SkSurface::kRetain_ContentChangeMode);
147 }
148}
149
150void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
151 ShaderOverrideOpacity overrideOpacity) {
152 if (fSurfaceBase) {
153 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
154 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
155 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
156 // and therefore we don't care which mode we're in.
157 //
158 if (fSurfaceBase->outstandingImageSnapshot()) {
159 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
160 mode = SkSurface::kDiscard_ContentChangeMode;
161 }
162 }
163 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000164 }
165}
166
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000168
reed4a8126e2014-09-22 07:29:03 -0700169static uint32_t filter_paint_flags(const SkSurfaceProps& props, uint32_t flags) {
170 const uint32_t propFlags = props.flags();
171 if (propFlags & SkSurfaceProps::kDisallowDither_Flag) {
172 flags &= ~SkPaint::kDither_Flag;
173 }
174 if (propFlags & SkSurfaceProps::kDisallowAntiAlias_Flag) {
175 flags &= ~SkPaint::kAntiAlias_Flag;
176 }
177 return flags;
178}
179
180///////////////////////////////////////////////////////////////////////////////
181
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000182/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000183 The clip/matrix/proc are fields that reflect the top of the save/restore
184 stack. Whenever the canvas changes, it marks a dirty flag, and then before
185 these are used (assuming we're not on a layer) we rebuild these cache
186 values: they reflect the top of the save stack, but translated and clipped
187 by the device's XY offset and bitmap-bounds.
188*/
189struct DeviceCM {
190 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000191 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000192 SkRasterClip fClip;
reed@google.com6f8f2922011-03-04 22:27:10 +0000193 SkPaint* fPaint; // may be null (in the future)
reed61f501f2015-04-29 08:34:00 -0700194 const SkMatrix* fMatrix;
195 SkMatrix fMatrixStorage;
196 const bool fDeviceIsBitmapDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000197
reed96e657d2015-03-10 17:30:07 -0700198 DeviceCM(SkBaseDevice* device, const SkPaint* paint, SkCanvas* canvas,
reed86a17e72015-05-14 12:25:22 -0700199 bool conservativeRasterClip, bool deviceIsBitmapDevice)
halcanary96fcdcc2015-08-27 07:41:13 -0700200 : fNext(nullptr)
reedd9544982014-09-09 18:46:22 -0700201 , fClip(conservativeRasterClip)
reed61f501f2015-04-29 08:34:00 -0700202 , fDeviceIsBitmapDevice(deviceIsBitmapDevice)
reedd9544982014-09-09 18:46:22 -0700203 {
halcanary96fcdcc2015-08-27 07:41:13 -0700204 if (nullptr != device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000205 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000206 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000207 }
reed@google.com4b226022011-01-11 18:32:13 +0000208 fDevice = device;
halcanary96fcdcc2015-08-27 07:41:13 -0700209 fPaint = paint ? new SkPaint(*paint) : nullptr;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000210 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000212 ~DeviceCM() {
bsalomon49f085d2014-09-05 13:34:00 -0700213 if (fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000214 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215 fDevice->unref();
216 }
halcanary385fe4d2015-08-26 13:07:48 -0700217 delete fPaint;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000218 }
reed@google.com4b226022011-01-11 18:32:13 +0000219
mtkleinfeaadee2015-04-08 11:25:48 -0700220 void reset(const SkIRect& bounds) {
221 SkASSERT(!fPaint);
222 SkASSERT(!fNext);
223 SkASSERT(fDevice);
224 fClip.setRect(bounds);
225 }
226
reed@google.com045e62d2011-10-24 12:19:46 +0000227 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
228 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000229 int x = fDevice->getOrigin().x();
230 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231 int width = fDevice->width();
232 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000233
reed@android.com8a1c16f2008-12-17 15:59:43 +0000234 if ((x | y) == 0) {
235 fMatrix = &totalMatrix;
236 fClip = totalClip;
237 } else {
238 fMatrixStorage = totalMatrix;
239 fMatrixStorage.postTranslate(SkIntToScalar(-x),
240 SkIntToScalar(-y));
241 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000242
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243 totalClip.translate(-x, -y, &fClip);
244 }
245
reed@google.com045e62d2011-10-24 12:19:46 +0000246 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247
248 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000249
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000251 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252 SkRegion::kDifference_Op);
253 }
reed@google.com4b226022011-01-11 18:32:13 +0000254
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000255 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
256
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257#ifdef SK_DEBUG
258 if (!fClip.isEmpty()) {
259 SkIRect deviceR;
260 deviceR.set(0, 0, width, height);
261 SkASSERT(deviceR.contains(fClip.getBounds()));
262 }
263#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000264 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265};
266
267/* This is the record we keep for each save/restore level in the stack.
268 Since a level optionally copies the matrix and/or stack, we have pointers
269 for these fields. If the value is copied for this level, the copy is
270 stored in the ...Storage field, and the pointer points to that. If the
271 value is not copied for this level, we ignore ...Storage, and just point
272 at the corresponding value in the previous level in the stack.
273*/
274class SkCanvas::MCRec {
275public:
reed1f836ee2014-07-07 07:49:34 -0700276 SkDrawFilter* fFilter; // the current filter (or null)
reedd9544982014-09-09 18:46:22 -0700277 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000278 /* If there are any layers in the stack, this points to the top-most
279 one that is at or below this level in the stack (so we know what
280 bitmap/device to draw into from this level. This value is NOT
281 reference counted, since the real owner is either our fLayer field,
282 or a previous one in a lower level.)
283 */
reed2ff1fce2014-12-11 07:07:37 -0800284 DeviceCM* fTopLayer;
285 SkRasterClip fRasterClip;
286 SkMatrix fMatrix;
287 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288
reedd9544982014-09-09 18:46:22 -0700289 MCRec(bool conservativeRasterClip) : fRasterClip(conservativeRasterClip) {
halcanary96fcdcc2015-08-27 07:41:13 -0700290 fFilter = nullptr;
291 fLayer = nullptr;
292 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800293 fMatrix.reset();
294 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700295
reedd9544982014-09-09 18:46:22 -0700296 // don't bother initializing fNext
297 inc_rec();
298 }
reed2ff1fce2014-12-11 07:07:37 -0800299 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
reedd9544982014-09-09 18:46:22 -0700300 fFilter = SkSafeRef(prev.fFilter);
halcanary96fcdcc2015-08-27 07:41:13 -0700301 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700302 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800303 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700304
reed@android.com8a1c16f2008-12-17 15:59:43 +0000305 // don't bother initializing fNext
306 inc_rec();
307 }
308 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000309 SkSafeUnref(fFilter);
halcanary385fe4d2015-08-26 13:07:48 -0700310 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000311 dec_rec();
312 }
mtkleinfeaadee2015-04-08 11:25:48 -0700313
314 void reset(const SkIRect& bounds) {
315 SkASSERT(fLayer);
316 SkASSERT(fDeferredSaveCount == 0);
317
318 fMatrix.reset();
319 fRasterClip.setRect(bounds);
320 fLayer->reset(bounds);
321 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000322};
323
324class SkDrawIter : public SkDraw {
325public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000326 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000327 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000328 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000329 canvas->updateDeviceCMCache();
330
reed687fa1c2015-04-07 08:00:56 -0700331 fClipStack = canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000332 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000333 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000334 }
reed@google.com4b226022011-01-11 18:32:13 +0000335
reed@android.com8a1c16f2008-12-17 15:59:43 +0000336 bool next() {
337 // skip over recs with empty clips
338 if (fSkipEmptyClips) {
339 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
340 fCurrLayer = fCurrLayer->fNext;
341 }
342 }
343
reed@google.comf68c5e22012-02-24 16:38:58 +0000344 const DeviceCM* rec = fCurrLayer;
345 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000346
347 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000348 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
349 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000350 fDevice = rec->fDevice;
reed41e010c2015-06-09 12:16:53 -0700351 if (!fDevice->accessPixels(&fDst)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700352 fDst.reset(fDevice->imageInfo(), nullptr, 0);
reed41e010c2015-06-09 12:16:53 -0700353 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000354 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000355 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000356
357 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700358 // fCurrLayer may be nullptr now
reed@android.com199f1082009-06-10 02:12:47 +0000359
reed@android.com8a1c16f2008-12-17 15:59:43 +0000360 return true;
361 }
362 return false;
363 }
reed@google.com4b226022011-01-11 18:32:13 +0000364
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000365 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000366 int getX() const { return fDevice->getOrigin().x(); }
367 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000368 const SkMatrix& getMatrix() const { return *fMatrix; }
369 const SkRegion& getClip() const { return *fClip; }
370 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000371
reed@android.com8a1c16f2008-12-17 15:59:43 +0000372private:
373 SkCanvas* fCanvas;
374 const DeviceCM* fCurrLayer;
375 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000376 SkBool8 fSkipEmptyClips;
377
378 typedef SkDraw INHERITED;
379};
380
381/////////////////////////////////////////////////////////////////////////////
382
reeddbc3cef2015-04-29 12:18:57 -0700383static SkPaint* set_if_needed(SkLazyPaint* lazy, const SkPaint& orig) {
384 return lazy->isValid() ? lazy->get() : lazy->set(orig);
385}
386
387/**
388 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700389 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700390 */
391static SkColorFilter* image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700392 SkImageFilter* imgf = paint.getImageFilter();
393 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700394 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700395 }
396
397 SkColorFilter* imgCF;
398 if (!imgf->asAColorFilter(&imgCF)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700399 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700400 }
401
402 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700403 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700404 // there is no existing paint colorfilter, so we can just return the imagefilter's
405 return imgCF;
406 }
407
408 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
409 // and we need to combine them into a single colorfilter.
410 SkAutoTUnref<SkColorFilter> autoImgCF(imgCF);
411 return SkColorFilter::CreateComposeFilter(imgCF, paintCF);
reeddbc3cef2015-04-29 12:18:57 -0700412}
413
senorblanco87e066e2015-10-28 11:23:36 -0700414/**
415 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
416 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
417 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
418 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
419 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
420 * conservative "effective" bounds based on the settings in the paint... with one exception. This
421 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
422 * deliberately ignored.
423 */
424static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
425 const SkRect& rawBounds,
426 SkRect* storage) {
427 SkPaint tmpUnfiltered(paint);
428 tmpUnfiltered.setImageFilter(nullptr);
429 if (tmpUnfiltered.canComputeFastBounds()) {
430 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
431 } else {
432 return rawBounds;
433 }
434}
435
reed@android.com8a1c16f2008-12-17 15:59:43 +0000436class AutoDrawLooper {
437public:
senorblanco87e066e2015-10-28 11:23:36 -0700438 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
439 // paint. It's used to determine the size of the offscreen layer for filters.
440 // If null, the clip will be used instead.
reed4a8126e2014-09-22 07:29:03 -0700441 AutoDrawLooper(SkCanvas* canvas, const SkSurfaceProps& props, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000442 bool skipLayerForImageFilter = false,
senorblanco87e066e2015-10-28 11:23:36 -0700443 const SkRect* rawBounds = nullptr) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000444 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000445 fFilter = canvas->getDrawFilter();
reed4a8126e2014-09-22 07:29:03 -0700446 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000447 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700448 fTempLayerForImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000449 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450
reeddbc3cef2015-04-29 12:18:57 -0700451 SkColorFilter* simplifiedCF = image_to_color_filter(fOrigPaint);
452 if (simplifiedCF) {
453 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
454 paint->setColorFilter(simplifiedCF)->unref();
halcanary96fcdcc2015-08-27 07:41:13 -0700455 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700456 fPaint = paint;
457 }
458
459 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700460 /**
461 * We implement ImageFilters for a given draw by creating a layer, then applying the
462 * imagefilter to the pixels of that layer (its backing surface/image), and then
463 * we call restore() to xfer that layer to the main canvas.
464 *
465 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
466 * 2. Generate the src pixels:
467 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
468 * return (fPaint). We then draw the primitive (using srcover) into a cleared
469 * buffer/surface.
470 * 3. Restore the layer created in #1
471 * The imagefilter is passed the buffer/surface from the layer (now filled with the
472 * src pixels of the primitive). It returns a new "filtered" buffer, which we
473 * draw onto the previous layer using the xfermode from the original paint.
474 */
reed@google.com8926b162012-03-23 15:36:36 +0000475 SkPaint tmp;
reeddbc3cef2015-04-29 12:18:57 -0700476 tmp.setImageFilter(fPaint->getImageFilter());
477 tmp.setXfermode(fPaint->getXfermode());
senorblanco87e066e2015-10-28 11:23:36 -0700478 SkRect storage;
479 if (rawBounds) {
480 // Make rawBounds include all paint outsets except for those due to image filters.
481 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
482 }
reedbfd5f172016-01-07 11:28:08 -0800483 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &tmp),
reed76033be2015-03-14 10:54:31 -0700484 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700485 fTempLayerForImageFilter = true;
486 // we remove the imagefilter/xfermode inside doNext()
reed@google.com8926b162012-03-23 15:36:36 +0000487 }
488
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000489 if (SkDrawLooper* looper = paint.getLooper()) {
490 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
491 looper->contextSize());
492 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000493 fIsSimple = false;
494 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700495 fLooperContext = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000496 // can we be marked as simple?
reed5c476fb2015-04-20 08:04:21 -0700497 fIsSimple = !fFilter && !fTempLayerForImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000498 }
piotaixrb5fae932014-09-24 13:03:30 -0700499
reed4a8126e2014-09-22 07:29:03 -0700500 uint32_t oldFlags = paint.getFlags();
501 fNewPaintFlags = filter_paint_flags(props, oldFlags);
502 if (fIsSimple && (fNewPaintFlags != oldFlags)) {
reeddbc3cef2015-04-29 12:18:57 -0700503 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700504 paint->setFlags(fNewPaintFlags);
505 fPaint = paint;
506 // if we're not simple, doNext() will take care of calling setFlags()
507 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000508 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000509
reed@android.com8a1c16f2008-12-17 15:59:43 +0000510 ~AutoDrawLooper() {
reed5c476fb2015-04-20 08:04:21 -0700511 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000512 fCanvas->internalRestore();
513 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000514 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000515 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000516
reed@google.com4e2b3d32011-04-07 14:18:59 +0000517 const SkPaint& paint() const {
518 SkASSERT(fPaint);
519 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000520 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000521
reed@google.com129ec222012-05-15 13:24:09 +0000522 bool next(SkDrawFilter::Type drawType) {
523 if (fDone) {
524 return false;
525 } else if (fIsSimple) {
526 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000527 return !fPaint->nothingToDraw();
528 } else {
529 return this->doNext(drawType);
530 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000531 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000532
reed@android.com8a1c16f2008-12-17 15:59:43 +0000533private:
reeddbc3cef2015-04-29 12:18:57 -0700534 SkLazyPaint fLazyPaintInit; // base paint storage in case we need to modify it
535 SkLazyPaint fLazyPaintPerLooper; // per-draw-looper storage, so the looper can modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000536 SkCanvas* fCanvas;
537 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000538 SkDrawFilter* fFilter;
539 const SkPaint* fPaint;
540 int fSaveCount;
reed4a8126e2014-09-22 07:29:03 -0700541 uint32_t fNewPaintFlags;
reed5c476fb2015-04-20 08:04:21 -0700542 bool fTempLayerForImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000543 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000544 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000545 SkDrawLooper::Context* fLooperContext;
546 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000547
548 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000549};
550
reed@google.com129ec222012-05-15 13:24:09 +0000551bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
halcanary96fcdcc2015-08-27 07:41:13 -0700552 fPaint = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000553 SkASSERT(!fIsSimple);
reed5c476fb2015-04-20 08:04:21 -0700554 SkASSERT(fLooperContext || fFilter || fTempLayerForImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000555
reeddbc3cef2015-04-29 12:18:57 -0700556 SkPaint* paint = fLazyPaintPerLooper.set(fLazyPaintInit.isValid() ?
557 *fLazyPaintInit.get() : fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700558 paint->setFlags(fNewPaintFlags);
reed@google.com129ec222012-05-15 13:24:09 +0000559
reed5c476fb2015-04-20 08:04:21 -0700560 if (fTempLayerForImageFilter) {
halcanary96fcdcc2015-08-27 07:41:13 -0700561 paint->setImageFilter(nullptr);
562 paint->setXfermode(nullptr);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000563 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000564
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000565 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000566 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000567 return false;
568 }
569 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000570 if (!fFilter->filter(paint, drawType)) {
571 fDone = true;
572 return false;
573 }
halcanary96fcdcc2015-08-27 07:41:13 -0700574 if (nullptr == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000575 // no looper means we only draw once
576 fDone = true;
577 }
578 }
579 fPaint = paint;
580
581 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000582 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000583 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000584 }
585
586 // call this after any possible paint modifiers
587 if (fPaint->nothingToDraw()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700588 fPaint = nullptr;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000589 return false;
590 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000591 return true;
592}
593
reed@android.com8a1c16f2008-12-17 15:59:43 +0000594////////// macros to place around the internal draw calls //////////////////
595
reed262a71b2015-12-05 13:07:27 -0800596#define LOOPER_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
597 this->predrawNotify(); \
598 AutoDrawLooper looper(this, fProps, paint, skipLayerForFilter, bounds); \
599 while (looper.next(SkDrawFilter::kBitmap_Type)) { \
600 SkDrawIter iter(this);
601
602
reed@google.com8926b162012-03-23 15:36:36 +0000603#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000604 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700605 AutoDrawLooper looper(this, fProps, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000606 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000607 SkDrawIter iter(this);
608
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000609#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000610 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700611 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000612 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000613 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000614
reedc83a2972015-07-16 07:40:45 -0700615#define LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, type, bounds, auxOpaque) \
616 this->predrawNotify(bounds, &paint, auxOpaque); \
617 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
618 while (looper.next(type)) { \
619 SkDrawIter iter(this);
620
reed@google.com4e2b3d32011-04-07 14:18:59 +0000621#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000622
623////////////////////////////////////////////////////////////////////////////
624
mtkleinfeaadee2015-04-08 11:25:48 -0700625void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
626 this->restoreToCount(1);
627 fCachedLocalClipBounds.setEmpty();
628 fCachedLocalClipBoundsDirty = true;
629 fClipStack->reset();
630 fMCRec->reset(bounds);
631
632 // We're peering through a lot of structs here. Only at this scope do we
633 // know that the device is an SkBitmapDevice (really an SkNoPixelsBitmapDevice).
634 static_cast<SkBitmapDevice*>(fMCRec->fLayer->fDevice)->setNewSize(bounds.size());
635}
636
reedd9544982014-09-09 18:46:22 -0700637SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
reed42b73eb2015-11-20 13:42:42 -0800638 if (device && device->forceConservativeRasterClip()) {
639 flags = InitFlags(flags | kConservativeRasterClip_InitFlag);
640 }
641 // Since init() is only called once by our constructors, it is safe to perform this
642 // const-cast.
643 *const_cast<bool*>(&fConservativeRasterClip) = SkToBool(flags & kConservativeRasterClip_InitFlag);
644
reed@google.comc0784db2013-12-13 21:16:12 +0000645 fCachedLocalClipBounds.setEmpty();
646 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000647 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000648 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700649 fDeviceCMDirty = true;
reed2ff1fce2014-12-11 07:07:37 -0800650 fSaveCount = 1;
halcanary96fcdcc2015-08-27 07:41:13 -0700651 fMetaData = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000652
halcanary385fe4d2015-08-26 13:07:48 -0700653 fClipStack.reset(new SkClipStack);
reed687fa1c2015-04-07 08:00:56 -0700654
reed@android.com8a1c16f2008-12-17 15:59:43 +0000655 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700656 new (fMCRec) MCRec(fConservativeRasterClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000657
reeda499f902015-05-01 09:34:31 -0700658 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
659 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
halcanary96fcdcc2015-08-27 07:41:13 -0700660 new (fDeviceCMStorage) DeviceCM(nullptr, nullptr, nullptr, fConservativeRasterClip, false);
reedb679ca82015-04-07 04:40:48 -0700661
reed@android.com8a1c16f2008-12-17 15:59:43 +0000662 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000663
halcanary96fcdcc2015-08-27 07:41:13 -0700664 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000665
reedf92c8662014-08-18 08:02:43 -0700666 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700667 // The root device and the canvas should always have the same pixel geometry
668 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reedf92c8662014-08-18 08:02:43 -0700669 device->onAttachToCanvas(this);
670 fMCRec->fLayer->fDevice = SkRef(device);
reed78e27682014-11-19 08:04:34 -0800671 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
reedf92c8662014-08-18 08:02:43 -0700672 }
673 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000674}
675
reed@google.comcde92112011-07-06 20:00:52 +0000676SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000677 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700678 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800679 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000680{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000681 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000682
halcanary96fcdcc2015-08-27 07:41:13 -0700683 this->init(nullptr, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000684}
685
reedd9544982014-09-09 18:46:22 -0700686static SkBitmap make_nopixels(int width, int height) {
687 SkBitmap bitmap;
688 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
689 return bitmap;
690}
691
692class SkNoPixelsBitmapDevice : public SkBitmapDevice {
693public:
robertphillipsfcf78292015-06-19 11:49:52 -0700694 SkNoPixelsBitmapDevice(const SkIRect& bounds, const SkSurfaceProps& surfaceProps)
695 : INHERITED(make_nopixels(bounds.width(), bounds.height()), surfaceProps)
reed78e27682014-11-19 08:04:34 -0800696 {
697 this->setOrigin(bounds.x(), bounds.y());
698 }
reedd9544982014-09-09 18:46:22 -0700699
700private:
piotaixrb5fae932014-09-24 13:03:30 -0700701
reedd9544982014-09-09 18:46:22 -0700702 typedef SkBitmapDevice INHERITED;
703};
704
reed96a857e2015-01-25 10:33:58 -0800705SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000706 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800707 , fProps(SkSurfacePropsCopyOrDefault(props))
reed42b73eb2015-11-20 13:42:42 -0800708 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000709{
710 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700711
halcanary385fe4d2015-08-26 13:07:48 -0700712 this->init(new SkNoPixelsBitmapDevice(SkIRect::MakeWH(width, height), fProps),
713 kDefault_InitFlags)->unref();
reedd9544982014-09-09 18:46:22 -0700714}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000715
reed78e27682014-11-19 08:04:34 -0800716SkCanvas::SkCanvas(const SkIRect& bounds, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700717 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700718 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800719 , fConservativeRasterClip(false)
reedd9544982014-09-09 18:46:22 -0700720{
721 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700722
halcanary385fe4d2015-08-26 13:07:48 -0700723 this->init(new SkNoPixelsBitmapDevice(bounds, fProps), flags)->unref();
reedd9544982014-09-09 18:46:22 -0700724}
725
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000726SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000727 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700728 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800729 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000730{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000731 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700732
reedd9544982014-09-09 18:46:22 -0700733 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000734}
735
robertphillipsfcf78292015-06-19 11:49:52 -0700736SkCanvas::SkCanvas(SkBaseDevice* device, InitFlags flags)
737 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700738 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800739 , fConservativeRasterClip(false)
robertphillipsfcf78292015-06-19 11:49:52 -0700740{
741 inc_canvas();
742
743 this->init(device, flags);
744}
745
reed4a8126e2014-09-22 07:29:03 -0700746SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700747 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700748 , fProps(props)
reed42b73eb2015-11-20 13:42:42 -0800749 , fConservativeRasterClip(false)
reed3716fd02014-09-21 09:39:55 -0700750{
751 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700752
halcanary385fe4d2015-08-26 13:07:48 -0700753 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700754 this->init(device, kDefault_InitFlags);
755}
reed29c857d2014-09-21 10:25:07 -0700756
reed4a8126e2014-09-22 07:29:03 -0700757SkCanvas::SkCanvas(const SkBitmap& bitmap)
758 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
759 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800760 , fConservativeRasterClip(false)
reed4a8126e2014-09-22 07:29:03 -0700761{
762 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700763
halcanary385fe4d2015-08-26 13:07:48 -0700764 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700765 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000766}
767
768SkCanvas::~SkCanvas() {
769 // free up the contents of our deque
770 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000771
reed@android.com8a1c16f2008-12-17 15:59:43 +0000772 this->internalRestore(); // restore the last, since we're going away
773
halcanary385fe4d2015-08-26 13:07:48 -0700774 delete fMetaData;
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000775
reed@android.com8a1c16f2008-12-17 15:59:43 +0000776 dec_canvas();
777}
778
reed@android.com8a1c16f2008-12-17 15:59:43 +0000779SkDrawFilter* SkCanvas::getDrawFilter() const {
780 return fMCRec->fFilter;
781}
782
783SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
reed51985e32015-04-11 08:04:56 -0700784 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000785 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
786 return filter;
787}
788
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000789SkMetaData& SkCanvas::getMetaData() {
790 // metadata users are rare, so we lazily allocate it. If that changes we
791 // can decide to just make it a field in the device (rather than a ptr)
halcanary96fcdcc2015-08-27 07:41:13 -0700792 if (nullptr == fMetaData) {
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000793 fMetaData = new SkMetaData;
794 }
795 return *fMetaData;
796}
797
reed@android.com8a1c16f2008-12-17 15:59:43 +0000798///////////////////////////////////////////////////////////////////////////////
799
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000800void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000801 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000802 if (device) {
803 device->flush();
804 }
805}
806
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000807SkISize SkCanvas::getTopLayerSize() const {
808 SkBaseDevice* d = this->getTopDevice();
809 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
810}
811
812SkIPoint SkCanvas::getTopLayerOrigin() const {
813 SkBaseDevice* d = this->getTopDevice();
814 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
815}
816
817SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000818 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000819 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
820}
821
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000822SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000823 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000824 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000825 SkASSERT(rec && rec->fLayer);
826 return rec->fLayer->fDevice;
827}
828
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000829SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000830 if (updateMatrixClip) {
831 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
832 }
reed@google.com9266fed2011-03-30 00:18:03 +0000833 return fMCRec->fTopLayer->fDevice;
834}
835
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000836bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
837 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
838 return false;
839 }
840
841 bool weAllocated = false;
halcanary96fcdcc2015-08-27 07:41:13 -0700842 if (nullptr == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700843 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000844 return false;
845 }
846 weAllocated = true;
847 }
848
reedcf01e312015-05-23 19:14:51 -0700849 SkAutoPixmapUnlock unlocker;
850 if (bitmap->requestLock(&unlocker)) {
851 const SkPixmap& pm = unlocker.pixmap();
852 if (this->readPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), x, y)) {
853 return true;
854 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000855 }
856
857 if (weAllocated) {
halcanary96fcdcc2015-08-27 07:41:13 -0700858 bitmap->setPixelRef(nullptr);
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000859 }
860 return false;
861}
reed@google.com51df9e32010-12-23 19:29:18 +0000862
bsalomon@google.comc6980972011-11-02 19:57:21 +0000863bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000864 SkIRect r = srcRect;
865 const SkISize size = this->getBaseLayerSize();
866 if (!r.intersect(0, 0, size.width(), size.height())) {
867 bitmap->reset();
868 return false;
869 }
870
reed84825042014-09-02 12:50:45 -0700871 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000872 // bitmap will already be reset.
873 return false;
874 }
875 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
876 bitmap->reset();
877 return false;
878 }
879 return true;
880}
881
reed96472de2014-12-10 09:53:42 -0800882bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000883 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000884 if (!device) {
885 return false;
886 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000887 const SkISize size = this->getBaseLayerSize();
mtkleinf0f14112014-12-12 08:46:25 -0800888
reed96472de2014-12-10 09:53:42 -0800889 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
890 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000891 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000892 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000893
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000894 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800895 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000896}
897
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000898bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
899 if (bitmap.getTexture()) {
900 return false;
901 }
reedcf01e312015-05-23 19:14:51 -0700902
903 SkAutoPixmapUnlock unlocker;
904 if (bitmap.requestLock(&unlocker)) {
905 const SkPixmap& pm = unlocker.pixmap();
906 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000907 }
908 return false;
909}
910
911bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
912 int x, int y) {
913 switch (origInfo.colorType()) {
914 case kUnknown_SkColorType:
915 case kIndex_8_SkColorType:
916 return false;
917 default:
918 break;
919 }
halcanary96fcdcc2015-08-27 07:41:13 -0700920 if (nullptr == pixels || rowBytes < origInfo.minRowBytes()) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000921 return false;
922 }
923
924 const SkISize size = this->getBaseLayerSize();
925 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
926 if (!target.intersect(0, 0, size.width(), size.height())) {
927 return false;
928 }
929
930 SkBaseDevice* device = this->getDevice();
931 if (!device) {
932 return false;
933 }
934
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000935 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700936 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000937
938 // if x or y are negative, then we have to adjust pixels
939 if (x > 0) {
940 x = 0;
941 }
942 if (y > 0) {
943 y = 0;
944 }
945 // here x,y are either 0 or negative
946 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
947
reed4af35f32014-06-27 17:47:49 -0700948 // Tell our owning surface to bump its generation ID
reedc83a2972015-07-16 07:40:45 -0700949 const bool completeOverwrite = info.dimensions() == size;
950 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700951
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000952 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000953 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000954}
reed@google.com51df9e32010-12-23 19:29:18 +0000955
junov@google.com4370aed2012-01-18 16:21:08 +0000956SkCanvas* SkCanvas::canvasForDrawIter() {
957 return this;
958}
959
reed@android.com8a1c16f2008-12-17 15:59:43 +0000960//////////////////////////////////////////////////////////////////////////////
961
reed@android.com8a1c16f2008-12-17 15:59:43 +0000962void SkCanvas::updateDeviceCMCache() {
963 if (fDeviceCMDirty) {
964 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700965 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000966 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000967
halcanary96fcdcc2015-08-27 07:41:13 -0700968 if (nullptr == layer->fNext) { // only one layer
969 layer->updateMC(totalMatrix, totalClip, *fClipStack, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000970 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000971 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000972 do {
reed687fa1c2015-04-07 08:00:56 -0700973 layer->updateMC(totalMatrix, clip, *fClipStack, &clip);
halcanary96fcdcc2015-08-27 07:41:13 -0700974 } while ((layer = layer->fNext) != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000975 }
976 fDeviceCMDirty = false;
977 }
978}
979
reed@android.com8a1c16f2008-12-17 15:59:43 +0000980///////////////////////////////////////////////////////////////////////////////
981
reed2ff1fce2014-12-11 07:07:37 -0800982void SkCanvas::checkForDeferredSave() {
983 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800984 this->doSave();
985 }
986}
987
reedf0090cb2014-11-26 08:55:51 -0800988int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800989#ifdef SK_DEBUG
990 int count = 0;
991 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
992 for (;;) {
993 const MCRec* rec = (const MCRec*)iter.next();
994 if (!rec) {
995 break;
996 }
997 count += 1 + rec->fDeferredSaveCount;
998 }
999 SkASSERT(count == fSaveCount);
1000#endif
1001 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -08001002}
1003
1004int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -08001005 fSaveCount += 1;
1006 fMCRec->fDeferredSaveCount += 1;
1007 return this->getSaveCount() - 1; // return our prev value
1008}
1009
1010void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -08001011 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -07001012
1013 SkASSERT(fMCRec->fDeferredSaveCount > 0);
1014 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001015 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -08001016}
1017
1018void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -08001019 if (fMCRec->fDeferredSaveCount > 0) {
1020 SkASSERT(fSaveCount > 1);
1021 fSaveCount -= 1;
1022 fMCRec->fDeferredSaveCount -= 1;
1023 } else {
1024 // check for underflow
1025 if (fMCStack.count() > 1) {
1026 this->willRestore();
1027 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -07001028 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001029 this->internalRestore();
1030 this->didRestore();
1031 }
reedf0090cb2014-11-26 08:55:51 -08001032 }
1033}
1034
1035void SkCanvas::restoreToCount(int count) {
1036 // sanity check
1037 if (count < 1) {
1038 count = 1;
1039 }
mtkleinf0f14112014-12-12 08:46:25 -08001040
reedf0090cb2014-11-26 08:55:51 -08001041 int n = this->getSaveCount() - count;
1042 for (int i = 0; i < n; ++i) {
1043 this->restore();
1044 }
1045}
1046
reed2ff1fce2014-12-11 07:07:37 -08001047void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001048 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -07001049 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +00001050 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +00001051
reed687fa1c2015-04-07 08:00:56 -07001052 fClipStack->save();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001053}
1054
reed4960eee2015-12-18 07:09:18 -08001055bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
reed@google.comb93ba452014-03-10 19:47:58 +00001056#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001057 return !(saveLayerFlags & SkCanvas::kDontClipToLayer_PrivateSaveLayerFlag);
reed@google.comb93ba452014-03-10 19:47:58 +00001058#else
1059 return true;
1060#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001061}
1062
reed4960eee2015-12-18 07:09:18 -08001063bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -07001064 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001065 SkIRect clipBounds;
1066 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001067 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +00001068 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001069
reed96e657d2015-03-10 17:30:07 -07001070 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
1071
senorblanco87e066e2015-10-28 11:23:36 -07001072// This is a temporary hack, until individual filters can do their own
1073// bloating, when this will be removed.
1074#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
1075 SkRect storage;
1076#endif
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001077 if (imageFilter) {
reed96e657d2015-03-10 17:30:07 -07001078 imageFilter->filterBounds(clipBounds, ctm, &clipBounds);
senorblanco87e066e2015-10-28 11:23:36 -07001079#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
1080 if (bounds && imageFilter->canComputeFastBounds()) {
1081 imageFilter->computeFastBounds(*bounds, &storage);
1082 bounds = &storage;
1083 } else {
1084 bounds = nullptr;
1085 }
senorblancodb64af32015-12-09 10:11:43 -08001086#else
1087 if (bounds && !imageFilter->canComputeFastBounds()) {
1088 bounds = nullptr;
1089 }
senorblanco87e066e2015-10-28 11:23:36 -07001090#endif
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001091 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001092 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -07001093 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001094 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +00001095
reed96e657d2015-03-10 17:30:07 -07001096 ctm.mapRect(&r, *bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001097 r.roundOut(&ir);
1098 // early exit if the layer's bounds are clipped out
1099 if (!ir.intersect(clipBounds)) {
reed4960eee2015-12-18 07:09:18 -08001100 if (BoundsAffectsClip(saveLayerFlags)) {
reed9b3aa542015-03-11 08:47:12 -07001101 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001102 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001103 }
junov@chromium.orga907ac32012-02-24 21:54:07 +00001104 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001105 }
1106 } else { // no user bounds, so just use the clip
1107 ir = clipBounds;
1108 }
reed180aec42015-03-11 10:39:04 -07001109 SkASSERT(!ir.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001110
reed4960eee2015-12-18 07:09:18 -08001111 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -07001112 // Simplify the current clips since they will be applied properly during restore()
reed9b3aa542015-03-11 08:47:12 -07001113 fCachedLocalClipBoundsDirty = true;
reed687fa1c2015-04-07 08:00:56 -07001114 fClipStack->clipDevRect(ir, SkRegion::kReplace_Op);
reed180aec42015-03-11 10:39:04 -07001115 fMCRec->fRasterClip.setRect(ir);
junov@chromium.orga907ac32012-02-24 21:54:07 +00001116 }
1117
1118 if (intersection) {
1119 *intersection = ir;
1120 }
1121 return true;
1122}
1123
reedbada1882015-12-21 13:09:44 -08001124#ifdef SK_SUPPORT_LEGACY_SAVEFLAGS
reed4960eee2015-12-18 07:09:18 -08001125uint32_t SkCanvas::SaveFlagsToSaveLayerFlags(SaveFlags flags) {
1126 uint32_t layerFlags = 0;
1127
1128 if (0 == (flags & kClipToLayer_SaveFlag)) {
1129 layerFlags |= kDontClipToLayer_PrivateSaveLayerFlag;
reedd990e2f2014-12-22 11:58:30 -08001130 }
reed4960eee2015-12-18 07:09:18 -08001131 if (0 == (flags & kHasAlphaLayer_SaveFlag)) {
1132 layerFlags |= kIsOpaque_SaveLayerFlag;
1133 }
1134 return layerFlags;
1135}
scroggoffe031e2016-01-04 07:16:32 -08001136
1137uint32_t SkCanvas::SaveLayerFlagsToSaveFlags(SaveLayerFlags layerFlags) {
1138 uint32_t saveFlags = 0;
1139
1140 if (0 == (layerFlags & kDontClipToLayer_PrivateSaveLayerFlag)) {
1141 saveFlags |= kClipToLayer_SaveFlag;
1142 }
1143 if (0 == (layerFlags & kIsOpaque_SaveLayerFlag)) {
1144 saveFlags |= kHasAlphaLayer_SaveFlag;
1145 }
1146 return saveFlags;
1147}
reedbada1882015-12-21 13:09:44 -08001148#endif
reed4960eee2015-12-18 07:09:18 -08001149
reed4960eee2015-12-18 07:09:18 -08001150int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
1151 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001152}
1153
reedbada1882015-12-21 13:09:44 -08001154#ifdef SK_SUPPORT_LEGACY_SAVEFLAGS
reed2ff1fce2014-12-11 07:07:37 -08001155int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags) {
reed4960eee2015-12-18 07:09:18 -08001156 return this->saveLayer(SaveLayerRec(bounds, paint, SaveFlagsToSaveLayerFlags(flags)));
reed@google.com8926b162012-03-23 15:36:36 +00001157}
reedbada1882015-12-21 13:09:44 -08001158#endif
reed@google.com8926b162012-03-23 15:36:36 +00001159
reed70ee31b2015-12-10 13:44:45 -08001160int SkCanvas::saveLayerPreserveLCDTextRequests(const SkRect* bounds, const SkPaint* paint) {
reed4960eee2015-12-18 07:09:18 -08001161 return this->saveLayer(SaveLayerRec(bounds, paint, kPreserveLCDText_SaveLayerFlag));
1162}
1163
1164int SkCanvas::saveLayer(const SaveLayerRec& origRec) {
1165 SaveLayerRec rec(origRec);
1166 if (gIgnoreSaveLayerBounds) {
1167 rec.fBounds = nullptr;
1168 }
1169 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
1170 fSaveCount += 1;
1171 this->internalSaveLayer(rec, strategy);
1172 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -08001173}
1174
reedbfd5f172016-01-07 11:28:08 -08001175static void draw_filter_into_device(SkBaseDevice* src, const SkImageFilter* filter,
1176 SkBaseDevice* dst, const SkMatrix& ctm) {
robertphillips7354a4b2015-12-16 05:08:27 -08001177
1178 SkBitmap srcBM;
1179
1180#if SK_SUPPORT_GPU
1181 GrRenderTarget* srcRT = src->accessRenderTarget();
1182 if (srcRT && !srcRT->asTexture() && dst->accessRenderTarget()) {
1183 // When both the src & the dst are on the gpu but the src doesn't have a texture,
1184 // we create a temporary texture for the draw.
1185 // TODO: we should actually only copy the portion of the source needed to apply the image
1186 // filter
1187 GrContext* context = srcRT->getContext();
1188 SkAutoTUnref<GrTexture> tex(context->textureProvider()->createTexture(srcRT->desc(), true));
1189
1190 context->copySurface(tex, srcRT);
1191
1192 GrWrapTextureInBitmap(tex, src->width(), src->height(), src->isOpaque(), &srcBM);
1193 } else
1194#endif
1195 {
1196 srcBM = src->accessBitmap(false);
1197 }
1198
1199 SkCanvas c(dst);
1200
reedbfd5f172016-01-07 11:28:08 -08001201 SkAutoTUnref<SkImageFilter> localF(filter->newWithLocalMatrix(ctm));
robertphillips7354a4b2015-12-16 05:08:27 -08001202 SkPaint p;
reedbfd5f172016-01-07 11:28:08 -08001203 p.setImageFilter(localF);
1204 const SkScalar x = SkIntToScalar(src->getOrigin().x());
1205 const SkScalar y = SkIntToScalar(src->getOrigin().y());
1206 c.drawBitmap(srcBM, x, y, &p);
robertphillips7354a4b2015-12-16 05:08:27 -08001207}
reed70ee31b2015-12-10 13:44:45 -08001208
reed4960eee2015-12-18 07:09:18 -08001209void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
1210 const SkRect* bounds = rec.fBounds;
1211 const SkPaint* paint = rec.fPaint;
1212 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1213
reed@google.comb93ba452014-03-10 19:47:58 +00001214#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001215 saveLayerFlags &= ~kDontClipToLayer_PrivateSaveLayerFlag;
reed@google.comb93ba452014-03-10 19:47:58 +00001216#endif
1217
junov@chromium.orga907ac32012-02-24 21:54:07 +00001218 // do this before we create the layer. We don't call the public save() since
1219 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001220 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001221
1222 fDeviceCMDirty = true;
1223
1224 SkIRect ir;
reed4960eee2015-12-18 07:09:18 -08001225 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, paint ? paint->getImageFilter() : nullptr)) {
reed2ff1fce2014-12-11 07:07:37 -08001226 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001227 }
1228
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001229 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1230 // the clipRectBounds() call above?
1231 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001232 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001233 }
1234
reed4960eee2015-12-18 07:09:18 -08001235 bool isOpaque = SkToBool(saveLayerFlags & kIsOpaque_SaveLayerFlag);
reed8dc0ccb2015-03-20 06:32:52 -07001236 SkPixelGeometry geo = fProps.pixelGeometry();
1237 if (paint) {
reed76033be2015-03-14 10:54:31 -07001238 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001239 if (paint->getImageFilter() || paint->getColorFilter()) {
reed76033be2015-03-14 10:54:31 -07001240 isOpaque = false;
reed8dc0ccb2015-03-20 06:32:52 -07001241 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001242 }
1243 }
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001244 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
1245 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001246
reedb2db8982014-11-13 12:41:02 -08001247 SkBaseDevice* device = this->getTopDevice();
halcanary96fcdcc2015-08-27 07:41:13 -07001248 if (nullptr == device) {
reedb2db8982014-11-13 12:41:02 -08001249 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001250 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001251 }
reedb2db8982014-11-13 12:41:02 -08001252
reed61f501f2015-04-29 08:34:00 -07001253 bool forceSpriteOnRestore = false;
1254 {
reed70ee31b2015-12-10 13:44:45 -08001255 const bool preserveLCDText = kOpaque_SkAlphaType == info.alphaType() ||
reed4960eee2015-12-18 07:09:18 -08001256 (saveLayerFlags & kPreserveLCDText_SaveLayerFlag);
reeddaa57bf2015-05-15 10:39:17 -07001257 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
reed70ee31b2015-12-10 13:44:45 -08001258 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
1259 preserveLCDText, false);
reed61f501f2015-04-29 08:34:00 -07001260 SkBaseDevice* newDev = device->onCreateDevice(createInfo, paint);
halcanary96fcdcc2015-08-27 07:41:13 -07001261 if (nullptr == newDev) {
reed61f501f2015-04-29 08:34:00 -07001262 // If onCreateDevice didn't succeed, try raster (e.g. PDF couldn't handle the paint)
robertphillips9a53fd72015-06-22 09:46:59 -07001263 const SkSurfaceProps surfaceProps(fProps.flags(), createInfo.fPixelGeometry);
1264 newDev = SkBitmapDevice::Create(createInfo.fInfo, surfaceProps);
halcanary96fcdcc2015-08-27 07:41:13 -07001265 if (nullptr == newDev) {
reed61f501f2015-04-29 08:34:00 -07001266 SkErrorInternals::SetError(kInternalError_SkError,
1267 "Unable to create device for layer.");
1268 return;
1269 }
1270 forceSpriteOnRestore = true;
1271 }
1272 device = newDev;
bungeman@google.come25c6842011-08-17 14:53:54 +00001273 }
reed@google.com6f8f2922011-03-04 22:27:10 +00001274 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips7354a4b2015-12-16 05:08:27 -08001275
reedbfd5f172016-01-07 11:28:08 -08001276 if (rec.fBackdrop) {
1277 draw_filter_into_device(fMCRec->fTopLayer->fDevice, rec.fBackdrop, device, fMCRec->fMatrix);
robertphillips7354a4b2015-12-16 05:08:27 -08001278 }
1279
halcanary385fe4d2015-08-26 13:07:48 -07001280 DeviceCM* layer =
1281 new DeviceCM(device, paint, this, fConservativeRasterClip, forceSpriteOnRestore);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001282 device->unref();
1283
1284 layer->fNext = fMCRec->fTopLayer;
1285 fMCRec->fLayer = layer;
1286 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reed@android.com8a1c16f2008-12-17 15:59:43 +00001287}
1288
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001289int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001290 if (0xFF == alpha) {
1291 return this->saveLayer(bounds, nullptr);
1292 } else {
1293 SkPaint tmpPaint;
1294 tmpPaint.setAlpha(alpha);
1295 return this->saveLayer(bounds, &tmpPaint);
1296 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001297}
1298
reedbada1882015-12-21 13:09:44 -08001299#ifdef SK_SUPPORT_LEGACY_SAVEFLAGS
reed@android.com8a1c16f2008-12-17 15:59:43 +00001300int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
1301 SaveFlags flags) {
1302 if (0xFF == alpha) {
halcanary96fcdcc2015-08-27 07:41:13 -07001303 return this->saveLayer(bounds, nullptr, flags);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001304 } else {
1305 SkPaint tmpPaint;
1306 tmpPaint.setAlpha(alpha);
1307 return this->saveLayer(bounds, &tmpPaint, flags);
1308 }
1309}
reedbada1882015-12-21 13:09:44 -08001310#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001311
reed@android.com8a1c16f2008-12-17 15:59:43 +00001312void SkCanvas::internalRestore() {
1313 SkASSERT(fMCStack.count() != 0);
1314
1315 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001316 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001317
reed687fa1c2015-04-07 08:00:56 -07001318 fClipStack->restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001319
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001320 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001321 DeviceCM* layer = fMCRec->fLayer; // may be null
1322 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001323 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001324
1325 // now do the normal restore()
1326 fMCRec->~MCRec(); // balanced in save()
1327 fMCStack.pop_back();
1328 fMCRec = (MCRec*)fMCStack.back();
1329
1330 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1331 since if we're being recorded, we don't want to record this (the
1332 recorder will have already recorded the restore).
1333 */
bsalomon49f085d2014-09-05 13:34:00 -07001334 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001335 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001336 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001337 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
reed61f501f2015-04-29 08:34:00 -07001338 layer->fPaint, layer->fDeviceIsBitmapDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001339 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001340 fDeviceCMDirty = true;
halcanary385fe4d2015-08-26 13:07:48 -07001341 delete layer;
reedb679ca82015-04-07 04:40:48 -07001342 } else {
1343 // we're at the root
reeda499f902015-05-01 09:34:31 -07001344 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001345 layer->~DeviceCM();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001346 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001347 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001348}
1349
reed4a8126e2014-09-22 07:29:03 -07001350SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001351 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001352 props = &fProps;
1353 }
1354 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001355}
1356
reed4a8126e2014-09-22 07:29:03 -07001357SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001358 SkBaseDevice* dev = this->getDevice();
halcanary96fcdcc2015-08-27 07:41:13 -07001359 return dev ? dev->newSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001360}
1361
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001362SkImageInfo SkCanvas::imageInfo() const {
1363 SkBaseDevice* dev = this->getDevice();
1364 if (dev) {
1365 return dev->imageInfo();
1366 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001367 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001368 }
1369}
1370
1371const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
reed884e97c2015-05-26 11:31:54 -07001372 SkPixmap pmap;
1373 if (!this->onPeekPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001374 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001375 }
1376 if (info) {
1377 *info = pmap.info();
1378 }
1379 if (rowBytes) {
1380 *rowBytes = pmap.rowBytes();
1381 }
1382 return pmap.addr();
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001383}
1384
reed884e97c2015-05-26 11:31:54 -07001385bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001386 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001387 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001388}
1389
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001390void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001391 SkPixmap pmap;
1392 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001393 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001394 }
1395 if (info) {
1396 *info = pmap.info();
1397 }
1398 if (rowBytes) {
1399 *rowBytes = pmap.rowBytes();
1400 }
1401 if (origin) {
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001402 *origin = this->getTopDevice(false)->getOrigin();
1403 }
reed884e97c2015-05-26 11:31:54 -07001404 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001405}
1406
reed884e97c2015-05-26 11:31:54 -07001407bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001408 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001409 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001410}
1411
reed@android.com8a1c16f2008-12-17 15:59:43 +00001412/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001413
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001414void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed61f501f2015-04-29 08:34:00 -07001415 const SkPaint* paint, bool deviceIsBitmapDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001416 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001417 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001418 paint = &tmp;
1419 }
reed@google.com4b226022011-01-11 18:32:13 +00001420
reed@google.com8926b162012-03-23 15:36:36 +00001421 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001422 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001423 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001424 paint = &looper.paint();
1425 SkImageFilter* filter = paint->getImageFilter();
1426 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001427 if (filter && !dstDev->canHandleImageFilter(filter)) {
reed88d064d2015-10-12 11:30:02 -07001428 SkImageFilter::DeviceProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001429 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001430 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001431 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001432 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001433 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblancodb64af32015-12-09 10:11:43 -08001434#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001435 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblancodb64af32015-12-09 10:11:43 -08001436#else
1437 SkIRect clipBounds = iter.fClip->getBounds().makeOffset(-pos.x(), -pos.y());
1438#endif
senorblancobe129b22014-08-08 07:14:35 -07001439 SkAutoTUnref<SkImageFilter::Cache> cache(dstDev->getImageFilterCache());
reedc9b5f8b2015-10-22 13:20:20 -07001440 SkImageFilter::Context ctx(matrix, clipBounds, cache.get(),
1441 SkImageFilter::kApprox_SizeConstraint);
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001442 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001443 SkPaint tmpUnfiltered(*paint);
halcanary96fcdcc2015-08-27 07:41:13 -07001444 tmpUnfiltered.setImageFilter(nullptr);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001445 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1446 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001447 }
reed61f501f2015-04-29 08:34:00 -07001448 } else if (deviceIsBitmapDevice) {
reed9572a102015-05-26 19:22:17 -07001449 const SkBitmap& src = static_cast<SkBitmapDevice*>(srcDev)->fBitmap;
reed61f501f2015-04-29 08:34:00 -07001450 dstDev->drawSprite(iter, src, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001451 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001452 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001453 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001454 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001455 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001456}
1457
reed32704672015-12-16 08:27:10 -08001458/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001459
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001460void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001461 SkMatrix m;
1462 m.setTranslate(dx, dy);
1463 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001464}
1465
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001466void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001467 SkMatrix m;
1468 m.setScale(sx, sy);
1469 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001470}
1471
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001472void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001473 SkMatrix m;
1474 m.setRotate(degrees);
1475 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001476}
1477
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001478void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001479 SkMatrix m;
1480 m.setSkew(sx, sy);
1481 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001482}
1483
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001484void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001485 if (matrix.isIdentity()) {
1486 return;
1487 }
1488
reed2ff1fce2014-12-11 07:07:37 -08001489 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001490 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001491 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001492 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001493
1494 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001495}
1496
reed86a17e72015-05-14 12:25:22 -07001497void SkCanvas::setMatrix(const SkMatrix& matrix) {
1498 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001499 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001500 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001501 fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001502 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001503}
1504
reed@android.com8a1c16f2008-12-17 15:59:43 +00001505void SkCanvas::resetMatrix() {
1506 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001507
reed@android.com8a1c16f2008-12-17 15:59:43 +00001508 matrix.reset();
1509 this->setMatrix(matrix);
1510}
1511
1512//////////////////////////////////////////////////////////////////////////////
1513
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001514void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001515 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001516 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1517 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001518}
1519
1520void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001521#ifdef SK_ENABLE_CLIP_QUICKREJECT
1522 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001523 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001524 return false;
1525 }
1526
reed@google.com3b3e8952012-08-16 20:53:31 +00001527 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001528 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001529 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001530
reed687fa1c2015-04-07 08:00:56 -07001531 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001532 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001533 }
1534 }
1535#endif
1536
bsalomonac8cabd2015-11-20 18:53:07 -08001537 if (!fAllowSoftClip) {
1538 edgeStyle = kHard_ClipEdgeStyle;
1539 }
reed90ba0952015-11-20 13:42:47 -08001540
reedc64eff52015-11-21 12:39:45 -08001541 const bool rectStaysRect = fMCRec->fMatrix.rectStaysRect();
1542 SkRect devR;
1543 if (rectStaysRect) {
1544 fMCRec->fMatrix.mapRect(&devR, rect);
1545 }
bsalomonac8cabd2015-11-20 18:53:07 -08001546
reedc64eff52015-11-21 12:39:45 -08001547 // Check if we can quick-accept the clip call (and do nothing)
1548 //
1549 // TODO: investigate if a (conservative) version of this could be done in ::clipRect,
1550 // so that subclasses (like PictureRecording) didn't see unnecessary clips, which in turn
1551 // might allow lazy save/restores to eliminate entire save/restore blocks.
1552 //
1553 if (SkRegion::kIntersect_Op == op &&
1554 kHard_ClipEdgeStyle == edgeStyle
1555 && rectStaysRect)
1556 {
1557 if (devR.round().contains(fMCRec->fRasterClip.getBounds())) {
1558#if 0
1559 SkDebugf("------- ignored clipRect [%g %g %g %g]\n",
1560 rect.left(), rect.top(), rect.right(), rect.bottom());
1561#endif
1562 return;
1563 }
1564 }
1565
1566 AutoValidateClip avc(this);
1567
1568 fDeviceCMDirty = true;
1569 fCachedLocalClipBoundsDirty = true;
1570
1571 if (rectStaysRect) {
1572 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1573 fClipStack->clipDevRect(devR, op, isAA);
1574 fMCRec->fRasterClip.op(devR, this->getBaseLayerSize(), op, isAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001575 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001576 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001577 // and clip against that, since it can handle any matrix. However, to
1578 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1579 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001580 SkPath path;
1581
1582 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001583 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001584 }
1585}
1586
reed73e714e2014-09-04 09:02:23 -07001587static void rasterclip_path(SkRasterClip* rc, const SkCanvas* canvas, const SkPath& devPath,
1588 SkRegion::Op op, bool doAA) {
reedd64c9482014-09-05 17:37:38 -07001589 rc->op(devPath, canvas->getBaseLayerSize(), op, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001590}
1591
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001592void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001593 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001594 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001595 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001596 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1597 } else {
1598 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001599 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001600}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001601
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001602void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001603 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001604 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001605 AutoValidateClip avc(this);
1606
1607 fDeviceCMDirty = true;
1608 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001609 if (!fAllowSoftClip) {
1610 edgeStyle = kHard_ClipEdgeStyle;
1611 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001612
reed687fa1c2015-04-07 08:00:56 -07001613 fClipStack->clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001614
robertphillips125f19a2015-11-23 09:00:05 -08001615 fMCRec->fRasterClip.op(transformedRRect, this->getBaseLayerSize(), op,
1616 kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001617 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001618 }
1619
1620 SkPath path;
1621 path.addRRect(rrect);
1622 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001623 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001624}
1625
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001626void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001627 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001628 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001629
1630 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1631 SkRect r;
1632 if (path.isRect(&r)) {
1633 this->onClipRect(r, op, edgeStyle);
1634 return;
1635 }
1636 SkRRect rrect;
1637 if (path.isOval(&r)) {
1638 rrect.setOval(r);
1639 this->onClipRRect(rrect, op, edgeStyle);
1640 return;
1641 }
1642 if (path.isRRect(&rrect)) {
1643 this->onClipRRect(rrect, op, edgeStyle);
1644 return;
1645 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001646 }
robertphillips39f05382015-11-24 09:30:12 -08001647
1648 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001649}
1650
1651void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001652#ifdef SK_ENABLE_CLIP_QUICKREJECT
1653 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001654 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001655 return false;
1656 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001657
reed@google.com3b3e8952012-08-16 20:53:31 +00001658 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001659 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001660 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001661
reed687fa1c2015-04-07 08:00:56 -07001662 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001663 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001664 }
1665 }
1666#endif
1667
reed@google.com5c3d1472011-02-22 19:12:23 +00001668 AutoValidateClip avc(this);
1669
reed@android.com8a1c16f2008-12-17 15:59:43 +00001670 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001671 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001672 if (!fAllowSoftClip) {
1673 edgeStyle = kHard_ClipEdgeStyle;
1674 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001675
1676 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001677 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001678
reed@google.comfe701122011-11-08 19:41:23 +00001679 // Check if the transfomation, or the original path itself
1680 // made us empty. Note this can also happen if we contained NaN
1681 // values. computing the bounds detects this, and will set our
1682 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1683 if (devPath.getBounds().isEmpty()) {
1684 // resetting the path will remove any NaN or other wanky values
1685 // that might upset our scan converter.
1686 devPath.reset();
1687 }
1688
reed@google.com5c3d1472011-02-22 19:12:23 +00001689 // if we called path.swap() we could avoid a deep copy of this path
reed687fa1c2015-04-07 08:00:56 -07001690 fClipStack->clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001691
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001692 if (fAllowSimplifyClip) {
fmalita1a481fe2015-02-04 07:39:34 -08001693 bool clipIsAA = getClipStack()->asPath(&devPath);
1694 if (clipIsAA) {
1695 edgeStyle = kSoft_ClipEdgeStyle;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001696 }
fmalita1a481fe2015-02-04 07:39:34 -08001697
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001698 op = SkRegion::kReplace_Op;
1699 }
1700
reed73e714e2014-09-04 09:02:23 -07001701 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001702}
1703
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001704void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed2ff1fce2014-12-11 07:07:37 -08001705 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001706 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001707}
1708
1709void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001710 AutoValidateClip avc(this);
1711
reed@android.com8a1c16f2008-12-17 15:59:43 +00001712 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001713 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001714
reed@google.com5c3d1472011-02-22 19:12:23 +00001715 // todo: signal fClipStack that we have a region, and therefore (I guess)
1716 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001717 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001718
reed1f836ee2014-07-07 07:49:34 -07001719 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001720}
1721
reed@google.com819c9212011-02-23 18:56:55 +00001722#ifdef SK_DEBUG
1723void SkCanvas::validateClip() const {
1724 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001725 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001726 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001727 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001728 return;
1729 }
1730
reed@google.com819c9212011-02-23 18:56:55 +00001731 SkIRect ir;
1732 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001733 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001734
reed687fa1c2015-04-07 08:00:56 -07001735 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001736 const SkClipStack::Element* element;
halcanary96fcdcc2015-08-27 07:41:13 -07001737 while ((element = iter.next()) != nullptr) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001738 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001739 case SkClipStack::Element::kRect_Type:
1740 element->getRect().round(&ir);
1741 tmpClip.op(ir, element->getOp());
1742 break;
1743 case SkClipStack::Element::kEmpty_Type:
1744 tmpClip.setEmpty();
1745 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001746 default: {
1747 SkPath path;
1748 element->asPath(&path);
reed73e714e2014-09-04 09:02:23 -07001749 rasterclip_path(&tmpClip, this, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001750 break;
1751 }
reed@google.com819c9212011-02-23 18:56:55 +00001752 }
1753 }
reed@google.com819c9212011-02-23 18:56:55 +00001754}
1755#endif
1756
reed@google.com90c07ea2012-04-13 13:50:27 +00001757void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001758 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001759 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001760
halcanary96fcdcc2015-08-27 07:41:13 -07001761 while ((element = iter.next()) != nullptr) {
fmalitac3b589a2014-06-05 12:40:07 -07001762 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001763 }
1764}
1765
reed@google.com5c3d1472011-02-22 19:12:23 +00001766///////////////////////////////////////////////////////////////////////////////
1767
reed@google.com754de5f2014-02-24 19:38:20 +00001768bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001769 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001770}
1771
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001772bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001773 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001774}
1775
reed@google.com3b3e8952012-08-16 20:53:31 +00001776bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001777 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001778 return true;
1779
reed1f836ee2014-07-07 07:49:34 -07001780 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001781 return true;
1782 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001783
reed1f836ee2014-07-07 07:49:34 -07001784 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001785 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001786 fMCRec->fMatrix.mapRect(&dst, rect);
reedb07a94f2014-11-19 05:03:18 -08001787 return !SkIRect::Intersects(dst.roundOut(), fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001788 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001789 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001790
reed@android.coma380ae42009-07-21 01:17:02 +00001791 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001792 // TODO: should we use | instead, or compare all 4 at once?
1793 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001794 return true;
1795 }
reed@google.comc0784db2013-12-13 21:16:12 +00001796 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001797 return true;
1798 }
1799 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001800 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001801}
1802
reed@google.com3b3e8952012-08-16 20:53:31 +00001803bool SkCanvas::quickReject(const SkPath& path) const {
1804 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001805}
1806
reed@google.com3b3e8952012-08-16 20:53:31 +00001807bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001808 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001809 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001810 return false;
1811 }
1812
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001813 SkMatrix inverse;
1814 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001815 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001816 if (bounds) {
1817 bounds->setEmpty();
1818 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001819 return false;
1820 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001821
bsalomon49f085d2014-09-05 13:34:00 -07001822 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001823 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001824 // adjust it outwards in case we are antialiasing
1825 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001826
reed@google.com8f4d2302013-12-17 16:44:46 +00001827 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1828 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001829 inverse.mapRect(bounds, r);
1830 }
1831 return true;
1832}
1833
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001834bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001835 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001836 if (clip.isEmpty()) {
1837 if (bounds) {
1838 bounds->setEmpty();
1839 }
1840 return false;
1841 }
1842
bsalomon49f085d2014-09-05 13:34:00 -07001843 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001844 *bounds = clip.getBounds();
1845 }
1846 return true;
1847}
1848
reed@android.com8a1c16f2008-12-17 15:59:43 +00001849const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001850 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001851}
1852
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001853const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001854 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001855}
1856
reed@google.com9c135db2014-03-12 18:28:35 +00001857GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1858 SkBaseDevice* dev = this->getTopDevice();
halcanary96fcdcc2015-08-27 07:41:13 -07001859 return dev ? dev->accessRenderTarget() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001860}
1861
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001862GrContext* SkCanvas::getGrContext() {
1863#if SK_SUPPORT_GPU
1864 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07001865 if (device) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001866 GrRenderTarget* renderTarget = device->accessRenderTarget();
bsalomon49f085d2014-09-05 13:34:00 -07001867 if (renderTarget) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001868 return renderTarget->getContext();
1869 }
1870 }
1871#endif
1872
halcanary96fcdcc2015-08-27 07:41:13 -07001873 return nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001874
1875}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001876
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001877void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1878 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001879 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001880 if (outer.isEmpty()) {
1881 return;
1882 }
1883 if (inner.isEmpty()) {
1884 this->drawRRect(outer, paint);
1885 return;
1886 }
1887
1888 // We don't have this method (yet), but technically this is what we should
1889 // be able to assert...
1890 // SkASSERT(outer.contains(inner));
1891 //
1892 // For now at least check for containment of bounds
1893 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1894
1895 this->onDrawDRRect(outer, inner, paint);
1896}
1897
reed41af9662015-01-05 07:49:08 -08001898// These need to stop being virtual -- clients need to override the onDraw... versions
1899
1900void SkCanvas::drawPaint(const SkPaint& paint) {
1901 this->onDrawPaint(paint);
1902}
1903
1904void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1905 this->onDrawRect(r, paint);
1906}
1907
1908void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1909 this->onDrawOval(r, paint);
1910}
1911
1912void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1913 this->onDrawRRect(rrect, paint);
1914}
1915
1916void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1917 this->onDrawPoints(mode, count, pts, paint);
1918}
1919
1920void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
1921 const SkPoint texs[], const SkColor colors[], SkXfermode* xmode,
1922 const uint16_t indices[], int indexCount, const SkPaint& paint) {
1923 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
1924 indices, indexCount, paint);
1925}
1926
1927void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1928 this->onDrawPath(path, paint);
1929}
1930
reeda85d4d02015-05-06 12:56:48 -07001931void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
1932 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001933}
1934
reede47829b2015-08-06 10:02:53 -07001935void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1936 const SkPaint* paint, SrcRectConstraint constraint) {
1937 if (dst.isEmpty() || src.isEmpty()) {
1938 return;
1939 }
1940 this->onDrawImageRect(image, &src, dst, paint, constraint);
1941}
reed41af9662015-01-05 07:49:08 -08001942
reed84984ef2015-07-17 07:09:43 -07001943void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1944 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001945 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001946}
1947
reede47829b2015-08-06 10:02:53 -07001948void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
1949 SrcRectConstraint constraint) {
1950 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
1951 constraint);
1952}
reede47829b2015-08-06 10:02:53 -07001953
reed4c21dc52015-06-25 12:32:03 -07001954void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1955 const SkPaint* paint) {
1956 if (dst.isEmpty()) {
1957 return;
1958 }
1959 if (!SkNinePatchIter::Valid(image->width(), image->height(), center)) {
reede47829b2015-08-06 10:02:53 -07001960 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001961 }
1962 this->onDrawImageNine(image, center, dst, paint);
1963}
1964
reed41af9662015-01-05 07:49:08 -08001965void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001966 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001967 return;
1968 }
reed41af9662015-01-05 07:49:08 -08001969 this->onDrawBitmap(bitmap, dx, dy, paint);
1970}
1971
reede47829b2015-08-06 10:02:53 -07001972void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07001973 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001974 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07001975 return;
1976 }
reede47829b2015-08-06 10:02:53 -07001977 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08001978}
1979
reed84984ef2015-07-17 07:09:43 -07001980void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
1981 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001982 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001983}
1984
reede47829b2015-08-06 10:02:53 -07001985void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
1986 SrcRectConstraint constraint) {
1987 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
1988 constraint);
1989}
reede47829b2015-08-06 10:02:53 -07001990
reed41af9662015-01-05 07:49:08 -08001991void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
1992 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001993 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001994 return;
1995 }
reed4c21dc52015-06-25 12:32:03 -07001996 if (!SkNinePatchIter::Valid(bitmap.width(), bitmap.height(), center)) {
reeda5517e22015-07-14 10:54:12 -07001997 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001998 }
reed41af9662015-01-05 07:49:08 -08001999 this->onDrawBitmapNine(bitmap, center, dst, paint);
2000}
2001
reed71c3c762015-06-24 10:29:17 -07002002void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2003 const SkColor colors[], int count, SkXfermode::Mode mode,
2004 const SkRect* cull, const SkPaint* paint) {
2005 if (count <= 0) {
2006 return;
2007 }
2008 SkASSERT(atlas);
2009 SkASSERT(xform);
2010 SkASSERT(tex);
2011 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
2012}
2013
reede47829b2015-08-06 10:02:53 -07002014void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2015 const SkPaint* paint, SrcRectConstraint constraint) {
2016 if (src) {
2017 this->drawImageRect(image, *src, dst, paint, constraint);
2018 } else {
2019 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2020 dst, paint, constraint);
2021 }
2022}
2023void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2024 const SkPaint* paint, SrcRectConstraint constraint) {
2025 if (src) {
2026 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2027 } else {
2028 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2029 dst, paint, constraint);
2030 }
2031}
2032
reed@android.com8a1c16f2008-12-17 15:59:43 +00002033//////////////////////////////////////////////////////////////////////////////
2034// These are the virtual drawing methods
2035//////////////////////////////////////////////////////////////////////////////
2036
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002037void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002038 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002039 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2040 }
2041}
2042
reed41af9662015-01-05 07:49:08 -08002043void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002044 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002045 this->internalDrawPaint(paint);
2046}
2047
2048void SkCanvas::internalDrawPaint(const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002049 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002050
2051 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002052 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002053 }
2054
reed@google.com4e2b3d32011-04-07 14:18:59 +00002055 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002056}
2057
reed41af9662015-01-05 07:49:08 -08002058void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2059 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002060 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002061 if ((long)count <= 0) {
2062 return;
2063 }
2064
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002065 SkRect r, storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002066 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002067 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002068 // special-case 2 points (common for drawing a single line)
2069 if (2 == count) {
2070 r.set(pts[0], pts[1]);
2071 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00002072 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002073 }
senorblanco87e066e2015-10-28 11:23:36 -07002074 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2075 return;
2076 }
2077 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002078 }
reed@google.coma584aed2012-05-16 14:06:02 +00002079
halcanary96fcdcc2015-08-27 07:41:13 -07002080 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002081
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002082 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002083
reed@android.com8a1c16f2008-12-17 15:59:43 +00002084 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002085 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002086 }
reed@google.com4b226022011-01-11 18:32:13 +00002087
reed@google.com4e2b3d32011-04-07 14:18:59 +00002088 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002089}
2090
reed41af9662015-01-05 07:49:08 -08002091void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002092 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002093 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002094 const SkRect* bounds = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002095 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08002096 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
2097 // To prevent accidental rejecting at this stage, we have to sort it before we check.
2098 SkRect tmp(r);
2099 tmp.sort();
2100
senorblanco87e066e2015-10-28 11:23:36 -07002101 if (this->quickReject(paint.computeFastBounds(tmp, &storage))) {
2102 return;
2103 }
2104 bounds = &r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002105 }
reed@google.com4b226022011-01-11 18:32:13 +00002106
reedc83a2972015-07-16 07:40:45 -07002107 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002108
2109 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002110 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002111 }
2112
reed@google.com4e2b3d32011-04-07 14:18:59 +00002113 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002114}
2115
reed41af9662015-01-05 07:49:08 -08002116void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002117 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002118 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002119 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002120 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002121 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2122 return;
2123 }
2124 bounds = &oval;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002125 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002126
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002127 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002128
2129 while (iter.next()) {
2130 iter.fDevice->drawOval(iter, oval, looper.paint());
2131 }
2132
2133 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002134}
2135
reed41af9662015-01-05 07:49:08 -08002136void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002137 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002138 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002139 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002140 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002141 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2142 return;
2143 }
2144 bounds = &rrect.getBounds();
reed@google.com4ed0fb72012-12-12 20:48:18 +00002145 }
2146
2147 if (rrect.isRect()) {
2148 // call the non-virtual version
2149 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002150 return;
2151 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002152 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002153 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2154 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002155 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002156
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002157 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002158
2159 while (iter.next()) {
2160 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2161 }
2162
2163 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002164}
2165
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002166void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2167 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002168 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002169 const SkRect* bounds = nullptr;
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002170 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002171 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2172 return;
2173 }
2174 bounds = &outer.getBounds();
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002175 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002176
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002177 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002178
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002179 while (iter.next()) {
2180 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2181 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002182
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002183 LOOPER_END
2184}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002185
reed41af9662015-01-05 07:49:08 -08002186void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002187 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002188 if (!path.isFinite()) {
2189 return;
2190 }
2191
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002192 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002193 const SkRect* bounds = nullptr;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002194 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002195 const SkRect& pathBounds = path.getBounds();
senorblanco87e066e2015-10-28 11:23:36 -07002196 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2197 return;
2198 }
2199 bounds = &pathBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002200 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002201
2202 const SkRect& r = path.getBounds();
2203 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002204 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002205 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002206 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002207 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002208 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002209
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002210 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002211
2212 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002213 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002214 }
2215
reed@google.com4e2b3d32011-04-07 14:18:59 +00002216 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002217}
2218
reed262a71b2015-12-05 13:07:27 -08002219bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002220 if (!paint.getImageFilter()) {
2221 return false;
2222 }
2223
2224 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002225 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002226 return false;
2227 }
2228
2229 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2230 // Once we can filter and the filter will return a result larger than itself, we should be
2231 // able to remove this constraint.
2232 // skbug.com/4526
2233 //
2234 SkPoint pt;
2235 ctm.mapXY(x, y, &pt);
2236 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2237 return ir.contains(fMCRec->fRasterClip.getBounds());
2238}
2239
reeda85d4d02015-05-06 12:56:48 -07002240void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002241 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002242 SkRect bounds = SkRect::MakeXYWH(x, y,
2243 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002244 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002245 SkRect tmp = bounds;
2246 if (paint) {
2247 paint->computeFastBounds(tmp, &tmp);
2248 }
2249 if (this->quickReject(tmp)) {
2250 return;
2251 }
reeda85d4d02015-05-06 12:56:48 -07002252 }
2253
2254 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002255 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002256 paint = lazy.init();
2257 }
reed262a71b2015-12-05 13:07:27 -08002258
2259 const bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2260 *paint);
2261 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
2262
reeda85d4d02015-05-06 12:56:48 -07002263 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002264 const SkPaint& pnt = looper.paint();
2265 if (drawAsSprite && pnt.getImageFilter()) {
2266 SkBitmap bitmap;
2267 if (as_IB(image)->asBitmapForImageFilters(&bitmap)) {
2268 SkPoint pt;
2269 iter.fMatrix->mapXY(x, y, &pt);
2270 iter.fDevice->drawBitmapAsSprite(iter, bitmap,
2271 SkScalarRoundToInt(pt.fX),
2272 SkScalarRoundToInt(pt.fY), pnt);
2273 }
2274 } else {
2275 iter.fDevice->drawImage(iter, image, x, y, pnt);
2276 }
reeda85d4d02015-05-06 12:56:48 -07002277 }
2278
2279 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002280}
2281
reed41af9662015-01-05 07:49:08 -08002282void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002283 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002284 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
halcanary96fcdcc2015-08-27 07:41:13 -07002285 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002286 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002287 if (paint) {
2288 paint->computeFastBounds(dst, &storage);
2289 }
2290 if (this->quickReject(storage)) {
2291 return;
2292 }
reeda85d4d02015-05-06 12:56:48 -07002293 }
2294 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002295 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002296 paint = lazy.init();
2297 }
2298
senorblancoc41e7e12015-12-07 12:51:30 -08002299 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002300 image->isOpaque())
reeda85d4d02015-05-06 12:56:48 -07002301
2302 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002303 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002304 }
2305
2306 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002307}
2308
reed41af9662015-01-05 07:49:08 -08002309void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002310 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002311 SkDEBUGCODE(bitmap.validate();)
2312
reed33366972015-10-08 09:22:02 -07002313 if (bitmap.drawsNothing()) {
2314 return;
2315 }
2316
2317 SkLazyPaint lazy;
2318 if (nullptr == paint) {
2319 paint = lazy.init();
2320 }
2321
2322 const SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2323
2324 SkRect storage;
2325 const SkRect* bounds = nullptr;
2326 if (paint->canComputeFastBounds()) {
2327 bitmap.getBounds(&storage);
2328 matrix.mapRect(&storage);
senorblanco87e066e2015-10-28 11:23:36 -07002329 SkRect tmp = storage;
2330 if (this->quickReject(paint->computeFastBounds(tmp, &tmp))) {
2331 return;
2332 }
2333 bounds = &storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002334 }
reed@google.com4b226022011-01-11 18:32:13 +00002335
reed262a71b2015-12-05 13:07:27 -08002336 const bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(),
2337 bitmap.height(), *paint);
2338 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds)
reed33366972015-10-08 09:22:02 -07002339
2340 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002341 const SkPaint& pnt = looper.paint();
2342 if (drawAsSprite && pnt.getImageFilter()) {
2343 SkPoint pt;
2344 iter.fMatrix->mapXY(x, y, &pt);
2345 iter.fDevice->drawBitmapAsSprite(iter, bitmap,
2346 SkScalarRoundToInt(pt.fX),
2347 SkScalarRoundToInt(pt.fY), pnt);
2348 } else {
2349 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
2350 }
reed33366972015-10-08 09:22:02 -07002351 }
reed262a71b2015-12-05 13:07:27 -08002352
reed33366972015-10-08 09:22:02 -07002353 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002354}
2355
reed@google.com9987ec32011-09-07 11:57:52 +00002356// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002357void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002358 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002359 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002360 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002361 return;
2362 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002363
halcanary96fcdcc2015-08-27 07:41:13 -07002364 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002365 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002366 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2367 return;
2368 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002369 }
reed@google.com3d608122011-11-21 15:16:16 +00002370
reed@google.com33535f32012-09-25 15:37:50 +00002371 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002372 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002373 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002374 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002375
senorblancoc41e7e12015-12-07 12:51:30 -08002376 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002377 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002378
reed@google.com33535f32012-09-25 15:37:50 +00002379 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002380 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002381 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002382
reed@google.com33535f32012-09-25 15:37:50 +00002383 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002384}
2385
reed41af9662015-01-05 07:49:08 -08002386void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002387 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002388 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002389 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002390 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002391}
2392
reed4c21dc52015-06-25 12:32:03 -07002393void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2394 const SkPaint* paint) {
2395 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
2396
halcanary96fcdcc2015-08-27 07:41:13 -07002397 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002398 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002399 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2400 return;
2401 }
reed@google.com3d608122011-11-21 15:16:16 +00002402 }
reed4c21dc52015-06-25 12:32:03 -07002403
2404 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002405 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002406 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002407 }
reed4c21dc52015-06-25 12:32:03 -07002408
senorblancoc41e7e12015-12-07 12:51:30 -08002409 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
reed4c21dc52015-06-25 12:32:03 -07002410
2411 while (iter.next()) {
2412 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002413 }
reed4c21dc52015-06-25 12:32:03 -07002414
2415 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002416}
2417
reed41af9662015-01-05 07:49:08 -08002418void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2419 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002420 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002421 SkDEBUGCODE(bitmap.validate();)
2422
halcanary96fcdcc2015-08-27 07:41:13 -07002423 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002424 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002425 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2426 return;
2427 }
reed4c21dc52015-06-25 12:32:03 -07002428 }
2429
2430 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002431 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002432 paint = lazy.init();
2433 }
2434
senorblancoc41e7e12015-12-07 12:51:30 -08002435 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
reed4c21dc52015-06-25 12:32:03 -07002436
2437 while (iter.next()) {
2438 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2439 }
2440
2441 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002442}
2443
reed@google.comf67e4cf2011-03-15 20:56:58 +00002444class SkDeviceFilteredPaint {
2445public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002446 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002447 uint32_t filteredFlags = device->filterTextFlags(paint);
2448 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002449 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002450 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002451 fPaint = newPaint;
2452 } else {
2453 fPaint = &paint;
2454 }
2455 }
2456
reed@google.comf67e4cf2011-03-15 20:56:58 +00002457 const SkPaint& paint() const { return *fPaint; }
2458
2459private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002460 const SkPaint* fPaint;
2461 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002462};
2463
bungeman@google.com52c748b2011-08-22 21:30:43 +00002464void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2465 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002466 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002467 draw.fDevice->drawRect(draw, r, paint);
2468 } else {
2469 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002470 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002471 draw.fDevice->drawRect(draw, r, p);
2472 }
2473}
2474
2475void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2476 const char text[], size_t byteLength,
2477 SkScalar x, SkScalar y) {
halcanary96fcdcc2015-08-27 07:41:13 -07002478 SkASSERT(byteLength == 0 || text != nullptr);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002479
2480 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07002481 if (text == nullptr || byteLength == 0 ||
bungeman@google.com52c748b2011-08-22 21:30:43 +00002482 draw.fClip->isEmpty() ||
halcanary96fcdcc2015-08-27 07:41:13 -07002483 (paint.getAlpha() == 0 && paint.getXfermode() == nullptr)) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002484 return;
2485 }
2486
2487 SkScalar width = 0;
2488 SkPoint start;
2489
2490 start.set(0, 0); // to avoid warning
2491 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2492 SkPaint::kStrikeThruText_Flag)) {
2493 width = paint.measureText(text, byteLength);
2494
2495 SkScalar offsetX = 0;
2496 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2497 offsetX = SkScalarHalf(width);
2498 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2499 offsetX = width;
2500 }
2501 start.set(x - offsetX, y);
2502 }
2503
2504 if (0 == width) {
2505 return;
2506 }
2507
2508 uint32_t flags = paint.getFlags();
2509
2510 if (flags & (SkPaint::kUnderlineText_Flag |
2511 SkPaint::kStrikeThruText_Flag)) {
2512 SkScalar textSize = paint.getTextSize();
2513 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2514 SkRect r;
2515
2516 r.fLeft = start.fX;
2517 r.fRight = start.fX + width;
2518
2519 if (flags & SkPaint::kUnderlineText_Flag) {
2520 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2521 start.fY);
2522 r.fTop = offset;
2523 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002524 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002525 }
2526 if (flags & SkPaint::kStrikeThruText_Flag) {
2527 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2528 start.fY);
2529 r.fTop = offset;
2530 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002531 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002532 }
2533 }
2534}
2535
reed@google.come0d9ce82014-04-23 04:00:17 +00002536void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2537 const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002538 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002539
2540 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002541 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002542 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002543 DrawTextDecorations(iter, dfp.paint(),
2544 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002545 }
2546
reed@google.com4e2b3d32011-04-07 14:18:59 +00002547 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002548}
2549
reed@google.come0d9ce82014-04-23 04:00:17 +00002550void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2551 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002552 SkPoint textOffset = SkPoint::Make(0, 0);
2553
halcanary96fcdcc2015-08-27 07:41:13 -07002554 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002555
reed@android.com8a1c16f2008-12-17 15:59:43 +00002556 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002557 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002558 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002559 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002560 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002561
reed@google.com4e2b3d32011-04-07 14:18:59 +00002562 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002563}
2564
reed@google.come0d9ce82014-04-23 04:00:17 +00002565void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2566 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002567
2568 SkPoint textOffset = SkPoint::Make(0, constY);
2569
halcanary96fcdcc2015-08-27 07:41:13 -07002570 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002571
reed@android.com8a1c16f2008-12-17 15:59:43 +00002572 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002573 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002574 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002575 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002576 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002577
reed@google.com4e2b3d32011-04-07 14:18:59 +00002578 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002579}
2580
reed@google.come0d9ce82014-04-23 04:00:17 +00002581void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2582 const SkMatrix* matrix, const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002583 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002584
reed@android.com8a1c16f2008-12-17 15:59:43 +00002585 while (iter.next()) {
2586 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002587 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002588 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002589
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002590 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002591}
2592
fmalita00d5c2c2014-08-21 08:53:26 -07002593void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2594 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002595
fmalita85d5eb92015-03-04 11:20:12 -08002596 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002597 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002598 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002599 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002600 SkRect tmp;
2601 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2602 return;
2603 }
2604 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002605 }
2606
fmalita024f9962015-03-03 19:08:17 -08002607 // We cannot filter in the looper as we normally do, because the paint is
2608 // incomplete at this point (text-related attributes are embedded within blob run paints).
2609 SkDrawFilter* drawFilter = fMCRec->fFilter;
halcanary96fcdcc2015-08-27 07:41:13 -07002610 fMCRec->fFilter = nullptr;
fmalita024f9962015-03-03 19:08:17 -08002611
fmalita85d5eb92015-03-04 11:20:12 -08002612 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002613
fmalitaaa1b9122014-08-28 14:32:24 -07002614 while (iter.next()) {
2615 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002616 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002617 }
2618
fmalitaaa1b9122014-08-28 14:32:24 -07002619 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002620
2621 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002622}
2623
reed@google.come0d9ce82014-04-23 04:00:17 +00002624// These will become non-virtual, so they always call the (virtual) onDraw... method
2625void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2626 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002627 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002628 this->onDrawText(text, byteLength, x, y, paint);
2629}
2630void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2631 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002632 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002633 this->onDrawPosText(text, byteLength, pos, paint);
2634}
2635void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2636 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002637 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002638 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2639}
2640void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2641 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002642 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002643 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2644}
fmalita00d5c2c2014-08-21 08:53:26 -07002645void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2646 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002647 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
bsalomon49f085d2014-09-05 13:34:00 -07002648 if (blob) {
fmalita00d5c2c2014-08-21 08:53:26 -07002649 this->onDrawTextBlob(blob, x, y, paint);
2650 }
2651}
reed@google.come0d9ce82014-04-23 04:00:17 +00002652
reed41af9662015-01-05 07:49:08 -08002653void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2654 const SkPoint verts[], const SkPoint texs[],
2655 const SkColor colors[], SkXfermode* xmode,
2656 const uint16_t indices[], int indexCount,
2657 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002658 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
halcanary96fcdcc2015-08-27 07:41:13 -07002659 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
reed@google.com4b226022011-01-11 18:32:13 +00002660
reed@android.com8a1c16f2008-12-17 15:59:43 +00002661 while (iter.next()) {
2662 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002663 colors, xmode, indices, indexCount,
2664 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002665 }
reed@google.com4b226022011-01-11 18:32:13 +00002666
reed@google.com4e2b3d32011-04-07 14:18:59 +00002667 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002668}
2669
dandovb3c9d1c2014-08-12 08:34:29 -07002670void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2671 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002672 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
halcanary96fcdcc2015-08-27 07:41:13 -07002673 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002674 return;
2675 }
mtklein6cfa73a2014-08-13 13:33:49 -07002676
dandovecfff212014-08-04 10:02:00 -07002677 // Since a patch is always within the convex hull of the control points, we discard it when its
2678 // bounding rectangle is completely outside the current clip.
2679 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002680 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002681 if (this->quickReject(bounds)) {
2682 return;
2683 }
mtklein6cfa73a2014-08-13 13:33:49 -07002684
dandovb3c9d1c2014-08-12 08:34:29 -07002685 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2686}
2687
2688void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2689 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2690
halcanary96fcdcc2015-08-27 07:41:13 -07002691 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002692
dandovecfff212014-08-04 10:02:00 -07002693 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002694 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002695 }
mtklein6cfa73a2014-08-13 13:33:49 -07002696
dandovecfff212014-08-04 10:02:00 -07002697 LOOPER_END
2698}
2699
reeda8db7282015-07-07 10:22:31 -07002700void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
2701 if (dr) {
2702 if (x || y) {
2703 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2704 this->onDrawDrawable(dr, &matrix);
2705 } else {
halcanary96fcdcc2015-08-27 07:41:13 -07002706 this->onDrawDrawable(dr, nullptr);
reeda8db7282015-07-07 10:22:31 -07002707 }
reed6a070dc2014-11-11 19:36:09 -08002708 }
2709}
2710
reeda8db7282015-07-07 10:22:31 -07002711void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2712 if (dr) {
2713 if (matrix && matrix->isIdentity()) {
halcanary96fcdcc2015-08-27 07:41:13 -07002714 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002715 }
2716 this->onDrawDrawable(dr, matrix);
2717 }
2718}
2719
2720void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2721 SkRect bounds = dr->getBounds();
2722 if (matrix) {
2723 matrix->mapRect(&bounds);
2724 }
2725 if (this->quickReject(bounds)) {
2726 return;
2727 }
2728 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002729}
2730
reed71c3c762015-06-24 10:29:17 -07002731void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2732 const SkColor colors[], int count, SkXfermode::Mode mode,
2733 const SkRect* cull, const SkPaint* paint) {
2734 if (cull && this->quickReject(*cull)) {
2735 return;
2736 }
2737
2738 SkPaint pnt;
2739 if (paint) {
2740 pnt = *paint;
2741 }
2742
halcanary96fcdcc2015-08-27 07:41:13 -07002743 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr)
reed71c3c762015-06-24 10:29:17 -07002744 while (iter.next()) {
2745 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, mode, pnt);
2746 }
2747 LOOPER_END
2748}
2749
reed@android.com8a1c16f2008-12-17 15:59:43 +00002750//////////////////////////////////////////////////////////////////////////////
2751// These methods are NOT virtual, and therefore must call back into virtual
2752// methods, rather than actually drawing themselves.
2753//////////////////////////////////////////////////////////////////////////////
2754
2755void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002756 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002757 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002758 SkPaint paint;
2759
2760 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002761 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002762 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002763 }
2764 this->drawPaint(paint);
2765}
2766
reed@android.com845fdac2009-06-23 03:01:32 +00002767void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002768 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002769 SkPaint paint;
2770
2771 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002772 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002773 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002774 }
2775 this->drawPaint(paint);
2776}
2777
2778void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002779 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002780 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002781
reed@android.com8a1c16f2008-12-17 15:59:43 +00002782 pt.set(x, y);
2783 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2784}
2785
2786void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002787 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002788 SkPoint pt;
2789 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002790
reed@android.com8a1c16f2008-12-17 15:59:43 +00002791 pt.set(x, y);
2792 paint.setColor(color);
2793 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2794}
2795
2796void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2797 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002798 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002799 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002800
reed@android.com8a1c16f2008-12-17 15:59:43 +00002801 pts[0].set(x0, y0);
2802 pts[1].set(x1, y1);
2803 this->drawPoints(kLines_PointMode, 2, pts, paint);
2804}
2805
2806void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2807 SkScalar right, SkScalar bottom,
2808 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002809 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002810 SkRect r;
2811
2812 r.set(left, top, right, bottom);
2813 this->drawRect(r, paint);
2814}
2815
2816void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2817 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002818 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002819 if (radius < 0) {
2820 radius = 0;
2821 }
2822
2823 SkRect r;
2824 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002825 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002826}
2827
2828void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2829 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002830 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002831 if (rx > 0 && ry > 0) {
2832 if (paint.canComputeFastBounds()) {
2833 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002834 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002835 return;
2836 }
2837 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002838 SkRRect rrect;
2839 rrect.setRectXY(r, rx, ry);
2840 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002841 } else {
2842 this->drawRect(r, paint);
2843 }
2844}
2845
reed@android.com8a1c16f2008-12-17 15:59:43 +00002846void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2847 SkScalar sweepAngle, bool useCenter,
2848 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002849 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002850 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2851 this->drawOval(oval, paint);
2852 } else {
2853 SkPath path;
2854 if (useCenter) {
2855 path.moveTo(oval.centerX(), oval.centerY());
2856 }
2857 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2858 if (useCenter) {
2859 path.close();
2860 }
2861 this->drawPath(path, paint);
2862 }
2863}
2864
2865void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2866 const SkPath& path, SkScalar hOffset,
2867 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002868 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002869 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002870
reed@android.com8a1c16f2008-12-17 15:59:43 +00002871 matrix.setTranslate(hOffset, vOffset);
2872 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2873}
2874
reed@android.comf76bacf2009-05-13 14:00:33 +00002875///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07002876
2877/**
2878 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2879 * against the playback cost of recursing into the subpicture to get at its actual ops.
2880 *
2881 * For now we pick a conservatively small value, though measurement (and other heuristics like
2882 * the type of ops contained) may justify changing this value.
2883 */
2884#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07002885
reedd5fa1a42014-08-09 11:08:05 -07002886void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reed1c2c4412015-04-30 13:09:24 -07002887 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
bsalomon49f085d2014-09-05 13:34:00 -07002888 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002889 if (matrix && matrix->isIdentity()) {
halcanary96fcdcc2015-08-27 07:41:13 -07002890 matrix = nullptr;
reedd5fa1a42014-08-09 11:08:05 -07002891 }
reed1c2c4412015-04-30 13:09:24 -07002892 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2893 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2894 picture->playback(this);
2895 } else {
2896 this->onDrawPicture(picture, matrix, paint);
2897 }
reedd5fa1a42014-08-09 11:08:05 -07002898 }
2899}
robertphillips9b14f262014-06-04 05:40:44 -07002900
reedd5fa1a42014-08-09 11:08:05 -07002901void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2902 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07002903 if (!paint || paint->canComputeFastBounds()) {
2904 SkRect bounds = picture->cullRect();
2905 if (paint) {
2906 paint->computeFastBounds(bounds, &bounds);
2907 }
2908 if (matrix) {
2909 matrix->mapRect(&bounds);
2910 }
2911 if (this->quickReject(bounds)) {
2912 return;
2913 }
2914 }
2915
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002916 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002917 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002918 // Canvas has to first give the device the opportunity to render
2919 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002920 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002921 return; // the device has rendered the entire picture
2922 }
2923 }
2924
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002925 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07002926 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002927}
2928
reed@android.com8a1c16f2008-12-17 15:59:43 +00002929///////////////////////////////////////////////////////////////////////////////
2930///////////////////////////////////////////////////////////////////////////////
2931
2932SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
bungeman99fe8222015-08-20 07:57:51 -07002933 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002934
2935 SkASSERT(canvas);
2936
2937 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2938 fDone = !fImpl->next();
2939}
2940
2941SkCanvas::LayerIter::~LayerIter() {
2942 fImpl->~SkDrawIter();
2943}
2944
2945void SkCanvas::LayerIter::next() {
2946 fDone = !fImpl->next();
2947}
2948
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002949SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002950 return fImpl->getDevice();
2951}
2952
2953const SkMatrix& SkCanvas::LayerIter::matrix() const {
2954 return fImpl->getMatrix();
2955}
2956
2957const SkPaint& SkCanvas::LayerIter::paint() const {
2958 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07002959 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002960 paint = &fDefaultPaint;
2961 }
2962 return *paint;
2963}
2964
2965const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2966int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2967int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002968
2969///////////////////////////////////////////////////////////////////////////////
2970
fmalitac3b589a2014-06-05 12:40:07 -07002971SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002972
2973///////////////////////////////////////////////////////////////////////////////
2974
2975static bool supported_for_raster_canvas(const SkImageInfo& info) {
2976 switch (info.alphaType()) {
2977 case kPremul_SkAlphaType:
2978 case kOpaque_SkAlphaType:
2979 break;
2980 default:
2981 return false;
2982 }
2983
2984 switch (info.colorType()) {
2985 case kAlpha_8_SkColorType:
2986 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002987 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002988 break;
2989 default:
2990 return false;
2991 }
2992
2993 return true;
2994}
2995
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002996SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2997 if (!supported_for_raster_canvas(info)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002998 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002999 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003000
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003001 SkBitmap bitmap;
3002 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003003 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003004 }
halcanary385fe4d2015-08-26 13:07:48 -07003005 return new SkCanvas(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003006}
reedd5fa1a42014-08-09 11:08:05 -07003007
3008///////////////////////////////////////////////////////////////////////////////
3009
3010SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003011 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07003012 : fCanvas(canvas)
3013 , fSaveCount(canvas->getSaveCount())
3014{
bsalomon49f085d2014-09-05 13:34:00 -07003015 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003016 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07003017 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003018 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07003019 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003020 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07003021 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003022 canvas->save();
3023 }
mtklein6cfa73a2014-08-13 13:33:49 -07003024
bsalomon49f085d2014-09-05 13:34:00 -07003025 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003026 canvas->concat(*matrix);
3027 }
3028}
3029
3030SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
3031 fCanvas->restoreToCount(fSaveCount);
3032}