blob: 895d36fe8c57d696a1effcff290fcd7608ed1a0d [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00003 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00004 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00006 */
7
bungemand3ebb482015-08-05 13:57:49 -07008#include "SkBitmapDevice.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +00009#include "SkCanvas.h"
reedd5fa1a42014-08-09 11:08:05 -070010#include "SkCanvasPriv.h"
bungemand3ebb482015-08-05 13:57:49 -070011#include "SkClipStack.h"
reeddbc3cef2015-04-29 12:18:57 -070012#include "SkColorFilter.h"
bungemand3ebb482015-08-05 13:57:49 -070013#include "SkDevice.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000014#include "SkDraw.h"
reed3cb38402015-02-06 08:36:15 -080015#include "SkDrawable.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000016#include "SkDrawFilter.h"
17#include "SkDrawLooper.h"
joshualitt5f5a8d72015-02-25 14:09:45 -080018#include "SkErrorInternals.h"
piotaixrb5fae932014-09-24 13:03:30 -070019#include "SkImage.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000020#include "SkMetaData.h"
reed4c21dc52015-06-25 12:32:03 -070021#include "SkNinePatchIter.h"
reedc83a2972015-07-16 07:40:45 -070022#include "SkPaintPriv.h"
dandovb3c9d1c2014-08-12 08:34:29 -070023#include "SkPatchUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000024#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000025#include "SkRasterClip.h"
reed96472de2014-12-10 09:53:42 -080026#include "SkReadPixelsRec.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000027#include "SkRRect.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000028#include "SkSmallAllocator.h"
reed@google.com97af1a62012-08-28 12:19:02 +000029#include "SkSurface_Base.h"
fmalita7ba7aa72014-08-29 09:46:36 -070030#include "SkTextBlob.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000031#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000032#include "SkTLazy.h"
danakj8f757f52014-11-04 11:48:43 -080033#include "SkTraceEvent.h"
bungemand3ebb482015-08-05 13:57:49 -070034
35#include <new>
reed@android.com8a1c16f2008-12-17 15:59:43 +000036
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000037#if SK_SUPPORT_GPU
38#include "GrRenderTarget.h"
39#endif
40
senorblanco87e066e2015-10-28 11:23:36 -070041#define SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
senorblanco87e066e2015-10-28 11:23:36 -070042
reedc83a2972015-07-16 07:40:45 -070043/*
44 * Return true if the drawing this rect would hit every pixels in the canvas.
45 *
46 * Returns false if
47 * - rect does not contain the canvas' bounds
48 * - paint is not fill
49 * - paint would blur or otherwise change the coverage of the rect
50 */
51bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
52 ShaderOverrideOpacity overrideOpacity) const {
bungeman99fe8222015-08-20 07:57:51 -070053 static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
54 (int)kNone_ShaderOverrideOpacity,
55 "need_matching_enums0");
56 static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
57 (int)kOpaque_ShaderOverrideOpacity,
58 "need_matching_enums1");
59 static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
60 (int)kNotOpaque_ShaderOverrideOpacity,
61 "need_matching_enums2");
reedc83a2972015-07-16 07:40:45 -070062
63 const SkISize size = this->getBaseLayerSize();
64 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
65 if (!this->getClipStack()->quickContains(bounds)) {
66 return false;
67 }
68
69 if (rect) {
70 if (!this->getTotalMatrix().rectStaysRect()) {
71 return false; // conservative
72 }
73
74 SkRect devRect;
75 this->getTotalMatrix().mapRect(&devRect, *rect);
fmalita8c0144c2015-07-22 05:56:16 -070076 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -070077 return false;
78 }
79 }
80
81 if (paint) {
82 SkPaint::Style paintStyle = paint->getStyle();
83 if (!(paintStyle == SkPaint::kFill_Style ||
84 paintStyle == SkPaint::kStrokeAndFill_Style)) {
85 return false;
86 }
87 if (paint->getMaskFilter() || paint->getLooper()
88 || paint->getPathEffect() || paint->getImageFilter()) {
89 return false; // conservative
90 }
91 }
92 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
93}
94
95///////////////////////////////////////////////////////////////////////////////////////////////////
96
reedd990e2f2014-12-22 11:58:30 -080097static bool gIgnoreSaveLayerBounds;
98void SkCanvas::Internal_Private_SetIgnoreSaveLayerBounds(bool ignore) {
99 gIgnoreSaveLayerBounds = ignore;
100}
101bool SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds() {
102 return gIgnoreSaveLayerBounds;
103}
104
reed0acf1b42014-12-22 16:12:38 -0800105static bool gTreatSpriteAsBitmap;
106void SkCanvas::Internal_Private_SetTreatSpriteAsBitmap(bool spriteAsBitmap) {
107 gTreatSpriteAsBitmap = spriteAsBitmap;
108}
109bool SkCanvas::Internal_Private_GetTreatSpriteAsBitmap() {
110 return gTreatSpriteAsBitmap;
111}
112
reed@google.comda17f752012-08-16 18:27:05 +0000113// experimental for faster tiled drawing...
114//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +0000115
reed@android.com8a1c16f2008-12-17 15:59:43 +0000116//#define SK_TRACE_SAVERESTORE
117
118#ifdef SK_TRACE_SAVERESTORE
119 static int gLayerCounter;
120 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
121 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
122
123 static int gRecCounter;
124 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
125 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
126
127 static int gCanvasCounter;
128 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
129 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
130#else
131 #define inc_layer()
132 #define dec_layer()
133 #define inc_rec()
134 #define dec_rec()
135 #define inc_canvas()
136 #define dec_canvas()
137#endif
138
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000139typedef SkTLazy<SkPaint> SkLazyPaint;
140
reedc83a2972015-07-16 07:40:45 -0700141void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000142 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700143 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
144 ? SkSurface::kDiscard_ContentChangeMode
145 : SkSurface::kRetain_ContentChangeMode);
146 }
147}
148
149void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
150 ShaderOverrideOpacity overrideOpacity) {
151 if (fSurfaceBase) {
152 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
153 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
154 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
155 // and therefore we don't care which mode we're in.
156 //
157 if (fSurfaceBase->outstandingImageSnapshot()) {
158 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
159 mode = SkSurface::kDiscard_ContentChangeMode;
160 }
161 }
162 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000163 }
164}
165
reed@android.com8a1c16f2008-12-17 15:59:43 +0000166///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167
reed4a8126e2014-09-22 07:29:03 -0700168static uint32_t filter_paint_flags(const SkSurfaceProps& props, uint32_t flags) {
169 const uint32_t propFlags = props.flags();
170 if (propFlags & SkSurfaceProps::kDisallowDither_Flag) {
171 flags &= ~SkPaint::kDither_Flag;
172 }
173 if (propFlags & SkSurfaceProps::kDisallowAntiAlias_Flag) {
174 flags &= ~SkPaint::kAntiAlias_Flag;
175 }
176 return flags;
177}
178
179///////////////////////////////////////////////////////////////////////////////
180
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000181/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000182 The clip/matrix/proc are fields that reflect the top of the save/restore
183 stack. Whenever the canvas changes, it marks a dirty flag, and then before
184 these are used (assuming we're not on a layer) we rebuild these cache
185 values: they reflect the top of the save stack, but translated and clipped
186 by the device's XY offset and bitmap-bounds.
187*/
188struct DeviceCM {
189 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000190 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000191 SkRasterClip fClip;
reed@google.com6f8f2922011-03-04 22:27:10 +0000192 SkPaint* fPaint; // may be null (in the future)
reed61f501f2015-04-29 08:34:00 -0700193 const SkMatrix* fMatrix;
194 SkMatrix fMatrixStorage;
195 const bool fDeviceIsBitmapDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000196
reed96e657d2015-03-10 17:30:07 -0700197 DeviceCM(SkBaseDevice* device, const SkPaint* paint, SkCanvas* canvas,
reed86a17e72015-05-14 12:25:22 -0700198 bool conservativeRasterClip, bool deviceIsBitmapDevice)
halcanary96fcdcc2015-08-27 07:41:13 -0700199 : fNext(nullptr)
reedd9544982014-09-09 18:46:22 -0700200 , fClip(conservativeRasterClip)
reed61f501f2015-04-29 08:34:00 -0700201 , fDeviceIsBitmapDevice(deviceIsBitmapDevice)
reedd9544982014-09-09 18:46:22 -0700202 {
halcanary96fcdcc2015-08-27 07:41:13 -0700203 if (nullptr != device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000205 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000206 }
reed@google.com4b226022011-01-11 18:32:13 +0000207 fDevice = device;
halcanary96fcdcc2015-08-27 07:41:13 -0700208 fPaint = paint ? new SkPaint(*paint) : nullptr;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000209 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000210
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000211 ~DeviceCM() {
bsalomon49f085d2014-09-05 13:34:00 -0700212 if (fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000213 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000214 fDevice->unref();
215 }
halcanary385fe4d2015-08-26 13:07:48 -0700216 delete fPaint;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000217 }
reed@google.com4b226022011-01-11 18:32:13 +0000218
mtkleinfeaadee2015-04-08 11:25:48 -0700219 void reset(const SkIRect& bounds) {
220 SkASSERT(!fPaint);
221 SkASSERT(!fNext);
222 SkASSERT(fDevice);
223 fClip.setRect(bounds);
224 }
225
reed@google.com045e62d2011-10-24 12:19:46 +0000226 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
227 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000228 int x = fDevice->getOrigin().x();
229 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000230 int width = fDevice->width();
231 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000232
reed@android.com8a1c16f2008-12-17 15:59:43 +0000233 if ((x | y) == 0) {
234 fMatrix = &totalMatrix;
235 fClip = totalClip;
236 } else {
237 fMatrixStorage = totalMatrix;
238 fMatrixStorage.postTranslate(SkIntToScalar(-x),
239 SkIntToScalar(-y));
240 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000241
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242 totalClip.translate(-x, -y, &fClip);
243 }
244
reed@google.com045e62d2011-10-24 12:19:46 +0000245 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000246
247 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000248
reed@android.com8a1c16f2008-12-17 15:59:43 +0000249 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000250 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000251 SkRegion::kDifference_Op);
252 }
reed@google.com4b226022011-01-11 18:32:13 +0000253
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000254 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
255
reed@android.com8a1c16f2008-12-17 15:59:43 +0000256#ifdef SK_DEBUG
257 if (!fClip.isEmpty()) {
258 SkIRect deviceR;
259 deviceR.set(0, 0, width, height);
260 SkASSERT(deviceR.contains(fClip.getBounds()));
261 }
262#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000263 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000264};
265
266/* This is the record we keep for each save/restore level in the stack.
267 Since a level optionally copies the matrix and/or stack, we have pointers
268 for these fields. If the value is copied for this level, the copy is
269 stored in the ...Storage field, and the pointer points to that. If the
270 value is not copied for this level, we ignore ...Storage, and just point
271 at the corresponding value in the previous level in the stack.
272*/
273class SkCanvas::MCRec {
274public:
reed1f836ee2014-07-07 07:49:34 -0700275 SkDrawFilter* fFilter; // the current filter (or null)
reedd9544982014-09-09 18:46:22 -0700276 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000277 /* If there are any layers in the stack, this points to the top-most
278 one that is at or below this level in the stack (so we know what
279 bitmap/device to draw into from this level. This value is NOT
280 reference counted, since the real owner is either our fLayer field,
281 or a previous one in a lower level.)
282 */
reed2ff1fce2014-12-11 07:07:37 -0800283 DeviceCM* fTopLayer;
284 SkRasterClip fRasterClip;
285 SkMatrix fMatrix;
286 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287
reedd9544982014-09-09 18:46:22 -0700288 MCRec(bool conservativeRasterClip) : fRasterClip(conservativeRasterClip) {
halcanary96fcdcc2015-08-27 07:41:13 -0700289 fFilter = nullptr;
290 fLayer = nullptr;
291 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800292 fMatrix.reset();
293 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700294
reedd9544982014-09-09 18:46:22 -0700295 // don't bother initializing fNext
296 inc_rec();
297 }
reed2ff1fce2014-12-11 07:07:37 -0800298 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
reedd9544982014-09-09 18:46:22 -0700299 fFilter = SkSafeRef(prev.fFilter);
halcanary96fcdcc2015-08-27 07:41:13 -0700300 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700301 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800302 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700303
reed@android.com8a1c16f2008-12-17 15:59:43 +0000304 // don't bother initializing fNext
305 inc_rec();
306 }
307 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000308 SkSafeUnref(fFilter);
halcanary385fe4d2015-08-26 13:07:48 -0700309 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000310 dec_rec();
311 }
mtkleinfeaadee2015-04-08 11:25:48 -0700312
313 void reset(const SkIRect& bounds) {
314 SkASSERT(fLayer);
315 SkASSERT(fDeferredSaveCount == 0);
316
317 fMatrix.reset();
318 fRasterClip.setRect(bounds);
319 fLayer->reset(bounds);
320 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000321};
322
323class SkDrawIter : public SkDraw {
324public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000325 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000326 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000327 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000328 canvas->updateDeviceCMCache();
329
reed687fa1c2015-04-07 08:00:56 -0700330 fClipStack = canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000331 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000332 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000333 }
reed@google.com4b226022011-01-11 18:32:13 +0000334
reed@android.com8a1c16f2008-12-17 15:59:43 +0000335 bool next() {
336 // skip over recs with empty clips
337 if (fSkipEmptyClips) {
338 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
339 fCurrLayer = fCurrLayer->fNext;
340 }
341 }
342
reed@google.comf68c5e22012-02-24 16:38:58 +0000343 const DeviceCM* rec = fCurrLayer;
344 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000345
346 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000347 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
348 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000349 fDevice = rec->fDevice;
reed41e010c2015-06-09 12:16:53 -0700350 if (!fDevice->accessPixels(&fDst)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700351 fDst.reset(fDevice->imageInfo(), nullptr, 0);
reed41e010c2015-06-09 12:16:53 -0700352 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000353 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000354 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000355
356 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700357 // fCurrLayer may be nullptr now
reed@android.com199f1082009-06-10 02:12:47 +0000358
reed@android.com8a1c16f2008-12-17 15:59:43 +0000359 return true;
360 }
361 return false;
362 }
reed@google.com4b226022011-01-11 18:32:13 +0000363
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000364 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000365 int getX() const { return fDevice->getOrigin().x(); }
366 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000367 const SkMatrix& getMatrix() const { return *fMatrix; }
368 const SkRegion& getClip() const { return *fClip; }
369 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000370
reed@android.com8a1c16f2008-12-17 15:59:43 +0000371private:
372 SkCanvas* fCanvas;
373 const DeviceCM* fCurrLayer;
374 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000375 SkBool8 fSkipEmptyClips;
376
377 typedef SkDraw INHERITED;
378};
379
380/////////////////////////////////////////////////////////////////////////////
381
reeddbc3cef2015-04-29 12:18:57 -0700382static SkPaint* set_if_needed(SkLazyPaint* lazy, const SkPaint& orig) {
383 return lazy->isValid() ? lazy->get() : lazy->set(orig);
384}
385
386/**
387 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700388 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700389 */
390static SkColorFilter* image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700391 SkImageFilter* imgf = paint.getImageFilter();
392 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700393 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700394 }
395
396 SkColorFilter* imgCF;
397 if (!imgf->asAColorFilter(&imgCF)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700398 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700399 }
400
401 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700402 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700403 // there is no existing paint colorfilter, so we can just return the imagefilter's
404 return imgCF;
405 }
406
407 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
408 // and we need to combine them into a single colorfilter.
409 SkAutoTUnref<SkColorFilter> autoImgCF(imgCF);
410 return SkColorFilter::CreateComposeFilter(imgCF, paintCF);
reeddbc3cef2015-04-29 12:18:57 -0700411}
412
senorblanco87e066e2015-10-28 11:23:36 -0700413/**
414 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
415 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
416 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
417 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
418 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
419 * conservative "effective" bounds based on the settings in the paint... with one exception. This
420 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
421 * deliberately ignored.
422 */
423static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
424 const SkRect& rawBounds,
425 SkRect* storage) {
426 SkPaint tmpUnfiltered(paint);
427 tmpUnfiltered.setImageFilter(nullptr);
428 if (tmpUnfiltered.canComputeFastBounds()) {
429 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
430 } else {
431 return rawBounds;
432 }
433}
434
reed@android.com8a1c16f2008-12-17 15:59:43 +0000435class AutoDrawLooper {
436public:
senorblanco87e066e2015-10-28 11:23:36 -0700437 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
438 // paint. It's used to determine the size of the offscreen layer for filters.
439 // If null, the clip will be used instead.
reed4a8126e2014-09-22 07:29:03 -0700440 AutoDrawLooper(SkCanvas* canvas, const SkSurfaceProps& props, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000441 bool skipLayerForImageFilter = false,
senorblanco87e066e2015-10-28 11:23:36 -0700442 const SkRect* rawBounds = nullptr) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000443 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000444 fFilter = canvas->getDrawFilter();
reed4a8126e2014-09-22 07:29:03 -0700445 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000446 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700447 fTempLayerForImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000448 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000449
reeddbc3cef2015-04-29 12:18:57 -0700450 SkColorFilter* simplifiedCF = image_to_color_filter(fOrigPaint);
451 if (simplifiedCF) {
452 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
453 paint->setColorFilter(simplifiedCF)->unref();
halcanary96fcdcc2015-08-27 07:41:13 -0700454 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700455 fPaint = paint;
456 }
457
458 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700459 /**
460 * We implement ImageFilters for a given draw by creating a layer, then applying the
461 * imagefilter to the pixels of that layer (its backing surface/image), and then
462 * we call restore() to xfer that layer to the main canvas.
463 *
464 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
465 * 2. Generate the src pixels:
466 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
467 * return (fPaint). We then draw the primitive (using srcover) into a cleared
468 * buffer/surface.
469 * 3. Restore the layer created in #1
470 * The imagefilter is passed the buffer/surface from the layer (now filled with the
471 * src pixels of the primitive). It returns a new "filtered" buffer, which we
472 * draw onto the previous layer using the xfermode from the original paint.
473 */
reed@google.com8926b162012-03-23 15:36:36 +0000474 SkPaint tmp;
reeddbc3cef2015-04-29 12:18:57 -0700475 tmp.setImageFilter(fPaint->getImageFilter());
476 tmp.setXfermode(fPaint->getXfermode());
senorblanco87e066e2015-10-28 11:23:36 -0700477 SkRect storage;
478 if (rawBounds) {
479 // Make rawBounds include all paint outsets except for those due to image filters.
480 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
481 }
senorblanco87e066e2015-10-28 11:23:36 -0700482 (void)canvas->internalSaveLayer(rawBounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag,
reed76033be2015-03-14 10:54:31 -0700483 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700484 fTempLayerForImageFilter = true;
485 // we remove the imagefilter/xfermode inside doNext()
reed@google.com8926b162012-03-23 15:36:36 +0000486 }
487
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000488 if (SkDrawLooper* looper = paint.getLooper()) {
489 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
490 looper->contextSize());
491 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000492 fIsSimple = false;
493 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700494 fLooperContext = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000495 // can we be marked as simple?
reed5c476fb2015-04-20 08:04:21 -0700496 fIsSimple = !fFilter && !fTempLayerForImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000497 }
piotaixrb5fae932014-09-24 13:03:30 -0700498
reed4a8126e2014-09-22 07:29:03 -0700499 uint32_t oldFlags = paint.getFlags();
500 fNewPaintFlags = filter_paint_flags(props, oldFlags);
501 if (fIsSimple && (fNewPaintFlags != oldFlags)) {
reeddbc3cef2015-04-29 12:18:57 -0700502 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700503 paint->setFlags(fNewPaintFlags);
504 fPaint = paint;
505 // if we're not simple, doNext() will take care of calling setFlags()
506 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000507 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000508
reed@android.com8a1c16f2008-12-17 15:59:43 +0000509 ~AutoDrawLooper() {
reed5c476fb2015-04-20 08:04:21 -0700510 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000511 fCanvas->internalRestore();
512 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000513 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000514 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000515
reed@google.com4e2b3d32011-04-07 14:18:59 +0000516 const SkPaint& paint() const {
517 SkASSERT(fPaint);
518 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000519 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000520
reed@google.com129ec222012-05-15 13:24:09 +0000521 bool next(SkDrawFilter::Type drawType) {
522 if (fDone) {
523 return false;
524 } else if (fIsSimple) {
525 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000526 return !fPaint->nothingToDraw();
527 } else {
528 return this->doNext(drawType);
529 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000530 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000531
reed@android.com8a1c16f2008-12-17 15:59:43 +0000532private:
reeddbc3cef2015-04-29 12:18:57 -0700533 SkLazyPaint fLazyPaintInit; // base paint storage in case we need to modify it
534 SkLazyPaint fLazyPaintPerLooper; // per-draw-looper storage, so the looper can modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000535 SkCanvas* fCanvas;
536 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000537 SkDrawFilter* fFilter;
538 const SkPaint* fPaint;
539 int fSaveCount;
reed4a8126e2014-09-22 07:29:03 -0700540 uint32_t fNewPaintFlags;
reed5c476fb2015-04-20 08:04:21 -0700541 bool fTempLayerForImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000542 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000543 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000544 SkDrawLooper::Context* fLooperContext;
545 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000546
547 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000548};
549
reed@google.com129ec222012-05-15 13:24:09 +0000550bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
halcanary96fcdcc2015-08-27 07:41:13 -0700551 fPaint = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000552 SkASSERT(!fIsSimple);
reed5c476fb2015-04-20 08:04:21 -0700553 SkASSERT(fLooperContext || fFilter || fTempLayerForImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000554
reeddbc3cef2015-04-29 12:18:57 -0700555 SkPaint* paint = fLazyPaintPerLooper.set(fLazyPaintInit.isValid() ?
556 *fLazyPaintInit.get() : fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700557 paint->setFlags(fNewPaintFlags);
reed@google.com129ec222012-05-15 13:24:09 +0000558
reed5c476fb2015-04-20 08:04:21 -0700559 if (fTempLayerForImageFilter) {
halcanary96fcdcc2015-08-27 07:41:13 -0700560 paint->setImageFilter(nullptr);
561 paint->setXfermode(nullptr);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000562 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000563
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000564 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000565 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000566 return false;
567 }
568 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000569 if (!fFilter->filter(paint, drawType)) {
570 fDone = true;
571 return false;
572 }
halcanary96fcdcc2015-08-27 07:41:13 -0700573 if (nullptr == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000574 // no looper means we only draw once
575 fDone = true;
576 }
577 }
578 fPaint = paint;
579
580 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000581 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000582 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000583 }
584
585 // call this after any possible paint modifiers
586 if (fPaint->nothingToDraw()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700587 fPaint = nullptr;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000588 return false;
589 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000590 return true;
591}
592
reed@android.com8a1c16f2008-12-17 15:59:43 +0000593////////// macros to place around the internal draw calls //////////////////
594
reed@google.com8926b162012-03-23 15:36:36 +0000595#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000596 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700597 AutoDrawLooper looper(this, fProps, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000598 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000599 SkDrawIter iter(this);
600
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000601#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000602 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700603 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000604 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000606
reedc83a2972015-07-16 07:40:45 -0700607#define LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, type, bounds, auxOpaque) \
608 this->predrawNotify(bounds, &paint, auxOpaque); \
609 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
610 while (looper.next(type)) { \
611 SkDrawIter iter(this);
612
reed@google.com4e2b3d32011-04-07 14:18:59 +0000613#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000614
615////////////////////////////////////////////////////////////////////////////
616
mtkleinfeaadee2015-04-08 11:25:48 -0700617void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
618 this->restoreToCount(1);
619 fCachedLocalClipBounds.setEmpty();
620 fCachedLocalClipBoundsDirty = true;
621 fClipStack->reset();
622 fMCRec->reset(bounds);
623
624 // We're peering through a lot of structs here. Only at this scope do we
625 // know that the device is an SkBitmapDevice (really an SkNoPixelsBitmapDevice).
626 static_cast<SkBitmapDevice*>(fMCRec->fLayer->fDevice)->setNewSize(bounds.size());
627}
628
reedd9544982014-09-09 18:46:22 -0700629SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
reed42b73eb2015-11-20 13:42:42 -0800630 if (device && device->forceConservativeRasterClip()) {
631 flags = InitFlags(flags | kConservativeRasterClip_InitFlag);
632 }
633 // Since init() is only called once by our constructors, it is safe to perform this
634 // const-cast.
635 *const_cast<bool*>(&fConservativeRasterClip) = SkToBool(flags & kConservativeRasterClip_InitFlag);
636
reed@google.comc0784db2013-12-13 21:16:12 +0000637 fCachedLocalClipBounds.setEmpty();
638 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000639 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000640 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700641 fDeviceCMDirty = true;
reed2ff1fce2014-12-11 07:07:37 -0800642 fSaveCount = 1;
halcanary96fcdcc2015-08-27 07:41:13 -0700643 fMetaData = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000644
halcanary385fe4d2015-08-26 13:07:48 -0700645 fClipStack.reset(new SkClipStack);
reed687fa1c2015-04-07 08:00:56 -0700646
reed@android.com8a1c16f2008-12-17 15:59:43 +0000647 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700648 new (fMCRec) MCRec(fConservativeRasterClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000649
reeda499f902015-05-01 09:34:31 -0700650 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
651 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
halcanary96fcdcc2015-08-27 07:41:13 -0700652 new (fDeviceCMStorage) DeviceCM(nullptr, nullptr, nullptr, fConservativeRasterClip, false);
reedb679ca82015-04-07 04:40:48 -0700653
reed@android.com8a1c16f2008-12-17 15:59:43 +0000654 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000655
halcanary96fcdcc2015-08-27 07:41:13 -0700656 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000657
reedf92c8662014-08-18 08:02:43 -0700658 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700659 // The root device and the canvas should always have the same pixel geometry
660 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reedf92c8662014-08-18 08:02:43 -0700661 device->onAttachToCanvas(this);
662 fMCRec->fLayer->fDevice = SkRef(device);
reed78e27682014-11-19 08:04:34 -0800663 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
reedf92c8662014-08-18 08:02:43 -0700664 }
665 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000666}
667
reed@google.comcde92112011-07-06 20:00:52 +0000668SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000669 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700670 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800671 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000672{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000673 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000674
halcanary96fcdcc2015-08-27 07:41:13 -0700675 this->init(nullptr, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000676}
677
reedd9544982014-09-09 18:46:22 -0700678static SkBitmap make_nopixels(int width, int height) {
679 SkBitmap bitmap;
680 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
681 return bitmap;
682}
683
684class SkNoPixelsBitmapDevice : public SkBitmapDevice {
685public:
robertphillipsfcf78292015-06-19 11:49:52 -0700686 SkNoPixelsBitmapDevice(const SkIRect& bounds, const SkSurfaceProps& surfaceProps)
687 : INHERITED(make_nopixels(bounds.width(), bounds.height()), surfaceProps)
reed78e27682014-11-19 08:04:34 -0800688 {
689 this->setOrigin(bounds.x(), bounds.y());
690 }
reedd9544982014-09-09 18:46:22 -0700691
692private:
piotaixrb5fae932014-09-24 13:03:30 -0700693
reedd9544982014-09-09 18:46:22 -0700694 typedef SkBitmapDevice INHERITED;
695};
696
reed96a857e2015-01-25 10:33:58 -0800697SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000698 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800699 , fProps(SkSurfacePropsCopyOrDefault(props))
reed42b73eb2015-11-20 13:42:42 -0800700 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000701{
702 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700703
halcanary385fe4d2015-08-26 13:07:48 -0700704 this->init(new SkNoPixelsBitmapDevice(SkIRect::MakeWH(width, height), fProps),
705 kDefault_InitFlags)->unref();
reedd9544982014-09-09 18:46:22 -0700706}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000707
reed78e27682014-11-19 08:04:34 -0800708SkCanvas::SkCanvas(const SkIRect& bounds, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700709 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700710 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800711 , fConservativeRasterClip(false)
reedd9544982014-09-09 18:46:22 -0700712{
713 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700714
halcanary385fe4d2015-08-26 13:07:48 -0700715 this->init(new SkNoPixelsBitmapDevice(bounds, fProps), flags)->unref();
reedd9544982014-09-09 18:46:22 -0700716}
717
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000718SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000719 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700720 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800721 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000722{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000723 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700724
reedd9544982014-09-09 18:46:22 -0700725 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000726}
727
robertphillipsfcf78292015-06-19 11:49:52 -0700728SkCanvas::SkCanvas(SkBaseDevice* device, InitFlags flags)
729 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700730 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800731 , fConservativeRasterClip(false)
robertphillipsfcf78292015-06-19 11:49:52 -0700732{
733 inc_canvas();
734
735 this->init(device, flags);
736}
737
reed4a8126e2014-09-22 07:29:03 -0700738SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700739 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700740 , fProps(props)
reed42b73eb2015-11-20 13:42:42 -0800741 , fConservativeRasterClip(false)
reed3716fd02014-09-21 09:39:55 -0700742{
743 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700744
halcanary385fe4d2015-08-26 13:07:48 -0700745 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700746 this->init(device, kDefault_InitFlags);
747}
reed29c857d2014-09-21 10:25:07 -0700748
reed4a8126e2014-09-22 07:29:03 -0700749SkCanvas::SkCanvas(const SkBitmap& bitmap)
750 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
751 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800752 , fConservativeRasterClip(false)
reed4a8126e2014-09-22 07:29:03 -0700753{
754 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700755
halcanary385fe4d2015-08-26 13:07:48 -0700756 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700757 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000758}
759
760SkCanvas::~SkCanvas() {
761 // free up the contents of our deque
762 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000763
reed@android.com8a1c16f2008-12-17 15:59:43 +0000764 this->internalRestore(); // restore the last, since we're going away
765
halcanary385fe4d2015-08-26 13:07:48 -0700766 delete fMetaData;
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000767
reed@android.com8a1c16f2008-12-17 15:59:43 +0000768 dec_canvas();
769}
770
reed@android.com8a1c16f2008-12-17 15:59:43 +0000771SkDrawFilter* SkCanvas::getDrawFilter() const {
772 return fMCRec->fFilter;
773}
774
775SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
reed51985e32015-04-11 08:04:56 -0700776 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000777 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
778 return filter;
779}
780
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000781SkMetaData& SkCanvas::getMetaData() {
782 // metadata users are rare, so we lazily allocate it. If that changes we
783 // can decide to just make it a field in the device (rather than a ptr)
halcanary96fcdcc2015-08-27 07:41:13 -0700784 if (nullptr == fMetaData) {
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000785 fMetaData = new SkMetaData;
786 }
787 return *fMetaData;
788}
789
reed@android.com8a1c16f2008-12-17 15:59:43 +0000790///////////////////////////////////////////////////////////////////////////////
791
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000792void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000793 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000794 if (device) {
795 device->flush();
796 }
797}
798
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000799SkISize SkCanvas::getTopLayerSize() const {
800 SkBaseDevice* d = this->getTopDevice();
801 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
802}
803
804SkIPoint SkCanvas::getTopLayerOrigin() const {
805 SkBaseDevice* d = this->getTopDevice();
806 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
807}
808
809SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000810 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000811 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
812}
813
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000814SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000815 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000816 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000817 SkASSERT(rec && rec->fLayer);
818 return rec->fLayer->fDevice;
819}
820
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000821SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000822 if (updateMatrixClip) {
823 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
824 }
reed@google.com9266fed2011-03-30 00:18:03 +0000825 return fMCRec->fTopLayer->fDevice;
826}
827
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000828bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
829 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
830 return false;
831 }
832
833 bool weAllocated = false;
halcanary96fcdcc2015-08-27 07:41:13 -0700834 if (nullptr == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700835 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000836 return false;
837 }
838 weAllocated = true;
839 }
840
reedcf01e312015-05-23 19:14:51 -0700841 SkAutoPixmapUnlock unlocker;
842 if (bitmap->requestLock(&unlocker)) {
843 const SkPixmap& pm = unlocker.pixmap();
844 if (this->readPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), x, y)) {
845 return true;
846 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000847 }
848
849 if (weAllocated) {
halcanary96fcdcc2015-08-27 07:41:13 -0700850 bitmap->setPixelRef(nullptr);
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000851 }
852 return false;
853}
reed@google.com51df9e32010-12-23 19:29:18 +0000854
bsalomon@google.comc6980972011-11-02 19:57:21 +0000855bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000856 SkIRect r = srcRect;
857 const SkISize size = this->getBaseLayerSize();
858 if (!r.intersect(0, 0, size.width(), size.height())) {
859 bitmap->reset();
860 return false;
861 }
862
reed84825042014-09-02 12:50:45 -0700863 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000864 // bitmap will already be reset.
865 return false;
866 }
867 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
868 bitmap->reset();
869 return false;
870 }
871 return true;
872}
873
reed96472de2014-12-10 09:53:42 -0800874bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000875 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000876 if (!device) {
877 return false;
878 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000879 const SkISize size = this->getBaseLayerSize();
mtkleinf0f14112014-12-12 08:46:25 -0800880
reed96472de2014-12-10 09:53:42 -0800881 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
882 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000883 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000884 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000885
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000886 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800887 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000888}
889
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000890bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
891 if (bitmap.getTexture()) {
892 return false;
893 }
reedcf01e312015-05-23 19:14:51 -0700894
895 SkAutoPixmapUnlock unlocker;
896 if (bitmap.requestLock(&unlocker)) {
897 const SkPixmap& pm = unlocker.pixmap();
898 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000899 }
900 return false;
901}
902
903bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
904 int x, int y) {
905 switch (origInfo.colorType()) {
906 case kUnknown_SkColorType:
907 case kIndex_8_SkColorType:
908 return false;
909 default:
910 break;
911 }
halcanary96fcdcc2015-08-27 07:41:13 -0700912 if (nullptr == pixels || rowBytes < origInfo.minRowBytes()) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000913 return false;
914 }
915
916 const SkISize size = this->getBaseLayerSize();
917 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
918 if (!target.intersect(0, 0, size.width(), size.height())) {
919 return false;
920 }
921
922 SkBaseDevice* device = this->getDevice();
923 if (!device) {
924 return false;
925 }
926
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000927 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700928 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000929
930 // if x or y are negative, then we have to adjust pixels
931 if (x > 0) {
932 x = 0;
933 }
934 if (y > 0) {
935 y = 0;
936 }
937 // here x,y are either 0 or negative
938 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
939
reed4af35f32014-06-27 17:47:49 -0700940 // Tell our owning surface to bump its generation ID
reedc83a2972015-07-16 07:40:45 -0700941 const bool completeOverwrite = info.dimensions() == size;
942 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700943
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000944 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000945 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000946}
reed@google.com51df9e32010-12-23 19:29:18 +0000947
junov@google.com4370aed2012-01-18 16:21:08 +0000948SkCanvas* SkCanvas::canvasForDrawIter() {
949 return this;
950}
951
reed@android.com8a1c16f2008-12-17 15:59:43 +0000952//////////////////////////////////////////////////////////////////////////////
953
reed@android.com8a1c16f2008-12-17 15:59:43 +0000954void SkCanvas::updateDeviceCMCache() {
955 if (fDeviceCMDirty) {
956 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700957 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000958 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000959
halcanary96fcdcc2015-08-27 07:41:13 -0700960 if (nullptr == layer->fNext) { // only one layer
961 layer->updateMC(totalMatrix, totalClip, *fClipStack, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000962 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000963 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000964 do {
reed687fa1c2015-04-07 08:00:56 -0700965 layer->updateMC(totalMatrix, clip, *fClipStack, &clip);
halcanary96fcdcc2015-08-27 07:41:13 -0700966 } while ((layer = layer->fNext) != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000967 }
968 fDeviceCMDirty = false;
969 }
970}
971
reed@android.com8a1c16f2008-12-17 15:59:43 +0000972///////////////////////////////////////////////////////////////////////////////
973
reed2ff1fce2014-12-11 07:07:37 -0800974void SkCanvas::checkForDeferredSave() {
975 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800976 this->doSave();
977 }
978}
979
reedf0090cb2014-11-26 08:55:51 -0800980int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800981#ifdef SK_DEBUG
982 int count = 0;
983 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
984 for (;;) {
985 const MCRec* rec = (const MCRec*)iter.next();
986 if (!rec) {
987 break;
988 }
989 count += 1 + rec->fDeferredSaveCount;
990 }
991 SkASSERT(count == fSaveCount);
992#endif
993 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -0800994}
995
996int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -0800997 fSaveCount += 1;
998 fMCRec->fDeferredSaveCount += 1;
999 return this->getSaveCount() - 1; // return our prev value
1000}
1001
1002void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -08001003 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -07001004
1005 SkASSERT(fMCRec->fDeferredSaveCount > 0);
1006 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001007 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -08001008}
1009
1010void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -08001011 if (fMCRec->fDeferredSaveCount > 0) {
1012 SkASSERT(fSaveCount > 1);
1013 fSaveCount -= 1;
1014 fMCRec->fDeferredSaveCount -= 1;
1015 } else {
1016 // check for underflow
1017 if (fMCStack.count() > 1) {
1018 this->willRestore();
1019 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -07001020 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001021 this->internalRestore();
1022 this->didRestore();
1023 }
reedf0090cb2014-11-26 08:55:51 -08001024 }
1025}
1026
1027void SkCanvas::restoreToCount(int count) {
1028 // sanity check
1029 if (count < 1) {
1030 count = 1;
1031 }
mtkleinf0f14112014-12-12 08:46:25 -08001032
reedf0090cb2014-11-26 08:55:51 -08001033 int n = this->getSaveCount() - count;
1034 for (int i = 0; i < n; ++i) {
1035 this->restore();
1036 }
1037}
1038
reed2ff1fce2014-12-11 07:07:37 -08001039void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001040 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -07001041 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +00001042 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +00001043
reed687fa1c2015-04-07 08:00:56 -07001044 fClipStack->save();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001045}
1046
reed@android.com8a1c16f2008-12-17 15:59:43 +00001047static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +00001048#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +00001049 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +00001050#else
1051 return true;
1052#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001053}
1054
junov@chromium.orga907ac32012-02-24 21:54:07 +00001055bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
reed9b3aa542015-03-11 08:47:12 -07001056 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001057 SkIRect clipBounds;
1058 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001059 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +00001060 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001061
reed96e657d2015-03-10 17:30:07 -07001062 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
1063
senorblanco87e066e2015-10-28 11:23:36 -07001064// This is a temporary hack, until individual filters can do their own
1065// bloating, when this will be removed.
1066#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
1067 SkRect storage;
1068#endif
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001069 if (imageFilter) {
reed96e657d2015-03-10 17:30:07 -07001070 imageFilter->filterBounds(clipBounds, ctm, &clipBounds);
senorblanco87e066e2015-10-28 11:23:36 -07001071#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
1072 if (bounds && imageFilter->canComputeFastBounds()) {
1073 imageFilter->computeFastBounds(*bounds, &storage);
1074 bounds = &storage;
1075 } else {
1076 bounds = nullptr;
1077 }
1078#endif
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001079 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001080 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -07001081 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001082 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +00001083
reed96e657d2015-03-10 17:30:07 -07001084 ctm.mapRect(&r, *bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001085 r.roundOut(&ir);
1086 // early exit if the layer's bounds are clipped out
1087 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001088 if (bounds_affects_clip(flags)) {
reed9b3aa542015-03-11 08:47:12 -07001089 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001090 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001091 }
junov@chromium.orga907ac32012-02-24 21:54:07 +00001092 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001093 }
1094 } else { // no user bounds, so just use the clip
1095 ir = clipBounds;
1096 }
reed180aec42015-03-11 10:39:04 -07001097 SkASSERT(!ir.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001098
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +00001099 if (bounds_affects_clip(flags)) {
reed180aec42015-03-11 10:39:04 -07001100 // Simplify the current clips since they will be applied properly during restore()
reed9b3aa542015-03-11 08:47:12 -07001101 fCachedLocalClipBoundsDirty = true;
reed687fa1c2015-04-07 08:00:56 -07001102 fClipStack->clipDevRect(ir, SkRegion::kReplace_Op);
reed180aec42015-03-11 10:39:04 -07001103 fMCRec->fRasterClip.setRect(ir);
junov@chromium.orga907ac32012-02-24 21:54:07 +00001104 }
1105
1106 if (intersection) {
1107 *intersection = ir;
1108 }
1109 return true;
1110}
1111
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001112int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
reedd990e2f2014-12-22 11:58:30 -08001113 if (gIgnoreSaveLayerBounds) {
halcanary96fcdcc2015-08-27 07:41:13 -07001114 bounds = nullptr;
reedd990e2f2014-12-22 11:58:30 -08001115 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001116 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag);
reeda6441162015-03-26 13:40:09 -07001117 fSaveCount += 1;
reed76033be2015-03-14 10:54:31 -07001118 this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, strategy);
reed2ff1fce2014-12-11 07:07:37 -08001119 return this->getSaveCount() - 1;
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001120}
1121
reed2ff1fce2014-12-11 07:07:37 -08001122int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags) {
reedd990e2f2014-12-22 11:58:30 -08001123 if (gIgnoreSaveLayerBounds) {
halcanary96fcdcc2015-08-27 07:41:13 -07001124 bounds = nullptr;
reedd990e2f2014-12-22 11:58:30 -08001125 }
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001126 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
reeda6441162015-03-26 13:40:09 -07001127 fSaveCount += 1;
reed76033be2015-03-14 10:54:31 -07001128 this->internalSaveLayer(bounds, paint, flags, strategy);
reed2ff1fce2014-12-11 07:07:37 -08001129 return this->getSaveCount() - 1;
reed@google.com8926b162012-03-23 15:36:36 +00001130}
1131
reed2ff1fce2014-12-11 07:07:37 -08001132void SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
reed76033be2015-03-14 10:54:31 -07001133 SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +00001134#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
commit-bot@chromium.org2a5cd602014-05-30 20:41:20 +00001135 flags |= kClipToLayer_SaveFlag;
reed@google.comb93ba452014-03-10 19:47:58 +00001136#endif
1137
junov@chromium.orga907ac32012-02-24 21:54:07 +00001138 // do this before we create the layer. We don't call the public save() since
1139 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001140 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001141
1142 fDeviceCMDirty = true;
1143
1144 SkIRect ir;
halcanary96fcdcc2015-08-27 07:41:13 -07001145 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : nullptr)) {
reed2ff1fce2014-12-11 07:07:37 -08001146 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001147 }
1148
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001149 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1150 // the clipRectBounds() call above?
1151 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001152 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001153 }
1154
reed76033be2015-03-14 10:54:31 -07001155 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
reed8dc0ccb2015-03-20 06:32:52 -07001156 SkPixelGeometry geo = fProps.pixelGeometry();
1157 if (paint) {
reed76033be2015-03-14 10:54:31 -07001158 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001159 if (paint->getImageFilter() || paint->getColorFilter()) {
reed76033be2015-03-14 10:54:31 -07001160 isOpaque = false;
reed8dc0ccb2015-03-20 06:32:52 -07001161 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001162 }
1163 }
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001164 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
1165 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001166
reedb2db8982014-11-13 12:41:02 -08001167 SkBaseDevice* device = this->getTopDevice();
halcanary96fcdcc2015-08-27 07:41:13 -07001168 if (nullptr == device) {
reedb2db8982014-11-13 12:41:02 -08001169 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001170 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001171 }
reedb2db8982014-11-13 12:41:02 -08001172
reed61f501f2015-04-29 08:34:00 -07001173 bool forceSpriteOnRestore = false;
1174 {
reeddaa57bf2015-05-15 10:39:17 -07001175 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
reed61f501f2015-04-29 08:34:00 -07001176 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo);
1177 SkBaseDevice* newDev = device->onCreateDevice(createInfo, paint);
halcanary96fcdcc2015-08-27 07:41:13 -07001178 if (nullptr == newDev) {
reed61f501f2015-04-29 08:34:00 -07001179 // If onCreateDevice didn't succeed, try raster (e.g. PDF couldn't handle the paint)
robertphillips9a53fd72015-06-22 09:46:59 -07001180 const SkSurfaceProps surfaceProps(fProps.flags(), createInfo.fPixelGeometry);
1181 newDev = SkBitmapDevice::Create(createInfo.fInfo, surfaceProps);
halcanary96fcdcc2015-08-27 07:41:13 -07001182 if (nullptr == newDev) {
reed61f501f2015-04-29 08:34:00 -07001183 SkErrorInternals::SetError(kInternalError_SkError,
1184 "Unable to create device for layer.");
1185 return;
1186 }
1187 forceSpriteOnRestore = true;
1188 }
1189 device = newDev;
bungeman@google.come25c6842011-08-17 14:53:54 +00001190 }
bsalomon@google.come97f0852011-06-17 13:10:25 +00001191
reed@google.com6f8f2922011-03-04 22:27:10 +00001192 device->setOrigin(ir.fLeft, ir.fTop);
halcanary385fe4d2015-08-26 13:07:48 -07001193 DeviceCM* layer =
1194 new DeviceCM(device, paint, this, fConservativeRasterClip, forceSpriteOnRestore);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001195 device->unref();
1196
1197 layer->fNext = fMCRec->fTopLayer;
1198 fMCRec->fLayer = layer;
1199 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reed@android.com8a1c16f2008-12-17 15:59:43 +00001200}
1201
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001202int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
1203 return this->saveLayerAlpha(bounds, alpha, kARGB_ClipLayer_SaveFlag);
1204}
1205
reed@android.com8a1c16f2008-12-17 15:59:43 +00001206int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
1207 SaveFlags flags) {
1208 if (0xFF == alpha) {
halcanary96fcdcc2015-08-27 07:41:13 -07001209 return this->saveLayer(bounds, nullptr, flags);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001210 } else {
1211 SkPaint tmpPaint;
1212 tmpPaint.setAlpha(alpha);
1213 return this->saveLayer(bounds, &tmpPaint, flags);
1214 }
1215}
1216
reed@android.com8a1c16f2008-12-17 15:59:43 +00001217void SkCanvas::internalRestore() {
1218 SkASSERT(fMCStack.count() != 0);
1219
1220 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001221 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001222
reed687fa1c2015-04-07 08:00:56 -07001223 fClipStack->restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001224
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001225 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001226 DeviceCM* layer = fMCRec->fLayer; // may be null
1227 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001228 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001229
1230 // now do the normal restore()
1231 fMCRec->~MCRec(); // balanced in save()
1232 fMCStack.pop_back();
1233 fMCRec = (MCRec*)fMCStack.back();
1234
1235 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1236 since if we're being recorded, we don't want to record this (the
1237 recorder will have already recorded the restore).
1238 */
bsalomon49f085d2014-09-05 13:34:00 -07001239 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001240 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001241 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001242 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
reed61f501f2015-04-29 08:34:00 -07001243 layer->fPaint, layer->fDeviceIsBitmapDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001244 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001245 fDeviceCMDirty = true;
halcanary385fe4d2015-08-26 13:07:48 -07001246 delete layer;
reedb679ca82015-04-07 04:40:48 -07001247 } else {
1248 // we're at the root
reeda499f902015-05-01 09:34:31 -07001249 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001250 layer->~DeviceCM();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001251 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001252 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001253}
1254
reed4a8126e2014-09-22 07:29:03 -07001255SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001256 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001257 props = &fProps;
1258 }
1259 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001260}
1261
reed4a8126e2014-09-22 07:29:03 -07001262SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001263 SkBaseDevice* dev = this->getDevice();
halcanary96fcdcc2015-08-27 07:41:13 -07001264 return dev ? dev->newSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001265}
1266
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001267SkImageInfo SkCanvas::imageInfo() const {
1268 SkBaseDevice* dev = this->getDevice();
1269 if (dev) {
1270 return dev->imageInfo();
1271 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001272 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001273 }
1274}
1275
1276const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
reed884e97c2015-05-26 11:31:54 -07001277 SkPixmap pmap;
1278 if (!this->onPeekPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001279 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001280 }
1281 if (info) {
1282 *info = pmap.info();
1283 }
1284 if (rowBytes) {
1285 *rowBytes = pmap.rowBytes();
1286 }
1287 return pmap.addr();
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001288}
1289
reed884e97c2015-05-26 11:31:54 -07001290bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001291 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001292 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001293}
1294
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001295void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001296 SkPixmap pmap;
1297 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001298 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001299 }
1300 if (info) {
1301 *info = pmap.info();
1302 }
1303 if (rowBytes) {
1304 *rowBytes = pmap.rowBytes();
1305 }
1306 if (origin) {
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001307 *origin = this->getTopDevice(false)->getOrigin();
1308 }
reed884e97c2015-05-26 11:31:54 -07001309 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001310}
1311
reed884e97c2015-05-26 11:31:54 -07001312bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001313 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001314 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001315}
1316
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001317SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1318 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
halcanary96fcdcc2015-08-27 07:41:13 -07001319 if (nullptr == fAddr) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001320 fInfo = canvas->imageInfo();
reed84825042014-09-02 12:50:45 -07001321 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.tryAllocPixels(fInfo)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001322 return; // failure, fAddr is nullptr
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001323 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001324 if (!canvas->readPixels(&fBitmap, 0, 0)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001325 return; // failure, fAddr is nullptr
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001326 }
1327 fAddr = fBitmap.getPixels();
1328 fRowBytes = fBitmap.rowBytes();
1329 }
1330 SkASSERT(fAddr); // success
1331}
1332
1333bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1334 if (fAddr) {
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +00001335 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001336 } else {
1337 bitmap->reset();
1338 return false;
1339 }
1340}
1341
reed@android.com8a1c16f2008-12-17 15:59:43 +00001342/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001343
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001344void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed61f501f2015-04-29 08:34:00 -07001345 const SkPaint* paint, bool deviceIsBitmapDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001346 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001347 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001348 paint = &tmp;
1349 }
reed@google.com4b226022011-01-11 18:32:13 +00001350
reed@google.com8926b162012-03-23 15:36:36 +00001351 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001352 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001353 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001354 paint = &looper.paint();
1355 SkImageFilter* filter = paint->getImageFilter();
1356 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001357 if (filter && !dstDev->canHandleImageFilter(filter)) {
reed88d064d2015-10-12 11:30:02 -07001358 SkImageFilter::DeviceProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001359 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001360 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001361 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001362 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001363 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001364 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblancobe129b22014-08-08 07:14:35 -07001365 SkAutoTUnref<SkImageFilter::Cache> cache(dstDev->getImageFilterCache());
reedc9b5f8b2015-10-22 13:20:20 -07001366 SkImageFilter::Context ctx(matrix, clipBounds, cache.get(),
1367 SkImageFilter::kApprox_SizeConstraint);
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001368 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001369 SkPaint tmpUnfiltered(*paint);
halcanary96fcdcc2015-08-27 07:41:13 -07001370 tmpUnfiltered.setImageFilter(nullptr);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001371 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1372 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001373 }
reed61f501f2015-04-29 08:34:00 -07001374 } else if (deviceIsBitmapDevice) {
reed9572a102015-05-26 19:22:17 -07001375 const SkBitmap& src = static_cast<SkBitmapDevice*>(srcDev)->fBitmap;
reed61f501f2015-04-29 08:34:00 -07001376 dstDev->drawSprite(iter, src, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001377 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001378 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001379 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001380 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001381 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001382}
1383
reed41af9662015-01-05 07:49:08 -08001384void SkCanvas::onDrawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint* paint) {
reed0acf1b42014-12-22 16:12:38 -08001385 if (gTreatSpriteAsBitmap) {
1386 this->save();
1387 this->resetMatrix();
1388 this->drawBitmap(bitmap, SkIntToScalar(x), SkIntToScalar(y), paint);
1389 this->restore();
1390 return;
1391 }
1392
danakj9881d632014-11-26 12:41:06 -08001393 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawSprite()");
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001394 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001395 return;
1396 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001397 SkDEBUGCODE(bitmap.validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001398
reed@google.com8926b162012-03-23 15:36:36 +00001399 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001400 if (nullptr == paint) {
reed@google.com8926b162012-03-23 15:36:36 +00001401 paint = &tmp;
1402 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001403
reed@google.com8926b162012-03-23 15:36:36 +00001404 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001405
reed@google.com8926b162012-03-23 15:36:36 +00001406 while (iter.next()) {
1407 paint = &looper.paint();
1408 SkImageFilter* filter = paint->getImageFilter();
1409 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1410 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
reed88d064d2015-10-12 11:30:02 -07001411 SkImageFilter::DeviceProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001412 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001413 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001414 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001415 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
halcanaryf622a6c2014-10-24 12:54:53 -07001416 const SkIRect clipBounds = bitmap.bounds();
senorblancobe129b22014-08-08 07:14:35 -07001417 SkAutoTUnref<SkImageFilter::Cache> cache(iter.fDevice->getImageFilterCache());
reedc9b5f8b2015-10-22 13:20:20 -07001418 SkImageFilter::Context ctx(matrix, clipBounds, cache.get(),
1419 SkImageFilter::kApprox_SizeConstraint);
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001420 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001421 SkPaint tmpUnfiltered(*paint);
halcanary96fcdcc2015-08-27 07:41:13 -07001422 tmpUnfiltered.setImageFilter(nullptr);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001423 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001424 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001425 }
1426 } else {
1427 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1428 }
1429 }
1430 LOOPER_END
1431}
1432
reed@android.com8a1c16f2008-12-17 15:59:43 +00001433/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001434void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001435 SkMatrix m;
1436 m.setTranslate(dx, dy);
1437 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001438}
1439
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001440void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001441 SkMatrix m;
1442 m.setScale(sx, sy);
1443 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001444}
1445
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001446void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001447 SkMatrix m;
1448 m.setRotate(degrees);
1449 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001450}
1451
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001452void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001453 SkMatrix m;
1454 m.setSkew(sx, sy);
1455 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001456}
1457
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001458void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001459 if (matrix.isIdentity()) {
1460 return;
1461 }
1462
reed2ff1fce2014-12-11 07:07:37 -08001463 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001464 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001465 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001466 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001467
1468 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001469}
1470
reed86a17e72015-05-14 12:25:22 -07001471void SkCanvas::setMatrix(const SkMatrix& matrix) {
1472 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001473 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001474 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001475 fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001476 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001477}
1478
reed@android.com8a1c16f2008-12-17 15:59:43 +00001479void SkCanvas::resetMatrix() {
1480 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001481
reed@android.com8a1c16f2008-12-17 15:59:43 +00001482 matrix.reset();
1483 this->setMatrix(matrix);
1484}
1485
1486//////////////////////////////////////////////////////////////////////////////
1487
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001488void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001489 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001490 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1491 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001492}
1493
1494void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001495#ifdef SK_ENABLE_CLIP_QUICKREJECT
1496 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001497 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001498 return false;
1499 }
1500
reed@google.com3b3e8952012-08-16 20:53:31 +00001501 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001502 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001503 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001504
reed687fa1c2015-04-07 08:00:56 -07001505 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001506 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001507 }
1508 }
1509#endif
1510
reed@google.com5c3d1472011-02-22 19:12:23 +00001511 AutoValidateClip avc(this);
1512
reed@android.com8a1c16f2008-12-17 15:59:43 +00001513 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001514 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001515 if (!fAllowSoftClip) {
1516 edgeStyle = kHard_ClipEdgeStyle;
1517 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001518
reed1f836ee2014-07-07 07:49:34 -07001519 if (fMCRec->fMatrix.rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001520 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001521 // the matrix. This means we don't have to a) make a path, and b) tell
1522 // the region code to scan-convert the path, only to discover that it
1523 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001524 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001525
reed1f836ee2014-07-07 07:49:34 -07001526 fMCRec->fMatrix.mapRect(&r, rect);
reed687fa1c2015-04-07 08:00:56 -07001527 fClipStack->clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reedd9544982014-09-09 18:46:22 -07001528 fMCRec->fRasterClip.op(r, this->getBaseLayerSize(), op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001529 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001530 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001531 // and clip against that, since it can handle any matrix. However, to
1532 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1533 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001534 SkPath path;
1535
1536 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001537 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001538 }
1539}
1540
reed73e714e2014-09-04 09:02:23 -07001541static void rasterclip_path(SkRasterClip* rc, const SkCanvas* canvas, const SkPath& devPath,
1542 SkRegion::Op op, bool doAA) {
reedd64c9482014-09-05 17:37:38 -07001543 rc->op(devPath, canvas->getBaseLayerSize(), op, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001544}
1545
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001546void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001547 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001548 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001549 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001550 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1551 } else {
1552 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001553 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001554}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001555
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001556void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001557 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001558 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001559 AutoValidateClip avc(this);
1560
1561 fDeviceCMDirty = true;
1562 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001563 if (!fAllowSoftClip) {
1564 edgeStyle = kHard_ClipEdgeStyle;
1565 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001566
reed687fa1c2015-04-07 08:00:56 -07001567 fClipStack->clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001568
1569 SkPath devPath;
1570 devPath.addRRect(transformedRRect);
1571
reed73e714e2014-09-04 09:02:23 -07001572 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001573 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001574 }
1575
1576 SkPath path;
1577 path.addRRect(rrect);
1578 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001579 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001580}
1581
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001582void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001583 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001584 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1585 SkRect r;
1586 if (!path.isInverseFillType() && path.isRect(&r)) {
1587 this->onClipRect(r, op, edgeStyle);
1588 } else {
1589 this->onClipPath(path, op, edgeStyle);
1590 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001591}
1592
1593void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001594#ifdef SK_ENABLE_CLIP_QUICKREJECT
1595 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001596 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001597 return false;
1598 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001599
reed@google.com3b3e8952012-08-16 20:53:31 +00001600 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001601 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001602 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001603
reed687fa1c2015-04-07 08:00:56 -07001604 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001605 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001606 }
1607 }
1608#endif
1609
reed@google.com5c3d1472011-02-22 19:12:23 +00001610 AutoValidateClip avc(this);
1611
reed@android.com8a1c16f2008-12-17 15:59:43 +00001612 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001613 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001614 if (!fAllowSoftClip) {
1615 edgeStyle = kHard_ClipEdgeStyle;
1616 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001617
1618 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001619 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001620
reed@google.comfe701122011-11-08 19:41:23 +00001621 // Check if the transfomation, or the original path itself
1622 // made us empty. Note this can also happen if we contained NaN
1623 // values. computing the bounds detects this, and will set our
1624 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1625 if (devPath.getBounds().isEmpty()) {
1626 // resetting the path will remove any NaN or other wanky values
1627 // that might upset our scan converter.
1628 devPath.reset();
1629 }
1630
reed@google.com5c3d1472011-02-22 19:12:23 +00001631 // if we called path.swap() we could avoid a deep copy of this path
reed687fa1c2015-04-07 08:00:56 -07001632 fClipStack->clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001633
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001634 if (fAllowSimplifyClip) {
fmalita1a481fe2015-02-04 07:39:34 -08001635 bool clipIsAA = getClipStack()->asPath(&devPath);
1636 if (clipIsAA) {
1637 edgeStyle = kSoft_ClipEdgeStyle;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001638 }
fmalita1a481fe2015-02-04 07:39:34 -08001639
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001640 op = SkRegion::kReplace_Op;
1641 }
1642
reed73e714e2014-09-04 09:02:23 -07001643 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001644}
1645
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001646void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed2ff1fce2014-12-11 07:07:37 -08001647 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001648 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001649}
1650
1651void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001652 AutoValidateClip avc(this);
1653
reed@android.com8a1c16f2008-12-17 15:59:43 +00001654 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001655 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001656
reed@google.com5c3d1472011-02-22 19:12:23 +00001657 // todo: signal fClipStack that we have a region, and therefore (I guess)
1658 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001659 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001660
reed1f836ee2014-07-07 07:49:34 -07001661 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001662}
1663
reed@google.com819c9212011-02-23 18:56:55 +00001664#ifdef SK_DEBUG
1665void SkCanvas::validateClip() const {
1666 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001667 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001668 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001669 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001670 return;
1671 }
1672
reed@google.com819c9212011-02-23 18:56:55 +00001673 SkIRect ir;
1674 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001675 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001676
reed687fa1c2015-04-07 08:00:56 -07001677 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001678 const SkClipStack::Element* element;
halcanary96fcdcc2015-08-27 07:41:13 -07001679 while ((element = iter.next()) != nullptr) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001680 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001681 case SkClipStack::Element::kRect_Type:
1682 element->getRect().round(&ir);
1683 tmpClip.op(ir, element->getOp());
1684 break;
1685 case SkClipStack::Element::kEmpty_Type:
1686 tmpClip.setEmpty();
1687 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001688 default: {
1689 SkPath path;
1690 element->asPath(&path);
reed73e714e2014-09-04 09:02:23 -07001691 rasterclip_path(&tmpClip, this, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001692 break;
1693 }
reed@google.com819c9212011-02-23 18:56:55 +00001694 }
1695 }
reed@google.com819c9212011-02-23 18:56:55 +00001696}
1697#endif
1698
reed@google.com90c07ea2012-04-13 13:50:27 +00001699void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001700 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001701 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001702
halcanary96fcdcc2015-08-27 07:41:13 -07001703 while ((element = iter.next()) != nullptr) {
fmalitac3b589a2014-06-05 12:40:07 -07001704 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001705 }
1706}
1707
reed@google.com5c3d1472011-02-22 19:12:23 +00001708///////////////////////////////////////////////////////////////////////////////
1709
reed@google.com754de5f2014-02-24 19:38:20 +00001710bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001711 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001712}
1713
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001714bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001715 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001716}
1717
reed@google.com3b3e8952012-08-16 20:53:31 +00001718bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001719 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001720 return true;
1721
reed1f836ee2014-07-07 07:49:34 -07001722 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001723 return true;
1724 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001725
reed1f836ee2014-07-07 07:49:34 -07001726 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001727 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001728 fMCRec->fMatrix.mapRect(&dst, rect);
reedb07a94f2014-11-19 05:03:18 -08001729 return !SkIRect::Intersects(dst.roundOut(), fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001730 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001731 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001732
reed@android.coma380ae42009-07-21 01:17:02 +00001733 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001734 // TODO: should we use | instead, or compare all 4 at once?
1735 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001736 return true;
1737 }
reed@google.comc0784db2013-12-13 21:16:12 +00001738 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001739 return true;
1740 }
1741 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001742 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001743}
1744
reed@google.com3b3e8952012-08-16 20:53:31 +00001745bool SkCanvas::quickReject(const SkPath& path) const {
1746 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001747}
1748
reed@google.com3b3e8952012-08-16 20:53:31 +00001749bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001750 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001751 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001752 return false;
1753 }
1754
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001755 SkMatrix inverse;
1756 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001757 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001758 if (bounds) {
1759 bounds->setEmpty();
1760 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001761 return false;
1762 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001763
bsalomon49f085d2014-09-05 13:34:00 -07001764 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001765 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001766 // adjust it outwards in case we are antialiasing
1767 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001768
reed@google.com8f4d2302013-12-17 16:44:46 +00001769 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1770 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001771 inverse.mapRect(bounds, r);
1772 }
1773 return true;
1774}
1775
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001776bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001777 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001778 if (clip.isEmpty()) {
1779 if (bounds) {
1780 bounds->setEmpty();
1781 }
1782 return false;
1783 }
1784
bsalomon49f085d2014-09-05 13:34:00 -07001785 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001786 *bounds = clip.getBounds();
1787 }
1788 return true;
1789}
1790
reed@android.com8a1c16f2008-12-17 15:59:43 +00001791const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001792 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001793}
1794
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001795const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001796 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001797}
1798
reed@google.com9c135db2014-03-12 18:28:35 +00001799GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1800 SkBaseDevice* dev = this->getTopDevice();
halcanary96fcdcc2015-08-27 07:41:13 -07001801 return dev ? dev->accessRenderTarget() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001802}
1803
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001804GrContext* SkCanvas::getGrContext() {
1805#if SK_SUPPORT_GPU
1806 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07001807 if (device) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001808 GrRenderTarget* renderTarget = device->accessRenderTarget();
bsalomon49f085d2014-09-05 13:34:00 -07001809 if (renderTarget) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001810 return renderTarget->getContext();
1811 }
1812 }
1813#endif
1814
halcanary96fcdcc2015-08-27 07:41:13 -07001815 return nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001816
1817}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001818
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001819void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1820 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001821 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001822 if (outer.isEmpty()) {
1823 return;
1824 }
1825 if (inner.isEmpty()) {
1826 this->drawRRect(outer, paint);
1827 return;
1828 }
1829
1830 // We don't have this method (yet), but technically this is what we should
1831 // be able to assert...
1832 // SkASSERT(outer.contains(inner));
1833 //
1834 // For now at least check for containment of bounds
1835 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1836
1837 this->onDrawDRRect(outer, inner, paint);
1838}
1839
reed41af9662015-01-05 07:49:08 -08001840// These need to stop being virtual -- clients need to override the onDraw... versions
1841
1842void SkCanvas::drawPaint(const SkPaint& paint) {
1843 this->onDrawPaint(paint);
1844}
1845
1846void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1847 this->onDrawRect(r, paint);
1848}
1849
1850void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1851 this->onDrawOval(r, paint);
1852}
1853
1854void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1855 this->onDrawRRect(rrect, paint);
1856}
1857
1858void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1859 this->onDrawPoints(mode, count, pts, paint);
1860}
1861
1862void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
1863 const SkPoint texs[], const SkColor colors[], SkXfermode* xmode,
1864 const uint16_t indices[], int indexCount, const SkPaint& paint) {
1865 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
1866 indices, indexCount, paint);
1867}
1868
1869void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1870 this->onDrawPath(path, paint);
1871}
1872
reeda85d4d02015-05-06 12:56:48 -07001873void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
1874 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001875}
1876
reede47829b2015-08-06 10:02:53 -07001877void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1878 const SkPaint* paint, SrcRectConstraint constraint) {
1879 if (dst.isEmpty() || src.isEmpty()) {
1880 return;
1881 }
1882 this->onDrawImageRect(image, &src, dst, paint, constraint);
1883}
reed41af9662015-01-05 07:49:08 -08001884
reed84984ef2015-07-17 07:09:43 -07001885void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1886 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001887 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001888}
1889
reede47829b2015-08-06 10:02:53 -07001890void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
1891 SrcRectConstraint constraint) {
1892 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
1893 constraint);
1894}
reede47829b2015-08-06 10:02:53 -07001895
reed4c21dc52015-06-25 12:32:03 -07001896void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1897 const SkPaint* paint) {
1898 if (dst.isEmpty()) {
1899 return;
1900 }
1901 if (!SkNinePatchIter::Valid(image->width(), image->height(), center)) {
reede47829b2015-08-06 10:02:53 -07001902 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001903 }
1904 this->onDrawImageNine(image, center, dst, paint);
1905}
1906
reed41af9662015-01-05 07:49:08 -08001907void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001908 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001909 return;
1910 }
reed41af9662015-01-05 07:49:08 -08001911 this->onDrawBitmap(bitmap, dx, dy, paint);
1912}
1913
reede47829b2015-08-06 10:02:53 -07001914void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07001915 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001916 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07001917 return;
1918 }
reede47829b2015-08-06 10:02:53 -07001919 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08001920}
1921
reed84984ef2015-07-17 07:09:43 -07001922void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
1923 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001924 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001925}
1926
reede47829b2015-08-06 10:02:53 -07001927void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
1928 SrcRectConstraint constraint) {
1929 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
1930 constraint);
1931}
reede47829b2015-08-06 10:02:53 -07001932
reed41af9662015-01-05 07:49:08 -08001933void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
1934 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001935 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001936 return;
1937 }
reed4c21dc52015-06-25 12:32:03 -07001938 if (!SkNinePatchIter::Valid(bitmap.width(), bitmap.height(), center)) {
reeda5517e22015-07-14 10:54:12 -07001939 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001940 }
reed41af9662015-01-05 07:49:08 -08001941 this->onDrawBitmapNine(bitmap, center, dst, paint);
1942}
1943
1944void SkCanvas::drawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001945 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001946 return;
1947 }
reed41af9662015-01-05 07:49:08 -08001948 this->onDrawSprite(bitmap, left, top, paint);
1949}
1950
reed71c3c762015-06-24 10:29:17 -07001951void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
1952 const SkColor colors[], int count, SkXfermode::Mode mode,
1953 const SkRect* cull, const SkPaint* paint) {
1954 if (count <= 0) {
1955 return;
1956 }
1957 SkASSERT(atlas);
1958 SkASSERT(xform);
1959 SkASSERT(tex);
1960 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
1961}
1962
reede47829b2015-08-06 10:02:53 -07001963void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
1964 const SkPaint* paint, SrcRectConstraint constraint) {
1965 if (src) {
1966 this->drawImageRect(image, *src, dst, paint, constraint);
1967 } else {
1968 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
1969 dst, paint, constraint);
1970 }
1971}
1972void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
1973 const SkPaint* paint, SrcRectConstraint constraint) {
1974 if (src) {
1975 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
1976 } else {
1977 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
1978 dst, paint, constraint);
1979 }
1980}
1981
reed@android.com8a1c16f2008-12-17 15:59:43 +00001982//////////////////////////////////////////////////////////////////////////////
1983// These are the virtual drawing methods
1984//////////////////////////////////////////////////////////////////////////////
1985
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001986void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07001987 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001988 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1989 }
1990}
1991
reed41af9662015-01-05 07:49:08 -08001992void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001993 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001994 this->internalDrawPaint(paint);
1995}
1996
1997void SkCanvas::internalDrawPaint(const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07001998 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001999
2000 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002001 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002002 }
2003
reed@google.com4e2b3d32011-04-07 14:18:59 +00002004 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002005}
2006
reed41af9662015-01-05 07:49:08 -08002007void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2008 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002009 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002010 if ((long)count <= 0) {
2011 return;
2012 }
2013
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002014 SkRect r, storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002015 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002016 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002017 // special-case 2 points (common for drawing a single line)
2018 if (2 == count) {
2019 r.set(pts[0], pts[1]);
2020 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00002021 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002022 }
senorblanco87e066e2015-10-28 11:23:36 -07002023 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2024 return;
2025 }
2026 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002027 }
reed@google.coma584aed2012-05-16 14:06:02 +00002028
halcanary96fcdcc2015-08-27 07:41:13 -07002029 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002030
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002031 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002032
reed@android.com8a1c16f2008-12-17 15:59:43 +00002033 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002034 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002035 }
reed@google.com4b226022011-01-11 18:32:13 +00002036
reed@google.com4e2b3d32011-04-07 14:18:59 +00002037 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002038}
2039
reed41af9662015-01-05 07:49:08 -08002040void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002041 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002042 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002043 const SkRect* bounds = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002044 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08002045 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
2046 // To prevent accidental rejecting at this stage, we have to sort it before we check.
2047 SkRect tmp(r);
2048 tmp.sort();
2049
senorblanco87e066e2015-10-28 11:23:36 -07002050 if (this->quickReject(paint.computeFastBounds(tmp, &storage))) {
2051 return;
2052 }
2053 bounds = &r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002054 }
reed@google.com4b226022011-01-11 18:32:13 +00002055
reedc83a2972015-07-16 07:40:45 -07002056 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002057
2058 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002059 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002060 }
2061
reed@google.com4e2b3d32011-04-07 14:18:59 +00002062 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002063}
2064
reed41af9662015-01-05 07:49:08 -08002065void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002066 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002067 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002068 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002069 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002070 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2071 return;
2072 }
2073 bounds = &oval;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002074 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002075
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002076 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002077
2078 while (iter.next()) {
2079 iter.fDevice->drawOval(iter, oval, looper.paint());
2080 }
2081
2082 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002083}
2084
reed41af9662015-01-05 07:49:08 -08002085void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002086 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002087 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002088 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002089 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002090 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2091 return;
2092 }
2093 bounds = &rrect.getBounds();
reed@google.com4ed0fb72012-12-12 20:48:18 +00002094 }
2095
2096 if (rrect.isRect()) {
2097 // call the non-virtual version
2098 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002099 return;
2100 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002101 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002102 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2103 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002104 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002105
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002106 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002107
2108 while (iter.next()) {
2109 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2110 }
2111
2112 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002113}
2114
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002115void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2116 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002117 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002118 const SkRect* bounds = nullptr;
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002119 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002120 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2121 return;
2122 }
2123 bounds = &outer.getBounds();
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002124 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002125
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002126 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002127
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002128 while (iter.next()) {
2129 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2130 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002131
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002132 LOOPER_END
2133}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002134
reed41af9662015-01-05 07:49:08 -08002135void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002136 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002137 if (!path.isFinite()) {
2138 return;
2139 }
2140
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002141 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002142 const SkRect* bounds = nullptr;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002143 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002144 const SkRect& pathBounds = path.getBounds();
senorblanco87e066e2015-10-28 11:23:36 -07002145 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2146 return;
2147 }
2148 bounds = &pathBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002149 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002150
2151 const SkRect& r = path.getBounds();
2152 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002153 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002154 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002155 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002156 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002157 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002158
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002159 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002160
2161 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002162 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002163 }
2164
reed@google.com4e2b3d32011-04-07 14:18:59 +00002165 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002166}
2167
reeda85d4d02015-05-06 12:56:48 -07002168void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002169 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002170 SkRect bounds = SkRect::MakeXYWH(x, y,
2171 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002172 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002173 SkRect tmp = bounds;
2174 if (paint) {
2175 paint->computeFastBounds(tmp, &tmp);
2176 }
2177 if (this->quickReject(tmp)) {
2178 return;
2179 }
reeda85d4d02015-05-06 12:56:48 -07002180 }
2181
2182 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002183 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002184 paint = lazy.init();
2185 }
2186
2187 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &bounds)
2188
2189 while (iter.next()) {
2190 iter.fDevice->drawImage(iter, image, x, y, looper.paint());
2191 }
2192
2193 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002194}
2195
reed41af9662015-01-05 07:49:08 -08002196void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002197 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002198 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
reeda85d4d02015-05-06 12:56:48 -07002199 SkRect storage;
2200 const SkRect* bounds = &dst;
halcanary96fcdcc2015-08-27 07:41:13 -07002201 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002202 storage = dst;
2203 if (paint) {
2204 paint->computeFastBounds(dst, &storage);
2205 }
2206 if (this->quickReject(storage)) {
2207 return;
2208 }
reeda85d4d02015-05-06 12:56:48 -07002209 }
2210 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002211 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002212 paint = lazy.init();
2213 }
2214
reedc83a2972015-07-16 07:40:45 -07002215 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, bounds,
2216 image->isOpaque())
reeda85d4d02015-05-06 12:56:48 -07002217
2218 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002219 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002220 }
2221
2222 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002223}
2224
reed41af9662015-01-05 07:49:08 -08002225void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002226 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002227 SkDEBUGCODE(bitmap.validate();)
2228
reed33366972015-10-08 09:22:02 -07002229 if (bitmap.drawsNothing()) {
2230 return;
2231 }
2232
2233 SkLazyPaint lazy;
2234 if (nullptr == paint) {
2235 paint = lazy.init();
2236 }
2237
2238 const SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2239
2240 SkRect storage;
2241 const SkRect* bounds = nullptr;
2242 if (paint->canComputeFastBounds()) {
2243 bitmap.getBounds(&storage);
2244 matrix.mapRect(&storage);
senorblanco87e066e2015-10-28 11:23:36 -07002245 SkRect tmp = storage;
2246 if (this->quickReject(paint->computeFastBounds(tmp, &tmp))) {
2247 return;
2248 }
2249 bounds = &storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002250 }
reed@google.com4b226022011-01-11 18:32:13 +00002251
reed33366972015-10-08 09:22:02 -07002252 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
2253
2254 while (iter.next()) {
2255 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
2256 }
2257
2258 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002259}
2260
reed@google.com9987ec32011-09-07 11:57:52 +00002261// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002262void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002263 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002264 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002265 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002266 return;
2267 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002268
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002269 SkRect storage;
2270 const SkRect* bounds = &dst;
halcanary96fcdcc2015-08-27 07:41:13 -07002271 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002272 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2273 return;
2274 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002275 }
reed@google.com3d608122011-11-21 15:16:16 +00002276
reed@google.com33535f32012-09-25 15:37:50 +00002277 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002278 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002279 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002280 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002281
reedc83a2972015-07-16 07:40:45 -07002282 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, bounds,
2283 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002284
reed@google.com33535f32012-09-25 15:37:50 +00002285 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002286 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002287 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002288
reed@google.com33535f32012-09-25 15:37:50 +00002289 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002290}
2291
reed41af9662015-01-05 07:49:08 -08002292void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002293 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002294 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002295 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002296 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002297}
2298
reed4c21dc52015-06-25 12:32:03 -07002299void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2300 const SkPaint* paint) {
2301 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
2302
2303 SkRect storage;
2304 const SkRect* bounds = &dst;
halcanary96fcdcc2015-08-27 07:41:13 -07002305 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002306 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2307 return;
2308 }
reed@google.com3d608122011-11-21 15:16:16 +00002309 }
reed4c21dc52015-06-25 12:32:03 -07002310
2311 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002312 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002313 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002314 }
reed4c21dc52015-06-25 12:32:03 -07002315
2316 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
2317
2318 while (iter.next()) {
2319 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002320 }
reed4c21dc52015-06-25 12:32:03 -07002321
2322 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002323}
2324
reed41af9662015-01-05 07:49:08 -08002325void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2326 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002327 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002328 SkDEBUGCODE(bitmap.validate();)
2329
reed4c21dc52015-06-25 12:32:03 -07002330 SkRect storage;
2331 const SkRect* bounds = &dst;
halcanary96fcdcc2015-08-27 07:41:13 -07002332 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002333 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2334 return;
2335 }
reed4c21dc52015-06-25 12:32:03 -07002336 }
2337
2338 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002339 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002340 paint = lazy.init();
2341 }
2342
2343 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
2344
2345 while (iter.next()) {
2346 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2347 }
2348
2349 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002350}
2351
reed@google.comf67e4cf2011-03-15 20:56:58 +00002352class SkDeviceFilteredPaint {
2353public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002354 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002355 uint32_t filteredFlags = device->filterTextFlags(paint);
2356 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002357 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002358 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002359 fPaint = newPaint;
2360 } else {
2361 fPaint = &paint;
2362 }
2363 }
2364
reed@google.comf67e4cf2011-03-15 20:56:58 +00002365 const SkPaint& paint() const { return *fPaint; }
2366
2367private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002368 const SkPaint* fPaint;
2369 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002370};
2371
bungeman@google.com52c748b2011-08-22 21:30:43 +00002372void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2373 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002374 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002375 draw.fDevice->drawRect(draw, r, paint);
2376 } else {
2377 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002378 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002379 draw.fDevice->drawRect(draw, r, p);
2380 }
2381}
2382
2383void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2384 const char text[], size_t byteLength,
2385 SkScalar x, SkScalar y) {
halcanary96fcdcc2015-08-27 07:41:13 -07002386 SkASSERT(byteLength == 0 || text != nullptr);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002387
2388 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07002389 if (text == nullptr || byteLength == 0 ||
bungeman@google.com52c748b2011-08-22 21:30:43 +00002390 draw.fClip->isEmpty() ||
halcanary96fcdcc2015-08-27 07:41:13 -07002391 (paint.getAlpha() == 0 && paint.getXfermode() == nullptr)) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002392 return;
2393 }
2394
2395 SkScalar width = 0;
2396 SkPoint start;
2397
2398 start.set(0, 0); // to avoid warning
2399 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2400 SkPaint::kStrikeThruText_Flag)) {
2401 width = paint.measureText(text, byteLength);
2402
2403 SkScalar offsetX = 0;
2404 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2405 offsetX = SkScalarHalf(width);
2406 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2407 offsetX = width;
2408 }
2409 start.set(x - offsetX, y);
2410 }
2411
2412 if (0 == width) {
2413 return;
2414 }
2415
2416 uint32_t flags = paint.getFlags();
2417
2418 if (flags & (SkPaint::kUnderlineText_Flag |
2419 SkPaint::kStrikeThruText_Flag)) {
2420 SkScalar textSize = paint.getTextSize();
2421 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2422 SkRect r;
2423
2424 r.fLeft = start.fX;
2425 r.fRight = start.fX + width;
2426
2427 if (flags & SkPaint::kUnderlineText_Flag) {
2428 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2429 start.fY);
2430 r.fTop = offset;
2431 r.fBottom = offset + height;
2432 DrawRect(draw, paint, r, textSize);
2433 }
2434 if (flags & SkPaint::kStrikeThruText_Flag) {
2435 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2436 start.fY);
2437 r.fTop = offset;
2438 r.fBottom = offset + height;
2439 DrawRect(draw, paint, r, textSize);
2440 }
2441 }
2442}
2443
reed@google.come0d9ce82014-04-23 04:00:17 +00002444void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2445 const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002446 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002447
2448 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002449 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002450 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002451 DrawTextDecorations(iter, dfp.paint(),
2452 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002453 }
2454
reed@google.com4e2b3d32011-04-07 14:18:59 +00002455 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002456}
2457
reed@google.come0d9ce82014-04-23 04:00:17 +00002458void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2459 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002460 SkPoint textOffset = SkPoint::Make(0, 0);
2461
halcanary96fcdcc2015-08-27 07:41:13 -07002462 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002463
reed@android.com8a1c16f2008-12-17 15:59:43 +00002464 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002465 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002466 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002467 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002468 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002469
reed@google.com4e2b3d32011-04-07 14:18:59 +00002470 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002471}
2472
reed@google.come0d9ce82014-04-23 04:00:17 +00002473void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2474 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002475
2476 SkPoint textOffset = SkPoint::Make(0, constY);
2477
halcanary96fcdcc2015-08-27 07:41:13 -07002478 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002479
reed@android.com8a1c16f2008-12-17 15:59:43 +00002480 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002481 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002482 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002483 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002484 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002485
reed@google.com4e2b3d32011-04-07 14:18:59 +00002486 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002487}
2488
reed@google.come0d9ce82014-04-23 04:00:17 +00002489void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2490 const SkMatrix* matrix, const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002491 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002492
reed@android.com8a1c16f2008-12-17 15:59:43 +00002493 while (iter.next()) {
2494 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002495 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002496 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002497
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002498 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002499}
2500
fmalita00d5c2c2014-08-21 08:53:26 -07002501void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2502 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002503
fmalita85d5eb92015-03-04 11:20:12 -08002504 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002505 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002506 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002507 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002508 SkRect tmp;
2509 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2510 return;
2511 }
2512 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002513 }
2514
fmalita024f9962015-03-03 19:08:17 -08002515 // We cannot filter in the looper as we normally do, because the paint is
2516 // incomplete at this point (text-related attributes are embedded within blob run paints).
2517 SkDrawFilter* drawFilter = fMCRec->fFilter;
halcanary96fcdcc2015-08-27 07:41:13 -07002518 fMCRec->fFilter = nullptr;
fmalita024f9962015-03-03 19:08:17 -08002519
fmalita85d5eb92015-03-04 11:20:12 -08002520 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002521
fmalitaaa1b9122014-08-28 14:32:24 -07002522 while (iter.next()) {
2523 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002524 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002525 }
2526
fmalitaaa1b9122014-08-28 14:32:24 -07002527 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002528
2529 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002530}
2531
reed@google.come0d9ce82014-04-23 04:00:17 +00002532// These will become non-virtual, so they always call the (virtual) onDraw... method
2533void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2534 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002535 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002536 this->onDrawText(text, byteLength, x, y, paint);
2537}
2538void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2539 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002540 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002541 this->onDrawPosText(text, byteLength, pos, paint);
2542}
2543void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2544 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002545 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002546 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2547}
2548void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2549 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002550 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002551 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2552}
fmalita00d5c2c2014-08-21 08:53:26 -07002553void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2554 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002555 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
bsalomon49f085d2014-09-05 13:34:00 -07002556 if (blob) {
fmalita00d5c2c2014-08-21 08:53:26 -07002557 this->onDrawTextBlob(blob, x, y, paint);
2558 }
2559}
reed@google.come0d9ce82014-04-23 04:00:17 +00002560
reed41af9662015-01-05 07:49:08 -08002561void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2562 const SkPoint verts[], const SkPoint texs[],
2563 const SkColor colors[], SkXfermode* xmode,
2564 const uint16_t indices[], int indexCount,
2565 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002566 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
halcanary96fcdcc2015-08-27 07:41:13 -07002567 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
reed@google.com4b226022011-01-11 18:32:13 +00002568
reed@android.com8a1c16f2008-12-17 15:59:43 +00002569 while (iter.next()) {
2570 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002571 colors, xmode, indices, indexCount,
2572 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002573 }
reed@google.com4b226022011-01-11 18:32:13 +00002574
reed@google.com4e2b3d32011-04-07 14:18:59 +00002575 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002576}
2577
dandovb3c9d1c2014-08-12 08:34:29 -07002578void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2579 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002580 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
halcanary96fcdcc2015-08-27 07:41:13 -07002581 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002582 return;
2583 }
mtklein6cfa73a2014-08-13 13:33:49 -07002584
dandovecfff212014-08-04 10:02:00 -07002585 // Since a patch is always within the convex hull of the control points, we discard it when its
2586 // bounding rectangle is completely outside the current clip.
2587 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002588 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002589 if (this->quickReject(bounds)) {
2590 return;
2591 }
mtklein6cfa73a2014-08-13 13:33:49 -07002592
dandovb3c9d1c2014-08-12 08:34:29 -07002593 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2594}
2595
2596void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2597 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2598
halcanary96fcdcc2015-08-27 07:41:13 -07002599 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002600
dandovecfff212014-08-04 10:02:00 -07002601 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002602 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002603 }
mtklein6cfa73a2014-08-13 13:33:49 -07002604
dandovecfff212014-08-04 10:02:00 -07002605 LOOPER_END
2606}
2607
reeda8db7282015-07-07 10:22:31 -07002608void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
2609 if (dr) {
2610 if (x || y) {
2611 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2612 this->onDrawDrawable(dr, &matrix);
2613 } else {
halcanary96fcdcc2015-08-27 07:41:13 -07002614 this->onDrawDrawable(dr, nullptr);
reeda8db7282015-07-07 10:22:31 -07002615 }
reed6a070dc2014-11-11 19:36:09 -08002616 }
2617}
2618
reeda8db7282015-07-07 10:22:31 -07002619void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2620 if (dr) {
2621 if (matrix && matrix->isIdentity()) {
halcanary96fcdcc2015-08-27 07:41:13 -07002622 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002623 }
2624 this->onDrawDrawable(dr, matrix);
2625 }
2626}
2627
2628void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2629 SkRect bounds = dr->getBounds();
2630 if (matrix) {
2631 matrix->mapRect(&bounds);
2632 }
2633 if (this->quickReject(bounds)) {
2634 return;
2635 }
2636 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002637}
2638
reed71c3c762015-06-24 10:29:17 -07002639void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2640 const SkColor colors[], int count, SkXfermode::Mode mode,
2641 const SkRect* cull, const SkPaint* paint) {
2642 if (cull && this->quickReject(*cull)) {
2643 return;
2644 }
2645
2646 SkPaint pnt;
2647 if (paint) {
2648 pnt = *paint;
2649 }
2650
halcanary96fcdcc2015-08-27 07:41:13 -07002651 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr)
reed71c3c762015-06-24 10:29:17 -07002652 while (iter.next()) {
2653 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, mode, pnt);
2654 }
2655 LOOPER_END
2656}
2657
reed@android.com8a1c16f2008-12-17 15:59:43 +00002658//////////////////////////////////////////////////////////////////////////////
2659// These methods are NOT virtual, and therefore must call back into virtual
2660// methods, rather than actually drawing themselves.
2661//////////////////////////////////////////////////////////////////////////////
2662
2663void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002664 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002665 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002666 SkPaint paint;
2667
2668 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002669 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002670 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002671 }
2672 this->drawPaint(paint);
2673}
2674
reed@android.com845fdac2009-06-23 03:01:32 +00002675void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002676 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002677 SkPaint paint;
2678
2679 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002680 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002681 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002682 }
2683 this->drawPaint(paint);
2684}
2685
2686void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002687 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002688 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002689
reed@android.com8a1c16f2008-12-17 15:59:43 +00002690 pt.set(x, y);
2691 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2692}
2693
2694void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002695 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002696 SkPoint pt;
2697 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002698
reed@android.com8a1c16f2008-12-17 15:59:43 +00002699 pt.set(x, y);
2700 paint.setColor(color);
2701 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2702}
2703
2704void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2705 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002706 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002707 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002708
reed@android.com8a1c16f2008-12-17 15:59:43 +00002709 pts[0].set(x0, y0);
2710 pts[1].set(x1, y1);
2711 this->drawPoints(kLines_PointMode, 2, pts, paint);
2712}
2713
2714void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2715 SkScalar right, SkScalar bottom,
2716 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002717 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002718 SkRect r;
2719
2720 r.set(left, top, right, bottom);
2721 this->drawRect(r, paint);
2722}
2723
2724void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2725 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002726 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002727 if (radius < 0) {
2728 radius = 0;
2729 }
2730
2731 SkRect r;
2732 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002733 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002734}
2735
2736void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2737 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002738 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002739 if (rx > 0 && ry > 0) {
2740 if (paint.canComputeFastBounds()) {
2741 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002742 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002743 return;
2744 }
2745 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002746 SkRRect rrect;
2747 rrect.setRectXY(r, rx, ry);
2748 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002749 } else {
2750 this->drawRect(r, paint);
2751 }
2752}
2753
reed@android.com8a1c16f2008-12-17 15:59:43 +00002754void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2755 SkScalar sweepAngle, bool useCenter,
2756 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002757 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002758 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2759 this->drawOval(oval, paint);
2760 } else {
2761 SkPath path;
2762 if (useCenter) {
2763 path.moveTo(oval.centerX(), oval.centerY());
2764 }
2765 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2766 if (useCenter) {
2767 path.close();
2768 }
2769 this->drawPath(path, paint);
2770 }
2771}
2772
2773void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2774 const SkPath& path, SkScalar hOffset,
2775 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002776 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002777 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002778
reed@android.com8a1c16f2008-12-17 15:59:43 +00002779 matrix.setTranslate(hOffset, vOffset);
2780 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2781}
2782
reed@android.comf76bacf2009-05-13 14:00:33 +00002783///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07002784
2785/**
2786 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2787 * against the playback cost of recursing into the subpicture to get at its actual ops.
2788 *
2789 * For now we pick a conservatively small value, though measurement (and other heuristics like
2790 * the type of ops contained) may justify changing this value.
2791 */
2792#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07002793
reedd5fa1a42014-08-09 11:08:05 -07002794void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reed1c2c4412015-04-30 13:09:24 -07002795 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
bsalomon49f085d2014-09-05 13:34:00 -07002796 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002797 if (matrix && matrix->isIdentity()) {
halcanary96fcdcc2015-08-27 07:41:13 -07002798 matrix = nullptr;
reedd5fa1a42014-08-09 11:08:05 -07002799 }
reed1c2c4412015-04-30 13:09:24 -07002800 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2801 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2802 picture->playback(this);
2803 } else {
2804 this->onDrawPicture(picture, matrix, paint);
2805 }
reedd5fa1a42014-08-09 11:08:05 -07002806 }
2807}
robertphillips9b14f262014-06-04 05:40:44 -07002808
reedd5fa1a42014-08-09 11:08:05 -07002809void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2810 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07002811 if (!paint || paint->canComputeFastBounds()) {
2812 SkRect bounds = picture->cullRect();
2813 if (paint) {
2814 paint->computeFastBounds(bounds, &bounds);
2815 }
2816 if (matrix) {
2817 matrix->mapRect(&bounds);
2818 }
2819 if (this->quickReject(bounds)) {
2820 return;
2821 }
2822 }
2823
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002824 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002825 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002826 // Canvas has to first give the device the opportunity to render
2827 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002828 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002829 return; // the device has rendered the entire picture
2830 }
2831 }
2832
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002833 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07002834 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002835}
2836
reed@android.com8a1c16f2008-12-17 15:59:43 +00002837///////////////////////////////////////////////////////////////////////////////
2838///////////////////////////////////////////////////////////////////////////////
2839
2840SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
bungeman99fe8222015-08-20 07:57:51 -07002841 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002842
2843 SkASSERT(canvas);
2844
2845 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2846 fDone = !fImpl->next();
2847}
2848
2849SkCanvas::LayerIter::~LayerIter() {
2850 fImpl->~SkDrawIter();
2851}
2852
2853void SkCanvas::LayerIter::next() {
2854 fDone = !fImpl->next();
2855}
2856
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002857SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002858 return fImpl->getDevice();
2859}
2860
2861const SkMatrix& SkCanvas::LayerIter::matrix() const {
2862 return fImpl->getMatrix();
2863}
2864
2865const SkPaint& SkCanvas::LayerIter::paint() const {
2866 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07002867 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002868 paint = &fDefaultPaint;
2869 }
2870 return *paint;
2871}
2872
2873const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2874int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2875int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002876
2877///////////////////////////////////////////////////////////////////////////////
2878
fmalitac3b589a2014-06-05 12:40:07 -07002879SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002880
2881///////////////////////////////////////////////////////////////////////////////
2882
2883static bool supported_for_raster_canvas(const SkImageInfo& info) {
2884 switch (info.alphaType()) {
2885 case kPremul_SkAlphaType:
2886 case kOpaque_SkAlphaType:
2887 break;
2888 default:
2889 return false;
2890 }
2891
2892 switch (info.colorType()) {
2893 case kAlpha_8_SkColorType:
2894 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002895 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002896 break;
2897 default:
2898 return false;
2899 }
2900
2901 return true;
2902}
2903
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002904SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2905 if (!supported_for_raster_canvas(info)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002906 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002907 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002908
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002909 SkBitmap bitmap;
2910 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002911 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002912 }
halcanary385fe4d2015-08-26 13:07:48 -07002913 return new SkCanvas(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002914}
reedd5fa1a42014-08-09 11:08:05 -07002915
2916///////////////////////////////////////////////////////////////////////////////
2917
2918SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002919 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07002920 : fCanvas(canvas)
2921 , fSaveCount(canvas->getSaveCount())
2922{
bsalomon49f085d2014-09-05 13:34:00 -07002923 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002924 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07002925 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002926 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07002927 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002928 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07002929 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002930 canvas->save();
2931 }
mtklein6cfa73a2014-08-13 13:33:49 -07002932
bsalomon49f085d2014-09-05 13:34:00 -07002933 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002934 canvas->concat(*matrix);
2935 }
2936}
2937
2938SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
2939 fCanvas->restoreToCount(fSaveCount);
2940}