blob: 0ce97fff20661f8e2c82aacb801d2bb1cf574718 [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00003 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00004 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00006 */
7
bungemand3ebb482015-08-05 13:57:49 -07008#include "SkBitmapDevice.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +00009#include "SkCanvas.h"
reedd5fa1a42014-08-09 11:08:05 -070010#include "SkCanvasPriv.h"
bungemand3ebb482015-08-05 13:57:49 -070011#include "SkClipStack.h"
reeddbc3cef2015-04-29 12:18:57 -070012#include "SkColorFilter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013#include "SkDraw.h"
reed3cb38402015-02-06 08:36:15 -080014#include "SkDrawable.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000015#include "SkDrawFilter.h"
16#include "SkDrawLooper.h"
joshualitt5f5a8d72015-02-25 14:09:45 -080017#include "SkErrorInternals.h"
piotaixrb5fae932014-09-24 13:03:30 -070018#include "SkImage.h"
reed262a71b2015-12-05 13:07:27 -080019#include "SkImage_Base.h"
senorblanco900c3672016-04-27 11:31:23 -070020#include "SkImageFilter.h"
21#include "SkImageFilterCache.h"
reed262a71b2015-12-05 13:07:27 -080022#include "SkMatrixUtils.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000023#include "SkMetaData.h"
reed4c21dc52015-06-25 12:32:03 -070024#include "SkNinePatchIter.h"
reedc83a2972015-07-16 07:40:45 -070025#include "SkPaintPriv.h"
dandovb3c9d1c2014-08-12 08:34:29 -070026#include "SkPatchUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000027#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000028#include "SkRasterClip.h"
reed96472de2014-12-10 09:53:42 -080029#include "SkReadPixelsRec.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000030#include "SkRRect.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000031#include "SkSmallAllocator.h"
robertphillips4418dba2016-03-07 12:45:14 -080032#include "SkSpecialImage.h"
reed@google.com97af1a62012-08-28 12:19:02 +000033#include "SkSurface_Base.h"
fmalita7ba7aa72014-08-29 09:46:36 -070034#include "SkTextBlob.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000035#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000036#include "SkTLazy.h"
danakj8f757f52014-11-04 11:48:43 -080037#include "SkTraceEvent.h"
bungemand3ebb482015-08-05 13:57:49 -070038
39#include <new>
reed@android.com8a1c16f2008-12-17 15:59:43 +000040
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000041#if SK_SUPPORT_GPU
robertphillips7354a4b2015-12-16 05:08:27 -080042#include "GrContext.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000043#include "GrRenderTarget.h"
robertphillips7354a4b2015-12-16 05:08:27 -080044#include "SkGr.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000045#endif
46
reede3b38ce2016-01-08 09:18:44 -080047#define RETURN_ON_NULL(ptr) do { if (nullptr == (ptr)) return; } while (0)
48
reed2d1afab2016-06-29 14:33:11 -070049//#define SK_SUPPORT_PRECHECK_CLIPRECT
50
reedc83a2972015-07-16 07:40:45 -070051/*
52 * Return true if the drawing this rect would hit every pixels in the canvas.
53 *
54 * Returns false if
55 * - rect does not contain the canvas' bounds
56 * - paint is not fill
57 * - paint would blur or otherwise change the coverage of the rect
58 */
59bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
60 ShaderOverrideOpacity overrideOpacity) const {
bungeman99fe8222015-08-20 07:57:51 -070061 static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
62 (int)kNone_ShaderOverrideOpacity,
63 "need_matching_enums0");
64 static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
65 (int)kOpaque_ShaderOverrideOpacity,
66 "need_matching_enums1");
67 static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
68 (int)kNotOpaque_ShaderOverrideOpacity,
69 "need_matching_enums2");
reedc83a2972015-07-16 07:40:45 -070070
71 const SkISize size = this->getBaseLayerSize();
72 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
73 if (!this->getClipStack()->quickContains(bounds)) {
74 return false;
75 }
76
77 if (rect) {
reed74467162016-06-30 08:15:35 -070078 if (!this->getTotalMatrix().isScaleTranslate()) {
reedc83a2972015-07-16 07:40:45 -070079 return false; // conservative
80 }
81
82 SkRect devRect;
reed74467162016-06-30 08:15:35 -070083 this->getTotalMatrix().mapRectScaleTranslate(&devRect, *rect);
fmalita8c0144c2015-07-22 05:56:16 -070084 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -070085 return false;
86 }
87 }
88
89 if (paint) {
90 SkPaint::Style paintStyle = paint->getStyle();
91 if (!(paintStyle == SkPaint::kFill_Style ||
92 paintStyle == SkPaint::kStrokeAndFill_Style)) {
93 return false;
94 }
95 if (paint->getMaskFilter() || paint->getLooper()
96 || paint->getPathEffect() || paint->getImageFilter()) {
97 return false; // conservative
98 }
99 }
100 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
101}
102
103///////////////////////////////////////////////////////////////////////////////////////////////////
104
reedd990e2f2014-12-22 11:58:30 -0800105static bool gIgnoreSaveLayerBounds;
106void SkCanvas::Internal_Private_SetIgnoreSaveLayerBounds(bool ignore) {
107 gIgnoreSaveLayerBounds = ignore;
108}
109bool SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds() {
110 return gIgnoreSaveLayerBounds;
111}
112
reed0acf1b42014-12-22 16:12:38 -0800113static bool gTreatSpriteAsBitmap;
114void SkCanvas::Internal_Private_SetTreatSpriteAsBitmap(bool spriteAsBitmap) {
115 gTreatSpriteAsBitmap = spriteAsBitmap;
116}
117bool SkCanvas::Internal_Private_GetTreatSpriteAsBitmap() {
118 return gTreatSpriteAsBitmap;
119}
120
reed@google.comda17f752012-08-16 18:27:05 +0000121// experimental for faster tiled drawing...
122//#define SK_ENABLE_CLIP_QUICKREJECT
reed@android.com8a1c16f2008-12-17 15:59:43 +0000123//#define SK_TRACE_SAVERESTORE
124
125#ifdef SK_TRACE_SAVERESTORE
126 static int gLayerCounter;
127 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
128 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
129
130 static int gRecCounter;
131 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
132 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
133
134 static int gCanvasCounter;
135 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
136 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
137#else
138 #define inc_layer()
139 #define dec_layer()
140 #define inc_rec()
141 #define dec_rec()
142 #define inc_canvas()
143 #define dec_canvas()
144#endif
145
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000146typedef SkTLazy<SkPaint> SkLazyPaint;
147
reedc83a2972015-07-16 07:40:45 -0700148void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000149 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700150 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
151 ? SkSurface::kDiscard_ContentChangeMode
152 : SkSurface::kRetain_ContentChangeMode);
153 }
154}
155
156void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
157 ShaderOverrideOpacity overrideOpacity) {
158 if (fSurfaceBase) {
159 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
160 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
161 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
162 // and therefore we don't care which mode we're in.
163 //
164 if (fSurfaceBase->outstandingImageSnapshot()) {
165 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
166 mode = SkSurface::kDiscard_ContentChangeMode;
167 }
168 }
169 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000170 }
171}
172
reed@android.com8a1c16f2008-12-17 15:59:43 +0000173///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000174
reed4a8126e2014-09-22 07:29:03 -0700175static uint32_t filter_paint_flags(const SkSurfaceProps& props, uint32_t flags) {
176 const uint32_t propFlags = props.flags();
177 if (propFlags & SkSurfaceProps::kDisallowDither_Flag) {
178 flags &= ~SkPaint::kDither_Flag;
179 }
180 if (propFlags & SkSurfaceProps::kDisallowAntiAlias_Flag) {
181 flags &= ~SkPaint::kAntiAlias_Flag;
182 }
183 return flags;
184}
185
186///////////////////////////////////////////////////////////////////////////////
187
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000188/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000189 The clip/matrix/proc are fields that reflect the top of the save/restore
190 stack. Whenever the canvas changes, it marks a dirty flag, and then before
191 these are used (assuming we're not on a layer) we rebuild these cache
192 values: they reflect the top of the save stack, but translated and clipped
193 by the device's XY offset and bitmap-bounds.
194*/
195struct DeviceCM {
196 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000197 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000198 SkRasterClip fClip;
reed@google.com6f8f2922011-03-04 22:27:10 +0000199 SkPaint* fPaint; // may be null (in the future)
reed61f501f2015-04-29 08:34:00 -0700200 const SkMatrix* fMatrix;
201 SkMatrix fMatrixStorage;
reed8c30a812016-04-20 16:36:51 -0700202 SkMatrix fStashedMatrix; // original CTM; used by imagefilter in saveLayer
reed61f501f2015-04-29 08:34:00 -0700203 const bool fDeviceIsBitmapDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204
reed96e657d2015-03-10 17:30:07 -0700205 DeviceCM(SkBaseDevice* device, const SkPaint* paint, SkCanvas* canvas,
reed8c30a812016-04-20 16:36:51 -0700206 bool conservativeRasterClip, bool deviceIsBitmapDevice, const SkMatrix& stashed)
halcanary96fcdcc2015-08-27 07:41:13 -0700207 : fNext(nullptr)
reedd9544982014-09-09 18:46:22 -0700208 , fClip(conservativeRasterClip)
reed8c30a812016-04-20 16:36:51 -0700209 , fStashedMatrix(stashed)
reed61f501f2015-04-29 08:34:00 -0700210 , fDeviceIsBitmapDevice(deviceIsBitmapDevice)
reedd9544982014-09-09 18:46:22 -0700211 {
halcanary96fcdcc2015-08-27 07:41:13 -0700212 if (nullptr != device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000213 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000214 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215 }
reed@google.com4b226022011-01-11 18:32:13 +0000216 fDevice = device;
halcanary96fcdcc2015-08-27 07:41:13 -0700217 fPaint = paint ? new SkPaint(*paint) : nullptr;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000218 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000219
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000220 ~DeviceCM() {
bsalomon49f085d2014-09-05 13:34:00 -0700221 if (fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000222 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000223 fDevice->unref();
224 }
halcanary385fe4d2015-08-26 13:07:48 -0700225 delete fPaint;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000226 }
reed@google.com4b226022011-01-11 18:32:13 +0000227
mtkleinfeaadee2015-04-08 11:25:48 -0700228 void reset(const SkIRect& bounds) {
229 SkASSERT(!fPaint);
230 SkASSERT(!fNext);
231 SkASSERT(fDevice);
232 fClip.setRect(bounds);
233 }
234
reed@google.com045e62d2011-10-24 12:19:46 +0000235 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
236 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000237 int x = fDevice->getOrigin().x();
238 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000239 int width = fDevice->width();
240 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000241
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242 if ((x | y) == 0) {
243 fMatrix = &totalMatrix;
244 fClip = totalClip;
245 } else {
246 fMatrixStorage = totalMatrix;
247 fMatrixStorage.postTranslate(SkIntToScalar(-x),
248 SkIntToScalar(-y));
249 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000250
reed@android.com8a1c16f2008-12-17 15:59:43 +0000251 totalClip.translate(-x, -y, &fClip);
252 }
253
reed@google.com045e62d2011-10-24 12:19:46 +0000254 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255
256 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000257
reed@android.com8a1c16f2008-12-17 15:59:43 +0000258 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000259 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260 SkRegion::kDifference_Op);
261 }
reed@google.com4b226022011-01-11 18:32:13 +0000262
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000263 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
264
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265#ifdef SK_DEBUG
266 if (!fClip.isEmpty()) {
267 SkIRect deviceR;
268 deviceR.set(0, 0, width, height);
269 SkASSERT(deviceR.contains(fClip.getBounds()));
270 }
271#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000272 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000273};
274
275/* This is the record we keep for each save/restore level in the stack.
276 Since a level optionally copies the matrix and/or stack, we have pointers
277 for these fields. If the value is copied for this level, the copy is
278 stored in the ...Storage field, and the pointer points to that. If the
279 value is not copied for this level, we ignore ...Storage, and just point
280 at the corresponding value in the previous level in the stack.
281*/
282class SkCanvas::MCRec {
283public:
reed1f836ee2014-07-07 07:49:34 -0700284 SkDrawFilter* fFilter; // the current filter (or null)
reedd9544982014-09-09 18:46:22 -0700285 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000286 /* If there are any layers in the stack, this points to the top-most
287 one that is at or below this level in the stack (so we know what
288 bitmap/device to draw into from this level. This value is NOT
289 reference counted, since the real owner is either our fLayer field,
290 or a previous one in a lower level.)
291 */
reed2ff1fce2014-12-11 07:07:37 -0800292 DeviceCM* fTopLayer;
293 SkRasterClip fRasterClip;
294 SkMatrix fMatrix;
295 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000296
reedd9544982014-09-09 18:46:22 -0700297 MCRec(bool conservativeRasterClip) : fRasterClip(conservativeRasterClip) {
halcanary96fcdcc2015-08-27 07:41:13 -0700298 fFilter = nullptr;
299 fLayer = nullptr;
300 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800301 fMatrix.reset();
302 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700303
reedd9544982014-09-09 18:46:22 -0700304 // don't bother initializing fNext
305 inc_rec();
306 }
reed2ff1fce2014-12-11 07:07:37 -0800307 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
reedd9544982014-09-09 18:46:22 -0700308 fFilter = SkSafeRef(prev.fFilter);
halcanary96fcdcc2015-08-27 07:41:13 -0700309 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700310 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800311 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700312
reed@android.com8a1c16f2008-12-17 15:59:43 +0000313 // don't bother initializing fNext
314 inc_rec();
315 }
316 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000317 SkSafeUnref(fFilter);
halcanary385fe4d2015-08-26 13:07:48 -0700318 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319 dec_rec();
320 }
mtkleinfeaadee2015-04-08 11:25:48 -0700321
322 void reset(const SkIRect& bounds) {
323 SkASSERT(fLayer);
324 SkASSERT(fDeferredSaveCount == 0);
325
326 fMatrix.reset();
327 fRasterClip.setRect(bounds);
328 fLayer->reset(bounds);
329 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000330};
331
332class SkDrawIter : public SkDraw {
333public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000334 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000335 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000336 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000337 canvas->updateDeviceCMCache();
338
reed687fa1c2015-04-07 08:00:56 -0700339 fClipStack = canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000340 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000341 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000342 }
reed@google.com4b226022011-01-11 18:32:13 +0000343
reed@android.com8a1c16f2008-12-17 15:59:43 +0000344 bool next() {
345 // skip over recs with empty clips
346 if (fSkipEmptyClips) {
347 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
348 fCurrLayer = fCurrLayer->fNext;
349 }
350 }
351
reed@google.comf68c5e22012-02-24 16:38:58 +0000352 const DeviceCM* rec = fCurrLayer;
353 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000354
355 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000356 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000357 fDevice = rec->fDevice;
reed41e010c2015-06-09 12:16:53 -0700358 if (!fDevice->accessPixels(&fDst)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700359 fDst.reset(fDevice->imageInfo(), nullptr, 0);
reed41e010c2015-06-09 12:16:53 -0700360 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000361 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000362 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000363
364 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700365 // fCurrLayer may be nullptr now
reed@android.com199f1082009-06-10 02:12:47 +0000366
reed@android.com8a1c16f2008-12-17 15:59:43 +0000367 return true;
368 }
369 return false;
370 }
reed@google.com4b226022011-01-11 18:32:13 +0000371
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000372 SkBaseDevice* getDevice() const { return fDevice; }
reed1e7f5e72016-04-27 07:49:17 -0700373 const SkRasterClip& getClip() const { return *fRC; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000374 int getX() const { return fDevice->getOrigin().x(); }
375 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000376 const SkMatrix& getMatrix() const { return *fMatrix; }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000377 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000378
reed@android.com8a1c16f2008-12-17 15:59:43 +0000379private:
380 SkCanvas* fCanvas;
381 const DeviceCM* fCurrLayer;
382 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000383 SkBool8 fSkipEmptyClips;
384
385 typedef SkDraw INHERITED;
386};
387
388/////////////////////////////////////////////////////////////////////////////
389
reeddbc3cef2015-04-29 12:18:57 -0700390static SkPaint* set_if_needed(SkLazyPaint* lazy, const SkPaint& orig) {
391 return lazy->isValid() ? lazy->get() : lazy->set(orig);
392}
393
394/**
395 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700396 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700397 */
reedd053ce92016-03-22 10:17:23 -0700398static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700399 SkImageFilter* imgf = paint.getImageFilter();
400 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700401 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700402 }
403
reedd053ce92016-03-22 10:17:23 -0700404 SkColorFilter* imgCFPtr;
405 if (!imgf->asAColorFilter(&imgCFPtr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700406 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700407 }
reedd053ce92016-03-22 10:17:23 -0700408 sk_sp<SkColorFilter> imgCF(imgCFPtr);
reeddbc3cef2015-04-29 12:18:57 -0700409
410 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700411 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700412 // there is no existing paint colorfilter, so we can just return the imagefilter's
413 return imgCF;
414 }
415
416 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
417 // and we need to combine them into a single colorfilter.
reedd053ce92016-03-22 10:17:23 -0700418 return SkColorFilter::MakeComposeFilter(std::move(imgCF), sk_ref_sp(paintCF));
reeddbc3cef2015-04-29 12:18:57 -0700419}
420
senorblanco87e066e2015-10-28 11:23:36 -0700421/**
422 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
423 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
424 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
425 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
426 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
427 * conservative "effective" bounds based on the settings in the paint... with one exception. This
428 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
429 * deliberately ignored.
430 */
431static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
432 const SkRect& rawBounds,
433 SkRect* storage) {
434 SkPaint tmpUnfiltered(paint);
435 tmpUnfiltered.setImageFilter(nullptr);
436 if (tmpUnfiltered.canComputeFastBounds()) {
437 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
438 } else {
439 return rawBounds;
440 }
441}
442
reed@android.com8a1c16f2008-12-17 15:59:43 +0000443class AutoDrawLooper {
444public:
senorblanco87e066e2015-10-28 11:23:36 -0700445 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
446 // paint. It's used to determine the size of the offscreen layer for filters.
447 // If null, the clip will be used instead.
reed4a8126e2014-09-22 07:29:03 -0700448 AutoDrawLooper(SkCanvas* canvas, const SkSurfaceProps& props, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000449 bool skipLayerForImageFilter = false,
senorblanco87e066e2015-10-28 11:23:36 -0700450 const SkRect* rawBounds = nullptr) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000451 fCanvas = canvas;
fmalita53d9f1c2016-01-25 06:23:54 -0800452#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000453 fFilter = canvas->getDrawFilter();
fmalita77650002016-01-21 18:47:11 -0800454#else
455 fFilter = nullptr;
456#endif
reed4a8126e2014-09-22 07:29:03 -0700457 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000458 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700459 fTempLayerForImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000460 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000461
reedd053ce92016-03-22 10:17:23 -0700462 auto simplifiedCF = image_to_color_filter(fOrigPaint);
reeddbc3cef2015-04-29 12:18:57 -0700463 if (simplifiedCF) {
464 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reedd053ce92016-03-22 10:17:23 -0700465 paint->setColorFilter(std::move(simplifiedCF));
halcanary96fcdcc2015-08-27 07:41:13 -0700466 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700467 fPaint = paint;
468 }
469
470 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700471 /**
472 * We implement ImageFilters for a given draw by creating a layer, then applying the
473 * imagefilter to the pixels of that layer (its backing surface/image), and then
474 * we call restore() to xfer that layer to the main canvas.
475 *
476 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
477 * 2. Generate the src pixels:
478 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
479 * return (fPaint). We then draw the primitive (using srcover) into a cleared
480 * buffer/surface.
481 * 3. Restore the layer created in #1
482 * The imagefilter is passed the buffer/surface from the layer (now filled with the
483 * src pixels of the primitive). It returns a new "filtered" buffer, which we
484 * draw onto the previous layer using the xfermode from the original paint.
485 */
reed@google.com8926b162012-03-23 15:36:36 +0000486 SkPaint tmp;
reeddbc3cef2015-04-29 12:18:57 -0700487 tmp.setImageFilter(fPaint->getImageFilter());
reedcfb6bdf2016-03-29 11:32:50 -0700488 tmp.setXfermode(sk_ref_sp(fPaint->getXfermode()));
senorblanco87e066e2015-10-28 11:23:36 -0700489 SkRect storage;
490 if (rawBounds) {
491 // Make rawBounds include all paint outsets except for those due to image filters.
492 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
493 }
reedbfd5f172016-01-07 11:28:08 -0800494 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &tmp),
reed76033be2015-03-14 10:54:31 -0700495 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700496 fTempLayerForImageFilter = true;
497 // we remove the imagefilter/xfermode inside doNext()
reed@google.com8926b162012-03-23 15:36:36 +0000498 }
499
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000500 if (SkDrawLooper* looper = paint.getLooper()) {
501 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
502 looper->contextSize());
503 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000504 fIsSimple = false;
505 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700506 fLooperContext = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000507 // can we be marked as simple?
reed5c476fb2015-04-20 08:04:21 -0700508 fIsSimple = !fFilter && !fTempLayerForImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000509 }
piotaixrb5fae932014-09-24 13:03:30 -0700510
reed4a8126e2014-09-22 07:29:03 -0700511 uint32_t oldFlags = paint.getFlags();
512 fNewPaintFlags = filter_paint_flags(props, oldFlags);
513 if (fIsSimple && (fNewPaintFlags != oldFlags)) {
reeddbc3cef2015-04-29 12:18:57 -0700514 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700515 paint->setFlags(fNewPaintFlags);
516 fPaint = paint;
517 // if we're not simple, doNext() will take care of calling setFlags()
518 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000519 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000520
reed@android.com8a1c16f2008-12-17 15:59:43 +0000521 ~AutoDrawLooper() {
reed5c476fb2015-04-20 08:04:21 -0700522 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000523 fCanvas->internalRestore();
524 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000525 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000526 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000527
reed@google.com4e2b3d32011-04-07 14:18:59 +0000528 const SkPaint& paint() const {
529 SkASSERT(fPaint);
530 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000531 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000532
reed@google.com129ec222012-05-15 13:24:09 +0000533 bool next(SkDrawFilter::Type drawType) {
534 if (fDone) {
535 return false;
536 } else if (fIsSimple) {
537 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000538 return !fPaint->nothingToDraw();
539 } else {
540 return this->doNext(drawType);
541 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000542 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000543
reed@android.com8a1c16f2008-12-17 15:59:43 +0000544private:
reeddbc3cef2015-04-29 12:18:57 -0700545 SkLazyPaint fLazyPaintInit; // base paint storage in case we need to modify it
546 SkLazyPaint fLazyPaintPerLooper; // per-draw-looper storage, so the looper can modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000547 SkCanvas* fCanvas;
548 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000549 SkDrawFilter* fFilter;
550 const SkPaint* fPaint;
551 int fSaveCount;
reed4a8126e2014-09-22 07:29:03 -0700552 uint32_t fNewPaintFlags;
reed5c476fb2015-04-20 08:04:21 -0700553 bool fTempLayerForImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000554 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000555 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000556 SkDrawLooper::Context* fLooperContext;
557 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000558
559 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000560};
561
reed@google.com129ec222012-05-15 13:24:09 +0000562bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
halcanary96fcdcc2015-08-27 07:41:13 -0700563 fPaint = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000564 SkASSERT(!fIsSimple);
reed5c476fb2015-04-20 08:04:21 -0700565 SkASSERT(fLooperContext || fFilter || fTempLayerForImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000566
reeddbc3cef2015-04-29 12:18:57 -0700567 SkPaint* paint = fLazyPaintPerLooper.set(fLazyPaintInit.isValid() ?
568 *fLazyPaintInit.get() : fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700569 paint->setFlags(fNewPaintFlags);
reed@google.com129ec222012-05-15 13:24:09 +0000570
reed5c476fb2015-04-20 08:04:21 -0700571 if (fTempLayerForImageFilter) {
halcanary96fcdcc2015-08-27 07:41:13 -0700572 paint->setImageFilter(nullptr);
573 paint->setXfermode(nullptr);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000574 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000575
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000576 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000577 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000578 return false;
579 }
580 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000581 if (!fFilter->filter(paint, drawType)) {
582 fDone = true;
583 return false;
584 }
halcanary96fcdcc2015-08-27 07:41:13 -0700585 if (nullptr == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000586 // no looper means we only draw once
587 fDone = true;
588 }
589 }
590 fPaint = paint;
591
592 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000593 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000594 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000595 }
596
597 // call this after any possible paint modifiers
598 if (fPaint->nothingToDraw()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700599 fPaint = nullptr;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000600 return false;
601 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000602 return true;
603}
604
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605////////// macros to place around the internal draw calls //////////////////
606
reed262a71b2015-12-05 13:07:27 -0800607#define LOOPER_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
608 this->predrawNotify(); \
609 AutoDrawLooper looper(this, fProps, paint, skipLayerForFilter, bounds); \
610 while (looper.next(SkDrawFilter::kBitmap_Type)) { \
611 SkDrawIter iter(this);
612
613
reed@google.com8926b162012-03-23 15:36:36 +0000614#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000615 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700616 AutoDrawLooper looper(this, fProps, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000617 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000618 SkDrawIter iter(this);
619
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000620#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000621 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700622 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000623 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000624 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000625
reedc83a2972015-07-16 07:40:45 -0700626#define LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, type, bounds, auxOpaque) \
627 this->predrawNotify(bounds, &paint, auxOpaque); \
628 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
629 while (looper.next(type)) { \
630 SkDrawIter iter(this);
631
reed@google.com4e2b3d32011-04-07 14:18:59 +0000632#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000633
634////////////////////////////////////////////////////////////////////////////
635
mtkleinfeaadee2015-04-08 11:25:48 -0700636void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
637 this->restoreToCount(1);
638 fCachedLocalClipBounds.setEmpty();
639 fCachedLocalClipBoundsDirty = true;
640 fClipStack->reset();
641 fMCRec->reset(bounds);
642
643 // We're peering through a lot of structs here. Only at this scope do we
644 // know that the device is an SkBitmapDevice (really an SkNoPixelsBitmapDevice).
645 static_cast<SkBitmapDevice*>(fMCRec->fLayer->fDevice)->setNewSize(bounds.size());
646}
647
reedd9544982014-09-09 18:46:22 -0700648SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
reed42b73eb2015-11-20 13:42:42 -0800649 if (device && device->forceConservativeRasterClip()) {
650 flags = InitFlags(flags | kConservativeRasterClip_InitFlag);
651 }
652 // Since init() is only called once by our constructors, it is safe to perform this
653 // const-cast.
654 *const_cast<bool*>(&fConservativeRasterClip) = SkToBool(flags & kConservativeRasterClip_InitFlag);
655
reed@google.comc0784db2013-12-13 21:16:12 +0000656 fCachedLocalClipBounds.setEmpty();
657 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000658 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000659 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700660 fDeviceCMDirty = true;
reed2ff1fce2014-12-11 07:07:37 -0800661 fSaveCount = 1;
halcanary96fcdcc2015-08-27 07:41:13 -0700662 fMetaData = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000663
halcanary385fe4d2015-08-26 13:07:48 -0700664 fClipStack.reset(new SkClipStack);
reed687fa1c2015-04-07 08:00:56 -0700665
reed@android.com8a1c16f2008-12-17 15:59:43 +0000666 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700667 new (fMCRec) MCRec(fConservativeRasterClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000668
reeda499f902015-05-01 09:34:31 -0700669 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
670 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
reed8c30a812016-04-20 16:36:51 -0700671 new (fDeviceCMStorage) DeviceCM(nullptr, nullptr, nullptr, fConservativeRasterClip, false,
672 fMCRec->fMatrix);
reedb679ca82015-04-07 04:40:48 -0700673
reed@android.com8a1c16f2008-12-17 15:59:43 +0000674 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000675
halcanary96fcdcc2015-08-27 07:41:13 -0700676 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000677
reedf92c8662014-08-18 08:02:43 -0700678 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700679 // The root device and the canvas should always have the same pixel geometry
680 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reedf92c8662014-08-18 08:02:43 -0700681 device->onAttachToCanvas(this);
682 fMCRec->fLayer->fDevice = SkRef(device);
reed78e27682014-11-19 08:04:34 -0800683 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
reedf92c8662014-08-18 08:02:43 -0700684 }
685 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000686}
687
reed@google.comcde92112011-07-06 20:00:52 +0000688SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000689 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700690 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800691 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000692{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000693 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000694
halcanary96fcdcc2015-08-27 07:41:13 -0700695 this->init(nullptr, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000696}
697
reedd9544982014-09-09 18:46:22 -0700698static SkBitmap make_nopixels(int width, int height) {
699 SkBitmap bitmap;
700 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
701 return bitmap;
702}
703
704class SkNoPixelsBitmapDevice : public SkBitmapDevice {
705public:
robertphillipsfcf78292015-06-19 11:49:52 -0700706 SkNoPixelsBitmapDevice(const SkIRect& bounds, const SkSurfaceProps& surfaceProps)
707 : INHERITED(make_nopixels(bounds.width(), bounds.height()), surfaceProps)
reed78e27682014-11-19 08:04:34 -0800708 {
709 this->setOrigin(bounds.x(), bounds.y());
710 }
reedd9544982014-09-09 18:46:22 -0700711
712private:
piotaixrb5fae932014-09-24 13:03:30 -0700713
reedd9544982014-09-09 18:46:22 -0700714 typedef SkBitmapDevice INHERITED;
715};
716
reed96a857e2015-01-25 10:33:58 -0800717SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000718 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800719 , fProps(SkSurfacePropsCopyOrDefault(props))
reed42b73eb2015-11-20 13:42:42 -0800720 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000721{
722 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700723
halcanary385fe4d2015-08-26 13:07:48 -0700724 this->init(new SkNoPixelsBitmapDevice(SkIRect::MakeWH(width, height), fProps),
725 kDefault_InitFlags)->unref();
reedd9544982014-09-09 18:46:22 -0700726}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000727
reed78e27682014-11-19 08:04:34 -0800728SkCanvas::SkCanvas(const SkIRect& bounds, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700729 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700730 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800731 , fConservativeRasterClip(false)
reedd9544982014-09-09 18:46:22 -0700732{
733 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700734
halcanary385fe4d2015-08-26 13:07:48 -0700735 this->init(new SkNoPixelsBitmapDevice(bounds, fProps), flags)->unref();
reedd9544982014-09-09 18:46:22 -0700736}
737
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000738SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000739 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700740 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800741 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000742{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000743 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700744
reedd9544982014-09-09 18:46:22 -0700745 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000746}
747
robertphillipsfcf78292015-06-19 11:49:52 -0700748SkCanvas::SkCanvas(SkBaseDevice* device, InitFlags flags)
749 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700750 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800751 , fConservativeRasterClip(false)
robertphillipsfcf78292015-06-19 11:49:52 -0700752{
753 inc_canvas();
754
755 this->init(device, flags);
756}
757
reed4a8126e2014-09-22 07:29:03 -0700758SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700759 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700760 , fProps(props)
reed42b73eb2015-11-20 13:42:42 -0800761 , fConservativeRasterClip(false)
reed3716fd02014-09-21 09:39:55 -0700762{
763 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700764
halcanary385fe4d2015-08-26 13:07:48 -0700765 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700766 this->init(device, kDefault_InitFlags);
767}
reed29c857d2014-09-21 10:25:07 -0700768
reed4a8126e2014-09-22 07:29:03 -0700769SkCanvas::SkCanvas(const SkBitmap& bitmap)
770 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
771 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800772 , fConservativeRasterClip(false)
reed4a8126e2014-09-22 07:29:03 -0700773{
774 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700775
halcanary385fe4d2015-08-26 13:07:48 -0700776 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700777 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000778}
779
780SkCanvas::~SkCanvas() {
781 // free up the contents of our deque
782 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000783
reed@android.com8a1c16f2008-12-17 15:59:43 +0000784 this->internalRestore(); // restore the last, since we're going away
785
halcanary385fe4d2015-08-26 13:07:48 -0700786 delete fMetaData;
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000787
reed@android.com8a1c16f2008-12-17 15:59:43 +0000788 dec_canvas();
789}
790
fmalita53d9f1c2016-01-25 06:23:54 -0800791#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000792SkDrawFilter* SkCanvas::getDrawFilter() const {
793 return fMCRec->fFilter;
794}
795
796SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
reed51985e32015-04-11 08:04:56 -0700797 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000798 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
799 return filter;
800}
fmalita77650002016-01-21 18:47:11 -0800801#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000802
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000803SkMetaData& SkCanvas::getMetaData() {
804 // metadata users are rare, so we lazily allocate it. If that changes we
805 // can decide to just make it a field in the device (rather than a ptr)
halcanary96fcdcc2015-08-27 07:41:13 -0700806 if (nullptr == fMetaData) {
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000807 fMetaData = new SkMetaData;
808 }
809 return *fMetaData;
810}
811
reed@android.com8a1c16f2008-12-17 15:59:43 +0000812///////////////////////////////////////////////////////////////////////////////
813
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000814void SkCanvas::flush() {
reedea5a6512016-07-07 16:44:27 -0700815 this->onFlush();
816}
817
818void SkCanvas::onFlush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000819 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000820 if (device) {
821 device->flush();
822 }
823}
824
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000825SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000826 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000827 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
828}
829
senorblancoafc7cce2016-02-02 18:44:15 -0800830SkIRect SkCanvas::getTopLayerBounds() const {
831 SkBaseDevice* d = this->getTopDevice();
832 if (!d) {
833 return SkIRect::MakeEmpty();
834 }
835 return SkIRect::MakeXYWH(d->getOrigin().x(), d->getOrigin().y(), d->width(), d->height());
836}
837
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000838SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000839 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000840 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000841 SkASSERT(rec && rec->fLayer);
842 return rec->fLayer->fDevice;
843}
844
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000845SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000846 if (updateMatrixClip) {
847 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
848 }
reed@google.com9266fed2011-03-30 00:18:03 +0000849 return fMCRec->fTopLayer->fDevice;
850}
851
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000852bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
853 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
854 return false;
855 }
856
857 bool weAllocated = false;
halcanary96fcdcc2015-08-27 07:41:13 -0700858 if (nullptr == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700859 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000860 return false;
861 }
862 weAllocated = true;
863 }
864
reedcf01e312015-05-23 19:14:51 -0700865 SkAutoPixmapUnlock unlocker;
866 if (bitmap->requestLock(&unlocker)) {
867 const SkPixmap& pm = unlocker.pixmap();
868 if (this->readPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), x, y)) {
869 return true;
870 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000871 }
872
873 if (weAllocated) {
halcanary96fcdcc2015-08-27 07:41:13 -0700874 bitmap->setPixelRef(nullptr);
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000875 }
876 return false;
877}
reed@google.com51df9e32010-12-23 19:29:18 +0000878
bsalomon@google.comc6980972011-11-02 19:57:21 +0000879bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000880 SkIRect r = srcRect;
881 const SkISize size = this->getBaseLayerSize();
882 if (!r.intersect(0, 0, size.width(), size.height())) {
883 bitmap->reset();
884 return false;
885 }
886
reed84825042014-09-02 12:50:45 -0700887 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000888 // bitmap will already be reset.
889 return false;
890 }
891 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
892 bitmap->reset();
893 return false;
894 }
895 return true;
896}
897
reed96472de2014-12-10 09:53:42 -0800898bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000899 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000900 if (!device) {
901 return false;
902 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000903 const SkISize size = this->getBaseLayerSize();
mtkleinf0f14112014-12-12 08:46:25 -0800904
reed96472de2014-12-10 09:53:42 -0800905 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
906 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000907 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000908 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000909
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000910 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800911 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000912}
913
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000914bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
915 if (bitmap.getTexture()) {
916 return false;
917 }
reedcf01e312015-05-23 19:14:51 -0700918
919 SkAutoPixmapUnlock unlocker;
920 if (bitmap.requestLock(&unlocker)) {
921 const SkPixmap& pm = unlocker.pixmap();
922 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000923 }
924 return false;
925}
926
927bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
928 int x, int y) {
929 switch (origInfo.colorType()) {
930 case kUnknown_SkColorType:
931 case kIndex_8_SkColorType:
932 return false;
933 default:
934 break;
935 }
halcanary96fcdcc2015-08-27 07:41:13 -0700936 if (nullptr == pixels || rowBytes < origInfo.minRowBytes()) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000937 return false;
938 }
939
940 const SkISize size = this->getBaseLayerSize();
941 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
942 if (!target.intersect(0, 0, size.width(), size.height())) {
943 return false;
944 }
945
946 SkBaseDevice* device = this->getDevice();
947 if (!device) {
948 return false;
949 }
950
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000951 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700952 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000953
954 // if x or y are negative, then we have to adjust pixels
955 if (x > 0) {
956 x = 0;
957 }
958 if (y > 0) {
959 y = 0;
960 }
961 // here x,y are either 0 or negative
962 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
963
reed4af35f32014-06-27 17:47:49 -0700964 // Tell our owning surface to bump its generation ID
reedc83a2972015-07-16 07:40:45 -0700965 const bool completeOverwrite = info.dimensions() == size;
966 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700967
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000968 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000969 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000970}
reed@google.com51df9e32010-12-23 19:29:18 +0000971
junov@google.com4370aed2012-01-18 16:21:08 +0000972SkCanvas* SkCanvas::canvasForDrawIter() {
973 return this;
974}
975
reed@android.com8a1c16f2008-12-17 15:59:43 +0000976//////////////////////////////////////////////////////////////////////////////
977
reed@android.com8a1c16f2008-12-17 15:59:43 +0000978void SkCanvas::updateDeviceCMCache() {
979 if (fDeviceCMDirty) {
980 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700981 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000982 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000983
halcanary96fcdcc2015-08-27 07:41:13 -0700984 if (nullptr == layer->fNext) { // only one layer
985 layer->updateMC(totalMatrix, totalClip, *fClipStack, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000986 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000987 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000988 do {
reed687fa1c2015-04-07 08:00:56 -0700989 layer->updateMC(totalMatrix, clip, *fClipStack, &clip);
halcanary96fcdcc2015-08-27 07:41:13 -0700990 } while ((layer = layer->fNext) != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000991 }
992 fDeviceCMDirty = false;
993 }
994}
995
reed@android.com8a1c16f2008-12-17 15:59:43 +0000996///////////////////////////////////////////////////////////////////////////////
997
reed2ff1fce2014-12-11 07:07:37 -0800998void SkCanvas::checkForDeferredSave() {
999 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -08001000 this->doSave();
1001 }
1002}
1003
reedf0090cb2014-11-26 08:55:51 -08001004int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -08001005#ifdef SK_DEBUG
1006 int count = 0;
1007 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
1008 for (;;) {
1009 const MCRec* rec = (const MCRec*)iter.next();
1010 if (!rec) {
1011 break;
1012 }
1013 count += 1 + rec->fDeferredSaveCount;
1014 }
1015 SkASSERT(count == fSaveCount);
1016#endif
1017 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -08001018}
1019
1020int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -08001021 fSaveCount += 1;
1022 fMCRec->fDeferredSaveCount += 1;
1023 return this->getSaveCount() - 1; // return our prev value
1024}
1025
1026void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -08001027 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -07001028
1029 SkASSERT(fMCRec->fDeferredSaveCount > 0);
1030 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001031 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -08001032}
1033
1034void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -08001035 if (fMCRec->fDeferredSaveCount > 0) {
1036 SkASSERT(fSaveCount > 1);
1037 fSaveCount -= 1;
1038 fMCRec->fDeferredSaveCount -= 1;
1039 } else {
1040 // check for underflow
1041 if (fMCStack.count() > 1) {
1042 this->willRestore();
1043 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -07001044 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001045 this->internalRestore();
1046 this->didRestore();
1047 }
reedf0090cb2014-11-26 08:55:51 -08001048 }
1049}
1050
1051void SkCanvas::restoreToCount(int count) {
1052 // sanity check
1053 if (count < 1) {
1054 count = 1;
1055 }
mtkleinf0f14112014-12-12 08:46:25 -08001056
reedf0090cb2014-11-26 08:55:51 -08001057 int n = this->getSaveCount() - count;
1058 for (int i = 0; i < n; ++i) {
1059 this->restore();
1060 }
1061}
1062
reed2ff1fce2014-12-11 07:07:37 -08001063void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001064 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -07001065 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +00001066 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +00001067
reed687fa1c2015-04-07 08:00:56 -07001068 fClipStack->save();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001069}
1070
reed4960eee2015-12-18 07:09:18 -08001071bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
reed@google.comb93ba452014-03-10 19:47:58 +00001072#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001073 return !(saveLayerFlags & SkCanvas::kDontClipToLayer_PrivateSaveLayerFlag);
reed@google.comb93ba452014-03-10 19:47:58 +00001074#else
1075 return true;
1076#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001077}
1078
reed4960eee2015-12-18 07:09:18 -08001079bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -07001080 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001081 SkIRect clipBounds;
1082 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001083 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +00001084 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001085
reed96e657d2015-03-10 17:30:07 -07001086 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
1087
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001088 if (imageFilter) {
senorblancoe5e79842016-03-21 14:51:59 -07001089 clipBounds = imageFilter->filterBounds(clipBounds, ctm);
senorblancodb64af32015-12-09 10:11:43 -08001090 if (bounds && !imageFilter->canComputeFastBounds()) {
1091 bounds = nullptr;
1092 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001093 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001094 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -07001095 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001096 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +00001097
reed96e657d2015-03-10 17:30:07 -07001098 ctm.mapRect(&r, *bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001099 r.roundOut(&ir);
1100 // early exit if the layer's bounds are clipped out
1101 if (!ir.intersect(clipBounds)) {
reed4960eee2015-12-18 07:09:18 -08001102 if (BoundsAffectsClip(saveLayerFlags)) {
reed9b3aa542015-03-11 08:47:12 -07001103 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001104 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001105 }
junov@chromium.orga907ac32012-02-24 21:54:07 +00001106 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001107 }
1108 } else { // no user bounds, so just use the clip
1109 ir = clipBounds;
1110 }
reed180aec42015-03-11 10:39:04 -07001111 SkASSERT(!ir.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001112
reed4960eee2015-12-18 07:09:18 -08001113 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -07001114 // Simplify the current clips since they will be applied properly during restore()
reed9b3aa542015-03-11 08:47:12 -07001115 fCachedLocalClipBoundsDirty = true;
reed687fa1c2015-04-07 08:00:56 -07001116 fClipStack->clipDevRect(ir, SkRegion::kReplace_Op);
reed180aec42015-03-11 10:39:04 -07001117 fMCRec->fRasterClip.setRect(ir);
junov@chromium.orga907ac32012-02-24 21:54:07 +00001118 }
1119
1120 if (intersection) {
1121 *intersection = ir;
1122 }
1123 return true;
1124}
1125
reed4960eee2015-12-18 07:09:18 -08001126
reed4960eee2015-12-18 07:09:18 -08001127int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
1128 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001129}
1130
reed70ee31b2015-12-10 13:44:45 -08001131int SkCanvas::saveLayerPreserveLCDTextRequests(const SkRect* bounds, const SkPaint* paint) {
reed4960eee2015-12-18 07:09:18 -08001132 return this->saveLayer(SaveLayerRec(bounds, paint, kPreserveLCDText_SaveLayerFlag));
1133}
1134
1135int SkCanvas::saveLayer(const SaveLayerRec& origRec) {
1136 SaveLayerRec rec(origRec);
1137 if (gIgnoreSaveLayerBounds) {
1138 rec.fBounds = nullptr;
1139 }
1140 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
1141 fSaveCount += 1;
1142 this->internalSaveLayer(rec, strategy);
1143 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -08001144}
1145
reedbfd5f172016-01-07 11:28:08 -08001146static void draw_filter_into_device(SkBaseDevice* src, const SkImageFilter* filter,
1147 SkBaseDevice* dst, const SkMatrix& ctm) {
robertphillips7354a4b2015-12-16 05:08:27 -08001148
1149 SkBitmap srcBM;
1150
1151#if SK_SUPPORT_GPU
robertphillips175dd9b2016-04-28 14:32:04 -07001152 // TODO: remove this virtual usage of accessRenderTarget! It is preventing
1153 // removal of the virtual on SkBaseDevice.
robertphillips7354a4b2015-12-16 05:08:27 -08001154 GrRenderTarget* srcRT = src->accessRenderTarget();
1155 if (srcRT && !srcRT->asTexture() && dst->accessRenderTarget()) {
1156 // When both the src & the dst are on the gpu but the src doesn't have a texture,
1157 // we create a temporary texture for the draw.
1158 // TODO: we should actually only copy the portion of the source needed to apply the image
1159 // filter
1160 GrContext* context = srcRT->getContext();
bsalomon5ec26ae2016-02-25 08:33:02 -08001161 SkAutoTUnref<GrTexture> tex(context->textureProvider()->createTexture(srcRT->desc(),
1162 SkBudgeted::kYes));
robertphillips7354a4b2015-12-16 05:08:27 -08001163
1164 context->copySurface(tex, srcRT);
1165
1166 GrWrapTextureInBitmap(tex, src->width(), src->height(), src->isOpaque(), &srcBM);
1167 } else
1168#endif
1169 {
1170 srcBM = src->accessBitmap(false);
1171 }
1172
1173 SkCanvas c(dst);
1174
1175 SkPaint p;
robertphillips372177e2016-03-30 07:32:28 -07001176 p.setImageFilter(filter->makeWithLocalMatrix(ctm));
reedbfd5f172016-01-07 11:28:08 -08001177 const SkScalar x = SkIntToScalar(src->getOrigin().x());
1178 const SkScalar y = SkIntToScalar(src->getOrigin().y());
1179 c.drawBitmap(srcBM, x, y, &p);
robertphillips7354a4b2015-12-16 05:08:27 -08001180}
reed70ee31b2015-12-10 13:44:45 -08001181
reed129ed1c2016-02-22 06:42:31 -08001182static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, bool isOpaque,
1183 const SkPaint* paint) {
1184 // need to force L32 for now if we have an image filter. Once filters support other colortypes
1185 // e.g. sRGB or F16, we can remove this check
brianosman52ede1d2016-06-20 08:25:02 -07001186 // SRGBTODO: Can we remove this check now?
reed129ed1c2016-02-22 06:42:31 -08001187 const bool hasImageFilter = paint && paint->getImageFilter();
1188
1189 SkAlphaType alphaType = isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
1190 if ((prev.bytesPerPixel() < 4) || hasImageFilter) {
1191 // force to L32
1192 return SkImageInfo::MakeN32(w, h, alphaType);
1193 } else {
1194 // keep the same characteristics as the prev
brianosman52ede1d2016-06-20 08:25:02 -07001195 return SkImageInfo::Make(w, h, prev.colorType(), alphaType, sk_ref_sp(prev.colorSpace()));
reed129ed1c2016-02-22 06:42:31 -08001196 }
1197}
1198
reed4960eee2015-12-18 07:09:18 -08001199void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
1200 const SkRect* bounds = rec.fBounds;
1201 const SkPaint* paint = rec.fPaint;
1202 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1203
reed@google.comb93ba452014-03-10 19:47:58 +00001204#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001205 saveLayerFlags &= ~kDontClipToLayer_PrivateSaveLayerFlag;
reed@google.comb93ba452014-03-10 19:47:58 +00001206#endif
1207
reed8c30a812016-04-20 16:36:51 -07001208 SkLazyPaint lazyP;
1209 SkImageFilter* imageFilter = paint ? paint->getImageFilter() : NULL;
1210 SkMatrix stashedMatrix = fMCRec->fMatrix;
reed8c30a812016-04-20 16:36:51 -07001211 SkMatrix remainder;
1212 SkSize scale;
1213 /*
1214 * ImageFilters (so far) do not correctly handle matrices (CTM) that contain rotation/skew/etc.
1215 * but they do handle scaling. To accommodate this, we do the following:
1216 *
1217 * 1. Stash off the current CTM
1218 * 2. Decompose the CTM into SCALE and REMAINDER
1219 * 3. Wack the CTM to be just SCALE, and wrap the imagefilter with a MatrixImageFilter that
1220 * contains the REMAINDER
1221 * 4. Proceed as usual, allowing the client to draw into the layer (now with a scale-only CTM)
1222 * 5. During restore, we process the MatrixImageFilter, which applies REMAINDER to the output
1223 * of the original imagefilter, and draw that (via drawSprite)
1224 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1225 *
1226 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1227 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1228 */
reed96a04f32016-04-25 09:25:15 -07001229 if (imageFilter && !stashedMatrix.isScaleTranslate() && !imageFilter->canHandleComplexCTM() &&
reed8c30a812016-04-20 16:36:51 -07001230 stashedMatrix.decomposeScale(&scale, &remainder))
1231 {
1232 // We will restore the matrix (which we are overwriting here) in restore via fStashedMatrix
1233 this->internalSetMatrix(SkMatrix::MakeScale(scale.width(), scale.height()));
1234 SkPaint* p = lazyP.set(*paint);
1235 p->setImageFilter(SkImageFilter::MakeMatrixFilter(remainder,
1236 SkFilterQuality::kLow_SkFilterQuality,
1237 sk_ref_sp(imageFilter)));
1238 imageFilter = p->getImageFilter();
1239 paint = p;
1240 }
reed8c30a812016-04-20 16:36:51 -07001241
junov@chromium.orga907ac32012-02-24 21:54:07 +00001242 // do this before we create the layer. We don't call the public save() since
1243 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001244 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001245
1246 fDeviceCMDirty = true;
1247
1248 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001249 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
reed2ff1fce2014-12-11 07:07:37 -08001250 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001251 }
1252
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001253 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1254 // the clipRectBounds() call above?
1255 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001256 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001257 }
1258
reed4960eee2015-12-18 07:09:18 -08001259 bool isOpaque = SkToBool(saveLayerFlags & kIsOpaque_SaveLayerFlag);
reed8dc0ccb2015-03-20 06:32:52 -07001260 SkPixelGeometry geo = fProps.pixelGeometry();
1261 if (paint) {
reed76033be2015-03-14 10:54:31 -07001262 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001263 if (paint->getImageFilter() || paint->getColorFilter()) {
reed76033be2015-03-14 10:54:31 -07001264 isOpaque = false;
reed8dc0ccb2015-03-20 06:32:52 -07001265 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001266 }
1267 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001268
reedb2db8982014-11-13 12:41:02 -08001269 SkBaseDevice* device = this->getTopDevice();
halcanary96fcdcc2015-08-27 07:41:13 -07001270 if (nullptr == device) {
reedb2db8982014-11-13 12:41:02 -08001271 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001272 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001273 }
reedb2db8982014-11-13 12:41:02 -08001274
reed129ed1c2016-02-22 06:42:31 -08001275 SkImageInfo info = make_layer_info(device->imageInfo(), ir.width(), ir.height(), isOpaque,
1276 paint);
1277
reed61f501f2015-04-29 08:34:00 -07001278 bool forceSpriteOnRestore = false;
1279 {
reed70ee31b2015-12-10 13:44:45 -08001280 const bool preserveLCDText = kOpaque_SkAlphaType == info.alphaType() ||
reed4960eee2015-12-18 07:09:18 -08001281 (saveLayerFlags & kPreserveLCDText_SaveLayerFlag);
reeddaa57bf2015-05-15 10:39:17 -07001282 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
reed70ee31b2015-12-10 13:44:45 -08001283 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
1284 preserveLCDText, false);
reed61f501f2015-04-29 08:34:00 -07001285 SkBaseDevice* newDev = device->onCreateDevice(createInfo, paint);
halcanary96fcdcc2015-08-27 07:41:13 -07001286 if (nullptr == newDev) {
reed61f501f2015-04-29 08:34:00 -07001287 // If onCreateDevice didn't succeed, try raster (e.g. PDF couldn't handle the paint)
robertphillips9a53fd72015-06-22 09:46:59 -07001288 const SkSurfaceProps surfaceProps(fProps.flags(), createInfo.fPixelGeometry);
1289 newDev = SkBitmapDevice::Create(createInfo.fInfo, surfaceProps);
halcanary96fcdcc2015-08-27 07:41:13 -07001290 if (nullptr == newDev) {
reed61f501f2015-04-29 08:34:00 -07001291 SkErrorInternals::SetError(kInternalError_SkError,
1292 "Unable to create device for layer.");
1293 return;
1294 }
1295 forceSpriteOnRestore = true;
1296 }
1297 device = newDev;
bungeman@google.come25c6842011-08-17 14:53:54 +00001298 }
reed@google.com6f8f2922011-03-04 22:27:10 +00001299 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips7354a4b2015-12-16 05:08:27 -08001300
reedbfd5f172016-01-07 11:28:08 -08001301 if (rec.fBackdrop) {
1302 draw_filter_into_device(fMCRec->fTopLayer->fDevice, rec.fBackdrop, device, fMCRec->fMatrix);
robertphillips7354a4b2015-12-16 05:08:27 -08001303 }
1304
reed8c30a812016-04-20 16:36:51 -07001305 DeviceCM* layer = new DeviceCM(device, paint, this, fConservativeRasterClip,
1306 forceSpriteOnRestore, stashedMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001307 device->unref();
1308
1309 layer->fNext = fMCRec->fTopLayer;
1310 fMCRec->fLayer = layer;
1311 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reed@android.com8a1c16f2008-12-17 15:59:43 +00001312}
1313
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001314int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001315 if (0xFF == alpha) {
1316 return this->saveLayer(bounds, nullptr);
1317 } else {
1318 SkPaint tmpPaint;
1319 tmpPaint.setAlpha(alpha);
1320 return this->saveLayer(bounds, &tmpPaint);
1321 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001322}
1323
reed@android.com8a1c16f2008-12-17 15:59:43 +00001324void SkCanvas::internalRestore() {
1325 SkASSERT(fMCStack.count() != 0);
1326
1327 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001328 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001329
reed687fa1c2015-04-07 08:00:56 -07001330 fClipStack->restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001331
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001332 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001333 DeviceCM* layer = fMCRec->fLayer; // may be null
1334 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001335 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001336
1337 // now do the normal restore()
1338 fMCRec->~MCRec(); // balanced in save()
1339 fMCStack.pop_back();
1340 fMCRec = (MCRec*)fMCStack.back();
1341
1342 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1343 since if we're being recorded, we don't want to record this (the
1344 recorder will have already recorded the restore).
1345 */
bsalomon49f085d2014-09-05 13:34:00 -07001346 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001347 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001348 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001349 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
reed61f501f2015-04-29 08:34:00 -07001350 layer->fPaint, layer->fDeviceIsBitmapDevice);
reed8c30a812016-04-20 16:36:51 -07001351 // restore what we smashed in internalSaveLayer
1352 fMCRec->fMatrix = layer->fStashedMatrix;
reed@google.com8926b162012-03-23 15:36:36 +00001353 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001354 fDeviceCMDirty = true;
halcanary385fe4d2015-08-26 13:07:48 -07001355 delete layer;
reedb679ca82015-04-07 04:40:48 -07001356 } else {
1357 // we're at the root
reeda499f902015-05-01 09:34:31 -07001358 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001359 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001360 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001361 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001362 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001363}
1364
reede8f30622016-03-23 18:59:25 -07001365sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001366 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001367 props = &fProps;
1368 }
1369 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001370}
1371
reede8f30622016-03-23 18:59:25 -07001372sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001373 SkBaseDevice* dev = this->getDevice();
reede8f30622016-03-23 18:59:25 -07001374 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001375}
1376
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001377SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001378 return this->onImageInfo();
1379}
1380
1381SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001382 SkBaseDevice* dev = this->getDevice();
1383 if (dev) {
1384 return dev->imageInfo();
1385 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001386 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001387 }
1388}
1389
brianosman898235c2016-04-06 07:38:23 -07001390bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001391 return this->onGetProps(props);
1392}
1393
1394bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001395 SkBaseDevice* dev = this->getDevice();
1396 if (dev) {
1397 if (props) {
1398 *props = fProps;
1399 }
1400 return true;
1401 } else {
1402 return false;
1403 }
1404}
1405
reed6ceeebd2016-03-09 14:26:26 -08001406#ifdef SK_SUPPORT_LEGACY_PEEKPIXELS_PARMS
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001407const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
reed884e97c2015-05-26 11:31:54 -07001408 SkPixmap pmap;
reed6ceeebd2016-03-09 14:26:26 -08001409 if (this->peekPixels(&pmap)) {
1410 if (info) {
1411 *info = pmap.info();
1412 }
1413 if (rowBytes) {
1414 *rowBytes = pmap.rowBytes();
1415 }
1416 return pmap.addr();
reed884e97c2015-05-26 11:31:54 -07001417 }
reed6ceeebd2016-03-09 14:26:26 -08001418 return nullptr;
1419}
1420#endif
1421
1422bool SkCanvas::peekPixels(SkPixmap* pmap) {
1423 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001424}
1425
reed884e97c2015-05-26 11:31:54 -07001426bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001427 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001428 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001429}
1430
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001431void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001432 SkPixmap pmap;
1433 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001434 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001435 }
1436 if (info) {
1437 *info = pmap.info();
1438 }
1439 if (rowBytes) {
1440 *rowBytes = pmap.rowBytes();
1441 }
1442 if (origin) {
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001443 *origin = this->getTopDevice(false)->getOrigin();
1444 }
reed884e97c2015-05-26 11:31:54 -07001445 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001446}
1447
reed884e97c2015-05-26 11:31:54 -07001448bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001449 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001450 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001451}
1452
reed@android.com8a1c16f2008-12-17 15:59:43 +00001453/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001454
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001455void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed61f501f2015-04-29 08:34:00 -07001456 const SkPaint* paint, bool deviceIsBitmapDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001457 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001458 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001459 paint = &tmp;
1460 }
reed@google.com4b226022011-01-11 18:32:13 +00001461
reed@google.com8926b162012-03-23 15:36:36 +00001462 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001463 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001464 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001465 paint = &looper.paint();
1466 SkImageFilter* filter = paint->getImageFilter();
1467 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
senorblancof35566e2016-04-18 10:32:02 -07001468 if (filter) {
robertphillips4418dba2016-03-07 12:45:14 -08001469 const SkBitmap& srcBM = srcDev->accessBitmap(false);
reed1eca1162016-04-25 12:29:38 -07001470 dstDev->drawSpriteWithFilter(iter, srcBM, pos.x(), pos.y(), *paint);
reed61f501f2015-04-29 08:34:00 -07001471 } else if (deviceIsBitmapDevice) {
reed9572a102015-05-26 19:22:17 -07001472 const SkBitmap& src = static_cast<SkBitmapDevice*>(srcDev)->fBitmap;
reed61f501f2015-04-29 08:34:00 -07001473 dstDev->drawSprite(iter, src, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001474 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001475 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001476 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001477 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001478 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001479}
1480
reed32704672015-12-16 08:27:10 -08001481/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001482
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001483void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001484 SkMatrix m;
1485 m.setTranslate(dx, dy);
1486 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001487}
1488
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001489void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001490 SkMatrix m;
1491 m.setScale(sx, sy);
1492 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001493}
1494
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001495void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001496 SkMatrix m;
1497 m.setRotate(degrees);
1498 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001499}
1500
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001501void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001502 SkMatrix m;
1503 m.setSkew(sx, sy);
1504 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001505}
1506
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001507void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001508 if (matrix.isIdentity()) {
1509 return;
1510 }
1511
reed2ff1fce2014-12-11 07:07:37 -08001512 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001513 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001514 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001515 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001516
1517 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001518}
1519
reed8c30a812016-04-20 16:36:51 -07001520void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
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 = matrix;
reed8c30a812016-04-20 16:36:51 -07001524}
1525
1526void SkCanvas::setMatrix(const SkMatrix& matrix) {
1527 this->checkForDeferredSave();
1528 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001529 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001530}
1531
reed@android.com8a1c16f2008-12-17 15:59:43 +00001532void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001533 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001534}
1535
1536//////////////////////////////////////////////////////////////////////////////
1537
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001538void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed2d1afab2016-06-29 14:33:11 -07001539 if (!fAllowSoftClip) {
1540 doAA = false;
1541 }
1542
1543#ifdef SK_SUPPORT_PRECHECK_CLIPRECT
1544 // Check if we can quick-accept the clip call (and do nothing)
1545 //
reed74467162016-06-30 08:15:35 -07001546 if (SkRegion::kIntersect_Op == op && !doAA && fMCRec->fMatrix.isScaleTranslate()) {
reed2d1afab2016-06-29 14:33:11 -07001547 SkRect devR;
reed74467162016-06-30 08:15:35 -07001548 fMCRec->fMatrix.mapRectScaleTranslate(&devR, rect);
reed2d1afab2016-06-29 14:33:11 -07001549 // NOTE: this check is CTM specific, since we might round differently with a different
1550 // CTM. Thus this is only 100% reliable if there is not global CTM scale to be
1551 // applied later (i.e. if this is going into a picture).
1552 if (devR.round().contains(fMCRec->fRasterClip.getBounds())) {
1553#if 0
1554 SkDebugf("ignored clipRect [%g %g %g %g]\n",
1555 rect.left(), rect.top(), rect.right(), rect.bottom());
1556#endif
1557 return;
1558 }
1559 }
1560#endif
1561
reed2ff1fce2014-12-11 07:07:37 -08001562 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001563 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1564 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001565}
1566
1567void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001568#ifdef SK_ENABLE_CLIP_QUICKREJECT
1569 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001570 if (fMCRec->fRasterClip.isEmpty()) {
reed2d1afab2016-06-29 14:33:11 -07001571 return;
reed@google.comda17f752012-08-16 18:27:05 +00001572 }
1573
reed@google.com3b3e8952012-08-16 20:53:31 +00001574 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001575 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001576 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001577
reed687fa1c2015-04-07 08:00:56 -07001578 fClipStack->clipEmpty();
reed2d1afab2016-06-29 14:33:11 -07001579 (void)fMCRec->fRasterClip.setEmpty();
1580 return;
reed@google.comda17f752012-08-16 18:27:05 +00001581 }
1582 }
1583#endif
1584
reed74467162016-06-30 08:15:35 -07001585 const bool isScaleTrans = fMCRec->fMatrix.isScaleTranslate();
reedc64eff52015-11-21 12:39:45 -08001586 SkRect devR;
reed74467162016-06-30 08:15:35 -07001587 if (isScaleTrans) {
1588 fMCRec->fMatrix.mapRectScaleTranslate(&devR, rect);
reedc64eff52015-11-21 12:39:45 -08001589 }
bsalomonac8cabd2015-11-20 18:53:07 -08001590
reed2d1afab2016-06-29 14:33:11 -07001591#ifndef SK_SUPPORT_PRECHECK_CLIPRECT
reedc64eff52015-11-21 12:39:45 -08001592 if (SkRegion::kIntersect_Op == op &&
1593 kHard_ClipEdgeStyle == edgeStyle
reed74467162016-06-30 08:15:35 -07001594 && isScaleTrans)
reedc64eff52015-11-21 12:39:45 -08001595 {
1596 if (devR.round().contains(fMCRec->fRasterClip.getBounds())) {
1597#if 0
1598 SkDebugf("------- ignored clipRect [%g %g %g %g]\n",
1599 rect.left(), rect.top(), rect.right(), rect.bottom());
1600#endif
1601 return;
1602 }
1603 }
reed2d1afab2016-06-29 14:33:11 -07001604#endif
reedc64eff52015-11-21 12:39:45 -08001605
1606 AutoValidateClip avc(this);
1607
1608 fDeviceCMDirty = true;
1609 fCachedLocalClipBoundsDirty = true;
1610
reed74467162016-06-30 08:15:35 -07001611 if (isScaleTrans) {
reedc64eff52015-11-21 12:39:45 -08001612 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1613 fClipStack->clipDevRect(devR, op, isAA);
senorblancoafc7cce2016-02-02 18:44:15 -08001614 fMCRec->fRasterClip.op(devR, this->getTopLayerBounds(), op, isAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001615 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001616 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001617 // and clip against that, since it can handle any matrix. However, to
1618 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1619 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001620 SkPath path;
1621
1622 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001623 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001624 }
1625}
1626
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001627void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001628 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001629 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001630 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001631 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1632 } else {
1633 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001634 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001635}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001636
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001637void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001638 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001639 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001640 AutoValidateClip avc(this);
1641
1642 fDeviceCMDirty = true;
1643 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001644 if (!fAllowSoftClip) {
1645 edgeStyle = kHard_ClipEdgeStyle;
1646 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001647
reed687fa1c2015-04-07 08:00:56 -07001648 fClipStack->clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001649
senorblancoafc7cce2016-02-02 18:44:15 -08001650 fMCRec->fRasterClip.op(transformedRRect, this->getTopLayerBounds(), op,
robertphillips125f19a2015-11-23 09:00:05 -08001651 kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001652 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001653 }
1654
1655 SkPath path;
1656 path.addRRect(rrect);
1657 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001658 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001659}
1660
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001661void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001662 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001663 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001664
1665 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1666 SkRect r;
1667 if (path.isRect(&r)) {
1668 this->onClipRect(r, op, edgeStyle);
1669 return;
1670 }
1671 SkRRect rrect;
1672 if (path.isOval(&r)) {
1673 rrect.setOval(r);
1674 this->onClipRRect(rrect, op, edgeStyle);
1675 return;
1676 }
1677 if (path.isRRect(&rrect)) {
1678 this->onClipRRect(rrect, op, edgeStyle);
1679 return;
1680 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001681 }
robertphillips39f05382015-11-24 09:30:12 -08001682
1683 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001684}
1685
1686void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001687#ifdef SK_ENABLE_CLIP_QUICKREJECT
1688 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001689 if (fMCRec->fRasterClip.isEmpty()) {
reed2d1afab2016-06-29 14:33:11 -07001690 return;
reed@google.comda17f752012-08-16 18:27:05 +00001691 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001692
reed@google.com3b3e8952012-08-16 20:53:31 +00001693 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001694 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001695 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001696
reed687fa1c2015-04-07 08:00:56 -07001697 fClipStack->clipEmpty();
reed2d1afab2016-06-29 14:33:11 -07001698 (void)fMCRec->fRasterClip.setEmpty();
1699 return;
reed@google.comda17f752012-08-16 18:27:05 +00001700 }
1701 }
1702#endif
1703
reed@google.com5c3d1472011-02-22 19:12:23 +00001704 AutoValidateClip avc(this);
1705
reed@android.com8a1c16f2008-12-17 15:59:43 +00001706 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001707 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001708 if (!fAllowSoftClip) {
1709 edgeStyle = kHard_ClipEdgeStyle;
1710 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001711
1712 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001713 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001714
reed@google.comfe701122011-11-08 19:41:23 +00001715 // Check if the transfomation, or the original path itself
1716 // made us empty. Note this can also happen if we contained NaN
1717 // values. computing the bounds detects this, and will set our
1718 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1719 if (devPath.getBounds().isEmpty()) {
1720 // resetting the path will remove any NaN or other wanky values
1721 // that might upset our scan converter.
1722 devPath.reset();
1723 }
1724
reed@google.com5c3d1472011-02-22 19:12:23 +00001725 // if we called path.swap() we could avoid a deep copy of this path
reed687fa1c2015-04-07 08:00:56 -07001726 fClipStack->clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001727
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001728 if (fAllowSimplifyClip) {
fmalita1a481fe2015-02-04 07:39:34 -08001729 bool clipIsAA = getClipStack()->asPath(&devPath);
1730 if (clipIsAA) {
1731 edgeStyle = kSoft_ClipEdgeStyle;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001732 }
fmalita1a481fe2015-02-04 07:39:34 -08001733
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001734 op = SkRegion::kReplace_Op;
1735 }
1736
senorblancoafc7cce2016-02-02 18:44:15 -08001737 fMCRec->fRasterClip.op(devPath, this->getTopLayerBounds(), op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001738}
1739
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001740void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed2ff1fce2014-12-11 07:07:37 -08001741 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001742 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001743}
1744
1745void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001746 AutoValidateClip avc(this);
1747
reed@android.com8a1c16f2008-12-17 15:59:43 +00001748 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001749 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001750
reed@google.com5c3d1472011-02-22 19:12:23 +00001751 // todo: signal fClipStack that we have a region, and therefore (I guess)
1752 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001753 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001754
reed1f836ee2014-07-07 07:49:34 -07001755 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001756}
1757
reed@google.com819c9212011-02-23 18:56:55 +00001758#ifdef SK_DEBUG
1759void SkCanvas::validateClip() const {
1760 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001761 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001762 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001763 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001764 return;
1765 }
1766
reed@google.com819c9212011-02-23 18:56:55 +00001767 SkIRect ir;
1768 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001769 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001770
reed687fa1c2015-04-07 08:00:56 -07001771 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001772 const SkClipStack::Element* element;
halcanary96fcdcc2015-08-27 07:41:13 -07001773 while ((element = iter.next()) != nullptr) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001774 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001775 case SkClipStack::Element::kRect_Type:
1776 element->getRect().round(&ir);
1777 tmpClip.op(ir, element->getOp());
1778 break;
1779 case SkClipStack::Element::kEmpty_Type:
1780 tmpClip.setEmpty();
1781 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001782 default: {
1783 SkPath path;
1784 element->asPath(&path);
senorblancoafc7cce2016-02-02 18:44:15 -08001785 tmpClip.op(path, this->getTopLayerBounds(), element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001786 break;
1787 }
reed@google.com819c9212011-02-23 18:56:55 +00001788 }
1789 }
reed@google.com819c9212011-02-23 18:56:55 +00001790}
1791#endif
1792
reed@google.com90c07ea2012-04-13 13:50:27 +00001793void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001794 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001795 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001796
halcanary96fcdcc2015-08-27 07:41:13 -07001797 while ((element = iter.next()) != nullptr) {
fmalitac3b589a2014-06-05 12:40:07 -07001798 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001799 }
1800}
1801
reed@google.com5c3d1472011-02-22 19:12:23 +00001802///////////////////////////////////////////////////////////////////////////////
1803
reed@google.com754de5f2014-02-24 19:38:20 +00001804bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001805 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001806}
1807
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001808bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001809 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001810}
1811
reed@google.com3b3e8952012-08-16 20:53:31 +00001812bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001813 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001814 return true;
1815
reed1f836ee2014-07-07 07:49:34 -07001816 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001817 return true;
1818 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001819
reed1f836ee2014-07-07 07:49:34 -07001820 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001821 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001822 fMCRec->fMatrix.mapRect(&dst, rect);
reedb07a94f2014-11-19 05:03:18 -08001823 return !SkIRect::Intersects(dst.roundOut(), fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001824 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001825 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001826
reed@android.coma380ae42009-07-21 01:17:02 +00001827 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001828 // TODO: should we use | instead, or compare all 4 at once?
1829 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001830 return true;
1831 }
reed@google.comc0784db2013-12-13 21:16:12 +00001832 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001833 return true;
1834 }
1835 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001836 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001837}
1838
reed@google.com3b3e8952012-08-16 20:53:31 +00001839bool SkCanvas::quickReject(const SkPath& path) const {
1840 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001841}
1842
reed@google.com3b3e8952012-08-16 20:53:31 +00001843bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001844 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001845 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001846 return false;
1847 }
1848
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001849 SkMatrix inverse;
1850 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001851 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001852 if (bounds) {
1853 bounds->setEmpty();
1854 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001855 return false;
1856 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001857
bsalomon49f085d2014-09-05 13:34:00 -07001858 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001859 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001860 // adjust it outwards in case we are antialiasing
1861 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001862
reed@google.com8f4d2302013-12-17 16:44:46 +00001863 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1864 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001865 inverse.mapRect(bounds, r);
1866 }
1867 return true;
1868}
1869
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001870bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001871 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001872 if (clip.isEmpty()) {
1873 if (bounds) {
1874 bounds->setEmpty();
1875 }
1876 return false;
1877 }
1878
bsalomon49f085d2014-09-05 13:34:00 -07001879 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001880 *bounds = clip.getBounds();
1881 }
1882 return true;
1883}
1884
reed@android.com8a1c16f2008-12-17 15:59:43 +00001885const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001886 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001887}
1888
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001889const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001890 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001891}
1892
robertphillips175dd9b2016-04-28 14:32:04 -07001893GrDrawContext* SkCanvas::internal_private_accessTopLayerDrawContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001894 SkBaseDevice* dev = this->getTopDevice();
robertphillips175dd9b2016-04-28 14:32:04 -07001895 return dev ? dev->accessDrawContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001896}
1897
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001898GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001899 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001900 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001901}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001902
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001903void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1904 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001905 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001906 if (outer.isEmpty()) {
1907 return;
1908 }
1909 if (inner.isEmpty()) {
1910 this->drawRRect(outer, paint);
1911 return;
1912 }
1913
1914 // We don't have this method (yet), but technically this is what we should
1915 // be able to assert...
1916 // SkASSERT(outer.contains(inner));
1917 //
1918 // For now at least check for containment of bounds
1919 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1920
1921 this->onDrawDRRect(outer, inner, paint);
1922}
1923
reed41af9662015-01-05 07:49:08 -08001924// These need to stop being virtual -- clients need to override the onDraw... versions
1925
1926void SkCanvas::drawPaint(const SkPaint& paint) {
1927 this->onDrawPaint(paint);
1928}
1929
1930void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1931 this->onDrawRect(r, paint);
1932}
1933
1934void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1935 this->onDrawOval(r, paint);
1936}
1937
1938void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1939 this->onDrawRRect(rrect, paint);
1940}
1941
1942void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1943 this->onDrawPoints(mode, count, pts, paint);
1944}
1945
1946void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
1947 const SkPoint texs[], const SkColor colors[], SkXfermode* xmode,
1948 const uint16_t indices[], int indexCount, const SkPaint& paint) {
1949 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
1950 indices, indexCount, paint);
1951}
1952
1953void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1954 this->onDrawPath(path, paint);
1955}
1956
reeda85d4d02015-05-06 12:56:48 -07001957void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001958 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001959 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001960}
1961
reede47829b2015-08-06 10:02:53 -07001962void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1963 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001964 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001965 if (dst.isEmpty() || src.isEmpty()) {
1966 return;
1967 }
1968 this->onDrawImageRect(image, &src, dst, paint, constraint);
1969}
reed41af9662015-01-05 07:49:08 -08001970
reed84984ef2015-07-17 07:09:43 -07001971void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1972 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001973 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001974 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001975}
1976
reede47829b2015-08-06 10:02:53 -07001977void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
1978 SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001979 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001980 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
1981 constraint);
1982}
reede47829b2015-08-06 10:02:53 -07001983
reed4c21dc52015-06-25 12:32:03 -07001984void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1985 const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001986 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07001987 if (dst.isEmpty()) {
1988 return;
1989 }
1990 if (!SkNinePatchIter::Valid(image->width(), image->height(), center)) {
reede47829b2015-08-06 10:02:53 -07001991 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001992 }
1993 this->onDrawImageNine(image, center, dst, paint);
1994}
1995
reed41af9662015-01-05 07:49:08 -08001996void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001997 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001998 return;
1999 }
reed41af9662015-01-05 07:49:08 -08002000 this->onDrawBitmap(bitmap, dx, dy, paint);
2001}
2002
reede47829b2015-08-06 10:02:53 -07002003void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07002004 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002005 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07002006 return;
2007 }
reede47829b2015-08-06 10:02:53 -07002008 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08002009}
2010
reed84984ef2015-07-17 07:09:43 -07002011void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2012 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002013 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002014}
2015
reede47829b2015-08-06 10:02:53 -07002016void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2017 SrcRectConstraint constraint) {
2018 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2019 constraint);
2020}
reede47829b2015-08-06 10:02:53 -07002021
reed41af9662015-01-05 07:49:08 -08002022void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2023 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07002024 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002025 return;
2026 }
reed4c21dc52015-06-25 12:32:03 -07002027 if (!SkNinePatchIter::Valid(bitmap.width(), bitmap.height(), center)) {
reeda5517e22015-07-14 10:54:12 -07002028 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002029 }
reed41af9662015-01-05 07:49:08 -08002030 this->onDrawBitmapNine(bitmap, center, dst, paint);
2031}
2032
reed71c3c762015-06-24 10:29:17 -07002033void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2034 const SkColor colors[], int count, SkXfermode::Mode mode,
2035 const SkRect* cull, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002036 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002037 if (count <= 0) {
2038 return;
2039 }
2040 SkASSERT(atlas);
2041 SkASSERT(xform);
2042 SkASSERT(tex);
2043 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
2044}
2045
reedf70b5312016-03-04 16:36:20 -08002046void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2047 if (key) {
2048 this->onDrawAnnotation(rect, key, value);
2049 }
2050}
2051
reede47829b2015-08-06 10:02:53 -07002052void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2053 const SkPaint* paint, SrcRectConstraint constraint) {
2054 if (src) {
2055 this->drawImageRect(image, *src, dst, paint, constraint);
2056 } else {
2057 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2058 dst, paint, constraint);
2059 }
2060}
2061void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2062 const SkPaint* paint, SrcRectConstraint constraint) {
2063 if (src) {
2064 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2065 } else {
2066 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2067 dst, paint, constraint);
2068 }
2069}
2070
tomhudsoncb3bd182016-05-18 07:24:16 -07002071void SkCanvas::temporary_internal_describeTopLayer(SkMatrix* matrix, SkIRect* clip_bounds) {
2072 SkIRect layer_bounds = this->getTopLayerBounds();
2073 if (matrix) {
2074 *matrix = this->getTotalMatrix();
2075 matrix->preTranslate(-layer_bounds.left(), -layer_bounds.top());
2076 }
2077 if (clip_bounds) {
2078 this->getClipDeviceBounds(clip_bounds);
2079 clip_bounds->offset(-layer_bounds.left(), -layer_bounds.top());
2080 }
2081}
2082
reed@android.com8a1c16f2008-12-17 15:59:43 +00002083//////////////////////////////////////////////////////////////////////////////
2084// These are the virtual drawing methods
2085//////////////////////////////////////////////////////////////////////////////
2086
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002087void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002088 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002089 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2090 }
2091}
2092
reed41af9662015-01-05 07:49:08 -08002093void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002094 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002095 this->internalDrawPaint(paint);
2096}
2097
2098void SkCanvas::internalDrawPaint(const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002099 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002100
2101 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002102 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002103 }
2104
reed@google.com4e2b3d32011-04-07 14:18:59 +00002105 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002106}
2107
reed41af9662015-01-05 07:49:08 -08002108void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2109 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002110 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002111 if ((long)count <= 0) {
2112 return;
2113 }
2114
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002115 SkRect r, storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002116 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002117 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002118 // special-case 2 points (common for drawing a single line)
2119 if (2 == count) {
2120 r.set(pts[0], pts[1]);
2121 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00002122 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002123 }
senorblanco87e066e2015-10-28 11:23:36 -07002124 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2125 return;
2126 }
2127 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002128 }
reed@google.coma584aed2012-05-16 14:06:02 +00002129
halcanary96fcdcc2015-08-27 07:41:13 -07002130 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002131
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002132 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002133
reed@android.com8a1c16f2008-12-17 15:59:43 +00002134 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002135 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002136 }
reed@google.com4b226022011-01-11 18:32:13 +00002137
reed@google.com4e2b3d32011-04-07 14:18:59 +00002138 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002139}
2140
reed41af9662015-01-05 07:49:08 -08002141void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002142 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002143 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002144 const SkRect* bounds = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002145 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08002146 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
2147 // To prevent accidental rejecting at this stage, we have to sort it before we check.
2148 SkRect tmp(r);
2149 tmp.sort();
2150
senorblanco87e066e2015-10-28 11:23:36 -07002151 if (this->quickReject(paint.computeFastBounds(tmp, &storage))) {
2152 return;
2153 }
2154 bounds = &r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002155 }
reed@google.com4b226022011-01-11 18:32:13 +00002156
reedc83a2972015-07-16 07:40:45 -07002157 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002158
2159 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002160 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002161 }
2162
reed@google.com4e2b3d32011-04-07 14:18:59 +00002163 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002164}
2165
reed41af9662015-01-05 07:49:08 -08002166void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002167 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002168 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002169 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002170 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002171 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2172 return;
2173 }
2174 bounds = &oval;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002175 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002176
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002177 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002178
2179 while (iter.next()) {
2180 iter.fDevice->drawOval(iter, oval, looper.paint());
2181 }
2182
2183 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002184}
2185
reed41af9662015-01-05 07:49:08 -08002186void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002187 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002188 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002189 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002190 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002191 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2192 return;
2193 }
2194 bounds = &rrect.getBounds();
reed@google.com4ed0fb72012-12-12 20:48:18 +00002195 }
2196
2197 if (rrect.isRect()) {
2198 // call the non-virtual version
2199 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002200 return;
2201 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002202 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002203 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2204 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002205 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002206
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002207 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002208
2209 while (iter.next()) {
2210 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2211 }
2212
2213 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002214}
2215
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002216void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2217 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002218 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002219 const SkRect* bounds = nullptr;
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002220 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002221 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2222 return;
2223 }
2224 bounds = &outer.getBounds();
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002225 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002226
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002227 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002228
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002229 while (iter.next()) {
2230 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2231 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002232
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002233 LOOPER_END
2234}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002235
reed41af9662015-01-05 07:49:08 -08002236void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002237 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002238 if (!path.isFinite()) {
2239 return;
2240 }
2241
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002242 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002243 const SkRect* bounds = nullptr;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002244 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002245 const SkRect& pathBounds = path.getBounds();
senorblanco87e066e2015-10-28 11:23:36 -07002246 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2247 return;
2248 }
2249 bounds = &pathBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002250 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002251
2252 const SkRect& r = path.getBounds();
2253 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002254 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002255 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002256 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002257 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002258 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002259
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002260 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002261
2262 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002263 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002264 }
2265
reed@google.com4e2b3d32011-04-07 14:18:59 +00002266 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002267}
2268
reed262a71b2015-12-05 13:07:27 -08002269bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002270 if (!paint.getImageFilter()) {
2271 return false;
2272 }
2273
2274 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002275 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002276 return false;
2277 }
2278
2279 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2280 // Once we can filter and the filter will return a result larger than itself, we should be
2281 // able to remove this constraint.
2282 // skbug.com/4526
2283 //
2284 SkPoint pt;
2285 ctm.mapXY(x, y, &pt);
2286 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2287 return ir.contains(fMCRec->fRasterClip.getBounds());
2288}
2289
reeda85d4d02015-05-06 12:56:48 -07002290void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002291 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002292 SkRect bounds = SkRect::MakeXYWH(x, y,
2293 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002294 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002295 SkRect tmp = bounds;
2296 if (paint) {
2297 paint->computeFastBounds(tmp, &tmp);
2298 }
2299 if (this->quickReject(tmp)) {
2300 return;
2301 }
reeda85d4d02015-05-06 12:56:48 -07002302 }
halcanary9d524f22016-03-29 09:03:52 -07002303
reeda85d4d02015-05-06 12:56:48 -07002304 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002305 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002306 paint = lazy.init();
2307 }
reed262a71b2015-12-05 13:07:27 -08002308
reed129ed1c2016-02-22 06:42:31 -08002309 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2310 *paint);
2311 if (drawAsSprite && paint->getImageFilter()) {
2312 SkBitmap bitmap;
2313 if (!as_IB(image)->asBitmapForImageFilters(&bitmap)) {
2314 drawAsSprite = false;
2315 } else{
2316 // Until imagefilters are updated, they cannot handle any src type but N32...
reeddabe5d32016-06-21 10:28:14 -07002317 if (bitmap.info().colorType() != kN32_SkColorType || bitmap.info().gammaCloseToSRGB()) {
reed129ed1c2016-02-22 06:42:31 -08002318 drawAsSprite = false;
2319 }
2320 }
2321 }
2322
reed262a71b2015-12-05 13:07:27 -08002323 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
2324
reeda85d4d02015-05-06 12:56:48 -07002325 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002326 const SkPaint& pnt = looper.paint();
2327 if (drawAsSprite && pnt.getImageFilter()) {
2328 SkBitmap bitmap;
2329 if (as_IB(image)->asBitmapForImageFilters(&bitmap)) {
2330 SkPoint pt;
2331 iter.fMatrix->mapXY(x, y, &pt);
robertphillips2302de92016-03-24 07:26:32 -07002332 iter.fDevice->drawSpriteWithFilter(iter, bitmap,
2333 SkScalarRoundToInt(pt.fX),
2334 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002335 }
2336 } else {
2337 iter.fDevice->drawImage(iter, image, x, y, pnt);
2338 }
reeda85d4d02015-05-06 12:56:48 -07002339 }
halcanary9d524f22016-03-29 09:03:52 -07002340
reeda85d4d02015-05-06 12:56:48 -07002341 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002342}
2343
reed41af9662015-01-05 07:49:08 -08002344void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002345 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002346 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
halcanary96fcdcc2015-08-27 07:41:13 -07002347 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002348 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002349 if (paint) {
2350 paint->computeFastBounds(dst, &storage);
2351 }
2352 if (this->quickReject(storage)) {
2353 return;
2354 }
reeda85d4d02015-05-06 12:56:48 -07002355 }
2356 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002357 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002358 paint = lazy.init();
2359 }
halcanary9d524f22016-03-29 09:03:52 -07002360
senorblancoc41e7e12015-12-07 12:51:30 -08002361 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002362 image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002363
reeda85d4d02015-05-06 12:56:48 -07002364 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002365 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002366 }
halcanary9d524f22016-03-29 09:03:52 -07002367
reeda85d4d02015-05-06 12:56:48 -07002368 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002369}
2370
reed41af9662015-01-05 07:49:08 -08002371void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002372 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002373 SkDEBUGCODE(bitmap.validate();)
2374
reed33366972015-10-08 09:22:02 -07002375 if (bitmap.drawsNothing()) {
2376 return;
2377 }
2378
2379 SkLazyPaint lazy;
2380 if (nullptr == paint) {
2381 paint = lazy.init();
2382 }
2383
2384 const SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2385
2386 SkRect storage;
2387 const SkRect* bounds = nullptr;
2388 if (paint->canComputeFastBounds()) {
2389 bitmap.getBounds(&storage);
2390 matrix.mapRect(&storage);
senorblanco87e066e2015-10-28 11:23:36 -07002391 SkRect tmp = storage;
2392 if (this->quickReject(paint->computeFastBounds(tmp, &tmp))) {
2393 return;
2394 }
2395 bounds = &storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002396 }
reed@google.com4b226022011-01-11 18:32:13 +00002397
reed129ed1c2016-02-22 06:42:31 -08002398 bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(), bitmap.height(),
2399 *paint);
2400 if (drawAsSprite && paint->getImageFilter()) {
2401 // Until imagefilters are updated, they cannot handle any src type but N32...
reeddabe5d32016-06-21 10:28:14 -07002402 if (bitmap.info().colorType() != kN32_SkColorType || bitmap.info().gammaCloseToSRGB()) {
reed129ed1c2016-02-22 06:42:31 -08002403 drawAsSprite = false;
2404 }
2405 }
2406
reed262a71b2015-12-05 13:07:27 -08002407 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds)
reed33366972015-10-08 09:22:02 -07002408
2409 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002410 const SkPaint& pnt = looper.paint();
2411 if (drawAsSprite && pnt.getImageFilter()) {
2412 SkPoint pt;
2413 iter.fMatrix->mapXY(x, y, &pt);
robertphillips2302de92016-03-24 07:26:32 -07002414 iter.fDevice->drawSpriteWithFilter(iter, bitmap,
2415 SkScalarRoundToInt(pt.fX),
2416 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002417 } else {
2418 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
2419 }
reed33366972015-10-08 09:22:02 -07002420 }
reed262a71b2015-12-05 13:07:27 -08002421
reed33366972015-10-08 09:22:02 -07002422 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002423}
2424
reed@google.com9987ec32011-09-07 11:57:52 +00002425// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002426void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002427 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002428 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002429 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002430 return;
2431 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002432
halcanary96fcdcc2015-08-27 07:41:13 -07002433 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002434 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002435 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2436 return;
2437 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002438 }
reed@google.com3d608122011-11-21 15:16:16 +00002439
reed@google.com33535f32012-09-25 15:37:50 +00002440 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002441 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002442 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002443 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002444
senorblancoc41e7e12015-12-07 12:51:30 -08002445 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002446 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002447
reed@google.com33535f32012-09-25 15:37:50 +00002448 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002449 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002450 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002451
reed@google.com33535f32012-09-25 15:37:50 +00002452 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002453}
2454
reed41af9662015-01-05 07:49:08 -08002455void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002456 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002457 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002458 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002459 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002460}
2461
reed4c21dc52015-06-25 12:32:03 -07002462void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2463 const SkPaint* paint) {
2464 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
halcanary9d524f22016-03-29 09:03:52 -07002465
halcanary96fcdcc2015-08-27 07:41:13 -07002466 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002467 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002468 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2469 return;
2470 }
reed@google.com3d608122011-11-21 15:16:16 +00002471 }
halcanary9d524f22016-03-29 09:03:52 -07002472
reed4c21dc52015-06-25 12:32:03 -07002473 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002474 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002475 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002476 }
halcanary9d524f22016-03-29 09:03:52 -07002477
senorblancoc41e7e12015-12-07 12:51:30 -08002478 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002479
reed4c21dc52015-06-25 12:32:03 -07002480 while (iter.next()) {
2481 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002482 }
halcanary9d524f22016-03-29 09:03:52 -07002483
reed4c21dc52015-06-25 12:32:03 -07002484 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002485}
2486
reed41af9662015-01-05 07:49:08 -08002487void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2488 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002489 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002490 SkDEBUGCODE(bitmap.validate();)
2491
halcanary96fcdcc2015-08-27 07:41:13 -07002492 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002493 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002494 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2495 return;
2496 }
reed4c21dc52015-06-25 12:32:03 -07002497 }
halcanary9d524f22016-03-29 09:03:52 -07002498
reed4c21dc52015-06-25 12:32:03 -07002499 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002500 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002501 paint = lazy.init();
2502 }
halcanary9d524f22016-03-29 09:03:52 -07002503
senorblancoc41e7e12015-12-07 12:51:30 -08002504 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002505
reed4c21dc52015-06-25 12:32:03 -07002506 while (iter.next()) {
2507 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2508 }
halcanary9d524f22016-03-29 09:03:52 -07002509
reed4c21dc52015-06-25 12:32:03 -07002510 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002511}
2512
reed@google.comf67e4cf2011-03-15 20:56:58 +00002513class SkDeviceFilteredPaint {
2514public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002515 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002516 uint32_t filteredFlags = device->filterTextFlags(paint);
2517 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002518 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002519 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002520 fPaint = newPaint;
2521 } else {
2522 fPaint = &paint;
2523 }
2524 }
2525
reed@google.comf67e4cf2011-03-15 20:56:58 +00002526 const SkPaint& paint() const { return *fPaint; }
2527
2528private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002529 const SkPaint* fPaint;
2530 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002531};
2532
bungeman@google.com52c748b2011-08-22 21:30:43 +00002533void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2534 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002535 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002536 draw.fDevice->drawRect(draw, r, paint);
2537 } else {
2538 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002539 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002540 draw.fDevice->drawRect(draw, r, p);
2541 }
2542}
2543
2544void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2545 const char text[], size_t byteLength,
2546 SkScalar x, SkScalar y) {
halcanary96fcdcc2015-08-27 07:41:13 -07002547 SkASSERT(byteLength == 0 || text != nullptr);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002548
2549 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07002550 if (text == nullptr || byteLength == 0 ||
reed1e7f5e72016-04-27 07:49:17 -07002551 draw.fRC->isEmpty() ||
halcanary96fcdcc2015-08-27 07:41:13 -07002552 (paint.getAlpha() == 0 && paint.getXfermode() == nullptr)) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002553 return;
2554 }
2555
2556 SkScalar width = 0;
2557 SkPoint start;
2558
2559 start.set(0, 0); // to avoid warning
2560 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2561 SkPaint::kStrikeThruText_Flag)) {
2562 width = paint.measureText(text, byteLength);
2563
2564 SkScalar offsetX = 0;
2565 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2566 offsetX = SkScalarHalf(width);
2567 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2568 offsetX = width;
2569 }
2570 start.set(x - offsetX, y);
2571 }
2572
2573 if (0 == width) {
2574 return;
2575 }
2576
2577 uint32_t flags = paint.getFlags();
2578
2579 if (flags & (SkPaint::kUnderlineText_Flag |
2580 SkPaint::kStrikeThruText_Flag)) {
2581 SkScalar textSize = paint.getTextSize();
2582 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2583 SkRect r;
2584
2585 r.fLeft = start.fX;
2586 r.fRight = start.fX + width;
2587
2588 if (flags & SkPaint::kUnderlineText_Flag) {
2589 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2590 start.fY);
2591 r.fTop = offset;
2592 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002593 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002594 }
2595 if (flags & SkPaint::kStrikeThruText_Flag) {
2596 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2597 start.fY);
2598 r.fTop = offset;
2599 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002600 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002601 }
2602 }
2603}
2604
reed@google.come0d9ce82014-04-23 04:00:17 +00002605void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2606 const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002607 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002608
2609 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002610 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002611 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002612 DrawTextDecorations(iter, dfp.paint(),
2613 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002614 }
2615
reed@google.com4e2b3d32011-04-07 14:18:59 +00002616 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002617}
2618
reed@google.come0d9ce82014-04-23 04:00:17 +00002619void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2620 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002621 SkPoint textOffset = SkPoint::Make(0, 0);
2622
halcanary96fcdcc2015-08-27 07:41:13 -07002623 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002624
reed@android.com8a1c16f2008-12-17 15:59:43 +00002625 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002626 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002627 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002628 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002629 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002630
reed@google.com4e2b3d32011-04-07 14:18:59 +00002631 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002632}
2633
reed@google.come0d9ce82014-04-23 04:00:17 +00002634void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2635 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002636
2637 SkPoint textOffset = SkPoint::Make(0, constY);
2638
halcanary96fcdcc2015-08-27 07:41:13 -07002639 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002640
reed@android.com8a1c16f2008-12-17 15:59:43 +00002641 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002642 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002643 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002644 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002645 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002646
reed@google.com4e2b3d32011-04-07 14:18:59 +00002647 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002648}
2649
reed@google.come0d9ce82014-04-23 04:00:17 +00002650void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2651 const SkMatrix* matrix, const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002652 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002653
reed@android.com8a1c16f2008-12-17 15:59:43 +00002654 while (iter.next()) {
2655 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002656 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002657 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002658
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002659 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002660}
2661
reed45561a02016-07-07 12:47:17 -07002662void SkCanvas::onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2663 const SkRect* cullRect, const SkPaint& paint) {
2664 if (cullRect && this->quickReject(*cullRect)) {
2665 return;
2666 }
2667
2668 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
2669
2670 while (iter.next()) {
2671 iter.fDevice->drawTextRSXform(iter, text, byteLength, xform, looper.paint());
2672 }
2673
2674 LOOPER_END
2675}
2676
fmalita00d5c2c2014-08-21 08:53:26 -07002677void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2678 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002679
fmalita85d5eb92015-03-04 11:20:12 -08002680 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002681 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002682 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002683 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002684 SkRect tmp;
2685 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2686 return;
2687 }
2688 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002689 }
2690
fmalita024f9962015-03-03 19:08:17 -08002691 // We cannot filter in the looper as we normally do, because the paint is
2692 // incomplete at this point (text-related attributes are embedded within blob run paints).
2693 SkDrawFilter* drawFilter = fMCRec->fFilter;
halcanary96fcdcc2015-08-27 07:41:13 -07002694 fMCRec->fFilter = nullptr;
fmalita024f9962015-03-03 19:08:17 -08002695
fmalita85d5eb92015-03-04 11:20:12 -08002696 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002697
fmalitaaa1b9122014-08-28 14:32:24 -07002698 while (iter.next()) {
2699 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002700 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002701 }
2702
fmalitaaa1b9122014-08-28 14:32:24 -07002703 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002704
2705 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002706}
2707
reed@google.come0d9ce82014-04-23 04:00:17 +00002708// These will become non-virtual, so they always call the (virtual) onDraw... method
2709void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2710 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002711 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002712 this->onDrawText(text, byteLength, x, y, paint);
2713}
2714void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2715 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002716 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002717 this->onDrawPosText(text, byteLength, pos, paint);
2718}
2719void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2720 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002721 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002722 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2723}
2724void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2725 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002726 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002727 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2728}
reed45561a02016-07-07 12:47:17 -07002729void SkCanvas::drawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2730 const SkRect* cullRect, const SkPaint& paint) {
2731 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextRSXform()");
2732 if (byteLength) {
2733 this->onDrawTextRSXform(text, byteLength, xform, cullRect, paint);
2734 }
2735}
fmalita00d5c2c2014-08-21 08:53:26 -07002736void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2737 const SkPaint& paint) {
reede3b38ce2016-01-08 09:18:44 -08002738 RETURN_ON_NULL(blob);
danakj9881d632014-11-26 12:41:06 -08002739 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
reede3b38ce2016-01-08 09:18:44 -08002740 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002741}
reed@google.come0d9ce82014-04-23 04:00:17 +00002742
reed41af9662015-01-05 07:49:08 -08002743void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2744 const SkPoint verts[], const SkPoint texs[],
2745 const SkColor colors[], SkXfermode* xmode,
2746 const uint16_t indices[], int indexCount,
2747 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002748 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
halcanary96fcdcc2015-08-27 07:41:13 -07002749 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
reed@google.com4b226022011-01-11 18:32:13 +00002750
reed@android.com8a1c16f2008-12-17 15:59:43 +00002751 while (iter.next()) {
2752 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002753 colors, xmode, indices, indexCount,
2754 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002755 }
reed@google.com4b226022011-01-11 18:32:13 +00002756
reed@google.com4e2b3d32011-04-07 14:18:59 +00002757 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002758}
2759
dandovb3c9d1c2014-08-12 08:34:29 -07002760void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2761 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002762 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
halcanary96fcdcc2015-08-27 07:41:13 -07002763 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002764 return;
2765 }
mtklein6cfa73a2014-08-13 13:33:49 -07002766
dandovecfff212014-08-04 10:02:00 -07002767 // Since a patch is always within the convex hull of the control points, we discard it when its
2768 // bounding rectangle is completely outside the current clip.
2769 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002770 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002771 if (this->quickReject(bounds)) {
2772 return;
2773 }
mtklein6cfa73a2014-08-13 13:33:49 -07002774
dandovb3c9d1c2014-08-12 08:34:29 -07002775 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2776}
2777
2778void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2779 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2780
halcanary96fcdcc2015-08-27 07:41:13 -07002781 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002782
dandovecfff212014-08-04 10:02:00 -07002783 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002784 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002785 }
mtklein6cfa73a2014-08-13 13:33:49 -07002786
dandovecfff212014-08-04 10:02:00 -07002787 LOOPER_END
2788}
2789
reeda8db7282015-07-07 10:22:31 -07002790void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
reede3b38ce2016-01-08 09:18:44 -08002791 RETURN_ON_NULL(dr);
2792 if (x || y) {
2793 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2794 this->onDrawDrawable(dr, &matrix);
2795 } else {
2796 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002797 }
2798}
2799
reeda8db7282015-07-07 10:22:31 -07002800void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reede3b38ce2016-01-08 09:18:44 -08002801 RETURN_ON_NULL(dr);
2802 if (matrix && matrix->isIdentity()) {
2803 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002804 }
reede3b38ce2016-01-08 09:18:44 -08002805 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002806}
2807
2808void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2809 SkRect bounds = dr->getBounds();
2810 if (matrix) {
2811 matrix->mapRect(&bounds);
2812 }
2813 if (this->quickReject(bounds)) {
2814 return;
2815 }
2816 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002817}
2818
reed71c3c762015-06-24 10:29:17 -07002819void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2820 const SkColor colors[], int count, SkXfermode::Mode mode,
2821 const SkRect* cull, const SkPaint* paint) {
2822 if (cull && this->quickReject(*cull)) {
2823 return;
2824 }
2825
2826 SkPaint pnt;
2827 if (paint) {
2828 pnt = *paint;
2829 }
halcanary9d524f22016-03-29 09:03:52 -07002830
halcanary96fcdcc2015-08-27 07:41:13 -07002831 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr)
reed71c3c762015-06-24 10:29:17 -07002832 while (iter.next()) {
2833 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, mode, pnt);
2834 }
2835 LOOPER_END
2836}
2837
reedf70b5312016-03-04 16:36:20 -08002838void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2839 SkASSERT(key);
2840
2841 SkPaint paint;
2842 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, nullptr)
2843 while (iter.next()) {
2844 iter.fDevice->drawAnnotation(iter, rect, key, value);
2845 }
2846 LOOPER_END
2847}
2848
reed@android.com8a1c16f2008-12-17 15:59:43 +00002849//////////////////////////////////////////////////////////////////////////////
2850// These methods are NOT virtual, and therefore must call back into virtual
2851// methods, rather than actually drawing themselves.
2852//////////////////////////////////////////////////////////////////////////////
2853
2854void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002855 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002856 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002857 SkPaint paint;
2858
2859 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002860 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002861 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002862 }
2863 this->drawPaint(paint);
2864}
2865
reed@android.com845fdac2009-06-23 03:01:32 +00002866void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002867 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002868 SkPaint paint;
2869
2870 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002871 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002872 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002873 }
2874 this->drawPaint(paint);
2875}
2876
2877void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002878 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002879 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002880
reed@android.com8a1c16f2008-12-17 15:59:43 +00002881 pt.set(x, y);
2882 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2883}
2884
2885void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002886 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002887 SkPoint pt;
2888 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002889
reed@android.com8a1c16f2008-12-17 15:59:43 +00002890 pt.set(x, y);
2891 paint.setColor(color);
2892 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2893}
2894
2895void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2896 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002897 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002898 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002899
reed@android.com8a1c16f2008-12-17 15:59:43 +00002900 pts[0].set(x0, y0);
2901 pts[1].set(x1, y1);
2902 this->drawPoints(kLines_PointMode, 2, pts, paint);
2903}
2904
2905void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2906 SkScalar right, SkScalar bottom,
2907 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002908 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002909 SkRect r;
2910
2911 r.set(left, top, right, bottom);
2912 this->drawRect(r, paint);
2913}
2914
2915void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2916 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002917 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002918 if (radius < 0) {
2919 radius = 0;
2920 }
2921
2922 SkRect r;
2923 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002924 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002925}
2926
2927void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2928 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002929 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002930 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002931 SkRRect rrect;
2932 rrect.setRectXY(r, rx, ry);
2933 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002934 } else {
2935 this->drawRect(r, paint);
2936 }
2937}
2938
reed@android.com8a1c16f2008-12-17 15:59:43 +00002939void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2940 SkScalar sweepAngle, bool useCenter,
2941 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002942 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002943 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2944 this->drawOval(oval, paint);
2945 } else {
2946 SkPath path;
2947 if (useCenter) {
2948 path.moveTo(oval.centerX(), oval.centerY());
2949 }
2950 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2951 if (useCenter) {
2952 path.close();
2953 }
2954 this->drawPath(path, paint);
2955 }
2956}
2957
2958void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2959 const SkPath& path, SkScalar hOffset,
2960 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002961 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002962 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002963
reed@android.com8a1c16f2008-12-17 15:59:43 +00002964 matrix.setTranslate(hOffset, vOffset);
2965 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2966}
2967
reed@android.comf76bacf2009-05-13 14:00:33 +00002968///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07002969
2970/**
2971 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2972 * against the playback cost of recursing into the subpicture to get at its actual ops.
2973 *
2974 * For now we pick a conservatively small value, though measurement (and other heuristics like
2975 * the type of ops contained) may justify changing this value.
2976 */
2977#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07002978
reedd5fa1a42014-08-09 11:08:05 -07002979void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002980 RETURN_ON_NULL(picture);
2981
reed1c2c4412015-04-30 13:09:24 -07002982 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
reede3b38ce2016-01-08 09:18:44 -08002983 if (matrix && matrix->isIdentity()) {
2984 matrix = nullptr;
2985 }
2986 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2987 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2988 picture->playback(this);
2989 } else {
2990 this->onDrawPicture(picture, matrix, paint);
reedd5fa1a42014-08-09 11:08:05 -07002991 }
2992}
robertphillips9b14f262014-06-04 05:40:44 -07002993
reedd5fa1a42014-08-09 11:08:05 -07002994void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2995 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07002996 if (!paint || paint->canComputeFastBounds()) {
2997 SkRect bounds = picture->cullRect();
2998 if (paint) {
2999 paint->computeFastBounds(bounds, &bounds);
3000 }
3001 if (matrix) {
3002 matrix->mapRect(&bounds);
3003 }
3004 if (this->quickReject(bounds)) {
3005 return;
3006 }
3007 }
3008
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00003009 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07003010 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00003011 // Canvas has to first give the device the opportunity to render
3012 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07003013 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00003014 return; // the device has rendered the entire picture
3015 }
3016 }
3017
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003018 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07003019 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003020}
3021
reed@android.com8a1c16f2008-12-17 15:59:43 +00003022///////////////////////////////////////////////////////////////////////////////
3023///////////////////////////////////////////////////////////////////////////////
3024
3025SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
bungeman99fe8222015-08-20 07:57:51 -07003026 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003027
3028 SkASSERT(canvas);
3029
3030 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
3031 fDone = !fImpl->next();
3032}
3033
3034SkCanvas::LayerIter::~LayerIter() {
3035 fImpl->~SkDrawIter();
3036}
3037
3038void SkCanvas::LayerIter::next() {
3039 fDone = !fImpl->next();
3040}
3041
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003042SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003043 return fImpl->getDevice();
3044}
3045
3046const SkMatrix& SkCanvas::LayerIter::matrix() const {
3047 return fImpl->getMatrix();
3048}
3049
3050const SkPaint& SkCanvas::LayerIter::paint() const {
3051 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003052 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003053 paint = &fDefaultPaint;
3054 }
3055 return *paint;
3056}
3057
reed1e7f5e72016-04-27 07:49:17 -07003058const SkRasterClip& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +00003059int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
3060int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003061
3062///////////////////////////////////////////////////////////////////////////////
3063
fmalitac3b589a2014-06-05 12:40:07 -07003064SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003065
3066///////////////////////////////////////////////////////////////////////////////
3067
3068static bool supported_for_raster_canvas(const SkImageInfo& info) {
3069 switch (info.alphaType()) {
3070 case kPremul_SkAlphaType:
3071 case kOpaque_SkAlphaType:
3072 break;
3073 default:
3074 return false;
3075 }
3076
3077 switch (info.colorType()) {
3078 case kAlpha_8_SkColorType:
3079 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00003080 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003081 break;
3082 default:
3083 return false;
3084 }
3085
3086 return true;
3087}
3088
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003089SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
3090 if (!supported_for_raster_canvas(info)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003091 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003092 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003093
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003094 SkBitmap bitmap;
3095 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003096 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003097 }
halcanary385fe4d2015-08-26 13:07:48 -07003098 return new SkCanvas(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003099}
reedd5fa1a42014-08-09 11:08:05 -07003100
3101///////////////////////////////////////////////////////////////////////////////
3102
3103SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003104 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07003105 : fCanvas(canvas)
3106 , fSaveCount(canvas->getSaveCount())
3107{
bsalomon49f085d2014-09-05 13:34:00 -07003108 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003109 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07003110 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003111 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07003112 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003113 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07003114 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003115 canvas->save();
3116 }
mtklein6cfa73a2014-08-13 13:33:49 -07003117
bsalomon49f085d2014-09-05 13:34:00 -07003118 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003119 canvas->concat(*matrix);
3120 }
3121}
3122
3123SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
3124 fCanvas->restoreToCount(fSaveCount);
3125}
reede8f30622016-03-23 18:59:25 -07003126
3127#ifdef SK_SUPPORT_LEGACY_NEW_SURFACE_API
3128SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
3129 return this->makeSurface(info, props).release();
3130}
3131#endif