blob: 89c3c9e0061d09ac1511d36303e629d142820cba [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"
20#include "SkMatrixUtils.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000021#include "SkMetaData.h"
reed4c21dc52015-06-25 12:32:03 -070022#include "SkNinePatchIter.h"
reedc83a2972015-07-16 07:40:45 -070023#include "SkPaintPriv.h"
dandovb3c9d1c2014-08-12 08:34:29 -070024#include "SkPatchUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000025#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000026#include "SkRasterClip.h"
reed96472de2014-12-10 09:53:42 -080027#include "SkReadPixelsRec.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000028#include "SkRRect.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000029#include "SkSmallAllocator.h"
robertphillips4418dba2016-03-07 12:45:14 -080030#include "SkSpecialImage.h"
reed@google.com97af1a62012-08-28 12:19:02 +000031#include "SkSurface_Base.h"
fmalita7ba7aa72014-08-29 09:46:36 -070032#include "SkTextBlob.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000033#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000034#include "SkTLazy.h"
danakj8f757f52014-11-04 11:48:43 -080035#include "SkTraceEvent.h"
bungemand3ebb482015-08-05 13:57:49 -070036
37#include <new>
reed@android.com8a1c16f2008-12-17 15:59:43 +000038
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000039#if SK_SUPPORT_GPU
robertphillips7354a4b2015-12-16 05:08:27 -080040#include "GrContext.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000041#include "GrRenderTarget.h"
robertphillips7354a4b2015-12-16 05:08:27 -080042#include "SkGr.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000043#endif
44
reede3b38ce2016-01-08 09:18:44 -080045#define RETURN_ON_NULL(ptr) do { if (nullptr == (ptr)) return; } while (0)
46
reedc83a2972015-07-16 07:40:45 -070047/*
48 * Return true if the drawing this rect would hit every pixels in the canvas.
49 *
50 * Returns false if
51 * - rect does not contain the canvas' bounds
52 * - paint is not fill
53 * - paint would blur or otherwise change the coverage of the rect
54 */
55bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
56 ShaderOverrideOpacity overrideOpacity) const {
bungeman99fe8222015-08-20 07:57:51 -070057 static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
58 (int)kNone_ShaderOverrideOpacity,
59 "need_matching_enums0");
60 static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
61 (int)kOpaque_ShaderOverrideOpacity,
62 "need_matching_enums1");
63 static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
64 (int)kNotOpaque_ShaderOverrideOpacity,
65 "need_matching_enums2");
reedc83a2972015-07-16 07:40:45 -070066
67 const SkISize size = this->getBaseLayerSize();
68 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
69 if (!this->getClipStack()->quickContains(bounds)) {
70 return false;
71 }
72
73 if (rect) {
74 if (!this->getTotalMatrix().rectStaysRect()) {
75 return false; // conservative
76 }
77
78 SkRect devRect;
79 this->getTotalMatrix().mapRect(&devRect, *rect);
fmalita8c0144c2015-07-22 05:56:16 -070080 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -070081 return false;
82 }
83 }
84
85 if (paint) {
86 SkPaint::Style paintStyle = paint->getStyle();
87 if (!(paintStyle == SkPaint::kFill_Style ||
88 paintStyle == SkPaint::kStrokeAndFill_Style)) {
89 return false;
90 }
91 if (paint->getMaskFilter() || paint->getLooper()
92 || paint->getPathEffect() || paint->getImageFilter()) {
93 return false; // conservative
94 }
95 }
96 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
97}
98
99///////////////////////////////////////////////////////////////////////////////////////////////////
100
reedd990e2f2014-12-22 11:58:30 -0800101static bool gIgnoreSaveLayerBounds;
102void SkCanvas::Internal_Private_SetIgnoreSaveLayerBounds(bool ignore) {
103 gIgnoreSaveLayerBounds = ignore;
104}
105bool SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds() {
106 return gIgnoreSaveLayerBounds;
107}
108
reed0acf1b42014-12-22 16:12:38 -0800109static bool gTreatSpriteAsBitmap;
110void SkCanvas::Internal_Private_SetTreatSpriteAsBitmap(bool spriteAsBitmap) {
111 gTreatSpriteAsBitmap = spriteAsBitmap;
112}
113bool SkCanvas::Internal_Private_GetTreatSpriteAsBitmap() {
114 return gTreatSpriteAsBitmap;
115}
116
reed@google.comda17f752012-08-16 18:27:05 +0000117// experimental for faster tiled drawing...
118//#define SK_ENABLE_CLIP_QUICKREJECT
reed@android.com8a1c16f2008-12-17 15:59:43 +0000119//#define SK_TRACE_SAVERESTORE
120
121#ifdef SK_TRACE_SAVERESTORE
122 static int gLayerCounter;
123 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
124 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
125
126 static int gRecCounter;
127 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
128 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
129
130 static int gCanvasCounter;
131 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
132 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
133#else
134 #define inc_layer()
135 #define dec_layer()
136 #define inc_rec()
137 #define dec_rec()
138 #define inc_canvas()
139 #define dec_canvas()
140#endif
141
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000142typedef SkTLazy<SkPaint> SkLazyPaint;
143
reedc83a2972015-07-16 07:40:45 -0700144void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000145 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700146 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
147 ? SkSurface::kDiscard_ContentChangeMode
148 : SkSurface::kRetain_ContentChangeMode);
149 }
150}
151
152void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
153 ShaderOverrideOpacity overrideOpacity) {
154 if (fSurfaceBase) {
155 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
156 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
157 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
158 // and therefore we don't care which mode we're in.
159 //
160 if (fSurfaceBase->outstandingImageSnapshot()) {
161 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
162 mode = SkSurface::kDiscard_ContentChangeMode;
163 }
164 }
165 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000166 }
167}
168
reed@android.com8a1c16f2008-12-17 15:59:43 +0000169///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000170
reed4a8126e2014-09-22 07:29:03 -0700171static uint32_t filter_paint_flags(const SkSurfaceProps& props, uint32_t flags) {
172 const uint32_t propFlags = props.flags();
173 if (propFlags & SkSurfaceProps::kDisallowDither_Flag) {
174 flags &= ~SkPaint::kDither_Flag;
175 }
176 if (propFlags & SkSurfaceProps::kDisallowAntiAlias_Flag) {
177 flags &= ~SkPaint::kAntiAlias_Flag;
178 }
179 return flags;
180}
181
182///////////////////////////////////////////////////////////////////////////////
183
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000184/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000185 The clip/matrix/proc are fields that reflect the top of the save/restore
186 stack. Whenever the canvas changes, it marks a dirty flag, and then before
187 these are used (assuming we're not on a layer) we rebuild these cache
188 values: they reflect the top of the save stack, but translated and clipped
189 by the device's XY offset and bitmap-bounds.
190*/
191struct DeviceCM {
192 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000193 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000194 SkRasterClip fClip;
reed@google.com6f8f2922011-03-04 22:27:10 +0000195 SkPaint* fPaint; // may be null (in the future)
reed61f501f2015-04-29 08:34:00 -0700196 const SkMatrix* fMatrix;
197 SkMatrix fMatrixStorage;
reed8c30a812016-04-20 16:36:51 -0700198 SkMatrix fStashedMatrix; // original CTM; used by imagefilter in saveLayer
reed61f501f2015-04-29 08:34:00 -0700199 const bool fDeviceIsBitmapDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000200
reed96e657d2015-03-10 17:30:07 -0700201 DeviceCM(SkBaseDevice* device, const SkPaint* paint, SkCanvas* canvas,
reed8c30a812016-04-20 16:36:51 -0700202 bool conservativeRasterClip, bool deviceIsBitmapDevice, const SkMatrix& stashed)
halcanary96fcdcc2015-08-27 07:41:13 -0700203 : fNext(nullptr)
reedd9544982014-09-09 18:46:22 -0700204 , fClip(conservativeRasterClip)
reed8c30a812016-04-20 16:36:51 -0700205 , fStashedMatrix(stashed)
reed61f501f2015-04-29 08:34:00 -0700206 , fDeviceIsBitmapDevice(deviceIsBitmapDevice)
reedd9544982014-09-09 18:46:22 -0700207 {
halcanary96fcdcc2015-08-27 07:41:13 -0700208 if (nullptr != device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000209 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000210 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211 }
reed@google.com4b226022011-01-11 18:32:13 +0000212 fDevice = device;
halcanary96fcdcc2015-08-27 07:41:13 -0700213 fPaint = paint ? new SkPaint(*paint) : nullptr;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000214 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000216 ~DeviceCM() {
bsalomon49f085d2014-09-05 13:34:00 -0700217 if (fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000218 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000219 fDevice->unref();
220 }
halcanary385fe4d2015-08-26 13:07:48 -0700221 delete fPaint;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000222 }
reed@google.com4b226022011-01-11 18:32:13 +0000223
mtkleinfeaadee2015-04-08 11:25:48 -0700224 void reset(const SkIRect& bounds) {
225 SkASSERT(!fPaint);
226 SkASSERT(!fNext);
227 SkASSERT(fDevice);
228 fClip.setRect(bounds);
229 }
230
reed@google.com045e62d2011-10-24 12:19:46 +0000231 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
232 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000233 int x = fDevice->getOrigin().x();
234 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000235 int width = fDevice->width();
236 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000237
reed@android.com8a1c16f2008-12-17 15:59:43 +0000238 if ((x | y) == 0) {
239 fMatrix = &totalMatrix;
240 fClip = totalClip;
241 } else {
242 fMatrixStorage = totalMatrix;
243 fMatrixStorage.postTranslate(SkIntToScalar(-x),
244 SkIntToScalar(-y));
245 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000246
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247 totalClip.translate(-x, -y, &fClip);
248 }
249
reed@google.com045e62d2011-10-24 12:19:46 +0000250 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000251
252 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000253
reed@android.com8a1c16f2008-12-17 15:59:43 +0000254 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000255 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000256 SkRegion::kDifference_Op);
257 }
reed@google.com4b226022011-01-11 18:32:13 +0000258
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000259 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
260
reed@android.com8a1c16f2008-12-17 15:59:43 +0000261#ifdef SK_DEBUG
262 if (!fClip.isEmpty()) {
263 SkIRect deviceR;
264 deviceR.set(0, 0, width, height);
265 SkASSERT(deviceR.contains(fClip.getBounds()));
266 }
267#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000268 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269};
270
271/* This is the record we keep for each save/restore level in the stack.
272 Since a level optionally copies the matrix and/or stack, we have pointers
273 for these fields. If the value is copied for this level, the copy is
274 stored in the ...Storage field, and the pointer points to that. If the
275 value is not copied for this level, we ignore ...Storage, and just point
276 at the corresponding value in the previous level in the stack.
277*/
278class SkCanvas::MCRec {
279public:
reed1f836ee2014-07-07 07:49:34 -0700280 SkDrawFilter* fFilter; // the current filter (or null)
reedd9544982014-09-09 18:46:22 -0700281 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000282 /* If there are any layers in the stack, this points to the top-most
283 one that is at or below this level in the stack (so we know what
284 bitmap/device to draw into from this level. This value is NOT
285 reference counted, since the real owner is either our fLayer field,
286 or a previous one in a lower level.)
287 */
reed2ff1fce2014-12-11 07:07:37 -0800288 DeviceCM* fTopLayer;
289 SkRasterClip fRasterClip;
290 SkMatrix fMatrix;
291 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000292
reedd9544982014-09-09 18:46:22 -0700293 MCRec(bool conservativeRasterClip) : fRasterClip(conservativeRasterClip) {
halcanary96fcdcc2015-08-27 07:41:13 -0700294 fFilter = nullptr;
295 fLayer = nullptr;
296 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800297 fMatrix.reset();
298 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700299
reedd9544982014-09-09 18:46:22 -0700300 // don't bother initializing fNext
301 inc_rec();
302 }
reed2ff1fce2014-12-11 07:07:37 -0800303 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
reedd9544982014-09-09 18:46:22 -0700304 fFilter = SkSafeRef(prev.fFilter);
halcanary96fcdcc2015-08-27 07:41:13 -0700305 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700306 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800307 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700308
reed@android.com8a1c16f2008-12-17 15:59:43 +0000309 // don't bother initializing fNext
310 inc_rec();
311 }
312 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000313 SkSafeUnref(fFilter);
halcanary385fe4d2015-08-26 13:07:48 -0700314 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000315 dec_rec();
316 }
mtkleinfeaadee2015-04-08 11:25:48 -0700317
318 void reset(const SkIRect& bounds) {
319 SkASSERT(fLayer);
320 SkASSERT(fDeferredSaveCount == 0);
321
322 fMatrix.reset();
323 fRasterClip.setRect(bounds);
324 fLayer->reset(bounds);
325 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000326};
327
328class SkDrawIter : public SkDraw {
329public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000330 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000331 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000332 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000333 canvas->updateDeviceCMCache();
334
reed687fa1c2015-04-07 08:00:56 -0700335 fClipStack = canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000336 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000337 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000338 }
reed@google.com4b226022011-01-11 18:32:13 +0000339
reed@android.com8a1c16f2008-12-17 15:59:43 +0000340 bool next() {
341 // skip over recs with empty clips
342 if (fSkipEmptyClips) {
343 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
344 fCurrLayer = fCurrLayer->fNext;
345 }
346 }
347
reed@google.comf68c5e22012-02-24 16:38:58 +0000348 const DeviceCM* rec = fCurrLayer;
349 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000350
351 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000352 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
353 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000354 fDevice = rec->fDevice;
reed41e010c2015-06-09 12:16:53 -0700355 if (!fDevice->accessPixels(&fDst)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700356 fDst.reset(fDevice->imageInfo(), nullptr, 0);
reed41e010c2015-06-09 12:16:53 -0700357 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000358 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000359 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000360
361 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700362 // fCurrLayer may be nullptr now
reed@android.com199f1082009-06-10 02:12:47 +0000363
reed@android.com8a1c16f2008-12-17 15:59:43 +0000364 return true;
365 }
366 return false;
367 }
reed@google.com4b226022011-01-11 18:32:13 +0000368
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000369 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000370 int getX() const { return fDevice->getOrigin().x(); }
371 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000372 const SkMatrix& getMatrix() const { return *fMatrix; }
373 const SkRegion& getClip() const { return *fClip; }
374 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000375
reed@android.com8a1c16f2008-12-17 15:59:43 +0000376private:
377 SkCanvas* fCanvas;
378 const DeviceCM* fCurrLayer;
379 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000380 SkBool8 fSkipEmptyClips;
381
382 typedef SkDraw INHERITED;
383};
384
385/////////////////////////////////////////////////////////////////////////////
386
reeddbc3cef2015-04-29 12:18:57 -0700387static SkPaint* set_if_needed(SkLazyPaint* lazy, const SkPaint& orig) {
388 return lazy->isValid() ? lazy->get() : lazy->set(orig);
389}
390
391/**
392 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700393 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700394 */
reedd053ce92016-03-22 10:17:23 -0700395static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700396 SkImageFilter* imgf = paint.getImageFilter();
397 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700398 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700399 }
400
reedd053ce92016-03-22 10:17:23 -0700401 SkColorFilter* imgCFPtr;
402 if (!imgf->asAColorFilter(&imgCFPtr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700403 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700404 }
reedd053ce92016-03-22 10:17:23 -0700405 sk_sp<SkColorFilter> imgCF(imgCFPtr);
reeddbc3cef2015-04-29 12:18:57 -0700406
407 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700408 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700409 // there is no existing paint colorfilter, so we can just return the imagefilter's
410 return imgCF;
411 }
412
413 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
414 // and we need to combine them into a single colorfilter.
reedd053ce92016-03-22 10:17:23 -0700415 return SkColorFilter::MakeComposeFilter(std::move(imgCF), sk_ref_sp(paintCF));
reeddbc3cef2015-04-29 12:18:57 -0700416}
417
senorblanco87e066e2015-10-28 11:23:36 -0700418/**
419 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
420 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
421 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
422 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
423 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
424 * conservative "effective" bounds based on the settings in the paint... with one exception. This
425 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
426 * deliberately ignored.
427 */
428static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
429 const SkRect& rawBounds,
430 SkRect* storage) {
431 SkPaint tmpUnfiltered(paint);
432 tmpUnfiltered.setImageFilter(nullptr);
433 if (tmpUnfiltered.canComputeFastBounds()) {
434 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
435 } else {
436 return rawBounds;
437 }
438}
439
reed@android.com8a1c16f2008-12-17 15:59:43 +0000440class AutoDrawLooper {
441public:
senorblanco87e066e2015-10-28 11:23:36 -0700442 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
443 // paint. It's used to determine the size of the offscreen layer for filters.
444 // If null, the clip will be used instead.
reed4a8126e2014-09-22 07:29:03 -0700445 AutoDrawLooper(SkCanvas* canvas, const SkSurfaceProps& props, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000446 bool skipLayerForImageFilter = false,
senorblanco87e066e2015-10-28 11:23:36 -0700447 const SkRect* rawBounds = nullptr) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000448 fCanvas = canvas;
fmalita53d9f1c2016-01-25 06:23:54 -0800449#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450 fFilter = canvas->getDrawFilter();
fmalita77650002016-01-21 18:47:11 -0800451#else
452 fFilter = nullptr;
453#endif
reed4a8126e2014-09-22 07:29:03 -0700454 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000455 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700456 fTempLayerForImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000457 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000458
reedd053ce92016-03-22 10:17:23 -0700459 auto simplifiedCF = image_to_color_filter(fOrigPaint);
reeddbc3cef2015-04-29 12:18:57 -0700460 if (simplifiedCF) {
461 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reedd053ce92016-03-22 10:17:23 -0700462 paint->setColorFilter(std::move(simplifiedCF));
halcanary96fcdcc2015-08-27 07:41:13 -0700463 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700464 fPaint = paint;
465 }
466
467 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700468 /**
469 * We implement ImageFilters for a given draw by creating a layer, then applying the
470 * imagefilter to the pixels of that layer (its backing surface/image), and then
471 * we call restore() to xfer that layer to the main canvas.
472 *
473 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
474 * 2. Generate the src pixels:
475 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
476 * return (fPaint). We then draw the primitive (using srcover) into a cleared
477 * buffer/surface.
478 * 3. Restore the layer created in #1
479 * The imagefilter is passed the buffer/surface from the layer (now filled with the
480 * src pixels of the primitive). It returns a new "filtered" buffer, which we
481 * draw onto the previous layer using the xfermode from the original paint.
482 */
reed@google.com8926b162012-03-23 15:36:36 +0000483 SkPaint tmp;
reeddbc3cef2015-04-29 12:18:57 -0700484 tmp.setImageFilter(fPaint->getImageFilter());
reedcfb6bdf2016-03-29 11:32:50 -0700485 tmp.setXfermode(sk_ref_sp(fPaint->getXfermode()));
senorblanco87e066e2015-10-28 11:23:36 -0700486 SkRect storage;
487 if (rawBounds) {
488 // Make rawBounds include all paint outsets except for those due to image filters.
489 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
490 }
reedbfd5f172016-01-07 11:28:08 -0800491 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &tmp),
reed76033be2015-03-14 10:54:31 -0700492 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700493 fTempLayerForImageFilter = true;
494 // we remove the imagefilter/xfermode inside doNext()
reed@google.com8926b162012-03-23 15:36:36 +0000495 }
496
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000497 if (SkDrawLooper* looper = paint.getLooper()) {
498 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
499 looper->contextSize());
500 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000501 fIsSimple = false;
502 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700503 fLooperContext = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000504 // can we be marked as simple?
reed5c476fb2015-04-20 08:04:21 -0700505 fIsSimple = !fFilter && !fTempLayerForImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000506 }
piotaixrb5fae932014-09-24 13:03:30 -0700507
reed4a8126e2014-09-22 07:29:03 -0700508 uint32_t oldFlags = paint.getFlags();
509 fNewPaintFlags = filter_paint_flags(props, oldFlags);
510 if (fIsSimple && (fNewPaintFlags != oldFlags)) {
reeddbc3cef2015-04-29 12:18:57 -0700511 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700512 paint->setFlags(fNewPaintFlags);
513 fPaint = paint;
514 // if we're not simple, doNext() will take care of calling setFlags()
515 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000516 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000517
reed@android.com8a1c16f2008-12-17 15:59:43 +0000518 ~AutoDrawLooper() {
reed5c476fb2015-04-20 08:04:21 -0700519 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000520 fCanvas->internalRestore();
521 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000522 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000523 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000524
reed@google.com4e2b3d32011-04-07 14:18:59 +0000525 const SkPaint& paint() const {
526 SkASSERT(fPaint);
527 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000528 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000529
reed@google.com129ec222012-05-15 13:24:09 +0000530 bool next(SkDrawFilter::Type drawType) {
531 if (fDone) {
532 return false;
533 } else if (fIsSimple) {
534 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000535 return !fPaint->nothingToDraw();
536 } else {
537 return this->doNext(drawType);
538 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000539 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000540
reed@android.com8a1c16f2008-12-17 15:59:43 +0000541private:
reeddbc3cef2015-04-29 12:18:57 -0700542 SkLazyPaint fLazyPaintInit; // base paint storage in case we need to modify it
543 SkLazyPaint fLazyPaintPerLooper; // per-draw-looper storage, so the looper can modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000544 SkCanvas* fCanvas;
545 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000546 SkDrawFilter* fFilter;
547 const SkPaint* fPaint;
548 int fSaveCount;
reed4a8126e2014-09-22 07:29:03 -0700549 uint32_t fNewPaintFlags;
reed5c476fb2015-04-20 08:04:21 -0700550 bool fTempLayerForImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000551 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000552 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000553 SkDrawLooper::Context* fLooperContext;
554 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000555
556 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000557};
558
reed@google.com129ec222012-05-15 13:24:09 +0000559bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
halcanary96fcdcc2015-08-27 07:41:13 -0700560 fPaint = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000561 SkASSERT(!fIsSimple);
reed5c476fb2015-04-20 08:04:21 -0700562 SkASSERT(fLooperContext || fFilter || fTempLayerForImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000563
reeddbc3cef2015-04-29 12:18:57 -0700564 SkPaint* paint = fLazyPaintPerLooper.set(fLazyPaintInit.isValid() ?
565 *fLazyPaintInit.get() : fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700566 paint->setFlags(fNewPaintFlags);
reed@google.com129ec222012-05-15 13:24:09 +0000567
reed5c476fb2015-04-20 08:04:21 -0700568 if (fTempLayerForImageFilter) {
halcanary96fcdcc2015-08-27 07:41:13 -0700569 paint->setImageFilter(nullptr);
570 paint->setXfermode(nullptr);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000571 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000572
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000573 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000574 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000575 return false;
576 }
577 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000578 if (!fFilter->filter(paint, drawType)) {
579 fDone = true;
580 return false;
581 }
halcanary96fcdcc2015-08-27 07:41:13 -0700582 if (nullptr == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000583 // no looper means we only draw once
584 fDone = true;
585 }
586 }
587 fPaint = paint;
588
589 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000590 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000591 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000592 }
593
594 // call this after any possible paint modifiers
595 if (fPaint->nothingToDraw()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700596 fPaint = nullptr;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000597 return false;
598 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000599 return true;
600}
601
reed@android.com8a1c16f2008-12-17 15:59:43 +0000602////////// macros to place around the internal draw calls //////////////////
603
reed262a71b2015-12-05 13:07:27 -0800604#define LOOPER_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
605 this->predrawNotify(); \
606 AutoDrawLooper looper(this, fProps, paint, skipLayerForFilter, bounds); \
607 while (looper.next(SkDrawFilter::kBitmap_Type)) { \
608 SkDrawIter iter(this);
609
610
reed@google.com8926b162012-03-23 15:36:36 +0000611#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000612 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700613 AutoDrawLooper looper(this, fProps, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000614 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000615 SkDrawIter iter(this);
616
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000617#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000618 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700619 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000620 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000621 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000622
reedc83a2972015-07-16 07:40:45 -0700623#define LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, type, bounds, auxOpaque) \
624 this->predrawNotify(bounds, &paint, auxOpaque); \
625 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
626 while (looper.next(type)) { \
627 SkDrawIter iter(this);
628
reed@google.com4e2b3d32011-04-07 14:18:59 +0000629#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000630
631////////////////////////////////////////////////////////////////////////////
632
mtkleinfeaadee2015-04-08 11:25:48 -0700633void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
634 this->restoreToCount(1);
635 fCachedLocalClipBounds.setEmpty();
636 fCachedLocalClipBoundsDirty = true;
637 fClipStack->reset();
638 fMCRec->reset(bounds);
639
640 // We're peering through a lot of structs here. Only at this scope do we
641 // know that the device is an SkBitmapDevice (really an SkNoPixelsBitmapDevice).
642 static_cast<SkBitmapDevice*>(fMCRec->fLayer->fDevice)->setNewSize(bounds.size());
643}
644
reedd9544982014-09-09 18:46:22 -0700645SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
reed42b73eb2015-11-20 13:42:42 -0800646 if (device && device->forceConservativeRasterClip()) {
647 flags = InitFlags(flags | kConservativeRasterClip_InitFlag);
648 }
649 // Since init() is only called once by our constructors, it is safe to perform this
650 // const-cast.
651 *const_cast<bool*>(&fConservativeRasterClip) = SkToBool(flags & kConservativeRasterClip_InitFlag);
652
reed@google.comc0784db2013-12-13 21:16:12 +0000653 fCachedLocalClipBounds.setEmpty();
654 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000655 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000656 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700657 fDeviceCMDirty = true;
reed2ff1fce2014-12-11 07:07:37 -0800658 fSaveCount = 1;
halcanary96fcdcc2015-08-27 07:41:13 -0700659 fMetaData = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000660
halcanary385fe4d2015-08-26 13:07:48 -0700661 fClipStack.reset(new SkClipStack);
reed687fa1c2015-04-07 08:00:56 -0700662
reed@android.com8a1c16f2008-12-17 15:59:43 +0000663 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700664 new (fMCRec) MCRec(fConservativeRasterClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000665
reeda499f902015-05-01 09:34:31 -0700666 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
667 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
reed8c30a812016-04-20 16:36:51 -0700668 new (fDeviceCMStorage) DeviceCM(nullptr, nullptr, nullptr, fConservativeRasterClip, false,
669 fMCRec->fMatrix);
reedb679ca82015-04-07 04:40:48 -0700670
reed@android.com8a1c16f2008-12-17 15:59:43 +0000671 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000672
halcanary96fcdcc2015-08-27 07:41:13 -0700673 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000674
reedf92c8662014-08-18 08:02:43 -0700675 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700676 // The root device and the canvas should always have the same pixel geometry
677 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reedf92c8662014-08-18 08:02:43 -0700678 device->onAttachToCanvas(this);
679 fMCRec->fLayer->fDevice = SkRef(device);
reed78e27682014-11-19 08:04:34 -0800680 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
reedf92c8662014-08-18 08:02:43 -0700681 }
682 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000683}
684
reed@google.comcde92112011-07-06 20:00:52 +0000685SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000686 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700687 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800688 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000689{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000690 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000691
halcanary96fcdcc2015-08-27 07:41:13 -0700692 this->init(nullptr, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000693}
694
reedd9544982014-09-09 18:46:22 -0700695static SkBitmap make_nopixels(int width, int height) {
696 SkBitmap bitmap;
697 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
698 return bitmap;
699}
700
701class SkNoPixelsBitmapDevice : public SkBitmapDevice {
702public:
robertphillipsfcf78292015-06-19 11:49:52 -0700703 SkNoPixelsBitmapDevice(const SkIRect& bounds, const SkSurfaceProps& surfaceProps)
704 : INHERITED(make_nopixels(bounds.width(), bounds.height()), surfaceProps)
reed78e27682014-11-19 08:04:34 -0800705 {
706 this->setOrigin(bounds.x(), bounds.y());
707 }
reedd9544982014-09-09 18:46:22 -0700708
709private:
piotaixrb5fae932014-09-24 13:03:30 -0700710
reedd9544982014-09-09 18:46:22 -0700711 typedef SkBitmapDevice INHERITED;
712};
713
reed96a857e2015-01-25 10:33:58 -0800714SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000715 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800716 , fProps(SkSurfacePropsCopyOrDefault(props))
reed42b73eb2015-11-20 13:42:42 -0800717 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000718{
719 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700720
halcanary385fe4d2015-08-26 13:07:48 -0700721 this->init(new SkNoPixelsBitmapDevice(SkIRect::MakeWH(width, height), fProps),
722 kDefault_InitFlags)->unref();
reedd9544982014-09-09 18:46:22 -0700723}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000724
reed78e27682014-11-19 08:04:34 -0800725SkCanvas::SkCanvas(const SkIRect& bounds, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700726 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700727 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800728 , fConservativeRasterClip(false)
reedd9544982014-09-09 18:46:22 -0700729{
730 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700731
halcanary385fe4d2015-08-26 13:07:48 -0700732 this->init(new SkNoPixelsBitmapDevice(bounds, fProps), flags)->unref();
reedd9544982014-09-09 18:46:22 -0700733}
734
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000735SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000736 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700737 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800738 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000739{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000740 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700741
reedd9544982014-09-09 18:46:22 -0700742 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000743}
744
robertphillipsfcf78292015-06-19 11:49:52 -0700745SkCanvas::SkCanvas(SkBaseDevice* device, InitFlags flags)
746 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700747 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800748 , fConservativeRasterClip(false)
robertphillipsfcf78292015-06-19 11:49:52 -0700749{
750 inc_canvas();
751
752 this->init(device, flags);
753}
754
reed4a8126e2014-09-22 07:29:03 -0700755SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700756 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700757 , fProps(props)
reed42b73eb2015-11-20 13:42:42 -0800758 , fConservativeRasterClip(false)
reed3716fd02014-09-21 09:39:55 -0700759{
760 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700761
halcanary385fe4d2015-08-26 13:07:48 -0700762 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700763 this->init(device, kDefault_InitFlags);
764}
reed29c857d2014-09-21 10:25:07 -0700765
reed4a8126e2014-09-22 07:29:03 -0700766SkCanvas::SkCanvas(const SkBitmap& bitmap)
767 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
768 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800769 , fConservativeRasterClip(false)
reed4a8126e2014-09-22 07:29:03 -0700770{
771 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700772
halcanary385fe4d2015-08-26 13:07:48 -0700773 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700774 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000775}
776
777SkCanvas::~SkCanvas() {
778 // free up the contents of our deque
779 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000780
reed@android.com8a1c16f2008-12-17 15:59:43 +0000781 this->internalRestore(); // restore the last, since we're going away
782
halcanary385fe4d2015-08-26 13:07:48 -0700783 delete fMetaData;
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000784
reed@android.com8a1c16f2008-12-17 15:59:43 +0000785 dec_canvas();
786}
787
fmalita53d9f1c2016-01-25 06:23:54 -0800788#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000789SkDrawFilter* SkCanvas::getDrawFilter() const {
790 return fMCRec->fFilter;
791}
792
793SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
reed51985e32015-04-11 08:04:56 -0700794 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000795 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
796 return filter;
797}
fmalita77650002016-01-21 18:47:11 -0800798#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000799
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000800SkMetaData& SkCanvas::getMetaData() {
801 // metadata users are rare, so we lazily allocate it. If that changes we
802 // can decide to just make it a field in the device (rather than a ptr)
halcanary96fcdcc2015-08-27 07:41:13 -0700803 if (nullptr == fMetaData) {
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000804 fMetaData = new SkMetaData;
805 }
806 return *fMetaData;
807}
808
reed@android.com8a1c16f2008-12-17 15:59:43 +0000809///////////////////////////////////////////////////////////////////////////////
810
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000811void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000812 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000813 if (device) {
814 device->flush();
815 }
816}
817
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000818SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000819 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000820 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
821}
822
senorblancoafc7cce2016-02-02 18:44:15 -0800823SkIRect SkCanvas::getTopLayerBounds() const {
824 SkBaseDevice* d = this->getTopDevice();
825 if (!d) {
826 return SkIRect::MakeEmpty();
827 }
828 return SkIRect::MakeXYWH(d->getOrigin().x(), d->getOrigin().y(), d->width(), d->height());
829}
830
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000831SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000832 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000833 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000834 SkASSERT(rec && rec->fLayer);
835 return rec->fLayer->fDevice;
836}
837
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000838SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000839 if (updateMatrixClip) {
840 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
841 }
reed@google.com9266fed2011-03-30 00:18:03 +0000842 return fMCRec->fTopLayer->fDevice;
843}
844
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000845bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
846 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
847 return false;
848 }
849
850 bool weAllocated = false;
halcanary96fcdcc2015-08-27 07:41:13 -0700851 if (nullptr == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700852 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000853 return false;
854 }
855 weAllocated = true;
856 }
857
reedcf01e312015-05-23 19:14:51 -0700858 SkAutoPixmapUnlock unlocker;
859 if (bitmap->requestLock(&unlocker)) {
860 const SkPixmap& pm = unlocker.pixmap();
861 if (this->readPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), x, y)) {
862 return true;
863 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000864 }
865
866 if (weAllocated) {
halcanary96fcdcc2015-08-27 07:41:13 -0700867 bitmap->setPixelRef(nullptr);
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000868 }
869 return false;
870}
reed@google.com51df9e32010-12-23 19:29:18 +0000871
bsalomon@google.comc6980972011-11-02 19:57:21 +0000872bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000873 SkIRect r = srcRect;
874 const SkISize size = this->getBaseLayerSize();
875 if (!r.intersect(0, 0, size.width(), size.height())) {
876 bitmap->reset();
877 return false;
878 }
879
reed84825042014-09-02 12:50:45 -0700880 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000881 // bitmap will already be reset.
882 return false;
883 }
884 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
885 bitmap->reset();
886 return false;
887 }
888 return true;
889}
890
reed96472de2014-12-10 09:53:42 -0800891bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000892 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000893 if (!device) {
894 return false;
895 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000896 const SkISize size = this->getBaseLayerSize();
mtkleinf0f14112014-12-12 08:46:25 -0800897
reed96472de2014-12-10 09:53:42 -0800898 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
899 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000900 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000901 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000902
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000903 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800904 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000905}
906
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000907bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
908 if (bitmap.getTexture()) {
909 return false;
910 }
reedcf01e312015-05-23 19:14:51 -0700911
912 SkAutoPixmapUnlock unlocker;
913 if (bitmap.requestLock(&unlocker)) {
914 const SkPixmap& pm = unlocker.pixmap();
915 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000916 }
917 return false;
918}
919
920bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
921 int x, int y) {
922 switch (origInfo.colorType()) {
923 case kUnknown_SkColorType:
924 case kIndex_8_SkColorType:
925 return false;
926 default:
927 break;
928 }
halcanary96fcdcc2015-08-27 07:41:13 -0700929 if (nullptr == pixels || rowBytes < origInfo.minRowBytes()) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000930 return false;
931 }
932
933 const SkISize size = this->getBaseLayerSize();
934 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
935 if (!target.intersect(0, 0, size.width(), size.height())) {
936 return false;
937 }
938
939 SkBaseDevice* device = this->getDevice();
940 if (!device) {
941 return false;
942 }
943
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000944 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700945 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000946
947 // if x or y are negative, then we have to adjust pixels
948 if (x > 0) {
949 x = 0;
950 }
951 if (y > 0) {
952 y = 0;
953 }
954 // here x,y are either 0 or negative
955 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
956
reed4af35f32014-06-27 17:47:49 -0700957 // Tell our owning surface to bump its generation ID
reedc83a2972015-07-16 07:40:45 -0700958 const bool completeOverwrite = info.dimensions() == size;
959 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700960
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000961 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000962 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000963}
reed@google.com51df9e32010-12-23 19:29:18 +0000964
junov@google.com4370aed2012-01-18 16:21:08 +0000965SkCanvas* SkCanvas::canvasForDrawIter() {
966 return this;
967}
968
reed@android.com8a1c16f2008-12-17 15:59:43 +0000969//////////////////////////////////////////////////////////////////////////////
970
reed@android.com8a1c16f2008-12-17 15:59:43 +0000971void SkCanvas::updateDeviceCMCache() {
972 if (fDeviceCMDirty) {
973 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700974 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000975 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000976
halcanary96fcdcc2015-08-27 07:41:13 -0700977 if (nullptr == layer->fNext) { // only one layer
978 layer->updateMC(totalMatrix, totalClip, *fClipStack, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000979 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000980 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000981 do {
reed687fa1c2015-04-07 08:00:56 -0700982 layer->updateMC(totalMatrix, clip, *fClipStack, &clip);
halcanary96fcdcc2015-08-27 07:41:13 -0700983 } while ((layer = layer->fNext) != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000984 }
985 fDeviceCMDirty = false;
986 }
987}
988
reed@android.com8a1c16f2008-12-17 15:59:43 +0000989///////////////////////////////////////////////////////////////////////////////
990
reed2ff1fce2014-12-11 07:07:37 -0800991void SkCanvas::checkForDeferredSave() {
992 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800993 this->doSave();
994 }
995}
996
reedf0090cb2014-11-26 08:55:51 -0800997int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800998#ifdef SK_DEBUG
999 int count = 0;
1000 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
1001 for (;;) {
1002 const MCRec* rec = (const MCRec*)iter.next();
1003 if (!rec) {
1004 break;
1005 }
1006 count += 1 + rec->fDeferredSaveCount;
1007 }
1008 SkASSERT(count == fSaveCount);
1009#endif
1010 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -08001011}
1012
1013int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -08001014 fSaveCount += 1;
1015 fMCRec->fDeferredSaveCount += 1;
1016 return this->getSaveCount() - 1; // return our prev value
1017}
1018
1019void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -08001020 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -07001021
1022 SkASSERT(fMCRec->fDeferredSaveCount > 0);
1023 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001024 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -08001025}
1026
1027void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -08001028 if (fMCRec->fDeferredSaveCount > 0) {
1029 SkASSERT(fSaveCount > 1);
1030 fSaveCount -= 1;
1031 fMCRec->fDeferredSaveCount -= 1;
1032 } else {
1033 // check for underflow
1034 if (fMCStack.count() > 1) {
1035 this->willRestore();
1036 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -07001037 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001038 this->internalRestore();
1039 this->didRestore();
1040 }
reedf0090cb2014-11-26 08:55:51 -08001041 }
1042}
1043
1044void SkCanvas::restoreToCount(int count) {
1045 // sanity check
1046 if (count < 1) {
1047 count = 1;
1048 }
mtkleinf0f14112014-12-12 08:46:25 -08001049
reedf0090cb2014-11-26 08:55:51 -08001050 int n = this->getSaveCount() - count;
1051 for (int i = 0; i < n; ++i) {
1052 this->restore();
1053 }
1054}
1055
reed2ff1fce2014-12-11 07:07:37 -08001056void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001057 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -07001058 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +00001059 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +00001060
reed687fa1c2015-04-07 08:00:56 -07001061 fClipStack->save();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001062}
1063
reed4960eee2015-12-18 07:09:18 -08001064bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
reed@google.comb93ba452014-03-10 19:47:58 +00001065#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001066 return !(saveLayerFlags & SkCanvas::kDontClipToLayer_PrivateSaveLayerFlag);
reed@google.comb93ba452014-03-10 19:47:58 +00001067#else
1068 return true;
1069#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001070}
1071
reed4960eee2015-12-18 07:09:18 -08001072bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -07001073 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001074 SkIRect clipBounds;
1075 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001076 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +00001077 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001078
reed96e657d2015-03-10 17:30:07 -07001079 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
1080
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001081 if (imageFilter) {
senorblancoe5e79842016-03-21 14:51:59 -07001082 clipBounds = imageFilter->filterBounds(clipBounds, ctm);
senorblancodb64af32015-12-09 10:11:43 -08001083 if (bounds && !imageFilter->canComputeFastBounds()) {
1084 bounds = nullptr;
1085 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001086 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001087 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -07001088 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001089 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +00001090
reed96e657d2015-03-10 17:30:07 -07001091 ctm.mapRect(&r, *bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001092 r.roundOut(&ir);
1093 // early exit if the layer's bounds are clipped out
1094 if (!ir.intersect(clipBounds)) {
reed4960eee2015-12-18 07:09:18 -08001095 if (BoundsAffectsClip(saveLayerFlags)) {
reed9b3aa542015-03-11 08:47:12 -07001096 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001097 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001098 }
junov@chromium.orga907ac32012-02-24 21:54:07 +00001099 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001100 }
1101 } else { // no user bounds, so just use the clip
1102 ir = clipBounds;
1103 }
reed180aec42015-03-11 10:39:04 -07001104 SkASSERT(!ir.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001105
reed4960eee2015-12-18 07:09:18 -08001106 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -07001107 // Simplify the current clips since they will be applied properly during restore()
reed9b3aa542015-03-11 08:47:12 -07001108 fCachedLocalClipBoundsDirty = true;
reed687fa1c2015-04-07 08:00:56 -07001109 fClipStack->clipDevRect(ir, SkRegion::kReplace_Op);
reed180aec42015-03-11 10:39:04 -07001110 fMCRec->fRasterClip.setRect(ir);
junov@chromium.orga907ac32012-02-24 21:54:07 +00001111 }
1112
1113 if (intersection) {
1114 *intersection = ir;
1115 }
1116 return true;
1117}
1118
reed4960eee2015-12-18 07:09:18 -08001119
reed4960eee2015-12-18 07:09:18 -08001120int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
1121 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001122}
1123
reed70ee31b2015-12-10 13:44:45 -08001124int SkCanvas::saveLayerPreserveLCDTextRequests(const SkRect* bounds, const SkPaint* paint) {
reed4960eee2015-12-18 07:09:18 -08001125 return this->saveLayer(SaveLayerRec(bounds, paint, kPreserveLCDText_SaveLayerFlag));
1126}
1127
1128int SkCanvas::saveLayer(const SaveLayerRec& origRec) {
1129 SaveLayerRec rec(origRec);
1130 if (gIgnoreSaveLayerBounds) {
1131 rec.fBounds = nullptr;
1132 }
1133 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
1134 fSaveCount += 1;
1135 this->internalSaveLayer(rec, strategy);
1136 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -08001137}
1138
reedbfd5f172016-01-07 11:28:08 -08001139static void draw_filter_into_device(SkBaseDevice* src, const SkImageFilter* filter,
1140 SkBaseDevice* dst, const SkMatrix& ctm) {
robertphillips7354a4b2015-12-16 05:08:27 -08001141
1142 SkBitmap srcBM;
1143
1144#if SK_SUPPORT_GPU
1145 GrRenderTarget* srcRT = src->accessRenderTarget();
1146 if (srcRT && !srcRT->asTexture() && dst->accessRenderTarget()) {
1147 // When both the src & the dst are on the gpu but the src doesn't have a texture,
1148 // we create a temporary texture for the draw.
1149 // TODO: we should actually only copy the portion of the source needed to apply the image
1150 // filter
1151 GrContext* context = srcRT->getContext();
bsalomon5ec26ae2016-02-25 08:33:02 -08001152 SkAutoTUnref<GrTexture> tex(context->textureProvider()->createTexture(srcRT->desc(),
1153 SkBudgeted::kYes));
robertphillips7354a4b2015-12-16 05:08:27 -08001154
1155 context->copySurface(tex, srcRT);
1156
1157 GrWrapTextureInBitmap(tex, src->width(), src->height(), src->isOpaque(), &srcBM);
1158 } else
1159#endif
1160 {
1161 srcBM = src->accessBitmap(false);
1162 }
1163
1164 SkCanvas c(dst);
1165
1166 SkPaint p;
robertphillips372177e2016-03-30 07:32:28 -07001167 p.setImageFilter(filter->makeWithLocalMatrix(ctm));
reedbfd5f172016-01-07 11:28:08 -08001168 const SkScalar x = SkIntToScalar(src->getOrigin().x());
1169 const SkScalar y = SkIntToScalar(src->getOrigin().y());
1170 c.drawBitmap(srcBM, x, y, &p);
robertphillips7354a4b2015-12-16 05:08:27 -08001171}
reed70ee31b2015-12-10 13:44:45 -08001172
reed129ed1c2016-02-22 06:42:31 -08001173static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, bool isOpaque,
1174 const SkPaint* paint) {
1175 // need to force L32 for now if we have an image filter. Once filters support other colortypes
1176 // e.g. sRGB or F16, we can remove this check
1177 const bool hasImageFilter = paint && paint->getImageFilter();
1178
1179 SkAlphaType alphaType = isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
1180 if ((prev.bytesPerPixel() < 4) || hasImageFilter) {
1181 // force to L32
1182 return SkImageInfo::MakeN32(w, h, alphaType);
1183 } else {
1184 // keep the same characteristics as the prev
1185 return SkImageInfo::Make(w, h, prev.colorType(), alphaType, prev.profileType());
1186 }
1187}
1188
reed4960eee2015-12-18 07:09:18 -08001189void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
1190 const SkRect* bounds = rec.fBounds;
1191 const SkPaint* paint = rec.fPaint;
1192 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1193
reed@google.comb93ba452014-03-10 19:47:58 +00001194#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001195 saveLayerFlags &= ~kDontClipToLayer_PrivateSaveLayerFlag;
reed@google.comb93ba452014-03-10 19:47:58 +00001196#endif
1197
reed8c30a812016-04-20 16:36:51 -07001198 SkLazyPaint lazyP;
1199 SkImageFilter* imageFilter = paint ? paint->getImageFilter() : NULL;
1200 SkMatrix stashedMatrix = fMCRec->fMatrix;
1201#ifndef SK_SUPPORT_LEGACY_IMAGEFILTER_CTM
1202 SkMatrix remainder;
1203 SkSize scale;
1204 /*
1205 * ImageFilters (so far) do not correctly handle matrices (CTM) that contain rotation/skew/etc.
1206 * but they do handle scaling. To accommodate this, we do the following:
1207 *
1208 * 1. Stash off the current CTM
1209 * 2. Decompose the CTM into SCALE and REMAINDER
1210 * 3. Wack the CTM to be just SCALE, and wrap the imagefilter with a MatrixImageFilter that
1211 * contains the REMAINDER
1212 * 4. Proceed as usual, allowing the client to draw into the layer (now with a scale-only CTM)
1213 * 5. During restore, we process the MatrixImageFilter, which applies REMAINDER to the output
1214 * of the original imagefilter, and draw that (via drawSprite)
1215 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1216 *
1217 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1218 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1219 */
1220 if (imageFilter &&
1221 !stashedMatrix.isScaleTranslate() &&
1222 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) {
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001454 SkIPoint offset = SkIPoint::Make(0, 0);
robertphillips4418dba2016-03-07 12:45:14 -08001455 const SkBitmap& srcBM = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001456 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001457 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
robertphillips4418dba2016-03-07 12:45:14 -08001458 const SkIRect clipBounds = iter.fClip->getBounds().makeOffset(-pos.x(), -pos.y());
senorblancobe129b22014-08-08 07:14:35 -07001459 SkAutoTUnref<SkImageFilter::Cache> cache(dstDev->getImageFilterCache());
reed4e23cda2016-01-11 10:56:59 -08001460 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
robertphillips4418dba2016-03-07 12:45:14 -08001461
robertphillips3e302272016-04-20 11:48:36 -07001462 sk_sp<SkSpecialImage> srcImg(SkSpecialImage::internal_fromBM(srcBM,
brianosman898235c2016-04-06 07:38:23 -07001463 &dstDev->surfaceProps()));
robertphillips4418dba2016-03-07 12:45:14 -08001464 if (!srcImg) {
1465 continue; // something disastrous happened
1466 }
1467
robertphillips2302de92016-03-24 07:26:32 -07001468 sk_sp<SkSpecialImage> resultImg(filter->filterImage(srcImg.get(), ctx, &offset));
robertphillips4418dba2016-03-07 12:45:14 -08001469 if (resultImg) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001470 SkPaint tmpUnfiltered(*paint);
halcanary96fcdcc2015-08-27 07:41:13 -07001471 tmpUnfiltered.setImageFilter(nullptr);
robertphillips4418dba2016-03-07 12:45:14 -08001472 SkBitmap resultBM;
1473 if (resultImg->internal_getBM(&resultBM)) {
1474 // TODO: add drawSprite(SkSpecialImage) to SkDevice? (see skbug.com/5073)
1475 dstDev->drawSprite(iter, resultBM, pos.x() + offset.x(), pos.y() + offset.y(),
1476 tmpUnfiltered);
1477 }
reed@google.com76dd2772012-01-05 21:15:07 +00001478 }
reed61f501f2015-04-29 08:34:00 -07001479 } else if (deviceIsBitmapDevice) {
reed9572a102015-05-26 19:22:17 -07001480 const SkBitmap& src = static_cast<SkBitmapDevice*>(srcDev)->fBitmap;
reed61f501f2015-04-29 08:34:00 -07001481 dstDev->drawSprite(iter, src, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001482 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001483 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001484 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001485 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001486 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001487}
1488
reed32704672015-12-16 08:27:10 -08001489/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001490
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001491void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001492 SkMatrix m;
1493 m.setTranslate(dx, dy);
1494 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001495}
1496
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001497void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001498 SkMatrix m;
1499 m.setScale(sx, sy);
1500 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001501}
1502
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001503void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001504 SkMatrix m;
1505 m.setRotate(degrees);
1506 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001507}
1508
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001509void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001510 SkMatrix m;
1511 m.setSkew(sx, sy);
1512 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001513}
1514
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001515void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001516 if (matrix.isIdentity()) {
1517 return;
1518 }
1519
reed2ff1fce2014-12-11 07:07:37 -08001520 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001521 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001522 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001523 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001524
1525 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001526}
1527
reed8c30a812016-04-20 16:36:51 -07001528void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001529 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001530 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001531 fMCRec->fMatrix = matrix;
reed8c30a812016-04-20 16:36:51 -07001532}
1533
1534void SkCanvas::setMatrix(const SkMatrix& matrix) {
1535 this->checkForDeferredSave();
1536 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001537 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001538}
1539
reed@android.com8a1c16f2008-12-17 15:59:43 +00001540void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001541 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001542}
1543
1544//////////////////////////////////////////////////////////////////////////////
1545
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001546void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001547 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001548 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1549 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001550}
1551
1552void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001553#ifdef SK_ENABLE_CLIP_QUICKREJECT
1554 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001555 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001556 return false;
1557 }
1558
reed@google.com3b3e8952012-08-16 20:53:31 +00001559 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001560 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001561 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001562
reed687fa1c2015-04-07 08:00:56 -07001563 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001564 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001565 }
1566 }
1567#endif
1568
bsalomonac8cabd2015-11-20 18:53:07 -08001569 if (!fAllowSoftClip) {
1570 edgeStyle = kHard_ClipEdgeStyle;
1571 }
reed90ba0952015-11-20 13:42:47 -08001572
reedc64eff52015-11-21 12:39:45 -08001573 const bool rectStaysRect = fMCRec->fMatrix.rectStaysRect();
1574 SkRect devR;
1575 if (rectStaysRect) {
1576 fMCRec->fMatrix.mapRect(&devR, rect);
1577 }
bsalomonac8cabd2015-11-20 18:53:07 -08001578
reedc64eff52015-11-21 12:39:45 -08001579 // Check if we can quick-accept the clip call (and do nothing)
1580 //
1581 // TODO: investigate if a (conservative) version of this could be done in ::clipRect,
1582 // so that subclasses (like PictureRecording) didn't see unnecessary clips, which in turn
1583 // might allow lazy save/restores to eliminate entire save/restore blocks.
1584 //
1585 if (SkRegion::kIntersect_Op == op &&
1586 kHard_ClipEdgeStyle == edgeStyle
1587 && rectStaysRect)
1588 {
1589 if (devR.round().contains(fMCRec->fRasterClip.getBounds())) {
1590#if 0
1591 SkDebugf("------- ignored clipRect [%g %g %g %g]\n",
1592 rect.left(), rect.top(), rect.right(), rect.bottom());
1593#endif
1594 return;
1595 }
1596 }
1597
1598 AutoValidateClip avc(this);
1599
1600 fDeviceCMDirty = true;
1601 fCachedLocalClipBoundsDirty = true;
1602
1603 if (rectStaysRect) {
1604 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1605 fClipStack->clipDevRect(devR, op, isAA);
senorblancoafc7cce2016-02-02 18:44:15 -08001606 fMCRec->fRasterClip.op(devR, this->getTopLayerBounds(), op, isAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001607 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001608 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001609 // and clip against that, since it can handle any matrix. However, to
1610 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1611 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001612 SkPath path;
1613
1614 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001615 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001616 }
1617}
1618
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001619void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001620 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001621 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001622 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001623 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1624 } else {
1625 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001626 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001627}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001628
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001629void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001630 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001631 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001632 AutoValidateClip avc(this);
1633
1634 fDeviceCMDirty = true;
1635 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001636 if (!fAllowSoftClip) {
1637 edgeStyle = kHard_ClipEdgeStyle;
1638 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001639
reed687fa1c2015-04-07 08:00:56 -07001640 fClipStack->clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001641
senorblancoafc7cce2016-02-02 18:44:15 -08001642 fMCRec->fRasterClip.op(transformedRRect, this->getTopLayerBounds(), op,
robertphillips125f19a2015-11-23 09:00:05 -08001643 kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001644 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001645 }
1646
1647 SkPath path;
1648 path.addRRect(rrect);
1649 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001650 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001651}
1652
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001653void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001654 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001655 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001656
1657 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1658 SkRect r;
1659 if (path.isRect(&r)) {
1660 this->onClipRect(r, op, edgeStyle);
1661 return;
1662 }
1663 SkRRect rrect;
1664 if (path.isOval(&r)) {
1665 rrect.setOval(r);
1666 this->onClipRRect(rrect, op, edgeStyle);
1667 return;
1668 }
1669 if (path.isRRect(&rrect)) {
1670 this->onClipRRect(rrect, op, edgeStyle);
1671 return;
1672 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001673 }
robertphillips39f05382015-11-24 09:30:12 -08001674
1675 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001676}
1677
1678void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001679#ifdef SK_ENABLE_CLIP_QUICKREJECT
1680 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001681 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001682 return false;
1683 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001684
reed@google.com3b3e8952012-08-16 20:53:31 +00001685 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001686 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001687 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001688
reed687fa1c2015-04-07 08:00:56 -07001689 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001690 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001691 }
1692 }
1693#endif
1694
reed@google.com5c3d1472011-02-22 19:12:23 +00001695 AutoValidateClip avc(this);
1696
reed@android.com8a1c16f2008-12-17 15:59:43 +00001697 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001698 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001699 if (!fAllowSoftClip) {
1700 edgeStyle = kHard_ClipEdgeStyle;
1701 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001702
1703 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001704 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001705
reed@google.comfe701122011-11-08 19:41:23 +00001706 // Check if the transfomation, or the original path itself
1707 // made us empty. Note this can also happen if we contained NaN
1708 // values. computing the bounds detects this, and will set our
1709 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1710 if (devPath.getBounds().isEmpty()) {
1711 // resetting the path will remove any NaN or other wanky values
1712 // that might upset our scan converter.
1713 devPath.reset();
1714 }
1715
reed@google.com5c3d1472011-02-22 19:12:23 +00001716 // if we called path.swap() we could avoid a deep copy of this path
reed687fa1c2015-04-07 08:00:56 -07001717 fClipStack->clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001718
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001719 if (fAllowSimplifyClip) {
fmalita1a481fe2015-02-04 07:39:34 -08001720 bool clipIsAA = getClipStack()->asPath(&devPath);
1721 if (clipIsAA) {
1722 edgeStyle = kSoft_ClipEdgeStyle;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001723 }
fmalita1a481fe2015-02-04 07:39:34 -08001724
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001725 op = SkRegion::kReplace_Op;
1726 }
1727
senorblancoafc7cce2016-02-02 18:44:15 -08001728 fMCRec->fRasterClip.op(devPath, this->getTopLayerBounds(), op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001729}
1730
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001731void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed2ff1fce2014-12-11 07:07:37 -08001732 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001733 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001734}
1735
1736void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001737 AutoValidateClip avc(this);
1738
reed@android.com8a1c16f2008-12-17 15:59:43 +00001739 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001740 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001741
reed@google.com5c3d1472011-02-22 19:12:23 +00001742 // todo: signal fClipStack that we have a region, and therefore (I guess)
1743 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001744 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001745
reed1f836ee2014-07-07 07:49:34 -07001746 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001747}
1748
reed@google.com819c9212011-02-23 18:56:55 +00001749#ifdef SK_DEBUG
1750void SkCanvas::validateClip() const {
1751 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001752 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001753 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001754 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001755 return;
1756 }
1757
reed@google.com819c9212011-02-23 18:56:55 +00001758 SkIRect ir;
1759 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001760 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001761
reed687fa1c2015-04-07 08:00:56 -07001762 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001763 const SkClipStack::Element* element;
halcanary96fcdcc2015-08-27 07:41:13 -07001764 while ((element = iter.next()) != nullptr) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001765 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001766 case SkClipStack::Element::kRect_Type:
1767 element->getRect().round(&ir);
1768 tmpClip.op(ir, element->getOp());
1769 break;
1770 case SkClipStack::Element::kEmpty_Type:
1771 tmpClip.setEmpty();
1772 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001773 default: {
1774 SkPath path;
1775 element->asPath(&path);
senorblancoafc7cce2016-02-02 18:44:15 -08001776 tmpClip.op(path, this->getTopLayerBounds(), element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001777 break;
1778 }
reed@google.com819c9212011-02-23 18:56:55 +00001779 }
1780 }
reed@google.com819c9212011-02-23 18:56:55 +00001781}
1782#endif
1783
reed@google.com90c07ea2012-04-13 13:50:27 +00001784void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001785 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001786 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001787
halcanary96fcdcc2015-08-27 07:41:13 -07001788 while ((element = iter.next()) != nullptr) {
fmalitac3b589a2014-06-05 12:40:07 -07001789 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001790 }
1791}
1792
reed@google.com5c3d1472011-02-22 19:12:23 +00001793///////////////////////////////////////////////////////////////////////////////
1794
reed@google.com754de5f2014-02-24 19:38:20 +00001795bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001796 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001797}
1798
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001799bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001800 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001801}
1802
reed@google.com3b3e8952012-08-16 20:53:31 +00001803bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001804 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001805 return true;
1806
reed1f836ee2014-07-07 07:49:34 -07001807 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001808 return true;
1809 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001810
reed1f836ee2014-07-07 07:49:34 -07001811 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001812 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001813 fMCRec->fMatrix.mapRect(&dst, rect);
reedb07a94f2014-11-19 05:03:18 -08001814 return !SkIRect::Intersects(dst.roundOut(), fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001815 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001816 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001817
reed@android.coma380ae42009-07-21 01:17:02 +00001818 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001819 // TODO: should we use | instead, or compare all 4 at once?
1820 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001821 return true;
1822 }
reed@google.comc0784db2013-12-13 21:16:12 +00001823 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001824 return true;
1825 }
1826 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001827 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001828}
1829
reed@google.com3b3e8952012-08-16 20:53:31 +00001830bool SkCanvas::quickReject(const SkPath& path) const {
1831 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001832}
1833
reed@google.com3b3e8952012-08-16 20:53:31 +00001834bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001835 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001836 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001837 return false;
1838 }
1839
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001840 SkMatrix inverse;
1841 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001842 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001843 if (bounds) {
1844 bounds->setEmpty();
1845 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001846 return false;
1847 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001848
bsalomon49f085d2014-09-05 13:34:00 -07001849 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001850 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001851 // adjust it outwards in case we are antialiasing
1852 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001853
reed@google.com8f4d2302013-12-17 16:44:46 +00001854 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1855 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001856 inverse.mapRect(bounds, r);
1857 }
1858 return true;
1859}
1860
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001861bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001862 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001863 if (clip.isEmpty()) {
1864 if (bounds) {
1865 bounds->setEmpty();
1866 }
1867 return false;
1868 }
1869
bsalomon49f085d2014-09-05 13:34:00 -07001870 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001871 *bounds = clip.getBounds();
1872 }
1873 return true;
1874}
1875
reed@android.com8a1c16f2008-12-17 15:59:43 +00001876const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001877 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001878}
1879
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001880const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001881 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001882}
1883
reed@google.com9c135db2014-03-12 18:28:35 +00001884GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1885 SkBaseDevice* dev = this->getTopDevice();
halcanary96fcdcc2015-08-27 07:41:13 -07001886 return dev ? dev->accessRenderTarget() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001887}
1888
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001889GrContext* SkCanvas::getGrContext() {
1890#if SK_SUPPORT_GPU
1891 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07001892 if (device) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001893 GrRenderTarget* renderTarget = device->accessRenderTarget();
bsalomon49f085d2014-09-05 13:34:00 -07001894 if (renderTarget) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001895 return renderTarget->getContext();
1896 }
1897 }
1898#endif
1899
halcanary96fcdcc2015-08-27 07:41:13 -07001900 return nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001901
1902}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001903
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001904void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1905 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001906 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001907 if (outer.isEmpty()) {
1908 return;
1909 }
1910 if (inner.isEmpty()) {
1911 this->drawRRect(outer, paint);
1912 return;
1913 }
1914
1915 // We don't have this method (yet), but technically this is what we should
1916 // be able to assert...
1917 // SkASSERT(outer.contains(inner));
1918 //
1919 // For now at least check for containment of bounds
1920 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1921
1922 this->onDrawDRRect(outer, inner, paint);
1923}
1924
reed41af9662015-01-05 07:49:08 -08001925// These need to stop being virtual -- clients need to override the onDraw... versions
1926
1927void SkCanvas::drawPaint(const SkPaint& paint) {
1928 this->onDrawPaint(paint);
1929}
1930
1931void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1932 this->onDrawRect(r, paint);
1933}
1934
1935void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1936 this->onDrawOval(r, paint);
1937}
1938
1939void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1940 this->onDrawRRect(rrect, paint);
1941}
1942
1943void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1944 this->onDrawPoints(mode, count, pts, paint);
1945}
1946
1947void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
1948 const SkPoint texs[], const SkColor colors[], SkXfermode* xmode,
1949 const uint16_t indices[], int indexCount, const SkPaint& paint) {
1950 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
1951 indices, indexCount, paint);
1952}
1953
1954void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1955 this->onDrawPath(path, paint);
1956}
1957
reeda85d4d02015-05-06 12:56:48 -07001958void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001959 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001960 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001961}
1962
reede47829b2015-08-06 10:02:53 -07001963void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1964 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001965 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001966 if (dst.isEmpty() || src.isEmpty()) {
1967 return;
1968 }
1969 this->onDrawImageRect(image, &src, dst, paint, constraint);
1970}
reed41af9662015-01-05 07:49:08 -08001971
reed84984ef2015-07-17 07:09:43 -07001972void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1973 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001974 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001975 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001976}
1977
reede47829b2015-08-06 10:02:53 -07001978void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
1979 SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001980 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001981 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
1982 constraint);
1983}
reede47829b2015-08-06 10:02:53 -07001984
reed4c21dc52015-06-25 12:32:03 -07001985void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1986 const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001987 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07001988 if (dst.isEmpty()) {
1989 return;
1990 }
1991 if (!SkNinePatchIter::Valid(image->width(), image->height(), center)) {
reede47829b2015-08-06 10:02:53 -07001992 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001993 }
1994 this->onDrawImageNine(image, center, dst, paint);
1995}
1996
reed41af9662015-01-05 07:49:08 -08001997void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001998 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001999 return;
2000 }
reed41af9662015-01-05 07:49:08 -08002001 this->onDrawBitmap(bitmap, dx, dy, paint);
2002}
2003
reede47829b2015-08-06 10:02:53 -07002004void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07002005 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002006 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07002007 return;
2008 }
reede47829b2015-08-06 10:02:53 -07002009 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08002010}
2011
reed84984ef2015-07-17 07:09:43 -07002012void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2013 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002014 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002015}
2016
reede47829b2015-08-06 10:02:53 -07002017void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2018 SrcRectConstraint constraint) {
2019 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2020 constraint);
2021}
reede47829b2015-08-06 10:02:53 -07002022
reed41af9662015-01-05 07:49:08 -08002023void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2024 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07002025 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002026 return;
2027 }
reed4c21dc52015-06-25 12:32:03 -07002028 if (!SkNinePatchIter::Valid(bitmap.width(), bitmap.height(), center)) {
reeda5517e22015-07-14 10:54:12 -07002029 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002030 }
reed41af9662015-01-05 07:49:08 -08002031 this->onDrawBitmapNine(bitmap, center, dst, paint);
2032}
2033
reed71c3c762015-06-24 10:29:17 -07002034void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2035 const SkColor colors[], int count, SkXfermode::Mode mode,
2036 const SkRect* cull, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002037 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002038 if (count <= 0) {
2039 return;
2040 }
2041 SkASSERT(atlas);
2042 SkASSERT(xform);
2043 SkASSERT(tex);
2044 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
2045}
2046
reedf70b5312016-03-04 16:36:20 -08002047void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2048 if (key) {
2049 this->onDrawAnnotation(rect, key, value);
2050 }
2051}
2052
reede47829b2015-08-06 10:02:53 -07002053void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2054 const SkPaint* paint, SrcRectConstraint constraint) {
2055 if (src) {
2056 this->drawImageRect(image, *src, dst, paint, constraint);
2057 } else {
2058 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2059 dst, paint, constraint);
2060 }
2061}
2062void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2063 const SkPaint* paint, SrcRectConstraint constraint) {
2064 if (src) {
2065 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2066 } else {
2067 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2068 dst, paint, constraint);
2069 }
2070}
2071
reed@android.com8a1c16f2008-12-17 15:59:43 +00002072//////////////////////////////////////////////////////////////////////////////
2073// These are the virtual drawing methods
2074//////////////////////////////////////////////////////////////////////////////
2075
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002076void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002077 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002078 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2079 }
2080}
2081
reed41af9662015-01-05 07:49:08 -08002082void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002083 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002084 this->internalDrawPaint(paint);
2085}
2086
2087void SkCanvas::internalDrawPaint(const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002088 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002089
2090 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002091 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002092 }
2093
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::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2098 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002099 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002100 if ((long)count <= 0) {
2101 return;
2102 }
2103
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002104 SkRect r, storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002105 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002106 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002107 // special-case 2 points (common for drawing a single line)
2108 if (2 == count) {
2109 r.set(pts[0], pts[1]);
2110 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00002111 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002112 }
senorblanco87e066e2015-10-28 11:23:36 -07002113 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2114 return;
2115 }
2116 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002117 }
reed@google.coma584aed2012-05-16 14:06:02 +00002118
halcanary96fcdcc2015-08-27 07:41:13 -07002119 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002120
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002121 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002122
reed@android.com8a1c16f2008-12-17 15:59:43 +00002123 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002124 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002125 }
reed@google.com4b226022011-01-11 18:32:13 +00002126
reed@google.com4e2b3d32011-04-07 14:18:59 +00002127 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002128}
2129
reed41af9662015-01-05 07:49:08 -08002130void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002131 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002132 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002133 const SkRect* bounds = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002134 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08002135 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
2136 // To prevent accidental rejecting at this stage, we have to sort it before we check.
2137 SkRect tmp(r);
2138 tmp.sort();
2139
senorblanco87e066e2015-10-28 11:23:36 -07002140 if (this->quickReject(paint.computeFastBounds(tmp, &storage))) {
2141 return;
2142 }
2143 bounds = &r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002144 }
reed@google.com4b226022011-01-11 18:32:13 +00002145
reedc83a2972015-07-16 07:40:45 -07002146 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002147
2148 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002149 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002150 }
2151
reed@google.com4e2b3d32011-04-07 14:18:59 +00002152 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002153}
2154
reed41af9662015-01-05 07:49:08 -08002155void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002156 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002157 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002158 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002159 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002160 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2161 return;
2162 }
2163 bounds = &oval;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002164 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002165
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002166 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002167
2168 while (iter.next()) {
2169 iter.fDevice->drawOval(iter, oval, looper.paint());
2170 }
2171
2172 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002173}
2174
reed41af9662015-01-05 07:49:08 -08002175void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002176 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002177 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002178 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002179 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002180 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2181 return;
2182 }
2183 bounds = &rrect.getBounds();
reed@google.com4ed0fb72012-12-12 20:48:18 +00002184 }
2185
2186 if (rrect.isRect()) {
2187 // call the non-virtual version
2188 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002189 return;
2190 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002191 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002192 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2193 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002194 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002195
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002196 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002197
2198 while (iter.next()) {
2199 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2200 }
2201
2202 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002203}
2204
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002205void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2206 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002207 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002208 const SkRect* bounds = nullptr;
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002209 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002210 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2211 return;
2212 }
2213 bounds = &outer.getBounds();
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002214 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002215
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002216 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002217
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002218 while (iter.next()) {
2219 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2220 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002221
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002222 LOOPER_END
2223}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002224
reed41af9662015-01-05 07:49:08 -08002225void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002226 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002227 if (!path.isFinite()) {
2228 return;
2229 }
2230
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002231 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002232 const SkRect* bounds = nullptr;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002233 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002234 const SkRect& pathBounds = path.getBounds();
senorblanco87e066e2015-10-28 11:23:36 -07002235 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2236 return;
2237 }
2238 bounds = &pathBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002239 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002240
2241 const SkRect& r = path.getBounds();
2242 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002243 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002244 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002245 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002246 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002247 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002248
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002249 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002250
2251 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002252 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002253 }
2254
reed@google.com4e2b3d32011-04-07 14:18:59 +00002255 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002256}
2257
reed262a71b2015-12-05 13:07:27 -08002258bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002259 if (!paint.getImageFilter()) {
2260 return false;
2261 }
2262
2263 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002264 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002265 return false;
2266 }
2267
2268 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2269 // Once we can filter and the filter will return a result larger than itself, we should be
2270 // able to remove this constraint.
2271 // skbug.com/4526
2272 //
2273 SkPoint pt;
2274 ctm.mapXY(x, y, &pt);
2275 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2276 return ir.contains(fMCRec->fRasterClip.getBounds());
2277}
2278
reeda85d4d02015-05-06 12:56:48 -07002279void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002280 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002281 SkRect bounds = SkRect::MakeXYWH(x, y,
2282 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002283 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002284 SkRect tmp = bounds;
2285 if (paint) {
2286 paint->computeFastBounds(tmp, &tmp);
2287 }
2288 if (this->quickReject(tmp)) {
2289 return;
2290 }
reeda85d4d02015-05-06 12:56:48 -07002291 }
halcanary9d524f22016-03-29 09:03:52 -07002292
reeda85d4d02015-05-06 12:56:48 -07002293 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002294 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002295 paint = lazy.init();
2296 }
reed262a71b2015-12-05 13:07:27 -08002297
reed129ed1c2016-02-22 06:42:31 -08002298 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2299 *paint);
2300 if (drawAsSprite && paint->getImageFilter()) {
2301 SkBitmap bitmap;
2302 if (!as_IB(image)->asBitmapForImageFilters(&bitmap)) {
2303 drawAsSprite = false;
2304 } else{
2305 // Until imagefilters are updated, they cannot handle any src type but N32...
2306 if (bitmap.info().colorType() != kN32_SkColorType || bitmap.info().isSRGB()) {
2307 drawAsSprite = false;
2308 }
2309 }
2310 }
2311
reed262a71b2015-12-05 13:07:27 -08002312 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
2313
reeda85d4d02015-05-06 12:56:48 -07002314 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002315 const SkPaint& pnt = looper.paint();
2316 if (drawAsSprite && pnt.getImageFilter()) {
2317 SkBitmap bitmap;
2318 if (as_IB(image)->asBitmapForImageFilters(&bitmap)) {
2319 SkPoint pt;
2320 iter.fMatrix->mapXY(x, y, &pt);
robertphillips2302de92016-03-24 07:26:32 -07002321 iter.fDevice->drawSpriteWithFilter(iter, bitmap,
2322 SkScalarRoundToInt(pt.fX),
2323 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002324 }
2325 } else {
2326 iter.fDevice->drawImage(iter, image, x, y, pnt);
2327 }
reeda85d4d02015-05-06 12:56:48 -07002328 }
halcanary9d524f22016-03-29 09:03:52 -07002329
reeda85d4d02015-05-06 12:56:48 -07002330 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002331}
2332
reed41af9662015-01-05 07:49:08 -08002333void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002334 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002335 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
halcanary96fcdcc2015-08-27 07:41:13 -07002336 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002337 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002338 if (paint) {
2339 paint->computeFastBounds(dst, &storage);
2340 }
2341 if (this->quickReject(storage)) {
2342 return;
2343 }
reeda85d4d02015-05-06 12:56:48 -07002344 }
2345 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002346 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002347 paint = lazy.init();
2348 }
halcanary9d524f22016-03-29 09:03:52 -07002349
senorblancoc41e7e12015-12-07 12:51:30 -08002350 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002351 image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002352
reeda85d4d02015-05-06 12:56:48 -07002353 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002354 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002355 }
halcanary9d524f22016-03-29 09:03:52 -07002356
reeda85d4d02015-05-06 12:56:48 -07002357 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002358}
2359
reed41af9662015-01-05 07:49:08 -08002360void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002361 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002362 SkDEBUGCODE(bitmap.validate();)
2363
reed33366972015-10-08 09:22:02 -07002364 if (bitmap.drawsNothing()) {
2365 return;
2366 }
2367
2368 SkLazyPaint lazy;
2369 if (nullptr == paint) {
2370 paint = lazy.init();
2371 }
2372
2373 const SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2374
2375 SkRect storage;
2376 const SkRect* bounds = nullptr;
2377 if (paint->canComputeFastBounds()) {
2378 bitmap.getBounds(&storage);
2379 matrix.mapRect(&storage);
senorblanco87e066e2015-10-28 11:23:36 -07002380 SkRect tmp = storage;
2381 if (this->quickReject(paint->computeFastBounds(tmp, &tmp))) {
2382 return;
2383 }
2384 bounds = &storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002385 }
reed@google.com4b226022011-01-11 18:32:13 +00002386
reed129ed1c2016-02-22 06:42:31 -08002387 bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(), bitmap.height(),
2388 *paint);
2389 if (drawAsSprite && paint->getImageFilter()) {
2390 // Until imagefilters are updated, they cannot handle any src type but N32...
2391 if (bitmap.info().colorType() != kN32_SkColorType || bitmap.info().isSRGB()) {
2392 drawAsSprite = false;
2393 }
2394 }
2395
reed262a71b2015-12-05 13:07:27 -08002396 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds)
reed33366972015-10-08 09:22:02 -07002397
2398 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002399 const SkPaint& pnt = looper.paint();
2400 if (drawAsSprite && pnt.getImageFilter()) {
2401 SkPoint pt;
2402 iter.fMatrix->mapXY(x, y, &pt);
robertphillips2302de92016-03-24 07:26:32 -07002403 iter.fDevice->drawSpriteWithFilter(iter, bitmap,
2404 SkScalarRoundToInt(pt.fX),
2405 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002406 } else {
2407 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
2408 }
reed33366972015-10-08 09:22:02 -07002409 }
reed262a71b2015-12-05 13:07:27 -08002410
reed33366972015-10-08 09:22:02 -07002411 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002412}
2413
reed@google.com9987ec32011-09-07 11:57:52 +00002414// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002415void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002416 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002417 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002418 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002419 return;
2420 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002421
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@android.com8a1c16f2008-12-17 15:59:43 +00002427 }
reed@google.com3d608122011-11-21 15:16:16 +00002428
reed@google.com33535f32012-09-25 15:37:50 +00002429 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002430 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002431 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002432 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002433
senorblancoc41e7e12015-12-07 12:51:30 -08002434 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002435 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002436
reed@google.com33535f32012-09-25 15:37:50 +00002437 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002438 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002439 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002440
reed@google.com33535f32012-09-25 15:37:50 +00002441 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002442}
2443
reed41af9662015-01-05 07:49:08 -08002444void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002445 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002446 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002447 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002448 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002449}
2450
reed4c21dc52015-06-25 12:32:03 -07002451void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2452 const SkPaint* paint) {
2453 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
halcanary9d524f22016-03-29 09:03:52 -07002454
halcanary96fcdcc2015-08-27 07:41:13 -07002455 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002456 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002457 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2458 return;
2459 }
reed@google.com3d608122011-11-21 15:16:16 +00002460 }
halcanary9d524f22016-03-29 09:03:52 -07002461
reed4c21dc52015-06-25 12:32:03 -07002462 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002463 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002464 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002465 }
halcanary9d524f22016-03-29 09:03:52 -07002466
senorblancoc41e7e12015-12-07 12:51:30 -08002467 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002468
reed4c21dc52015-06-25 12:32:03 -07002469 while (iter.next()) {
2470 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002471 }
halcanary9d524f22016-03-29 09:03:52 -07002472
reed4c21dc52015-06-25 12:32:03 -07002473 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002474}
2475
reed41af9662015-01-05 07:49:08 -08002476void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2477 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002478 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002479 SkDEBUGCODE(bitmap.validate();)
2480
halcanary96fcdcc2015-08-27 07:41:13 -07002481 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002482 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002483 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2484 return;
2485 }
reed4c21dc52015-06-25 12:32:03 -07002486 }
halcanary9d524f22016-03-29 09:03:52 -07002487
reed4c21dc52015-06-25 12:32:03 -07002488 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002489 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002490 paint = lazy.init();
2491 }
halcanary9d524f22016-03-29 09:03:52 -07002492
senorblancoc41e7e12015-12-07 12:51:30 -08002493 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002494
reed4c21dc52015-06-25 12:32:03 -07002495 while (iter.next()) {
2496 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2497 }
halcanary9d524f22016-03-29 09:03:52 -07002498
reed4c21dc52015-06-25 12:32:03 -07002499 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002500}
2501
reed@google.comf67e4cf2011-03-15 20:56:58 +00002502class SkDeviceFilteredPaint {
2503public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002504 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002505 uint32_t filteredFlags = device->filterTextFlags(paint);
2506 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002507 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002508 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002509 fPaint = newPaint;
2510 } else {
2511 fPaint = &paint;
2512 }
2513 }
2514
reed@google.comf67e4cf2011-03-15 20:56:58 +00002515 const SkPaint& paint() const { return *fPaint; }
2516
2517private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002518 const SkPaint* fPaint;
2519 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002520};
2521
bungeman@google.com52c748b2011-08-22 21:30:43 +00002522void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2523 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002524 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002525 draw.fDevice->drawRect(draw, r, paint);
2526 } else {
2527 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002528 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002529 draw.fDevice->drawRect(draw, r, p);
2530 }
2531}
2532
2533void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2534 const char text[], size_t byteLength,
2535 SkScalar x, SkScalar y) {
halcanary96fcdcc2015-08-27 07:41:13 -07002536 SkASSERT(byteLength == 0 || text != nullptr);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002537
2538 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07002539 if (text == nullptr || byteLength == 0 ||
bungeman@google.com52c748b2011-08-22 21:30:43 +00002540 draw.fClip->isEmpty() ||
halcanary96fcdcc2015-08-27 07:41:13 -07002541 (paint.getAlpha() == 0 && paint.getXfermode() == nullptr)) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002542 return;
2543 }
2544
2545 SkScalar width = 0;
2546 SkPoint start;
2547
2548 start.set(0, 0); // to avoid warning
2549 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2550 SkPaint::kStrikeThruText_Flag)) {
2551 width = paint.measureText(text, byteLength);
2552
2553 SkScalar offsetX = 0;
2554 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2555 offsetX = SkScalarHalf(width);
2556 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2557 offsetX = width;
2558 }
2559 start.set(x - offsetX, y);
2560 }
2561
2562 if (0 == width) {
2563 return;
2564 }
2565
2566 uint32_t flags = paint.getFlags();
2567
2568 if (flags & (SkPaint::kUnderlineText_Flag |
2569 SkPaint::kStrikeThruText_Flag)) {
2570 SkScalar textSize = paint.getTextSize();
2571 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2572 SkRect r;
2573
2574 r.fLeft = start.fX;
2575 r.fRight = start.fX + width;
2576
2577 if (flags & SkPaint::kUnderlineText_Flag) {
2578 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2579 start.fY);
2580 r.fTop = offset;
2581 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002582 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002583 }
2584 if (flags & SkPaint::kStrikeThruText_Flag) {
2585 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2586 start.fY);
2587 r.fTop = offset;
2588 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002589 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002590 }
2591 }
2592}
2593
reed@google.come0d9ce82014-04-23 04:00:17 +00002594void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2595 const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002596 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002597
2598 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002599 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002600 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002601 DrawTextDecorations(iter, dfp.paint(),
2602 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002603 }
2604
reed@google.com4e2b3d32011-04-07 14:18:59 +00002605 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002606}
2607
reed@google.come0d9ce82014-04-23 04:00:17 +00002608void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2609 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002610 SkPoint textOffset = SkPoint::Make(0, 0);
2611
halcanary96fcdcc2015-08-27 07:41:13 -07002612 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002613
reed@android.com8a1c16f2008-12-17 15:59:43 +00002614 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002615 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002616 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002617 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002618 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002619
reed@google.com4e2b3d32011-04-07 14:18:59 +00002620 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002621}
2622
reed@google.come0d9ce82014-04-23 04:00:17 +00002623void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2624 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002625
2626 SkPoint textOffset = SkPoint::Make(0, constY);
2627
halcanary96fcdcc2015-08-27 07:41:13 -07002628 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002629
reed@android.com8a1c16f2008-12-17 15:59:43 +00002630 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002631 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002632 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002633 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002634 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002635
reed@google.com4e2b3d32011-04-07 14:18:59 +00002636 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002637}
2638
reed@google.come0d9ce82014-04-23 04:00:17 +00002639void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2640 const SkMatrix* matrix, const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002641 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002642
reed@android.com8a1c16f2008-12-17 15:59:43 +00002643 while (iter.next()) {
2644 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002645 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002646 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002647
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002648 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002649}
2650
fmalita00d5c2c2014-08-21 08:53:26 -07002651void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2652 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002653
fmalita85d5eb92015-03-04 11:20:12 -08002654 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002655 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002656 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002657 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002658 SkRect tmp;
2659 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2660 return;
2661 }
2662 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002663 }
2664
fmalita024f9962015-03-03 19:08:17 -08002665 // We cannot filter in the looper as we normally do, because the paint is
2666 // incomplete at this point (text-related attributes are embedded within blob run paints).
2667 SkDrawFilter* drawFilter = fMCRec->fFilter;
halcanary96fcdcc2015-08-27 07:41:13 -07002668 fMCRec->fFilter = nullptr;
fmalita024f9962015-03-03 19:08:17 -08002669
fmalita85d5eb92015-03-04 11:20:12 -08002670 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002671
fmalitaaa1b9122014-08-28 14:32:24 -07002672 while (iter.next()) {
2673 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002674 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002675 }
2676
fmalitaaa1b9122014-08-28 14:32:24 -07002677 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002678
2679 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002680}
2681
reed@google.come0d9ce82014-04-23 04:00:17 +00002682// These will become non-virtual, so they always call the (virtual) onDraw... method
2683void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2684 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002685 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002686 this->onDrawText(text, byteLength, x, y, paint);
2687}
2688void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2689 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002690 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002691 this->onDrawPosText(text, byteLength, pos, paint);
2692}
2693void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2694 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002695 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002696 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2697}
2698void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2699 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002700 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002701 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2702}
fmalita00d5c2c2014-08-21 08:53:26 -07002703void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2704 const SkPaint& paint) {
reede3b38ce2016-01-08 09:18:44 -08002705 RETURN_ON_NULL(blob);
danakj9881d632014-11-26 12:41:06 -08002706 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
reede3b38ce2016-01-08 09:18:44 -08002707 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002708}
reed@google.come0d9ce82014-04-23 04:00:17 +00002709
reed41af9662015-01-05 07:49:08 -08002710void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2711 const SkPoint verts[], const SkPoint texs[],
2712 const SkColor colors[], SkXfermode* xmode,
2713 const uint16_t indices[], int indexCount,
2714 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002715 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
halcanary96fcdcc2015-08-27 07:41:13 -07002716 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
reed@google.com4b226022011-01-11 18:32:13 +00002717
reed@android.com8a1c16f2008-12-17 15:59:43 +00002718 while (iter.next()) {
2719 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002720 colors, xmode, indices, indexCount,
2721 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002722 }
reed@google.com4b226022011-01-11 18:32:13 +00002723
reed@google.com4e2b3d32011-04-07 14:18:59 +00002724 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002725}
2726
dandovb3c9d1c2014-08-12 08:34:29 -07002727void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2728 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002729 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
halcanary96fcdcc2015-08-27 07:41:13 -07002730 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002731 return;
2732 }
mtklein6cfa73a2014-08-13 13:33:49 -07002733
dandovecfff212014-08-04 10:02:00 -07002734 // Since a patch is always within the convex hull of the control points, we discard it when its
2735 // bounding rectangle is completely outside the current clip.
2736 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002737 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002738 if (this->quickReject(bounds)) {
2739 return;
2740 }
mtklein6cfa73a2014-08-13 13:33:49 -07002741
dandovb3c9d1c2014-08-12 08:34:29 -07002742 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2743}
2744
2745void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2746 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2747
halcanary96fcdcc2015-08-27 07:41:13 -07002748 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002749
dandovecfff212014-08-04 10:02:00 -07002750 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002751 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002752 }
mtklein6cfa73a2014-08-13 13:33:49 -07002753
dandovecfff212014-08-04 10:02:00 -07002754 LOOPER_END
2755}
2756
reeda8db7282015-07-07 10:22:31 -07002757void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
reede3b38ce2016-01-08 09:18:44 -08002758 RETURN_ON_NULL(dr);
2759 if (x || y) {
2760 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2761 this->onDrawDrawable(dr, &matrix);
2762 } else {
2763 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002764 }
2765}
2766
reeda8db7282015-07-07 10:22:31 -07002767void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reede3b38ce2016-01-08 09:18:44 -08002768 RETURN_ON_NULL(dr);
2769 if (matrix && matrix->isIdentity()) {
2770 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002771 }
reede3b38ce2016-01-08 09:18:44 -08002772 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002773}
2774
2775void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2776 SkRect bounds = dr->getBounds();
2777 if (matrix) {
2778 matrix->mapRect(&bounds);
2779 }
2780 if (this->quickReject(bounds)) {
2781 return;
2782 }
2783 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002784}
2785
reed71c3c762015-06-24 10:29:17 -07002786void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2787 const SkColor colors[], int count, SkXfermode::Mode mode,
2788 const SkRect* cull, const SkPaint* paint) {
2789 if (cull && this->quickReject(*cull)) {
2790 return;
2791 }
2792
2793 SkPaint pnt;
2794 if (paint) {
2795 pnt = *paint;
2796 }
halcanary9d524f22016-03-29 09:03:52 -07002797
halcanary96fcdcc2015-08-27 07:41:13 -07002798 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr)
reed71c3c762015-06-24 10:29:17 -07002799 while (iter.next()) {
2800 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, mode, pnt);
2801 }
2802 LOOPER_END
2803}
2804
reedf70b5312016-03-04 16:36:20 -08002805void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2806 SkASSERT(key);
2807
2808 SkPaint paint;
2809 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, nullptr)
2810 while (iter.next()) {
2811 iter.fDevice->drawAnnotation(iter, rect, key, value);
2812 }
2813 LOOPER_END
2814}
2815
reed@android.com8a1c16f2008-12-17 15:59:43 +00002816//////////////////////////////////////////////////////////////////////////////
2817// These methods are NOT virtual, and therefore must call back into virtual
2818// methods, rather than actually drawing themselves.
2819//////////////////////////////////////////////////////////////////////////////
2820
2821void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002822 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002823 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002824 SkPaint paint;
2825
2826 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002827 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002828 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002829 }
2830 this->drawPaint(paint);
2831}
2832
reed@android.com845fdac2009-06-23 03:01:32 +00002833void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002834 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002835 SkPaint paint;
2836
2837 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002838 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002839 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002840 }
2841 this->drawPaint(paint);
2842}
2843
2844void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002845 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002846 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002847
reed@android.com8a1c16f2008-12-17 15:59:43 +00002848 pt.set(x, y);
2849 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2850}
2851
2852void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002853 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002854 SkPoint pt;
2855 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002856
reed@android.com8a1c16f2008-12-17 15:59:43 +00002857 pt.set(x, y);
2858 paint.setColor(color);
2859 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2860}
2861
2862void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2863 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002864 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002865 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002866
reed@android.com8a1c16f2008-12-17 15:59:43 +00002867 pts[0].set(x0, y0);
2868 pts[1].set(x1, y1);
2869 this->drawPoints(kLines_PointMode, 2, pts, paint);
2870}
2871
2872void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2873 SkScalar right, SkScalar bottom,
2874 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002875 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002876 SkRect r;
2877
2878 r.set(left, top, right, bottom);
2879 this->drawRect(r, paint);
2880}
2881
2882void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2883 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002884 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002885 if (radius < 0) {
2886 radius = 0;
2887 }
2888
2889 SkRect r;
2890 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002891 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002892}
2893
2894void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2895 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002896 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002897 if (rx > 0 && ry > 0) {
2898 if (paint.canComputeFastBounds()) {
2899 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002900 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002901 return;
2902 }
2903 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002904 SkRRect rrect;
2905 rrect.setRectXY(r, rx, ry);
2906 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002907 } else {
2908 this->drawRect(r, paint);
2909 }
2910}
2911
reed@android.com8a1c16f2008-12-17 15:59:43 +00002912void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2913 SkScalar sweepAngle, bool useCenter,
2914 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002915 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002916 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2917 this->drawOval(oval, paint);
2918 } else {
2919 SkPath path;
2920 if (useCenter) {
2921 path.moveTo(oval.centerX(), oval.centerY());
2922 }
2923 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2924 if (useCenter) {
2925 path.close();
2926 }
2927 this->drawPath(path, paint);
2928 }
2929}
2930
2931void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2932 const SkPath& path, SkScalar hOffset,
2933 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002934 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002935 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002936
reed@android.com8a1c16f2008-12-17 15:59:43 +00002937 matrix.setTranslate(hOffset, vOffset);
2938 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2939}
2940
reed@android.comf76bacf2009-05-13 14:00:33 +00002941///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07002942
2943/**
2944 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2945 * against the playback cost of recursing into the subpicture to get at its actual ops.
2946 *
2947 * For now we pick a conservatively small value, though measurement (and other heuristics like
2948 * the type of ops contained) may justify changing this value.
2949 */
2950#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07002951
reedd5fa1a42014-08-09 11:08:05 -07002952void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002953 RETURN_ON_NULL(picture);
2954
reed1c2c4412015-04-30 13:09:24 -07002955 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
reede3b38ce2016-01-08 09:18:44 -08002956 if (matrix && matrix->isIdentity()) {
2957 matrix = nullptr;
2958 }
2959 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2960 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2961 picture->playback(this);
2962 } else {
2963 this->onDrawPicture(picture, matrix, paint);
reedd5fa1a42014-08-09 11:08:05 -07002964 }
2965}
robertphillips9b14f262014-06-04 05:40:44 -07002966
reedd5fa1a42014-08-09 11:08:05 -07002967void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2968 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07002969 if (!paint || paint->canComputeFastBounds()) {
2970 SkRect bounds = picture->cullRect();
2971 if (paint) {
2972 paint->computeFastBounds(bounds, &bounds);
2973 }
2974 if (matrix) {
2975 matrix->mapRect(&bounds);
2976 }
2977 if (this->quickReject(bounds)) {
2978 return;
2979 }
2980 }
2981
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002982 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002983 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002984 // Canvas has to first give the device the opportunity to render
2985 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002986 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002987 return; // the device has rendered the entire picture
2988 }
2989 }
2990
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002991 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07002992 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002993}
2994
reed@android.com8a1c16f2008-12-17 15:59:43 +00002995///////////////////////////////////////////////////////////////////////////////
2996///////////////////////////////////////////////////////////////////////////////
2997
2998SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
bungeman99fe8222015-08-20 07:57:51 -07002999 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003000
3001 SkASSERT(canvas);
3002
3003 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
3004 fDone = !fImpl->next();
3005}
3006
3007SkCanvas::LayerIter::~LayerIter() {
3008 fImpl->~SkDrawIter();
3009}
3010
3011void SkCanvas::LayerIter::next() {
3012 fDone = !fImpl->next();
3013}
3014
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003015SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003016 return fImpl->getDevice();
3017}
3018
3019const SkMatrix& SkCanvas::LayerIter::matrix() const {
3020 return fImpl->getMatrix();
3021}
3022
3023const SkPaint& SkCanvas::LayerIter::paint() const {
3024 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003025 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003026 paint = &fDefaultPaint;
3027 }
3028 return *paint;
3029}
3030
3031const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
3032int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
3033int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003034
3035///////////////////////////////////////////////////////////////////////////////
3036
fmalitac3b589a2014-06-05 12:40:07 -07003037SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003038
3039///////////////////////////////////////////////////////////////////////////////
3040
3041static bool supported_for_raster_canvas(const SkImageInfo& info) {
3042 switch (info.alphaType()) {
3043 case kPremul_SkAlphaType:
3044 case kOpaque_SkAlphaType:
3045 break;
3046 default:
3047 return false;
3048 }
3049
3050 switch (info.colorType()) {
3051 case kAlpha_8_SkColorType:
3052 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00003053 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003054 break;
3055 default:
3056 return false;
3057 }
3058
3059 return true;
3060}
3061
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003062SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
3063 if (!supported_for_raster_canvas(info)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003064 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003065 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003066
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003067 SkBitmap bitmap;
3068 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003069 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003070 }
halcanary385fe4d2015-08-26 13:07:48 -07003071 return new SkCanvas(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003072}
reedd5fa1a42014-08-09 11:08:05 -07003073
3074///////////////////////////////////////////////////////////////////////////////
3075
3076SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003077 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07003078 : fCanvas(canvas)
3079 , fSaveCount(canvas->getSaveCount())
3080{
bsalomon49f085d2014-09-05 13:34:00 -07003081 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003082 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07003083 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003084 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07003085 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003086 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07003087 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003088 canvas->save();
3089 }
mtklein6cfa73a2014-08-13 13:33:49 -07003090
bsalomon49f085d2014-09-05 13:34:00 -07003091 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003092 canvas->concat(*matrix);
3093 }
3094}
3095
3096SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
3097 fCanvas->restoreToCount(fSaveCount);
3098}
reede8f30622016-03-23 18:59:25 -07003099
3100#ifdef SK_SUPPORT_LEGACY_NEW_SURFACE_API
3101SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
3102 return this->makeSurface(info, props).release();
3103}
3104#endif