blob: de69c368cd23ea53a69dcc575d5b796a5960b38b [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());
reedcfb6bdf2016-03-29 11:32:50 -0700484 tmp.setXfermode(sk_ref_sp(fPaint->getXfermode()));
senorblanco87e066e2015-10-28 11:23:36 -0700485 SkRect storage;
486 if (rawBounds) {
487 // Make rawBounds include all paint outsets except for those due to image filters.
488 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
489 }
reedbfd5f172016-01-07 11:28:08 -0800490 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &tmp),
reed76033be2015-03-14 10:54:31 -0700491 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700492 fTempLayerForImageFilter = true;
493 // we remove the imagefilter/xfermode inside doNext()
reed@google.com8926b162012-03-23 15:36:36 +0000494 }
495
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000496 if (SkDrawLooper* looper = paint.getLooper()) {
497 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
498 looper->contextSize());
499 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000500 fIsSimple = false;
501 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700502 fLooperContext = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000503 // can we be marked as simple?
reed5c476fb2015-04-20 08:04:21 -0700504 fIsSimple = !fFilter && !fTempLayerForImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000505 }
piotaixrb5fae932014-09-24 13:03:30 -0700506
reed4a8126e2014-09-22 07:29:03 -0700507 uint32_t oldFlags = paint.getFlags();
508 fNewPaintFlags = filter_paint_flags(props, oldFlags);
509 if (fIsSimple && (fNewPaintFlags != oldFlags)) {
reeddbc3cef2015-04-29 12:18:57 -0700510 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700511 paint->setFlags(fNewPaintFlags);
512 fPaint = paint;
513 // if we're not simple, doNext() will take care of calling setFlags()
514 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000515 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000516
reed@android.com8a1c16f2008-12-17 15:59:43 +0000517 ~AutoDrawLooper() {
reed5c476fb2015-04-20 08:04:21 -0700518 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000519 fCanvas->internalRestore();
520 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000521 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000522 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000523
reed@google.com4e2b3d32011-04-07 14:18:59 +0000524 const SkPaint& paint() const {
525 SkASSERT(fPaint);
526 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000527 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000528
reed@google.com129ec222012-05-15 13:24:09 +0000529 bool next(SkDrawFilter::Type drawType) {
530 if (fDone) {
531 return false;
532 } else if (fIsSimple) {
533 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000534 return !fPaint->nothingToDraw();
535 } else {
536 return this->doNext(drawType);
537 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000538 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000539
reed@android.com8a1c16f2008-12-17 15:59:43 +0000540private:
reeddbc3cef2015-04-29 12:18:57 -0700541 SkLazyPaint fLazyPaintInit; // base paint storage in case we need to modify it
542 SkLazyPaint fLazyPaintPerLooper; // per-draw-looper storage, so the looper can modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000543 SkCanvas* fCanvas;
544 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000545 SkDrawFilter* fFilter;
546 const SkPaint* fPaint;
547 int fSaveCount;
reed4a8126e2014-09-22 07:29:03 -0700548 uint32_t fNewPaintFlags;
reed5c476fb2015-04-20 08:04:21 -0700549 bool fTempLayerForImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000550 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000551 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000552 SkDrawLooper::Context* fLooperContext;
553 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000554
555 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000556};
557
reed@google.com129ec222012-05-15 13:24:09 +0000558bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
halcanary96fcdcc2015-08-27 07:41:13 -0700559 fPaint = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000560 SkASSERT(!fIsSimple);
reed5c476fb2015-04-20 08:04:21 -0700561 SkASSERT(fLooperContext || fFilter || fTempLayerForImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000562
reeddbc3cef2015-04-29 12:18:57 -0700563 SkPaint* paint = fLazyPaintPerLooper.set(fLazyPaintInit.isValid() ?
564 *fLazyPaintInit.get() : fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700565 paint->setFlags(fNewPaintFlags);
reed@google.com129ec222012-05-15 13:24:09 +0000566
reed5c476fb2015-04-20 08:04:21 -0700567 if (fTempLayerForImageFilter) {
halcanary96fcdcc2015-08-27 07:41:13 -0700568 paint->setImageFilter(nullptr);
569 paint->setXfermode(nullptr);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000570 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000571
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000572 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000573 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000574 return false;
575 }
576 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000577 if (!fFilter->filter(paint, drawType)) {
578 fDone = true;
579 return false;
580 }
halcanary96fcdcc2015-08-27 07:41:13 -0700581 if (nullptr == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000582 // no looper means we only draw once
583 fDone = true;
584 }
585 }
586 fPaint = paint;
587
588 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000589 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000590 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000591 }
592
593 // call this after any possible paint modifiers
594 if (fPaint->nothingToDraw()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700595 fPaint = nullptr;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000596 return false;
597 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000598 return true;
599}
600
reed@android.com8a1c16f2008-12-17 15:59:43 +0000601////////// macros to place around the internal draw calls //////////////////
602
reed262a71b2015-12-05 13:07:27 -0800603#define LOOPER_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
604 this->predrawNotify(); \
605 AutoDrawLooper looper(this, fProps, paint, skipLayerForFilter, bounds); \
606 while (looper.next(SkDrawFilter::kBitmap_Type)) { \
607 SkDrawIter iter(this);
608
609
reed@google.com8926b162012-03-23 15:36:36 +0000610#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000611 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700612 AutoDrawLooper looper(this, fProps, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000613 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000614 SkDrawIter iter(this);
615
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000616#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000617 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700618 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000619 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000620 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000621
reedc83a2972015-07-16 07:40:45 -0700622#define LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, type, bounds, auxOpaque) \
623 this->predrawNotify(bounds, &paint, auxOpaque); \
624 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
625 while (looper.next(type)) { \
626 SkDrawIter iter(this);
627
reed@google.com4e2b3d32011-04-07 14:18:59 +0000628#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000629
630////////////////////////////////////////////////////////////////////////////
631
mtkleinfeaadee2015-04-08 11:25:48 -0700632void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
633 this->restoreToCount(1);
634 fCachedLocalClipBounds.setEmpty();
635 fCachedLocalClipBoundsDirty = true;
636 fClipStack->reset();
637 fMCRec->reset(bounds);
638
639 // We're peering through a lot of structs here. Only at this scope do we
640 // know that the device is an SkBitmapDevice (really an SkNoPixelsBitmapDevice).
641 static_cast<SkBitmapDevice*>(fMCRec->fLayer->fDevice)->setNewSize(bounds.size());
642}
643
reedd9544982014-09-09 18:46:22 -0700644SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
reed42b73eb2015-11-20 13:42:42 -0800645 if (device && device->forceConservativeRasterClip()) {
646 flags = InitFlags(flags | kConservativeRasterClip_InitFlag);
647 }
648 // Since init() is only called once by our constructors, it is safe to perform this
649 // const-cast.
650 *const_cast<bool*>(&fConservativeRasterClip) = SkToBool(flags & kConservativeRasterClip_InitFlag);
651
reed@google.comc0784db2013-12-13 21:16:12 +0000652 fCachedLocalClipBounds.setEmpty();
653 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000654 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000655 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700656 fDeviceCMDirty = true;
reed2ff1fce2014-12-11 07:07:37 -0800657 fSaveCount = 1;
halcanary96fcdcc2015-08-27 07:41:13 -0700658 fMetaData = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000659
halcanary385fe4d2015-08-26 13:07:48 -0700660 fClipStack.reset(new SkClipStack);
reed687fa1c2015-04-07 08:00:56 -0700661
reed@android.com8a1c16f2008-12-17 15:59:43 +0000662 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700663 new (fMCRec) MCRec(fConservativeRasterClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000664
reeda499f902015-05-01 09:34:31 -0700665 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
666 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
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
1164 SkPaint p;
robertphillips372177e2016-03-30 07:32:28 -07001165 p.setImageFilter(filter->makeWithLocalMatrix(ctm));
reedbfd5f172016-01-07 11:28:08 -08001166 const SkScalar x = SkIntToScalar(src->getOrigin().x());
1167 const SkScalar y = SkIntToScalar(src->getOrigin().y());
1168 c.drawBitmap(srcBM, x, y, &p);
robertphillips7354a4b2015-12-16 05:08:27 -08001169}
reed70ee31b2015-12-10 13:44:45 -08001170
reed129ed1c2016-02-22 06:42:31 -08001171static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, bool isOpaque,
1172 const SkPaint* paint) {
1173 // need to force L32 for now if we have an image filter. Once filters support other colortypes
1174 // e.g. sRGB or F16, we can remove this check
1175 const bool hasImageFilter = paint && paint->getImageFilter();
1176
1177 SkAlphaType alphaType = isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
1178 if ((prev.bytesPerPixel() < 4) || hasImageFilter) {
1179 // force to L32
1180 return SkImageInfo::MakeN32(w, h, alphaType);
1181 } else {
1182 // keep the same characteristics as the prev
1183 return SkImageInfo::Make(w, h, prev.colorType(), alphaType, prev.profileType());
1184 }
1185}
1186
reed4960eee2015-12-18 07:09:18 -08001187void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
1188 const SkRect* bounds = rec.fBounds;
1189 const SkPaint* paint = rec.fPaint;
1190 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1191
reed@google.comb93ba452014-03-10 19:47:58 +00001192#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001193 saveLayerFlags &= ~kDontClipToLayer_PrivateSaveLayerFlag;
reed@google.comb93ba452014-03-10 19:47:58 +00001194#endif
1195
junov@chromium.orga907ac32012-02-24 21:54:07 +00001196 // do this before we create the layer. We don't call the public save() since
1197 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001198 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001199
1200 fDeviceCMDirty = true;
1201
1202 SkIRect ir;
reed4960eee2015-12-18 07:09:18 -08001203 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, paint ? paint->getImageFilter() : nullptr)) {
reed2ff1fce2014-12-11 07:07:37 -08001204 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001205 }
1206
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001207 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1208 // the clipRectBounds() call above?
1209 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001210 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001211 }
1212
reed4960eee2015-12-18 07:09:18 -08001213 bool isOpaque = SkToBool(saveLayerFlags & kIsOpaque_SaveLayerFlag);
reed8dc0ccb2015-03-20 06:32:52 -07001214 SkPixelGeometry geo = fProps.pixelGeometry();
1215 if (paint) {
reed76033be2015-03-14 10:54:31 -07001216 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001217 if (paint->getImageFilter() || paint->getColorFilter()) {
reed76033be2015-03-14 10:54:31 -07001218 isOpaque = false;
reed8dc0ccb2015-03-20 06:32:52 -07001219 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001220 }
1221 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001222
reedb2db8982014-11-13 12:41:02 -08001223 SkBaseDevice* device = this->getTopDevice();
halcanary96fcdcc2015-08-27 07:41:13 -07001224 if (nullptr == device) {
reedb2db8982014-11-13 12:41:02 -08001225 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001226 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001227 }
reedb2db8982014-11-13 12:41:02 -08001228
reed129ed1c2016-02-22 06:42:31 -08001229 SkImageInfo info = make_layer_info(device->imageInfo(), ir.width(), ir.height(), isOpaque,
1230 paint);
1231
reed61f501f2015-04-29 08:34:00 -07001232 bool forceSpriteOnRestore = false;
1233 {
reed70ee31b2015-12-10 13:44:45 -08001234 const bool preserveLCDText = kOpaque_SkAlphaType == info.alphaType() ||
reed4960eee2015-12-18 07:09:18 -08001235 (saveLayerFlags & kPreserveLCDText_SaveLayerFlag);
reeddaa57bf2015-05-15 10:39:17 -07001236 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
reed70ee31b2015-12-10 13:44:45 -08001237 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
1238 preserveLCDText, false);
reed61f501f2015-04-29 08:34:00 -07001239 SkBaseDevice* newDev = device->onCreateDevice(createInfo, paint);
halcanary96fcdcc2015-08-27 07:41:13 -07001240 if (nullptr == newDev) {
reed61f501f2015-04-29 08:34:00 -07001241 // If onCreateDevice didn't succeed, try raster (e.g. PDF couldn't handle the paint)
robertphillips9a53fd72015-06-22 09:46:59 -07001242 const SkSurfaceProps surfaceProps(fProps.flags(), createInfo.fPixelGeometry);
1243 newDev = SkBitmapDevice::Create(createInfo.fInfo, surfaceProps);
halcanary96fcdcc2015-08-27 07:41:13 -07001244 if (nullptr == newDev) {
reed61f501f2015-04-29 08:34:00 -07001245 SkErrorInternals::SetError(kInternalError_SkError,
1246 "Unable to create device for layer.");
1247 return;
1248 }
1249 forceSpriteOnRestore = true;
1250 }
1251 device = newDev;
bungeman@google.come25c6842011-08-17 14:53:54 +00001252 }
reed@google.com6f8f2922011-03-04 22:27:10 +00001253 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips7354a4b2015-12-16 05:08:27 -08001254
reedbfd5f172016-01-07 11:28:08 -08001255 if (rec.fBackdrop) {
1256 draw_filter_into_device(fMCRec->fTopLayer->fDevice, rec.fBackdrop, device, fMCRec->fMatrix);
robertphillips7354a4b2015-12-16 05:08:27 -08001257 }
1258
halcanary385fe4d2015-08-26 13:07:48 -07001259 DeviceCM* layer =
1260 new DeviceCM(device, paint, this, fConservativeRasterClip, forceSpriteOnRestore);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001261 device->unref();
1262
1263 layer->fNext = fMCRec->fTopLayer;
1264 fMCRec->fLayer = layer;
1265 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reed@android.com8a1c16f2008-12-17 15:59:43 +00001266}
1267
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001268int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001269 if (0xFF == alpha) {
1270 return this->saveLayer(bounds, nullptr);
1271 } else {
1272 SkPaint tmpPaint;
1273 tmpPaint.setAlpha(alpha);
1274 return this->saveLayer(bounds, &tmpPaint);
1275 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001276}
1277
reed@android.com8a1c16f2008-12-17 15:59:43 +00001278void SkCanvas::internalRestore() {
1279 SkASSERT(fMCStack.count() != 0);
1280
1281 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001282 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001283
reed687fa1c2015-04-07 08:00:56 -07001284 fClipStack->restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001285
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001286 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001287 DeviceCM* layer = fMCRec->fLayer; // may be null
1288 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001289 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001290
1291 // now do the normal restore()
1292 fMCRec->~MCRec(); // balanced in save()
1293 fMCStack.pop_back();
1294 fMCRec = (MCRec*)fMCStack.back();
1295
1296 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1297 since if we're being recorded, we don't want to record this (the
1298 recorder will have already recorded the restore).
1299 */
bsalomon49f085d2014-09-05 13:34:00 -07001300 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001301 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001302 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001303 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
reed61f501f2015-04-29 08:34:00 -07001304 layer->fPaint, layer->fDeviceIsBitmapDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001305 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001306 fDeviceCMDirty = true;
halcanary385fe4d2015-08-26 13:07:48 -07001307 delete layer;
reedb679ca82015-04-07 04:40:48 -07001308 } else {
1309 // we're at the root
reeda499f902015-05-01 09:34:31 -07001310 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001311 layer->~DeviceCM();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001312 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001313 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001314}
1315
reede8f30622016-03-23 18:59:25 -07001316sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001317 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001318 props = &fProps;
1319 }
1320 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001321}
1322
reede8f30622016-03-23 18:59:25 -07001323sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001324 SkBaseDevice* dev = this->getDevice();
reede8f30622016-03-23 18:59:25 -07001325 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001326}
1327
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001328SkImageInfo SkCanvas::imageInfo() const {
1329 SkBaseDevice* dev = this->getDevice();
1330 if (dev) {
1331 return dev->imageInfo();
1332 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001333 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001334 }
1335}
1336
brianosman898235c2016-04-06 07:38:23 -07001337bool SkCanvas::getProps(SkSurfaceProps* props) const {
1338 SkBaseDevice* dev = this->getDevice();
1339 if (dev) {
1340 if (props) {
1341 *props = fProps;
1342 }
1343 return true;
1344 } else {
1345 return false;
1346 }
1347}
1348
reed6ceeebd2016-03-09 14:26:26 -08001349#ifdef SK_SUPPORT_LEGACY_PEEKPIXELS_PARMS
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001350const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
reed884e97c2015-05-26 11:31:54 -07001351 SkPixmap pmap;
reed6ceeebd2016-03-09 14:26:26 -08001352 if (this->peekPixels(&pmap)) {
1353 if (info) {
1354 *info = pmap.info();
1355 }
1356 if (rowBytes) {
1357 *rowBytes = pmap.rowBytes();
1358 }
1359 return pmap.addr();
reed884e97c2015-05-26 11:31:54 -07001360 }
reed6ceeebd2016-03-09 14:26:26 -08001361 return nullptr;
1362}
1363#endif
1364
1365bool SkCanvas::peekPixels(SkPixmap* pmap) {
1366 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001367}
1368
reed884e97c2015-05-26 11:31:54 -07001369bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001370 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001371 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001372}
1373
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001374void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001375 SkPixmap pmap;
1376 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001377 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001378 }
1379 if (info) {
1380 *info = pmap.info();
1381 }
1382 if (rowBytes) {
1383 *rowBytes = pmap.rowBytes();
1384 }
1385 if (origin) {
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001386 *origin = this->getTopDevice(false)->getOrigin();
1387 }
reed884e97c2015-05-26 11:31:54 -07001388 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001389}
1390
reed884e97c2015-05-26 11:31:54 -07001391bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001392 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001393 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001394}
1395
reed@android.com8a1c16f2008-12-17 15:59:43 +00001396/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001397
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001398void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed61f501f2015-04-29 08:34:00 -07001399 const SkPaint* paint, bool deviceIsBitmapDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001400 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001401 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001402 paint = &tmp;
1403 }
reed@google.com4b226022011-01-11 18:32:13 +00001404
reed@google.com8926b162012-03-23 15:36:36 +00001405 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001406 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001407 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001408 paint = &looper.paint();
1409 SkImageFilter* filter = paint->getImageFilter();
1410 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001411 if (filter && !dstDev->canHandleImageFilter(filter)) {
reed88d064d2015-10-12 11:30:02 -07001412 SkImageFilter::DeviceProxy proxy(dstDev);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001413 SkIPoint offset = SkIPoint::Make(0, 0);
robertphillips4418dba2016-03-07 12:45:14 -08001414 const SkBitmap& srcBM = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001415 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001416 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
robertphillips4418dba2016-03-07 12:45:14 -08001417 const SkIRect clipBounds = iter.fClip->getBounds().makeOffset(-pos.x(), -pos.y());
senorblancobe129b22014-08-08 07:14:35 -07001418 SkAutoTUnref<SkImageFilter::Cache> cache(dstDev->getImageFilterCache());
reed4e23cda2016-01-11 10:56:59 -08001419 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
robertphillips4418dba2016-03-07 12:45:14 -08001420
brianosman898235c2016-04-06 07:38:23 -07001421 sk_sp<SkSpecialImage> srcImg(SkSpecialImage::internal_fromBM(&proxy, srcBM,
1422 &dstDev->surfaceProps()));
robertphillips4418dba2016-03-07 12:45:14 -08001423 if (!srcImg) {
1424 continue; // something disastrous happened
1425 }
1426
robertphillips2302de92016-03-24 07:26:32 -07001427 sk_sp<SkSpecialImage> resultImg(filter->filterImage(srcImg.get(), ctx, &offset));
robertphillips4418dba2016-03-07 12:45:14 -08001428 if (resultImg) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001429 SkPaint tmpUnfiltered(*paint);
halcanary96fcdcc2015-08-27 07:41:13 -07001430 tmpUnfiltered.setImageFilter(nullptr);
robertphillips4418dba2016-03-07 12:45:14 -08001431 SkBitmap resultBM;
1432 if (resultImg->internal_getBM(&resultBM)) {
1433 // TODO: add drawSprite(SkSpecialImage) to SkDevice? (see skbug.com/5073)
1434 dstDev->drawSprite(iter, resultBM, pos.x() + offset.x(), pos.y() + offset.y(),
1435 tmpUnfiltered);
1436 }
reed@google.com76dd2772012-01-05 21:15:07 +00001437 }
reed61f501f2015-04-29 08:34:00 -07001438 } else if (deviceIsBitmapDevice) {
reed9572a102015-05-26 19:22:17 -07001439 const SkBitmap& src = static_cast<SkBitmapDevice*>(srcDev)->fBitmap;
reed61f501f2015-04-29 08:34:00 -07001440 dstDev->drawSprite(iter, src, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001441 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001442 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001443 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001444 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001445 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001446}
1447
reed32704672015-12-16 08:27:10 -08001448/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001449
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001450void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001451 SkMatrix m;
1452 m.setTranslate(dx, dy);
1453 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001454}
1455
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001456void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001457 SkMatrix m;
1458 m.setScale(sx, sy);
1459 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001460}
1461
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001462void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001463 SkMatrix m;
1464 m.setRotate(degrees);
1465 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001466}
1467
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001468void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001469 SkMatrix m;
1470 m.setSkew(sx, sy);
1471 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001472}
1473
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001474void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001475 if (matrix.isIdentity()) {
1476 return;
1477 }
1478
reed2ff1fce2014-12-11 07:07:37 -08001479 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001480 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001481 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001482 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001483
1484 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001485}
1486
reed86a17e72015-05-14 12:25:22 -07001487void SkCanvas::setMatrix(const SkMatrix& matrix) {
1488 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001489 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001490 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001491 fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001492 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001493}
1494
reed@android.com8a1c16f2008-12-17 15:59:43 +00001495void SkCanvas::resetMatrix() {
1496 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001497
reed@android.com8a1c16f2008-12-17 15:59:43 +00001498 matrix.reset();
1499 this->setMatrix(matrix);
1500}
1501
1502//////////////////////////////////////////////////////////////////////////////
1503
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001504void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001505 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001506 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1507 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001508}
1509
1510void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001511#ifdef SK_ENABLE_CLIP_QUICKREJECT
1512 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001513 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001514 return false;
1515 }
1516
reed@google.com3b3e8952012-08-16 20:53:31 +00001517 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001518 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001519 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001520
reed687fa1c2015-04-07 08:00:56 -07001521 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001522 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001523 }
1524 }
1525#endif
1526
bsalomonac8cabd2015-11-20 18:53:07 -08001527 if (!fAllowSoftClip) {
1528 edgeStyle = kHard_ClipEdgeStyle;
1529 }
reed90ba0952015-11-20 13:42:47 -08001530
reedc64eff52015-11-21 12:39:45 -08001531 const bool rectStaysRect = fMCRec->fMatrix.rectStaysRect();
1532 SkRect devR;
1533 if (rectStaysRect) {
1534 fMCRec->fMatrix.mapRect(&devR, rect);
1535 }
bsalomonac8cabd2015-11-20 18:53:07 -08001536
reedc64eff52015-11-21 12:39:45 -08001537 // Check if we can quick-accept the clip call (and do nothing)
1538 //
1539 // TODO: investigate if a (conservative) version of this could be done in ::clipRect,
1540 // so that subclasses (like PictureRecording) didn't see unnecessary clips, which in turn
1541 // might allow lazy save/restores to eliminate entire save/restore blocks.
1542 //
1543 if (SkRegion::kIntersect_Op == op &&
1544 kHard_ClipEdgeStyle == edgeStyle
1545 && rectStaysRect)
1546 {
1547 if (devR.round().contains(fMCRec->fRasterClip.getBounds())) {
1548#if 0
1549 SkDebugf("------- ignored clipRect [%g %g %g %g]\n",
1550 rect.left(), rect.top(), rect.right(), rect.bottom());
1551#endif
1552 return;
1553 }
1554 }
1555
1556 AutoValidateClip avc(this);
1557
1558 fDeviceCMDirty = true;
1559 fCachedLocalClipBoundsDirty = true;
1560
1561 if (rectStaysRect) {
1562 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1563 fClipStack->clipDevRect(devR, op, isAA);
senorblancoafc7cce2016-02-02 18:44:15 -08001564 fMCRec->fRasterClip.op(devR, this->getTopLayerBounds(), op, isAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001565 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001566 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001567 // and clip against that, since it can handle any matrix. However, to
1568 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1569 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001570 SkPath path;
1571
1572 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001573 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001574 }
1575}
1576
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001577void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001578 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001579 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001580 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001581 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1582 } else {
1583 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001584 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001585}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001586
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001587void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001588 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001589 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001590 AutoValidateClip avc(this);
1591
1592 fDeviceCMDirty = true;
1593 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001594 if (!fAllowSoftClip) {
1595 edgeStyle = kHard_ClipEdgeStyle;
1596 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001597
reed687fa1c2015-04-07 08:00:56 -07001598 fClipStack->clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001599
senorblancoafc7cce2016-02-02 18:44:15 -08001600 fMCRec->fRasterClip.op(transformedRRect, this->getTopLayerBounds(), op,
robertphillips125f19a2015-11-23 09:00:05 -08001601 kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001602 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001603 }
1604
1605 SkPath path;
1606 path.addRRect(rrect);
1607 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001608 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001609}
1610
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001611void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001612 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001613 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001614
1615 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1616 SkRect r;
1617 if (path.isRect(&r)) {
1618 this->onClipRect(r, op, edgeStyle);
1619 return;
1620 }
1621 SkRRect rrect;
1622 if (path.isOval(&r)) {
1623 rrect.setOval(r);
1624 this->onClipRRect(rrect, op, edgeStyle);
1625 return;
1626 }
1627 if (path.isRRect(&rrect)) {
1628 this->onClipRRect(rrect, op, edgeStyle);
1629 return;
1630 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001631 }
robertphillips39f05382015-11-24 09:30:12 -08001632
1633 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001634}
1635
1636void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001637#ifdef SK_ENABLE_CLIP_QUICKREJECT
1638 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001639 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001640 return false;
1641 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001642
reed@google.com3b3e8952012-08-16 20:53:31 +00001643 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001644 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001645 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001646
reed687fa1c2015-04-07 08:00:56 -07001647 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001648 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001649 }
1650 }
1651#endif
1652
reed@google.com5c3d1472011-02-22 19:12:23 +00001653 AutoValidateClip avc(this);
1654
reed@android.com8a1c16f2008-12-17 15:59:43 +00001655 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001656 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001657 if (!fAllowSoftClip) {
1658 edgeStyle = kHard_ClipEdgeStyle;
1659 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001660
1661 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001662 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001663
reed@google.comfe701122011-11-08 19:41:23 +00001664 // Check if the transfomation, or the original path itself
1665 // made us empty. Note this can also happen if we contained NaN
1666 // values. computing the bounds detects this, and will set our
1667 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1668 if (devPath.getBounds().isEmpty()) {
1669 // resetting the path will remove any NaN or other wanky values
1670 // that might upset our scan converter.
1671 devPath.reset();
1672 }
1673
reed@google.com5c3d1472011-02-22 19:12:23 +00001674 // if we called path.swap() we could avoid a deep copy of this path
reed687fa1c2015-04-07 08:00:56 -07001675 fClipStack->clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001676
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001677 if (fAllowSimplifyClip) {
fmalita1a481fe2015-02-04 07:39:34 -08001678 bool clipIsAA = getClipStack()->asPath(&devPath);
1679 if (clipIsAA) {
1680 edgeStyle = kSoft_ClipEdgeStyle;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001681 }
fmalita1a481fe2015-02-04 07:39:34 -08001682
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001683 op = SkRegion::kReplace_Op;
1684 }
1685
senorblancoafc7cce2016-02-02 18:44:15 -08001686 fMCRec->fRasterClip.op(devPath, this->getTopLayerBounds(), op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001687}
1688
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001689void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed2ff1fce2014-12-11 07:07:37 -08001690 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001691 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001692}
1693
1694void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001695 AutoValidateClip avc(this);
1696
reed@android.com8a1c16f2008-12-17 15:59:43 +00001697 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001698 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001699
reed@google.com5c3d1472011-02-22 19:12:23 +00001700 // todo: signal fClipStack that we have a region, and therefore (I guess)
1701 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001702 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001703
reed1f836ee2014-07-07 07:49:34 -07001704 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001705}
1706
reed@google.com819c9212011-02-23 18:56:55 +00001707#ifdef SK_DEBUG
1708void SkCanvas::validateClip() const {
1709 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001710 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001711 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001712 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001713 return;
1714 }
1715
reed@google.com819c9212011-02-23 18:56:55 +00001716 SkIRect ir;
1717 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001718 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001719
reed687fa1c2015-04-07 08:00:56 -07001720 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001721 const SkClipStack::Element* element;
halcanary96fcdcc2015-08-27 07:41:13 -07001722 while ((element = iter.next()) != nullptr) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001723 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001724 case SkClipStack::Element::kRect_Type:
1725 element->getRect().round(&ir);
1726 tmpClip.op(ir, element->getOp());
1727 break;
1728 case SkClipStack::Element::kEmpty_Type:
1729 tmpClip.setEmpty();
1730 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001731 default: {
1732 SkPath path;
1733 element->asPath(&path);
senorblancoafc7cce2016-02-02 18:44:15 -08001734 tmpClip.op(path, this->getTopLayerBounds(), element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001735 break;
1736 }
reed@google.com819c9212011-02-23 18:56:55 +00001737 }
1738 }
reed@google.com819c9212011-02-23 18:56:55 +00001739}
1740#endif
1741
reed@google.com90c07ea2012-04-13 13:50:27 +00001742void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001743 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001744 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001745
halcanary96fcdcc2015-08-27 07:41:13 -07001746 while ((element = iter.next()) != nullptr) {
fmalitac3b589a2014-06-05 12:40:07 -07001747 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001748 }
1749}
1750
reed@google.com5c3d1472011-02-22 19:12:23 +00001751///////////////////////////////////////////////////////////////////////////////
1752
reed@google.com754de5f2014-02-24 19:38:20 +00001753bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001754 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001755}
1756
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001757bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001758 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001759}
1760
reed@google.com3b3e8952012-08-16 20:53:31 +00001761bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001762 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001763 return true;
1764
reed1f836ee2014-07-07 07:49:34 -07001765 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001766 return true;
1767 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001768
reed1f836ee2014-07-07 07:49:34 -07001769 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001770 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001771 fMCRec->fMatrix.mapRect(&dst, rect);
reedb07a94f2014-11-19 05:03:18 -08001772 return !SkIRect::Intersects(dst.roundOut(), fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001773 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001774 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001775
reed@android.coma380ae42009-07-21 01:17:02 +00001776 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001777 // TODO: should we use | instead, or compare all 4 at once?
1778 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001779 return true;
1780 }
reed@google.comc0784db2013-12-13 21:16:12 +00001781 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001782 return true;
1783 }
1784 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001785 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001786}
1787
reed@google.com3b3e8952012-08-16 20:53:31 +00001788bool SkCanvas::quickReject(const SkPath& path) const {
1789 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001790}
1791
reed@google.com3b3e8952012-08-16 20:53:31 +00001792bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001793 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001794 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001795 return false;
1796 }
1797
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001798 SkMatrix inverse;
1799 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001800 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001801 if (bounds) {
1802 bounds->setEmpty();
1803 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001804 return false;
1805 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001806
bsalomon49f085d2014-09-05 13:34:00 -07001807 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001808 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001809 // adjust it outwards in case we are antialiasing
1810 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001811
reed@google.com8f4d2302013-12-17 16:44:46 +00001812 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1813 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001814 inverse.mapRect(bounds, r);
1815 }
1816 return true;
1817}
1818
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001819bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001820 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001821 if (clip.isEmpty()) {
1822 if (bounds) {
1823 bounds->setEmpty();
1824 }
1825 return false;
1826 }
1827
bsalomon49f085d2014-09-05 13:34:00 -07001828 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001829 *bounds = clip.getBounds();
1830 }
1831 return true;
1832}
1833
reed@android.com8a1c16f2008-12-17 15:59:43 +00001834const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001835 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001836}
1837
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001838const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001839 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001840}
1841
reed@google.com9c135db2014-03-12 18:28:35 +00001842GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1843 SkBaseDevice* dev = this->getTopDevice();
halcanary96fcdcc2015-08-27 07:41:13 -07001844 return dev ? dev->accessRenderTarget() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001845}
1846
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001847GrContext* SkCanvas::getGrContext() {
1848#if SK_SUPPORT_GPU
1849 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07001850 if (device) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001851 GrRenderTarget* renderTarget = device->accessRenderTarget();
bsalomon49f085d2014-09-05 13:34:00 -07001852 if (renderTarget) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001853 return renderTarget->getContext();
1854 }
1855 }
1856#endif
1857
halcanary96fcdcc2015-08-27 07:41:13 -07001858 return nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001859
1860}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001861
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001862void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1863 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001864 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001865 if (outer.isEmpty()) {
1866 return;
1867 }
1868 if (inner.isEmpty()) {
1869 this->drawRRect(outer, paint);
1870 return;
1871 }
1872
1873 // We don't have this method (yet), but technically this is what we should
1874 // be able to assert...
1875 // SkASSERT(outer.contains(inner));
1876 //
1877 // For now at least check for containment of bounds
1878 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1879
1880 this->onDrawDRRect(outer, inner, paint);
1881}
1882
reed41af9662015-01-05 07:49:08 -08001883// These need to stop being virtual -- clients need to override the onDraw... versions
1884
1885void SkCanvas::drawPaint(const SkPaint& paint) {
1886 this->onDrawPaint(paint);
1887}
1888
1889void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1890 this->onDrawRect(r, paint);
1891}
1892
1893void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1894 this->onDrawOval(r, paint);
1895}
1896
1897void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1898 this->onDrawRRect(rrect, paint);
1899}
1900
1901void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1902 this->onDrawPoints(mode, count, pts, paint);
1903}
1904
1905void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
1906 const SkPoint texs[], const SkColor colors[], SkXfermode* xmode,
1907 const uint16_t indices[], int indexCount, const SkPaint& paint) {
1908 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
1909 indices, indexCount, paint);
1910}
1911
1912void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1913 this->onDrawPath(path, paint);
1914}
1915
reeda85d4d02015-05-06 12:56:48 -07001916void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001917 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001918 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001919}
1920
reede47829b2015-08-06 10:02:53 -07001921void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1922 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001923 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001924 if (dst.isEmpty() || src.isEmpty()) {
1925 return;
1926 }
1927 this->onDrawImageRect(image, &src, dst, paint, constraint);
1928}
reed41af9662015-01-05 07:49:08 -08001929
reed84984ef2015-07-17 07:09:43 -07001930void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1931 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001932 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001933 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001934}
1935
reede47829b2015-08-06 10:02:53 -07001936void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
1937 SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001938 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001939 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
1940 constraint);
1941}
reede47829b2015-08-06 10:02:53 -07001942
reed4c21dc52015-06-25 12:32:03 -07001943void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1944 const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001945 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07001946 if (dst.isEmpty()) {
1947 return;
1948 }
1949 if (!SkNinePatchIter::Valid(image->width(), image->height(), center)) {
reede47829b2015-08-06 10:02:53 -07001950 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001951 }
1952 this->onDrawImageNine(image, center, dst, paint);
1953}
1954
reed41af9662015-01-05 07:49:08 -08001955void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001956 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001957 return;
1958 }
reed41af9662015-01-05 07:49:08 -08001959 this->onDrawBitmap(bitmap, dx, dy, paint);
1960}
1961
reede47829b2015-08-06 10:02:53 -07001962void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07001963 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001964 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07001965 return;
1966 }
reede47829b2015-08-06 10:02:53 -07001967 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08001968}
1969
reed84984ef2015-07-17 07:09:43 -07001970void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
1971 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001972 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001973}
1974
reede47829b2015-08-06 10:02:53 -07001975void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
1976 SrcRectConstraint constraint) {
1977 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
1978 constraint);
1979}
reede47829b2015-08-06 10:02:53 -07001980
reed41af9662015-01-05 07:49:08 -08001981void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
1982 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001983 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001984 return;
1985 }
reed4c21dc52015-06-25 12:32:03 -07001986 if (!SkNinePatchIter::Valid(bitmap.width(), bitmap.height(), center)) {
reeda5517e22015-07-14 10:54:12 -07001987 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001988 }
reed41af9662015-01-05 07:49:08 -08001989 this->onDrawBitmapNine(bitmap, center, dst, paint);
1990}
1991
reed71c3c762015-06-24 10:29:17 -07001992void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
1993 const SkColor colors[], int count, SkXfermode::Mode mode,
1994 const SkRect* cull, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001995 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07001996 if (count <= 0) {
1997 return;
1998 }
1999 SkASSERT(atlas);
2000 SkASSERT(xform);
2001 SkASSERT(tex);
2002 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
2003}
2004
reedf70b5312016-03-04 16:36:20 -08002005void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2006 if (key) {
2007 this->onDrawAnnotation(rect, key, value);
2008 }
2009}
2010
reede47829b2015-08-06 10:02:53 -07002011void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2012 const SkPaint* paint, SrcRectConstraint constraint) {
2013 if (src) {
2014 this->drawImageRect(image, *src, dst, paint, constraint);
2015 } else {
2016 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2017 dst, paint, constraint);
2018 }
2019}
2020void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2021 const SkPaint* paint, SrcRectConstraint constraint) {
2022 if (src) {
2023 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2024 } else {
2025 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2026 dst, paint, constraint);
2027 }
2028}
2029
reed@android.com8a1c16f2008-12-17 15:59:43 +00002030//////////////////////////////////////////////////////////////////////////////
2031// These are the virtual drawing methods
2032//////////////////////////////////////////////////////////////////////////////
2033
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002034void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002035 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002036 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2037 }
2038}
2039
reed41af9662015-01-05 07:49:08 -08002040void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002041 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002042 this->internalDrawPaint(paint);
2043}
2044
2045void SkCanvas::internalDrawPaint(const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002046 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002047
2048 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002049 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002050 }
2051
reed@google.com4e2b3d32011-04-07 14:18:59 +00002052 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002053}
2054
reed41af9662015-01-05 07:49:08 -08002055void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2056 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002057 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002058 if ((long)count <= 0) {
2059 return;
2060 }
2061
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002062 SkRect r, storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002063 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002064 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002065 // special-case 2 points (common for drawing a single line)
2066 if (2 == count) {
2067 r.set(pts[0], pts[1]);
2068 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00002069 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002070 }
senorblanco87e066e2015-10-28 11:23:36 -07002071 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2072 return;
2073 }
2074 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002075 }
reed@google.coma584aed2012-05-16 14:06:02 +00002076
halcanary96fcdcc2015-08-27 07:41:13 -07002077 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002078
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002079 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002080
reed@android.com8a1c16f2008-12-17 15:59:43 +00002081 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002082 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002083 }
reed@google.com4b226022011-01-11 18:32:13 +00002084
reed@google.com4e2b3d32011-04-07 14:18:59 +00002085 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002086}
2087
reed41af9662015-01-05 07:49:08 -08002088void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002089 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002090 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002091 const SkRect* bounds = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002092 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08002093 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
2094 // To prevent accidental rejecting at this stage, we have to sort it before we check.
2095 SkRect tmp(r);
2096 tmp.sort();
2097
senorblanco87e066e2015-10-28 11:23:36 -07002098 if (this->quickReject(paint.computeFastBounds(tmp, &storage))) {
2099 return;
2100 }
2101 bounds = &r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002102 }
reed@google.com4b226022011-01-11 18:32:13 +00002103
reedc83a2972015-07-16 07:40:45 -07002104 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002105
2106 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002107 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002108 }
2109
reed@google.com4e2b3d32011-04-07 14:18:59 +00002110 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002111}
2112
reed41af9662015-01-05 07:49:08 -08002113void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002114 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002115 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002116 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002117 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002118 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2119 return;
2120 }
2121 bounds = &oval;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002122 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002123
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002124 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002125
2126 while (iter.next()) {
2127 iter.fDevice->drawOval(iter, oval, looper.paint());
2128 }
2129
2130 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002131}
2132
reed41af9662015-01-05 07:49:08 -08002133void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002134 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002135 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002136 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002137 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002138 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2139 return;
2140 }
2141 bounds = &rrect.getBounds();
reed@google.com4ed0fb72012-12-12 20:48:18 +00002142 }
2143
2144 if (rrect.isRect()) {
2145 // call the non-virtual version
2146 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002147 return;
2148 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002149 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002150 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2151 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002152 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002153
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002154 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002155
2156 while (iter.next()) {
2157 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2158 }
2159
2160 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002161}
2162
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002163void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2164 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002165 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002166 const SkRect* bounds = nullptr;
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002167 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002168 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2169 return;
2170 }
2171 bounds = &outer.getBounds();
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002172 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002173
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002174 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002175
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002176 while (iter.next()) {
2177 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2178 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002179
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002180 LOOPER_END
2181}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002182
reed41af9662015-01-05 07:49:08 -08002183void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002184 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002185 if (!path.isFinite()) {
2186 return;
2187 }
2188
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002189 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002190 const SkRect* bounds = nullptr;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002191 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002192 const SkRect& pathBounds = path.getBounds();
senorblanco87e066e2015-10-28 11:23:36 -07002193 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2194 return;
2195 }
2196 bounds = &pathBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002197 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002198
2199 const SkRect& r = path.getBounds();
2200 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002201 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002202 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002203 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002204 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002205 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002206
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002207 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002208
2209 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002210 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002211 }
2212
reed@google.com4e2b3d32011-04-07 14:18:59 +00002213 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002214}
2215
reed262a71b2015-12-05 13:07:27 -08002216bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002217 if (!paint.getImageFilter()) {
2218 return false;
2219 }
2220
2221 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002222 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002223 return false;
2224 }
2225
2226 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2227 // Once we can filter and the filter will return a result larger than itself, we should be
2228 // able to remove this constraint.
2229 // skbug.com/4526
2230 //
2231 SkPoint pt;
2232 ctm.mapXY(x, y, &pt);
2233 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2234 return ir.contains(fMCRec->fRasterClip.getBounds());
2235}
2236
reeda85d4d02015-05-06 12:56:48 -07002237void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002238 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002239 SkRect bounds = SkRect::MakeXYWH(x, y,
2240 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002241 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002242 SkRect tmp = bounds;
2243 if (paint) {
2244 paint->computeFastBounds(tmp, &tmp);
2245 }
2246 if (this->quickReject(tmp)) {
2247 return;
2248 }
reeda85d4d02015-05-06 12:56:48 -07002249 }
halcanary9d524f22016-03-29 09:03:52 -07002250
reeda85d4d02015-05-06 12:56:48 -07002251 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002252 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002253 paint = lazy.init();
2254 }
reed262a71b2015-12-05 13:07:27 -08002255
reed129ed1c2016-02-22 06:42:31 -08002256 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2257 *paint);
2258 if (drawAsSprite && paint->getImageFilter()) {
2259 SkBitmap bitmap;
2260 if (!as_IB(image)->asBitmapForImageFilters(&bitmap)) {
2261 drawAsSprite = false;
2262 } else{
2263 // Until imagefilters are updated, they cannot handle any src type but N32...
2264 if (bitmap.info().colorType() != kN32_SkColorType || bitmap.info().isSRGB()) {
2265 drawAsSprite = false;
2266 }
2267 }
2268 }
2269
reed262a71b2015-12-05 13:07:27 -08002270 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
2271
reeda85d4d02015-05-06 12:56:48 -07002272 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002273 const SkPaint& pnt = looper.paint();
2274 if (drawAsSprite && pnt.getImageFilter()) {
2275 SkBitmap bitmap;
2276 if (as_IB(image)->asBitmapForImageFilters(&bitmap)) {
2277 SkPoint pt;
2278 iter.fMatrix->mapXY(x, y, &pt);
robertphillips2302de92016-03-24 07:26:32 -07002279 iter.fDevice->drawSpriteWithFilter(iter, bitmap,
2280 SkScalarRoundToInt(pt.fX),
2281 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002282 }
2283 } else {
2284 iter.fDevice->drawImage(iter, image, x, y, pnt);
2285 }
reeda85d4d02015-05-06 12:56:48 -07002286 }
halcanary9d524f22016-03-29 09:03:52 -07002287
reeda85d4d02015-05-06 12:56:48 -07002288 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002289}
2290
reed41af9662015-01-05 07:49:08 -08002291void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002292 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002293 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
halcanary96fcdcc2015-08-27 07:41:13 -07002294 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002295 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002296 if (paint) {
2297 paint->computeFastBounds(dst, &storage);
2298 }
2299 if (this->quickReject(storage)) {
2300 return;
2301 }
reeda85d4d02015-05-06 12:56:48 -07002302 }
2303 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002304 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002305 paint = lazy.init();
2306 }
halcanary9d524f22016-03-29 09:03:52 -07002307
senorblancoc41e7e12015-12-07 12:51:30 -08002308 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002309 image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002310
reeda85d4d02015-05-06 12:56:48 -07002311 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002312 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002313 }
halcanary9d524f22016-03-29 09:03:52 -07002314
reeda85d4d02015-05-06 12:56:48 -07002315 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002316}
2317
reed41af9662015-01-05 07:49:08 -08002318void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002319 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002320 SkDEBUGCODE(bitmap.validate();)
2321
reed33366972015-10-08 09:22:02 -07002322 if (bitmap.drawsNothing()) {
2323 return;
2324 }
2325
2326 SkLazyPaint lazy;
2327 if (nullptr == paint) {
2328 paint = lazy.init();
2329 }
2330
2331 const SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2332
2333 SkRect storage;
2334 const SkRect* bounds = nullptr;
2335 if (paint->canComputeFastBounds()) {
2336 bitmap.getBounds(&storage);
2337 matrix.mapRect(&storage);
senorblanco87e066e2015-10-28 11:23:36 -07002338 SkRect tmp = storage;
2339 if (this->quickReject(paint->computeFastBounds(tmp, &tmp))) {
2340 return;
2341 }
2342 bounds = &storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002343 }
reed@google.com4b226022011-01-11 18:32:13 +00002344
reed129ed1c2016-02-22 06:42:31 -08002345 bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(), bitmap.height(),
2346 *paint);
2347 if (drawAsSprite && paint->getImageFilter()) {
2348 // Until imagefilters are updated, they cannot handle any src type but N32...
2349 if (bitmap.info().colorType() != kN32_SkColorType || bitmap.info().isSRGB()) {
2350 drawAsSprite = false;
2351 }
2352 }
2353
reed262a71b2015-12-05 13:07:27 -08002354 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds)
reed33366972015-10-08 09:22:02 -07002355
2356 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002357 const SkPaint& pnt = looper.paint();
2358 if (drawAsSprite && pnt.getImageFilter()) {
2359 SkPoint pt;
2360 iter.fMatrix->mapXY(x, y, &pt);
robertphillips2302de92016-03-24 07:26:32 -07002361 iter.fDevice->drawSpriteWithFilter(iter, bitmap,
2362 SkScalarRoundToInt(pt.fX),
2363 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002364 } else {
2365 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
2366 }
reed33366972015-10-08 09:22:02 -07002367 }
reed262a71b2015-12-05 13:07:27 -08002368
reed33366972015-10-08 09:22:02 -07002369 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002370}
2371
reed@google.com9987ec32011-09-07 11:57:52 +00002372// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002373void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002374 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002375 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002376 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002377 return;
2378 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002379
halcanary96fcdcc2015-08-27 07:41:13 -07002380 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002381 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002382 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2383 return;
2384 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002385 }
reed@google.com3d608122011-11-21 15:16:16 +00002386
reed@google.com33535f32012-09-25 15:37:50 +00002387 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002388 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002389 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002390 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002391
senorblancoc41e7e12015-12-07 12:51:30 -08002392 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002393 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002394
reed@google.com33535f32012-09-25 15:37:50 +00002395 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002396 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002397 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002398
reed@google.com33535f32012-09-25 15:37:50 +00002399 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002400}
2401
reed41af9662015-01-05 07:49:08 -08002402void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002403 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002404 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002405 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002406 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002407}
2408
reed4c21dc52015-06-25 12:32:03 -07002409void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2410 const SkPaint* paint) {
2411 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
halcanary9d524f22016-03-29 09:03:52 -07002412
halcanary96fcdcc2015-08-27 07:41:13 -07002413 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002414 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002415 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2416 return;
2417 }
reed@google.com3d608122011-11-21 15:16:16 +00002418 }
halcanary9d524f22016-03-29 09:03:52 -07002419
reed4c21dc52015-06-25 12:32:03 -07002420 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002421 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002422 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002423 }
halcanary9d524f22016-03-29 09:03:52 -07002424
senorblancoc41e7e12015-12-07 12:51:30 -08002425 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002426
reed4c21dc52015-06-25 12:32:03 -07002427 while (iter.next()) {
2428 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002429 }
halcanary9d524f22016-03-29 09:03:52 -07002430
reed4c21dc52015-06-25 12:32:03 -07002431 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002432}
2433
reed41af9662015-01-05 07:49:08 -08002434void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2435 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002436 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002437 SkDEBUGCODE(bitmap.validate();)
2438
halcanary96fcdcc2015-08-27 07:41:13 -07002439 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002440 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002441 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2442 return;
2443 }
reed4c21dc52015-06-25 12:32:03 -07002444 }
halcanary9d524f22016-03-29 09:03:52 -07002445
reed4c21dc52015-06-25 12:32:03 -07002446 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002447 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002448 paint = lazy.init();
2449 }
halcanary9d524f22016-03-29 09:03:52 -07002450
senorblancoc41e7e12015-12-07 12:51:30 -08002451 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002452
reed4c21dc52015-06-25 12:32:03 -07002453 while (iter.next()) {
2454 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2455 }
halcanary9d524f22016-03-29 09:03:52 -07002456
reed4c21dc52015-06-25 12:32:03 -07002457 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002458}
2459
reed@google.comf67e4cf2011-03-15 20:56:58 +00002460class SkDeviceFilteredPaint {
2461public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002462 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002463 uint32_t filteredFlags = device->filterTextFlags(paint);
2464 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002465 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002466 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002467 fPaint = newPaint;
2468 } else {
2469 fPaint = &paint;
2470 }
2471 }
2472
reed@google.comf67e4cf2011-03-15 20:56:58 +00002473 const SkPaint& paint() const { return *fPaint; }
2474
2475private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002476 const SkPaint* fPaint;
2477 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002478};
2479
bungeman@google.com52c748b2011-08-22 21:30:43 +00002480void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2481 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002482 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002483 draw.fDevice->drawRect(draw, r, paint);
2484 } else {
2485 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002486 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002487 draw.fDevice->drawRect(draw, r, p);
2488 }
2489}
2490
2491void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2492 const char text[], size_t byteLength,
2493 SkScalar x, SkScalar y) {
halcanary96fcdcc2015-08-27 07:41:13 -07002494 SkASSERT(byteLength == 0 || text != nullptr);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002495
2496 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07002497 if (text == nullptr || byteLength == 0 ||
bungeman@google.com52c748b2011-08-22 21:30:43 +00002498 draw.fClip->isEmpty() ||
halcanary96fcdcc2015-08-27 07:41:13 -07002499 (paint.getAlpha() == 0 && paint.getXfermode() == nullptr)) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002500 return;
2501 }
2502
2503 SkScalar width = 0;
2504 SkPoint start;
2505
2506 start.set(0, 0); // to avoid warning
2507 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2508 SkPaint::kStrikeThruText_Flag)) {
2509 width = paint.measureText(text, byteLength);
2510
2511 SkScalar offsetX = 0;
2512 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2513 offsetX = SkScalarHalf(width);
2514 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2515 offsetX = width;
2516 }
2517 start.set(x - offsetX, y);
2518 }
2519
2520 if (0 == width) {
2521 return;
2522 }
2523
2524 uint32_t flags = paint.getFlags();
2525
2526 if (flags & (SkPaint::kUnderlineText_Flag |
2527 SkPaint::kStrikeThruText_Flag)) {
2528 SkScalar textSize = paint.getTextSize();
2529 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2530 SkRect r;
2531
2532 r.fLeft = start.fX;
2533 r.fRight = start.fX + width;
2534
2535 if (flags & SkPaint::kUnderlineText_Flag) {
2536 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2537 start.fY);
2538 r.fTop = offset;
2539 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002540 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002541 }
2542 if (flags & SkPaint::kStrikeThruText_Flag) {
2543 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2544 start.fY);
2545 r.fTop = offset;
2546 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002547 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002548 }
2549 }
2550}
2551
reed@google.come0d9ce82014-04-23 04:00:17 +00002552void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2553 const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002554 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002555
2556 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002557 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002558 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002559 DrawTextDecorations(iter, dfp.paint(),
2560 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002561 }
2562
reed@google.com4e2b3d32011-04-07 14:18:59 +00002563 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002564}
2565
reed@google.come0d9ce82014-04-23 04:00:17 +00002566void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2567 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002568 SkPoint textOffset = SkPoint::Make(0, 0);
2569
halcanary96fcdcc2015-08-27 07:41:13 -07002570 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002571
reed@android.com8a1c16f2008-12-17 15:59:43 +00002572 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002573 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002574 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002575 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002576 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002577
reed@google.com4e2b3d32011-04-07 14:18:59 +00002578 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002579}
2580
reed@google.come0d9ce82014-04-23 04:00:17 +00002581void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2582 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002583
2584 SkPoint textOffset = SkPoint::Make(0, constY);
2585
halcanary96fcdcc2015-08-27 07:41:13 -07002586 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002587
reed@android.com8a1c16f2008-12-17 15:59:43 +00002588 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002589 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002590 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002591 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002592 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002593
reed@google.com4e2b3d32011-04-07 14:18:59 +00002594 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002595}
2596
reed@google.come0d9ce82014-04-23 04:00:17 +00002597void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2598 const SkMatrix* matrix, const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002599 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002600
reed@android.com8a1c16f2008-12-17 15:59:43 +00002601 while (iter.next()) {
2602 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002603 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002604 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002605
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002606 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002607}
2608
fmalita00d5c2c2014-08-21 08:53:26 -07002609void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2610 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002611
fmalita85d5eb92015-03-04 11:20:12 -08002612 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002613 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002614 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002615 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002616 SkRect tmp;
2617 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2618 return;
2619 }
2620 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002621 }
2622
fmalita024f9962015-03-03 19:08:17 -08002623 // We cannot filter in the looper as we normally do, because the paint is
2624 // incomplete at this point (text-related attributes are embedded within blob run paints).
2625 SkDrawFilter* drawFilter = fMCRec->fFilter;
halcanary96fcdcc2015-08-27 07:41:13 -07002626 fMCRec->fFilter = nullptr;
fmalita024f9962015-03-03 19:08:17 -08002627
fmalita85d5eb92015-03-04 11:20:12 -08002628 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002629
fmalitaaa1b9122014-08-28 14:32:24 -07002630 while (iter.next()) {
2631 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002632 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002633 }
2634
fmalitaaa1b9122014-08-28 14:32:24 -07002635 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002636
2637 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002638}
2639
reed@google.come0d9ce82014-04-23 04:00:17 +00002640// These will become non-virtual, so they always call the (virtual) onDraw... method
2641void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2642 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002643 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002644 this->onDrawText(text, byteLength, x, y, paint);
2645}
2646void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2647 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002648 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002649 this->onDrawPosText(text, byteLength, pos, paint);
2650}
2651void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2652 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002653 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002654 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2655}
2656void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2657 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002658 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002659 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2660}
fmalita00d5c2c2014-08-21 08:53:26 -07002661void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2662 const SkPaint& paint) {
reede3b38ce2016-01-08 09:18:44 -08002663 RETURN_ON_NULL(blob);
danakj9881d632014-11-26 12:41:06 -08002664 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
reede3b38ce2016-01-08 09:18:44 -08002665 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002666}
reed@google.come0d9ce82014-04-23 04:00:17 +00002667
reed41af9662015-01-05 07:49:08 -08002668void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2669 const SkPoint verts[], const SkPoint texs[],
2670 const SkColor colors[], SkXfermode* xmode,
2671 const uint16_t indices[], int indexCount,
2672 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002673 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
halcanary96fcdcc2015-08-27 07:41:13 -07002674 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
reed@google.com4b226022011-01-11 18:32:13 +00002675
reed@android.com8a1c16f2008-12-17 15:59:43 +00002676 while (iter.next()) {
2677 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002678 colors, xmode, indices, indexCount,
2679 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002680 }
reed@google.com4b226022011-01-11 18:32:13 +00002681
reed@google.com4e2b3d32011-04-07 14:18:59 +00002682 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002683}
2684
dandovb3c9d1c2014-08-12 08:34:29 -07002685void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2686 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002687 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
halcanary96fcdcc2015-08-27 07:41:13 -07002688 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002689 return;
2690 }
mtklein6cfa73a2014-08-13 13:33:49 -07002691
dandovecfff212014-08-04 10:02:00 -07002692 // Since a patch is always within the convex hull of the control points, we discard it when its
2693 // bounding rectangle is completely outside the current clip.
2694 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002695 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002696 if (this->quickReject(bounds)) {
2697 return;
2698 }
mtklein6cfa73a2014-08-13 13:33:49 -07002699
dandovb3c9d1c2014-08-12 08:34:29 -07002700 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2701}
2702
2703void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2704 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2705
halcanary96fcdcc2015-08-27 07:41:13 -07002706 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002707
dandovecfff212014-08-04 10:02:00 -07002708 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002709 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002710 }
mtklein6cfa73a2014-08-13 13:33:49 -07002711
dandovecfff212014-08-04 10:02:00 -07002712 LOOPER_END
2713}
2714
reeda8db7282015-07-07 10:22:31 -07002715void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
reede3b38ce2016-01-08 09:18:44 -08002716 RETURN_ON_NULL(dr);
2717 if (x || y) {
2718 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2719 this->onDrawDrawable(dr, &matrix);
2720 } else {
2721 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002722 }
2723}
2724
reeda8db7282015-07-07 10:22:31 -07002725void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reede3b38ce2016-01-08 09:18:44 -08002726 RETURN_ON_NULL(dr);
2727 if (matrix && matrix->isIdentity()) {
2728 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002729 }
reede3b38ce2016-01-08 09:18:44 -08002730 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002731}
2732
2733void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2734 SkRect bounds = dr->getBounds();
2735 if (matrix) {
2736 matrix->mapRect(&bounds);
2737 }
2738 if (this->quickReject(bounds)) {
2739 return;
2740 }
2741 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002742}
2743
reed71c3c762015-06-24 10:29:17 -07002744void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2745 const SkColor colors[], int count, SkXfermode::Mode mode,
2746 const SkRect* cull, const SkPaint* paint) {
2747 if (cull && this->quickReject(*cull)) {
2748 return;
2749 }
2750
2751 SkPaint pnt;
2752 if (paint) {
2753 pnt = *paint;
2754 }
halcanary9d524f22016-03-29 09:03:52 -07002755
halcanary96fcdcc2015-08-27 07:41:13 -07002756 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr)
reed71c3c762015-06-24 10:29:17 -07002757 while (iter.next()) {
2758 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, mode, pnt);
2759 }
2760 LOOPER_END
2761}
2762
reedf70b5312016-03-04 16:36:20 -08002763void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2764 SkASSERT(key);
2765
2766 SkPaint paint;
2767 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, nullptr)
2768 while (iter.next()) {
2769 iter.fDevice->drawAnnotation(iter, rect, key, value);
2770 }
2771 LOOPER_END
2772}
2773
reed@android.com8a1c16f2008-12-17 15:59:43 +00002774//////////////////////////////////////////////////////////////////////////////
2775// These methods are NOT virtual, and therefore must call back into virtual
2776// methods, rather than actually drawing themselves.
2777//////////////////////////////////////////////////////////////////////////////
2778
2779void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002780 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002781 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002782 SkPaint paint;
2783
2784 paint.setARGB(a, r, g, b);
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
reed@android.com845fdac2009-06-23 03:01:32 +00002791void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002792 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002793 SkPaint paint;
2794
2795 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002796 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002797 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002798 }
2799 this->drawPaint(paint);
2800}
2801
2802void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002803 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002804 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002805
reed@android.com8a1c16f2008-12-17 15:59:43 +00002806 pt.set(x, y);
2807 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2808}
2809
2810void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002811 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002812 SkPoint pt;
2813 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002814
reed@android.com8a1c16f2008-12-17 15:59:43 +00002815 pt.set(x, y);
2816 paint.setColor(color);
2817 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2818}
2819
2820void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2821 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002822 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002823 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002824
reed@android.com8a1c16f2008-12-17 15:59:43 +00002825 pts[0].set(x0, y0);
2826 pts[1].set(x1, y1);
2827 this->drawPoints(kLines_PointMode, 2, pts, paint);
2828}
2829
2830void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2831 SkScalar right, SkScalar bottom,
2832 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002833 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002834 SkRect r;
2835
2836 r.set(left, top, right, bottom);
2837 this->drawRect(r, paint);
2838}
2839
2840void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2841 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002842 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002843 if (radius < 0) {
2844 radius = 0;
2845 }
2846
2847 SkRect r;
2848 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002849 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002850}
2851
2852void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2853 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002854 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002855 if (rx > 0 && ry > 0) {
2856 if (paint.canComputeFastBounds()) {
2857 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002858 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002859 return;
2860 }
2861 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002862 SkRRect rrect;
2863 rrect.setRectXY(r, rx, ry);
2864 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002865 } else {
2866 this->drawRect(r, paint);
2867 }
2868}
2869
reed@android.com8a1c16f2008-12-17 15:59:43 +00002870void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2871 SkScalar sweepAngle, bool useCenter,
2872 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002873 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002874 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2875 this->drawOval(oval, paint);
2876 } else {
2877 SkPath path;
2878 if (useCenter) {
2879 path.moveTo(oval.centerX(), oval.centerY());
2880 }
2881 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2882 if (useCenter) {
2883 path.close();
2884 }
2885 this->drawPath(path, paint);
2886 }
2887}
2888
2889void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2890 const SkPath& path, SkScalar hOffset,
2891 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002892 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002893 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002894
reed@android.com8a1c16f2008-12-17 15:59:43 +00002895 matrix.setTranslate(hOffset, vOffset);
2896 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2897}
2898
reed@android.comf76bacf2009-05-13 14:00:33 +00002899///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07002900
2901/**
2902 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2903 * against the playback cost of recursing into the subpicture to get at its actual ops.
2904 *
2905 * For now we pick a conservatively small value, though measurement (and other heuristics like
2906 * the type of ops contained) may justify changing this value.
2907 */
2908#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07002909
reedd5fa1a42014-08-09 11:08:05 -07002910void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002911 RETURN_ON_NULL(picture);
2912
reed1c2c4412015-04-30 13:09:24 -07002913 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
reede3b38ce2016-01-08 09:18:44 -08002914 if (matrix && matrix->isIdentity()) {
2915 matrix = nullptr;
2916 }
2917 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2918 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2919 picture->playback(this);
2920 } else {
2921 this->onDrawPicture(picture, matrix, paint);
reedd5fa1a42014-08-09 11:08:05 -07002922 }
2923}
robertphillips9b14f262014-06-04 05:40:44 -07002924
reedd5fa1a42014-08-09 11:08:05 -07002925void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2926 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07002927 if (!paint || paint->canComputeFastBounds()) {
2928 SkRect bounds = picture->cullRect();
2929 if (paint) {
2930 paint->computeFastBounds(bounds, &bounds);
2931 }
2932 if (matrix) {
2933 matrix->mapRect(&bounds);
2934 }
2935 if (this->quickReject(bounds)) {
2936 return;
2937 }
2938 }
2939
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002940 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002941 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002942 // Canvas has to first give the device the opportunity to render
2943 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002944 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002945 return; // the device has rendered the entire picture
2946 }
2947 }
2948
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002949 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07002950 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002951}
2952
reed@android.com8a1c16f2008-12-17 15:59:43 +00002953///////////////////////////////////////////////////////////////////////////////
2954///////////////////////////////////////////////////////////////////////////////
2955
2956SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
bungeman99fe8222015-08-20 07:57:51 -07002957 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002958
2959 SkASSERT(canvas);
2960
2961 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2962 fDone = !fImpl->next();
2963}
2964
2965SkCanvas::LayerIter::~LayerIter() {
2966 fImpl->~SkDrawIter();
2967}
2968
2969void SkCanvas::LayerIter::next() {
2970 fDone = !fImpl->next();
2971}
2972
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002973SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002974 return fImpl->getDevice();
2975}
2976
2977const SkMatrix& SkCanvas::LayerIter::matrix() const {
2978 return fImpl->getMatrix();
2979}
2980
2981const SkPaint& SkCanvas::LayerIter::paint() const {
2982 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07002983 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002984 paint = &fDefaultPaint;
2985 }
2986 return *paint;
2987}
2988
2989const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2990int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2991int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002992
2993///////////////////////////////////////////////////////////////////////////////
2994
fmalitac3b589a2014-06-05 12:40:07 -07002995SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002996
2997///////////////////////////////////////////////////////////////////////////////
2998
2999static bool supported_for_raster_canvas(const SkImageInfo& info) {
3000 switch (info.alphaType()) {
3001 case kPremul_SkAlphaType:
3002 case kOpaque_SkAlphaType:
3003 break;
3004 default:
3005 return false;
3006 }
3007
3008 switch (info.colorType()) {
3009 case kAlpha_8_SkColorType:
3010 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00003011 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003012 break;
3013 default:
3014 return false;
3015 }
3016
3017 return true;
3018}
3019
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003020SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
3021 if (!supported_for_raster_canvas(info)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003022 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003023 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003024
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003025 SkBitmap bitmap;
3026 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003027 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003028 }
halcanary385fe4d2015-08-26 13:07:48 -07003029 return new SkCanvas(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003030}
reedd5fa1a42014-08-09 11:08:05 -07003031
3032///////////////////////////////////////////////////////////////////////////////
3033
3034SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003035 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07003036 : fCanvas(canvas)
3037 , fSaveCount(canvas->getSaveCount())
3038{
bsalomon49f085d2014-09-05 13:34:00 -07003039 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003040 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07003041 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003042 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07003043 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003044 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07003045 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003046 canvas->save();
3047 }
mtklein6cfa73a2014-08-13 13:33:49 -07003048
bsalomon49f085d2014-09-05 13:34:00 -07003049 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003050 canvas->concat(*matrix);
3051 }
3052}
3053
3054SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
3055 fCanvas->restoreToCount(fSaveCount);
3056}
reede8f30622016-03-23 18:59:25 -07003057
3058#ifdef SK_SUPPORT_LEGACY_NEW_SURFACE_API
3059SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
3060 return this->makeSurface(info, props).release();
3061}
3062#endif