blob: f7f870210d0844b6347030575ec159d210f15ea9 [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"
robertphillips4418dba2016-03-07 12:45:14 -080031#include "SkSpecialImage.h"
reed@google.com97af1a62012-08-28 12:19:02 +000032#include "SkSurface_Base.h"
fmalita7ba7aa72014-08-29 09:46:36 -070033#include "SkTextBlob.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000034#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000035#include "SkTLazy.h"
danakj8f757f52014-11-04 11:48:43 -080036#include "SkTraceEvent.h"
bungemand3ebb482015-08-05 13:57:49 -070037
38#include <new>
reed@android.com8a1c16f2008-12-17 15:59:43 +000039
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000040#if SK_SUPPORT_GPU
robertphillips7354a4b2015-12-16 05:08:27 -080041#include "GrContext.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000042#include "GrRenderTarget.h"
robertphillips7354a4b2015-12-16 05:08:27 -080043#include "SkGr.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000044#endif
45
reede3b38ce2016-01-08 09:18:44 -080046#define RETURN_ON_NULL(ptr) do { if (nullptr == (ptr)) return; } while (0)
47
reedc83a2972015-07-16 07:40:45 -070048/*
49 * Return true if the drawing this rect would hit every pixels in the canvas.
50 *
51 * Returns false if
52 * - rect does not contain the canvas' bounds
53 * - paint is not fill
54 * - paint would blur or otherwise change the coverage of the rect
55 */
56bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
57 ShaderOverrideOpacity overrideOpacity) const {
bungeman99fe8222015-08-20 07:57:51 -070058 static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
59 (int)kNone_ShaderOverrideOpacity,
60 "need_matching_enums0");
61 static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
62 (int)kOpaque_ShaderOverrideOpacity,
63 "need_matching_enums1");
64 static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
65 (int)kNotOpaque_ShaderOverrideOpacity,
66 "need_matching_enums2");
reedc83a2972015-07-16 07:40:45 -070067
68 const SkISize size = this->getBaseLayerSize();
69 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
70 if (!this->getClipStack()->quickContains(bounds)) {
71 return false;
72 }
73
74 if (rect) {
75 if (!this->getTotalMatrix().rectStaysRect()) {
76 return false; // conservative
77 }
78
79 SkRect devRect;
80 this->getTotalMatrix().mapRect(&devRect, *rect);
fmalita8c0144c2015-07-22 05:56:16 -070081 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -070082 return false;
83 }
84 }
85
86 if (paint) {
87 SkPaint::Style paintStyle = paint->getStyle();
88 if (!(paintStyle == SkPaint::kFill_Style ||
89 paintStyle == SkPaint::kStrokeAndFill_Style)) {
90 return false;
91 }
92 if (paint->getMaskFilter() || paint->getLooper()
93 || paint->getPathEffect() || paint->getImageFilter()) {
94 return false; // conservative
95 }
96 }
97 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
98}
99
100///////////////////////////////////////////////////////////////////////////////////////////////////
101
reedd990e2f2014-12-22 11:58:30 -0800102static bool gIgnoreSaveLayerBounds;
103void SkCanvas::Internal_Private_SetIgnoreSaveLayerBounds(bool ignore) {
104 gIgnoreSaveLayerBounds = ignore;
105}
106bool SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds() {
107 return gIgnoreSaveLayerBounds;
108}
109
reed0acf1b42014-12-22 16:12:38 -0800110static bool gTreatSpriteAsBitmap;
111void SkCanvas::Internal_Private_SetTreatSpriteAsBitmap(bool spriteAsBitmap) {
112 gTreatSpriteAsBitmap = spriteAsBitmap;
113}
114bool SkCanvas::Internal_Private_GetTreatSpriteAsBitmap() {
115 return gTreatSpriteAsBitmap;
116}
117
reed@google.comda17f752012-08-16 18:27:05 +0000118// experimental for faster tiled drawing...
119//#define SK_ENABLE_CLIP_QUICKREJECT
reed@android.com8a1c16f2008-12-17 15:59:43 +0000120//#define SK_TRACE_SAVERESTORE
121
122#ifdef SK_TRACE_SAVERESTORE
123 static int gLayerCounter;
124 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
125 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
126
127 static int gRecCounter;
128 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
129 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
130
131 static int gCanvasCounter;
132 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
133 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
134#else
135 #define inc_layer()
136 #define dec_layer()
137 #define inc_rec()
138 #define dec_rec()
139 #define inc_canvas()
140 #define dec_canvas()
141#endif
142
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000143typedef SkTLazy<SkPaint> SkLazyPaint;
144
reedc83a2972015-07-16 07:40:45 -0700145void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000146 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700147 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
148 ? SkSurface::kDiscard_ContentChangeMode
149 : SkSurface::kRetain_ContentChangeMode);
150 }
151}
152
153void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
154 ShaderOverrideOpacity overrideOpacity) {
155 if (fSurfaceBase) {
156 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
157 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
158 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
159 // and therefore we don't care which mode we're in.
160 //
161 if (fSurfaceBase->outstandingImageSnapshot()) {
162 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
163 mode = SkSurface::kDiscard_ContentChangeMode;
164 }
165 }
166 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000167 }
168}
169
reed@android.com8a1c16f2008-12-17 15:59:43 +0000170///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000171
reed4a8126e2014-09-22 07:29:03 -0700172static uint32_t filter_paint_flags(const SkSurfaceProps& props, uint32_t flags) {
173 const uint32_t propFlags = props.flags();
174 if (propFlags & SkSurfaceProps::kDisallowDither_Flag) {
175 flags &= ~SkPaint::kDither_Flag;
176 }
177 if (propFlags & SkSurfaceProps::kDisallowAntiAlias_Flag) {
178 flags &= ~SkPaint::kAntiAlias_Flag;
179 }
180 return flags;
181}
182
183///////////////////////////////////////////////////////////////////////////////
184
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000185/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000186 The clip/matrix/proc are fields that reflect the top of the save/restore
187 stack. Whenever the canvas changes, it marks a dirty flag, and then before
188 these are used (assuming we're not on a layer) we rebuild these cache
189 values: they reflect the top of the save stack, but translated and clipped
190 by the device's XY offset and bitmap-bounds.
191*/
192struct DeviceCM {
193 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000194 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000195 SkRasterClip fClip;
reed@google.com6f8f2922011-03-04 22:27:10 +0000196 SkPaint* fPaint; // may be null (in the future)
reed61f501f2015-04-29 08:34:00 -0700197 const SkMatrix* fMatrix;
198 SkMatrix fMatrixStorage;
199 const bool fDeviceIsBitmapDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000200
reed96e657d2015-03-10 17:30:07 -0700201 DeviceCM(SkBaseDevice* device, const SkPaint* paint, SkCanvas* canvas,
reed86a17e72015-05-14 12:25:22 -0700202 bool conservativeRasterClip, bool deviceIsBitmapDevice)
halcanary96fcdcc2015-08-27 07:41:13 -0700203 : fNext(nullptr)
reedd9544982014-09-09 18:46:22 -0700204 , fClip(conservativeRasterClip)
reed61f501f2015-04-29 08:34:00 -0700205 , fDeviceIsBitmapDevice(deviceIsBitmapDevice)
reedd9544982014-09-09 18:46:22 -0700206 {
halcanary96fcdcc2015-08-27 07:41:13 -0700207 if (nullptr != device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000208 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000209 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000210 }
reed@google.com4b226022011-01-11 18:32:13 +0000211 fDevice = device;
halcanary96fcdcc2015-08-27 07:41:13 -0700212 fPaint = paint ? new SkPaint(*paint) : nullptr;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000213 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000214
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000215 ~DeviceCM() {
bsalomon49f085d2014-09-05 13:34:00 -0700216 if (fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000217 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000218 fDevice->unref();
219 }
halcanary385fe4d2015-08-26 13:07:48 -0700220 delete fPaint;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000221 }
reed@google.com4b226022011-01-11 18:32:13 +0000222
mtkleinfeaadee2015-04-08 11:25:48 -0700223 void reset(const SkIRect& bounds) {
224 SkASSERT(!fPaint);
225 SkASSERT(!fNext);
226 SkASSERT(fDevice);
227 fClip.setRect(bounds);
228 }
229
reed@google.com045e62d2011-10-24 12:19:46 +0000230 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
231 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000232 int x = fDevice->getOrigin().x();
233 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000234 int width = fDevice->width();
235 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000236
reed@android.com8a1c16f2008-12-17 15:59:43 +0000237 if ((x | y) == 0) {
238 fMatrix = &totalMatrix;
239 fClip = totalClip;
240 } else {
241 fMatrixStorage = totalMatrix;
242 fMatrixStorage.postTranslate(SkIntToScalar(-x),
243 SkIntToScalar(-y));
244 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000245
reed@android.com8a1c16f2008-12-17 15:59:43 +0000246 totalClip.translate(-x, -y, &fClip);
247 }
248
reed@google.com045e62d2011-10-24 12:19:46 +0000249 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250
251 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000252
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000254 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255 SkRegion::kDifference_Op);
256 }
reed@google.com4b226022011-01-11 18:32:13 +0000257
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000258 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
259
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260#ifdef SK_DEBUG
261 if (!fClip.isEmpty()) {
262 SkIRect deviceR;
263 deviceR.set(0, 0, width, height);
264 SkASSERT(deviceR.contains(fClip.getBounds()));
265 }
266#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000267 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000268};
269
270/* This is the record we keep for each save/restore level in the stack.
271 Since a level optionally copies the matrix and/or stack, we have pointers
272 for these fields. If the value is copied for this level, the copy is
273 stored in the ...Storage field, and the pointer points to that. If the
274 value is not copied for this level, we ignore ...Storage, and just point
275 at the corresponding value in the previous level in the stack.
276*/
277class SkCanvas::MCRec {
278public:
reed1f836ee2014-07-07 07:49:34 -0700279 SkDrawFilter* fFilter; // the current filter (or null)
reedd9544982014-09-09 18:46:22 -0700280 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281 /* If there are any layers in the stack, this points to the top-most
282 one that is at or below this level in the stack (so we know what
283 bitmap/device to draw into from this level. This value is NOT
284 reference counted, since the real owner is either our fLayer field,
285 or a previous one in a lower level.)
286 */
reed2ff1fce2014-12-11 07:07:37 -0800287 DeviceCM* fTopLayer;
288 SkRasterClip fRasterClip;
289 SkMatrix fMatrix;
290 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000291
reedd9544982014-09-09 18:46:22 -0700292 MCRec(bool conservativeRasterClip) : fRasterClip(conservativeRasterClip) {
halcanary96fcdcc2015-08-27 07:41:13 -0700293 fFilter = nullptr;
294 fLayer = nullptr;
295 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800296 fMatrix.reset();
297 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700298
reedd9544982014-09-09 18:46:22 -0700299 // don't bother initializing fNext
300 inc_rec();
301 }
reed2ff1fce2014-12-11 07:07:37 -0800302 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
reedd9544982014-09-09 18:46:22 -0700303 fFilter = SkSafeRef(prev.fFilter);
halcanary96fcdcc2015-08-27 07:41:13 -0700304 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700305 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800306 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700307
reed@android.com8a1c16f2008-12-17 15:59:43 +0000308 // don't bother initializing fNext
309 inc_rec();
310 }
311 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000312 SkSafeUnref(fFilter);
halcanary385fe4d2015-08-26 13:07:48 -0700313 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000314 dec_rec();
315 }
mtkleinfeaadee2015-04-08 11:25:48 -0700316
317 void reset(const SkIRect& bounds) {
318 SkASSERT(fLayer);
319 SkASSERT(fDeferredSaveCount == 0);
320
321 fMatrix.reset();
322 fRasterClip.setRect(bounds);
323 fLayer->reset(bounds);
324 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000325};
326
327class SkDrawIter : public SkDraw {
328public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000329 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000330 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000331 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000332 canvas->updateDeviceCMCache();
333
reed687fa1c2015-04-07 08:00:56 -0700334 fClipStack = canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000335 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000336 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000337 }
reed@google.com4b226022011-01-11 18:32:13 +0000338
reed@android.com8a1c16f2008-12-17 15:59:43 +0000339 bool next() {
340 // skip over recs with empty clips
341 if (fSkipEmptyClips) {
342 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
343 fCurrLayer = fCurrLayer->fNext;
344 }
345 }
346
reed@google.comf68c5e22012-02-24 16:38:58 +0000347 const DeviceCM* rec = fCurrLayer;
348 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000349
350 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000351 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
352 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000353 fDevice = rec->fDevice;
reed41e010c2015-06-09 12:16:53 -0700354 if (!fDevice->accessPixels(&fDst)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700355 fDst.reset(fDevice->imageInfo(), nullptr, 0);
reed41e010c2015-06-09 12:16:53 -0700356 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000357 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000358 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000359
360 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700361 // fCurrLayer may be nullptr now
reed@android.com199f1082009-06-10 02:12:47 +0000362
reed@android.com8a1c16f2008-12-17 15:59:43 +0000363 return true;
364 }
365 return false;
366 }
reed@google.com4b226022011-01-11 18:32:13 +0000367
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000368 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000369 int getX() const { return fDevice->getOrigin().x(); }
370 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000371 const SkMatrix& getMatrix() const { return *fMatrix; }
372 const SkRegion& getClip() const { return *fClip; }
373 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000374
reed@android.com8a1c16f2008-12-17 15:59:43 +0000375private:
376 SkCanvas* fCanvas;
377 const DeviceCM* fCurrLayer;
378 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000379 SkBool8 fSkipEmptyClips;
380
381 typedef SkDraw INHERITED;
382};
383
384/////////////////////////////////////////////////////////////////////////////
385
reeddbc3cef2015-04-29 12:18:57 -0700386static SkPaint* set_if_needed(SkLazyPaint* lazy, const SkPaint& orig) {
387 return lazy->isValid() ? lazy->get() : lazy->set(orig);
388}
389
390/**
391 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700392 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700393 */
reedd053ce92016-03-22 10:17:23 -0700394static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700395 SkImageFilter* imgf = paint.getImageFilter();
396 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700397 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700398 }
399
reedd053ce92016-03-22 10:17:23 -0700400 SkColorFilter* imgCFPtr;
401 if (!imgf->asAColorFilter(&imgCFPtr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700402 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700403 }
reedd053ce92016-03-22 10:17:23 -0700404 sk_sp<SkColorFilter> imgCF(imgCFPtr);
reeddbc3cef2015-04-29 12:18:57 -0700405
406 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700407 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700408 // there is no existing paint colorfilter, so we can just return the imagefilter's
409 return imgCF;
410 }
411
412 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
413 // and we need to combine them into a single colorfilter.
reedd053ce92016-03-22 10:17:23 -0700414 return SkColorFilter::MakeComposeFilter(std::move(imgCF), sk_ref_sp(paintCF));
reeddbc3cef2015-04-29 12:18:57 -0700415}
416
senorblanco87e066e2015-10-28 11:23:36 -0700417/**
418 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
419 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
420 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
421 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
422 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
423 * conservative "effective" bounds based on the settings in the paint... with one exception. This
424 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
425 * deliberately ignored.
426 */
427static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
428 const SkRect& rawBounds,
429 SkRect* storage) {
430 SkPaint tmpUnfiltered(paint);
431 tmpUnfiltered.setImageFilter(nullptr);
432 if (tmpUnfiltered.canComputeFastBounds()) {
433 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
434 } else {
435 return rawBounds;
436 }
437}
438
reed@android.com8a1c16f2008-12-17 15:59:43 +0000439class AutoDrawLooper {
440public:
senorblanco87e066e2015-10-28 11:23:36 -0700441 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
442 // paint. It's used to determine the size of the offscreen layer for filters.
443 // If null, the clip will be used instead.
reed4a8126e2014-09-22 07:29:03 -0700444 AutoDrawLooper(SkCanvas* canvas, const SkSurfaceProps& props, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000445 bool skipLayerForImageFilter = false,
senorblanco87e066e2015-10-28 11:23:36 -0700446 const SkRect* rawBounds = nullptr) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000447 fCanvas = canvas;
fmalita53d9f1c2016-01-25 06:23:54 -0800448#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000449 fFilter = canvas->getDrawFilter();
fmalita77650002016-01-21 18:47:11 -0800450#else
451 fFilter = nullptr;
452#endif
reed4a8126e2014-09-22 07:29:03 -0700453 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000454 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700455 fTempLayerForImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000456 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000457
reedd053ce92016-03-22 10:17:23 -0700458 auto simplifiedCF = image_to_color_filter(fOrigPaint);
reeddbc3cef2015-04-29 12:18:57 -0700459 if (simplifiedCF) {
460 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reedd053ce92016-03-22 10:17:23 -0700461 paint->setColorFilter(std::move(simplifiedCF));
halcanary96fcdcc2015-08-27 07:41:13 -0700462 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700463 fPaint = paint;
464 }
465
466 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700467 /**
468 * We implement ImageFilters for a given draw by creating a layer, then applying the
469 * imagefilter to the pixels of that layer (its backing surface/image), and then
470 * we call restore() to xfer that layer to the main canvas.
471 *
472 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
473 * 2. Generate the src pixels:
474 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
475 * return (fPaint). We then draw the primitive (using srcover) into a cleared
476 * buffer/surface.
477 * 3. Restore the layer created in #1
478 * The imagefilter is passed the buffer/surface from the layer (now filled with the
479 * src pixels of the primitive). It returns a new "filtered" buffer, which we
480 * draw onto the previous layer using the xfermode from the original paint.
481 */
reed@google.com8926b162012-03-23 15:36:36 +0000482 SkPaint tmp;
reeddbc3cef2015-04-29 12:18:57 -0700483 tmp.setImageFilter(fPaint->getImageFilter());
484 tmp.setXfermode(fPaint->getXfermode());
senorblanco87e066e2015-10-28 11:23:36 -0700485 SkRect storage;
486 if (rawBounds) {
487 // Make rawBounds include all paint outsets except for those due to image filters.
488 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
489 }
reedbfd5f172016-01-07 11:28:08 -0800490 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &tmp),
reed76033be2015-03-14 10:54:31 -0700491 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700492 fTempLayerForImageFilter = true;
493 // we remove the imagefilter/xfermode inside doNext()
reed@google.com8926b162012-03-23 15:36:36 +0000494 }
495
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000496 if (SkDrawLooper* looper = paint.getLooper()) {
497 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
498 looper->contextSize());
499 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000500 fIsSimple = false;
501 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700502 fLooperContext = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000503 // can we be marked as simple?
reed5c476fb2015-04-20 08:04:21 -0700504 fIsSimple = !fFilter && !fTempLayerForImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000505 }
piotaixrb5fae932014-09-24 13:03:30 -0700506
reed4a8126e2014-09-22 07:29:03 -0700507 uint32_t oldFlags = paint.getFlags();
508 fNewPaintFlags = filter_paint_flags(props, oldFlags);
509 if (fIsSimple && (fNewPaintFlags != oldFlags)) {
reeddbc3cef2015-04-29 12:18:57 -0700510 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700511 paint->setFlags(fNewPaintFlags);
512 fPaint = paint;
513 // if we're not simple, doNext() will take care of calling setFlags()
514 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000515 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000516
reed@android.com8a1c16f2008-12-17 15:59:43 +0000517 ~AutoDrawLooper() {
reed5c476fb2015-04-20 08:04:21 -0700518 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000519 fCanvas->internalRestore();
520 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000521 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000522 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000523
reed@google.com4e2b3d32011-04-07 14:18:59 +0000524 const SkPaint& paint() const {
525 SkASSERT(fPaint);
526 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000527 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000528
reed@google.com129ec222012-05-15 13:24:09 +0000529 bool next(SkDrawFilter::Type drawType) {
530 if (fDone) {
531 return false;
532 } else if (fIsSimple) {
533 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000534 return !fPaint->nothingToDraw();
535 } else {
536 return this->doNext(drawType);
537 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000538 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000539
reed@android.com8a1c16f2008-12-17 15:59:43 +0000540private:
reeddbc3cef2015-04-29 12:18:57 -0700541 SkLazyPaint fLazyPaintInit; // base paint storage in case we need to modify it
542 SkLazyPaint fLazyPaintPerLooper; // per-draw-looper storage, so the looper can modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000543 SkCanvas* fCanvas;
544 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000545 SkDrawFilter* fFilter;
546 const SkPaint* fPaint;
547 int fSaveCount;
reed4a8126e2014-09-22 07:29:03 -0700548 uint32_t fNewPaintFlags;
reed5c476fb2015-04-20 08:04:21 -0700549 bool fTempLayerForImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000550 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000551 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000552 SkDrawLooper::Context* fLooperContext;
553 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000554
555 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000556};
557
reed@google.com129ec222012-05-15 13:24:09 +0000558bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
halcanary96fcdcc2015-08-27 07:41:13 -0700559 fPaint = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000560 SkASSERT(!fIsSimple);
reed5c476fb2015-04-20 08:04:21 -0700561 SkASSERT(fLooperContext || fFilter || fTempLayerForImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000562
reeddbc3cef2015-04-29 12:18:57 -0700563 SkPaint* paint = fLazyPaintPerLooper.set(fLazyPaintInit.isValid() ?
564 *fLazyPaintInit.get() : fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700565 paint->setFlags(fNewPaintFlags);
reed@google.com129ec222012-05-15 13:24:09 +0000566
reed5c476fb2015-04-20 08:04:21 -0700567 if (fTempLayerForImageFilter) {
halcanary96fcdcc2015-08-27 07:41:13 -0700568 paint->setImageFilter(nullptr);
569 paint->setXfermode(nullptr);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000570 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000571
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000572 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000573 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000574 return false;
575 }
576 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000577 if (!fFilter->filter(paint, drawType)) {
578 fDone = true;
579 return false;
580 }
halcanary96fcdcc2015-08-27 07:41:13 -0700581 if (nullptr == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000582 // no looper means we only draw once
583 fDone = true;
584 }
585 }
586 fPaint = paint;
587
588 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000589 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000590 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000591 }
592
593 // call this after any possible paint modifiers
594 if (fPaint->nothingToDraw()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700595 fPaint = nullptr;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000596 return false;
597 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000598 return true;
599}
600
reed@android.com8a1c16f2008-12-17 15:59:43 +0000601////////// macros to place around the internal draw calls //////////////////
602
reed262a71b2015-12-05 13:07:27 -0800603#define LOOPER_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
604 this->predrawNotify(); \
605 AutoDrawLooper looper(this, fProps, paint, skipLayerForFilter, bounds); \
606 while (looper.next(SkDrawFilter::kBitmap_Type)) { \
607 SkDrawIter iter(this);
608
609
reed@google.com8926b162012-03-23 15:36:36 +0000610#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000611 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700612 AutoDrawLooper looper(this, fProps, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000613 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000614 SkDrawIter iter(this);
615
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000616#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000617 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700618 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000619 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000620 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000621
reedc83a2972015-07-16 07:40:45 -0700622#define LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, type, bounds, auxOpaque) \
623 this->predrawNotify(bounds, &paint, auxOpaque); \
624 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
625 while (looper.next(type)) { \
626 SkDrawIter iter(this);
627
reed@google.com4e2b3d32011-04-07 14:18:59 +0000628#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000629
630////////////////////////////////////////////////////////////////////////////
631
mtkleinfeaadee2015-04-08 11:25:48 -0700632void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
633 this->restoreToCount(1);
634 fCachedLocalClipBounds.setEmpty();
635 fCachedLocalClipBoundsDirty = true;
636 fClipStack->reset();
637 fMCRec->reset(bounds);
638
639 // We're peering through a lot of structs here. Only at this scope do we
640 // know that the device is an SkBitmapDevice (really an SkNoPixelsBitmapDevice).
641 static_cast<SkBitmapDevice*>(fMCRec->fLayer->fDevice)->setNewSize(bounds.size());
642}
643
reedd9544982014-09-09 18:46:22 -0700644SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
reed42b73eb2015-11-20 13:42:42 -0800645 if (device && device->forceConservativeRasterClip()) {
646 flags = InitFlags(flags | kConservativeRasterClip_InitFlag);
647 }
648 // Since init() is only called once by our constructors, it is safe to perform this
649 // const-cast.
650 *const_cast<bool*>(&fConservativeRasterClip) = SkToBool(flags & kConservativeRasterClip_InitFlag);
651
reed@google.comc0784db2013-12-13 21:16:12 +0000652 fCachedLocalClipBounds.setEmpty();
653 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000654 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000655 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700656 fDeviceCMDirty = true;
reed2ff1fce2014-12-11 07:07:37 -0800657 fSaveCount = 1;
halcanary96fcdcc2015-08-27 07:41:13 -0700658 fMetaData = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000659
halcanary385fe4d2015-08-26 13:07:48 -0700660 fClipStack.reset(new SkClipStack);
reed687fa1c2015-04-07 08:00:56 -0700661
reed@android.com8a1c16f2008-12-17 15:59:43 +0000662 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700663 new (fMCRec) MCRec(fConservativeRasterClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000664
reeda499f902015-05-01 09:34:31 -0700665 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
666 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
halcanary96fcdcc2015-08-27 07:41:13 -0700667 new (fDeviceCMStorage) DeviceCM(nullptr, nullptr, nullptr, fConservativeRasterClip, false);
reedb679ca82015-04-07 04:40:48 -0700668
reed@android.com8a1c16f2008-12-17 15:59:43 +0000669 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000670
halcanary96fcdcc2015-08-27 07:41:13 -0700671 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000672
reedf92c8662014-08-18 08:02:43 -0700673 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700674 // The root device and the canvas should always have the same pixel geometry
675 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reedf92c8662014-08-18 08:02:43 -0700676 device->onAttachToCanvas(this);
677 fMCRec->fLayer->fDevice = SkRef(device);
reed78e27682014-11-19 08:04:34 -0800678 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
reedf92c8662014-08-18 08:02:43 -0700679 }
680 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000681}
682
reed@google.comcde92112011-07-06 20:00:52 +0000683SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000684 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700685 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800686 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000687{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000688 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000689
halcanary96fcdcc2015-08-27 07:41:13 -0700690 this->init(nullptr, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000691}
692
reedd9544982014-09-09 18:46:22 -0700693static SkBitmap make_nopixels(int width, int height) {
694 SkBitmap bitmap;
695 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
696 return bitmap;
697}
698
699class SkNoPixelsBitmapDevice : public SkBitmapDevice {
700public:
robertphillipsfcf78292015-06-19 11:49:52 -0700701 SkNoPixelsBitmapDevice(const SkIRect& bounds, const SkSurfaceProps& surfaceProps)
702 : INHERITED(make_nopixels(bounds.width(), bounds.height()), surfaceProps)
reed78e27682014-11-19 08:04:34 -0800703 {
704 this->setOrigin(bounds.x(), bounds.y());
705 }
reedd9544982014-09-09 18:46:22 -0700706
707private:
piotaixrb5fae932014-09-24 13:03:30 -0700708
reedd9544982014-09-09 18:46:22 -0700709 typedef SkBitmapDevice INHERITED;
710};
711
reed96a857e2015-01-25 10:33:58 -0800712SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000713 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800714 , fProps(SkSurfacePropsCopyOrDefault(props))
reed42b73eb2015-11-20 13:42:42 -0800715 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000716{
717 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700718
halcanary385fe4d2015-08-26 13:07:48 -0700719 this->init(new SkNoPixelsBitmapDevice(SkIRect::MakeWH(width, height), fProps),
720 kDefault_InitFlags)->unref();
reedd9544982014-09-09 18:46:22 -0700721}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000722
reed78e27682014-11-19 08:04:34 -0800723SkCanvas::SkCanvas(const SkIRect& bounds, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700724 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700725 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800726 , fConservativeRasterClip(false)
reedd9544982014-09-09 18:46:22 -0700727{
728 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700729
halcanary385fe4d2015-08-26 13:07:48 -0700730 this->init(new SkNoPixelsBitmapDevice(bounds, fProps), flags)->unref();
reedd9544982014-09-09 18:46:22 -0700731}
732
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000733SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000734 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700735 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800736 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000737{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000738 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700739
reedd9544982014-09-09 18:46:22 -0700740 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000741}
742
robertphillipsfcf78292015-06-19 11:49:52 -0700743SkCanvas::SkCanvas(SkBaseDevice* device, InitFlags flags)
744 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700745 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800746 , fConservativeRasterClip(false)
robertphillipsfcf78292015-06-19 11:49:52 -0700747{
748 inc_canvas();
749
750 this->init(device, flags);
751}
752
reed4a8126e2014-09-22 07:29:03 -0700753SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700754 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700755 , fProps(props)
reed42b73eb2015-11-20 13:42:42 -0800756 , fConservativeRasterClip(false)
reed3716fd02014-09-21 09:39:55 -0700757{
758 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700759
halcanary385fe4d2015-08-26 13:07:48 -0700760 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700761 this->init(device, kDefault_InitFlags);
762}
reed29c857d2014-09-21 10:25:07 -0700763
reed4a8126e2014-09-22 07:29:03 -0700764SkCanvas::SkCanvas(const SkBitmap& bitmap)
765 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
766 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800767 , fConservativeRasterClip(false)
reed4a8126e2014-09-22 07:29:03 -0700768{
769 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700770
halcanary385fe4d2015-08-26 13:07:48 -0700771 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700772 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000773}
774
775SkCanvas::~SkCanvas() {
776 // free up the contents of our deque
777 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000778
reed@android.com8a1c16f2008-12-17 15:59:43 +0000779 this->internalRestore(); // restore the last, since we're going away
780
halcanary385fe4d2015-08-26 13:07:48 -0700781 delete fMetaData;
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000782
reed@android.com8a1c16f2008-12-17 15:59:43 +0000783 dec_canvas();
784}
785
fmalita53d9f1c2016-01-25 06:23:54 -0800786#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000787SkDrawFilter* SkCanvas::getDrawFilter() const {
788 return fMCRec->fFilter;
789}
790
791SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
reed51985e32015-04-11 08:04:56 -0700792 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000793 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
794 return filter;
795}
fmalita77650002016-01-21 18:47:11 -0800796#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000797
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000798SkMetaData& SkCanvas::getMetaData() {
799 // metadata users are rare, so we lazily allocate it. If that changes we
800 // can decide to just make it a field in the device (rather than a ptr)
halcanary96fcdcc2015-08-27 07:41:13 -0700801 if (nullptr == fMetaData) {
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000802 fMetaData = new SkMetaData;
803 }
804 return *fMetaData;
805}
806
reed@android.com8a1c16f2008-12-17 15:59:43 +0000807///////////////////////////////////////////////////////////////////////////////
808
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000809void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000810 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000811 if (device) {
812 device->flush();
813 }
814}
815
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000816SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000817 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000818 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
819}
820
senorblancoafc7cce2016-02-02 18:44:15 -0800821SkIRect SkCanvas::getTopLayerBounds() const {
822 SkBaseDevice* d = this->getTopDevice();
823 if (!d) {
824 return SkIRect::MakeEmpty();
825 }
826 return SkIRect::MakeXYWH(d->getOrigin().x(), d->getOrigin().y(), d->width(), d->height());
827}
828
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000829SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000830 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000831 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000832 SkASSERT(rec && rec->fLayer);
833 return rec->fLayer->fDevice;
834}
835
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000836SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000837 if (updateMatrixClip) {
838 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
839 }
reed@google.com9266fed2011-03-30 00:18:03 +0000840 return fMCRec->fTopLayer->fDevice;
841}
842
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000843bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
844 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
845 return false;
846 }
847
848 bool weAllocated = false;
halcanary96fcdcc2015-08-27 07:41:13 -0700849 if (nullptr == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700850 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000851 return false;
852 }
853 weAllocated = true;
854 }
855
reedcf01e312015-05-23 19:14:51 -0700856 SkAutoPixmapUnlock unlocker;
857 if (bitmap->requestLock(&unlocker)) {
858 const SkPixmap& pm = unlocker.pixmap();
859 if (this->readPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), x, y)) {
860 return true;
861 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000862 }
863
864 if (weAllocated) {
halcanary96fcdcc2015-08-27 07:41:13 -0700865 bitmap->setPixelRef(nullptr);
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000866 }
867 return false;
868}
reed@google.com51df9e32010-12-23 19:29:18 +0000869
bsalomon@google.comc6980972011-11-02 19:57:21 +0000870bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000871 SkIRect r = srcRect;
872 const SkISize size = this->getBaseLayerSize();
873 if (!r.intersect(0, 0, size.width(), size.height())) {
874 bitmap->reset();
875 return false;
876 }
877
reed84825042014-09-02 12:50:45 -0700878 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000879 // bitmap will already be reset.
880 return false;
881 }
882 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
883 bitmap->reset();
884 return false;
885 }
886 return true;
887}
888
reed96472de2014-12-10 09:53:42 -0800889bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000890 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000891 if (!device) {
892 return false;
893 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000894 const SkISize size = this->getBaseLayerSize();
mtkleinf0f14112014-12-12 08:46:25 -0800895
reed96472de2014-12-10 09:53:42 -0800896 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
897 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000898 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000899 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000900
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000901 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800902 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000903}
904
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000905bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
906 if (bitmap.getTexture()) {
907 return false;
908 }
reedcf01e312015-05-23 19:14:51 -0700909
910 SkAutoPixmapUnlock unlocker;
911 if (bitmap.requestLock(&unlocker)) {
912 const SkPixmap& pm = unlocker.pixmap();
913 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000914 }
915 return false;
916}
917
918bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
919 int x, int y) {
920 switch (origInfo.colorType()) {
921 case kUnknown_SkColorType:
922 case kIndex_8_SkColorType:
923 return false;
924 default:
925 break;
926 }
halcanary96fcdcc2015-08-27 07:41:13 -0700927 if (nullptr == pixels || rowBytes < origInfo.minRowBytes()) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000928 return false;
929 }
930
931 const SkISize size = this->getBaseLayerSize();
932 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
933 if (!target.intersect(0, 0, size.width(), size.height())) {
934 return false;
935 }
936
937 SkBaseDevice* device = this->getDevice();
938 if (!device) {
939 return false;
940 }
941
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000942 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700943 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000944
945 // if x or y are negative, then we have to adjust pixels
946 if (x > 0) {
947 x = 0;
948 }
949 if (y > 0) {
950 y = 0;
951 }
952 // here x,y are either 0 or negative
953 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
954
reed4af35f32014-06-27 17:47:49 -0700955 // Tell our owning surface to bump its generation ID
reedc83a2972015-07-16 07:40:45 -0700956 const bool completeOverwrite = info.dimensions() == size;
957 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700958
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000959 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000960 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000961}
reed@google.com51df9e32010-12-23 19:29:18 +0000962
junov@google.com4370aed2012-01-18 16:21:08 +0000963SkCanvas* SkCanvas::canvasForDrawIter() {
964 return this;
965}
966
reed@android.com8a1c16f2008-12-17 15:59:43 +0000967//////////////////////////////////////////////////////////////////////////////
968
reed@android.com8a1c16f2008-12-17 15:59:43 +0000969void SkCanvas::updateDeviceCMCache() {
970 if (fDeviceCMDirty) {
971 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700972 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000973 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000974
halcanary96fcdcc2015-08-27 07:41:13 -0700975 if (nullptr == layer->fNext) { // only one layer
976 layer->updateMC(totalMatrix, totalClip, *fClipStack, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000977 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000978 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000979 do {
reed687fa1c2015-04-07 08:00:56 -0700980 layer->updateMC(totalMatrix, clip, *fClipStack, &clip);
halcanary96fcdcc2015-08-27 07:41:13 -0700981 } while ((layer = layer->fNext) != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000982 }
983 fDeviceCMDirty = false;
984 }
985}
986
reed@android.com8a1c16f2008-12-17 15:59:43 +0000987///////////////////////////////////////////////////////////////////////////////
988
reed2ff1fce2014-12-11 07:07:37 -0800989void SkCanvas::checkForDeferredSave() {
990 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800991 this->doSave();
992 }
993}
994
reedf0090cb2014-11-26 08:55:51 -0800995int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800996#ifdef SK_DEBUG
997 int count = 0;
998 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
999 for (;;) {
1000 const MCRec* rec = (const MCRec*)iter.next();
1001 if (!rec) {
1002 break;
1003 }
1004 count += 1 + rec->fDeferredSaveCount;
1005 }
1006 SkASSERT(count == fSaveCount);
1007#endif
1008 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -08001009}
1010
1011int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -08001012 fSaveCount += 1;
1013 fMCRec->fDeferredSaveCount += 1;
1014 return this->getSaveCount() - 1; // return our prev value
1015}
1016
1017void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -08001018 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -07001019
1020 SkASSERT(fMCRec->fDeferredSaveCount > 0);
1021 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001022 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -08001023}
1024
1025void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -08001026 if (fMCRec->fDeferredSaveCount > 0) {
1027 SkASSERT(fSaveCount > 1);
1028 fSaveCount -= 1;
1029 fMCRec->fDeferredSaveCount -= 1;
1030 } else {
1031 // check for underflow
1032 if (fMCStack.count() > 1) {
1033 this->willRestore();
1034 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -07001035 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001036 this->internalRestore();
1037 this->didRestore();
1038 }
reedf0090cb2014-11-26 08:55:51 -08001039 }
1040}
1041
1042void SkCanvas::restoreToCount(int count) {
1043 // sanity check
1044 if (count < 1) {
1045 count = 1;
1046 }
mtkleinf0f14112014-12-12 08:46:25 -08001047
reedf0090cb2014-11-26 08:55:51 -08001048 int n = this->getSaveCount() - count;
1049 for (int i = 0; i < n; ++i) {
1050 this->restore();
1051 }
1052}
1053
reed2ff1fce2014-12-11 07:07:37 -08001054void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001055 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -07001056 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +00001057 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +00001058
reed687fa1c2015-04-07 08:00:56 -07001059 fClipStack->save();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001060}
1061
reed4960eee2015-12-18 07:09:18 -08001062bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
reed@google.comb93ba452014-03-10 19:47:58 +00001063#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001064 return !(saveLayerFlags & SkCanvas::kDontClipToLayer_PrivateSaveLayerFlag);
reed@google.comb93ba452014-03-10 19:47:58 +00001065#else
1066 return true;
1067#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001068}
1069
reed4960eee2015-12-18 07:09:18 -08001070bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -07001071 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001072 SkIRect clipBounds;
1073 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001074 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +00001075 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001076
reed96e657d2015-03-10 17:30:07 -07001077 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
1078
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001079 if (imageFilter) {
senorblancoe5e79842016-03-21 14:51:59 -07001080 clipBounds = imageFilter->filterBounds(clipBounds, ctm);
senorblancodb64af32015-12-09 10:11:43 -08001081 if (bounds && !imageFilter->canComputeFastBounds()) {
1082 bounds = nullptr;
1083 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001084 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001085 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -07001086 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001087 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +00001088
reed96e657d2015-03-10 17:30:07 -07001089 ctm.mapRect(&r, *bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001090 r.roundOut(&ir);
1091 // early exit if the layer's bounds are clipped out
1092 if (!ir.intersect(clipBounds)) {
reed4960eee2015-12-18 07:09:18 -08001093 if (BoundsAffectsClip(saveLayerFlags)) {
reed9b3aa542015-03-11 08:47:12 -07001094 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001095 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001096 }
junov@chromium.orga907ac32012-02-24 21:54:07 +00001097 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001098 }
1099 } else { // no user bounds, so just use the clip
1100 ir = clipBounds;
1101 }
reed180aec42015-03-11 10:39:04 -07001102 SkASSERT(!ir.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001103
reed4960eee2015-12-18 07:09:18 -08001104 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -07001105 // Simplify the current clips since they will be applied properly during restore()
reed9b3aa542015-03-11 08:47:12 -07001106 fCachedLocalClipBoundsDirty = true;
reed687fa1c2015-04-07 08:00:56 -07001107 fClipStack->clipDevRect(ir, SkRegion::kReplace_Op);
reed180aec42015-03-11 10:39:04 -07001108 fMCRec->fRasterClip.setRect(ir);
junov@chromium.orga907ac32012-02-24 21:54:07 +00001109 }
1110
1111 if (intersection) {
1112 *intersection = ir;
1113 }
1114 return true;
1115}
1116
reed4960eee2015-12-18 07:09:18 -08001117
reed4960eee2015-12-18 07:09:18 -08001118int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
1119 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001120}
1121
reed70ee31b2015-12-10 13:44:45 -08001122int SkCanvas::saveLayerPreserveLCDTextRequests(const SkRect* bounds, const SkPaint* paint) {
reed4960eee2015-12-18 07:09:18 -08001123 return this->saveLayer(SaveLayerRec(bounds, paint, kPreserveLCDText_SaveLayerFlag));
1124}
1125
1126int SkCanvas::saveLayer(const SaveLayerRec& origRec) {
1127 SaveLayerRec rec(origRec);
1128 if (gIgnoreSaveLayerBounds) {
1129 rec.fBounds = nullptr;
1130 }
1131 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
1132 fSaveCount += 1;
1133 this->internalSaveLayer(rec, strategy);
1134 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -08001135}
1136
reedbfd5f172016-01-07 11:28:08 -08001137static void draw_filter_into_device(SkBaseDevice* src, const SkImageFilter* filter,
1138 SkBaseDevice* dst, const SkMatrix& ctm) {
robertphillips7354a4b2015-12-16 05:08:27 -08001139
1140 SkBitmap srcBM;
1141
1142#if SK_SUPPORT_GPU
1143 GrRenderTarget* srcRT = src->accessRenderTarget();
1144 if (srcRT && !srcRT->asTexture() && dst->accessRenderTarget()) {
1145 // When both the src & the dst are on the gpu but the src doesn't have a texture,
1146 // we create a temporary texture for the draw.
1147 // TODO: we should actually only copy the portion of the source needed to apply the image
1148 // filter
1149 GrContext* context = srcRT->getContext();
bsalomon5ec26ae2016-02-25 08:33:02 -08001150 SkAutoTUnref<GrTexture> tex(context->textureProvider()->createTexture(srcRT->desc(),
1151 SkBudgeted::kYes));
robertphillips7354a4b2015-12-16 05:08:27 -08001152
1153 context->copySurface(tex, srcRT);
1154
1155 GrWrapTextureInBitmap(tex, src->width(), src->height(), src->isOpaque(), &srcBM);
1156 } else
1157#endif
1158 {
1159 srcBM = src->accessBitmap(false);
1160 }
1161
1162 SkCanvas c(dst);
1163
reedbfd5f172016-01-07 11:28:08 -08001164 SkAutoTUnref<SkImageFilter> localF(filter->newWithLocalMatrix(ctm));
robertphillips7354a4b2015-12-16 05:08:27 -08001165 SkPaint p;
reedbfd5f172016-01-07 11:28:08 -08001166 p.setImageFilter(localF);
1167 const SkScalar x = SkIntToScalar(src->getOrigin().x());
1168 const SkScalar y = SkIntToScalar(src->getOrigin().y());
1169 c.drawBitmap(srcBM, x, y, &p);
robertphillips7354a4b2015-12-16 05:08:27 -08001170}
reed70ee31b2015-12-10 13:44:45 -08001171
reed129ed1c2016-02-22 06:42:31 -08001172static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, bool isOpaque,
1173 const SkPaint* paint) {
1174 // need to force L32 for now if we have an image filter. Once filters support other colortypes
1175 // e.g. sRGB or F16, we can remove this check
1176 const bool hasImageFilter = paint && paint->getImageFilter();
1177
1178 SkAlphaType alphaType = isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
1179 if ((prev.bytesPerPixel() < 4) || hasImageFilter) {
1180 // force to L32
1181 return SkImageInfo::MakeN32(w, h, alphaType);
1182 } else {
1183 // keep the same characteristics as the prev
1184 return SkImageInfo::Make(w, h, prev.colorType(), alphaType, prev.profileType());
1185 }
1186}
1187
reed4960eee2015-12-18 07:09:18 -08001188void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
1189 const SkRect* bounds = rec.fBounds;
1190 const SkPaint* paint = rec.fPaint;
1191 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1192
reed@google.comb93ba452014-03-10 19:47:58 +00001193#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001194 saveLayerFlags &= ~kDontClipToLayer_PrivateSaveLayerFlag;
reed@google.comb93ba452014-03-10 19:47:58 +00001195#endif
1196
junov@chromium.orga907ac32012-02-24 21:54:07 +00001197 // do this before we create the layer. We don't call the public save() since
1198 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001199 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001200
1201 fDeviceCMDirty = true;
1202
1203 SkIRect ir;
reed4960eee2015-12-18 07:09:18 -08001204 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, paint ? paint->getImageFilter() : nullptr)) {
reed2ff1fce2014-12-11 07:07:37 -08001205 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001206 }
1207
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001208 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1209 // the clipRectBounds() call above?
1210 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001211 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001212 }
1213
reed4960eee2015-12-18 07:09:18 -08001214 bool isOpaque = SkToBool(saveLayerFlags & kIsOpaque_SaveLayerFlag);
reed8dc0ccb2015-03-20 06:32:52 -07001215 SkPixelGeometry geo = fProps.pixelGeometry();
1216 if (paint) {
reed76033be2015-03-14 10:54:31 -07001217 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001218 if (paint->getImageFilter() || paint->getColorFilter()) {
reed76033be2015-03-14 10:54:31 -07001219 isOpaque = false;
reed8dc0ccb2015-03-20 06:32:52 -07001220 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001221 }
1222 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001223
reedb2db8982014-11-13 12:41:02 -08001224 SkBaseDevice* device = this->getTopDevice();
halcanary96fcdcc2015-08-27 07:41:13 -07001225 if (nullptr == device) {
reedb2db8982014-11-13 12:41:02 -08001226 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001227 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001228 }
reedb2db8982014-11-13 12:41:02 -08001229
reed129ed1c2016-02-22 06:42:31 -08001230 SkImageInfo info = make_layer_info(device->imageInfo(), ir.width(), ir.height(), isOpaque,
1231 paint);
1232
reed61f501f2015-04-29 08:34:00 -07001233 bool forceSpriteOnRestore = false;
1234 {
reed70ee31b2015-12-10 13:44:45 -08001235 const bool preserveLCDText = kOpaque_SkAlphaType == info.alphaType() ||
reed4960eee2015-12-18 07:09:18 -08001236 (saveLayerFlags & kPreserveLCDText_SaveLayerFlag);
reeddaa57bf2015-05-15 10:39:17 -07001237 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
reed70ee31b2015-12-10 13:44:45 -08001238 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
1239 preserveLCDText, false);
reed61f501f2015-04-29 08:34:00 -07001240 SkBaseDevice* newDev = device->onCreateDevice(createInfo, paint);
halcanary96fcdcc2015-08-27 07:41:13 -07001241 if (nullptr == newDev) {
reed61f501f2015-04-29 08:34:00 -07001242 // If onCreateDevice didn't succeed, try raster (e.g. PDF couldn't handle the paint)
robertphillips9a53fd72015-06-22 09:46:59 -07001243 const SkSurfaceProps surfaceProps(fProps.flags(), createInfo.fPixelGeometry);
1244 newDev = SkBitmapDevice::Create(createInfo.fInfo, surfaceProps);
halcanary96fcdcc2015-08-27 07:41:13 -07001245 if (nullptr == newDev) {
reed61f501f2015-04-29 08:34:00 -07001246 SkErrorInternals::SetError(kInternalError_SkError,
1247 "Unable to create device for layer.");
1248 return;
1249 }
1250 forceSpriteOnRestore = true;
1251 }
1252 device = newDev;
bungeman@google.come25c6842011-08-17 14:53:54 +00001253 }
reed@google.com6f8f2922011-03-04 22:27:10 +00001254 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips7354a4b2015-12-16 05:08:27 -08001255
reedbfd5f172016-01-07 11:28:08 -08001256 if (rec.fBackdrop) {
1257 draw_filter_into_device(fMCRec->fTopLayer->fDevice, rec.fBackdrop, device, fMCRec->fMatrix);
robertphillips7354a4b2015-12-16 05:08:27 -08001258 }
1259
halcanary385fe4d2015-08-26 13:07:48 -07001260 DeviceCM* layer =
1261 new DeviceCM(device, paint, this, fConservativeRasterClip, forceSpriteOnRestore);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001262 device->unref();
1263
1264 layer->fNext = fMCRec->fTopLayer;
1265 fMCRec->fLayer = layer;
1266 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reed@android.com8a1c16f2008-12-17 15:59:43 +00001267}
1268
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001269int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001270 if (0xFF == alpha) {
1271 return this->saveLayer(bounds, nullptr);
1272 } else {
1273 SkPaint tmpPaint;
1274 tmpPaint.setAlpha(alpha);
1275 return this->saveLayer(bounds, &tmpPaint);
1276 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001277}
1278
reed@android.com8a1c16f2008-12-17 15:59:43 +00001279void SkCanvas::internalRestore() {
1280 SkASSERT(fMCStack.count() != 0);
1281
1282 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001283 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001284
reed687fa1c2015-04-07 08:00:56 -07001285 fClipStack->restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001286
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001287 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001288 DeviceCM* layer = fMCRec->fLayer; // may be null
1289 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001290 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001291
1292 // now do the normal restore()
1293 fMCRec->~MCRec(); // balanced in save()
1294 fMCStack.pop_back();
1295 fMCRec = (MCRec*)fMCStack.back();
1296
1297 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1298 since if we're being recorded, we don't want to record this (the
1299 recorder will have already recorded the restore).
1300 */
bsalomon49f085d2014-09-05 13:34:00 -07001301 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001302 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001303 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001304 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
reed61f501f2015-04-29 08:34:00 -07001305 layer->fPaint, layer->fDeviceIsBitmapDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001306 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001307 fDeviceCMDirty = true;
halcanary385fe4d2015-08-26 13:07:48 -07001308 delete layer;
reedb679ca82015-04-07 04:40:48 -07001309 } else {
1310 // we're at the root
reeda499f902015-05-01 09:34:31 -07001311 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001312 layer->~DeviceCM();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001313 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001314 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001315}
1316
reed4a8126e2014-09-22 07:29:03 -07001317SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001318 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001319 props = &fProps;
1320 }
1321 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001322}
1323
reed4a8126e2014-09-22 07:29:03 -07001324SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001325 SkBaseDevice* dev = this->getDevice();
halcanary96fcdcc2015-08-27 07:41:13 -07001326 return dev ? dev->newSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001327}
1328
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001329SkImageInfo SkCanvas::imageInfo() const {
1330 SkBaseDevice* dev = this->getDevice();
1331 if (dev) {
1332 return dev->imageInfo();
1333 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001334 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001335 }
1336}
1337
reed6ceeebd2016-03-09 14:26:26 -08001338#ifdef SK_SUPPORT_LEGACY_PEEKPIXELS_PARMS
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001339const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
reed884e97c2015-05-26 11:31:54 -07001340 SkPixmap pmap;
reed6ceeebd2016-03-09 14:26:26 -08001341 if (this->peekPixels(&pmap)) {
1342 if (info) {
1343 *info = pmap.info();
1344 }
1345 if (rowBytes) {
1346 *rowBytes = pmap.rowBytes();
1347 }
1348 return pmap.addr();
reed884e97c2015-05-26 11:31:54 -07001349 }
reed6ceeebd2016-03-09 14:26:26 -08001350 return nullptr;
1351}
1352#endif
1353
1354bool SkCanvas::peekPixels(SkPixmap* pmap) {
1355 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001356}
1357
reed884e97c2015-05-26 11:31:54 -07001358bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001359 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001360 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001361}
1362
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001363void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001364 SkPixmap pmap;
1365 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001366 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001367 }
1368 if (info) {
1369 *info = pmap.info();
1370 }
1371 if (rowBytes) {
1372 *rowBytes = pmap.rowBytes();
1373 }
1374 if (origin) {
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001375 *origin = this->getTopDevice(false)->getOrigin();
1376 }
reed884e97c2015-05-26 11:31:54 -07001377 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001378}
1379
reed884e97c2015-05-26 11:31:54 -07001380bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001381 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001382 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001383}
1384
reed@android.com8a1c16f2008-12-17 15:59:43 +00001385/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001386
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001387void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed61f501f2015-04-29 08:34:00 -07001388 const SkPaint* paint, bool deviceIsBitmapDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001389 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001390 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001391 paint = &tmp;
1392 }
reed@google.com4b226022011-01-11 18:32:13 +00001393
reed@google.com8926b162012-03-23 15:36:36 +00001394 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001395 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001396 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001397 paint = &looper.paint();
1398 SkImageFilter* filter = paint->getImageFilter();
1399 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001400 if (filter && !dstDev->canHandleImageFilter(filter)) {
reed88d064d2015-10-12 11:30:02 -07001401 SkImageFilter::DeviceProxy proxy(dstDev);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001402 SkIPoint offset = SkIPoint::Make(0, 0);
robertphillips4418dba2016-03-07 12:45:14 -08001403 const SkBitmap& srcBM = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001404 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001405 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
robertphillips4418dba2016-03-07 12:45:14 -08001406 const SkIRect clipBounds = iter.fClip->getBounds().makeOffset(-pos.x(), -pos.y());
senorblancobe129b22014-08-08 07:14:35 -07001407 SkAutoTUnref<SkImageFilter::Cache> cache(dstDev->getImageFilterCache());
reed4e23cda2016-01-11 10:56:59 -08001408 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
robertphillips4418dba2016-03-07 12:45:14 -08001409
robertphillips37bd7c32016-03-17 14:31:39 -07001410 sk_sp<SkSpecialImage> srcImg(SkSpecialImage::internal_fromBM(&proxy, srcBM));
robertphillips4418dba2016-03-07 12:45:14 -08001411 if (!srcImg) {
1412 continue; // something disastrous happened
1413 }
1414
robertphillips37bd7c32016-03-17 14:31:39 -07001415 SkAutoTUnref<SkSpecialImage> resultImg(filter->filterImage(srcImg.get(), ctx,
1416 &offset));
robertphillips4418dba2016-03-07 12:45:14 -08001417 if (resultImg) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001418 SkPaint tmpUnfiltered(*paint);
halcanary96fcdcc2015-08-27 07:41:13 -07001419 tmpUnfiltered.setImageFilter(nullptr);
robertphillips4418dba2016-03-07 12:45:14 -08001420 SkBitmap resultBM;
1421 if (resultImg->internal_getBM(&resultBM)) {
1422 // TODO: add drawSprite(SkSpecialImage) to SkDevice? (see skbug.com/5073)
1423 dstDev->drawSprite(iter, resultBM, pos.x() + offset.x(), pos.y() + offset.y(),
1424 tmpUnfiltered);
1425 }
reed@google.com76dd2772012-01-05 21:15:07 +00001426 }
reed61f501f2015-04-29 08:34:00 -07001427 } else if (deviceIsBitmapDevice) {
reed9572a102015-05-26 19:22:17 -07001428 const SkBitmap& src = static_cast<SkBitmapDevice*>(srcDev)->fBitmap;
reed61f501f2015-04-29 08:34:00 -07001429 dstDev->drawSprite(iter, src, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001430 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001431 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001432 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001433 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001434 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001435}
1436
reed32704672015-12-16 08:27:10 -08001437/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001438
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001439void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001440 SkMatrix m;
1441 m.setTranslate(dx, dy);
1442 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001443}
1444
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001445void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001446 SkMatrix m;
1447 m.setScale(sx, sy);
1448 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001449}
1450
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001451void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001452 SkMatrix m;
1453 m.setRotate(degrees);
1454 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001455}
1456
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001457void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001458 SkMatrix m;
1459 m.setSkew(sx, sy);
1460 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001461}
1462
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001463void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001464 if (matrix.isIdentity()) {
1465 return;
1466 }
1467
reed2ff1fce2014-12-11 07:07:37 -08001468 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001469 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001470 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001471 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001472
1473 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001474}
1475
reed86a17e72015-05-14 12:25:22 -07001476void SkCanvas::setMatrix(const SkMatrix& matrix) {
1477 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001478 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001479 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001480 fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001481 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001482}
1483
reed@android.com8a1c16f2008-12-17 15:59:43 +00001484void SkCanvas::resetMatrix() {
1485 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001486
reed@android.com8a1c16f2008-12-17 15:59:43 +00001487 matrix.reset();
1488 this->setMatrix(matrix);
1489}
1490
1491//////////////////////////////////////////////////////////////////////////////
1492
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001493void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001494 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001495 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1496 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001497}
1498
1499void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001500#ifdef SK_ENABLE_CLIP_QUICKREJECT
1501 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001502 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001503 return false;
1504 }
1505
reed@google.com3b3e8952012-08-16 20:53:31 +00001506 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001507 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001508 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001509
reed687fa1c2015-04-07 08:00:56 -07001510 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001511 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001512 }
1513 }
1514#endif
1515
bsalomonac8cabd2015-11-20 18:53:07 -08001516 if (!fAllowSoftClip) {
1517 edgeStyle = kHard_ClipEdgeStyle;
1518 }
reed90ba0952015-11-20 13:42:47 -08001519
reedc64eff52015-11-21 12:39:45 -08001520 const bool rectStaysRect = fMCRec->fMatrix.rectStaysRect();
1521 SkRect devR;
1522 if (rectStaysRect) {
1523 fMCRec->fMatrix.mapRect(&devR, rect);
1524 }
bsalomonac8cabd2015-11-20 18:53:07 -08001525
reedc64eff52015-11-21 12:39:45 -08001526 // Check if we can quick-accept the clip call (and do nothing)
1527 //
1528 // TODO: investigate if a (conservative) version of this could be done in ::clipRect,
1529 // so that subclasses (like PictureRecording) didn't see unnecessary clips, which in turn
1530 // might allow lazy save/restores to eliminate entire save/restore blocks.
1531 //
1532 if (SkRegion::kIntersect_Op == op &&
1533 kHard_ClipEdgeStyle == edgeStyle
1534 && rectStaysRect)
1535 {
1536 if (devR.round().contains(fMCRec->fRasterClip.getBounds())) {
1537#if 0
1538 SkDebugf("------- ignored clipRect [%g %g %g %g]\n",
1539 rect.left(), rect.top(), rect.right(), rect.bottom());
1540#endif
1541 return;
1542 }
1543 }
1544
1545 AutoValidateClip avc(this);
1546
1547 fDeviceCMDirty = true;
1548 fCachedLocalClipBoundsDirty = true;
1549
1550 if (rectStaysRect) {
1551 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1552 fClipStack->clipDevRect(devR, op, isAA);
senorblancoafc7cce2016-02-02 18:44:15 -08001553 fMCRec->fRasterClip.op(devR, this->getTopLayerBounds(), op, isAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001554 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001555 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001556 // and clip against that, since it can handle any matrix. However, to
1557 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1558 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001559 SkPath path;
1560
1561 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001562 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001563 }
1564}
1565
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001566void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001567 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001568 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001569 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001570 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1571 } else {
1572 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001573 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001574}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001575
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001576void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001577 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001578 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001579 AutoValidateClip avc(this);
1580
1581 fDeviceCMDirty = true;
1582 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001583 if (!fAllowSoftClip) {
1584 edgeStyle = kHard_ClipEdgeStyle;
1585 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001586
reed687fa1c2015-04-07 08:00:56 -07001587 fClipStack->clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001588
senorblancoafc7cce2016-02-02 18:44:15 -08001589 fMCRec->fRasterClip.op(transformedRRect, this->getTopLayerBounds(), op,
robertphillips125f19a2015-11-23 09:00:05 -08001590 kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001591 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001592 }
1593
1594 SkPath path;
1595 path.addRRect(rrect);
1596 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001597 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001598}
1599
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001600void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001601 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001602 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001603
1604 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1605 SkRect r;
1606 if (path.isRect(&r)) {
1607 this->onClipRect(r, op, edgeStyle);
1608 return;
1609 }
1610 SkRRect rrect;
1611 if (path.isOval(&r)) {
1612 rrect.setOval(r);
1613 this->onClipRRect(rrect, op, edgeStyle);
1614 return;
1615 }
1616 if (path.isRRect(&rrect)) {
1617 this->onClipRRect(rrect, op, edgeStyle);
1618 return;
1619 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001620 }
robertphillips39f05382015-11-24 09:30:12 -08001621
1622 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001623}
1624
1625void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001626#ifdef SK_ENABLE_CLIP_QUICKREJECT
1627 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001628 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001629 return false;
1630 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001631
reed@google.com3b3e8952012-08-16 20:53:31 +00001632 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001633 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001634 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001635
reed687fa1c2015-04-07 08:00:56 -07001636 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001637 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001638 }
1639 }
1640#endif
1641
reed@google.com5c3d1472011-02-22 19:12:23 +00001642 AutoValidateClip avc(this);
1643
reed@android.com8a1c16f2008-12-17 15:59:43 +00001644 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001645 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001646 if (!fAllowSoftClip) {
1647 edgeStyle = kHard_ClipEdgeStyle;
1648 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001649
1650 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001651 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001652
reed@google.comfe701122011-11-08 19:41:23 +00001653 // Check if the transfomation, or the original path itself
1654 // made us empty. Note this can also happen if we contained NaN
1655 // values. computing the bounds detects this, and will set our
1656 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1657 if (devPath.getBounds().isEmpty()) {
1658 // resetting the path will remove any NaN or other wanky values
1659 // that might upset our scan converter.
1660 devPath.reset();
1661 }
1662
reed@google.com5c3d1472011-02-22 19:12:23 +00001663 // if we called path.swap() we could avoid a deep copy of this path
reed687fa1c2015-04-07 08:00:56 -07001664 fClipStack->clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001665
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001666 if (fAllowSimplifyClip) {
fmalita1a481fe2015-02-04 07:39:34 -08001667 bool clipIsAA = getClipStack()->asPath(&devPath);
1668 if (clipIsAA) {
1669 edgeStyle = kSoft_ClipEdgeStyle;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001670 }
fmalita1a481fe2015-02-04 07:39:34 -08001671
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001672 op = SkRegion::kReplace_Op;
1673 }
1674
senorblancoafc7cce2016-02-02 18:44:15 -08001675 fMCRec->fRasterClip.op(devPath, this->getTopLayerBounds(), op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001676}
1677
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001678void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed2ff1fce2014-12-11 07:07:37 -08001679 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001680 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001681}
1682
1683void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001684 AutoValidateClip avc(this);
1685
reed@android.com8a1c16f2008-12-17 15:59:43 +00001686 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001687 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001688
reed@google.com5c3d1472011-02-22 19:12:23 +00001689 // todo: signal fClipStack that we have a region, and therefore (I guess)
1690 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001691 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001692
reed1f836ee2014-07-07 07:49:34 -07001693 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001694}
1695
reed@google.com819c9212011-02-23 18:56:55 +00001696#ifdef SK_DEBUG
1697void SkCanvas::validateClip() const {
1698 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001699 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001700 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001701 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001702 return;
1703 }
1704
reed@google.com819c9212011-02-23 18:56:55 +00001705 SkIRect ir;
1706 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001707 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001708
reed687fa1c2015-04-07 08:00:56 -07001709 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001710 const SkClipStack::Element* element;
halcanary96fcdcc2015-08-27 07:41:13 -07001711 while ((element = iter.next()) != nullptr) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001712 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001713 case SkClipStack::Element::kRect_Type:
1714 element->getRect().round(&ir);
1715 tmpClip.op(ir, element->getOp());
1716 break;
1717 case SkClipStack::Element::kEmpty_Type:
1718 tmpClip.setEmpty();
1719 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001720 default: {
1721 SkPath path;
1722 element->asPath(&path);
senorblancoafc7cce2016-02-02 18:44:15 -08001723 tmpClip.op(path, this->getTopLayerBounds(), element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001724 break;
1725 }
reed@google.com819c9212011-02-23 18:56:55 +00001726 }
1727 }
reed@google.com819c9212011-02-23 18:56:55 +00001728}
1729#endif
1730
reed@google.com90c07ea2012-04-13 13:50:27 +00001731void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001732 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001733 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001734
halcanary96fcdcc2015-08-27 07:41:13 -07001735 while ((element = iter.next()) != nullptr) {
fmalitac3b589a2014-06-05 12:40:07 -07001736 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001737 }
1738}
1739
reed@google.com5c3d1472011-02-22 19:12:23 +00001740///////////////////////////////////////////////////////////////////////////////
1741
reed@google.com754de5f2014-02-24 19:38:20 +00001742bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001743 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001744}
1745
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001746bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001747 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001748}
1749
reed@google.com3b3e8952012-08-16 20:53:31 +00001750bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001751 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001752 return true;
1753
reed1f836ee2014-07-07 07:49:34 -07001754 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001755 return true;
1756 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001757
reed1f836ee2014-07-07 07:49:34 -07001758 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001759 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001760 fMCRec->fMatrix.mapRect(&dst, rect);
reedb07a94f2014-11-19 05:03:18 -08001761 return !SkIRect::Intersects(dst.roundOut(), fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001762 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001763 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001764
reed@android.coma380ae42009-07-21 01:17:02 +00001765 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001766 // TODO: should we use | instead, or compare all 4 at once?
1767 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001768 return true;
1769 }
reed@google.comc0784db2013-12-13 21:16:12 +00001770 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001771 return true;
1772 }
1773 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001774 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001775}
1776
reed@google.com3b3e8952012-08-16 20:53:31 +00001777bool SkCanvas::quickReject(const SkPath& path) const {
1778 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001779}
1780
reed@google.com3b3e8952012-08-16 20:53:31 +00001781bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001782 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001783 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001784 return false;
1785 }
1786
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001787 SkMatrix inverse;
1788 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001789 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001790 if (bounds) {
1791 bounds->setEmpty();
1792 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001793 return false;
1794 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001795
bsalomon49f085d2014-09-05 13:34:00 -07001796 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001797 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001798 // adjust it outwards in case we are antialiasing
1799 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001800
reed@google.com8f4d2302013-12-17 16:44:46 +00001801 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1802 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001803 inverse.mapRect(bounds, r);
1804 }
1805 return true;
1806}
1807
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001808bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001809 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001810 if (clip.isEmpty()) {
1811 if (bounds) {
1812 bounds->setEmpty();
1813 }
1814 return false;
1815 }
1816
bsalomon49f085d2014-09-05 13:34:00 -07001817 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001818 *bounds = clip.getBounds();
1819 }
1820 return true;
1821}
1822
reed@android.com8a1c16f2008-12-17 15:59:43 +00001823const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001824 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001825}
1826
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001827const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001828 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001829}
1830
reed@google.com9c135db2014-03-12 18:28:35 +00001831GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1832 SkBaseDevice* dev = this->getTopDevice();
halcanary96fcdcc2015-08-27 07:41:13 -07001833 return dev ? dev->accessRenderTarget() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001834}
1835
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001836GrContext* SkCanvas::getGrContext() {
1837#if SK_SUPPORT_GPU
1838 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07001839 if (device) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001840 GrRenderTarget* renderTarget = device->accessRenderTarget();
bsalomon49f085d2014-09-05 13:34:00 -07001841 if (renderTarget) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001842 return renderTarget->getContext();
1843 }
1844 }
1845#endif
1846
halcanary96fcdcc2015-08-27 07:41:13 -07001847 return nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001848
1849}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001850
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001851void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1852 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001853 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001854 if (outer.isEmpty()) {
1855 return;
1856 }
1857 if (inner.isEmpty()) {
1858 this->drawRRect(outer, paint);
1859 return;
1860 }
1861
1862 // We don't have this method (yet), but technically this is what we should
1863 // be able to assert...
1864 // SkASSERT(outer.contains(inner));
1865 //
1866 // For now at least check for containment of bounds
1867 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1868
1869 this->onDrawDRRect(outer, inner, paint);
1870}
1871
reed41af9662015-01-05 07:49:08 -08001872// These need to stop being virtual -- clients need to override the onDraw... versions
1873
1874void SkCanvas::drawPaint(const SkPaint& paint) {
1875 this->onDrawPaint(paint);
1876}
1877
1878void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1879 this->onDrawRect(r, paint);
1880}
1881
1882void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1883 this->onDrawOval(r, paint);
1884}
1885
1886void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1887 this->onDrawRRect(rrect, paint);
1888}
1889
1890void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1891 this->onDrawPoints(mode, count, pts, paint);
1892}
1893
1894void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
1895 const SkPoint texs[], const SkColor colors[], SkXfermode* xmode,
1896 const uint16_t indices[], int indexCount, const SkPaint& paint) {
1897 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
1898 indices, indexCount, paint);
1899}
1900
1901void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1902 this->onDrawPath(path, paint);
1903}
1904
reeda85d4d02015-05-06 12:56:48 -07001905void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001906 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001907 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001908}
1909
reede47829b2015-08-06 10:02:53 -07001910void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1911 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001912 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001913 if (dst.isEmpty() || src.isEmpty()) {
1914 return;
1915 }
1916 this->onDrawImageRect(image, &src, dst, paint, constraint);
1917}
reed41af9662015-01-05 07:49:08 -08001918
reed84984ef2015-07-17 07:09:43 -07001919void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1920 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001921 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001922 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001923}
1924
reede47829b2015-08-06 10:02:53 -07001925void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
1926 SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001927 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001928 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
1929 constraint);
1930}
reede47829b2015-08-06 10:02:53 -07001931
reed4c21dc52015-06-25 12:32:03 -07001932void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1933 const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001934 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07001935 if (dst.isEmpty()) {
1936 return;
1937 }
1938 if (!SkNinePatchIter::Valid(image->width(), image->height(), center)) {
reede47829b2015-08-06 10:02:53 -07001939 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001940 }
1941 this->onDrawImageNine(image, center, dst, paint);
1942}
1943
reed41af9662015-01-05 07:49:08 -08001944void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001945 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001946 return;
1947 }
reed41af9662015-01-05 07:49:08 -08001948 this->onDrawBitmap(bitmap, dx, dy, paint);
1949}
1950
reede47829b2015-08-06 10:02:53 -07001951void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07001952 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001953 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07001954 return;
1955 }
reede47829b2015-08-06 10:02:53 -07001956 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08001957}
1958
reed84984ef2015-07-17 07:09:43 -07001959void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
1960 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001961 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001962}
1963
reede47829b2015-08-06 10:02:53 -07001964void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
1965 SrcRectConstraint constraint) {
1966 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
1967 constraint);
1968}
reede47829b2015-08-06 10:02:53 -07001969
reed41af9662015-01-05 07:49:08 -08001970void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
1971 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001972 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001973 return;
1974 }
reed4c21dc52015-06-25 12:32:03 -07001975 if (!SkNinePatchIter::Valid(bitmap.width(), bitmap.height(), center)) {
reeda5517e22015-07-14 10:54:12 -07001976 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001977 }
reed41af9662015-01-05 07:49:08 -08001978 this->onDrawBitmapNine(bitmap, center, dst, paint);
1979}
1980
reed71c3c762015-06-24 10:29:17 -07001981void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
1982 const SkColor colors[], int count, SkXfermode::Mode mode,
1983 const SkRect* cull, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001984 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07001985 if (count <= 0) {
1986 return;
1987 }
1988 SkASSERT(atlas);
1989 SkASSERT(xform);
1990 SkASSERT(tex);
1991 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
1992}
1993
reedf70b5312016-03-04 16:36:20 -08001994void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
1995 if (key) {
1996 this->onDrawAnnotation(rect, key, value);
1997 }
1998}
1999
reede47829b2015-08-06 10:02:53 -07002000void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2001 const SkPaint* paint, SrcRectConstraint constraint) {
2002 if (src) {
2003 this->drawImageRect(image, *src, dst, paint, constraint);
2004 } else {
2005 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2006 dst, paint, constraint);
2007 }
2008}
2009void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2010 const SkPaint* paint, SrcRectConstraint constraint) {
2011 if (src) {
2012 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2013 } else {
2014 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2015 dst, paint, constraint);
2016 }
2017}
2018
reed@android.com8a1c16f2008-12-17 15:59:43 +00002019//////////////////////////////////////////////////////////////////////////////
2020// These are the virtual drawing methods
2021//////////////////////////////////////////////////////////////////////////////
2022
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002023void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002024 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002025 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2026 }
2027}
2028
reed41af9662015-01-05 07:49:08 -08002029void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002030 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002031 this->internalDrawPaint(paint);
2032}
2033
2034void SkCanvas::internalDrawPaint(const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002035 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002036
2037 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002038 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002039 }
2040
reed@google.com4e2b3d32011-04-07 14:18:59 +00002041 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002042}
2043
reed41af9662015-01-05 07:49:08 -08002044void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2045 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002046 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002047 if ((long)count <= 0) {
2048 return;
2049 }
2050
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002051 SkRect r, storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002052 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002053 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002054 // special-case 2 points (common for drawing a single line)
2055 if (2 == count) {
2056 r.set(pts[0], pts[1]);
2057 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00002058 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002059 }
senorblanco87e066e2015-10-28 11:23:36 -07002060 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2061 return;
2062 }
2063 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002064 }
reed@google.coma584aed2012-05-16 14:06:02 +00002065
halcanary96fcdcc2015-08-27 07:41:13 -07002066 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002067
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002068 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002069
reed@android.com8a1c16f2008-12-17 15:59:43 +00002070 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002071 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002072 }
reed@google.com4b226022011-01-11 18:32:13 +00002073
reed@google.com4e2b3d32011-04-07 14:18:59 +00002074 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002075}
2076
reed41af9662015-01-05 07:49:08 -08002077void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002078 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002079 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002080 const SkRect* bounds = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002081 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08002082 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
2083 // To prevent accidental rejecting at this stage, we have to sort it before we check.
2084 SkRect tmp(r);
2085 tmp.sort();
2086
senorblanco87e066e2015-10-28 11:23:36 -07002087 if (this->quickReject(paint.computeFastBounds(tmp, &storage))) {
2088 return;
2089 }
2090 bounds = &r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002091 }
reed@google.com4b226022011-01-11 18:32:13 +00002092
reedc83a2972015-07-16 07:40:45 -07002093 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002094
2095 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002096 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002097 }
2098
reed@google.com4e2b3d32011-04-07 14:18:59 +00002099 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002100}
2101
reed41af9662015-01-05 07:49:08 -08002102void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002103 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002104 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002105 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002106 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002107 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2108 return;
2109 }
2110 bounds = &oval;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002111 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002112
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002113 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002114
2115 while (iter.next()) {
2116 iter.fDevice->drawOval(iter, oval, looper.paint());
2117 }
2118
2119 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002120}
2121
reed41af9662015-01-05 07:49:08 -08002122void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002123 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002124 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002125 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002126 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002127 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2128 return;
2129 }
2130 bounds = &rrect.getBounds();
reed@google.com4ed0fb72012-12-12 20:48:18 +00002131 }
2132
2133 if (rrect.isRect()) {
2134 // call the non-virtual version
2135 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002136 return;
2137 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002138 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002139 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2140 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002141 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002142
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002143 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002144
2145 while (iter.next()) {
2146 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2147 }
2148
2149 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002150}
2151
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002152void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2153 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002154 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002155 const SkRect* bounds = nullptr;
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002156 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002157 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2158 return;
2159 }
2160 bounds = &outer.getBounds();
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002161 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002162
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002163 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002164
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002165 while (iter.next()) {
2166 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2167 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002168
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002169 LOOPER_END
2170}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002171
reed41af9662015-01-05 07:49:08 -08002172void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002173 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002174 if (!path.isFinite()) {
2175 return;
2176 }
2177
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002178 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002179 const SkRect* bounds = nullptr;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002180 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002181 const SkRect& pathBounds = path.getBounds();
senorblanco87e066e2015-10-28 11:23:36 -07002182 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2183 return;
2184 }
2185 bounds = &pathBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002186 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002187
2188 const SkRect& r = path.getBounds();
2189 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002190 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002191 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002192 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002193 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002194 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002195
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002196 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002197
2198 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002199 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002200 }
2201
reed@google.com4e2b3d32011-04-07 14:18:59 +00002202 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002203}
2204
reed262a71b2015-12-05 13:07:27 -08002205bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002206 if (!paint.getImageFilter()) {
2207 return false;
2208 }
2209
2210 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002211 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002212 return false;
2213 }
2214
2215 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2216 // Once we can filter and the filter will return a result larger than itself, we should be
2217 // able to remove this constraint.
2218 // skbug.com/4526
2219 //
2220 SkPoint pt;
2221 ctm.mapXY(x, y, &pt);
2222 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2223 return ir.contains(fMCRec->fRasterClip.getBounds());
2224}
2225
reeda85d4d02015-05-06 12:56:48 -07002226void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002227 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002228 SkRect bounds = SkRect::MakeXYWH(x, y,
2229 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002230 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002231 SkRect tmp = bounds;
2232 if (paint) {
2233 paint->computeFastBounds(tmp, &tmp);
2234 }
2235 if (this->quickReject(tmp)) {
2236 return;
2237 }
reeda85d4d02015-05-06 12:56:48 -07002238 }
2239
2240 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002241 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002242 paint = lazy.init();
2243 }
reed262a71b2015-12-05 13:07:27 -08002244
reed129ed1c2016-02-22 06:42:31 -08002245 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2246 *paint);
2247 if (drawAsSprite && paint->getImageFilter()) {
2248 SkBitmap bitmap;
2249 if (!as_IB(image)->asBitmapForImageFilters(&bitmap)) {
2250 drawAsSprite = false;
2251 } else{
2252 // Until imagefilters are updated, they cannot handle any src type but N32...
2253 if (bitmap.info().colorType() != kN32_SkColorType || bitmap.info().isSRGB()) {
2254 drawAsSprite = false;
2255 }
2256 }
2257 }
2258
reed262a71b2015-12-05 13:07:27 -08002259 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
2260
reeda85d4d02015-05-06 12:56:48 -07002261 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002262 const SkPaint& pnt = looper.paint();
2263 if (drawAsSprite && pnt.getImageFilter()) {
2264 SkBitmap bitmap;
2265 if (as_IB(image)->asBitmapForImageFilters(&bitmap)) {
2266 SkPoint pt;
2267 iter.fMatrix->mapXY(x, y, &pt);
2268 iter.fDevice->drawBitmapAsSprite(iter, bitmap,
2269 SkScalarRoundToInt(pt.fX),
2270 SkScalarRoundToInt(pt.fY), pnt);
2271 }
2272 } else {
2273 iter.fDevice->drawImage(iter, image, x, y, pnt);
2274 }
reeda85d4d02015-05-06 12:56:48 -07002275 }
2276
2277 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002278}
2279
reed41af9662015-01-05 07:49:08 -08002280void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002281 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002282 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
halcanary96fcdcc2015-08-27 07:41:13 -07002283 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002284 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002285 if (paint) {
2286 paint->computeFastBounds(dst, &storage);
2287 }
2288 if (this->quickReject(storage)) {
2289 return;
2290 }
reeda85d4d02015-05-06 12:56:48 -07002291 }
2292 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002293 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002294 paint = lazy.init();
2295 }
2296
senorblancoc41e7e12015-12-07 12:51:30 -08002297 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002298 image->isOpaque())
reeda85d4d02015-05-06 12:56:48 -07002299
2300 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002301 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002302 }
2303
2304 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002305}
2306
reed41af9662015-01-05 07:49:08 -08002307void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002308 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002309 SkDEBUGCODE(bitmap.validate();)
2310
reed33366972015-10-08 09:22:02 -07002311 if (bitmap.drawsNothing()) {
2312 return;
2313 }
2314
2315 SkLazyPaint lazy;
2316 if (nullptr == paint) {
2317 paint = lazy.init();
2318 }
2319
2320 const SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2321
2322 SkRect storage;
2323 const SkRect* bounds = nullptr;
2324 if (paint->canComputeFastBounds()) {
2325 bitmap.getBounds(&storage);
2326 matrix.mapRect(&storage);
senorblanco87e066e2015-10-28 11:23:36 -07002327 SkRect tmp = storage;
2328 if (this->quickReject(paint->computeFastBounds(tmp, &tmp))) {
2329 return;
2330 }
2331 bounds = &storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002332 }
reed@google.com4b226022011-01-11 18:32:13 +00002333
reed129ed1c2016-02-22 06:42:31 -08002334 bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(), bitmap.height(),
2335 *paint);
2336 if (drawAsSprite && paint->getImageFilter()) {
2337 // Until imagefilters are updated, they cannot handle any src type but N32...
2338 if (bitmap.info().colorType() != kN32_SkColorType || bitmap.info().isSRGB()) {
2339 drawAsSprite = false;
2340 }
2341 }
2342
reed262a71b2015-12-05 13:07:27 -08002343 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds)
reed33366972015-10-08 09:22:02 -07002344
2345 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002346 const SkPaint& pnt = looper.paint();
2347 if (drawAsSprite && pnt.getImageFilter()) {
2348 SkPoint pt;
2349 iter.fMatrix->mapXY(x, y, &pt);
2350 iter.fDevice->drawBitmapAsSprite(iter, bitmap,
2351 SkScalarRoundToInt(pt.fX),
2352 SkScalarRoundToInt(pt.fY), pnt);
2353 } else {
2354 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
2355 }
reed33366972015-10-08 09:22:02 -07002356 }
reed262a71b2015-12-05 13:07:27 -08002357
reed33366972015-10-08 09:22:02 -07002358 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002359}
2360
reed@google.com9987ec32011-09-07 11:57:52 +00002361// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002362void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002363 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002364 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002365 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002366 return;
2367 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002368
halcanary96fcdcc2015-08-27 07:41:13 -07002369 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002370 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002371 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2372 return;
2373 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002374 }
reed@google.com3d608122011-11-21 15:16:16 +00002375
reed@google.com33535f32012-09-25 15:37:50 +00002376 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002377 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002378 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002379 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002380
senorblancoc41e7e12015-12-07 12:51:30 -08002381 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002382 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002383
reed@google.com33535f32012-09-25 15:37:50 +00002384 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002385 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002386 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002387
reed@google.com33535f32012-09-25 15:37:50 +00002388 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002389}
2390
reed41af9662015-01-05 07:49:08 -08002391void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002392 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002393 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002394 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002395 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002396}
2397
reed4c21dc52015-06-25 12:32:03 -07002398void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2399 const SkPaint* paint) {
2400 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
2401
halcanary96fcdcc2015-08-27 07:41:13 -07002402 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002403 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002404 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2405 return;
2406 }
reed@google.com3d608122011-11-21 15:16:16 +00002407 }
reed4c21dc52015-06-25 12:32:03 -07002408
2409 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002410 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002411 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002412 }
reed4c21dc52015-06-25 12:32:03 -07002413
senorblancoc41e7e12015-12-07 12:51:30 -08002414 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
reed4c21dc52015-06-25 12:32:03 -07002415
2416 while (iter.next()) {
2417 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002418 }
reed4c21dc52015-06-25 12:32:03 -07002419
2420 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002421}
2422
reed41af9662015-01-05 07:49:08 -08002423void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2424 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002425 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002426 SkDEBUGCODE(bitmap.validate();)
2427
halcanary96fcdcc2015-08-27 07:41:13 -07002428 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002429 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002430 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2431 return;
2432 }
reed4c21dc52015-06-25 12:32:03 -07002433 }
2434
2435 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002436 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002437 paint = lazy.init();
2438 }
2439
senorblancoc41e7e12015-12-07 12:51:30 -08002440 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
reed4c21dc52015-06-25 12:32:03 -07002441
2442 while (iter.next()) {
2443 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2444 }
2445
2446 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002447}
2448
reed@google.comf67e4cf2011-03-15 20:56:58 +00002449class SkDeviceFilteredPaint {
2450public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002451 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002452 uint32_t filteredFlags = device->filterTextFlags(paint);
2453 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002454 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002455 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002456 fPaint = newPaint;
2457 } else {
2458 fPaint = &paint;
2459 }
2460 }
2461
reed@google.comf67e4cf2011-03-15 20:56:58 +00002462 const SkPaint& paint() const { return *fPaint; }
2463
2464private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002465 const SkPaint* fPaint;
2466 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002467};
2468
bungeman@google.com52c748b2011-08-22 21:30:43 +00002469void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2470 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002471 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002472 draw.fDevice->drawRect(draw, r, paint);
2473 } else {
2474 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002475 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002476 draw.fDevice->drawRect(draw, r, p);
2477 }
2478}
2479
2480void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2481 const char text[], size_t byteLength,
2482 SkScalar x, SkScalar y) {
halcanary96fcdcc2015-08-27 07:41:13 -07002483 SkASSERT(byteLength == 0 || text != nullptr);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002484
2485 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07002486 if (text == nullptr || byteLength == 0 ||
bungeman@google.com52c748b2011-08-22 21:30:43 +00002487 draw.fClip->isEmpty() ||
halcanary96fcdcc2015-08-27 07:41:13 -07002488 (paint.getAlpha() == 0 && paint.getXfermode() == nullptr)) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002489 return;
2490 }
2491
2492 SkScalar width = 0;
2493 SkPoint start;
2494
2495 start.set(0, 0); // to avoid warning
2496 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2497 SkPaint::kStrikeThruText_Flag)) {
2498 width = paint.measureText(text, byteLength);
2499
2500 SkScalar offsetX = 0;
2501 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2502 offsetX = SkScalarHalf(width);
2503 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2504 offsetX = width;
2505 }
2506 start.set(x - offsetX, y);
2507 }
2508
2509 if (0 == width) {
2510 return;
2511 }
2512
2513 uint32_t flags = paint.getFlags();
2514
2515 if (flags & (SkPaint::kUnderlineText_Flag |
2516 SkPaint::kStrikeThruText_Flag)) {
2517 SkScalar textSize = paint.getTextSize();
2518 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2519 SkRect r;
2520
2521 r.fLeft = start.fX;
2522 r.fRight = start.fX + width;
2523
2524 if (flags & SkPaint::kUnderlineText_Flag) {
2525 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2526 start.fY);
2527 r.fTop = offset;
2528 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002529 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002530 }
2531 if (flags & SkPaint::kStrikeThruText_Flag) {
2532 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2533 start.fY);
2534 r.fTop = offset;
2535 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002536 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002537 }
2538 }
2539}
2540
reed@google.come0d9ce82014-04-23 04:00:17 +00002541void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2542 const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002543 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002544
2545 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002546 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002547 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002548 DrawTextDecorations(iter, dfp.paint(),
2549 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002550 }
2551
reed@google.com4e2b3d32011-04-07 14:18:59 +00002552 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002553}
2554
reed@google.come0d9ce82014-04-23 04:00:17 +00002555void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2556 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002557 SkPoint textOffset = SkPoint::Make(0, 0);
2558
halcanary96fcdcc2015-08-27 07:41:13 -07002559 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002560
reed@android.com8a1c16f2008-12-17 15:59:43 +00002561 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002562 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002563 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002564 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002565 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002566
reed@google.com4e2b3d32011-04-07 14:18:59 +00002567 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002568}
2569
reed@google.come0d9ce82014-04-23 04:00:17 +00002570void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2571 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002572
2573 SkPoint textOffset = SkPoint::Make(0, constY);
2574
halcanary96fcdcc2015-08-27 07:41:13 -07002575 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002576
reed@android.com8a1c16f2008-12-17 15:59:43 +00002577 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002578 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002579 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002580 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002581 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002582
reed@google.com4e2b3d32011-04-07 14:18:59 +00002583 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002584}
2585
reed@google.come0d9ce82014-04-23 04:00:17 +00002586void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2587 const SkMatrix* matrix, const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002588 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002589
reed@android.com8a1c16f2008-12-17 15:59:43 +00002590 while (iter.next()) {
2591 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002592 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002593 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002594
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002595 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002596}
2597
fmalita00d5c2c2014-08-21 08:53:26 -07002598void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2599 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002600
fmalita85d5eb92015-03-04 11:20:12 -08002601 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002602 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002603 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002604 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002605 SkRect tmp;
2606 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2607 return;
2608 }
2609 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002610 }
2611
fmalita024f9962015-03-03 19:08:17 -08002612 // We cannot filter in the looper as we normally do, because the paint is
2613 // incomplete at this point (text-related attributes are embedded within blob run paints).
2614 SkDrawFilter* drawFilter = fMCRec->fFilter;
halcanary96fcdcc2015-08-27 07:41:13 -07002615 fMCRec->fFilter = nullptr;
fmalita024f9962015-03-03 19:08:17 -08002616
fmalita85d5eb92015-03-04 11:20:12 -08002617 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002618
fmalitaaa1b9122014-08-28 14:32:24 -07002619 while (iter.next()) {
2620 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002621 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002622 }
2623
fmalitaaa1b9122014-08-28 14:32:24 -07002624 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002625
2626 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002627}
2628
reed@google.come0d9ce82014-04-23 04:00:17 +00002629// These will become non-virtual, so they always call the (virtual) onDraw... method
2630void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2631 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002632 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002633 this->onDrawText(text, byteLength, x, y, paint);
2634}
2635void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2636 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002637 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002638 this->onDrawPosText(text, byteLength, pos, paint);
2639}
2640void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2641 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002642 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002643 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2644}
2645void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2646 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002647 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002648 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2649}
fmalita00d5c2c2014-08-21 08:53:26 -07002650void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2651 const SkPaint& paint) {
reede3b38ce2016-01-08 09:18:44 -08002652 RETURN_ON_NULL(blob);
danakj9881d632014-11-26 12:41:06 -08002653 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
reede3b38ce2016-01-08 09:18:44 -08002654 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002655}
reed@google.come0d9ce82014-04-23 04:00:17 +00002656
reed41af9662015-01-05 07:49:08 -08002657void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2658 const SkPoint verts[], const SkPoint texs[],
2659 const SkColor colors[], SkXfermode* xmode,
2660 const uint16_t indices[], int indexCount,
2661 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002662 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
halcanary96fcdcc2015-08-27 07:41:13 -07002663 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
reed@google.com4b226022011-01-11 18:32:13 +00002664
reed@android.com8a1c16f2008-12-17 15:59:43 +00002665 while (iter.next()) {
2666 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002667 colors, xmode, indices, indexCount,
2668 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002669 }
reed@google.com4b226022011-01-11 18:32:13 +00002670
reed@google.com4e2b3d32011-04-07 14:18:59 +00002671 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002672}
2673
dandovb3c9d1c2014-08-12 08:34:29 -07002674void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2675 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002676 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
halcanary96fcdcc2015-08-27 07:41:13 -07002677 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002678 return;
2679 }
mtklein6cfa73a2014-08-13 13:33:49 -07002680
dandovecfff212014-08-04 10:02:00 -07002681 // Since a patch is always within the convex hull of the control points, we discard it when its
2682 // bounding rectangle is completely outside the current clip.
2683 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002684 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002685 if (this->quickReject(bounds)) {
2686 return;
2687 }
mtklein6cfa73a2014-08-13 13:33:49 -07002688
dandovb3c9d1c2014-08-12 08:34:29 -07002689 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2690}
2691
2692void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2693 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2694
halcanary96fcdcc2015-08-27 07:41:13 -07002695 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002696
dandovecfff212014-08-04 10:02:00 -07002697 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002698 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002699 }
mtklein6cfa73a2014-08-13 13:33:49 -07002700
dandovecfff212014-08-04 10:02:00 -07002701 LOOPER_END
2702}
2703
reeda8db7282015-07-07 10:22:31 -07002704void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
reede3b38ce2016-01-08 09:18:44 -08002705 RETURN_ON_NULL(dr);
2706 if (x || y) {
2707 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2708 this->onDrawDrawable(dr, &matrix);
2709 } else {
2710 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002711 }
2712}
2713
reeda8db7282015-07-07 10:22:31 -07002714void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reede3b38ce2016-01-08 09:18:44 -08002715 RETURN_ON_NULL(dr);
2716 if (matrix && matrix->isIdentity()) {
2717 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002718 }
reede3b38ce2016-01-08 09:18:44 -08002719 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002720}
2721
2722void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2723 SkRect bounds = dr->getBounds();
2724 if (matrix) {
2725 matrix->mapRect(&bounds);
2726 }
2727 if (this->quickReject(bounds)) {
2728 return;
2729 }
2730 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002731}
2732
reed71c3c762015-06-24 10:29:17 -07002733void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2734 const SkColor colors[], int count, SkXfermode::Mode mode,
2735 const SkRect* cull, const SkPaint* paint) {
2736 if (cull && this->quickReject(*cull)) {
2737 return;
2738 }
2739
2740 SkPaint pnt;
2741 if (paint) {
2742 pnt = *paint;
2743 }
2744
halcanary96fcdcc2015-08-27 07:41:13 -07002745 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr)
reed71c3c762015-06-24 10:29:17 -07002746 while (iter.next()) {
2747 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, mode, pnt);
2748 }
2749 LOOPER_END
2750}
2751
reedf70b5312016-03-04 16:36:20 -08002752void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2753 SkASSERT(key);
2754
2755 SkPaint paint;
2756 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, nullptr)
2757 while (iter.next()) {
2758 iter.fDevice->drawAnnotation(iter, rect, key, value);
2759 }
2760 LOOPER_END
2761}
2762
reed@android.com8a1c16f2008-12-17 15:59:43 +00002763//////////////////////////////////////////////////////////////////////////////
2764// These methods are NOT virtual, and therefore must call back into virtual
2765// methods, rather than actually drawing themselves.
2766//////////////////////////////////////////////////////////////////////////////
2767
2768void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002769 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002770 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002771 SkPaint paint;
2772
2773 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002774 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002775 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002776 }
2777 this->drawPaint(paint);
2778}
2779
reed@android.com845fdac2009-06-23 03:01:32 +00002780void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002781 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002782 SkPaint paint;
2783
2784 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002785 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002786 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002787 }
2788 this->drawPaint(paint);
2789}
2790
2791void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002792 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002793 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002794
reed@android.com8a1c16f2008-12-17 15:59:43 +00002795 pt.set(x, y);
2796 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2797}
2798
2799void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002800 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002801 SkPoint pt;
2802 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002803
reed@android.com8a1c16f2008-12-17 15:59:43 +00002804 pt.set(x, y);
2805 paint.setColor(color);
2806 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2807}
2808
2809void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2810 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002811 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002812 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002813
reed@android.com8a1c16f2008-12-17 15:59:43 +00002814 pts[0].set(x0, y0);
2815 pts[1].set(x1, y1);
2816 this->drawPoints(kLines_PointMode, 2, pts, paint);
2817}
2818
2819void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2820 SkScalar right, SkScalar bottom,
2821 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002822 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002823 SkRect r;
2824
2825 r.set(left, top, right, bottom);
2826 this->drawRect(r, paint);
2827}
2828
2829void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2830 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002831 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002832 if (radius < 0) {
2833 radius = 0;
2834 }
2835
2836 SkRect r;
2837 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002838 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002839}
2840
2841void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2842 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002843 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002844 if (rx > 0 && ry > 0) {
2845 if (paint.canComputeFastBounds()) {
2846 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002847 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002848 return;
2849 }
2850 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002851 SkRRect rrect;
2852 rrect.setRectXY(r, rx, ry);
2853 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002854 } else {
2855 this->drawRect(r, paint);
2856 }
2857}
2858
reed@android.com8a1c16f2008-12-17 15:59:43 +00002859void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2860 SkScalar sweepAngle, bool useCenter,
2861 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002862 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002863 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2864 this->drawOval(oval, paint);
2865 } else {
2866 SkPath path;
2867 if (useCenter) {
2868 path.moveTo(oval.centerX(), oval.centerY());
2869 }
2870 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2871 if (useCenter) {
2872 path.close();
2873 }
2874 this->drawPath(path, paint);
2875 }
2876}
2877
2878void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2879 const SkPath& path, SkScalar hOffset,
2880 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002881 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002882 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002883
reed@android.com8a1c16f2008-12-17 15:59:43 +00002884 matrix.setTranslate(hOffset, vOffset);
2885 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2886}
2887
reed@android.comf76bacf2009-05-13 14:00:33 +00002888///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07002889
2890/**
2891 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2892 * against the playback cost of recursing into the subpicture to get at its actual ops.
2893 *
2894 * For now we pick a conservatively small value, though measurement (and other heuristics like
2895 * the type of ops contained) may justify changing this value.
2896 */
2897#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07002898
reedd5fa1a42014-08-09 11:08:05 -07002899void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002900 RETURN_ON_NULL(picture);
2901
reed1c2c4412015-04-30 13:09:24 -07002902 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
reede3b38ce2016-01-08 09:18:44 -08002903 if (matrix && matrix->isIdentity()) {
2904 matrix = nullptr;
2905 }
2906 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2907 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2908 picture->playback(this);
2909 } else {
2910 this->onDrawPicture(picture, matrix, paint);
reedd5fa1a42014-08-09 11:08:05 -07002911 }
2912}
robertphillips9b14f262014-06-04 05:40:44 -07002913
reedd5fa1a42014-08-09 11:08:05 -07002914void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2915 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07002916 if (!paint || paint->canComputeFastBounds()) {
2917 SkRect bounds = picture->cullRect();
2918 if (paint) {
2919 paint->computeFastBounds(bounds, &bounds);
2920 }
2921 if (matrix) {
2922 matrix->mapRect(&bounds);
2923 }
2924 if (this->quickReject(bounds)) {
2925 return;
2926 }
2927 }
2928
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002929 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002930 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002931 // Canvas has to first give the device the opportunity to render
2932 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002933 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002934 return; // the device has rendered the entire picture
2935 }
2936 }
2937
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002938 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07002939 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002940}
2941
reed@android.com8a1c16f2008-12-17 15:59:43 +00002942///////////////////////////////////////////////////////////////////////////////
2943///////////////////////////////////////////////////////////////////////////////
2944
2945SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
bungeman99fe8222015-08-20 07:57:51 -07002946 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002947
2948 SkASSERT(canvas);
2949
2950 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2951 fDone = !fImpl->next();
2952}
2953
2954SkCanvas::LayerIter::~LayerIter() {
2955 fImpl->~SkDrawIter();
2956}
2957
2958void SkCanvas::LayerIter::next() {
2959 fDone = !fImpl->next();
2960}
2961
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002962SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002963 return fImpl->getDevice();
2964}
2965
2966const SkMatrix& SkCanvas::LayerIter::matrix() const {
2967 return fImpl->getMatrix();
2968}
2969
2970const SkPaint& SkCanvas::LayerIter::paint() const {
2971 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07002972 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002973 paint = &fDefaultPaint;
2974 }
2975 return *paint;
2976}
2977
2978const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2979int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2980int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002981
2982///////////////////////////////////////////////////////////////////////////////
2983
fmalitac3b589a2014-06-05 12:40:07 -07002984SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002985
2986///////////////////////////////////////////////////////////////////////////////
2987
2988static bool supported_for_raster_canvas(const SkImageInfo& info) {
2989 switch (info.alphaType()) {
2990 case kPremul_SkAlphaType:
2991 case kOpaque_SkAlphaType:
2992 break;
2993 default:
2994 return false;
2995 }
2996
2997 switch (info.colorType()) {
2998 case kAlpha_8_SkColorType:
2999 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00003000 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003001 break;
3002 default:
3003 return false;
3004 }
3005
3006 return true;
3007}
3008
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003009SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
3010 if (!supported_for_raster_canvas(info)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003011 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003012 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003013
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003014 SkBitmap bitmap;
3015 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003016 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003017 }
halcanary385fe4d2015-08-26 13:07:48 -07003018 return new SkCanvas(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003019}
reedd5fa1a42014-08-09 11:08:05 -07003020
3021///////////////////////////////////////////////////////////////////////////////
3022
3023SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003024 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07003025 : fCanvas(canvas)
3026 , fSaveCount(canvas->getSaveCount())
3027{
bsalomon49f085d2014-09-05 13:34:00 -07003028 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003029 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07003030 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003031 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07003032 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003033 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07003034 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003035 canvas->save();
3036 }
mtklein6cfa73a2014-08-13 13:33:49 -07003037
bsalomon49f085d2014-09-05 13:34:00 -07003038 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003039 canvas->concat(*matrix);
3040 }
3041}
3042
3043SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
3044 fCanvas->restoreToCount(fSaveCount);
3045}