blob: c7a9dc72f299e75347298965966dc7554cb80c84 [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00003 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00004 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00006 */
7
bungemand3ebb482015-08-05 13:57:49 -07008#include "SkBitmapDevice.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +00009#include "SkCanvas.h"
reedd5fa1a42014-08-09 11:08:05 -070010#include "SkCanvasPriv.h"
bungemand3ebb482015-08-05 13:57:49 -070011#include "SkClipStack.h"
reeddbc3cef2015-04-29 12:18:57 -070012#include "SkColorFilter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013#include "SkDraw.h"
reed3cb38402015-02-06 08:36:15 -080014#include "SkDrawable.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000015#include "SkDrawFilter.h"
16#include "SkDrawLooper.h"
joshualitt5f5a8d72015-02-25 14:09:45 -080017#include "SkErrorInternals.h"
piotaixrb5fae932014-09-24 13:03:30 -070018#include "SkImage.h"
reed262a71b2015-12-05 13:07:27 -080019#include "SkImage_Base.h"
senorblanco900c3672016-04-27 11:31:23 -070020#include "SkImageFilter.h"
21#include "SkImageFilterCache.h"
reed262a71b2015-12-05 13:07:27 -080022#include "SkMatrixUtils.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000023#include "SkMetaData.h"
reed4c21dc52015-06-25 12:32:03 -070024#include "SkNinePatchIter.h"
reedc83a2972015-07-16 07:40:45 -070025#include "SkPaintPriv.h"
dandovb3c9d1c2014-08-12 08:34:29 -070026#include "SkPatchUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000027#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000028#include "SkRasterClip.h"
reed96472de2014-12-10 09:53:42 -080029#include "SkReadPixelsRec.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000030#include "SkRRect.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000031#include "SkSmallAllocator.h"
robertphillips4418dba2016-03-07 12:45:14 -080032#include "SkSpecialImage.h"
reed@google.com97af1a62012-08-28 12:19:02 +000033#include "SkSurface_Base.h"
fmalita7ba7aa72014-08-29 09:46:36 -070034#include "SkTextBlob.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000035#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000036#include "SkTLazy.h"
danakj8f757f52014-11-04 11:48:43 -080037#include "SkTraceEvent.h"
bungemand3ebb482015-08-05 13:57:49 -070038
39#include <new>
reed@android.com8a1c16f2008-12-17 15:59:43 +000040
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000041#if SK_SUPPORT_GPU
robertphillips7354a4b2015-12-16 05:08:27 -080042#include "GrContext.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000043#include "GrRenderTarget.h"
robertphillips7354a4b2015-12-16 05:08:27 -080044#include "SkGr.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000045#endif
46
reede3b38ce2016-01-08 09:18:44 -080047#define RETURN_ON_NULL(ptr) do { if (nullptr == (ptr)) return; } while (0)
48
reedc83a2972015-07-16 07:40:45 -070049/*
50 * Return true if the drawing this rect would hit every pixels in the canvas.
51 *
52 * Returns false if
53 * - rect does not contain the canvas' bounds
54 * - paint is not fill
55 * - paint would blur or otherwise change the coverage of the rect
56 */
57bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
58 ShaderOverrideOpacity overrideOpacity) const {
bungeman99fe8222015-08-20 07:57:51 -070059 static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
60 (int)kNone_ShaderOverrideOpacity,
61 "need_matching_enums0");
62 static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
63 (int)kOpaque_ShaderOverrideOpacity,
64 "need_matching_enums1");
65 static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
66 (int)kNotOpaque_ShaderOverrideOpacity,
67 "need_matching_enums2");
reedc83a2972015-07-16 07:40:45 -070068
69 const SkISize size = this->getBaseLayerSize();
70 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
71 if (!this->getClipStack()->quickContains(bounds)) {
72 return false;
73 }
74
75 if (rect) {
76 if (!this->getTotalMatrix().rectStaysRect()) {
77 return false; // conservative
78 }
79
80 SkRect devRect;
81 this->getTotalMatrix().mapRect(&devRect, *rect);
fmalita8c0144c2015-07-22 05:56:16 -070082 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -070083 return false;
84 }
85 }
86
87 if (paint) {
88 SkPaint::Style paintStyle = paint->getStyle();
89 if (!(paintStyle == SkPaint::kFill_Style ||
90 paintStyle == SkPaint::kStrokeAndFill_Style)) {
91 return false;
92 }
93 if (paint->getMaskFilter() || paint->getLooper()
94 || paint->getPathEffect() || paint->getImageFilter()) {
95 return false; // conservative
96 }
97 }
98 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
99}
100
101///////////////////////////////////////////////////////////////////////////////////////////////////
102
reedd990e2f2014-12-22 11:58:30 -0800103static bool gIgnoreSaveLayerBounds;
104void SkCanvas::Internal_Private_SetIgnoreSaveLayerBounds(bool ignore) {
105 gIgnoreSaveLayerBounds = ignore;
106}
107bool SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds() {
108 return gIgnoreSaveLayerBounds;
109}
110
reed0acf1b42014-12-22 16:12:38 -0800111static bool gTreatSpriteAsBitmap;
112void SkCanvas::Internal_Private_SetTreatSpriteAsBitmap(bool spriteAsBitmap) {
113 gTreatSpriteAsBitmap = spriteAsBitmap;
114}
115bool SkCanvas::Internal_Private_GetTreatSpriteAsBitmap() {
116 return gTreatSpriteAsBitmap;
117}
118
reed@google.comda17f752012-08-16 18:27:05 +0000119// experimental for faster tiled drawing...
120//#define SK_ENABLE_CLIP_QUICKREJECT
reed@android.com8a1c16f2008-12-17 15:59:43 +0000121//#define SK_TRACE_SAVERESTORE
122
123#ifdef SK_TRACE_SAVERESTORE
124 static int gLayerCounter;
125 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
126 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
127
128 static int gRecCounter;
129 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
130 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
131
132 static int gCanvasCounter;
133 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
134 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
135#else
136 #define inc_layer()
137 #define dec_layer()
138 #define inc_rec()
139 #define dec_rec()
140 #define inc_canvas()
141 #define dec_canvas()
142#endif
143
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000144typedef SkTLazy<SkPaint> SkLazyPaint;
145
reedc83a2972015-07-16 07:40:45 -0700146void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000147 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700148 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
149 ? SkSurface::kDiscard_ContentChangeMode
150 : SkSurface::kRetain_ContentChangeMode);
151 }
152}
153
154void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
155 ShaderOverrideOpacity overrideOpacity) {
156 if (fSurfaceBase) {
157 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
158 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
159 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
160 // and therefore we don't care which mode we're in.
161 //
162 if (fSurfaceBase->outstandingImageSnapshot()) {
163 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
164 mode = SkSurface::kDiscard_ContentChangeMode;
165 }
166 }
167 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000168 }
169}
170
reed@android.com8a1c16f2008-12-17 15:59:43 +0000171///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000172
reed4a8126e2014-09-22 07:29:03 -0700173static uint32_t filter_paint_flags(const SkSurfaceProps& props, uint32_t flags) {
174 const uint32_t propFlags = props.flags();
175 if (propFlags & SkSurfaceProps::kDisallowDither_Flag) {
176 flags &= ~SkPaint::kDither_Flag;
177 }
178 if (propFlags & SkSurfaceProps::kDisallowAntiAlias_Flag) {
179 flags &= ~SkPaint::kAntiAlias_Flag;
180 }
181 return flags;
182}
183
184///////////////////////////////////////////////////////////////////////////////
185
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000186/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000187 The clip/matrix/proc are fields that reflect the top of the save/restore
188 stack. Whenever the canvas changes, it marks a dirty flag, and then before
189 these are used (assuming we're not on a layer) we rebuild these cache
190 values: they reflect the top of the save stack, but translated and clipped
191 by the device's XY offset and bitmap-bounds.
192*/
193struct DeviceCM {
194 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000195 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000196 SkRasterClip fClip;
reed@google.com6f8f2922011-03-04 22:27:10 +0000197 SkPaint* fPaint; // may be null (in the future)
reed61f501f2015-04-29 08:34:00 -0700198 const SkMatrix* fMatrix;
199 SkMatrix fMatrixStorage;
reed8c30a812016-04-20 16:36:51 -0700200 SkMatrix fStashedMatrix; // original CTM; used by imagefilter in saveLayer
reed61f501f2015-04-29 08:34:00 -0700201 const bool fDeviceIsBitmapDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000202
reed96e657d2015-03-10 17:30:07 -0700203 DeviceCM(SkBaseDevice* device, const SkPaint* paint, SkCanvas* canvas,
reed8c30a812016-04-20 16:36:51 -0700204 bool conservativeRasterClip, bool deviceIsBitmapDevice, const SkMatrix& stashed)
halcanary96fcdcc2015-08-27 07:41:13 -0700205 : fNext(nullptr)
reedd9544982014-09-09 18:46:22 -0700206 , fClip(conservativeRasterClip)
reed8c30a812016-04-20 16:36:51 -0700207 , fStashedMatrix(stashed)
reed61f501f2015-04-29 08:34:00 -0700208 , fDeviceIsBitmapDevice(deviceIsBitmapDevice)
reedd9544982014-09-09 18:46:22 -0700209 {
halcanary96fcdcc2015-08-27 07:41:13 -0700210 if (nullptr != device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000212 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000213 }
reed@google.com4b226022011-01-11 18:32:13 +0000214 fDevice = device;
halcanary96fcdcc2015-08-27 07:41:13 -0700215 fPaint = paint ? new SkPaint(*paint) : nullptr;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000216 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000218 ~DeviceCM() {
bsalomon49f085d2014-09-05 13:34:00 -0700219 if (fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000220 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221 fDevice->unref();
222 }
halcanary385fe4d2015-08-26 13:07:48 -0700223 delete fPaint;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000224 }
reed@google.com4b226022011-01-11 18:32:13 +0000225
mtkleinfeaadee2015-04-08 11:25:48 -0700226 void reset(const SkIRect& bounds) {
227 SkASSERT(!fPaint);
228 SkASSERT(!fNext);
229 SkASSERT(fDevice);
230 fClip.setRect(bounds);
231 }
232
reed@google.com045e62d2011-10-24 12:19:46 +0000233 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
234 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000235 int x = fDevice->getOrigin().x();
236 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000237 int width = fDevice->width();
238 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000239
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240 if ((x | y) == 0) {
241 fMatrix = &totalMatrix;
242 fClip = totalClip;
243 } else {
244 fMatrixStorage = totalMatrix;
245 fMatrixStorage.postTranslate(SkIntToScalar(-x),
246 SkIntToScalar(-y));
247 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000248
reed@android.com8a1c16f2008-12-17 15:59:43 +0000249 totalClip.translate(-x, -y, &fClip);
250 }
251
reed@google.com045e62d2011-10-24 12:19:46 +0000252 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253
254 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000255
reed@android.com8a1c16f2008-12-17 15:59:43 +0000256 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000257 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000258 SkRegion::kDifference_Op);
259 }
reed@google.com4b226022011-01-11 18:32:13 +0000260
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000261 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
262
reed@android.com8a1c16f2008-12-17 15:59:43 +0000263#ifdef SK_DEBUG
264 if (!fClip.isEmpty()) {
265 SkIRect deviceR;
266 deviceR.set(0, 0, width, height);
267 SkASSERT(deviceR.contains(fClip.getBounds()));
268 }
269#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000270 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000271};
272
273/* This is the record we keep for each save/restore level in the stack.
274 Since a level optionally copies the matrix and/or stack, we have pointers
275 for these fields. If the value is copied for this level, the copy is
276 stored in the ...Storage field, and the pointer points to that. If the
277 value is not copied for this level, we ignore ...Storage, and just point
278 at the corresponding value in the previous level in the stack.
279*/
280class SkCanvas::MCRec {
281public:
reed1f836ee2014-07-07 07:49:34 -0700282 SkDrawFilter* fFilter; // the current filter (or null)
reedd9544982014-09-09 18:46:22 -0700283 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284 /* If there are any layers in the stack, this points to the top-most
285 one that is at or below this level in the stack (so we know what
286 bitmap/device to draw into from this level. This value is NOT
287 reference counted, since the real owner is either our fLayer field,
288 or a previous one in a lower level.)
289 */
reed2ff1fce2014-12-11 07:07:37 -0800290 DeviceCM* fTopLayer;
291 SkRasterClip fRasterClip;
292 SkMatrix fMatrix;
293 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000294
reedd9544982014-09-09 18:46:22 -0700295 MCRec(bool conservativeRasterClip) : fRasterClip(conservativeRasterClip) {
halcanary96fcdcc2015-08-27 07:41:13 -0700296 fFilter = nullptr;
297 fLayer = nullptr;
298 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800299 fMatrix.reset();
300 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700301
reedd9544982014-09-09 18:46:22 -0700302 // don't bother initializing fNext
303 inc_rec();
304 }
reed2ff1fce2014-12-11 07:07:37 -0800305 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
reedd9544982014-09-09 18:46:22 -0700306 fFilter = SkSafeRef(prev.fFilter);
halcanary96fcdcc2015-08-27 07:41:13 -0700307 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700308 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800309 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700310
reed@android.com8a1c16f2008-12-17 15:59:43 +0000311 // don't bother initializing fNext
312 inc_rec();
313 }
314 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000315 SkSafeUnref(fFilter);
halcanary385fe4d2015-08-26 13:07:48 -0700316 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000317 dec_rec();
318 }
mtkleinfeaadee2015-04-08 11:25:48 -0700319
320 void reset(const SkIRect& bounds) {
321 SkASSERT(fLayer);
322 SkASSERT(fDeferredSaveCount == 0);
323
324 fMatrix.reset();
325 fRasterClip.setRect(bounds);
326 fLayer->reset(bounds);
327 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000328};
329
330class SkDrawIter : public SkDraw {
331public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000332 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000333 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000334 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000335 canvas->updateDeviceCMCache();
336
reed687fa1c2015-04-07 08:00:56 -0700337 fClipStack = canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000338 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000339 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000340 }
reed@google.com4b226022011-01-11 18:32:13 +0000341
reed@android.com8a1c16f2008-12-17 15:59:43 +0000342 bool next() {
343 // skip over recs with empty clips
344 if (fSkipEmptyClips) {
345 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
346 fCurrLayer = fCurrLayer->fNext;
347 }
348 }
349
reed@google.comf68c5e22012-02-24 16:38:58 +0000350 const DeviceCM* rec = fCurrLayer;
351 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000352
353 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000354 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000355 fDevice = rec->fDevice;
reed41e010c2015-06-09 12:16:53 -0700356 if (!fDevice->accessPixels(&fDst)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700357 fDst.reset(fDevice->imageInfo(), nullptr, 0);
reed41e010c2015-06-09 12:16:53 -0700358 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000359 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000360 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000361
362 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700363 // fCurrLayer may be nullptr now
reed@android.com199f1082009-06-10 02:12:47 +0000364
reed@android.com8a1c16f2008-12-17 15:59:43 +0000365 return true;
366 }
367 return false;
368 }
reed@google.com4b226022011-01-11 18:32:13 +0000369
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000370 SkBaseDevice* getDevice() const { return fDevice; }
reed1e7f5e72016-04-27 07:49:17 -0700371 const SkRasterClip& getClip() const { return *fRC; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000372 int getX() const { return fDevice->getOrigin().x(); }
373 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000374 const SkMatrix& getMatrix() const { return *fMatrix; }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000375 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000376
reed@android.com8a1c16f2008-12-17 15:59:43 +0000377private:
378 SkCanvas* fCanvas;
379 const DeviceCM* fCurrLayer;
380 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000381 SkBool8 fSkipEmptyClips;
382
383 typedef SkDraw INHERITED;
384};
385
386/////////////////////////////////////////////////////////////////////////////
387
reeddbc3cef2015-04-29 12:18:57 -0700388static SkPaint* set_if_needed(SkLazyPaint* lazy, const SkPaint& orig) {
389 return lazy->isValid() ? lazy->get() : lazy->set(orig);
390}
391
392/**
393 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700394 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700395 */
reedd053ce92016-03-22 10:17:23 -0700396static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700397 SkImageFilter* imgf = paint.getImageFilter();
398 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700399 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700400 }
401
reedd053ce92016-03-22 10:17:23 -0700402 SkColorFilter* imgCFPtr;
403 if (!imgf->asAColorFilter(&imgCFPtr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700404 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700405 }
reedd053ce92016-03-22 10:17:23 -0700406 sk_sp<SkColorFilter> imgCF(imgCFPtr);
reeddbc3cef2015-04-29 12:18:57 -0700407
408 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700409 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700410 // there is no existing paint colorfilter, so we can just return the imagefilter's
411 return imgCF;
412 }
413
414 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
415 // and we need to combine them into a single colorfilter.
reedd053ce92016-03-22 10:17:23 -0700416 return SkColorFilter::MakeComposeFilter(std::move(imgCF), sk_ref_sp(paintCF));
reeddbc3cef2015-04-29 12:18:57 -0700417}
418
senorblanco87e066e2015-10-28 11:23:36 -0700419/**
420 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
421 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
422 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
423 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
424 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
425 * conservative "effective" bounds based on the settings in the paint... with one exception. This
426 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
427 * deliberately ignored.
428 */
429static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
430 const SkRect& rawBounds,
431 SkRect* storage) {
432 SkPaint tmpUnfiltered(paint);
433 tmpUnfiltered.setImageFilter(nullptr);
434 if (tmpUnfiltered.canComputeFastBounds()) {
435 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
436 } else {
437 return rawBounds;
438 }
439}
440
reed@android.com8a1c16f2008-12-17 15:59:43 +0000441class AutoDrawLooper {
442public:
senorblanco87e066e2015-10-28 11:23:36 -0700443 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
444 // paint. It's used to determine the size of the offscreen layer for filters.
445 // If null, the clip will be used instead.
reed4a8126e2014-09-22 07:29:03 -0700446 AutoDrawLooper(SkCanvas* canvas, const SkSurfaceProps& props, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000447 bool skipLayerForImageFilter = false,
senorblanco87e066e2015-10-28 11:23:36 -0700448 const SkRect* rawBounds = nullptr) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000449 fCanvas = canvas;
fmalita53d9f1c2016-01-25 06:23:54 -0800450#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000451 fFilter = canvas->getDrawFilter();
fmalita77650002016-01-21 18:47:11 -0800452#else
453 fFilter = nullptr;
454#endif
reed4a8126e2014-09-22 07:29:03 -0700455 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000456 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700457 fTempLayerForImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000458 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000459
reedd053ce92016-03-22 10:17:23 -0700460 auto simplifiedCF = image_to_color_filter(fOrigPaint);
reeddbc3cef2015-04-29 12:18:57 -0700461 if (simplifiedCF) {
462 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reedd053ce92016-03-22 10:17:23 -0700463 paint->setColorFilter(std::move(simplifiedCF));
halcanary96fcdcc2015-08-27 07:41:13 -0700464 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700465 fPaint = paint;
466 }
467
468 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700469 /**
470 * We implement ImageFilters for a given draw by creating a layer, then applying the
471 * imagefilter to the pixels of that layer (its backing surface/image), and then
472 * we call restore() to xfer that layer to the main canvas.
473 *
474 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
475 * 2. Generate the src pixels:
476 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
477 * return (fPaint). We then draw the primitive (using srcover) into a cleared
478 * buffer/surface.
479 * 3. Restore the layer created in #1
480 * The imagefilter is passed the buffer/surface from the layer (now filled with the
481 * src pixels of the primitive). It returns a new "filtered" buffer, which we
482 * draw onto the previous layer using the xfermode from the original paint.
483 */
reed@google.com8926b162012-03-23 15:36:36 +0000484 SkPaint tmp;
reeddbc3cef2015-04-29 12:18:57 -0700485 tmp.setImageFilter(fPaint->getImageFilter());
reedcfb6bdf2016-03-29 11:32:50 -0700486 tmp.setXfermode(sk_ref_sp(fPaint->getXfermode()));
senorblanco87e066e2015-10-28 11:23:36 -0700487 SkRect storage;
488 if (rawBounds) {
489 // Make rawBounds include all paint outsets except for those due to image filters.
490 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
491 }
reedbfd5f172016-01-07 11:28:08 -0800492 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &tmp),
reed76033be2015-03-14 10:54:31 -0700493 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700494 fTempLayerForImageFilter = true;
495 // we remove the imagefilter/xfermode inside doNext()
reed@google.com8926b162012-03-23 15:36:36 +0000496 }
497
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000498 if (SkDrawLooper* looper = paint.getLooper()) {
499 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
500 looper->contextSize());
501 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000502 fIsSimple = false;
503 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700504 fLooperContext = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000505 // can we be marked as simple?
reed5c476fb2015-04-20 08:04:21 -0700506 fIsSimple = !fFilter && !fTempLayerForImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000507 }
piotaixrb5fae932014-09-24 13:03:30 -0700508
reed4a8126e2014-09-22 07:29:03 -0700509 uint32_t oldFlags = paint.getFlags();
510 fNewPaintFlags = filter_paint_flags(props, oldFlags);
511 if (fIsSimple && (fNewPaintFlags != oldFlags)) {
reeddbc3cef2015-04-29 12:18:57 -0700512 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700513 paint->setFlags(fNewPaintFlags);
514 fPaint = paint;
515 // if we're not simple, doNext() will take care of calling setFlags()
516 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000517 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000518
reed@android.com8a1c16f2008-12-17 15:59:43 +0000519 ~AutoDrawLooper() {
reed5c476fb2015-04-20 08:04:21 -0700520 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000521 fCanvas->internalRestore();
522 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000523 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000524 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000525
reed@google.com4e2b3d32011-04-07 14:18:59 +0000526 const SkPaint& paint() const {
527 SkASSERT(fPaint);
528 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000529 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000530
reed@google.com129ec222012-05-15 13:24:09 +0000531 bool next(SkDrawFilter::Type drawType) {
532 if (fDone) {
533 return false;
534 } else if (fIsSimple) {
535 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000536 return !fPaint->nothingToDraw();
537 } else {
538 return this->doNext(drawType);
539 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000540 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000541
reed@android.com8a1c16f2008-12-17 15:59:43 +0000542private:
reeddbc3cef2015-04-29 12:18:57 -0700543 SkLazyPaint fLazyPaintInit; // base paint storage in case we need to modify it
544 SkLazyPaint fLazyPaintPerLooper; // per-draw-looper storage, so the looper can modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000545 SkCanvas* fCanvas;
546 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000547 SkDrawFilter* fFilter;
548 const SkPaint* fPaint;
549 int fSaveCount;
reed4a8126e2014-09-22 07:29:03 -0700550 uint32_t fNewPaintFlags;
reed5c476fb2015-04-20 08:04:21 -0700551 bool fTempLayerForImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000552 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000553 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000554 SkDrawLooper::Context* fLooperContext;
555 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000556
557 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000558};
559
reed@google.com129ec222012-05-15 13:24:09 +0000560bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
halcanary96fcdcc2015-08-27 07:41:13 -0700561 fPaint = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000562 SkASSERT(!fIsSimple);
reed5c476fb2015-04-20 08:04:21 -0700563 SkASSERT(fLooperContext || fFilter || fTempLayerForImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000564
reeddbc3cef2015-04-29 12:18:57 -0700565 SkPaint* paint = fLazyPaintPerLooper.set(fLazyPaintInit.isValid() ?
566 *fLazyPaintInit.get() : fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700567 paint->setFlags(fNewPaintFlags);
reed@google.com129ec222012-05-15 13:24:09 +0000568
reed5c476fb2015-04-20 08:04:21 -0700569 if (fTempLayerForImageFilter) {
halcanary96fcdcc2015-08-27 07:41:13 -0700570 paint->setImageFilter(nullptr);
571 paint->setXfermode(nullptr);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000572 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000573
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000574 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000575 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000576 return false;
577 }
578 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000579 if (!fFilter->filter(paint, drawType)) {
580 fDone = true;
581 return false;
582 }
halcanary96fcdcc2015-08-27 07:41:13 -0700583 if (nullptr == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000584 // no looper means we only draw once
585 fDone = true;
586 }
587 }
588 fPaint = paint;
589
590 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000591 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000592 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000593 }
594
595 // call this after any possible paint modifiers
596 if (fPaint->nothingToDraw()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700597 fPaint = nullptr;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000598 return false;
599 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000600 return true;
601}
602
reed@android.com8a1c16f2008-12-17 15:59:43 +0000603////////// macros to place around the internal draw calls //////////////////
604
reed262a71b2015-12-05 13:07:27 -0800605#define LOOPER_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
606 this->predrawNotify(); \
607 AutoDrawLooper looper(this, fProps, paint, skipLayerForFilter, bounds); \
608 while (looper.next(SkDrawFilter::kBitmap_Type)) { \
609 SkDrawIter iter(this);
610
611
reed@google.com8926b162012-03-23 15:36:36 +0000612#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000613 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700614 AutoDrawLooper looper(this, fProps, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000615 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000616 SkDrawIter iter(this);
617
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000618#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000619 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700620 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000621 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000622 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000623
reedc83a2972015-07-16 07:40:45 -0700624#define LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, type, bounds, auxOpaque) \
625 this->predrawNotify(bounds, &paint, auxOpaque); \
626 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
627 while (looper.next(type)) { \
628 SkDrawIter iter(this);
629
reed@google.com4e2b3d32011-04-07 14:18:59 +0000630#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000631
632////////////////////////////////////////////////////////////////////////////
633
mtkleinfeaadee2015-04-08 11:25:48 -0700634void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
635 this->restoreToCount(1);
636 fCachedLocalClipBounds.setEmpty();
637 fCachedLocalClipBoundsDirty = true;
638 fClipStack->reset();
639 fMCRec->reset(bounds);
640
641 // We're peering through a lot of structs here. Only at this scope do we
642 // know that the device is an SkBitmapDevice (really an SkNoPixelsBitmapDevice).
643 static_cast<SkBitmapDevice*>(fMCRec->fLayer->fDevice)->setNewSize(bounds.size());
644}
645
reedd9544982014-09-09 18:46:22 -0700646SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
reed42b73eb2015-11-20 13:42:42 -0800647 if (device && device->forceConservativeRasterClip()) {
648 flags = InitFlags(flags | kConservativeRasterClip_InitFlag);
649 }
650 // Since init() is only called once by our constructors, it is safe to perform this
651 // const-cast.
652 *const_cast<bool*>(&fConservativeRasterClip) = SkToBool(flags & kConservativeRasterClip_InitFlag);
653
reed@google.comc0784db2013-12-13 21:16:12 +0000654 fCachedLocalClipBounds.setEmpty();
655 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000656 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000657 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700658 fDeviceCMDirty = true;
reed2ff1fce2014-12-11 07:07:37 -0800659 fSaveCount = 1;
halcanary96fcdcc2015-08-27 07:41:13 -0700660 fMetaData = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000661
halcanary385fe4d2015-08-26 13:07:48 -0700662 fClipStack.reset(new SkClipStack);
reed687fa1c2015-04-07 08:00:56 -0700663
reed@android.com8a1c16f2008-12-17 15:59:43 +0000664 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700665 new (fMCRec) MCRec(fConservativeRasterClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000666
reeda499f902015-05-01 09:34:31 -0700667 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
668 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
reed8c30a812016-04-20 16:36:51 -0700669 new (fDeviceCMStorage) DeviceCM(nullptr, nullptr, nullptr, fConservativeRasterClip, false,
670 fMCRec->fMatrix);
reedb679ca82015-04-07 04:40:48 -0700671
reed@android.com8a1c16f2008-12-17 15:59:43 +0000672 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000673
halcanary96fcdcc2015-08-27 07:41:13 -0700674 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000675
reedf92c8662014-08-18 08:02:43 -0700676 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700677 // The root device and the canvas should always have the same pixel geometry
678 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reedf92c8662014-08-18 08:02:43 -0700679 device->onAttachToCanvas(this);
680 fMCRec->fLayer->fDevice = SkRef(device);
reed78e27682014-11-19 08:04:34 -0800681 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
reedf92c8662014-08-18 08:02:43 -0700682 }
683 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000684}
685
reed@google.comcde92112011-07-06 20:00:52 +0000686SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000687 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700688 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800689 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000690{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000691 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000692
halcanary96fcdcc2015-08-27 07:41:13 -0700693 this->init(nullptr, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000694}
695
reedd9544982014-09-09 18:46:22 -0700696static SkBitmap make_nopixels(int width, int height) {
697 SkBitmap bitmap;
698 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
699 return bitmap;
700}
701
702class SkNoPixelsBitmapDevice : public SkBitmapDevice {
703public:
robertphillipsfcf78292015-06-19 11:49:52 -0700704 SkNoPixelsBitmapDevice(const SkIRect& bounds, const SkSurfaceProps& surfaceProps)
705 : INHERITED(make_nopixels(bounds.width(), bounds.height()), surfaceProps)
reed78e27682014-11-19 08:04:34 -0800706 {
707 this->setOrigin(bounds.x(), bounds.y());
708 }
reedd9544982014-09-09 18:46:22 -0700709
710private:
piotaixrb5fae932014-09-24 13:03:30 -0700711
reedd9544982014-09-09 18:46:22 -0700712 typedef SkBitmapDevice INHERITED;
713};
714
reed96a857e2015-01-25 10:33:58 -0800715SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000716 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800717 , fProps(SkSurfacePropsCopyOrDefault(props))
reed42b73eb2015-11-20 13:42:42 -0800718 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000719{
720 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700721
halcanary385fe4d2015-08-26 13:07:48 -0700722 this->init(new SkNoPixelsBitmapDevice(SkIRect::MakeWH(width, height), fProps),
723 kDefault_InitFlags)->unref();
reedd9544982014-09-09 18:46:22 -0700724}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000725
reed78e27682014-11-19 08:04:34 -0800726SkCanvas::SkCanvas(const SkIRect& bounds, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700727 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700728 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800729 , fConservativeRasterClip(false)
reedd9544982014-09-09 18:46:22 -0700730{
731 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700732
halcanary385fe4d2015-08-26 13:07:48 -0700733 this->init(new SkNoPixelsBitmapDevice(bounds, fProps), flags)->unref();
reedd9544982014-09-09 18:46:22 -0700734}
735
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000736SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000737 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700738 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800739 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000740{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000741 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700742
reedd9544982014-09-09 18:46:22 -0700743 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000744}
745
robertphillipsfcf78292015-06-19 11:49:52 -0700746SkCanvas::SkCanvas(SkBaseDevice* device, InitFlags flags)
747 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700748 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800749 , fConservativeRasterClip(false)
robertphillipsfcf78292015-06-19 11:49:52 -0700750{
751 inc_canvas();
752
753 this->init(device, flags);
754}
755
reed4a8126e2014-09-22 07:29:03 -0700756SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700757 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700758 , fProps(props)
reed42b73eb2015-11-20 13:42:42 -0800759 , fConservativeRasterClip(false)
reed3716fd02014-09-21 09:39:55 -0700760{
761 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700762
halcanary385fe4d2015-08-26 13:07:48 -0700763 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700764 this->init(device, kDefault_InitFlags);
765}
reed29c857d2014-09-21 10:25:07 -0700766
reed4a8126e2014-09-22 07:29:03 -0700767SkCanvas::SkCanvas(const SkBitmap& bitmap)
768 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
769 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800770 , fConservativeRasterClip(false)
reed4a8126e2014-09-22 07:29:03 -0700771{
772 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700773
halcanary385fe4d2015-08-26 13:07:48 -0700774 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700775 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000776}
777
778SkCanvas::~SkCanvas() {
779 // free up the contents of our deque
780 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000781
reed@android.com8a1c16f2008-12-17 15:59:43 +0000782 this->internalRestore(); // restore the last, since we're going away
783
halcanary385fe4d2015-08-26 13:07:48 -0700784 delete fMetaData;
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000785
reed@android.com8a1c16f2008-12-17 15:59:43 +0000786 dec_canvas();
787}
788
fmalita53d9f1c2016-01-25 06:23:54 -0800789#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000790SkDrawFilter* SkCanvas::getDrawFilter() const {
791 return fMCRec->fFilter;
792}
793
794SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
reed51985e32015-04-11 08:04:56 -0700795 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000796 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
797 return filter;
798}
fmalita77650002016-01-21 18:47:11 -0800799#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000800
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000801SkMetaData& SkCanvas::getMetaData() {
802 // metadata users are rare, so we lazily allocate it. If that changes we
803 // can decide to just make it a field in the device (rather than a ptr)
halcanary96fcdcc2015-08-27 07:41:13 -0700804 if (nullptr == fMetaData) {
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000805 fMetaData = new SkMetaData;
806 }
807 return *fMetaData;
808}
809
reed@android.com8a1c16f2008-12-17 15:59:43 +0000810///////////////////////////////////////////////////////////////////////////////
811
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000812void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000813 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000814 if (device) {
815 device->flush();
816 }
817}
818
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000819SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000820 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000821 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
822}
823
senorblancoafc7cce2016-02-02 18:44:15 -0800824SkIRect SkCanvas::getTopLayerBounds() const {
825 SkBaseDevice* d = this->getTopDevice();
826 if (!d) {
827 return SkIRect::MakeEmpty();
828 }
829 return SkIRect::MakeXYWH(d->getOrigin().x(), d->getOrigin().y(), d->width(), d->height());
830}
831
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000832SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000833 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000834 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000835 SkASSERT(rec && rec->fLayer);
836 return rec->fLayer->fDevice;
837}
838
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000839SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000840 if (updateMatrixClip) {
841 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
842 }
reed@google.com9266fed2011-03-30 00:18:03 +0000843 return fMCRec->fTopLayer->fDevice;
844}
845
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000846bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
847 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
848 return false;
849 }
850
851 bool weAllocated = false;
halcanary96fcdcc2015-08-27 07:41:13 -0700852 if (nullptr == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700853 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000854 return false;
855 }
856 weAllocated = true;
857 }
858
reedcf01e312015-05-23 19:14:51 -0700859 SkAutoPixmapUnlock unlocker;
860 if (bitmap->requestLock(&unlocker)) {
861 const SkPixmap& pm = unlocker.pixmap();
862 if (this->readPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), x, y)) {
863 return true;
864 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000865 }
866
867 if (weAllocated) {
halcanary96fcdcc2015-08-27 07:41:13 -0700868 bitmap->setPixelRef(nullptr);
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000869 }
870 return false;
871}
reed@google.com51df9e32010-12-23 19:29:18 +0000872
bsalomon@google.comc6980972011-11-02 19:57:21 +0000873bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000874 SkIRect r = srcRect;
875 const SkISize size = this->getBaseLayerSize();
876 if (!r.intersect(0, 0, size.width(), size.height())) {
877 bitmap->reset();
878 return false;
879 }
880
reed84825042014-09-02 12:50:45 -0700881 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000882 // bitmap will already be reset.
883 return false;
884 }
885 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
886 bitmap->reset();
887 return false;
888 }
889 return true;
890}
891
reed96472de2014-12-10 09:53:42 -0800892bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000893 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000894 if (!device) {
895 return false;
896 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000897 const SkISize size = this->getBaseLayerSize();
mtkleinf0f14112014-12-12 08:46:25 -0800898
reed96472de2014-12-10 09:53:42 -0800899 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
900 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000901 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000902 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000903
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000904 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800905 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000906}
907
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000908bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
909 if (bitmap.getTexture()) {
910 return false;
911 }
reedcf01e312015-05-23 19:14:51 -0700912
913 SkAutoPixmapUnlock unlocker;
914 if (bitmap.requestLock(&unlocker)) {
915 const SkPixmap& pm = unlocker.pixmap();
916 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000917 }
918 return false;
919}
920
921bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
922 int x, int y) {
923 switch (origInfo.colorType()) {
924 case kUnknown_SkColorType:
925 case kIndex_8_SkColorType:
926 return false;
927 default:
928 break;
929 }
halcanary96fcdcc2015-08-27 07:41:13 -0700930 if (nullptr == pixels || rowBytes < origInfo.minRowBytes()) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000931 return false;
932 }
933
934 const SkISize size = this->getBaseLayerSize();
935 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
936 if (!target.intersect(0, 0, size.width(), size.height())) {
937 return false;
938 }
939
940 SkBaseDevice* device = this->getDevice();
941 if (!device) {
942 return false;
943 }
944
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000945 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700946 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000947
948 // if x or y are negative, then we have to adjust pixels
949 if (x > 0) {
950 x = 0;
951 }
952 if (y > 0) {
953 y = 0;
954 }
955 // here x,y are either 0 or negative
956 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
957
reed4af35f32014-06-27 17:47:49 -0700958 // Tell our owning surface to bump its generation ID
reedc83a2972015-07-16 07:40:45 -0700959 const bool completeOverwrite = info.dimensions() == size;
960 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700961
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000962 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000963 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000964}
reed@google.com51df9e32010-12-23 19:29:18 +0000965
junov@google.com4370aed2012-01-18 16:21:08 +0000966SkCanvas* SkCanvas::canvasForDrawIter() {
967 return this;
968}
969
reed@android.com8a1c16f2008-12-17 15:59:43 +0000970//////////////////////////////////////////////////////////////////////////////
971
reed@android.com8a1c16f2008-12-17 15:59:43 +0000972void SkCanvas::updateDeviceCMCache() {
973 if (fDeviceCMDirty) {
974 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700975 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000976 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000977
halcanary96fcdcc2015-08-27 07:41:13 -0700978 if (nullptr == layer->fNext) { // only one layer
979 layer->updateMC(totalMatrix, totalClip, *fClipStack, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000980 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000981 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000982 do {
reed687fa1c2015-04-07 08:00:56 -0700983 layer->updateMC(totalMatrix, clip, *fClipStack, &clip);
halcanary96fcdcc2015-08-27 07:41:13 -0700984 } while ((layer = layer->fNext) != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000985 }
986 fDeviceCMDirty = false;
987 }
988}
989
reed@android.com8a1c16f2008-12-17 15:59:43 +0000990///////////////////////////////////////////////////////////////////////////////
991
reed2ff1fce2014-12-11 07:07:37 -0800992void SkCanvas::checkForDeferredSave() {
993 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800994 this->doSave();
995 }
996}
997
reedf0090cb2014-11-26 08:55:51 -0800998int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800999#ifdef SK_DEBUG
1000 int count = 0;
1001 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
1002 for (;;) {
1003 const MCRec* rec = (const MCRec*)iter.next();
1004 if (!rec) {
1005 break;
1006 }
1007 count += 1 + rec->fDeferredSaveCount;
1008 }
1009 SkASSERT(count == fSaveCount);
1010#endif
1011 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -08001012}
1013
1014int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -08001015 fSaveCount += 1;
1016 fMCRec->fDeferredSaveCount += 1;
1017 return this->getSaveCount() - 1; // return our prev value
1018}
1019
1020void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -08001021 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -07001022
1023 SkASSERT(fMCRec->fDeferredSaveCount > 0);
1024 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001025 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -08001026}
1027
1028void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -08001029 if (fMCRec->fDeferredSaveCount > 0) {
1030 SkASSERT(fSaveCount > 1);
1031 fSaveCount -= 1;
1032 fMCRec->fDeferredSaveCount -= 1;
1033 } else {
1034 // check for underflow
1035 if (fMCStack.count() > 1) {
1036 this->willRestore();
1037 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -07001038 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001039 this->internalRestore();
1040 this->didRestore();
1041 }
reedf0090cb2014-11-26 08:55:51 -08001042 }
1043}
1044
1045void SkCanvas::restoreToCount(int count) {
1046 // sanity check
1047 if (count < 1) {
1048 count = 1;
1049 }
mtkleinf0f14112014-12-12 08:46:25 -08001050
reedf0090cb2014-11-26 08:55:51 -08001051 int n = this->getSaveCount() - count;
1052 for (int i = 0; i < n; ++i) {
1053 this->restore();
1054 }
1055}
1056
reed2ff1fce2014-12-11 07:07:37 -08001057void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001058 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -07001059 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +00001060 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +00001061
reed687fa1c2015-04-07 08:00:56 -07001062 fClipStack->save();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001063}
1064
reed4960eee2015-12-18 07:09:18 -08001065bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
reed@google.comb93ba452014-03-10 19:47:58 +00001066#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001067 return !(saveLayerFlags & SkCanvas::kDontClipToLayer_PrivateSaveLayerFlag);
reed@google.comb93ba452014-03-10 19:47:58 +00001068#else
1069 return true;
1070#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001071}
1072
reed4960eee2015-12-18 07:09:18 -08001073bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -07001074 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001075 SkIRect clipBounds;
1076 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001077 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +00001078 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001079
reed96e657d2015-03-10 17:30:07 -07001080 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
1081
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001082 if (imageFilter) {
senorblancoe5e79842016-03-21 14:51:59 -07001083 clipBounds = imageFilter->filterBounds(clipBounds, ctm);
senorblancodb64af32015-12-09 10:11:43 -08001084 if (bounds && !imageFilter->canComputeFastBounds()) {
1085 bounds = nullptr;
1086 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001087 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001088 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -07001089 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001090 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +00001091
reed96e657d2015-03-10 17:30:07 -07001092 ctm.mapRect(&r, *bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001093 r.roundOut(&ir);
1094 // early exit if the layer's bounds are clipped out
1095 if (!ir.intersect(clipBounds)) {
reed4960eee2015-12-18 07:09:18 -08001096 if (BoundsAffectsClip(saveLayerFlags)) {
reed9b3aa542015-03-11 08:47:12 -07001097 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001098 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001099 }
junov@chromium.orga907ac32012-02-24 21:54:07 +00001100 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001101 }
1102 } else { // no user bounds, so just use the clip
1103 ir = clipBounds;
1104 }
reed180aec42015-03-11 10:39:04 -07001105 SkASSERT(!ir.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001106
reed4960eee2015-12-18 07:09:18 -08001107 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -07001108 // Simplify the current clips since they will be applied properly during restore()
reed9b3aa542015-03-11 08:47:12 -07001109 fCachedLocalClipBoundsDirty = true;
reed687fa1c2015-04-07 08:00:56 -07001110 fClipStack->clipDevRect(ir, SkRegion::kReplace_Op);
reed180aec42015-03-11 10:39:04 -07001111 fMCRec->fRasterClip.setRect(ir);
junov@chromium.orga907ac32012-02-24 21:54:07 +00001112 }
1113
1114 if (intersection) {
1115 *intersection = ir;
1116 }
1117 return true;
1118}
1119
reed4960eee2015-12-18 07:09:18 -08001120
reed4960eee2015-12-18 07:09:18 -08001121int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
1122 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001123}
1124
reed70ee31b2015-12-10 13:44:45 -08001125int SkCanvas::saveLayerPreserveLCDTextRequests(const SkRect* bounds, const SkPaint* paint) {
reed4960eee2015-12-18 07:09:18 -08001126 return this->saveLayer(SaveLayerRec(bounds, paint, kPreserveLCDText_SaveLayerFlag));
1127}
1128
1129int SkCanvas::saveLayer(const SaveLayerRec& origRec) {
1130 SaveLayerRec rec(origRec);
1131 if (gIgnoreSaveLayerBounds) {
1132 rec.fBounds = nullptr;
1133 }
1134 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
1135 fSaveCount += 1;
1136 this->internalSaveLayer(rec, strategy);
1137 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -08001138}
1139
reedbfd5f172016-01-07 11:28:08 -08001140static void draw_filter_into_device(SkBaseDevice* src, const SkImageFilter* filter,
1141 SkBaseDevice* dst, const SkMatrix& ctm) {
robertphillips7354a4b2015-12-16 05:08:27 -08001142
1143 SkBitmap srcBM;
1144
1145#if SK_SUPPORT_GPU
1146 GrRenderTarget* srcRT = src->accessRenderTarget();
1147 if (srcRT && !srcRT->asTexture() && dst->accessRenderTarget()) {
1148 // When both the src & the dst are on the gpu but the src doesn't have a texture,
1149 // we create a temporary texture for the draw.
1150 // TODO: we should actually only copy the portion of the source needed to apply the image
1151 // filter
1152 GrContext* context = srcRT->getContext();
bsalomon5ec26ae2016-02-25 08:33:02 -08001153 SkAutoTUnref<GrTexture> tex(context->textureProvider()->createTexture(srcRT->desc(),
1154 SkBudgeted::kYes));
robertphillips7354a4b2015-12-16 05:08:27 -08001155
1156 context->copySurface(tex, srcRT);
1157
1158 GrWrapTextureInBitmap(tex, src->width(), src->height(), src->isOpaque(), &srcBM);
1159 } else
1160#endif
1161 {
1162 srcBM = src->accessBitmap(false);
1163 }
1164
1165 SkCanvas c(dst);
1166
1167 SkPaint p;
robertphillips372177e2016-03-30 07:32:28 -07001168 p.setImageFilter(filter->makeWithLocalMatrix(ctm));
reedbfd5f172016-01-07 11:28:08 -08001169 const SkScalar x = SkIntToScalar(src->getOrigin().x());
1170 const SkScalar y = SkIntToScalar(src->getOrigin().y());
1171 c.drawBitmap(srcBM, x, y, &p);
robertphillips7354a4b2015-12-16 05:08:27 -08001172}
reed70ee31b2015-12-10 13:44:45 -08001173
reed129ed1c2016-02-22 06:42:31 -08001174static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, bool isOpaque,
1175 const SkPaint* paint) {
1176 // need to force L32 for now if we have an image filter. Once filters support other colortypes
1177 // e.g. sRGB or F16, we can remove this check
1178 const bool hasImageFilter = paint && paint->getImageFilter();
1179
1180 SkAlphaType alphaType = isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
1181 if ((prev.bytesPerPixel() < 4) || hasImageFilter) {
1182 // force to L32
1183 return SkImageInfo::MakeN32(w, h, alphaType);
1184 } else {
1185 // keep the same characteristics as the prev
1186 return SkImageInfo::Make(w, h, prev.colorType(), alphaType, prev.profileType());
1187 }
1188}
1189
reed4960eee2015-12-18 07:09:18 -08001190void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
1191 const SkRect* bounds = rec.fBounds;
1192 const SkPaint* paint = rec.fPaint;
1193 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1194
reed@google.comb93ba452014-03-10 19:47:58 +00001195#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001196 saveLayerFlags &= ~kDontClipToLayer_PrivateSaveLayerFlag;
reed@google.comb93ba452014-03-10 19:47:58 +00001197#endif
1198
reed8c30a812016-04-20 16:36:51 -07001199 SkLazyPaint lazyP;
1200 SkImageFilter* imageFilter = paint ? paint->getImageFilter() : NULL;
1201 SkMatrix stashedMatrix = fMCRec->fMatrix;
1202#ifndef SK_SUPPORT_LEGACY_IMAGEFILTER_CTM
1203 SkMatrix remainder;
1204 SkSize scale;
1205 /*
1206 * ImageFilters (so far) do not correctly handle matrices (CTM) that contain rotation/skew/etc.
1207 * but they do handle scaling. To accommodate this, we do the following:
1208 *
1209 * 1. Stash off the current CTM
1210 * 2. Decompose the CTM into SCALE and REMAINDER
1211 * 3. Wack the CTM to be just SCALE, and wrap the imagefilter with a MatrixImageFilter that
1212 * contains the REMAINDER
1213 * 4. Proceed as usual, allowing the client to draw into the layer (now with a scale-only CTM)
1214 * 5. During restore, we process the MatrixImageFilter, which applies REMAINDER to the output
1215 * of the original imagefilter, and draw that (via drawSprite)
1216 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1217 *
1218 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1219 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1220 */
reed96a04f32016-04-25 09:25:15 -07001221 if (imageFilter && !stashedMatrix.isScaleTranslate() && !imageFilter->canHandleComplexCTM() &&
reed8c30a812016-04-20 16:36:51 -07001222 stashedMatrix.decomposeScale(&scale, &remainder))
1223 {
1224 // We will restore the matrix (which we are overwriting here) in restore via fStashedMatrix
1225 this->internalSetMatrix(SkMatrix::MakeScale(scale.width(), scale.height()));
1226 SkPaint* p = lazyP.set(*paint);
1227 p->setImageFilter(SkImageFilter::MakeMatrixFilter(remainder,
1228 SkFilterQuality::kLow_SkFilterQuality,
1229 sk_ref_sp(imageFilter)));
1230 imageFilter = p->getImageFilter();
1231 paint = p;
1232 }
1233#endif
1234
junov@chromium.orga907ac32012-02-24 21:54:07 +00001235 // do this before we create the layer. We don't call the public save() since
1236 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001237 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001238
1239 fDeviceCMDirty = true;
1240
1241 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001242 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
reed2ff1fce2014-12-11 07:07:37 -08001243 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001244 }
1245
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001246 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1247 // the clipRectBounds() call above?
1248 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001249 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001250 }
1251
reed4960eee2015-12-18 07:09:18 -08001252 bool isOpaque = SkToBool(saveLayerFlags & kIsOpaque_SaveLayerFlag);
reed8dc0ccb2015-03-20 06:32:52 -07001253 SkPixelGeometry geo = fProps.pixelGeometry();
1254 if (paint) {
reed76033be2015-03-14 10:54:31 -07001255 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001256 if (paint->getImageFilter() || paint->getColorFilter()) {
reed76033be2015-03-14 10:54:31 -07001257 isOpaque = false;
reed8dc0ccb2015-03-20 06:32:52 -07001258 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001259 }
1260 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001261
reedb2db8982014-11-13 12:41:02 -08001262 SkBaseDevice* device = this->getTopDevice();
halcanary96fcdcc2015-08-27 07:41:13 -07001263 if (nullptr == device) {
reedb2db8982014-11-13 12:41:02 -08001264 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001265 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001266 }
reedb2db8982014-11-13 12:41:02 -08001267
reed129ed1c2016-02-22 06:42:31 -08001268 SkImageInfo info = make_layer_info(device->imageInfo(), ir.width(), ir.height(), isOpaque,
1269 paint);
1270
reed61f501f2015-04-29 08:34:00 -07001271 bool forceSpriteOnRestore = false;
1272 {
reed70ee31b2015-12-10 13:44:45 -08001273 const bool preserveLCDText = kOpaque_SkAlphaType == info.alphaType() ||
reed4960eee2015-12-18 07:09:18 -08001274 (saveLayerFlags & kPreserveLCDText_SaveLayerFlag);
reeddaa57bf2015-05-15 10:39:17 -07001275 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
reed70ee31b2015-12-10 13:44:45 -08001276 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
1277 preserveLCDText, false);
reed61f501f2015-04-29 08:34:00 -07001278 SkBaseDevice* newDev = device->onCreateDevice(createInfo, paint);
halcanary96fcdcc2015-08-27 07:41:13 -07001279 if (nullptr == newDev) {
reed61f501f2015-04-29 08:34:00 -07001280 // If onCreateDevice didn't succeed, try raster (e.g. PDF couldn't handle the paint)
robertphillips9a53fd72015-06-22 09:46:59 -07001281 const SkSurfaceProps surfaceProps(fProps.flags(), createInfo.fPixelGeometry);
1282 newDev = SkBitmapDevice::Create(createInfo.fInfo, surfaceProps);
halcanary96fcdcc2015-08-27 07:41:13 -07001283 if (nullptr == newDev) {
reed61f501f2015-04-29 08:34:00 -07001284 SkErrorInternals::SetError(kInternalError_SkError,
1285 "Unable to create device for layer.");
1286 return;
1287 }
1288 forceSpriteOnRestore = true;
1289 }
1290 device = newDev;
bungeman@google.come25c6842011-08-17 14:53:54 +00001291 }
reed@google.com6f8f2922011-03-04 22:27:10 +00001292 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips7354a4b2015-12-16 05:08:27 -08001293
reedbfd5f172016-01-07 11:28:08 -08001294 if (rec.fBackdrop) {
1295 draw_filter_into_device(fMCRec->fTopLayer->fDevice, rec.fBackdrop, device, fMCRec->fMatrix);
robertphillips7354a4b2015-12-16 05:08:27 -08001296 }
1297
reed8c30a812016-04-20 16:36:51 -07001298 DeviceCM* layer = new DeviceCM(device, paint, this, fConservativeRasterClip,
1299 forceSpriteOnRestore, stashedMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001300 device->unref();
1301
1302 layer->fNext = fMCRec->fTopLayer;
1303 fMCRec->fLayer = layer;
1304 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reed@android.com8a1c16f2008-12-17 15:59:43 +00001305}
1306
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001307int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001308 if (0xFF == alpha) {
1309 return this->saveLayer(bounds, nullptr);
1310 } else {
1311 SkPaint tmpPaint;
1312 tmpPaint.setAlpha(alpha);
1313 return this->saveLayer(bounds, &tmpPaint);
1314 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001315}
1316
reed@android.com8a1c16f2008-12-17 15:59:43 +00001317void SkCanvas::internalRestore() {
1318 SkASSERT(fMCStack.count() != 0);
1319
1320 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001321 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001322
reed687fa1c2015-04-07 08:00:56 -07001323 fClipStack->restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001324
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001325 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001326 DeviceCM* layer = fMCRec->fLayer; // may be null
1327 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001328 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001329
1330 // now do the normal restore()
1331 fMCRec->~MCRec(); // balanced in save()
1332 fMCStack.pop_back();
1333 fMCRec = (MCRec*)fMCStack.back();
1334
1335 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1336 since if we're being recorded, we don't want to record this (the
1337 recorder will have already recorded the restore).
1338 */
bsalomon49f085d2014-09-05 13:34:00 -07001339 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001340 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001341 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001342 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
reed61f501f2015-04-29 08:34:00 -07001343 layer->fPaint, layer->fDeviceIsBitmapDevice);
reed8c30a812016-04-20 16:36:51 -07001344 // restore what we smashed in internalSaveLayer
1345 fMCRec->fMatrix = layer->fStashedMatrix;
reed@google.com8926b162012-03-23 15:36:36 +00001346 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001347 fDeviceCMDirty = true;
halcanary385fe4d2015-08-26 13:07:48 -07001348 delete layer;
reedb679ca82015-04-07 04:40:48 -07001349 } else {
1350 // we're at the root
reeda499f902015-05-01 09:34:31 -07001351 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001352 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001353 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001354 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001355 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001356}
1357
reede8f30622016-03-23 18:59:25 -07001358sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001359 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001360 props = &fProps;
1361 }
1362 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001363}
1364
reede8f30622016-03-23 18:59:25 -07001365sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001366 SkBaseDevice* dev = this->getDevice();
reede8f30622016-03-23 18:59:25 -07001367 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001368}
1369
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001370SkImageInfo SkCanvas::imageInfo() const {
1371 SkBaseDevice* dev = this->getDevice();
1372 if (dev) {
1373 return dev->imageInfo();
1374 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001375 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001376 }
1377}
1378
brianosman898235c2016-04-06 07:38:23 -07001379bool SkCanvas::getProps(SkSurfaceProps* props) const {
1380 SkBaseDevice* dev = this->getDevice();
1381 if (dev) {
1382 if (props) {
1383 *props = fProps;
1384 }
1385 return true;
1386 } else {
1387 return false;
1388 }
1389}
1390
reed6ceeebd2016-03-09 14:26:26 -08001391#ifdef SK_SUPPORT_LEGACY_PEEKPIXELS_PARMS
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001392const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
reed884e97c2015-05-26 11:31:54 -07001393 SkPixmap pmap;
reed6ceeebd2016-03-09 14:26:26 -08001394 if (this->peekPixels(&pmap)) {
1395 if (info) {
1396 *info = pmap.info();
1397 }
1398 if (rowBytes) {
1399 *rowBytes = pmap.rowBytes();
1400 }
1401 return pmap.addr();
reed884e97c2015-05-26 11:31:54 -07001402 }
reed6ceeebd2016-03-09 14:26:26 -08001403 return nullptr;
1404}
1405#endif
1406
1407bool SkCanvas::peekPixels(SkPixmap* pmap) {
1408 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001409}
1410
reed884e97c2015-05-26 11:31:54 -07001411bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001412 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001413 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001414}
1415
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001416void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001417 SkPixmap pmap;
1418 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001419 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001420 }
1421 if (info) {
1422 *info = pmap.info();
1423 }
1424 if (rowBytes) {
1425 *rowBytes = pmap.rowBytes();
1426 }
1427 if (origin) {
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001428 *origin = this->getTopDevice(false)->getOrigin();
1429 }
reed884e97c2015-05-26 11:31:54 -07001430 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001431}
1432
reed884e97c2015-05-26 11:31:54 -07001433bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001434 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001435 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001436}
1437
reed@android.com8a1c16f2008-12-17 15:59:43 +00001438/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001439
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001440void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed61f501f2015-04-29 08:34:00 -07001441 const SkPaint* paint, bool deviceIsBitmapDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001442 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001443 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001444 paint = &tmp;
1445 }
reed@google.com4b226022011-01-11 18:32:13 +00001446
reed@google.com8926b162012-03-23 15:36:36 +00001447 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001448 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001449 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001450 paint = &looper.paint();
1451 SkImageFilter* filter = paint->getImageFilter();
1452 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
senorblancof35566e2016-04-18 10:32:02 -07001453 if (filter) {
robertphillips4418dba2016-03-07 12:45:14 -08001454 const SkBitmap& srcBM = srcDev->accessBitmap(false);
reed1eca1162016-04-25 12:29:38 -07001455 dstDev->drawSpriteWithFilter(iter, srcBM, pos.x(), pos.y(), *paint);
reed61f501f2015-04-29 08:34:00 -07001456 } else if (deviceIsBitmapDevice) {
reed9572a102015-05-26 19:22:17 -07001457 const SkBitmap& src = static_cast<SkBitmapDevice*>(srcDev)->fBitmap;
reed61f501f2015-04-29 08:34:00 -07001458 dstDev->drawSprite(iter, src, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001459 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001460 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001461 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001462 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001463 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001464}
1465
reed32704672015-12-16 08:27:10 -08001466/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001467
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001468void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001469 SkMatrix m;
1470 m.setTranslate(dx, dy);
1471 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001472}
1473
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001474void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001475 SkMatrix m;
1476 m.setScale(sx, sy);
1477 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001478}
1479
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001480void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001481 SkMatrix m;
1482 m.setRotate(degrees);
1483 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001484}
1485
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001486void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001487 SkMatrix m;
1488 m.setSkew(sx, sy);
1489 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001490}
1491
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001492void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001493 if (matrix.isIdentity()) {
1494 return;
1495 }
1496
reed2ff1fce2014-12-11 07:07:37 -08001497 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001498 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001499 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001500 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001501
1502 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001503}
1504
reed8c30a812016-04-20 16:36:51 -07001505void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001506 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001507 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001508 fMCRec->fMatrix = matrix;
reed8c30a812016-04-20 16:36:51 -07001509}
1510
1511void SkCanvas::setMatrix(const SkMatrix& matrix) {
1512 this->checkForDeferredSave();
1513 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001514 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001515}
1516
reed@android.com8a1c16f2008-12-17 15:59:43 +00001517void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001518 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001519}
1520
1521//////////////////////////////////////////////////////////////////////////////
1522
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001523void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001524 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001525 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1526 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001527}
1528
1529void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001530#ifdef SK_ENABLE_CLIP_QUICKREJECT
1531 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001532 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001533 return false;
1534 }
1535
reed@google.com3b3e8952012-08-16 20:53:31 +00001536 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001537 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001538 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001539
reed687fa1c2015-04-07 08:00:56 -07001540 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001541 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001542 }
1543 }
1544#endif
1545
bsalomonac8cabd2015-11-20 18:53:07 -08001546 if (!fAllowSoftClip) {
1547 edgeStyle = kHard_ClipEdgeStyle;
1548 }
reed90ba0952015-11-20 13:42:47 -08001549
reedc64eff52015-11-21 12:39:45 -08001550 const bool rectStaysRect = fMCRec->fMatrix.rectStaysRect();
1551 SkRect devR;
1552 if (rectStaysRect) {
1553 fMCRec->fMatrix.mapRect(&devR, rect);
1554 }
bsalomonac8cabd2015-11-20 18:53:07 -08001555
reedc64eff52015-11-21 12:39:45 -08001556 // Check if we can quick-accept the clip call (and do nothing)
1557 //
1558 // TODO: investigate if a (conservative) version of this could be done in ::clipRect,
1559 // so that subclasses (like PictureRecording) didn't see unnecessary clips, which in turn
1560 // might allow lazy save/restores to eliminate entire save/restore blocks.
1561 //
1562 if (SkRegion::kIntersect_Op == op &&
1563 kHard_ClipEdgeStyle == edgeStyle
1564 && rectStaysRect)
1565 {
1566 if (devR.round().contains(fMCRec->fRasterClip.getBounds())) {
1567#if 0
1568 SkDebugf("------- ignored clipRect [%g %g %g %g]\n",
1569 rect.left(), rect.top(), rect.right(), rect.bottom());
1570#endif
1571 return;
1572 }
1573 }
1574
1575 AutoValidateClip avc(this);
1576
1577 fDeviceCMDirty = true;
1578 fCachedLocalClipBoundsDirty = true;
1579
1580 if (rectStaysRect) {
1581 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1582 fClipStack->clipDevRect(devR, op, isAA);
senorblancoafc7cce2016-02-02 18:44:15 -08001583 fMCRec->fRasterClip.op(devR, this->getTopLayerBounds(), op, isAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001584 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001585 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001586 // and clip against that, since it can handle any matrix. However, to
1587 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1588 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001589 SkPath path;
1590
1591 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001592 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001593 }
1594}
1595
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001596void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001597 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001598 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001599 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001600 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1601 } else {
1602 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001603 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001604}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001605
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001606void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001607 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001608 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001609 AutoValidateClip avc(this);
1610
1611 fDeviceCMDirty = true;
1612 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001613 if (!fAllowSoftClip) {
1614 edgeStyle = kHard_ClipEdgeStyle;
1615 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001616
reed687fa1c2015-04-07 08:00:56 -07001617 fClipStack->clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001618
senorblancoafc7cce2016-02-02 18:44:15 -08001619 fMCRec->fRasterClip.op(transformedRRect, this->getTopLayerBounds(), op,
robertphillips125f19a2015-11-23 09:00:05 -08001620 kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001621 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001622 }
1623
1624 SkPath path;
1625 path.addRRect(rrect);
1626 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001627 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001628}
1629
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001630void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001631 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001632 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001633
1634 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1635 SkRect r;
1636 if (path.isRect(&r)) {
1637 this->onClipRect(r, op, edgeStyle);
1638 return;
1639 }
1640 SkRRect rrect;
1641 if (path.isOval(&r)) {
1642 rrect.setOval(r);
1643 this->onClipRRect(rrect, op, edgeStyle);
1644 return;
1645 }
1646 if (path.isRRect(&rrect)) {
1647 this->onClipRRect(rrect, op, edgeStyle);
1648 return;
1649 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001650 }
robertphillips39f05382015-11-24 09:30:12 -08001651
1652 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001653}
1654
1655void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001656#ifdef SK_ENABLE_CLIP_QUICKREJECT
1657 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001658 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001659 return false;
1660 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001661
reed@google.com3b3e8952012-08-16 20:53:31 +00001662 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001663 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001664 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001665
reed687fa1c2015-04-07 08:00:56 -07001666 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001667 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001668 }
1669 }
1670#endif
1671
reed@google.com5c3d1472011-02-22 19:12:23 +00001672 AutoValidateClip avc(this);
1673
reed@android.com8a1c16f2008-12-17 15:59:43 +00001674 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001675 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001676 if (!fAllowSoftClip) {
1677 edgeStyle = kHard_ClipEdgeStyle;
1678 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001679
1680 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001681 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001682
reed@google.comfe701122011-11-08 19:41:23 +00001683 // Check if the transfomation, or the original path itself
1684 // made us empty. Note this can also happen if we contained NaN
1685 // values. computing the bounds detects this, and will set our
1686 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1687 if (devPath.getBounds().isEmpty()) {
1688 // resetting the path will remove any NaN or other wanky values
1689 // that might upset our scan converter.
1690 devPath.reset();
1691 }
1692
reed@google.com5c3d1472011-02-22 19:12:23 +00001693 // if we called path.swap() we could avoid a deep copy of this path
reed687fa1c2015-04-07 08:00:56 -07001694 fClipStack->clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001695
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001696 if (fAllowSimplifyClip) {
fmalita1a481fe2015-02-04 07:39:34 -08001697 bool clipIsAA = getClipStack()->asPath(&devPath);
1698 if (clipIsAA) {
1699 edgeStyle = kSoft_ClipEdgeStyle;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001700 }
fmalita1a481fe2015-02-04 07:39:34 -08001701
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001702 op = SkRegion::kReplace_Op;
1703 }
1704
senorblancoafc7cce2016-02-02 18:44:15 -08001705 fMCRec->fRasterClip.op(devPath, this->getTopLayerBounds(), op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001706}
1707
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001708void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed2ff1fce2014-12-11 07:07:37 -08001709 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001710 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001711}
1712
1713void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001714 AutoValidateClip avc(this);
1715
reed@android.com8a1c16f2008-12-17 15:59:43 +00001716 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001717 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001718
reed@google.com5c3d1472011-02-22 19:12:23 +00001719 // todo: signal fClipStack that we have a region, and therefore (I guess)
1720 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001721 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001722
reed1f836ee2014-07-07 07:49:34 -07001723 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001724}
1725
reed@google.com819c9212011-02-23 18:56:55 +00001726#ifdef SK_DEBUG
1727void SkCanvas::validateClip() const {
1728 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001729 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001730 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001731 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001732 return;
1733 }
1734
reed@google.com819c9212011-02-23 18:56:55 +00001735 SkIRect ir;
1736 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001737 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001738
reed687fa1c2015-04-07 08:00:56 -07001739 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001740 const SkClipStack::Element* element;
halcanary96fcdcc2015-08-27 07:41:13 -07001741 while ((element = iter.next()) != nullptr) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001742 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001743 case SkClipStack::Element::kRect_Type:
1744 element->getRect().round(&ir);
1745 tmpClip.op(ir, element->getOp());
1746 break;
1747 case SkClipStack::Element::kEmpty_Type:
1748 tmpClip.setEmpty();
1749 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001750 default: {
1751 SkPath path;
1752 element->asPath(&path);
senorblancoafc7cce2016-02-02 18:44:15 -08001753 tmpClip.op(path, this->getTopLayerBounds(), element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001754 break;
1755 }
reed@google.com819c9212011-02-23 18:56:55 +00001756 }
1757 }
reed@google.com819c9212011-02-23 18:56:55 +00001758}
1759#endif
1760
reed@google.com90c07ea2012-04-13 13:50:27 +00001761void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001762 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001763 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001764
halcanary96fcdcc2015-08-27 07:41:13 -07001765 while ((element = iter.next()) != nullptr) {
fmalitac3b589a2014-06-05 12:40:07 -07001766 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001767 }
1768}
1769
reed@google.com5c3d1472011-02-22 19:12:23 +00001770///////////////////////////////////////////////////////////////////////////////
1771
reed@google.com754de5f2014-02-24 19:38:20 +00001772bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001773 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001774}
1775
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001776bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001777 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001778}
1779
reed@google.com3b3e8952012-08-16 20:53:31 +00001780bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001781 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001782 return true;
1783
reed1f836ee2014-07-07 07:49:34 -07001784 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001785 return true;
1786 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001787
reed1f836ee2014-07-07 07:49:34 -07001788 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001789 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001790 fMCRec->fMatrix.mapRect(&dst, rect);
reedb07a94f2014-11-19 05:03:18 -08001791 return !SkIRect::Intersects(dst.roundOut(), fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001792 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001793 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001794
reed@android.coma380ae42009-07-21 01:17:02 +00001795 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001796 // TODO: should we use | instead, or compare all 4 at once?
1797 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001798 return true;
1799 }
reed@google.comc0784db2013-12-13 21:16:12 +00001800 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001801 return true;
1802 }
1803 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001804 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001805}
1806
reed@google.com3b3e8952012-08-16 20:53:31 +00001807bool SkCanvas::quickReject(const SkPath& path) const {
1808 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001809}
1810
reed@google.com3b3e8952012-08-16 20:53:31 +00001811bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001812 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001813 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001814 return false;
1815 }
1816
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001817 SkMatrix inverse;
1818 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001819 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001820 if (bounds) {
1821 bounds->setEmpty();
1822 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001823 return false;
1824 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001825
bsalomon49f085d2014-09-05 13:34:00 -07001826 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001827 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001828 // adjust it outwards in case we are antialiasing
1829 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001830
reed@google.com8f4d2302013-12-17 16:44:46 +00001831 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1832 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001833 inverse.mapRect(bounds, r);
1834 }
1835 return true;
1836}
1837
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001838bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001839 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001840 if (clip.isEmpty()) {
1841 if (bounds) {
1842 bounds->setEmpty();
1843 }
1844 return false;
1845 }
1846
bsalomon49f085d2014-09-05 13:34:00 -07001847 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001848 *bounds = clip.getBounds();
1849 }
1850 return true;
1851}
1852
reed@android.com8a1c16f2008-12-17 15:59:43 +00001853const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001854 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001855}
1856
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001857const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001858 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001859}
1860
reed@google.com9c135db2014-03-12 18:28:35 +00001861GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1862 SkBaseDevice* dev = this->getTopDevice();
halcanary96fcdcc2015-08-27 07:41:13 -07001863 return dev ? dev->accessRenderTarget() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001864}
1865
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001866GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001867 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001868 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001869}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001870
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001871void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1872 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001873 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001874 if (outer.isEmpty()) {
1875 return;
1876 }
1877 if (inner.isEmpty()) {
1878 this->drawRRect(outer, paint);
1879 return;
1880 }
1881
1882 // We don't have this method (yet), but technically this is what we should
1883 // be able to assert...
1884 // SkASSERT(outer.contains(inner));
1885 //
1886 // For now at least check for containment of bounds
1887 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1888
1889 this->onDrawDRRect(outer, inner, paint);
1890}
1891
reed41af9662015-01-05 07:49:08 -08001892// These need to stop being virtual -- clients need to override the onDraw... versions
1893
1894void SkCanvas::drawPaint(const SkPaint& paint) {
1895 this->onDrawPaint(paint);
1896}
1897
1898void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1899 this->onDrawRect(r, paint);
1900}
1901
1902void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1903 this->onDrawOval(r, paint);
1904}
1905
1906void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1907 this->onDrawRRect(rrect, paint);
1908}
1909
1910void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1911 this->onDrawPoints(mode, count, pts, paint);
1912}
1913
1914void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
1915 const SkPoint texs[], const SkColor colors[], SkXfermode* xmode,
1916 const uint16_t indices[], int indexCount, const SkPaint& paint) {
1917 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
1918 indices, indexCount, paint);
1919}
1920
1921void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1922 this->onDrawPath(path, paint);
1923}
1924
reeda85d4d02015-05-06 12:56:48 -07001925void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001926 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001927 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001928}
1929
reede47829b2015-08-06 10:02:53 -07001930void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, 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 if (dst.isEmpty() || src.isEmpty()) {
1934 return;
1935 }
1936 this->onDrawImageRect(image, &src, dst, paint, constraint);
1937}
reed41af9662015-01-05 07:49:08 -08001938
reed84984ef2015-07-17 07:09:43 -07001939void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1940 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001941 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001942 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001943}
1944
reede47829b2015-08-06 10:02:53 -07001945void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
1946 SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001947 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001948 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
1949 constraint);
1950}
reede47829b2015-08-06 10:02:53 -07001951
reed4c21dc52015-06-25 12:32:03 -07001952void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1953 const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001954 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07001955 if (dst.isEmpty()) {
1956 return;
1957 }
1958 if (!SkNinePatchIter::Valid(image->width(), image->height(), center)) {
reede47829b2015-08-06 10:02:53 -07001959 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001960 }
1961 this->onDrawImageNine(image, center, dst, paint);
1962}
1963
reed41af9662015-01-05 07:49:08 -08001964void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001965 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001966 return;
1967 }
reed41af9662015-01-05 07:49:08 -08001968 this->onDrawBitmap(bitmap, dx, dy, paint);
1969}
1970
reede47829b2015-08-06 10:02:53 -07001971void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07001972 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001973 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07001974 return;
1975 }
reede47829b2015-08-06 10:02:53 -07001976 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08001977}
1978
reed84984ef2015-07-17 07:09:43 -07001979void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
1980 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001981 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001982}
1983
reede47829b2015-08-06 10:02:53 -07001984void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
1985 SrcRectConstraint constraint) {
1986 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
1987 constraint);
1988}
reede47829b2015-08-06 10:02:53 -07001989
reed41af9662015-01-05 07:49:08 -08001990void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
1991 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001992 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001993 return;
1994 }
reed4c21dc52015-06-25 12:32:03 -07001995 if (!SkNinePatchIter::Valid(bitmap.width(), bitmap.height(), center)) {
reeda5517e22015-07-14 10:54:12 -07001996 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001997 }
reed41af9662015-01-05 07:49:08 -08001998 this->onDrawBitmapNine(bitmap, center, dst, paint);
1999}
2000
reed71c3c762015-06-24 10:29:17 -07002001void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2002 const SkColor colors[], int count, SkXfermode::Mode mode,
2003 const SkRect* cull, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002004 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002005 if (count <= 0) {
2006 return;
2007 }
2008 SkASSERT(atlas);
2009 SkASSERT(xform);
2010 SkASSERT(tex);
2011 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
2012}
2013
reedf70b5312016-03-04 16:36:20 -08002014void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2015 if (key) {
2016 this->onDrawAnnotation(rect, key, value);
2017 }
2018}
2019
reede47829b2015-08-06 10:02:53 -07002020void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2021 const SkPaint* paint, SrcRectConstraint constraint) {
2022 if (src) {
2023 this->drawImageRect(image, *src, dst, paint, constraint);
2024 } else {
2025 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2026 dst, paint, constraint);
2027 }
2028}
2029void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2030 const SkPaint* paint, SrcRectConstraint constraint) {
2031 if (src) {
2032 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2033 } else {
2034 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2035 dst, paint, constraint);
2036 }
2037}
2038
reed@android.com8a1c16f2008-12-17 15:59:43 +00002039//////////////////////////////////////////////////////////////////////////////
2040// These are the virtual drawing methods
2041//////////////////////////////////////////////////////////////////////////////
2042
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002043void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002044 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002045 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2046 }
2047}
2048
reed41af9662015-01-05 07:49:08 -08002049void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002050 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002051 this->internalDrawPaint(paint);
2052}
2053
2054void SkCanvas::internalDrawPaint(const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002055 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002056
2057 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002058 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002059 }
2060
reed@google.com4e2b3d32011-04-07 14:18:59 +00002061 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002062}
2063
reed41af9662015-01-05 07:49:08 -08002064void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2065 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002066 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002067 if ((long)count <= 0) {
2068 return;
2069 }
2070
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002071 SkRect r, storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002072 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002073 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002074 // special-case 2 points (common for drawing a single line)
2075 if (2 == count) {
2076 r.set(pts[0], pts[1]);
2077 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00002078 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002079 }
senorblanco87e066e2015-10-28 11:23:36 -07002080 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2081 return;
2082 }
2083 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002084 }
reed@google.coma584aed2012-05-16 14:06:02 +00002085
halcanary96fcdcc2015-08-27 07:41:13 -07002086 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002087
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002088 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002089
reed@android.com8a1c16f2008-12-17 15:59:43 +00002090 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002091 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002092 }
reed@google.com4b226022011-01-11 18:32:13 +00002093
reed@google.com4e2b3d32011-04-07 14:18:59 +00002094 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002095}
2096
reed41af9662015-01-05 07:49:08 -08002097void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002098 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002099 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002100 const SkRect* bounds = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002101 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08002102 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
2103 // To prevent accidental rejecting at this stage, we have to sort it before we check.
2104 SkRect tmp(r);
2105 tmp.sort();
2106
senorblanco87e066e2015-10-28 11:23:36 -07002107 if (this->quickReject(paint.computeFastBounds(tmp, &storage))) {
2108 return;
2109 }
2110 bounds = &r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002111 }
reed@google.com4b226022011-01-11 18:32:13 +00002112
reedc83a2972015-07-16 07:40:45 -07002113 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002114
2115 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002116 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002117 }
2118
reed@google.com4e2b3d32011-04-07 14:18:59 +00002119 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002120}
2121
reed41af9662015-01-05 07:49:08 -08002122void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002123 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002124 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002125 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002126 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002127 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2128 return;
2129 }
2130 bounds = &oval;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002131 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002132
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002133 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002134
2135 while (iter.next()) {
2136 iter.fDevice->drawOval(iter, oval, looper.paint());
2137 }
2138
2139 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002140}
2141
reed41af9662015-01-05 07:49:08 -08002142void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002143 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002144 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002145 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002146 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002147 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2148 return;
2149 }
2150 bounds = &rrect.getBounds();
reed@google.com4ed0fb72012-12-12 20:48:18 +00002151 }
2152
2153 if (rrect.isRect()) {
2154 // call the non-virtual version
2155 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002156 return;
2157 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002158 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002159 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2160 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002161 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002162
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002163 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002164
2165 while (iter.next()) {
2166 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2167 }
2168
2169 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002170}
2171
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002172void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2173 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002174 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002175 const SkRect* bounds = nullptr;
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002176 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002177 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2178 return;
2179 }
2180 bounds = &outer.getBounds();
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002181 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002182
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002183 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002184
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002185 while (iter.next()) {
2186 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2187 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002188
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002189 LOOPER_END
2190}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002191
reed41af9662015-01-05 07:49:08 -08002192void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002193 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002194 if (!path.isFinite()) {
2195 return;
2196 }
2197
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002198 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002199 const SkRect* bounds = nullptr;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002200 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002201 const SkRect& pathBounds = path.getBounds();
senorblanco87e066e2015-10-28 11:23:36 -07002202 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2203 return;
2204 }
2205 bounds = &pathBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002206 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002207
2208 const SkRect& r = path.getBounds();
2209 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002210 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002211 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002212 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002213 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002214 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002215
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002216 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002217
2218 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002219 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002220 }
2221
reed@google.com4e2b3d32011-04-07 14:18:59 +00002222 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002223}
2224
reed262a71b2015-12-05 13:07:27 -08002225bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002226 if (!paint.getImageFilter()) {
2227 return false;
2228 }
2229
2230 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002231 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002232 return false;
2233 }
2234
2235 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2236 // Once we can filter and the filter will return a result larger than itself, we should be
2237 // able to remove this constraint.
2238 // skbug.com/4526
2239 //
2240 SkPoint pt;
2241 ctm.mapXY(x, y, &pt);
2242 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2243 return ir.contains(fMCRec->fRasterClip.getBounds());
2244}
2245
reeda85d4d02015-05-06 12:56:48 -07002246void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002247 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002248 SkRect bounds = SkRect::MakeXYWH(x, y,
2249 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002250 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002251 SkRect tmp = bounds;
2252 if (paint) {
2253 paint->computeFastBounds(tmp, &tmp);
2254 }
2255 if (this->quickReject(tmp)) {
2256 return;
2257 }
reeda85d4d02015-05-06 12:56:48 -07002258 }
halcanary9d524f22016-03-29 09:03:52 -07002259
reeda85d4d02015-05-06 12:56:48 -07002260 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002261 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002262 paint = lazy.init();
2263 }
reed262a71b2015-12-05 13:07:27 -08002264
reed129ed1c2016-02-22 06:42:31 -08002265 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2266 *paint);
2267 if (drawAsSprite && paint->getImageFilter()) {
2268 SkBitmap bitmap;
2269 if (!as_IB(image)->asBitmapForImageFilters(&bitmap)) {
2270 drawAsSprite = false;
2271 } else{
2272 // Until imagefilters are updated, they cannot handle any src type but N32...
2273 if (bitmap.info().colorType() != kN32_SkColorType || bitmap.info().isSRGB()) {
2274 drawAsSprite = false;
2275 }
2276 }
2277 }
2278
reed262a71b2015-12-05 13:07:27 -08002279 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
2280
reeda85d4d02015-05-06 12:56:48 -07002281 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002282 const SkPaint& pnt = looper.paint();
2283 if (drawAsSprite && pnt.getImageFilter()) {
2284 SkBitmap bitmap;
2285 if (as_IB(image)->asBitmapForImageFilters(&bitmap)) {
2286 SkPoint pt;
2287 iter.fMatrix->mapXY(x, y, &pt);
robertphillips2302de92016-03-24 07:26:32 -07002288 iter.fDevice->drawSpriteWithFilter(iter, bitmap,
2289 SkScalarRoundToInt(pt.fX),
2290 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002291 }
2292 } else {
2293 iter.fDevice->drawImage(iter, image, x, y, pnt);
2294 }
reeda85d4d02015-05-06 12:56:48 -07002295 }
halcanary9d524f22016-03-29 09:03:52 -07002296
reeda85d4d02015-05-06 12:56:48 -07002297 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002298}
2299
reed41af9662015-01-05 07:49:08 -08002300void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002301 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002302 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
halcanary96fcdcc2015-08-27 07:41:13 -07002303 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002304 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002305 if (paint) {
2306 paint->computeFastBounds(dst, &storage);
2307 }
2308 if (this->quickReject(storage)) {
2309 return;
2310 }
reeda85d4d02015-05-06 12:56:48 -07002311 }
2312 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002313 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002314 paint = lazy.init();
2315 }
halcanary9d524f22016-03-29 09:03:52 -07002316
senorblancoc41e7e12015-12-07 12:51:30 -08002317 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002318 image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002319
reeda85d4d02015-05-06 12:56:48 -07002320 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002321 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002322 }
halcanary9d524f22016-03-29 09:03:52 -07002323
reeda85d4d02015-05-06 12:56:48 -07002324 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002325}
2326
reed41af9662015-01-05 07:49:08 -08002327void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002328 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002329 SkDEBUGCODE(bitmap.validate();)
2330
reed33366972015-10-08 09:22:02 -07002331 if (bitmap.drawsNothing()) {
2332 return;
2333 }
2334
2335 SkLazyPaint lazy;
2336 if (nullptr == paint) {
2337 paint = lazy.init();
2338 }
2339
2340 const SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2341
2342 SkRect storage;
2343 const SkRect* bounds = nullptr;
2344 if (paint->canComputeFastBounds()) {
2345 bitmap.getBounds(&storage);
2346 matrix.mapRect(&storage);
senorblanco87e066e2015-10-28 11:23:36 -07002347 SkRect tmp = storage;
2348 if (this->quickReject(paint->computeFastBounds(tmp, &tmp))) {
2349 return;
2350 }
2351 bounds = &storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002352 }
reed@google.com4b226022011-01-11 18:32:13 +00002353
reed129ed1c2016-02-22 06:42:31 -08002354 bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(), bitmap.height(),
2355 *paint);
2356 if (drawAsSprite && paint->getImageFilter()) {
2357 // Until imagefilters are updated, they cannot handle any src type but N32...
2358 if (bitmap.info().colorType() != kN32_SkColorType || bitmap.info().isSRGB()) {
2359 drawAsSprite = false;
2360 }
2361 }
2362
reed262a71b2015-12-05 13:07:27 -08002363 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds)
reed33366972015-10-08 09:22:02 -07002364
2365 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002366 const SkPaint& pnt = looper.paint();
2367 if (drawAsSprite && pnt.getImageFilter()) {
2368 SkPoint pt;
2369 iter.fMatrix->mapXY(x, y, &pt);
robertphillips2302de92016-03-24 07:26:32 -07002370 iter.fDevice->drawSpriteWithFilter(iter, bitmap,
2371 SkScalarRoundToInt(pt.fX),
2372 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002373 } else {
2374 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
2375 }
reed33366972015-10-08 09:22:02 -07002376 }
reed262a71b2015-12-05 13:07:27 -08002377
reed33366972015-10-08 09:22:02 -07002378 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002379}
2380
reed@google.com9987ec32011-09-07 11:57:52 +00002381// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002382void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002383 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002384 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002385 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002386 return;
2387 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002388
halcanary96fcdcc2015-08-27 07:41:13 -07002389 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002390 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002391 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2392 return;
2393 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002394 }
reed@google.com3d608122011-11-21 15:16:16 +00002395
reed@google.com33535f32012-09-25 15:37:50 +00002396 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002397 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002398 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002399 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002400
senorblancoc41e7e12015-12-07 12:51:30 -08002401 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002402 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002403
reed@google.com33535f32012-09-25 15:37:50 +00002404 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002405 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002406 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002407
reed@google.com33535f32012-09-25 15:37:50 +00002408 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002409}
2410
reed41af9662015-01-05 07:49:08 -08002411void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002412 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002413 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002414 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002415 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002416}
2417
reed4c21dc52015-06-25 12:32:03 -07002418void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2419 const SkPaint* paint) {
2420 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
halcanary9d524f22016-03-29 09:03:52 -07002421
halcanary96fcdcc2015-08-27 07:41:13 -07002422 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002423 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002424 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2425 return;
2426 }
reed@google.com3d608122011-11-21 15:16:16 +00002427 }
halcanary9d524f22016-03-29 09:03:52 -07002428
reed4c21dc52015-06-25 12:32:03 -07002429 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002430 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002431 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002432 }
halcanary9d524f22016-03-29 09:03:52 -07002433
senorblancoc41e7e12015-12-07 12:51:30 -08002434 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002435
reed4c21dc52015-06-25 12:32:03 -07002436 while (iter.next()) {
2437 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002438 }
halcanary9d524f22016-03-29 09:03:52 -07002439
reed4c21dc52015-06-25 12:32:03 -07002440 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002441}
2442
reed41af9662015-01-05 07:49:08 -08002443void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2444 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002445 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002446 SkDEBUGCODE(bitmap.validate();)
2447
halcanary96fcdcc2015-08-27 07:41:13 -07002448 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002449 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002450 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2451 return;
2452 }
reed4c21dc52015-06-25 12:32:03 -07002453 }
halcanary9d524f22016-03-29 09:03:52 -07002454
reed4c21dc52015-06-25 12:32:03 -07002455 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002456 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002457 paint = lazy.init();
2458 }
halcanary9d524f22016-03-29 09:03:52 -07002459
senorblancoc41e7e12015-12-07 12:51:30 -08002460 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002461
reed4c21dc52015-06-25 12:32:03 -07002462 while (iter.next()) {
2463 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2464 }
halcanary9d524f22016-03-29 09:03:52 -07002465
reed4c21dc52015-06-25 12:32:03 -07002466 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002467}
2468
reed@google.comf67e4cf2011-03-15 20:56:58 +00002469class SkDeviceFilteredPaint {
2470public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002471 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002472 uint32_t filteredFlags = device->filterTextFlags(paint);
2473 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002474 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002475 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002476 fPaint = newPaint;
2477 } else {
2478 fPaint = &paint;
2479 }
2480 }
2481
reed@google.comf67e4cf2011-03-15 20:56:58 +00002482 const SkPaint& paint() const { return *fPaint; }
2483
2484private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002485 const SkPaint* fPaint;
2486 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002487};
2488
bungeman@google.com52c748b2011-08-22 21:30:43 +00002489void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2490 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002491 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002492 draw.fDevice->drawRect(draw, r, paint);
2493 } else {
2494 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002495 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002496 draw.fDevice->drawRect(draw, r, p);
2497 }
2498}
2499
2500void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2501 const char text[], size_t byteLength,
2502 SkScalar x, SkScalar y) {
halcanary96fcdcc2015-08-27 07:41:13 -07002503 SkASSERT(byteLength == 0 || text != nullptr);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002504
2505 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07002506 if (text == nullptr || byteLength == 0 ||
reed1e7f5e72016-04-27 07:49:17 -07002507 draw.fRC->isEmpty() ||
halcanary96fcdcc2015-08-27 07:41:13 -07002508 (paint.getAlpha() == 0 && paint.getXfermode() == nullptr)) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002509 return;
2510 }
2511
2512 SkScalar width = 0;
2513 SkPoint start;
2514
2515 start.set(0, 0); // to avoid warning
2516 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2517 SkPaint::kStrikeThruText_Flag)) {
2518 width = paint.measureText(text, byteLength);
2519
2520 SkScalar offsetX = 0;
2521 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2522 offsetX = SkScalarHalf(width);
2523 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2524 offsetX = width;
2525 }
2526 start.set(x - offsetX, y);
2527 }
2528
2529 if (0 == width) {
2530 return;
2531 }
2532
2533 uint32_t flags = paint.getFlags();
2534
2535 if (flags & (SkPaint::kUnderlineText_Flag |
2536 SkPaint::kStrikeThruText_Flag)) {
2537 SkScalar textSize = paint.getTextSize();
2538 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2539 SkRect r;
2540
2541 r.fLeft = start.fX;
2542 r.fRight = start.fX + width;
2543
2544 if (flags & SkPaint::kUnderlineText_Flag) {
2545 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2546 start.fY);
2547 r.fTop = offset;
2548 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002549 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002550 }
2551 if (flags & SkPaint::kStrikeThruText_Flag) {
2552 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2553 start.fY);
2554 r.fTop = offset;
2555 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002556 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002557 }
2558 }
2559}
2560
reed@google.come0d9ce82014-04-23 04:00:17 +00002561void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2562 const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002563 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002564
2565 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002566 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002567 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002568 DrawTextDecorations(iter, dfp.paint(),
2569 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002570 }
2571
reed@google.com4e2b3d32011-04-07 14:18:59 +00002572 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002573}
2574
reed@google.come0d9ce82014-04-23 04:00:17 +00002575void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2576 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002577 SkPoint textOffset = SkPoint::Make(0, 0);
2578
halcanary96fcdcc2015-08-27 07:41:13 -07002579 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002580
reed@android.com8a1c16f2008-12-17 15:59:43 +00002581 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002582 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002583 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002584 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002585 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002586
reed@google.com4e2b3d32011-04-07 14:18:59 +00002587 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002588}
2589
reed@google.come0d9ce82014-04-23 04:00:17 +00002590void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2591 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002592
2593 SkPoint textOffset = SkPoint::Make(0, constY);
2594
halcanary96fcdcc2015-08-27 07:41:13 -07002595 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002596
reed@android.com8a1c16f2008-12-17 15:59:43 +00002597 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002598 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002599 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002600 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002601 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002602
reed@google.com4e2b3d32011-04-07 14:18:59 +00002603 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002604}
2605
reed@google.come0d9ce82014-04-23 04:00:17 +00002606void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2607 const SkMatrix* matrix, const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002608 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002609
reed@android.com8a1c16f2008-12-17 15:59:43 +00002610 while (iter.next()) {
2611 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002612 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002613 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002614
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002615 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002616}
2617
fmalita00d5c2c2014-08-21 08:53:26 -07002618void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2619 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002620
fmalita85d5eb92015-03-04 11:20:12 -08002621 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002622 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002623 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002624 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002625 SkRect tmp;
2626 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2627 return;
2628 }
2629 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002630 }
2631
fmalita024f9962015-03-03 19:08:17 -08002632 // We cannot filter in the looper as we normally do, because the paint is
2633 // incomplete at this point (text-related attributes are embedded within blob run paints).
2634 SkDrawFilter* drawFilter = fMCRec->fFilter;
halcanary96fcdcc2015-08-27 07:41:13 -07002635 fMCRec->fFilter = nullptr;
fmalita024f9962015-03-03 19:08:17 -08002636
fmalita85d5eb92015-03-04 11:20:12 -08002637 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002638
fmalitaaa1b9122014-08-28 14:32:24 -07002639 while (iter.next()) {
2640 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002641 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002642 }
2643
fmalitaaa1b9122014-08-28 14:32:24 -07002644 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002645
2646 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002647}
2648
reed@google.come0d9ce82014-04-23 04:00:17 +00002649// These will become non-virtual, so they always call the (virtual) onDraw... method
2650void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2651 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002652 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002653 this->onDrawText(text, byteLength, x, y, paint);
2654}
2655void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2656 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002657 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002658 this->onDrawPosText(text, byteLength, pos, paint);
2659}
2660void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2661 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002662 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002663 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2664}
2665void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2666 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002667 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002668 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2669}
fmalita00d5c2c2014-08-21 08:53:26 -07002670void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2671 const SkPaint& paint) {
reede3b38ce2016-01-08 09:18:44 -08002672 RETURN_ON_NULL(blob);
danakj9881d632014-11-26 12:41:06 -08002673 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
reede3b38ce2016-01-08 09:18:44 -08002674 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002675}
reed@google.come0d9ce82014-04-23 04:00:17 +00002676
reed41af9662015-01-05 07:49:08 -08002677void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2678 const SkPoint verts[], const SkPoint texs[],
2679 const SkColor colors[], SkXfermode* xmode,
2680 const uint16_t indices[], int indexCount,
2681 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002682 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
halcanary96fcdcc2015-08-27 07:41:13 -07002683 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
reed@google.com4b226022011-01-11 18:32:13 +00002684
reed@android.com8a1c16f2008-12-17 15:59:43 +00002685 while (iter.next()) {
2686 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002687 colors, xmode, indices, indexCount,
2688 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002689 }
reed@google.com4b226022011-01-11 18:32:13 +00002690
reed@google.com4e2b3d32011-04-07 14:18:59 +00002691 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002692}
2693
dandovb3c9d1c2014-08-12 08:34:29 -07002694void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2695 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002696 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
halcanary96fcdcc2015-08-27 07:41:13 -07002697 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002698 return;
2699 }
mtklein6cfa73a2014-08-13 13:33:49 -07002700
dandovecfff212014-08-04 10:02:00 -07002701 // Since a patch is always within the convex hull of the control points, we discard it when its
2702 // bounding rectangle is completely outside the current clip.
2703 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002704 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002705 if (this->quickReject(bounds)) {
2706 return;
2707 }
mtklein6cfa73a2014-08-13 13:33:49 -07002708
dandovb3c9d1c2014-08-12 08:34:29 -07002709 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2710}
2711
2712void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2713 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2714
halcanary96fcdcc2015-08-27 07:41:13 -07002715 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002716
dandovecfff212014-08-04 10:02:00 -07002717 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002718 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002719 }
mtklein6cfa73a2014-08-13 13:33:49 -07002720
dandovecfff212014-08-04 10:02:00 -07002721 LOOPER_END
2722}
2723
reeda8db7282015-07-07 10:22:31 -07002724void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
reede3b38ce2016-01-08 09:18:44 -08002725 RETURN_ON_NULL(dr);
2726 if (x || y) {
2727 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2728 this->onDrawDrawable(dr, &matrix);
2729 } else {
2730 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002731 }
2732}
2733
reeda8db7282015-07-07 10:22:31 -07002734void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reede3b38ce2016-01-08 09:18:44 -08002735 RETURN_ON_NULL(dr);
2736 if (matrix && matrix->isIdentity()) {
2737 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002738 }
reede3b38ce2016-01-08 09:18:44 -08002739 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002740}
2741
2742void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2743 SkRect bounds = dr->getBounds();
2744 if (matrix) {
2745 matrix->mapRect(&bounds);
2746 }
2747 if (this->quickReject(bounds)) {
2748 return;
2749 }
2750 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002751}
2752
reed71c3c762015-06-24 10:29:17 -07002753void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2754 const SkColor colors[], int count, SkXfermode::Mode mode,
2755 const SkRect* cull, const SkPaint* paint) {
2756 if (cull && this->quickReject(*cull)) {
2757 return;
2758 }
2759
2760 SkPaint pnt;
2761 if (paint) {
2762 pnt = *paint;
2763 }
halcanary9d524f22016-03-29 09:03:52 -07002764
halcanary96fcdcc2015-08-27 07:41:13 -07002765 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr)
reed71c3c762015-06-24 10:29:17 -07002766 while (iter.next()) {
2767 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, mode, pnt);
2768 }
2769 LOOPER_END
2770}
2771
reedf70b5312016-03-04 16:36:20 -08002772void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2773 SkASSERT(key);
2774
2775 SkPaint paint;
2776 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, nullptr)
2777 while (iter.next()) {
2778 iter.fDevice->drawAnnotation(iter, rect, key, value);
2779 }
2780 LOOPER_END
2781}
2782
reed@android.com8a1c16f2008-12-17 15:59:43 +00002783//////////////////////////////////////////////////////////////////////////////
2784// These methods are NOT virtual, and therefore must call back into virtual
2785// methods, rather than actually drawing themselves.
2786//////////////////////////////////////////////////////////////////////////////
2787
2788void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002789 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002790 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002791 SkPaint paint;
2792
2793 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002794 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002795 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002796 }
2797 this->drawPaint(paint);
2798}
2799
reed@android.com845fdac2009-06-23 03:01:32 +00002800void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002801 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002802 SkPaint paint;
2803
2804 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002805 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002806 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002807 }
2808 this->drawPaint(paint);
2809}
2810
2811void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002812 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002813 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002814
reed@android.com8a1c16f2008-12-17 15:59:43 +00002815 pt.set(x, y);
2816 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2817}
2818
2819void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002820 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002821 SkPoint pt;
2822 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002823
reed@android.com8a1c16f2008-12-17 15:59:43 +00002824 pt.set(x, y);
2825 paint.setColor(color);
2826 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2827}
2828
2829void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2830 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002831 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002832 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002833
reed@android.com8a1c16f2008-12-17 15:59:43 +00002834 pts[0].set(x0, y0);
2835 pts[1].set(x1, y1);
2836 this->drawPoints(kLines_PointMode, 2, pts, paint);
2837}
2838
2839void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2840 SkScalar right, SkScalar bottom,
2841 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002842 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002843 SkRect r;
2844
2845 r.set(left, top, right, bottom);
2846 this->drawRect(r, paint);
2847}
2848
2849void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2850 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002851 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002852 if (radius < 0) {
2853 radius = 0;
2854 }
2855
2856 SkRect r;
2857 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002858 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002859}
2860
2861void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2862 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002863 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002864 if (rx > 0 && ry > 0) {
2865 if (paint.canComputeFastBounds()) {
2866 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002867 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002868 return;
2869 }
2870 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002871 SkRRect rrect;
2872 rrect.setRectXY(r, rx, ry);
2873 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002874 } else {
2875 this->drawRect(r, paint);
2876 }
2877}
2878
reed@android.com8a1c16f2008-12-17 15:59:43 +00002879void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2880 SkScalar sweepAngle, bool useCenter,
2881 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002882 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002883 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2884 this->drawOval(oval, paint);
2885 } else {
2886 SkPath path;
2887 if (useCenter) {
2888 path.moveTo(oval.centerX(), oval.centerY());
2889 }
2890 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2891 if (useCenter) {
2892 path.close();
2893 }
2894 this->drawPath(path, paint);
2895 }
2896}
2897
2898void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2899 const SkPath& path, SkScalar hOffset,
2900 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002901 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002902 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002903
reed@android.com8a1c16f2008-12-17 15:59:43 +00002904 matrix.setTranslate(hOffset, vOffset);
2905 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2906}
2907
reed@android.comf76bacf2009-05-13 14:00:33 +00002908///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07002909
2910/**
2911 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2912 * against the playback cost of recursing into the subpicture to get at its actual ops.
2913 *
2914 * For now we pick a conservatively small value, though measurement (and other heuristics like
2915 * the type of ops contained) may justify changing this value.
2916 */
2917#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07002918
reedd5fa1a42014-08-09 11:08:05 -07002919void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002920 RETURN_ON_NULL(picture);
2921
reed1c2c4412015-04-30 13:09:24 -07002922 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
reede3b38ce2016-01-08 09:18:44 -08002923 if (matrix && matrix->isIdentity()) {
2924 matrix = nullptr;
2925 }
2926 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2927 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2928 picture->playback(this);
2929 } else {
2930 this->onDrawPicture(picture, matrix, paint);
reedd5fa1a42014-08-09 11:08:05 -07002931 }
2932}
robertphillips9b14f262014-06-04 05:40:44 -07002933
reedd5fa1a42014-08-09 11:08:05 -07002934void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2935 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07002936 if (!paint || paint->canComputeFastBounds()) {
2937 SkRect bounds = picture->cullRect();
2938 if (paint) {
2939 paint->computeFastBounds(bounds, &bounds);
2940 }
2941 if (matrix) {
2942 matrix->mapRect(&bounds);
2943 }
2944 if (this->quickReject(bounds)) {
2945 return;
2946 }
2947 }
2948
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002949 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002950 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002951 // Canvas has to first give the device the opportunity to render
2952 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002953 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002954 return; // the device has rendered the entire picture
2955 }
2956 }
2957
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002958 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07002959 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002960}
2961
reed@android.com8a1c16f2008-12-17 15:59:43 +00002962///////////////////////////////////////////////////////////////////////////////
2963///////////////////////////////////////////////////////////////////////////////
2964
2965SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
bungeman99fe8222015-08-20 07:57:51 -07002966 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002967
2968 SkASSERT(canvas);
2969
2970 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2971 fDone = !fImpl->next();
2972}
2973
2974SkCanvas::LayerIter::~LayerIter() {
2975 fImpl->~SkDrawIter();
2976}
2977
2978void SkCanvas::LayerIter::next() {
2979 fDone = !fImpl->next();
2980}
2981
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002982SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002983 return fImpl->getDevice();
2984}
2985
2986const SkMatrix& SkCanvas::LayerIter::matrix() const {
2987 return fImpl->getMatrix();
2988}
2989
2990const SkPaint& SkCanvas::LayerIter::paint() const {
2991 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07002992 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002993 paint = &fDefaultPaint;
2994 }
2995 return *paint;
2996}
2997
reed1e7f5e72016-04-27 07:49:17 -07002998const SkRasterClip& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002999int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
3000int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003001
3002///////////////////////////////////////////////////////////////////////////////
3003
fmalitac3b589a2014-06-05 12:40:07 -07003004SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003005
3006///////////////////////////////////////////////////////////////////////////////
3007
3008static bool supported_for_raster_canvas(const SkImageInfo& info) {
3009 switch (info.alphaType()) {
3010 case kPremul_SkAlphaType:
3011 case kOpaque_SkAlphaType:
3012 break;
3013 default:
3014 return false;
3015 }
3016
3017 switch (info.colorType()) {
3018 case kAlpha_8_SkColorType:
3019 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00003020 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003021 break;
3022 default:
3023 return false;
3024 }
3025
3026 return true;
3027}
3028
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003029SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
3030 if (!supported_for_raster_canvas(info)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003031 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003032 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003033
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003034 SkBitmap bitmap;
3035 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003036 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003037 }
halcanary385fe4d2015-08-26 13:07:48 -07003038 return new SkCanvas(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003039}
reedd5fa1a42014-08-09 11:08:05 -07003040
3041///////////////////////////////////////////////////////////////////////////////
3042
3043SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003044 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07003045 : fCanvas(canvas)
3046 , fSaveCount(canvas->getSaveCount())
3047{
bsalomon49f085d2014-09-05 13:34:00 -07003048 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003049 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07003050 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003051 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07003052 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003053 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07003054 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003055 canvas->save();
3056 }
mtklein6cfa73a2014-08-13 13:33:49 -07003057
bsalomon49f085d2014-09-05 13:34:00 -07003058 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003059 canvas->concat(*matrix);
3060 }
3061}
3062
3063SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
3064 fCanvas->restoreToCount(fSaveCount);
3065}
reede8f30622016-03-23 18:59:25 -07003066
3067#ifdef SK_SUPPORT_LEGACY_NEW_SURFACE_API
3068SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
3069 return this->makeSurface(info, props).release();
3070}
3071#endif