blob: 733ff19f91cbadb6c0f2bff6601423b2f503c854 [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() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000815 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000816 if (device) {
817 device->flush();
818 }
819}
820
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000821SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000822 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000823 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
824}
825
senorblancoafc7cce2016-02-02 18:44:15 -0800826SkIRect SkCanvas::getTopLayerBounds() const {
827 SkBaseDevice* d = this->getTopDevice();
828 if (!d) {
829 return SkIRect::MakeEmpty();
830 }
831 return SkIRect::MakeXYWH(d->getOrigin().x(), d->getOrigin().y(), d->width(), d->height());
832}
833
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000834SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000835 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000836 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000837 SkASSERT(rec && rec->fLayer);
838 return rec->fLayer->fDevice;
839}
840
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000841SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000842 if (updateMatrixClip) {
843 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
844 }
reed@google.com9266fed2011-03-30 00:18:03 +0000845 return fMCRec->fTopLayer->fDevice;
846}
847
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000848bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
849 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
850 return false;
851 }
852
853 bool weAllocated = false;
halcanary96fcdcc2015-08-27 07:41:13 -0700854 if (nullptr == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700855 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000856 return false;
857 }
858 weAllocated = true;
859 }
860
reedcf01e312015-05-23 19:14:51 -0700861 SkAutoPixmapUnlock unlocker;
862 if (bitmap->requestLock(&unlocker)) {
863 const SkPixmap& pm = unlocker.pixmap();
864 if (this->readPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), x, y)) {
865 return true;
866 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000867 }
868
869 if (weAllocated) {
halcanary96fcdcc2015-08-27 07:41:13 -0700870 bitmap->setPixelRef(nullptr);
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000871 }
872 return false;
873}
reed@google.com51df9e32010-12-23 19:29:18 +0000874
bsalomon@google.comc6980972011-11-02 19:57:21 +0000875bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000876 SkIRect r = srcRect;
877 const SkISize size = this->getBaseLayerSize();
878 if (!r.intersect(0, 0, size.width(), size.height())) {
879 bitmap->reset();
880 return false;
881 }
882
reed84825042014-09-02 12:50:45 -0700883 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000884 // bitmap will already be reset.
885 return false;
886 }
887 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
888 bitmap->reset();
889 return false;
890 }
891 return true;
892}
893
reed96472de2014-12-10 09:53:42 -0800894bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000895 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000896 if (!device) {
897 return false;
898 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000899 const SkISize size = this->getBaseLayerSize();
mtkleinf0f14112014-12-12 08:46:25 -0800900
reed96472de2014-12-10 09:53:42 -0800901 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
902 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000903 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000904 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000905
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000906 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800907 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000908}
909
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000910bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
911 if (bitmap.getTexture()) {
912 return false;
913 }
reedcf01e312015-05-23 19:14:51 -0700914
915 SkAutoPixmapUnlock unlocker;
916 if (bitmap.requestLock(&unlocker)) {
917 const SkPixmap& pm = unlocker.pixmap();
918 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000919 }
920 return false;
921}
922
923bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
924 int x, int y) {
925 switch (origInfo.colorType()) {
926 case kUnknown_SkColorType:
927 case kIndex_8_SkColorType:
928 return false;
929 default:
930 break;
931 }
halcanary96fcdcc2015-08-27 07:41:13 -0700932 if (nullptr == pixels || rowBytes < origInfo.minRowBytes()) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000933 return false;
934 }
935
936 const SkISize size = this->getBaseLayerSize();
937 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
938 if (!target.intersect(0, 0, size.width(), size.height())) {
939 return false;
940 }
941
942 SkBaseDevice* device = this->getDevice();
943 if (!device) {
944 return false;
945 }
946
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000947 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700948 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000949
950 // if x or y are negative, then we have to adjust pixels
951 if (x > 0) {
952 x = 0;
953 }
954 if (y > 0) {
955 y = 0;
956 }
957 // here x,y are either 0 or negative
958 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
959
reed4af35f32014-06-27 17:47:49 -0700960 // Tell our owning surface to bump its generation ID
reedc83a2972015-07-16 07:40:45 -0700961 const bool completeOverwrite = info.dimensions() == size;
962 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700963
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000964 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000965 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000966}
reed@google.com51df9e32010-12-23 19:29:18 +0000967
junov@google.com4370aed2012-01-18 16:21:08 +0000968SkCanvas* SkCanvas::canvasForDrawIter() {
969 return this;
970}
971
reed@android.com8a1c16f2008-12-17 15:59:43 +0000972//////////////////////////////////////////////////////////////////////////////
973
reed@android.com8a1c16f2008-12-17 15:59:43 +0000974void SkCanvas::updateDeviceCMCache() {
975 if (fDeviceCMDirty) {
976 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700977 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000978 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000979
halcanary96fcdcc2015-08-27 07:41:13 -0700980 if (nullptr == layer->fNext) { // only one layer
981 layer->updateMC(totalMatrix, totalClip, *fClipStack, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000982 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000983 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000984 do {
reed687fa1c2015-04-07 08:00:56 -0700985 layer->updateMC(totalMatrix, clip, *fClipStack, &clip);
halcanary96fcdcc2015-08-27 07:41:13 -0700986 } while ((layer = layer->fNext) != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000987 }
988 fDeviceCMDirty = false;
989 }
990}
991
reed@android.com8a1c16f2008-12-17 15:59:43 +0000992///////////////////////////////////////////////////////////////////////////////
993
reed2ff1fce2014-12-11 07:07:37 -0800994void SkCanvas::checkForDeferredSave() {
995 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800996 this->doSave();
997 }
998}
999
reedf0090cb2014-11-26 08:55:51 -08001000int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -08001001#ifdef SK_DEBUG
1002 int count = 0;
1003 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
1004 for (;;) {
1005 const MCRec* rec = (const MCRec*)iter.next();
1006 if (!rec) {
1007 break;
1008 }
1009 count += 1 + rec->fDeferredSaveCount;
1010 }
1011 SkASSERT(count == fSaveCount);
1012#endif
1013 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -08001014}
1015
1016int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -08001017 fSaveCount += 1;
1018 fMCRec->fDeferredSaveCount += 1;
1019 return this->getSaveCount() - 1; // return our prev value
1020}
1021
1022void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -08001023 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -07001024
1025 SkASSERT(fMCRec->fDeferredSaveCount > 0);
1026 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001027 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -08001028}
1029
1030void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -08001031 if (fMCRec->fDeferredSaveCount > 0) {
1032 SkASSERT(fSaveCount > 1);
1033 fSaveCount -= 1;
1034 fMCRec->fDeferredSaveCount -= 1;
1035 } else {
1036 // check for underflow
1037 if (fMCStack.count() > 1) {
1038 this->willRestore();
1039 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -07001040 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001041 this->internalRestore();
1042 this->didRestore();
1043 }
reedf0090cb2014-11-26 08:55:51 -08001044 }
1045}
1046
1047void SkCanvas::restoreToCount(int count) {
1048 // sanity check
1049 if (count < 1) {
1050 count = 1;
1051 }
mtkleinf0f14112014-12-12 08:46:25 -08001052
reedf0090cb2014-11-26 08:55:51 -08001053 int n = this->getSaveCount() - count;
1054 for (int i = 0; i < n; ++i) {
1055 this->restore();
1056 }
1057}
1058
reed2ff1fce2014-12-11 07:07:37 -08001059void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001060 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -07001061 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +00001062 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +00001063
reed687fa1c2015-04-07 08:00:56 -07001064 fClipStack->save();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001065}
1066
reed4960eee2015-12-18 07:09:18 -08001067bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
reed@google.comb93ba452014-03-10 19:47:58 +00001068#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001069 return !(saveLayerFlags & SkCanvas::kDontClipToLayer_PrivateSaveLayerFlag);
reed@google.comb93ba452014-03-10 19:47:58 +00001070#else
1071 return true;
1072#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001073}
1074
reed4960eee2015-12-18 07:09:18 -08001075bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -07001076 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001077 SkIRect clipBounds;
1078 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001079 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +00001080 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001081
reed96e657d2015-03-10 17:30:07 -07001082 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
1083
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001084 if (imageFilter) {
senorblancoe5e79842016-03-21 14:51:59 -07001085 clipBounds = imageFilter->filterBounds(clipBounds, ctm);
senorblancodb64af32015-12-09 10:11:43 -08001086 if (bounds && !imageFilter->canComputeFastBounds()) {
1087 bounds = nullptr;
1088 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001089 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001090 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -07001091 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001092 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +00001093
reed96e657d2015-03-10 17:30:07 -07001094 ctm.mapRect(&r, *bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001095 r.roundOut(&ir);
1096 // early exit if the layer's bounds are clipped out
1097 if (!ir.intersect(clipBounds)) {
reed4960eee2015-12-18 07:09:18 -08001098 if (BoundsAffectsClip(saveLayerFlags)) {
reed9b3aa542015-03-11 08:47:12 -07001099 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001100 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001101 }
junov@chromium.orga907ac32012-02-24 21:54:07 +00001102 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001103 }
1104 } else { // no user bounds, so just use the clip
1105 ir = clipBounds;
1106 }
reed180aec42015-03-11 10:39:04 -07001107 SkASSERT(!ir.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001108
reed4960eee2015-12-18 07:09:18 -08001109 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -07001110 // Simplify the current clips since they will be applied properly during restore()
reed9b3aa542015-03-11 08:47:12 -07001111 fCachedLocalClipBoundsDirty = true;
reed687fa1c2015-04-07 08:00:56 -07001112 fClipStack->clipDevRect(ir, SkRegion::kReplace_Op);
reed180aec42015-03-11 10:39:04 -07001113 fMCRec->fRasterClip.setRect(ir);
junov@chromium.orga907ac32012-02-24 21:54:07 +00001114 }
1115
1116 if (intersection) {
1117 *intersection = ir;
1118 }
1119 return true;
1120}
1121
reed4960eee2015-12-18 07:09:18 -08001122
reed4960eee2015-12-18 07:09:18 -08001123int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
1124 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001125}
1126
reed70ee31b2015-12-10 13:44:45 -08001127int SkCanvas::saveLayerPreserveLCDTextRequests(const SkRect* bounds, const SkPaint* paint) {
reed4960eee2015-12-18 07:09:18 -08001128 return this->saveLayer(SaveLayerRec(bounds, paint, kPreserveLCDText_SaveLayerFlag));
1129}
1130
1131int SkCanvas::saveLayer(const SaveLayerRec& origRec) {
1132 SaveLayerRec rec(origRec);
1133 if (gIgnoreSaveLayerBounds) {
1134 rec.fBounds = nullptr;
1135 }
1136 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
1137 fSaveCount += 1;
1138 this->internalSaveLayer(rec, strategy);
1139 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -08001140}
1141
reedbfd5f172016-01-07 11:28:08 -08001142static void draw_filter_into_device(SkBaseDevice* src, const SkImageFilter* filter,
1143 SkBaseDevice* dst, const SkMatrix& ctm) {
robertphillips7354a4b2015-12-16 05:08:27 -08001144
1145 SkBitmap srcBM;
1146
1147#if SK_SUPPORT_GPU
robertphillips175dd9b2016-04-28 14:32:04 -07001148 // TODO: remove this virtual usage of accessRenderTarget! It is preventing
1149 // removal of the virtual on SkBaseDevice.
robertphillips7354a4b2015-12-16 05:08:27 -08001150 GrRenderTarget* srcRT = src->accessRenderTarget();
1151 if (srcRT && !srcRT->asTexture() && dst->accessRenderTarget()) {
1152 // When both the src & the dst are on the gpu but the src doesn't have a texture,
1153 // we create a temporary texture for the draw.
1154 // TODO: we should actually only copy the portion of the source needed to apply the image
1155 // filter
1156 GrContext* context = srcRT->getContext();
bsalomon5ec26ae2016-02-25 08:33:02 -08001157 SkAutoTUnref<GrTexture> tex(context->textureProvider()->createTexture(srcRT->desc(),
1158 SkBudgeted::kYes));
robertphillips7354a4b2015-12-16 05:08:27 -08001159
1160 context->copySurface(tex, srcRT);
1161
1162 GrWrapTextureInBitmap(tex, src->width(), src->height(), src->isOpaque(), &srcBM);
1163 } else
1164#endif
1165 {
1166 srcBM = src->accessBitmap(false);
1167 }
1168
1169 SkCanvas c(dst);
1170
1171 SkPaint p;
robertphillips372177e2016-03-30 07:32:28 -07001172 p.setImageFilter(filter->makeWithLocalMatrix(ctm));
reedbfd5f172016-01-07 11:28:08 -08001173 const SkScalar x = SkIntToScalar(src->getOrigin().x());
1174 const SkScalar y = SkIntToScalar(src->getOrigin().y());
1175 c.drawBitmap(srcBM, x, y, &p);
robertphillips7354a4b2015-12-16 05:08:27 -08001176}
reed70ee31b2015-12-10 13:44:45 -08001177
reed129ed1c2016-02-22 06:42:31 -08001178static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, bool isOpaque,
1179 const SkPaint* paint) {
1180 // need to force L32 for now if we have an image filter. Once filters support other colortypes
1181 // e.g. sRGB or F16, we can remove this check
brianosman52ede1d2016-06-20 08:25:02 -07001182 // SRGBTODO: Can we remove this check now?
reed129ed1c2016-02-22 06:42:31 -08001183 const bool hasImageFilter = paint && paint->getImageFilter();
1184
1185 SkAlphaType alphaType = isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
1186 if ((prev.bytesPerPixel() < 4) || hasImageFilter) {
1187 // force to L32
1188 return SkImageInfo::MakeN32(w, h, alphaType);
1189 } else {
1190 // keep the same characteristics as the prev
brianosman52ede1d2016-06-20 08:25:02 -07001191 return SkImageInfo::Make(w, h, prev.colorType(), alphaType, sk_ref_sp(prev.colorSpace()));
reed129ed1c2016-02-22 06:42:31 -08001192 }
1193}
1194
reed4960eee2015-12-18 07:09:18 -08001195void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
1196 const SkRect* bounds = rec.fBounds;
1197 const SkPaint* paint = rec.fPaint;
1198 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1199
reed@google.comb93ba452014-03-10 19:47:58 +00001200#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001201 saveLayerFlags &= ~kDontClipToLayer_PrivateSaveLayerFlag;
reed@google.comb93ba452014-03-10 19:47:58 +00001202#endif
1203
reed8c30a812016-04-20 16:36:51 -07001204 SkLazyPaint lazyP;
1205 SkImageFilter* imageFilter = paint ? paint->getImageFilter() : NULL;
1206 SkMatrix stashedMatrix = fMCRec->fMatrix;
reed8c30a812016-04-20 16:36:51 -07001207 SkMatrix remainder;
1208 SkSize scale;
1209 /*
1210 * ImageFilters (so far) do not correctly handle matrices (CTM) that contain rotation/skew/etc.
1211 * but they do handle scaling. To accommodate this, we do the following:
1212 *
1213 * 1. Stash off the current CTM
1214 * 2. Decompose the CTM into SCALE and REMAINDER
1215 * 3. Wack the CTM to be just SCALE, and wrap the imagefilter with a MatrixImageFilter that
1216 * contains the REMAINDER
1217 * 4. Proceed as usual, allowing the client to draw into the layer (now with a scale-only CTM)
1218 * 5. During restore, we process the MatrixImageFilter, which applies REMAINDER to the output
1219 * of the original imagefilter, and draw that (via drawSprite)
1220 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1221 *
1222 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1223 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1224 */
reed96a04f32016-04-25 09:25:15 -07001225 if (imageFilter && !stashedMatrix.isScaleTranslate() && !imageFilter->canHandleComplexCTM() &&
reed8c30a812016-04-20 16:36:51 -07001226 stashedMatrix.decomposeScale(&scale, &remainder))
1227 {
1228 // We will restore the matrix (which we are overwriting here) in restore via fStashedMatrix
1229 this->internalSetMatrix(SkMatrix::MakeScale(scale.width(), scale.height()));
1230 SkPaint* p = lazyP.set(*paint);
1231 p->setImageFilter(SkImageFilter::MakeMatrixFilter(remainder,
1232 SkFilterQuality::kLow_SkFilterQuality,
1233 sk_ref_sp(imageFilter)));
1234 imageFilter = p->getImageFilter();
1235 paint = p;
1236 }
reed8c30a812016-04-20 16:36:51 -07001237
junov@chromium.orga907ac32012-02-24 21:54:07 +00001238 // do this before we create the layer. We don't call the public save() since
1239 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001240 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001241
1242 fDeviceCMDirty = true;
1243
1244 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001245 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
reed2ff1fce2014-12-11 07:07:37 -08001246 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001247 }
1248
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001249 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1250 // the clipRectBounds() call above?
1251 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001252 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001253 }
1254
reed4960eee2015-12-18 07:09:18 -08001255 bool isOpaque = SkToBool(saveLayerFlags & kIsOpaque_SaveLayerFlag);
reed8dc0ccb2015-03-20 06:32:52 -07001256 SkPixelGeometry geo = fProps.pixelGeometry();
1257 if (paint) {
reed76033be2015-03-14 10:54:31 -07001258 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001259 if (paint->getImageFilter() || paint->getColorFilter()) {
reed76033be2015-03-14 10:54:31 -07001260 isOpaque = false;
reed8dc0ccb2015-03-20 06:32:52 -07001261 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001262 }
1263 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001264
reedb2db8982014-11-13 12:41:02 -08001265 SkBaseDevice* device = this->getTopDevice();
halcanary96fcdcc2015-08-27 07:41:13 -07001266 if (nullptr == device) {
reedb2db8982014-11-13 12:41:02 -08001267 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001268 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001269 }
reedb2db8982014-11-13 12:41:02 -08001270
reed129ed1c2016-02-22 06:42:31 -08001271 SkImageInfo info = make_layer_info(device->imageInfo(), ir.width(), ir.height(), isOpaque,
1272 paint);
1273
reed61f501f2015-04-29 08:34:00 -07001274 bool forceSpriteOnRestore = false;
1275 {
reed70ee31b2015-12-10 13:44:45 -08001276 const bool preserveLCDText = kOpaque_SkAlphaType == info.alphaType() ||
reed4960eee2015-12-18 07:09:18 -08001277 (saveLayerFlags & kPreserveLCDText_SaveLayerFlag);
reeddaa57bf2015-05-15 10:39:17 -07001278 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
reed70ee31b2015-12-10 13:44:45 -08001279 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
1280 preserveLCDText, false);
reed61f501f2015-04-29 08:34:00 -07001281 SkBaseDevice* newDev = device->onCreateDevice(createInfo, paint);
halcanary96fcdcc2015-08-27 07:41:13 -07001282 if (nullptr == newDev) {
reed61f501f2015-04-29 08:34:00 -07001283 // If onCreateDevice didn't succeed, try raster (e.g. PDF couldn't handle the paint)
robertphillips9a53fd72015-06-22 09:46:59 -07001284 const SkSurfaceProps surfaceProps(fProps.flags(), createInfo.fPixelGeometry);
1285 newDev = SkBitmapDevice::Create(createInfo.fInfo, surfaceProps);
halcanary96fcdcc2015-08-27 07:41:13 -07001286 if (nullptr == newDev) {
reed61f501f2015-04-29 08:34:00 -07001287 SkErrorInternals::SetError(kInternalError_SkError,
1288 "Unable to create device for layer.");
1289 return;
1290 }
1291 forceSpriteOnRestore = true;
1292 }
1293 device = newDev;
bungeman@google.come25c6842011-08-17 14:53:54 +00001294 }
reed@google.com6f8f2922011-03-04 22:27:10 +00001295 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips7354a4b2015-12-16 05:08:27 -08001296
reedbfd5f172016-01-07 11:28:08 -08001297 if (rec.fBackdrop) {
1298 draw_filter_into_device(fMCRec->fTopLayer->fDevice, rec.fBackdrop, device, fMCRec->fMatrix);
robertphillips7354a4b2015-12-16 05:08:27 -08001299 }
1300
reed8c30a812016-04-20 16:36:51 -07001301 DeviceCM* layer = new DeviceCM(device, paint, this, fConservativeRasterClip,
1302 forceSpriteOnRestore, stashedMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001303 device->unref();
1304
1305 layer->fNext = fMCRec->fTopLayer;
1306 fMCRec->fLayer = layer;
1307 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reed@android.com8a1c16f2008-12-17 15:59:43 +00001308}
1309
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001310int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001311 if (0xFF == alpha) {
1312 return this->saveLayer(bounds, nullptr);
1313 } else {
1314 SkPaint tmpPaint;
1315 tmpPaint.setAlpha(alpha);
1316 return this->saveLayer(bounds, &tmpPaint);
1317 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001318}
1319
reed@android.com8a1c16f2008-12-17 15:59:43 +00001320void SkCanvas::internalRestore() {
1321 SkASSERT(fMCStack.count() != 0);
1322
1323 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001324 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001325
reed687fa1c2015-04-07 08:00:56 -07001326 fClipStack->restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001327
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001328 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001329 DeviceCM* layer = fMCRec->fLayer; // may be null
1330 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001331 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001332
1333 // now do the normal restore()
1334 fMCRec->~MCRec(); // balanced in save()
1335 fMCStack.pop_back();
1336 fMCRec = (MCRec*)fMCStack.back();
1337
1338 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1339 since if we're being recorded, we don't want to record this (the
1340 recorder will have already recorded the restore).
1341 */
bsalomon49f085d2014-09-05 13:34:00 -07001342 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001343 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001344 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001345 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
reed61f501f2015-04-29 08:34:00 -07001346 layer->fPaint, layer->fDeviceIsBitmapDevice);
reed8c30a812016-04-20 16:36:51 -07001347 // restore what we smashed in internalSaveLayer
1348 fMCRec->fMatrix = layer->fStashedMatrix;
reed@google.com8926b162012-03-23 15:36:36 +00001349 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001350 fDeviceCMDirty = true;
halcanary385fe4d2015-08-26 13:07:48 -07001351 delete layer;
reedb679ca82015-04-07 04:40:48 -07001352 } else {
1353 // we're at the root
reeda499f902015-05-01 09:34:31 -07001354 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001355 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001356 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001357 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001358 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001359}
1360
reede8f30622016-03-23 18:59:25 -07001361sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001362 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001363 props = &fProps;
1364 }
1365 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001366}
1367
reede8f30622016-03-23 18:59:25 -07001368sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001369 SkBaseDevice* dev = this->getDevice();
reede8f30622016-03-23 18:59:25 -07001370 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001371}
1372
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001373SkImageInfo SkCanvas::imageInfo() const {
1374 SkBaseDevice* dev = this->getDevice();
1375 if (dev) {
1376 return dev->imageInfo();
1377 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001378 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001379 }
1380}
1381
brianosman898235c2016-04-06 07:38:23 -07001382bool SkCanvas::getProps(SkSurfaceProps* props) const {
1383 SkBaseDevice* dev = this->getDevice();
1384 if (dev) {
1385 if (props) {
1386 *props = fProps;
1387 }
1388 return true;
1389 } else {
1390 return false;
1391 }
1392}
1393
reed6ceeebd2016-03-09 14:26:26 -08001394#ifdef SK_SUPPORT_LEGACY_PEEKPIXELS_PARMS
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001395const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
reed884e97c2015-05-26 11:31:54 -07001396 SkPixmap pmap;
reed6ceeebd2016-03-09 14:26:26 -08001397 if (this->peekPixels(&pmap)) {
1398 if (info) {
1399 *info = pmap.info();
1400 }
1401 if (rowBytes) {
1402 *rowBytes = pmap.rowBytes();
1403 }
1404 return pmap.addr();
reed884e97c2015-05-26 11:31:54 -07001405 }
reed6ceeebd2016-03-09 14:26:26 -08001406 return nullptr;
1407}
1408#endif
1409
1410bool SkCanvas::peekPixels(SkPixmap* pmap) {
1411 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001412}
1413
reed884e97c2015-05-26 11:31:54 -07001414bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001415 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001416 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001417}
1418
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001419void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001420 SkPixmap pmap;
1421 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001422 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001423 }
1424 if (info) {
1425 *info = pmap.info();
1426 }
1427 if (rowBytes) {
1428 *rowBytes = pmap.rowBytes();
1429 }
1430 if (origin) {
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001431 *origin = this->getTopDevice(false)->getOrigin();
1432 }
reed884e97c2015-05-26 11:31:54 -07001433 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001434}
1435
reed884e97c2015-05-26 11:31:54 -07001436bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001437 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001438 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001439}
1440
reed@android.com8a1c16f2008-12-17 15:59:43 +00001441/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001442
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001443void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed61f501f2015-04-29 08:34:00 -07001444 const SkPaint* paint, bool deviceIsBitmapDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001445 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001446 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001447 paint = &tmp;
1448 }
reed@google.com4b226022011-01-11 18:32:13 +00001449
reed@google.com8926b162012-03-23 15:36:36 +00001450 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001451 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001452 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001453 paint = &looper.paint();
1454 SkImageFilter* filter = paint->getImageFilter();
1455 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
senorblancof35566e2016-04-18 10:32:02 -07001456 if (filter) {
robertphillips4418dba2016-03-07 12:45:14 -08001457 const SkBitmap& srcBM = srcDev->accessBitmap(false);
reed1eca1162016-04-25 12:29:38 -07001458 dstDev->drawSpriteWithFilter(iter, srcBM, pos.x(), pos.y(), *paint);
reed61f501f2015-04-29 08:34:00 -07001459 } else if (deviceIsBitmapDevice) {
reed9572a102015-05-26 19:22:17 -07001460 const SkBitmap& src = static_cast<SkBitmapDevice*>(srcDev)->fBitmap;
reed61f501f2015-04-29 08:34:00 -07001461 dstDev->drawSprite(iter, src, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001462 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001463 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001464 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001465 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001466 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001467}
1468
reed32704672015-12-16 08:27:10 -08001469/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001470
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001471void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001472 SkMatrix m;
1473 m.setTranslate(dx, dy);
1474 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001475}
1476
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001477void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001478 SkMatrix m;
1479 m.setScale(sx, sy);
1480 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001481}
1482
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001483void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001484 SkMatrix m;
1485 m.setRotate(degrees);
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::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001490 SkMatrix m;
1491 m.setSkew(sx, sy);
1492 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001493}
1494
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001495void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001496 if (matrix.isIdentity()) {
1497 return;
1498 }
1499
reed2ff1fce2014-12-11 07:07:37 -08001500 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001501 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001502 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001503 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001504
1505 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001506}
1507
reed8c30a812016-04-20 16:36:51 -07001508void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001509 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001510 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001511 fMCRec->fMatrix = matrix;
reed8c30a812016-04-20 16:36:51 -07001512}
1513
1514void SkCanvas::setMatrix(const SkMatrix& matrix) {
1515 this->checkForDeferredSave();
1516 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001517 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001518}
1519
reed@android.com8a1c16f2008-12-17 15:59:43 +00001520void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001521 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001522}
1523
1524//////////////////////////////////////////////////////////////////////////////
1525
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001526void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed2d1afab2016-06-29 14:33:11 -07001527 if (!fAllowSoftClip) {
1528 doAA = false;
1529 }
1530
1531#ifdef SK_SUPPORT_PRECHECK_CLIPRECT
1532 // Check if we can quick-accept the clip call (and do nothing)
1533 //
reed74467162016-06-30 08:15:35 -07001534 if (SkRegion::kIntersect_Op == op && !doAA && fMCRec->fMatrix.isScaleTranslate()) {
reed2d1afab2016-06-29 14:33:11 -07001535 SkRect devR;
reed74467162016-06-30 08:15:35 -07001536 fMCRec->fMatrix.mapRectScaleTranslate(&devR, rect);
reed2d1afab2016-06-29 14:33:11 -07001537 // NOTE: this check is CTM specific, since we might round differently with a different
1538 // CTM. Thus this is only 100% reliable if there is not global CTM scale to be
1539 // applied later (i.e. if this is going into a picture).
1540 if (devR.round().contains(fMCRec->fRasterClip.getBounds())) {
1541#if 0
1542 SkDebugf("ignored clipRect [%g %g %g %g]\n",
1543 rect.left(), rect.top(), rect.right(), rect.bottom());
1544#endif
1545 return;
1546 }
1547 }
1548#endif
1549
reed2ff1fce2014-12-11 07:07:37 -08001550 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001551 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1552 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001553}
1554
1555void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001556#ifdef SK_ENABLE_CLIP_QUICKREJECT
1557 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001558 if (fMCRec->fRasterClip.isEmpty()) {
reed2d1afab2016-06-29 14:33:11 -07001559 return;
reed@google.comda17f752012-08-16 18:27:05 +00001560 }
1561
reed@google.com3b3e8952012-08-16 20:53:31 +00001562 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001563 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001564 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001565
reed687fa1c2015-04-07 08:00:56 -07001566 fClipStack->clipEmpty();
reed2d1afab2016-06-29 14:33:11 -07001567 (void)fMCRec->fRasterClip.setEmpty();
1568 return;
reed@google.comda17f752012-08-16 18:27:05 +00001569 }
1570 }
1571#endif
1572
reed74467162016-06-30 08:15:35 -07001573 const bool isScaleTrans = fMCRec->fMatrix.isScaleTranslate();
reedc64eff52015-11-21 12:39:45 -08001574 SkRect devR;
reed74467162016-06-30 08:15:35 -07001575 if (isScaleTrans) {
1576 fMCRec->fMatrix.mapRectScaleTranslate(&devR, rect);
reedc64eff52015-11-21 12:39:45 -08001577 }
bsalomonac8cabd2015-11-20 18:53:07 -08001578
reed2d1afab2016-06-29 14:33:11 -07001579#ifndef SK_SUPPORT_PRECHECK_CLIPRECT
reedc64eff52015-11-21 12:39:45 -08001580 if (SkRegion::kIntersect_Op == op &&
1581 kHard_ClipEdgeStyle == edgeStyle
reed74467162016-06-30 08:15:35 -07001582 && isScaleTrans)
reedc64eff52015-11-21 12:39:45 -08001583 {
1584 if (devR.round().contains(fMCRec->fRasterClip.getBounds())) {
1585#if 0
1586 SkDebugf("------- ignored clipRect [%g %g %g %g]\n",
1587 rect.left(), rect.top(), rect.right(), rect.bottom());
1588#endif
1589 return;
1590 }
1591 }
reed2d1afab2016-06-29 14:33:11 -07001592#endif
reedc64eff52015-11-21 12:39:45 -08001593
1594 AutoValidateClip avc(this);
1595
1596 fDeviceCMDirty = true;
1597 fCachedLocalClipBoundsDirty = true;
1598
reed74467162016-06-30 08:15:35 -07001599 if (isScaleTrans) {
reedc64eff52015-11-21 12:39:45 -08001600 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1601 fClipStack->clipDevRect(devR, op, isAA);
senorblancoafc7cce2016-02-02 18:44:15 -08001602 fMCRec->fRasterClip.op(devR, this->getTopLayerBounds(), op, isAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001603 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001604 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001605 // and clip against that, since it can handle any matrix. However, to
1606 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1607 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001608 SkPath path;
1609
1610 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001611 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001612 }
1613}
1614
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001615void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001616 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001617 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001618 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001619 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1620 } else {
1621 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001622 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001623}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001624
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001625void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001626 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001627 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001628 AutoValidateClip avc(this);
1629
1630 fDeviceCMDirty = true;
1631 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001632 if (!fAllowSoftClip) {
1633 edgeStyle = kHard_ClipEdgeStyle;
1634 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001635
reed687fa1c2015-04-07 08:00:56 -07001636 fClipStack->clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001637
senorblancoafc7cce2016-02-02 18:44:15 -08001638 fMCRec->fRasterClip.op(transformedRRect, this->getTopLayerBounds(), op,
robertphillips125f19a2015-11-23 09:00:05 -08001639 kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001640 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001641 }
1642
1643 SkPath path;
1644 path.addRRect(rrect);
1645 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001646 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001647}
1648
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001649void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001650 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001651 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001652
1653 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1654 SkRect r;
1655 if (path.isRect(&r)) {
1656 this->onClipRect(r, op, edgeStyle);
1657 return;
1658 }
1659 SkRRect rrect;
1660 if (path.isOval(&r)) {
1661 rrect.setOval(r);
1662 this->onClipRRect(rrect, op, edgeStyle);
1663 return;
1664 }
1665 if (path.isRRect(&rrect)) {
1666 this->onClipRRect(rrect, op, edgeStyle);
1667 return;
1668 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001669 }
robertphillips39f05382015-11-24 09:30:12 -08001670
1671 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001672}
1673
1674void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001675#ifdef SK_ENABLE_CLIP_QUICKREJECT
1676 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001677 if (fMCRec->fRasterClip.isEmpty()) {
reed2d1afab2016-06-29 14:33:11 -07001678 return;
reed@google.comda17f752012-08-16 18:27:05 +00001679 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001680
reed@google.com3b3e8952012-08-16 20:53:31 +00001681 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001682 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001683 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001684
reed687fa1c2015-04-07 08:00:56 -07001685 fClipStack->clipEmpty();
reed2d1afab2016-06-29 14:33:11 -07001686 (void)fMCRec->fRasterClip.setEmpty();
1687 return;
reed@google.comda17f752012-08-16 18:27:05 +00001688 }
1689 }
1690#endif
1691
reed@google.com5c3d1472011-02-22 19:12:23 +00001692 AutoValidateClip avc(this);
1693
reed@android.com8a1c16f2008-12-17 15:59:43 +00001694 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001695 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001696 if (!fAllowSoftClip) {
1697 edgeStyle = kHard_ClipEdgeStyle;
1698 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001699
1700 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001701 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001702
reed@google.comfe701122011-11-08 19:41:23 +00001703 // Check if the transfomation, or the original path itself
1704 // made us empty. Note this can also happen if we contained NaN
1705 // values. computing the bounds detects this, and will set our
1706 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1707 if (devPath.getBounds().isEmpty()) {
1708 // resetting the path will remove any NaN or other wanky values
1709 // that might upset our scan converter.
1710 devPath.reset();
1711 }
1712
reed@google.com5c3d1472011-02-22 19:12:23 +00001713 // if we called path.swap() we could avoid a deep copy of this path
reed687fa1c2015-04-07 08:00:56 -07001714 fClipStack->clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001715
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001716 if (fAllowSimplifyClip) {
fmalita1a481fe2015-02-04 07:39:34 -08001717 bool clipIsAA = getClipStack()->asPath(&devPath);
1718 if (clipIsAA) {
1719 edgeStyle = kSoft_ClipEdgeStyle;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001720 }
fmalita1a481fe2015-02-04 07:39:34 -08001721
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001722 op = SkRegion::kReplace_Op;
1723 }
1724
senorblancoafc7cce2016-02-02 18:44:15 -08001725 fMCRec->fRasterClip.op(devPath, this->getTopLayerBounds(), op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001726}
1727
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001728void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed2ff1fce2014-12-11 07:07:37 -08001729 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001730 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001731}
1732
1733void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001734 AutoValidateClip avc(this);
1735
reed@android.com8a1c16f2008-12-17 15:59:43 +00001736 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001737 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001738
reed@google.com5c3d1472011-02-22 19:12:23 +00001739 // todo: signal fClipStack that we have a region, and therefore (I guess)
1740 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001741 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001742
reed1f836ee2014-07-07 07:49:34 -07001743 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001744}
1745
reed@google.com819c9212011-02-23 18:56:55 +00001746#ifdef SK_DEBUG
1747void SkCanvas::validateClip() const {
1748 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001749 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001750 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001751 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001752 return;
1753 }
1754
reed@google.com819c9212011-02-23 18:56:55 +00001755 SkIRect ir;
1756 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001757 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001758
reed687fa1c2015-04-07 08:00:56 -07001759 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001760 const SkClipStack::Element* element;
halcanary96fcdcc2015-08-27 07:41:13 -07001761 while ((element = iter.next()) != nullptr) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001762 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001763 case SkClipStack::Element::kRect_Type:
1764 element->getRect().round(&ir);
1765 tmpClip.op(ir, element->getOp());
1766 break;
1767 case SkClipStack::Element::kEmpty_Type:
1768 tmpClip.setEmpty();
1769 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001770 default: {
1771 SkPath path;
1772 element->asPath(&path);
senorblancoafc7cce2016-02-02 18:44:15 -08001773 tmpClip.op(path, this->getTopLayerBounds(), element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001774 break;
1775 }
reed@google.com819c9212011-02-23 18:56:55 +00001776 }
1777 }
reed@google.com819c9212011-02-23 18:56:55 +00001778}
1779#endif
1780
reed@google.com90c07ea2012-04-13 13:50:27 +00001781void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001782 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001783 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001784
halcanary96fcdcc2015-08-27 07:41:13 -07001785 while ((element = iter.next()) != nullptr) {
fmalitac3b589a2014-06-05 12:40:07 -07001786 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001787 }
1788}
1789
reed@google.com5c3d1472011-02-22 19:12:23 +00001790///////////////////////////////////////////////////////////////////////////////
1791
reed@google.com754de5f2014-02-24 19:38:20 +00001792bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001793 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001794}
1795
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001796bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001797 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001798}
1799
reed@google.com3b3e8952012-08-16 20:53:31 +00001800bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001801 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001802 return true;
1803
reed1f836ee2014-07-07 07:49:34 -07001804 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001805 return true;
1806 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001807
reed1f836ee2014-07-07 07:49:34 -07001808 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001809 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001810 fMCRec->fMatrix.mapRect(&dst, rect);
reedb07a94f2014-11-19 05:03:18 -08001811 return !SkIRect::Intersects(dst.roundOut(), fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001812 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001813 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001814
reed@android.coma380ae42009-07-21 01:17:02 +00001815 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001816 // TODO: should we use | instead, or compare all 4 at once?
1817 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001818 return true;
1819 }
reed@google.comc0784db2013-12-13 21:16:12 +00001820 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001821 return true;
1822 }
1823 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001824 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001825}
1826
reed@google.com3b3e8952012-08-16 20:53:31 +00001827bool SkCanvas::quickReject(const SkPath& path) const {
1828 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001829}
1830
reed@google.com3b3e8952012-08-16 20:53:31 +00001831bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001832 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001833 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001834 return false;
1835 }
1836
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001837 SkMatrix inverse;
1838 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001839 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001840 if (bounds) {
1841 bounds->setEmpty();
1842 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001843 return false;
1844 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001845
bsalomon49f085d2014-09-05 13:34:00 -07001846 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001847 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001848 // adjust it outwards in case we are antialiasing
1849 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001850
reed@google.com8f4d2302013-12-17 16:44:46 +00001851 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1852 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001853 inverse.mapRect(bounds, r);
1854 }
1855 return true;
1856}
1857
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001858bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001859 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001860 if (clip.isEmpty()) {
1861 if (bounds) {
1862 bounds->setEmpty();
1863 }
1864 return false;
1865 }
1866
bsalomon49f085d2014-09-05 13:34:00 -07001867 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001868 *bounds = clip.getBounds();
1869 }
1870 return true;
1871}
1872
reed@android.com8a1c16f2008-12-17 15:59:43 +00001873const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001874 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001875}
1876
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001877const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001878 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001879}
1880
robertphillips175dd9b2016-04-28 14:32:04 -07001881GrDrawContext* SkCanvas::internal_private_accessTopLayerDrawContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001882 SkBaseDevice* dev = this->getTopDevice();
robertphillips175dd9b2016-04-28 14:32:04 -07001883 return dev ? dev->accessDrawContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001884}
1885
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001886GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001887 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001888 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001889}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001890
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001891void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1892 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001893 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001894 if (outer.isEmpty()) {
1895 return;
1896 }
1897 if (inner.isEmpty()) {
1898 this->drawRRect(outer, paint);
1899 return;
1900 }
1901
1902 // We don't have this method (yet), but technically this is what we should
1903 // be able to assert...
1904 // SkASSERT(outer.contains(inner));
1905 //
1906 // For now at least check for containment of bounds
1907 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1908
1909 this->onDrawDRRect(outer, inner, paint);
1910}
1911
reed41af9662015-01-05 07:49:08 -08001912// These need to stop being virtual -- clients need to override the onDraw... versions
1913
1914void SkCanvas::drawPaint(const SkPaint& paint) {
1915 this->onDrawPaint(paint);
1916}
1917
1918void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1919 this->onDrawRect(r, paint);
1920}
1921
1922void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1923 this->onDrawOval(r, paint);
1924}
1925
1926void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1927 this->onDrawRRect(rrect, paint);
1928}
1929
1930void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1931 this->onDrawPoints(mode, count, pts, paint);
1932}
1933
1934void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
1935 const SkPoint texs[], const SkColor colors[], SkXfermode* xmode,
1936 const uint16_t indices[], int indexCount, const SkPaint& paint) {
1937 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
1938 indices, indexCount, paint);
1939}
1940
1941void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1942 this->onDrawPath(path, paint);
1943}
1944
reeda85d4d02015-05-06 12:56:48 -07001945void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001946 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001947 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001948}
1949
reede47829b2015-08-06 10:02:53 -07001950void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1951 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001952 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001953 if (dst.isEmpty() || src.isEmpty()) {
1954 return;
1955 }
1956 this->onDrawImageRect(image, &src, dst, paint, constraint);
1957}
reed41af9662015-01-05 07:49:08 -08001958
reed84984ef2015-07-17 07:09:43 -07001959void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1960 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001961 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001962 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001963}
1964
reede47829b2015-08-06 10:02:53 -07001965void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
1966 SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001967 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001968 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
1969 constraint);
1970}
reede47829b2015-08-06 10:02:53 -07001971
reed4c21dc52015-06-25 12:32:03 -07001972void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1973 const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001974 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07001975 if (dst.isEmpty()) {
1976 return;
1977 }
1978 if (!SkNinePatchIter::Valid(image->width(), image->height(), center)) {
reede47829b2015-08-06 10:02:53 -07001979 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001980 }
1981 this->onDrawImageNine(image, center, dst, paint);
1982}
1983
reed41af9662015-01-05 07:49:08 -08001984void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001985 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001986 return;
1987 }
reed41af9662015-01-05 07:49:08 -08001988 this->onDrawBitmap(bitmap, dx, dy, paint);
1989}
1990
reede47829b2015-08-06 10:02:53 -07001991void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07001992 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001993 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07001994 return;
1995 }
reede47829b2015-08-06 10:02:53 -07001996 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08001997}
1998
reed84984ef2015-07-17 07:09:43 -07001999void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2000 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002001 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002002}
2003
reede47829b2015-08-06 10:02:53 -07002004void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2005 SrcRectConstraint constraint) {
2006 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2007 constraint);
2008}
reede47829b2015-08-06 10:02:53 -07002009
reed41af9662015-01-05 07:49:08 -08002010void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2011 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07002012 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002013 return;
2014 }
reed4c21dc52015-06-25 12:32:03 -07002015 if (!SkNinePatchIter::Valid(bitmap.width(), bitmap.height(), center)) {
reeda5517e22015-07-14 10:54:12 -07002016 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002017 }
reed41af9662015-01-05 07:49:08 -08002018 this->onDrawBitmapNine(bitmap, center, dst, paint);
2019}
2020
reed71c3c762015-06-24 10:29:17 -07002021void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2022 const SkColor colors[], int count, SkXfermode::Mode mode,
2023 const SkRect* cull, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002024 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002025 if (count <= 0) {
2026 return;
2027 }
2028 SkASSERT(atlas);
2029 SkASSERT(xform);
2030 SkASSERT(tex);
2031 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
2032}
2033
reedf70b5312016-03-04 16:36:20 -08002034void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2035 if (key) {
2036 this->onDrawAnnotation(rect, key, value);
2037 }
2038}
2039
reede47829b2015-08-06 10:02:53 -07002040void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2041 const SkPaint* paint, SrcRectConstraint constraint) {
2042 if (src) {
2043 this->drawImageRect(image, *src, dst, paint, constraint);
2044 } else {
2045 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2046 dst, paint, constraint);
2047 }
2048}
2049void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2050 const SkPaint* paint, SrcRectConstraint constraint) {
2051 if (src) {
2052 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2053 } else {
2054 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2055 dst, paint, constraint);
2056 }
2057}
2058
tomhudsoncb3bd182016-05-18 07:24:16 -07002059void SkCanvas::temporary_internal_describeTopLayer(SkMatrix* matrix, SkIRect* clip_bounds) {
2060 SkIRect layer_bounds = this->getTopLayerBounds();
2061 if (matrix) {
2062 *matrix = this->getTotalMatrix();
2063 matrix->preTranslate(-layer_bounds.left(), -layer_bounds.top());
2064 }
2065 if (clip_bounds) {
2066 this->getClipDeviceBounds(clip_bounds);
2067 clip_bounds->offset(-layer_bounds.left(), -layer_bounds.top());
2068 }
2069}
2070
reed@android.com8a1c16f2008-12-17 15:59:43 +00002071//////////////////////////////////////////////////////////////////////////////
2072// These are the virtual drawing methods
2073//////////////////////////////////////////////////////////////////////////////
2074
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002075void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002076 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002077 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2078 }
2079}
2080
reed41af9662015-01-05 07:49:08 -08002081void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002082 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002083 this->internalDrawPaint(paint);
2084}
2085
2086void SkCanvas::internalDrawPaint(const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002087 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002088
2089 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002090 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002091 }
2092
reed@google.com4e2b3d32011-04-07 14:18:59 +00002093 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002094}
2095
reed41af9662015-01-05 07:49:08 -08002096void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2097 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002098 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002099 if ((long)count <= 0) {
2100 return;
2101 }
2102
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002103 SkRect r, storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002104 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002105 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002106 // special-case 2 points (common for drawing a single line)
2107 if (2 == count) {
2108 r.set(pts[0], pts[1]);
2109 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00002110 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002111 }
senorblanco87e066e2015-10-28 11:23:36 -07002112 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2113 return;
2114 }
2115 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002116 }
reed@google.coma584aed2012-05-16 14:06:02 +00002117
halcanary96fcdcc2015-08-27 07:41:13 -07002118 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002119
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002120 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002121
reed@android.com8a1c16f2008-12-17 15:59:43 +00002122 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002123 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002124 }
reed@google.com4b226022011-01-11 18:32:13 +00002125
reed@google.com4e2b3d32011-04-07 14:18:59 +00002126 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002127}
2128
reed41af9662015-01-05 07:49:08 -08002129void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002130 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002131 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002132 const SkRect* bounds = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002133 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08002134 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
2135 // To prevent accidental rejecting at this stage, we have to sort it before we check.
2136 SkRect tmp(r);
2137 tmp.sort();
2138
senorblanco87e066e2015-10-28 11:23:36 -07002139 if (this->quickReject(paint.computeFastBounds(tmp, &storage))) {
2140 return;
2141 }
2142 bounds = &r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002143 }
reed@google.com4b226022011-01-11 18:32:13 +00002144
reedc83a2972015-07-16 07:40:45 -07002145 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002146
2147 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002148 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002149 }
2150
reed@google.com4e2b3d32011-04-07 14:18:59 +00002151 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002152}
2153
reed41af9662015-01-05 07:49:08 -08002154void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002155 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002156 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002157 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002158 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002159 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2160 return;
2161 }
2162 bounds = &oval;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002163 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002164
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002165 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002166
2167 while (iter.next()) {
2168 iter.fDevice->drawOval(iter, oval, looper.paint());
2169 }
2170
2171 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002172}
2173
reed41af9662015-01-05 07:49:08 -08002174void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002175 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002176 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002177 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002178 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002179 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2180 return;
2181 }
2182 bounds = &rrect.getBounds();
reed@google.com4ed0fb72012-12-12 20:48:18 +00002183 }
2184
2185 if (rrect.isRect()) {
2186 // call the non-virtual version
2187 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002188 return;
2189 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002190 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002191 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2192 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002193 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002194
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002195 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002196
2197 while (iter.next()) {
2198 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2199 }
2200
2201 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002202}
2203
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002204void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2205 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002206 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002207 const SkRect* bounds = nullptr;
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002208 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002209 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2210 return;
2211 }
2212 bounds = &outer.getBounds();
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002213 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002214
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002215 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002216
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002217 while (iter.next()) {
2218 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2219 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002220
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002221 LOOPER_END
2222}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002223
reed41af9662015-01-05 07:49:08 -08002224void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002225 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002226 if (!path.isFinite()) {
2227 return;
2228 }
2229
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002230 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002231 const SkRect* bounds = nullptr;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002232 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002233 const SkRect& pathBounds = path.getBounds();
senorblanco87e066e2015-10-28 11:23:36 -07002234 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2235 return;
2236 }
2237 bounds = &pathBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002238 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002239
2240 const SkRect& r = path.getBounds();
2241 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002242 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002243 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002244 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002245 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002246 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002247
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002248 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002249
2250 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002251 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002252 }
2253
reed@google.com4e2b3d32011-04-07 14:18:59 +00002254 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002255}
2256
reed262a71b2015-12-05 13:07:27 -08002257bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002258 if (!paint.getImageFilter()) {
2259 return false;
2260 }
2261
2262 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002263 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002264 return false;
2265 }
2266
2267 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2268 // Once we can filter and the filter will return a result larger than itself, we should be
2269 // able to remove this constraint.
2270 // skbug.com/4526
2271 //
2272 SkPoint pt;
2273 ctm.mapXY(x, y, &pt);
2274 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2275 return ir.contains(fMCRec->fRasterClip.getBounds());
2276}
2277
reeda85d4d02015-05-06 12:56:48 -07002278void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002279 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002280 SkRect bounds = SkRect::MakeXYWH(x, y,
2281 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002282 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002283 SkRect tmp = bounds;
2284 if (paint) {
2285 paint->computeFastBounds(tmp, &tmp);
2286 }
2287 if (this->quickReject(tmp)) {
2288 return;
2289 }
reeda85d4d02015-05-06 12:56:48 -07002290 }
halcanary9d524f22016-03-29 09:03:52 -07002291
reeda85d4d02015-05-06 12:56:48 -07002292 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002293 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002294 paint = lazy.init();
2295 }
reed262a71b2015-12-05 13:07:27 -08002296
reed129ed1c2016-02-22 06:42:31 -08002297 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2298 *paint);
2299 if (drawAsSprite && paint->getImageFilter()) {
2300 SkBitmap bitmap;
2301 if (!as_IB(image)->asBitmapForImageFilters(&bitmap)) {
2302 drawAsSprite = false;
2303 } else{
2304 // Until imagefilters are updated, they cannot handle any src type but N32...
reeddabe5d32016-06-21 10:28:14 -07002305 if (bitmap.info().colorType() != kN32_SkColorType || bitmap.info().gammaCloseToSRGB()) {
reed129ed1c2016-02-22 06:42:31 -08002306 drawAsSprite = false;
2307 }
2308 }
2309 }
2310
reed262a71b2015-12-05 13:07:27 -08002311 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
2312
reeda85d4d02015-05-06 12:56:48 -07002313 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002314 const SkPaint& pnt = looper.paint();
2315 if (drawAsSprite && pnt.getImageFilter()) {
2316 SkBitmap bitmap;
2317 if (as_IB(image)->asBitmapForImageFilters(&bitmap)) {
2318 SkPoint pt;
2319 iter.fMatrix->mapXY(x, y, &pt);
robertphillips2302de92016-03-24 07:26:32 -07002320 iter.fDevice->drawSpriteWithFilter(iter, bitmap,
2321 SkScalarRoundToInt(pt.fX),
2322 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002323 }
2324 } else {
2325 iter.fDevice->drawImage(iter, image, x, y, pnt);
2326 }
reeda85d4d02015-05-06 12:56:48 -07002327 }
halcanary9d524f22016-03-29 09:03:52 -07002328
reeda85d4d02015-05-06 12:56:48 -07002329 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002330}
2331
reed41af9662015-01-05 07:49:08 -08002332void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002333 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002334 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
halcanary96fcdcc2015-08-27 07:41:13 -07002335 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002336 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002337 if (paint) {
2338 paint->computeFastBounds(dst, &storage);
2339 }
2340 if (this->quickReject(storage)) {
2341 return;
2342 }
reeda85d4d02015-05-06 12:56:48 -07002343 }
2344 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002345 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002346 paint = lazy.init();
2347 }
halcanary9d524f22016-03-29 09:03:52 -07002348
senorblancoc41e7e12015-12-07 12:51:30 -08002349 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002350 image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002351
reeda85d4d02015-05-06 12:56:48 -07002352 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002353 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002354 }
halcanary9d524f22016-03-29 09:03:52 -07002355
reeda85d4d02015-05-06 12:56:48 -07002356 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002357}
2358
reed41af9662015-01-05 07:49:08 -08002359void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002360 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002361 SkDEBUGCODE(bitmap.validate();)
2362
reed33366972015-10-08 09:22:02 -07002363 if (bitmap.drawsNothing()) {
2364 return;
2365 }
2366
2367 SkLazyPaint lazy;
2368 if (nullptr == paint) {
2369 paint = lazy.init();
2370 }
2371
2372 const SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2373
2374 SkRect storage;
2375 const SkRect* bounds = nullptr;
2376 if (paint->canComputeFastBounds()) {
2377 bitmap.getBounds(&storage);
2378 matrix.mapRect(&storage);
senorblanco87e066e2015-10-28 11:23:36 -07002379 SkRect tmp = storage;
2380 if (this->quickReject(paint->computeFastBounds(tmp, &tmp))) {
2381 return;
2382 }
2383 bounds = &storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002384 }
reed@google.com4b226022011-01-11 18:32:13 +00002385
reed129ed1c2016-02-22 06:42:31 -08002386 bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(), bitmap.height(),
2387 *paint);
2388 if (drawAsSprite && paint->getImageFilter()) {
2389 // Until imagefilters are updated, they cannot handle any src type but N32...
reeddabe5d32016-06-21 10:28:14 -07002390 if (bitmap.info().colorType() != kN32_SkColorType || bitmap.info().gammaCloseToSRGB()) {
reed129ed1c2016-02-22 06:42:31 -08002391 drawAsSprite = false;
2392 }
2393 }
2394
reed262a71b2015-12-05 13:07:27 -08002395 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds)
reed33366972015-10-08 09:22:02 -07002396
2397 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002398 const SkPaint& pnt = looper.paint();
2399 if (drawAsSprite && pnt.getImageFilter()) {
2400 SkPoint pt;
2401 iter.fMatrix->mapXY(x, y, &pt);
robertphillips2302de92016-03-24 07:26:32 -07002402 iter.fDevice->drawSpriteWithFilter(iter, bitmap,
2403 SkScalarRoundToInt(pt.fX),
2404 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002405 } else {
2406 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
2407 }
reed33366972015-10-08 09:22:02 -07002408 }
reed262a71b2015-12-05 13:07:27 -08002409
reed33366972015-10-08 09:22:02 -07002410 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002411}
2412
reed@google.com9987ec32011-09-07 11:57:52 +00002413// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002414void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002415 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002416 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002417 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002418 return;
2419 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002420
halcanary96fcdcc2015-08-27 07:41:13 -07002421 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002422 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002423 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2424 return;
2425 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002426 }
reed@google.com3d608122011-11-21 15:16:16 +00002427
reed@google.com33535f32012-09-25 15:37:50 +00002428 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002429 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002430 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002431 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002432
senorblancoc41e7e12015-12-07 12:51:30 -08002433 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002434 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002435
reed@google.com33535f32012-09-25 15:37:50 +00002436 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002437 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002438 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002439
reed@google.com33535f32012-09-25 15:37:50 +00002440 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002441}
2442
reed41af9662015-01-05 07:49:08 -08002443void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002444 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002445 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002446 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002447 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002448}
2449
reed4c21dc52015-06-25 12:32:03 -07002450void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2451 const SkPaint* paint) {
2452 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
halcanary9d524f22016-03-29 09:03:52 -07002453
halcanary96fcdcc2015-08-27 07:41:13 -07002454 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002455 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002456 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2457 return;
2458 }
reed@google.com3d608122011-11-21 15:16:16 +00002459 }
halcanary9d524f22016-03-29 09:03:52 -07002460
reed4c21dc52015-06-25 12:32:03 -07002461 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002462 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002463 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002464 }
halcanary9d524f22016-03-29 09:03:52 -07002465
senorblancoc41e7e12015-12-07 12:51:30 -08002466 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002467
reed4c21dc52015-06-25 12:32:03 -07002468 while (iter.next()) {
2469 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002470 }
halcanary9d524f22016-03-29 09:03:52 -07002471
reed4c21dc52015-06-25 12:32:03 -07002472 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002473}
2474
reed41af9662015-01-05 07:49:08 -08002475void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2476 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002477 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002478 SkDEBUGCODE(bitmap.validate();)
2479
halcanary96fcdcc2015-08-27 07:41:13 -07002480 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002481 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002482 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2483 return;
2484 }
reed4c21dc52015-06-25 12:32:03 -07002485 }
halcanary9d524f22016-03-29 09:03:52 -07002486
reed4c21dc52015-06-25 12:32:03 -07002487 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002488 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002489 paint = lazy.init();
2490 }
halcanary9d524f22016-03-29 09:03:52 -07002491
senorblancoc41e7e12015-12-07 12:51:30 -08002492 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002493
reed4c21dc52015-06-25 12:32:03 -07002494 while (iter.next()) {
2495 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2496 }
halcanary9d524f22016-03-29 09:03:52 -07002497
reed4c21dc52015-06-25 12:32:03 -07002498 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002499}
2500
reed@google.comf67e4cf2011-03-15 20:56:58 +00002501class SkDeviceFilteredPaint {
2502public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002503 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002504 uint32_t filteredFlags = device->filterTextFlags(paint);
2505 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002506 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002507 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002508 fPaint = newPaint;
2509 } else {
2510 fPaint = &paint;
2511 }
2512 }
2513
reed@google.comf67e4cf2011-03-15 20:56:58 +00002514 const SkPaint& paint() const { return *fPaint; }
2515
2516private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002517 const SkPaint* fPaint;
2518 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002519};
2520
bungeman@google.com52c748b2011-08-22 21:30:43 +00002521void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2522 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002523 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002524 draw.fDevice->drawRect(draw, r, paint);
2525 } else {
2526 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002527 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002528 draw.fDevice->drawRect(draw, r, p);
2529 }
2530}
2531
2532void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2533 const char text[], size_t byteLength,
2534 SkScalar x, SkScalar y) {
halcanary96fcdcc2015-08-27 07:41:13 -07002535 SkASSERT(byteLength == 0 || text != nullptr);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002536
2537 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07002538 if (text == nullptr || byteLength == 0 ||
reed1e7f5e72016-04-27 07:49:17 -07002539 draw.fRC->isEmpty() ||
halcanary96fcdcc2015-08-27 07:41:13 -07002540 (paint.getAlpha() == 0 && paint.getXfermode() == nullptr)) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002541 return;
2542 }
2543
2544 SkScalar width = 0;
2545 SkPoint start;
2546
2547 start.set(0, 0); // to avoid warning
2548 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2549 SkPaint::kStrikeThruText_Flag)) {
2550 width = paint.measureText(text, byteLength);
2551
2552 SkScalar offsetX = 0;
2553 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2554 offsetX = SkScalarHalf(width);
2555 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2556 offsetX = width;
2557 }
2558 start.set(x - offsetX, y);
2559 }
2560
2561 if (0 == width) {
2562 return;
2563 }
2564
2565 uint32_t flags = paint.getFlags();
2566
2567 if (flags & (SkPaint::kUnderlineText_Flag |
2568 SkPaint::kStrikeThruText_Flag)) {
2569 SkScalar textSize = paint.getTextSize();
2570 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2571 SkRect r;
2572
2573 r.fLeft = start.fX;
2574 r.fRight = start.fX + width;
2575
2576 if (flags & SkPaint::kUnderlineText_Flag) {
2577 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2578 start.fY);
2579 r.fTop = offset;
2580 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002581 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002582 }
2583 if (flags & SkPaint::kStrikeThruText_Flag) {
2584 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2585 start.fY);
2586 r.fTop = offset;
2587 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002588 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002589 }
2590 }
2591}
2592
reed@google.come0d9ce82014-04-23 04:00:17 +00002593void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2594 const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002595 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002596
2597 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002598 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002599 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002600 DrawTextDecorations(iter, dfp.paint(),
2601 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002602 }
2603
reed@google.com4e2b3d32011-04-07 14:18:59 +00002604 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002605}
2606
reed@google.come0d9ce82014-04-23 04:00:17 +00002607void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2608 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002609 SkPoint textOffset = SkPoint::Make(0, 0);
2610
halcanary96fcdcc2015-08-27 07:41:13 -07002611 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002612
reed@android.com8a1c16f2008-12-17 15:59:43 +00002613 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002614 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002615 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002616 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002617 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002618
reed@google.com4e2b3d32011-04-07 14:18:59 +00002619 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002620}
2621
reed@google.come0d9ce82014-04-23 04:00:17 +00002622void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2623 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002624
2625 SkPoint textOffset = SkPoint::Make(0, constY);
2626
halcanary96fcdcc2015-08-27 07:41:13 -07002627 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002628
reed@android.com8a1c16f2008-12-17 15:59:43 +00002629 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002630 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002631 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002632 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002633 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002634
reed@google.com4e2b3d32011-04-07 14:18:59 +00002635 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002636}
2637
reed@google.come0d9ce82014-04-23 04:00:17 +00002638void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2639 const SkMatrix* matrix, const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002640 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002641
reed@android.com8a1c16f2008-12-17 15:59:43 +00002642 while (iter.next()) {
2643 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002644 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002645 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002646
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002647 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002648}
2649
reed45561a02016-07-07 12:47:17 -07002650void SkCanvas::onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2651 const SkRect* cullRect, const SkPaint& paint) {
2652 if (cullRect && this->quickReject(*cullRect)) {
2653 return;
2654 }
2655
2656 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
2657
2658 while (iter.next()) {
2659 iter.fDevice->drawTextRSXform(iter, text, byteLength, xform, looper.paint());
2660 }
2661
2662 LOOPER_END
2663}
2664
fmalita00d5c2c2014-08-21 08:53:26 -07002665void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2666 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002667
fmalita85d5eb92015-03-04 11:20:12 -08002668 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002669 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002670 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002671 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002672 SkRect tmp;
2673 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2674 return;
2675 }
2676 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002677 }
2678
fmalita024f9962015-03-03 19:08:17 -08002679 // We cannot filter in the looper as we normally do, because the paint is
2680 // incomplete at this point (text-related attributes are embedded within blob run paints).
2681 SkDrawFilter* drawFilter = fMCRec->fFilter;
halcanary96fcdcc2015-08-27 07:41:13 -07002682 fMCRec->fFilter = nullptr;
fmalita024f9962015-03-03 19:08:17 -08002683
fmalita85d5eb92015-03-04 11:20:12 -08002684 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002685
fmalitaaa1b9122014-08-28 14:32:24 -07002686 while (iter.next()) {
2687 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002688 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002689 }
2690
fmalitaaa1b9122014-08-28 14:32:24 -07002691 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002692
2693 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002694}
2695
reed@google.come0d9ce82014-04-23 04:00:17 +00002696// These will become non-virtual, so they always call the (virtual) onDraw... method
2697void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2698 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002699 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002700 this->onDrawText(text, byteLength, x, y, paint);
2701}
2702void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2703 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002704 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002705 this->onDrawPosText(text, byteLength, pos, paint);
2706}
2707void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2708 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002709 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002710 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2711}
2712void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2713 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002714 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002715 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2716}
reed45561a02016-07-07 12:47:17 -07002717void SkCanvas::drawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2718 const SkRect* cullRect, const SkPaint& paint) {
2719 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextRSXform()");
2720 if (byteLength) {
2721 this->onDrawTextRSXform(text, byteLength, xform, cullRect, paint);
2722 }
2723}
fmalita00d5c2c2014-08-21 08:53:26 -07002724void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2725 const SkPaint& paint) {
reede3b38ce2016-01-08 09:18:44 -08002726 RETURN_ON_NULL(blob);
danakj9881d632014-11-26 12:41:06 -08002727 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
reede3b38ce2016-01-08 09:18:44 -08002728 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002729}
reed@google.come0d9ce82014-04-23 04:00:17 +00002730
reed41af9662015-01-05 07:49:08 -08002731void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2732 const SkPoint verts[], const SkPoint texs[],
2733 const SkColor colors[], SkXfermode* xmode,
2734 const uint16_t indices[], int indexCount,
2735 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002736 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
halcanary96fcdcc2015-08-27 07:41:13 -07002737 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
reed@google.com4b226022011-01-11 18:32:13 +00002738
reed@android.com8a1c16f2008-12-17 15:59:43 +00002739 while (iter.next()) {
2740 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002741 colors, xmode, indices, indexCount,
2742 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002743 }
reed@google.com4b226022011-01-11 18:32:13 +00002744
reed@google.com4e2b3d32011-04-07 14:18:59 +00002745 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002746}
2747
dandovb3c9d1c2014-08-12 08:34:29 -07002748void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2749 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002750 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
halcanary96fcdcc2015-08-27 07:41:13 -07002751 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002752 return;
2753 }
mtklein6cfa73a2014-08-13 13:33:49 -07002754
dandovecfff212014-08-04 10:02:00 -07002755 // Since a patch is always within the convex hull of the control points, we discard it when its
2756 // bounding rectangle is completely outside the current clip.
2757 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002758 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002759 if (this->quickReject(bounds)) {
2760 return;
2761 }
mtklein6cfa73a2014-08-13 13:33:49 -07002762
dandovb3c9d1c2014-08-12 08:34:29 -07002763 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2764}
2765
2766void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2767 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2768
halcanary96fcdcc2015-08-27 07:41:13 -07002769 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002770
dandovecfff212014-08-04 10:02:00 -07002771 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002772 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002773 }
mtklein6cfa73a2014-08-13 13:33:49 -07002774
dandovecfff212014-08-04 10:02:00 -07002775 LOOPER_END
2776}
2777
reeda8db7282015-07-07 10:22:31 -07002778void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
reede3b38ce2016-01-08 09:18:44 -08002779 RETURN_ON_NULL(dr);
2780 if (x || y) {
2781 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2782 this->onDrawDrawable(dr, &matrix);
2783 } else {
2784 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002785 }
2786}
2787
reeda8db7282015-07-07 10:22:31 -07002788void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reede3b38ce2016-01-08 09:18:44 -08002789 RETURN_ON_NULL(dr);
2790 if (matrix && matrix->isIdentity()) {
2791 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002792 }
reede3b38ce2016-01-08 09:18:44 -08002793 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002794}
2795
2796void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2797 SkRect bounds = dr->getBounds();
2798 if (matrix) {
2799 matrix->mapRect(&bounds);
2800 }
2801 if (this->quickReject(bounds)) {
2802 return;
2803 }
2804 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002805}
2806
reed71c3c762015-06-24 10:29:17 -07002807void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2808 const SkColor colors[], int count, SkXfermode::Mode mode,
2809 const SkRect* cull, const SkPaint* paint) {
2810 if (cull && this->quickReject(*cull)) {
2811 return;
2812 }
2813
2814 SkPaint pnt;
2815 if (paint) {
2816 pnt = *paint;
2817 }
halcanary9d524f22016-03-29 09:03:52 -07002818
halcanary96fcdcc2015-08-27 07:41:13 -07002819 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr)
reed71c3c762015-06-24 10:29:17 -07002820 while (iter.next()) {
2821 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, mode, pnt);
2822 }
2823 LOOPER_END
2824}
2825
reedf70b5312016-03-04 16:36:20 -08002826void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2827 SkASSERT(key);
2828
2829 SkPaint paint;
2830 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, nullptr)
2831 while (iter.next()) {
2832 iter.fDevice->drawAnnotation(iter, rect, key, value);
2833 }
2834 LOOPER_END
2835}
2836
reed@android.com8a1c16f2008-12-17 15:59:43 +00002837//////////////////////////////////////////////////////////////////////////////
2838// These methods are NOT virtual, and therefore must call back into virtual
2839// methods, rather than actually drawing themselves.
2840//////////////////////////////////////////////////////////////////////////////
2841
2842void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002843 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002844 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002845 SkPaint paint;
2846
2847 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002848 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002849 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002850 }
2851 this->drawPaint(paint);
2852}
2853
reed@android.com845fdac2009-06-23 03:01:32 +00002854void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002855 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002856 SkPaint paint;
2857
2858 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002859 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002860 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002861 }
2862 this->drawPaint(paint);
2863}
2864
2865void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002866 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002867 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002868
reed@android.com8a1c16f2008-12-17 15:59:43 +00002869 pt.set(x, y);
2870 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2871}
2872
2873void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002874 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002875 SkPoint pt;
2876 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002877
reed@android.com8a1c16f2008-12-17 15:59:43 +00002878 pt.set(x, y);
2879 paint.setColor(color);
2880 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2881}
2882
2883void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2884 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002885 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002886 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002887
reed@android.com8a1c16f2008-12-17 15:59:43 +00002888 pts[0].set(x0, y0);
2889 pts[1].set(x1, y1);
2890 this->drawPoints(kLines_PointMode, 2, pts, paint);
2891}
2892
2893void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2894 SkScalar right, SkScalar bottom,
2895 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002896 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002897 SkRect r;
2898
2899 r.set(left, top, right, bottom);
2900 this->drawRect(r, paint);
2901}
2902
2903void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2904 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002905 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002906 if (radius < 0) {
2907 radius = 0;
2908 }
2909
2910 SkRect r;
2911 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002912 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002913}
2914
2915void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2916 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002917 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002918 if (rx > 0 && ry > 0) {
2919 if (paint.canComputeFastBounds()) {
2920 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002921 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002922 return;
2923 }
2924 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002925 SkRRect rrect;
2926 rrect.setRectXY(r, rx, ry);
2927 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002928 } else {
2929 this->drawRect(r, paint);
2930 }
2931}
2932
reed@android.com8a1c16f2008-12-17 15:59:43 +00002933void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2934 SkScalar sweepAngle, bool useCenter,
2935 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002936 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002937 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2938 this->drawOval(oval, paint);
2939 } else {
2940 SkPath path;
2941 if (useCenter) {
2942 path.moveTo(oval.centerX(), oval.centerY());
2943 }
2944 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2945 if (useCenter) {
2946 path.close();
2947 }
2948 this->drawPath(path, paint);
2949 }
2950}
2951
2952void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2953 const SkPath& path, SkScalar hOffset,
2954 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002955 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002956 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002957
reed@android.com8a1c16f2008-12-17 15:59:43 +00002958 matrix.setTranslate(hOffset, vOffset);
2959 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2960}
2961
reed@android.comf76bacf2009-05-13 14:00:33 +00002962///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07002963
2964/**
2965 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2966 * against the playback cost of recursing into the subpicture to get at its actual ops.
2967 *
2968 * For now we pick a conservatively small value, though measurement (and other heuristics like
2969 * the type of ops contained) may justify changing this value.
2970 */
2971#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07002972
reedd5fa1a42014-08-09 11:08:05 -07002973void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002974 RETURN_ON_NULL(picture);
2975
reed1c2c4412015-04-30 13:09:24 -07002976 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
reede3b38ce2016-01-08 09:18:44 -08002977 if (matrix && matrix->isIdentity()) {
2978 matrix = nullptr;
2979 }
2980 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2981 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2982 picture->playback(this);
2983 } else {
2984 this->onDrawPicture(picture, matrix, paint);
reedd5fa1a42014-08-09 11:08:05 -07002985 }
2986}
robertphillips9b14f262014-06-04 05:40:44 -07002987
reedd5fa1a42014-08-09 11:08:05 -07002988void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2989 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07002990 if (!paint || paint->canComputeFastBounds()) {
2991 SkRect bounds = picture->cullRect();
2992 if (paint) {
2993 paint->computeFastBounds(bounds, &bounds);
2994 }
2995 if (matrix) {
2996 matrix->mapRect(&bounds);
2997 }
2998 if (this->quickReject(bounds)) {
2999 return;
3000 }
3001 }
3002
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00003003 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07003004 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00003005 // Canvas has to first give the device the opportunity to render
3006 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07003007 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00003008 return; // the device has rendered the entire picture
3009 }
3010 }
3011
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003012 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07003013 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003014}
3015
reed@android.com8a1c16f2008-12-17 15:59:43 +00003016///////////////////////////////////////////////////////////////////////////////
3017///////////////////////////////////////////////////////////////////////////////
3018
3019SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
bungeman99fe8222015-08-20 07:57:51 -07003020 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003021
3022 SkASSERT(canvas);
3023
3024 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
3025 fDone = !fImpl->next();
3026}
3027
3028SkCanvas::LayerIter::~LayerIter() {
3029 fImpl->~SkDrawIter();
3030}
3031
3032void SkCanvas::LayerIter::next() {
3033 fDone = !fImpl->next();
3034}
3035
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003036SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003037 return fImpl->getDevice();
3038}
3039
3040const SkMatrix& SkCanvas::LayerIter::matrix() const {
3041 return fImpl->getMatrix();
3042}
3043
3044const SkPaint& SkCanvas::LayerIter::paint() const {
3045 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003046 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003047 paint = &fDefaultPaint;
3048 }
3049 return *paint;
3050}
3051
reed1e7f5e72016-04-27 07:49:17 -07003052const SkRasterClip& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +00003053int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
3054int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003055
3056///////////////////////////////////////////////////////////////////////////////
3057
fmalitac3b589a2014-06-05 12:40:07 -07003058SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003059
3060///////////////////////////////////////////////////////////////////////////////
3061
3062static bool supported_for_raster_canvas(const SkImageInfo& info) {
3063 switch (info.alphaType()) {
3064 case kPremul_SkAlphaType:
3065 case kOpaque_SkAlphaType:
3066 break;
3067 default:
3068 return false;
3069 }
3070
3071 switch (info.colorType()) {
3072 case kAlpha_8_SkColorType:
3073 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00003074 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003075 break;
3076 default:
3077 return false;
3078 }
3079
3080 return true;
3081}
3082
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003083SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
3084 if (!supported_for_raster_canvas(info)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003085 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003086 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003087
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003088 SkBitmap bitmap;
3089 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003090 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003091 }
halcanary385fe4d2015-08-26 13:07:48 -07003092 return new SkCanvas(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003093}
reedd5fa1a42014-08-09 11:08:05 -07003094
3095///////////////////////////////////////////////////////////////////////////////
3096
3097SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003098 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07003099 : fCanvas(canvas)
3100 , fSaveCount(canvas->getSaveCount())
3101{
bsalomon49f085d2014-09-05 13:34:00 -07003102 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003103 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07003104 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003105 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07003106 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003107 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07003108 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003109 canvas->save();
3110 }
mtklein6cfa73a2014-08-13 13:33:49 -07003111
bsalomon49f085d2014-09-05 13:34:00 -07003112 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003113 canvas->concat(*matrix);
3114 }
3115}
3116
3117SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
3118 fCanvas->restoreToCount(fSaveCount);
3119}
reede8f30622016-03-23 18:59:25 -07003120
3121#ifdef SK_SUPPORT_LEGACY_NEW_SURFACE_API
3122SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
3123 return this->makeSurface(info, props).release();
3124}
3125#endif