blob: 570435869b498dbed2bfc7d557084afe66bf2db4 [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
8#include "SkCanvas.h"
reedd5fa1a42014-08-09 11:08:05 -07009#include "SkCanvasPriv.h"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000010#include "SkBitmapDevice.h"
reeddbc3cef2015-04-29 12:18:57 -070011#include "SkColorFilter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkDraw.h"
reed3cb38402015-02-06 08:36:15 -080013#include "SkDrawable.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000014#include "SkDrawFilter.h"
15#include "SkDrawLooper.h"
joshualitt5f5a8d72015-02-25 14:09:45 -080016#include "SkErrorInternals.h"
piotaixrb5fae932014-09-24 13:03:30 -070017#include "SkImage.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000018#include "SkMetaData.h"
reed4c21dc52015-06-25 12:32:03 -070019#include "SkNinePatchIter.h"
reedc83a2972015-07-16 07:40:45 -070020#include "SkPaintPriv.h"
caryclark@google.com45a75fb2013-04-25 13:34:40 +000021#include "SkPathOps.h"
dandovb3c9d1c2014-08-12 08:34:29 -070022#include "SkPatchUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000023#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000024#include "SkRasterClip.h"
reed96472de2014-12-10 09:53:42 -080025#include "SkReadPixelsRec.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000026#include "SkRRect.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000027#include "SkSmallAllocator.h"
reed@google.com97af1a62012-08-28 12:19:02 +000028#include "SkSurface_Base.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000029#include "SkTemplates.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"
reed@android.com8a1c16f2008-12-17 15:59:43 +000034#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000035
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000036#if SK_SUPPORT_GPU
37#include "GrRenderTarget.h"
38#endif
39
reedc83a2972015-07-16 07:40:45 -070040/*
41 * Return true if the drawing this rect would hit every pixels in the canvas.
42 *
43 * Returns false if
44 * - rect does not contain the canvas' bounds
45 * - paint is not fill
46 * - paint would blur or otherwise change the coverage of the rect
47 */
48bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
49 ShaderOverrideOpacity overrideOpacity) const {
50 SK_COMPILE_ASSERT((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
51 (int)kNone_ShaderOverrideOpacity,
52 need_matching_enums0);
53 SK_COMPILE_ASSERT((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
54 (int)kOpaque_ShaderOverrideOpacity,
55 need_matching_enums1);
56 SK_COMPILE_ASSERT((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
57 (int)kNotOpaque_ShaderOverrideOpacity,
58 need_matching_enums2);
59
60 const SkISize size = this->getBaseLayerSize();
61 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
62 if (!this->getClipStack()->quickContains(bounds)) {
63 return false;
64 }
65
66 if (rect) {
67 if (!this->getTotalMatrix().rectStaysRect()) {
68 return false; // conservative
69 }
70
71 SkRect devRect;
72 this->getTotalMatrix().mapRect(&devRect, *rect);
73 if (devRect.contains(bounds)) {
74 return false;
75 }
76 }
77
78 if (paint) {
79 SkPaint::Style paintStyle = paint->getStyle();
80 if (!(paintStyle == SkPaint::kFill_Style ||
81 paintStyle == SkPaint::kStrokeAndFill_Style)) {
82 return false;
83 }
84 if (paint->getMaskFilter() || paint->getLooper()
85 || paint->getPathEffect() || paint->getImageFilter()) {
86 return false; // conservative
87 }
88 }
89 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
90}
91
92///////////////////////////////////////////////////////////////////////////////////////////////////
93
reedd990e2f2014-12-22 11:58:30 -080094static bool gIgnoreSaveLayerBounds;
95void SkCanvas::Internal_Private_SetIgnoreSaveLayerBounds(bool ignore) {
96 gIgnoreSaveLayerBounds = ignore;
97}
98bool SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds() {
99 return gIgnoreSaveLayerBounds;
100}
101
reed0acf1b42014-12-22 16:12:38 -0800102static bool gTreatSpriteAsBitmap;
103void SkCanvas::Internal_Private_SetTreatSpriteAsBitmap(bool spriteAsBitmap) {
104 gTreatSpriteAsBitmap = spriteAsBitmap;
105}
106bool SkCanvas::Internal_Private_GetTreatSpriteAsBitmap() {
107 return gTreatSpriteAsBitmap;
108}
109
reed@google.comda17f752012-08-16 18:27:05 +0000110// experimental for faster tiled drawing...
111//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +0000112
reed@android.com8a1c16f2008-12-17 15:59:43 +0000113//#define SK_TRACE_SAVERESTORE
114
115#ifdef SK_TRACE_SAVERESTORE
116 static int gLayerCounter;
117 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
118 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
119
120 static int gRecCounter;
121 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
122 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
123
124 static int gCanvasCounter;
125 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
126 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
127#else
128 #define inc_layer()
129 #define dec_layer()
130 #define inc_rec()
131 #define dec_rec()
132 #define inc_canvas()
133 #define dec_canvas()
134#endif
135
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000136typedef SkTLazy<SkPaint> SkLazyPaint;
137
reedc83a2972015-07-16 07:40:45 -0700138void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000139 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700140 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
141 ? SkSurface::kDiscard_ContentChangeMode
142 : SkSurface::kRetain_ContentChangeMode);
143 }
144}
145
146void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
147 ShaderOverrideOpacity overrideOpacity) {
148 if (fSurfaceBase) {
149 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
150 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
151 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
152 // and therefore we don't care which mode we're in.
153 //
154 if (fSurfaceBase->outstandingImageSnapshot()) {
155 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
156 mode = SkSurface::kDiscard_ContentChangeMode;
157 }
158 }
159 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000160 }
161}
162
reed@android.com8a1c16f2008-12-17 15:59:43 +0000163///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000164
reed4a8126e2014-09-22 07:29:03 -0700165static uint32_t filter_paint_flags(const SkSurfaceProps& props, uint32_t flags) {
166 const uint32_t propFlags = props.flags();
167 if (propFlags & SkSurfaceProps::kDisallowDither_Flag) {
168 flags &= ~SkPaint::kDither_Flag;
169 }
170 if (propFlags & SkSurfaceProps::kDisallowAntiAlias_Flag) {
171 flags &= ~SkPaint::kAntiAlias_Flag;
172 }
173 return flags;
174}
175
176///////////////////////////////////////////////////////////////////////////////
177
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000178/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179 The clip/matrix/proc are fields that reflect the top of the save/restore
180 stack. Whenever the canvas changes, it marks a dirty flag, and then before
181 these are used (assuming we're not on a layer) we rebuild these cache
182 values: they reflect the top of the save stack, but translated and clipped
183 by the device's XY offset and bitmap-bounds.
184*/
185struct DeviceCM {
186 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000187 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000188 SkRasterClip fClip;
reed@google.com6f8f2922011-03-04 22:27:10 +0000189 SkPaint* fPaint; // may be null (in the future)
reed61f501f2015-04-29 08:34:00 -0700190 const SkMatrix* fMatrix;
191 SkMatrix fMatrixStorage;
192 const bool fDeviceIsBitmapDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000193
reed96e657d2015-03-10 17:30:07 -0700194 DeviceCM(SkBaseDevice* device, const SkPaint* paint, SkCanvas* canvas,
reed86a17e72015-05-14 12:25:22 -0700195 bool conservativeRasterClip, bool deviceIsBitmapDevice)
reedd9544982014-09-09 18:46:22 -0700196 : fNext(NULL)
197 , fClip(conservativeRasterClip)
reed61f501f2015-04-29 08:34:00 -0700198 , fDeviceIsBitmapDevice(deviceIsBitmapDevice)
reedd9544982014-09-09 18:46:22 -0700199 {
200 if (NULL != device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000201 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000202 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000203 }
reed@google.com4b226022011-01-11 18:32:13 +0000204 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000205 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000206 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000207
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000208 ~DeviceCM() {
bsalomon49f085d2014-09-05 13:34:00 -0700209 if (fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000210 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211 fDevice->unref();
212 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000213 SkDELETE(fPaint);
214 }
reed@google.com4b226022011-01-11 18:32:13 +0000215
mtkleinfeaadee2015-04-08 11:25:48 -0700216 void reset(const SkIRect& bounds) {
217 SkASSERT(!fPaint);
218 SkASSERT(!fNext);
219 SkASSERT(fDevice);
220 fClip.setRect(bounds);
221 }
222
reed@google.com045e62d2011-10-24 12:19:46 +0000223 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
224 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000225 int x = fDevice->getOrigin().x();
226 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000227 int width = fDevice->width();
228 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000229
reed@android.com8a1c16f2008-12-17 15:59:43 +0000230 if ((x | y) == 0) {
231 fMatrix = &totalMatrix;
232 fClip = totalClip;
233 } else {
234 fMatrixStorage = totalMatrix;
235 fMatrixStorage.postTranslate(SkIntToScalar(-x),
236 SkIntToScalar(-y));
237 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000238
reed@android.com8a1c16f2008-12-17 15:59:43 +0000239 totalClip.translate(-x, -y, &fClip);
240 }
241
reed@google.com045e62d2011-10-24 12:19:46 +0000242 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243
244 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000245
reed@android.com8a1c16f2008-12-17 15:59:43 +0000246 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000247 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248 SkRegion::kDifference_Op);
249 }
reed@google.com4b226022011-01-11 18:32:13 +0000250
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000251 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
252
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253#ifdef SK_DEBUG
254 if (!fClip.isEmpty()) {
255 SkIRect deviceR;
256 deviceR.set(0, 0, width, height);
257 SkASSERT(deviceR.contains(fClip.getBounds()));
258 }
259#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000260 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000261};
262
263/* This is the record we keep for each save/restore level in the stack.
264 Since a level optionally copies the matrix and/or stack, we have pointers
265 for these fields. If the value is copied for this level, the copy is
266 stored in the ...Storage field, and the pointer points to that. If the
267 value is not copied for this level, we ignore ...Storage, and just point
268 at the corresponding value in the previous level in the stack.
269*/
270class SkCanvas::MCRec {
271public:
reed1f836ee2014-07-07 07:49:34 -0700272 SkDrawFilter* fFilter; // the current filter (or null)
reedd9544982014-09-09 18:46:22 -0700273 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000274 /* If there are any layers in the stack, this points to the top-most
275 one that is at or below this level in the stack (so we know what
276 bitmap/device to draw into from this level. This value is NOT
277 reference counted, since the real owner is either our fLayer field,
278 or a previous one in a lower level.)
279 */
reed2ff1fce2014-12-11 07:07:37 -0800280 DeviceCM* fTopLayer;
281 SkRasterClip fRasterClip;
282 SkMatrix fMatrix;
283 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284
reedd9544982014-09-09 18:46:22 -0700285 MCRec(bool conservativeRasterClip) : fRasterClip(conservativeRasterClip) {
reedd9544982014-09-09 18:46:22 -0700286 fFilter = NULL;
287 fLayer = NULL;
288 fTopLayer = NULL;
reed2ff1fce2014-12-11 07:07:37 -0800289 fMatrix.reset();
290 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700291
reedd9544982014-09-09 18:46:22 -0700292 // don't bother initializing fNext
293 inc_rec();
294 }
reed2ff1fce2014-12-11 07:07:37 -0800295 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
reedd9544982014-09-09 18:46:22 -0700296 fFilter = SkSafeRef(prev.fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297 fLayer = NULL;
reedd9544982014-09-09 18:46:22 -0700298 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800299 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700300
reed@android.com8a1c16f2008-12-17 15:59:43 +0000301 // don't bother initializing fNext
302 inc_rec();
303 }
304 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000305 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000306 SkDELETE(fLayer);
307 dec_rec();
308 }
mtkleinfeaadee2015-04-08 11:25:48 -0700309
310 void reset(const SkIRect& bounds) {
311 SkASSERT(fLayer);
312 SkASSERT(fDeferredSaveCount == 0);
313
314 fMatrix.reset();
315 fRasterClip.setRect(bounds);
316 fLayer->reset(bounds);
317 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000318};
319
320class SkDrawIter : public SkDraw {
321public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000322 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000323 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000324 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000325 canvas->updateDeviceCMCache();
326
reed687fa1c2015-04-07 08:00:56 -0700327 fClipStack = canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000328 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000329 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000330 }
reed@google.com4b226022011-01-11 18:32:13 +0000331
reed@android.com8a1c16f2008-12-17 15:59:43 +0000332 bool next() {
333 // skip over recs with empty clips
334 if (fSkipEmptyClips) {
335 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
336 fCurrLayer = fCurrLayer->fNext;
337 }
338 }
339
reed@google.comf68c5e22012-02-24 16:38:58 +0000340 const DeviceCM* rec = fCurrLayer;
341 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000342
343 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000344 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
345 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000346 fDevice = rec->fDevice;
reed41e010c2015-06-09 12:16:53 -0700347 if (!fDevice->accessPixels(&fDst)) {
348 fDst.reset(fDevice->imageInfo(), NULL, 0);
349 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000350 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000351 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000352
353 fCurrLayer = rec->fNext;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000354 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000355
reed@android.com8a1c16f2008-12-17 15:59:43 +0000356 return true;
357 }
358 return false;
359 }
reed@google.com4b226022011-01-11 18:32:13 +0000360
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000361 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000362 int getX() const { return fDevice->getOrigin().x(); }
363 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000364 const SkMatrix& getMatrix() const { return *fMatrix; }
365 const SkRegion& getClip() const { return *fClip; }
366 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000367
reed@android.com8a1c16f2008-12-17 15:59:43 +0000368private:
369 SkCanvas* fCanvas;
370 const DeviceCM* fCurrLayer;
371 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000372 SkBool8 fSkipEmptyClips;
373
374 typedef SkDraw INHERITED;
375};
376
377/////////////////////////////////////////////////////////////////////////////
378
reeddbc3cef2015-04-29 12:18:57 -0700379static SkPaint* set_if_needed(SkLazyPaint* lazy, const SkPaint& orig) {
380 return lazy->isValid() ? lazy->get() : lazy->set(orig);
381}
382
383/**
384 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
385 * colorfilter, else return NULL.
386 */
387static SkColorFilter* image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700388 SkImageFilter* imgf = paint.getImageFilter();
389 if (!imgf) {
390 return NULL;
391 }
392
393 SkColorFilter* imgCF;
394 if (!imgf->asAColorFilter(&imgCF)) {
395 return NULL;
396 }
397
398 SkColorFilter* paintCF = paint.getColorFilter();
399 if (NULL == paintCF) {
400 // there is no existing paint colorfilter, so we can just return the imagefilter's
401 return imgCF;
402 }
403
404 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
405 // and we need to combine them into a single colorfilter.
406 SkAutoTUnref<SkColorFilter> autoImgCF(imgCF);
407 return SkColorFilter::CreateComposeFilter(imgCF, paintCF);
reeddbc3cef2015-04-29 12:18:57 -0700408}
409
reed@android.com8a1c16f2008-12-17 15:59:43 +0000410class AutoDrawLooper {
411public:
reed4a8126e2014-09-22 07:29:03 -0700412 AutoDrawLooper(SkCanvas* canvas, const SkSurfaceProps& props, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000413 bool skipLayerForImageFilter = false,
414 const SkRect* bounds = NULL) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000415 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000416 fFilter = canvas->getDrawFilter();
reed4a8126e2014-09-22 07:29:03 -0700417 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000418 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700419 fTempLayerForImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000420 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000421
reeddbc3cef2015-04-29 12:18:57 -0700422 SkColorFilter* simplifiedCF = image_to_color_filter(fOrigPaint);
423 if (simplifiedCF) {
424 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
425 paint->setColorFilter(simplifiedCF)->unref();
426 paint->setImageFilter(NULL);
427 fPaint = paint;
428 }
429
430 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700431 /**
432 * We implement ImageFilters for a given draw by creating a layer, then applying the
433 * imagefilter to the pixels of that layer (its backing surface/image), and then
434 * we call restore() to xfer that layer to the main canvas.
435 *
436 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
437 * 2. Generate the src pixels:
438 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
439 * return (fPaint). We then draw the primitive (using srcover) into a cleared
440 * buffer/surface.
441 * 3. Restore the layer created in #1
442 * The imagefilter is passed the buffer/surface from the layer (now filled with the
443 * src pixels of the primitive). It returns a new "filtered" buffer, which we
444 * draw onto the previous layer using the xfermode from the original paint.
445 */
reed@google.com8926b162012-03-23 15:36:36 +0000446 SkPaint tmp;
reeddbc3cef2015-04-29 12:18:57 -0700447 tmp.setImageFilter(fPaint->getImageFilter());
448 tmp.setXfermode(fPaint->getXfermode());
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000449 (void)canvas->internalSaveLayer(bounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag,
reed76033be2015-03-14 10:54:31 -0700450 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700451 fTempLayerForImageFilter = true;
452 // we remove the imagefilter/xfermode inside doNext()
reed@google.com8926b162012-03-23 15:36:36 +0000453 }
454
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000455 if (SkDrawLooper* looper = paint.getLooper()) {
456 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
457 looper->contextSize());
458 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000459 fIsSimple = false;
460 } else {
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000461 fLooperContext = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000462 // can we be marked as simple?
reed5c476fb2015-04-20 08:04:21 -0700463 fIsSimple = !fFilter && !fTempLayerForImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000464 }
piotaixrb5fae932014-09-24 13:03:30 -0700465
reed4a8126e2014-09-22 07:29:03 -0700466 uint32_t oldFlags = paint.getFlags();
467 fNewPaintFlags = filter_paint_flags(props, oldFlags);
468 if (fIsSimple && (fNewPaintFlags != oldFlags)) {
reeddbc3cef2015-04-29 12:18:57 -0700469 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700470 paint->setFlags(fNewPaintFlags);
471 fPaint = paint;
472 // if we're not simple, doNext() will take care of calling setFlags()
473 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000474 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000475
reed@android.com8a1c16f2008-12-17 15:59:43 +0000476 ~AutoDrawLooper() {
reed5c476fb2015-04-20 08:04:21 -0700477 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000478 fCanvas->internalRestore();
479 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000480 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000481 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000482
reed@google.com4e2b3d32011-04-07 14:18:59 +0000483 const SkPaint& paint() const {
484 SkASSERT(fPaint);
485 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000486 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000487
reed@google.com129ec222012-05-15 13:24:09 +0000488 bool next(SkDrawFilter::Type drawType) {
489 if (fDone) {
490 return false;
491 } else if (fIsSimple) {
492 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000493 return !fPaint->nothingToDraw();
494 } else {
495 return this->doNext(drawType);
496 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000497 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000498
reed@android.com8a1c16f2008-12-17 15:59:43 +0000499private:
reeddbc3cef2015-04-29 12:18:57 -0700500 SkLazyPaint fLazyPaintInit; // base paint storage in case we need to modify it
501 SkLazyPaint fLazyPaintPerLooper; // per-draw-looper storage, so the looper can modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000502 SkCanvas* fCanvas;
503 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000504 SkDrawFilter* fFilter;
505 const SkPaint* fPaint;
506 int fSaveCount;
reed4a8126e2014-09-22 07:29:03 -0700507 uint32_t fNewPaintFlags;
reed5c476fb2015-04-20 08:04:21 -0700508 bool fTempLayerForImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000509 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000510 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000511 SkDrawLooper::Context* fLooperContext;
512 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000513
514 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000515};
516
reed@google.com129ec222012-05-15 13:24:09 +0000517bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000518 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000519 SkASSERT(!fIsSimple);
reed5c476fb2015-04-20 08:04:21 -0700520 SkASSERT(fLooperContext || fFilter || fTempLayerForImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000521
reeddbc3cef2015-04-29 12:18:57 -0700522 SkPaint* paint = fLazyPaintPerLooper.set(fLazyPaintInit.isValid() ?
523 *fLazyPaintInit.get() : fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700524 paint->setFlags(fNewPaintFlags);
reed@google.com129ec222012-05-15 13:24:09 +0000525
reed5c476fb2015-04-20 08:04:21 -0700526 if (fTempLayerForImageFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000527 paint->setImageFilter(NULL);
reed5c476fb2015-04-20 08:04:21 -0700528 paint->setXfermode(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000529 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000530
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000531 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000532 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000533 return false;
534 }
535 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000536 if (!fFilter->filter(paint, drawType)) {
537 fDone = true;
538 return false;
539 }
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000540 if (NULL == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000541 // no looper means we only draw once
542 fDone = true;
543 }
544 }
545 fPaint = paint;
546
547 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000548 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000549 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000550 }
551
552 // call this after any possible paint modifiers
553 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000554 fPaint = NULL;
555 return false;
556 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000557 return true;
558}
559
reed@android.com8a1c16f2008-12-17 15:59:43 +0000560////////// macros to place around the internal draw calls //////////////////
561
reed@google.com8926b162012-03-23 15:36:36 +0000562#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000563 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700564 AutoDrawLooper looper(this, fProps, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000565 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000566 SkDrawIter iter(this);
567
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000568#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000569 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700570 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000571 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000572 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000573
reedc83a2972015-07-16 07:40:45 -0700574#define LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, type, bounds, auxOpaque) \
575 this->predrawNotify(bounds, &paint, auxOpaque); \
576 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
577 while (looper.next(type)) { \
578 SkDrawIter iter(this);
579
reed@google.com4e2b3d32011-04-07 14:18:59 +0000580#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000581
582////////////////////////////////////////////////////////////////////////////
583
mtkleinfeaadee2015-04-08 11:25:48 -0700584void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
585 this->restoreToCount(1);
586 fCachedLocalClipBounds.setEmpty();
587 fCachedLocalClipBoundsDirty = true;
588 fClipStack->reset();
589 fMCRec->reset(bounds);
590
591 // We're peering through a lot of structs here. Only at this scope do we
592 // know that the device is an SkBitmapDevice (really an SkNoPixelsBitmapDevice).
593 static_cast<SkBitmapDevice*>(fMCRec->fLayer->fDevice)->setNewSize(bounds.size());
594}
595
reedd9544982014-09-09 18:46:22 -0700596SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
597 fConservativeRasterClip = SkToBool(flags & kConservativeRasterClip_InitFlag);
reed@google.comc0784db2013-12-13 21:16:12 +0000598 fCachedLocalClipBounds.setEmpty();
599 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000600 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000601 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700602 fDeviceCMDirty = true;
reed2ff1fce2014-12-11 07:07:37 -0800603 fSaveCount = 1;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000604 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605
reed687fa1c2015-04-07 08:00:56 -0700606 fClipStack.reset(SkNEW(SkClipStack));
607
reed@android.com8a1c16f2008-12-17 15:59:43 +0000608 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700609 new (fMCRec) MCRec(fConservativeRasterClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000610
reeda499f902015-05-01 09:34:31 -0700611 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
612 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
reed86a17e72015-05-14 12:25:22 -0700613 new (fDeviceCMStorage) DeviceCM(NULL, NULL, NULL, fConservativeRasterClip, false);
reedb679ca82015-04-07 04:40:48 -0700614
reed@android.com8a1c16f2008-12-17 15:59:43 +0000615 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000616
reed@google.com97af1a62012-08-28 12:19:02 +0000617 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000618
reedf92c8662014-08-18 08:02:43 -0700619 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700620 // The root device and the canvas should always have the same pixel geometry
621 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reed4a8126e2014-09-22 07:29:03 -0700622 if (device->forceConservativeRasterClip()) {
623 fConservativeRasterClip = true;
624 }
reedf92c8662014-08-18 08:02:43 -0700625 device->onAttachToCanvas(this);
626 fMCRec->fLayer->fDevice = SkRef(device);
reed78e27682014-11-19 08:04:34 -0800627 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
reedf92c8662014-08-18 08:02:43 -0700628 }
629 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000630}
631
reed@google.comcde92112011-07-06 20:00:52 +0000632SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000633 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700634 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000635{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000636 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000637
reedd9544982014-09-09 18:46:22 -0700638 this->init(NULL, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000639}
640
reedd9544982014-09-09 18:46:22 -0700641static SkBitmap make_nopixels(int width, int height) {
642 SkBitmap bitmap;
643 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
644 return bitmap;
645}
646
647class SkNoPixelsBitmapDevice : public SkBitmapDevice {
648public:
robertphillipsfcf78292015-06-19 11:49:52 -0700649 SkNoPixelsBitmapDevice(const SkIRect& bounds, const SkSurfaceProps& surfaceProps)
650 : INHERITED(make_nopixels(bounds.width(), bounds.height()), surfaceProps)
reed78e27682014-11-19 08:04:34 -0800651 {
652 this->setOrigin(bounds.x(), bounds.y());
653 }
reedd9544982014-09-09 18:46:22 -0700654
655private:
piotaixrb5fae932014-09-24 13:03:30 -0700656
reedd9544982014-09-09 18:46:22 -0700657 typedef SkBitmapDevice INHERITED;
658};
659
reed96a857e2015-01-25 10:33:58 -0800660SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000661 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800662 , fProps(SkSurfacePropsCopyOrDefault(props))
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000663{
664 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700665
reed78e27682014-11-19 08:04:34 -0800666 this->init(SkNEW_ARGS(SkNoPixelsBitmapDevice,
robertphillipsfcf78292015-06-19 11:49:52 -0700667 (SkIRect::MakeWH(width, height), fProps)), kDefault_InitFlags)->unref();
reedd9544982014-09-09 18:46:22 -0700668}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000669
reed78e27682014-11-19 08:04:34 -0800670SkCanvas::SkCanvas(const SkIRect& bounds, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700671 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700672 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reedd9544982014-09-09 18:46:22 -0700673{
674 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700675
robertphillipsfcf78292015-06-19 11:49:52 -0700676 this->init(SkNEW_ARGS(SkNoPixelsBitmapDevice, (bounds, fProps)), flags)->unref();
reedd9544982014-09-09 18:46:22 -0700677}
678
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000679SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000680 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700681 , fProps(device->surfaceProps())
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000682{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000683 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700684
reedd9544982014-09-09 18:46:22 -0700685 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000686}
687
robertphillipsfcf78292015-06-19 11:49:52 -0700688SkCanvas::SkCanvas(SkBaseDevice* device, InitFlags flags)
689 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700690 , fProps(device->surfaceProps())
robertphillipsfcf78292015-06-19 11:49:52 -0700691{
692 inc_canvas();
693
694 this->init(device, flags);
695}
696
reed4a8126e2014-09-22 07:29:03 -0700697SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700698 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700699 , fProps(props)
reed3716fd02014-09-21 09:39:55 -0700700{
701 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700702
robertphillipsfcf78292015-06-19 11:49:52 -0700703 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap, fProps)));
reed4a8126e2014-09-22 07:29:03 -0700704 this->init(device, kDefault_InitFlags);
705}
reed29c857d2014-09-21 10:25:07 -0700706
reed4a8126e2014-09-22 07:29:03 -0700707SkCanvas::SkCanvas(const SkBitmap& bitmap)
708 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
709 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
710{
711 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700712
robertphillipsfcf78292015-06-19 11:49:52 -0700713 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap, fProps)));
reed4a8126e2014-09-22 07:29:03 -0700714 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000715}
716
717SkCanvas::~SkCanvas() {
718 // free up the contents of our deque
719 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000720
reed@android.com8a1c16f2008-12-17 15:59:43 +0000721 this->internalRestore(); // restore the last, since we're going away
722
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000723 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000724
reed@android.com8a1c16f2008-12-17 15:59:43 +0000725 dec_canvas();
726}
727
reed@android.com8a1c16f2008-12-17 15:59:43 +0000728SkDrawFilter* SkCanvas::getDrawFilter() const {
729 return fMCRec->fFilter;
730}
731
732SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
reed51985e32015-04-11 08:04:56 -0700733 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000734 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
735 return filter;
736}
737
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000738SkMetaData& SkCanvas::getMetaData() {
739 // metadata users are rare, so we lazily allocate it. If that changes we
740 // can decide to just make it a field in the device (rather than a ptr)
741 if (NULL == fMetaData) {
742 fMetaData = new SkMetaData;
743 }
744 return *fMetaData;
745}
746
reed@android.com8a1c16f2008-12-17 15:59:43 +0000747///////////////////////////////////////////////////////////////////////////////
748
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000749void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000750 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000751 if (device) {
752 device->flush();
753 }
754}
755
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000756SkISize SkCanvas::getTopLayerSize() const {
757 SkBaseDevice* d = this->getTopDevice();
758 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
759}
760
761SkIPoint SkCanvas::getTopLayerOrigin() const {
762 SkBaseDevice* d = this->getTopDevice();
763 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
764}
765
766SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000767 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000768 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
769}
770
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000771SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000772 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000773 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000774 SkASSERT(rec && rec->fLayer);
775 return rec->fLayer->fDevice;
776}
777
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000778SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000779 if (updateMatrixClip) {
780 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
781 }
reed@google.com9266fed2011-03-30 00:18:03 +0000782 return fMCRec->fTopLayer->fDevice;
783}
784
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000785bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
786 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
787 return false;
788 }
789
790 bool weAllocated = false;
791 if (NULL == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700792 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000793 return false;
794 }
795 weAllocated = true;
796 }
797
reedcf01e312015-05-23 19:14:51 -0700798 SkAutoPixmapUnlock unlocker;
799 if (bitmap->requestLock(&unlocker)) {
800 const SkPixmap& pm = unlocker.pixmap();
801 if (this->readPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), x, y)) {
802 return true;
803 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000804 }
805
806 if (weAllocated) {
807 bitmap->setPixelRef(NULL);
808 }
809 return false;
810}
reed@google.com51df9e32010-12-23 19:29:18 +0000811
bsalomon@google.comc6980972011-11-02 19:57:21 +0000812bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000813 SkIRect r = srcRect;
814 const SkISize size = this->getBaseLayerSize();
815 if (!r.intersect(0, 0, size.width(), size.height())) {
816 bitmap->reset();
817 return false;
818 }
819
reed84825042014-09-02 12:50:45 -0700820 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000821 // bitmap will already be reset.
822 return false;
823 }
824 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
825 bitmap->reset();
826 return false;
827 }
828 return true;
829}
830
reed96472de2014-12-10 09:53:42 -0800831bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000832 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000833 if (!device) {
834 return false;
835 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000836 const SkISize size = this->getBaseLayerSize();
mtkleinf0f14112014-12-12 08:46:25 -0800837
reed96472de2014-12-10 09:53:42 -0800838 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
839 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000840 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000841 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000842
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000843 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800844 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000845}
846
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000847bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
848 if (bitmap.getTexture()) {
849 return false;
850 }
reedcf01e312015-05-23 19:14:51 -0700851
852 SkAutoPixmapUnlock unlocker;
853 if (bitmap.requestLock(&unlocker)) {
854 const SkPixmap& pm = unlocker.pixmap();
855 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000856 }
857 return false;
858}
859
860bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
861 int x, int y) {
862 switch (origInfo.colorType()) {
863 case kUnknown_SkColorType:
864 case kIndex_8_SkColorType:
865 return false;
866 default:
867 break;
868 }
869 if (NULL == pixels || rowBytes < origInfo.minRowBytes()) {
870 return false;
871 }
872
873 const SkISize size = this->getBaseLayerSize();
874 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
875 if (!target.intersect(0, 0, size.width(), size.height())) {
876 return false;
877 }
878
879 SkBaseDevice* device = this->getDevice();
880 if (!device) {
881 return false;
882 }
883
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000884 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700885 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000886
887 // if x or y are negative, then we have to adjust pixels
888 if (x > 0) {
889 x = 0;
890 }
891 if (y > 0) {
892 y = 0;
893 }
894 // here x,y are either 0 or negative
895 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
896
reed4af35f32014-06-27 17:47:49 -0700897 // Tell our owning surface to bump its generation ID
reedc83a2972015-07-16 07:40:45 -0700898 const bool completeOverwrite = info.dimensions() == size;
899 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700900
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000901 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000902 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000903}
reed@google.com51df9e32010-12-23 19:29:18 +0000904
junov@google.com4370aed2012-01-18 16:21:08 +0000905SkCanvas* SkCanvas::canvasForDrawIter() {
906 return this;
907}
908
reed@android.com8a1c16f2008-12-17 15:59:43 +0000909//////////////////////////////////////////////////////////////////////////////
910
reed@android.com8a1c16f2008-12-17 15:59:43 +0000911void SkCanvas::updateDeviceCMCache() {
912 if (fDeviceCMDirty) {
913 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700914 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000915 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000916
reed@android.com8a1c16f2008-12-17 15:59:43 +0000917 if (NULL == layer->fNext) { // only one layer
reed687fa1c2015-04-07 08:00:56 -0700918 layer->updateMC(totalMatrix, totalClip, *fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000919 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000920 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000921 do {
reed687fa1c2015-04-07 08:00:56 -0700922 layer->updateMC(totalMatrix, clip, *fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000923 } while ((layer = layer->fNext) != NULL);
924 }
925 fDeviceCMDirty = false;
926 }
927}
928
reed@android.com8a1c16f2008-12-17 15:59:43 +0000929///////////////////////////////////////////////////////////////////////////////
930
reed2ff1fce2014-12-11 07:07:37 -0800931void SkCanvas::checkForDeferredSave() {
932 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800933 this->doSave();
934 }
935}
936
reedf0090cb2014-11-26 08:55:51 -0800937int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800938#ifdef SK_DEBUG
939 int count = 0;
940 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
941 for (;;) {
942 const MCRec* rec = (const MCRec*)iter.next();
943 if (!rec) {
944 break;
945 }
946 count += 1 + rec->fDeferredSaveCount;
947 }
948 SkASSERT(count == fSaveCount);
949#endif
950 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -0800951}
952
953int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -0800954 fSaveCount += 1;
955 fMCRec->fDeferredSaveCount += 1;
956 return this->getSaveCount() - 1; // return our prev value
957}
958
959void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -0800960 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -0700961
962 SkASSERT(fMCRec->fDeferredSaveCount > 0);
963 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800964 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -0800965}
966
967void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -0800968 if (fMCRec->fDeferredSaveCount > 0) {
969 SkASSERT(fSaveCount > 1);
970 fSaveCount -= 1;
971 fMCRec->fDeferredSaveCount -= 1;
972 } else {
973 // check for underflow
974 if (fMCStack.count() > 1) {
975 this->willRestore();
976 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -0700977 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800978 this->internalRestore();
979 this->didRestore();
980 }
reedf0090cb2014-11-26 08:55:51 -0800981 }
982}
983
984void SkCanvas::restoreToCount(int count) {
985 // sanity check
986 if (count < 1) {
987 count = 1;
988 }
mtkleinf0f14112014-12-12 08:46:25 -0800989
reedf0090cb2014-11-26 08:55:51 -0800990 int n = this->getSaveCount() - count;
991 for (int i = 0; i < n; ++i) {
992 this->restore();
993 }
994}
995
reed2ff1fce2014-12-11 07:07:37 -0800996void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000997 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700998 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000999 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +00001000
reed687fa1c2015-04-07 08:00:56 -07001001 fClipStack->save();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001002}
1003
reed@android.com8a1c16f2008-12-17 15:59:43 +00001004static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +00001005#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +00001006 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +00001007#else
1008 return true;
1009#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001010}
1011
junov@chromium.orga907ac32012-02-24 21:54:07 +00001012bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
reed9b3aa542015-03-11 08:47:12 -07001013 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001014 SkIRect clipBounds;
1015 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001016 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +00001017 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001018
reed96e657d2015-03-10 17:30:07 -07001019 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
1020
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001021 if (imageFilter) {
reed96e657d2015-03-10 17:30:07 -07001022 imageFilter->filterBounds(clipBounds, ctm, &clipBounds);
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001023 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001024 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -07001025 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001026 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +00001027
reed96e657d2015-03-10 17:30:07 -07001028 ctm.mapRect(&r, *bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001029 r.roundOut(&ir);
1030 // early exit if the layer's bounds are clipped out
1031 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001032 if (bounds_affects_clip(flags)) {
reed9b3aa542015-03-11 08:47:12 -07001033 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001034 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001035 }
junov@chromium.orga907ac32012-02-24 21:54:07 +00001036 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001037 }
1038 } else { // no user bounds, so just use the clip
1039 ir = clipBounds;
1040 }
reed180aec42015-03-11 10:39:04 -07001041 SkASSERT(!ir.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001042
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +00001043 if (bounds_affects_clip(flags)) {
reed180aec42015-03-11 10:39:04 -07001044 // Simplify the current clips since they will be applied properly during restore()
reed9b3aa542015-03-11 08:47:12 -07001045 fCachedLocalClipBoundsDirty = true;
reed687fa1c2015-04-07 08:00:56 -07001046 fClipStack->clipDevRect(ir, SkRegion::kReplace_Op);
reed180aec42015-03-11 10:39:04 -07001047 fMCRec->fRasterClip.setRect(ir);
junov@chromium.orga907ac32012-02-24 21:54:07 +00001048 }
1049
1050 if (intersection) {
1051 *intersection = ir;
1052 }
1053 return true;
1054}
1055
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001056int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
reedd990e2f2014-12-22 11:58:30 -08001057 if (gIgnoreSaveLayerBounds) {
1058 bounds = NULL;
1059 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001060 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag);
reeda6441162015-03-26 13:40:09 -07001061 fSaveCount += 1;
reed76033be2015-03-14 10:54:31 -07001062 this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, strategy);
reed2ff1fce2014-12-11 07:07:37 -08001063 return this->getSaveCount() - 1;
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001064}
1065
reed2ff1fce2014-12-11 07:07:37 -08001066int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags) {
reedd990e2f2014-12-22 11:58:30 -08001067 if (gIgnoreSaveLayerBounds) {
1068 bounds = NULL;
1069 }
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001070 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
reeda6441162015-03-26 13:40:09 -07001071 fSaveCount += 1;
reed76033be2015-03-14 10:54:31 -07001072 this->internalSaveLayer(bounds, paint, flags, strategy);
reed2ff1fce2014-12-11 07:07:37 -08001073 return this->getSaveCount() - 1;
reed@google.com8926b162012-03-23 15:36:36 +00001074}
1075
reed2ff1fce2014-12-11 07:07:37 -08001076void SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
reed76033be2015-03-14 10:54:31 -07001077 SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +00001078#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
commit-bot@chromium.org2a5cd602014-05-30 20:41:20 +00001079 flags |= kClipToLayer_SaveFlag;
reed@google.comb93ba452014-03-10 19:47:58 +00001080#endif
1081
junov@chromium.orga907ac32012-02-24 21:54:07 +00001082 // do this before we create the layer. We don't call the public save() since
1083 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001084 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001085
1086 fDeviceCMDirty = true;
1087
1088 SkIRect ir;
reeddaa57bf2015-05-15 10:39:17 -07001089 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed2ff1fce2014-12-11 07:07:37 -08001090 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001091 }
1092
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001093 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1094 // the clipRectBounds() call above?
1095 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001096 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001097 }
1098
reed76033be2015-03-14 10:54:31 -07001099 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
reed8dc0ccb2015-03-20 06:32:52 -07001100 SkPixelGeometry geo = fProps.pixelGeometry();
1101 if (paint) {
reed76033be2015-03-14 10:54:31 -07001102 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001103 if (paint->getImageFilter() || paint->getColorFilter()) {
reed76033be2015-03-14 10:54:31 -07001104 isOpaque = false;
reed8dc0ccb2015-03-20 06:32:52 -07001105 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001106 }
1107 }
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001108 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
1109 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001110
reedb2db8982014-11-13 12:41:02 -08001111 SkBaseDevice* device = this->getTopDevice();
1112 if (NULL == device) {
1113 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001114 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001115 }
reedb2db8982014-11-13 12:41:02 -08001116
reed61f501f2015-04-29 08:34:00 -07001117 bool forceSpriteOnRestore = false;
1118 {
reeddaa57bf2015-05-15 10:39:17 -07001119 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
reed61f501f2015-04-29 08:34:00 -07001120 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo);
1121 SkBaseDevice* newDev = device->onCreateDevice(createInfo, paint);
1122 if (NULL == newDev) {
1123 // If onCreateDevice didn't succeed, try raster (e.g. PDF couldn't handle the paint)
robertphillips9a53fd72015-06-22 09:46:59 -07001124 const SkSurfaceProps surfaceProps(fProps.flags(), createInfo.fPixelGeometry);
1125 newDev = SkBitmapDevice::Create(createInfo.fInfo, surfaceProps);
reed61f501f2015-04-29 08:34:00 -07001126 if (NULL == newDev) {
1127 SkErrorInternals::SetError(kInternalError_SkError,
1128 "Unable to create device for layer.");
1129 return;
1130 }
1131 forceSpriteOnRestore = true;
1132 }
1133 device = newDev;
bungeman@google.come25c6842011-08-17 14:53:54 +00001134 }
bsalomon@google.come97f0852011-06-17 13:10:25 +00001135
reed@google.com6f8f2922011-03-04 22:27:10 +00001136 device->setOrigin(ir.fLeft, ir.fTop);
reed61f501f2015-04-29 08:34:00 -07001137 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, paint, this, fConservativeRasterClip,
reed86a17e72015-05-14 12:25:22 -07001138 forceSpriteOnRestore));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001139 device->unref();
1140
1141 layer->fNext = fMCRec->fTopLayer;
1142 fMCRec->fLayer = layer;
1143 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reed@android.com8a1c16f2008-12-17 15:59:43 +00001144}
1145
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001146int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
1147 return this->saveLayerAlpha(bounds, alpha, kARGB_ClipLayer_SaveFlag);
1148}
1149
reed@android.com8a1c16f2008-12-17 15:59:43 +00001150int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
1151 SaveFlags flags) {
1152 if (0xFF == alpha) {
1153 return this->saveLayer(bounds, NULL, flags);
1154 } else {
1155 SkPaint tmpPaint;
1156 tmpPaint.setAlpha(alpha);
1157 return this->saveLayer(bounds, &tmpPaint, flags);
1158 }
1159}
1160
reed@android.com8a1c16f2008-12-17 15:59:43 +00001161void SkCanvas::internalRestore() {
1162 SkASSERT(fMCStack.count() != 0);
1163
1164 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001165 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001166
reed687fa1c2015-04-07 08:00:56 -07001167 fClipStack->restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001168
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001169 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001170 DeviceCM* layer = fMCRec->fLayer; // may be null
1171 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
1172 fMCRec->fLayer = NULL;
1173
1174 // now do the normal restore()
1175 fMCRec->~MCRec(); // balanced in save()
1176 fMCStack.pop_back();
1177 fMCRec = (MCRec*)fMCStack.back();
1178
1179 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1180 since if we're being recorded, we don't want to record this (the
1181 recorder will have already recorded the restore).
1182 */
bsalomon49f085d2014-09-05 13:34:00 -07001183 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001184 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001185 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001186 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
reed61f501f2015-04-29 08:34:00 -07001187 layer->fPaint, layer->fDeviceIsBitmapDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001188 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001189 fDeviceCMDirty = true;
reedb679ca82015-04-07 04:40:48 -07001190 SkDELETE(layer);
1191 } else {
1192 // we're at the root
reeda499f902015-05-01 09:34:31 -07001193 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001194 layer->~DeviceCM();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001195 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001196 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001197}
1198
reed4a8126e2014-09-22 07:29:03 -07001199SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
1200 if (NULL == props) {
1201 props = &fProps;
1202 }
1203 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001204}
1205
reed4a8126e2014-09-22 07:29:03 -07001206SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001207 SkBaseDevice* dev = this->getDevice();
reed4a8126e2014-09-22 07:29:03 -07001208 return dev ? dev->newSurface(info, props) : NULL;
reed@google.com76f10a32014-02-05 15:32:21 +00001209}
1210
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001211SkImageInfo SkCanvas::imageInfo() const {
1212 SkBaseDevice* dev = this->getDevice();
1213 if (dev) {
1214 return dev->imageInfo();
1215 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001216 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001217 }
1218}
1219
1220const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
reed884e97c2015-05-26 11:31:54 -07001221 SkPixmap pmap;
1222 if (!this->onPeekPixels(&pmap)) {
1223 return NULL;
1224 }
1225 if (info) {
1226 *info = pmap.info();
1227 }
1228 if (rowBytes) {
1229 *rowBytes = pmap.rowBytes();
1230 }
1231 return pmap.addr();
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001232}
1233
reed884e97c2015-05-26 11:31:54 -07001234bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001235 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001236 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001237}
1238
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001239void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001240 SkPixmap pmap;
1241 if (!this->onAccessTopLayerPixels(&pmap)) {
1242 return NULL;
1243 }
1244 if (info) {
1245 *info = pmap.info();
1246 }
1247 if (rowBytes) {
1248 *rowBytes = pmap.rowBytes();
1249 }
1250 if (origin) {
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001251 *origin = this->getTopDevice(false)->getOrigin();
1252 }
reed884e97c2015-05-26 11:31:54 -07001253 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001254}
1255
reed884e97c2015-05-26 11:31:54 -07001256bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001257 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001258 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001259}
1260
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001261SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1262 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1263 if (NULL == fAddr) {
1264 fInfo = canvas->imageInfo();
reed84825042014-09-02 12:50:45 -07001265 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.tryAllocPixels(fInfo)) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001266 return; // failure, fAddr is NULL
1267 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001268 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1269 return; // failure, fAddr is NULL
1270 }
1271 fAddr = fBitmap.getPixels();
1272 fRowBytes = fBitmap.rowBytes();
1273 }
1274 SkASSERT(fAddr); // success
1275}
1276
1277bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1278 if (fAddr) {
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +00001279 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001280 } else {
1281 bitmap->reset();
1282 return false;
1283 }
1284}
1285
reed@android.com8a1c16f2008-12-17 15:59:43 +00001286/////////////////////////////////////////////////////////////////////////////
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001287void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001288 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001289 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001290 return;
1291 }
1292
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001293 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001294 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001295 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001296 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001297
1298 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001299
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001300 SkRect storage;
1301 const SkRect* bounds = NULL;
1302 if (paint && paint->canComputeFastBounds()) {
1303 bitmap.getBounds(&storage);
1304 matrix.mapRect(&storage);
1305 bounds = &paint->computeFastBounds(storage, &storage);
1306 }
1307
1308 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001309
1310 while (iter.next()) {
1311 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1312 }
1313
1314 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001315}
1316
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001317void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed61f501f2015-04-29 08:34:00 -07001318 const SkPaint* paint, bool deviceIsBitmapDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001319 SkPaint tmp;
1320 if (NULL == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001321 paint = &tmp;
1322 }
reed@google.com4b226022011-01-11 18:32:13 +00001323
reed@google.com8926b162012-03-23 15:36:36 +00001324 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001325 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001326 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001327 paint = &looper.paint();
1328 SkImageFilter* filter = paint->getImageFilter();
1329 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001330 if (filter && !dstDev->canHandleImageFilter(filter)) {
robertphillipsefbffed2015-06-22 12:06:08 -07001331 SkImageFilter::Proxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001332 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001333 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001334 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001335 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001336 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001337 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblancobe129b22014-08-08 07:14:35 -07001338 SkAutoTUnref<SkImageFilter::Cache> cache(dstDev->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001339 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001340 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001341 SkPaint tmpUnfiltered(*paint);
1342 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001343 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1344 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001345 }
reed61f501f2015-04-29 08:34:00 -07001346 } else if (deviceIsBitmapDevice) {
reed9572a102015-05-26 19:22:17 -07001347 const SkBitmap& src = static_cast<SkBitmapDevice*>(srcDev)->fBitmap;
reed61f501f2015-04-29 08:34:00 -07001348 dstDev->drawSprite(iter, src, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001349 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001350 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001351 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001352 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001353 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001354}
1355
reed41af9662015-01-05 07:49:08 -08001356void SkCanvas::onDrawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint* paint) {
reed0acf1b42014-12-22 16:12:38 -08001357 if (gTreatSpriteAsBitmap) {
1358 this->save();
1359 this->resetMatrix();
1360 this->drawBitmap(bitmap, SkIntToScalar(x), SkIntToScalar(y), paint);
1361 this->restore();
1362 return;
1363 }
1364
danakj9881d632014-11-26 12:41:06 -08001365 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawSprite()");
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001366 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001367 return;
1368 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001369 SkDEBUGCODE(bitmap.validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001370
reed@google.com8926b162012-03-23 15:36:36 +00001371 SkPaint tmp;
1372 if (NULL == paint) {
1373 paint = &tmp;
1374 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001375
reed@google.com8926b162012-03-23 15:36:36 +00001376 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001377
reed@google.com8926b162012-03-23 15:36:36 +00001378 while (iter.next()) {
1379 paint = &looper.paint();
1380 SkImageFilter* filter = paint->getImageFilter();
1381 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1382 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
robertphillipsefbffed2015-06-22 12:06:08 -07001383 SkImageFilter::Proxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001384 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001385 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001386 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001387 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
halcanaryf622a6c2014-10-24 12:54:53 -07001388 const SkIRect clipBounds = bitmap.bounds();
senorblancobe129b22014-08-08 07:14:35 -07001389 SkAutoTUnref<SkImageFilter::Cache> cache(iter.fDevice->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001390 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001391 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001392 SkPaint tmpUnfiltered(*paint);
1393 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001394 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001395 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001396 }
1397 } else {
1398 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1399 }
1400 }
1401 LOOPER_END
1402}
1403
reed@android.com8a1c16f2008-12-17 15:59:43 +00001404/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001405void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001406 SkMatrix m;
1407 m.setTranslate(dx, dy);
1408 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001409}
1410
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001411void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001412 SkMatrix m;
1413 m.setScale(sx, sy);
1414 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001415}
1416
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001417void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001418 SkMatrix m;
1419 m.setRotate(degrees);
1420 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001421}
1422
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001423void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001424 SkMatrix m;
1425 m.setSkew(sx, sy);
1426 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001427}
1428
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001429void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001430 if (matrix.isIdentity()) {
1431 return;
1432 }
1433
reed2ff1fce2014-12-11 07:07:37 -08001434 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001435 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001436 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001437 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001438
1439 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001440}
1441
reed86a17e72015-05-14 12:25:22 -07001442void SkCanvas::setMatrix(const SkMatrix& matrix) {
1443 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001444 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001445 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001446 fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001447 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001448}
1449
reed@android.com8a1c16f2008-12-17 15:59:43 +00001450void SkCanvas::resetMatrix() {
1451 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001452
reed@android.com8a1c16f2008-12-17 15:59:43 +00001453 matrix.reset();
1454 this->setMatrix(matrix);
1455}
1456
1457//////////////////////////////////////////////////////////////////////////////
1458
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001459void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001460 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001461 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1462 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001463}
1464
1465void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001466#ifdef SK_ENABLE_CLIP_QUICKREJECT
1467 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001468 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001469 return false;
1470 }
1471
reed@google.com3b3e8952012-08-16 20:53:31 +00001472 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001473 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001474 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001475
reed687fa1c2015-04-07 08:00:56 -07001476 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001477 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001478 }
1479 }
1480#endif
1481
reed@google.com5c3d1472011-02-22 19:12:23 +00001482 AutoValidateClip avc(this);
1483
reed@android.com8a1c16f2008-12-17 15:59:43 +00001484 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001485 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001486 if (!fAllowSoftClip) {
1487 edgeStyle = kHard_ClipEdgeStyle;
1488 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001489
reed1f836ee2014-07-07 07:49:34 -07001490 if (fMCRec->fMatrix.rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001491 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001492 // the matrix. This means we don't have to a) make a path, and b) tell
1493 // the region code to scan-convert the path, only to discover that it
1494 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001495 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001496
reed1f836ee2014-07-07 07:49:34 -07001497 fMCRec->fMatrix.mapRect(&r, rect);
reed687fa1c2015-04-07 08:00:56 -07001498 fClipStack->clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reedd9544982014-09-09 18:46:22 -07001499 fMCRec->fRasterClip.op(r, this->getBaseLayerSize(), op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001500 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001501 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001502 // and clip against that, since it can handle any matrix. However, to
1503 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1504 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001505 SkPath path;
1506
1507 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001508 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001509 }
1510}
1511
reed73e714e2014-09-04 09:02:23 -07001512static void rasterclip_path(SkRasterClip* rc, const SkCanvas* canvas, const SkPath& devPath,
1513 SkRegion::Op op, bool doAA) {
reedd64c9482014-09-05 17:37:38 -07001514 rc->op(devPath, canvas->getBaseLayerSize(), op, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001515}
1516
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001517void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001518 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001519 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001520 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001521 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1522 } else {
1523 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001524 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001525}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001526
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001527void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001528 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001529 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001530 AutoValidateClip avc(this);
1531
1532 fDeviceCMDirty = true;
1533 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001534 if (!fAllowSoftClip) {
1535 edgeStyle = kHard_ClipEdgeStyle;
1536 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001537
reed687fa1c2015-04-07 08:00:56 -07001538 fClipStack->clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001539
1540 SkPath devPath;
1541 devPath.addRRect(transformedRRect);
1542
reed73e714e2014-09-04 09:02:23 -07001543 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001544 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001545 }
1546
1547 SkPath path;
1548 path.addRRect(rrect);
1549 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001550 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001551}
1552
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001553void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001554 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001555 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1556 SkRect r;
1557 if (!path.isInverseFillType() && path.isRect(&r)) {
1558 this->onClipRect(r, op, edgeStyle);
1559 } else {
1560 this->onClipPath(path, op, edgeStyle);
1561 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001562}
1563
1564void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001565#ifdef SK_ENABLE_CLIP_QUICKREJECT
1566 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001567 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001568 return false;
1569 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001570
reed@google.com3b3e8952012-08-16 20:53:31 +00001571 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001572 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001573 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001574
reed687fa1c2015-04-07 08:00:56 -07001575 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001576 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001577 }
1578 }
1579#endif
1580
reed@google.com5c3d1472011-02-22 19:12:23 +00001581 AutoValidateClip avc(this);
1582
reed@android.com8a1c16f2008-12-17 15:59:43 +00001583 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001584 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001585 if (!fAllowSoftClip) {
1586 edgeStyle = kHard_ClipEdgeStyle;
1587 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001588
1589 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001590 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001591
reed@google.comfe701122011-11-08 19:41:23 +00001592 // Check if the transfomation, or the original path itself
1593 // made us empty. Note this can also happen if we contained NaN
1594 // values. computing the bounds detects this, and will set our
1595 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1596 if (devPath.getBounds().isEmpty()) {
1597 // resetting the path will remove any NaN or other wanky values
1598 // that might upset our scan converter.
1599 devPath.reset();
1600 }
1601
reed@google.com5c3d1472011-02-22 19:12:23 +00001602 // if we called path.swap() we could avoid a deep copy of this path
reed687fa1c2015-04-07 08:00:56 -07001603 fClipStack->clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001604
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001605 if (fAllowSimplifyClip) {
fmalita1a481fe2015-02-04 07:39:34 -08001606 bool clipIsAA = getClipStack()->asPath(&devPath);
1607 if (clipIsAA) {
1608 edgeStyle = kSoft_ClipEdgeStyle;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001609 }
fmalita1a481fe2015-02-04 07:39:34 -08001610
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001611 op = SkRegion::kReplace_Op;
1612 }
1613
reed73e714e2014-09-04 09:02:23 -07001614 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001615}
1616
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001617void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed2ff1fce2014-12-11 07:07:37 -08001618 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001619 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001620}
1621
1622void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001623 AutoValidateClip avc(this);
1624
reed@android.com8a1c16f2008-12-17 15:59:43 +00001625 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001626 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001627
reed@google.com5c3d1472011-02-22 19:12:23 +00001628 // todo: signal fClipStack that we have a region, and therefore (I guess)
1629 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001630 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001631
reed1f836ee2014-07-07 07:49:34 -07001632 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001633}
1634
reed@google.com819c9212011-02-23 18:56:55 +00001635#ifdef SK_DEBUG
1636void SkCanvas::validateClip() const {
1637 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001638 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001639 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001640 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001641 return;
1642 }
1643
reed@google.com819c9212011-02-23 18:56:55 +00001644 SkIRect ir;
1645 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001646 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001647
reed687fa1c2015-04-07 08:00:56 -07001648 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001649 const SkClipStack::Element* element;
1650 while ((element = iter.next()) != NULL) {
1651 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001652 case SkClipStack::Element::kRect_Type:
1653 element->getRect().round(&ir);
1654 tmpClip.op(ir, element->getOp());
1655 break;
1656 case SkClipStack::Element::kEmpty_Type:
1657 tmpClip.setEmpty();
1658 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001659 default: {
1660 SkPath path;
1661 element->asPath(&path);
reed73e714e2014-09-04 09:02:23 -07001662 rasterclip_path(&tmpClip, this, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001663 break;
1664 }
reed@google.com819c9212011-02-23 18:56:55 +00001665 }
1666 }
reed@google.com819c9212011-02-23 18:56:55 +00001667}
1668#endif
1669
reed@google.com90c07ea2012-04-13 13:50:27 +00001670void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001671 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001672 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001673
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001674 while ((element = iter.next()) != NULL) {
fmalitac3b589a2014-06-05 12:40:07 -07001675 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001676 }
1677}
1678
reed@google.com5c3d1472011-02-22 19:12:23 +00001679///////////////////////////////////////////////////////////////////////////////
1680
reed@google.com754de5f2014-02-24 19:38:20 +00001681bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001682 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001683}
1684
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001685bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001686 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001687}
1688
reed@google.com3b3e8952012-08-16 20:53:31 +00001689bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001690 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001691 return true;
1692
reed1f836ee2014-07-07 07:49:34 -07001693 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001694 return true;
1695 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001696
reed1f836ee2014-07-07 07:49:34 -07001697 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001698 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001699 fMCRec->fMatrix.mapRect(&dst, rect);
reedb07a94f2014-11-19 05:03:18 -08001700 return !SkIRect::Intersects(dst.roundOut(), fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001701 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001702 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001703
reed@android.coma380ae42009-07-21 01:17:02 +00001704 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001705 // TODO: should we use | instead, or compare all 4 at once?
1706 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001707 return true;
1708 }
reed@google.comc0784db2013-12-13 21:16:12 +00001709 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001710 return true;
1711 }
1712 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001713 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001714}
1715
reed@google.com3b3e8952012-08-16 20:53:31 +00001716bool SkCanvas::quickReject(const SkPath& path) const {
1717 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001718}
1719
reed@google.com3b3e8952012-08-16 20:53:31 +00001720bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001721 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001722 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001723 return false;
1724 }
1725
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001726 SkMatrix inverse;
1727 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001728 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001729 if (bounds) {
1730 bounds->setEmpty();
1731 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001732 return false;
1733 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001734
bsalomon49f085d2014-09-05 13:34:00 -07001735 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001736 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001737 // adjust it outwards in case we are antialiasing
1738 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001739
reed@google.com8f4d2302013-12-17 16:44:46 +00001740 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1741 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001742 inverse.mapRect(bounds, r);
1743 }
1744 return true;
1745}
1746
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001747bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001748 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001749 if (clip.isEmpty()) {
1750 if (bounds) {
1751 bounds->setEmpty();
1752 }
1753 return false;
1754 }
1755
bsalomon49f085d2014-09-05 13:34:00 -07001756 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001757 *bounds = clip.getBounds();
1758 }
1759 return true;
1760}
1761
reed@android.com8a1c16f2008-12-17 15:59:43 +00001762const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001763 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001764}
1765
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001766const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001767 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001768}
1769
reed@google.com9c135db2014-03-12 18:28:35 +00001770GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1771 SkBaseDevice* dev = this->getTopDevice();
1772 return dev ? dev->accessRenderTarget() : NULL;
1773}
1774
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001775GrContext* SkCanvas::getGrContext() {
1776#if SK_SUPPORT_GPU
1777 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07001778 if (device) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001779 GrRenderTarget* renderTarget = device->accessRenderTarget();
bsalomon49f085d2014-09-05 13:34:00 -07001780 if (renderTarget) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001781 return renderTarget->getContext();
1782 }
1783 }
1784#endif
1785
1786 return NULL;
1787
1788}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001789
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001790void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1791 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001792 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001793 if (outer.isEmpty()) {
1794 return;
1795 }
1796 if (inner.isEmpty()) {
1797 this->drawRRect(outer, paint);
1798 return;
1799 }
1800
1801 // We don't have this method (yet), but technically this is what we should
1802 // be able to assert...
1803 // SkASSERT(outer.contains(inner));
1804 //
1805 // For now at least check for containment of bounds
1806 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1807
1808 this->onDrawDRRect(outer, inner, paint);
1809}
1810
reed41af9662015-01-05 07:49:08 -08001811// These need to stop being virtual -- clients need to override the onDraw... versions
1812
1813void SkCanvas::drawPaint(const SkPaint& paint) {
1814 this->onDrawPaint(paint);
1815}
1816
1817void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1818 this->onDrawRect(r, paint);
1819}
1820
1821void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1822 this->onDrawOval(r, paint);
1823}
1824
1825void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1826 this->onDrawRRect(rrect, paint);
1827}
1828
1829void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1830 this->onDrawPoints(mode, count, pts, paint);
1831}
1832
1833void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
1834 const SkPoint texs[], const SkColor colors[], SkXfermode* xmode,
1835 const uint16_t indices[], int indexCount, const SkPaint& paint) {
1836 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
1837 indices, indexCount, paint);
1838}
1839
1840void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1841 this->onDrawPath(path, paint);
1842}
1843
reeda85d4d02015-05-06 12:56:48 -07001844void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
1845 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001846}
1847
1848void SkCanvas::drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07001849 const SkPaint* paint, SrcRectConstraint constraint) {
reeda85d4d02015-05-06 12:56:48 -07001850 if (dst.isEmpty()) {
1851 return;
1852 }
reeda5517e22015-07-14 10:54:12 -07001853 this->onDrawImageRect(image, src, dst, paint SRC_RECT_CONSTRAINT_ARG(constraint));
reed41af9662015-01-05 07:49:08 -08001854}
1855
reed84984ef2015-07-17 07:09:43 -07001856void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1857 const SkPaint* paint, SrcRectConstraint constraint) {
1858 SkRect src = SkRect::Make(isrc);
1859 this->drawImageRect(image, &src, dst, paint, constraint);
1860}
1861
reed4c21dc52015-06-25 12:32:03 -07001862void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1863 const SkPaint* paint) {
1864 if (dst.isEmpty()) {
1865 return;
1866 }
1867 if (!SkNinePatchIter::Valid(image->width(), image->height(), center)) {
1868 this->drawImageRect(image, NULL, dst, paint);
1869 }
1870 this->onDrawImageNine(image, center, dst, paint);
1871}
1872
reed41af9662015-01-05 07:49:08 -08001873void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001874 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001875 return;
1876 }
reed41af9662015-01-05 07:49:08 -08001877 this->onDrawBitmap(bitmap, dx, dy, paint);
1878}
1879
reed84984ef2015-07-17 07:09:43 -07001880#ifdef SK_SUPPORT_LEGACY_DRAWBITMAPRECTFLAGS_TYPE
reed41af9662015-01-05 07:49:08 -08001881void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
1882 const SkPaint* paint, DrawBitmapRectFlags flags) {
reed4c21dc52015-06-25 12:32:03 -07001883 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001884 return;
1885 }
reeda5517e22015-07-14 10:54:12 -07001886 this->onDrawBitmapRect(bitmap, src, dst, paint, (SK_VIRTUAL_CONSTRAINT_TYPE)flags);
1887}
reed84984ef2015-07-17 07:09:43 -07001888#endif
reeda5517e22015-07-14 10:54:12 -07001889
1890void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
1891 const SkPaint* paint, SrcRectConstraint constraint) {
1892 if (bitmap.drawsNothing() || dst.isEmpty()) {
1893 return;
1894 }
1895 this->onDrawBitmapRect(bitmap, src, dst, paint, (SK_VIRTUAL_CONSTRAINT_TYPE)constraint);
reed41af9662015-01-05 07:49:08 -08001896}
1897
reed84984ef2015-07-17 07:09:43 -07001898void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
1899 const SkPaint* paint, SrcRectConstraint constraint) {
1900 SkRect src = SkRect::Make(isrc);
1901 this->drawBitmapRect(bitmap, &src, dst, paint, constraint);
1902}
1903
reed41af9662015-01-05 07:49:08 -08001904void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
1905 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001906 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001907 return;
1908 }
reed4c21dc52015-06-25 12:32:03 -07001909 if (!SkNinePatchIter::Valid(bitmap.width(), bitmap.height(), center)) {
reeda5517e22015-07-14 10:54:12 -07001910 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001911 }
reed41af9662015-01-05 07:49:08 -08001912 this->onDrawBitmapNine(bitmap, center, dst, paint);
1913}
1914
1915void SkCanvas::drawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001916 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001917 return;
1918 }
reed41af9662015-01-05 07:49:08 -08001919 this->onDrawSprite(bitmap, left, top, paint);
1920}
1921
reed71c3c762015-06-24 10:29:17 -07001922void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
1923 const SkColor colors[], int count, SkXfermode::Mode mode,
1924 const SkRect* cull, const SkPaint* paint) {
1925 if (count <= 0) {
1926 return;
1927 }
1928 SkASSERT(atlas);
1929 SkASSERT(xform);
1930 SkASSERT(tex);
1931 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
1932}
1933
reed@android.com8a1c16f2008-12-17 15:59:43 +00001934//////////////////////////////////////////////////////////////////////////////
1935// These are the virtual drawing methods
1936//////////////////////////////////////////////////////////////////////////////
1937
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001938void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07001939 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001940 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1941 }
1942}
1943
reed41af9662015-01-05 07:49:08 -08001944void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001945 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001946 this->internalDrawPaint(paint);
1947}
1948
1949void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reedc83a2972015-07-16 07:40:45 -07001950 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, NULL, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001951
1952 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001953 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001954 }
1955
reed@google.com4e2b3d32011-04-07 14:18:59 +00001956 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001957}
1958
reed41af9662015-01-05 07:49:08 -08001959void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
1960 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001961 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001962 if ((long)count <= 0) {
1963 return;
1964 }
1965
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001966 SkRect r, storage;
1967 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001968 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001969 // special-case 2 points (common for drawing a single line)
1970 if (2 == count) {
1971 r.set(pts[0], pts[1]);
1972 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001973 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001974 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001975 bounds = &paint.computeFastStrokeBounds(r, &storage);
1976 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001977 return;
1978 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001979 }
reed@google.coma584aed2012-05-16 14:06:02 +00001980
reed@android.com8a1c16f2008-12-17 15:59:43 +00001981 SkASSERT(pts != NULL);
1982
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001983 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001984
reed@android.com8a1c16f2008-12-17 15:59:43 +00001985 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001986 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001987 }
reed@google.com4b226022011-01-11 18:32:13 +00001988
reed@google.com4e2b3d32011-04-07 14:18:59 +00001989 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001990}
1991
reed41af9662015-01-05 07:49:08 -08001992void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001993 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001994 SkRect storage;
1995 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001996 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08001997 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
1998 // To prevent accidental rejecting at this stage, we have to sort it before we check.
1999 SkRect tmp(r);
2000 tmp.sort();
2001
2002 bounds = &paint.computeFastBounds(tmp, &storage);
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002003 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002004 return;
2005 }
2006 }
reed@google.com4b226022011-01-11 18:32:13 +00002007
reedc83a2972015-07-16 07:40:45 -07002008 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002009
2010 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002011 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002012 }
2013
reed@google.com4e2b3d32011-04-07 14:18:59 +00002014 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002015}
2016
reed41af9662015-01-05 07:49:08 -08002017void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002018 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002019 SkRect storage;
2020 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002021 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002022 bounds = &paint.computeFastBounds(oval, &storage);
2023 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002024 return;
2025 }
2026 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002027
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002028 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002029
2030 while (iter.next()) {
2031 iter.fDevice->drawOval(iter, oval, looper.paint());
2032 }
2033
2034 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002035}
2036
reed41af9662015-01-05 07:49:08 -08002037void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002038 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002039 SkRect storage;
2040 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002041 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002042 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
2043 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002044 return;
2045 }
2046 }
2047
2048 if (rrect.isRect()) {
2049 // call the non-virtual version
2050 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002051 return;
2052 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002053 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002054 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2055 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002056 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002057
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002058 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002059
2060 while (iter.next()) {
2061 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2062 }
2063
2064 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002065}
2066
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002067void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2068 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002069 SkRect storage;
2070 const SkRect* bounds = NULL;
2071 if (paint.canComputeFastBounds()) {
2072 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
2073 if (this->quickReject(*bounds)) {
2074 return;
2075 }
2076 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002077
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002078 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002079
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002080 while (iter.next()) {
2081 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2082 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002083
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002084 LOOPER_END
2085}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002086
reed41af9662015-01-05 07:49:08 -08002087void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002088 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002089 if (!path.isFinite()) {
2090 return;
2091 }
2092
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002093 SkRect storage;
2094 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002095 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002096 const SkRect& pathBounds = path.getBounds();
2097 bounds = &paint.computeFastBounds(pathBounds, &storage);
2098 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002099 return;
2100 }
2101 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002102
2103 const SkRect& r = path.getBounds();
2104 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002105 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002106 this->internalDrawPaint(paint);
2107 }
2108 return;
2109 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002110
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002111 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002112
2113 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002114 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002115 }
2116
reed@google.com4e2b3d32011-04-07 14:18:59 +00002117 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002118}
2119
reeda85d4d02015-05-06 12:56:48 -07002120void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002121 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002122 SkRect bounds = SkRect::MakeXYWH(x, y,
2123 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
2124 if (NULL == paint || paint->canComputeFastBounds()) {
2125 if (paint) {
2126 paint->computeFastBounds(bounds, &bounds);
2127 }
2128 if (this->quickReject(bounds)) {
2129 return;
2130 }
2131 }
2132
2133 SkLazyPaint lazy;
2134 if (NULL == paint) {
2135 paint = lazy.init();
2136 }
2137
2138 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &bounds)
2139
2140 while (iter.next()) {
2141 iter.fDevice->drawImage(iter, image, x, y, looper.paint());
2142 }
2143
2144 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002145}
2146
reed41af9662015-01-05 07:49:08 -08002147void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07002148 const SkPaint* paint SRC_RECT_CONSTRAINT_PARAM(constraint)) {
danakj9881d632014-11-26 12:41:06 -08002149 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
reeda5517e22015-07-14 10:54:12 -07002150 SRC_RECT_CONSTRAINT_LOCAL_DEFAULT(constraint)
reeda85d4d02015-05-06 12:56:48 -07002151 SkRect storage;
2152 const SkRect* bounds = &dst;
2153 if (NULL == paint || paint->canComputeFastBounds()) {
2154 if (paint) {
2155 bounds = &paint->computeFastBounds(dst, &storage);
2156 }
2157 if (this->quickReject(*bounds)) {
2158 return;
2159 }
2160 }
2161 SkLazyPaint lazy;
2162 if (NULL == paint) {
2163 paint = lazy.init();
2164 }
2165
reedc83a2972015-07-16 07:40:45 -07002166 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, bounds,
2167 image->isOpaque())
reeda85d4d02015-05-06 12:56:48 -07002168
2169 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002170 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002171 }
2172
2173 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002174}
2175
reed41af9662015-01-05 07:49:08 -08002176void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002177 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002178 SkDEBUGCODE(bitmap.validate();)
2179
reed@google.com3d608122011-11-21 15:16:16 +00002180 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002181 SkRect bounds = {
2182 x, y,
2183 x + SkIntToScalar(bitmap.width()),
2184 y + SkIntToScalar(bitmap.height())
2185 };
2186 if (paint) {
2187 (void)paint->computeFastBounds(bounds, &bounds);
2188 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002189 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002190 return;
2191 }
2192 }
reed@google.com4b226022011-01-11 18:32:13 +00002193
reed@android.com8a1c16f2008-12-17 15:59:43 +00002194 SkMatrix matrix;
2195 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00002196 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002197}
2198
reed@google.com9987ec32011-09-07 11:57:52 +00002199// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002200void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002201 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002202 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002203 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002204 return;
2205 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002206
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002207 SkRect storage;
2208 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00002209 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002210 if (paint) {
2211 bounds = &paint->computeFastBounds(dst, &storage);
2212 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002213 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002214 return;
2215 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002216 }
reed@google.com3d608122011-11-21 15:16:16 +00002217
reed@google.com33535f32012-09-25 15:37:50 +00002218 SkLazyPaint lazy;
2219 if (NULL == paint) {
2220 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002221 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002222
reedc83a2972015-07-16 07:40:45 -07002223 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, bounds,
2224 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002225
reed@google.com33535f32012-09-25 15:37:50 +00002226 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002227 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(),
2228 (SK_VIRTUAL_CONSTRAINT_TYPE)constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002229 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002230
reed@google.com33535f32012-09-25 15:37:50 +00002231 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002232}
2233
reed41af9662015-01-05 07:49:08 -08002234void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07002235 const SkPaint* paint, SK_VIRTUAL_CONSTRAINT_TYPE constraint) {
danakj9881d632014-11-26 12:41:06 -08002236 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002237 SkDEBUGCODE(bitmap.validate();)
reeda5517e22015-07-14 10:54:12 -07002238 this->internalDrawBitmapRect(bitmap, src, dst, paint, (SrcRectConstraint)constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002239}
2240
reed4c21dc52015-06-25 12:32:03 -07002241void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2242 const SkPaint* paint) {
2243 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
2244
2245 SkRect storage;
2246 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00002247 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00002248 if (paint) {
2249 bounds = &paint->computeFastBounds(dst, &storage);
2250 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002251 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002252 return;
2253 }
2254 }
reed4c21dc52015-06-25 12:32:03 -07002255
2256 SkLazyPaint lazy;
2257 if (NULL == paint) {
2258 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002259 }
reed4c21dc52015-06-25 12:32:03 -07002260
2261 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
2262
2263 while (iter.next()) {
2264 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002265 }
reed4c21dc52015-06-25 12:32:03 -07002266
2267 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002268}
2269
reed41af9662015-01-05 07:49:08 -08002270void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2271 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002272 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002273 SkDEBUGCODE(bitmap.validate();)
2274
reed4c21dc52015-06-25 12:32:03 -07002275 SkRect storage;
2276 const SkRect* bounds = &dst;
2277 if (NULL == paint || paint->canComputeFastBounds()) {
2278 if (paint) {
2279 bounds = &paint->computeFastBounds(dst, &storage);
2280 }
2281 if (this->quickReject(*bounds)) {
2282 return;
2283 }
2284 }
2285
2286 SkLazyPaint lazy;
2287 if (NULL == paint) {
2288 paint = lazy.init();
2289 }
2290
2291 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
2292
2293 while (iter.next()) {
2294 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2295 }
2296
2297 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002298}
2299
reed@google.comf67e4cf2011-03-15 20:56:58 +00002300class SkDeviceFilteredPaint {
2301public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002302 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002303 uint32_t filteredFlags = device->filterTextFlags(paint);
2304 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002305 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002306 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002307 fPaint = newPaint;
2308 } else {
2309 fPaint = &paint;
2310 }
2311 }
2312
reed@google.comf67e4cf2011-03-15 20:56:58 +00002313 const SkPaint& paint() const { return *fPaint; }
2314
2315private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002316 const SkPaint* fPaint;
2317 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002318};
2319
bungeman@google.com52c748b2011-08-22 21:30:43 +00002320void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2321 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002322 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002323 draw.fDevice->drawRect(draw, r, paint);
2324 } else {
2325 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002326 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002327 draw.fDevice->drawRect(draw, r, p);
2328 }
2329}
2330
2331void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2332 const char text[], size_t byteLength,
2333 SkScalar x, SkScalar y) {
2334 SkASSERT(byteLength == 0 || text != NULL);
2335
2336 // nothing to draw
2337 if (text == NULL || byteLength == 0 ||
2338 draw.fClip->isEmpty() ||
2339 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2340 return;
2341 }
2342
2343 SkScalar width = 0;
2344 SkPoint start;
2345
2346 start.set(0, 0); // to avoid warning
2347 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2348 SkPaint::kStrikeThruText_Flag)) {
2349 width = paint.measureText(text, byteLength);
2350
2351 SkScalar offsetX = 0;
2352 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2353 offsetX = SkScalarHalf(width);
2354 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2355 offsetX = width;
2356 }
2357 start.set(x - offsetX, y);
2358 }
2359
2360 if (0 == width) {
2361 return;
2362 }
2363
2364 uint32_t flags = paint.getFlags();
2365
2366 if (flags & (SkPaint::kUnderlineText_Flag |
2367 SkPaint::kStrikeThruText_Flag)) {
2368 SkScalar textSize = paint.getTextSize();
2369 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2370 SkRect r;
2371
2372 r.fLeft = start.fX;
2373 r.fRight = start.fX + width;
2374
2375 if (flags & SkPaint::kUnderlineText_Flag) {
2376 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2377 start.fY);
2378 r.fTop = offset;
2379 r.fBottom = offset + height;
2380 DrawRect(draw, paint, r, textSize);
2381 }
2382 if (flags & SkPaint::kStrikeThruText_Flag) {
2383 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2384 start.fY);
2385 r.fTop = offset;
2386 r.fBottom = offset + height;
2387 DrawRect(draw, paint, r, textSize);
2388 }
2389 }
2390}
2391
reed@google.come0d9ce82014-04-23 04:00:17 +00002392void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2393 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002394 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002395
2396 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002397 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002398 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002399 DrawTextDecorations(iter, dfp.paint(),
2400 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002401 }
2402
reed@google.com4e2b3d32011-04-07 14:18:59 +00002403 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002404}
2405
reed@google.come0d9ce82014-04-23 04:00:17 +00002406void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2407 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002408 SkPoint textOffset = SkPoint::Make(0, 0);
2409
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002410 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002411
reed@android.com8a1c16f2008-12-17 15:59:43 +00002412 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002413 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002414 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002415 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002416 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002417
reed@google.com4e2b3d32011-04-07 14:18:59 +00002418 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002419}
2420
reed@google.come0d9ce82014-04-23 04:00:17 +00002421void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2422 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002423
2424 SkPoint textOffset = SkPoint::Make(0, constY);
2425
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002426 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002427
reed@android.com8a1c16f2008-12-17 15:59:43 +00002428 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002429 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002430 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002431 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002432 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002433
reed@google.com4e2b3d32011-04-07 14:18:59 +00002434 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002435}
2436
reed@google.come0d9ce82014-04-23 04:00:17 +00002437void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2438 const SkMatrix* matrix, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002439 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002440
reed@android.com8a1c16f2008-12-17 15:59:43 +00002441 while (iter.next()) {
2442 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002443 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002444 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002445
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002446 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002447}
2448
fmalita00d5c2c2014-08-21 08:53:26 -07002449void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2450 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002451
fmalita85d5eb92015-03-04 11:20:12 -08002452 SkRect storage;
2453 const SkRect* bounds = NULL;
fmalita19653d12014-10-16 11:53:30 -07002454 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002455 storage = blob->bounds().makeOffset(x, y);
2456 bounds = &paint.computeFastBounds(storage, &storage);
fmalita7ba7aa72014-08-29 09:46:36 -07002457
fmalita85d5eb92015-03-04 11:20:12 -08002458 if (this->quickReject(*bounds)) {
fmalita7ba7aa72014-08-29 09:46:36 -07002459 return;
2460 }
2461 }
2462
fmalita024f9962015-03-03 19:08:17 -08002463 // We cannot filter in the looper as we normally do, because the paint is
2464 // incomplete at this point (text-related attributes are embedded within blob run paints).
2465 SkDrawFilter* drawFilter = fMCRec->fFilter;
2466 fMCRec->fFilter = NULL;
2467
fmalita85d5eb92015-03-04 11:20:12 -08002468 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002469
fmalitaaa1b9122014-08-28 14:32:24 -07002470 while (iter.next()) {
2471 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002472 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002473 }
2474
fmalitaaa1b9122014-08-28 14:32:24 -07002475 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002476
2477 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002478}
2479
reed@google.come0d9ce82014-04-23 04:00:17 +00002480// These will become non-virtual, so they always call the (virtual) onDraw... method
2481void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2482 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002483 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002484 this->onDrawText(text, byteLength, x, y, paint);
2485}
2486void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2487 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002488 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002489 this->onDrawPosText(text, byteLength, pos, paint);
2490}
2491void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2492 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002493 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002494 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2495}
2496void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2497 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002498 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002499 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2500}
fmalita00d5c2c2014-08-21 08:53:26 -07002501void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2502 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002503 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
bsalomon49f085d2014-09-05 13:34:00 -07002504 if (blob) {
fmalita00d5c2c2014-08-21 08:53:26 -07002505 this->onDrawTextBlob(blob, x, y, paint);
2506 }
2507}
reed@google.come0d9ce82014-04-23 04:00:17 +00002508
reed41af9662015-01-05 07:49:08 -08002509void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2510 const SkPoint verts[], const SkPoint texs[],
2511 const SkColor colors[], SkXfermode* xmode,
2512 const uint16_t indices[], int indexCount,
2513 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002514 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002515 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002516
reed@android.com8a1c16f2008-12-17 15:59:43 +00002517 while (iter.next()) {
2518 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002519 colors, xmode, indices, indexCount,
2520 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002521 }
reed@google.com4b226022011-01-11 18:32:13 +00002522
reed@google.com4e2b3d32011-04-07 14:18:59 +00002523 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002524}
2525
dandovb3c9d1c2014-08-12 08:34:29 -07002526void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2527 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002528 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
dandovb3c9d1c2014-08-12 08:34:29 -07002529 if (NULL == cubics) {
2530 return;
2531 }
mtklein6cfa73a2014-08-13 13:33:49 -07002532
dandovecfff212014-08-04 10:02:00 -07002533 // Since a patch is always within the convex hull of the control points, we discard it when its
2534 // bounding rectangle is completely outside the current clip.
2535 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002536 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002537 if (this->quickReject(bounds)) {
2538 return;
2539 }
mtklein6cfa73a2014-08-13 13:33:49 -07002540
dandovb3c9d1c2014-08-12 08:34:29 -07002541 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2542}
2543
2544void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2545 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2546
dandovecfff212014-08-04 10:02:00 -07002547 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
mtklein6cfa73a2014-08-13 13:33:49 -07002548
dandovecfff212014-08-04 10:02:00 -07002549 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002550 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002551 }
mtklein6cfa73a2014-08-13 13:33:49 -07002552
dandovecfff212014-08-04 10:02:00 -07002553 LOOPER_END
2554}
2555
reeda8db7282015-07-07 10:22:31 -07002556void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
2557 if (dr) {
2558 if (x || y) {
2559 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2560 this->onDrawDrawable(dr, &matrix);
2561 } else {
2562 this->onDrawDrawable(dr, NULL);
2563 }
reed6a070dc2014-11-11 19:36:09 -08002564 }
2565}
2566
reeda8db7282015-07-07 10:22:31 -07002567void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2568 if (dr) {
2569 if (matrix && matrix->isIdentity()) {
2570 matrix = NULL;
2571 }
2572 this->onDrawDrawable(dr, matrix);
2573 }
2574}
2575
2576void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2577 SkRect bounds = dr->getBounds();
2578 if (matrix) {
2579 matrix->mapRect(&bounds);
2580 }
2581 if (this->quickReject(bounds)) {
2582 return;
2583 }
2584 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002585}
2586
reed71c3c762015-06-24 10:29:17 -07002587void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2588 const SkColor colors[], int count, SkXfermode::Mode mode,
2589 const SkRect* cull, const SkPaint* paint) {
2590 if (cull && this->quickReject(*cull)) {
2591 return;
2592 }
2593
2594 SkPaint pnt;
2595 if (paint) {
2596 pnt = *paint;
2597 }
2598
2599 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, NULL)
2600 while (iter.next()) {
2601 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, mode, pnt);
2602 }
2603 LOOPER_END
2604}
2605
reed@android.com8a1c16f2008-12-17 15:59:43 +00002606//////////////////////////////////////////////////////////////////////////////
2607// These methods are NOT virtual, and therefore must call back into virtual
2608// methods, rather than actually drawing themselves.
2609//////////////////////////////////////////////////////////////////////////////
2610
2611void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002612 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002613 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002614 SkPaint paint;
2615
2616 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002617 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002618 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002619 }
2620 this->drawPaint(paint);
2621}
2622
reed@android.com845fdac2009-06-23 03:01:32 +00002623void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002624 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002625 SkPaint paint;
2626
2627 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002628 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002629 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002630 }
2631 this->drawPaint(paint);
2632}
2633
2634void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002635 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002636 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002637
reed@android.com8a1c16f2008-12-17 15:59:43 +00002638 pt.set(x, y);
2639 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2640}
2641
2642void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002643 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002644 SkPoint pt;
2645 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002646
reed@android.com8a1c16f2008-12-17 15:59:43 +00002647 pt.set(x, y);
2648 paint.setColor(color);
2649 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2650}
2651
2652void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2653 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002654 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002655 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002656
reed@android.com8a1c16f2008-12-17 15:59:43 +00002657 pts[0].set(x0, y0);
2658 pts[1].set(x1, y1);
2659 this->drawPoints(kLines_PointMode, 2, pts, paint);
2660}
2661
2662void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2663 SkScalar right, SkScalar bottom,
2664 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002665 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002666 SkRect r;
2667
2668 r.set(left, top, right, bottom);
2669 this->drawRect(r, paint);
2670}
2671
2672void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2673 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002674 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002675 if (radius < 0) {
2676 radius = 0;
2677 }
2678
2679 SkRect r;
2680 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002681 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002682}
2683
2684void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2685 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002686 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002687 if (rx > 0 && ry > 0) {
2688 if (paint.canComputeFastBounds()) {
2689 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002690 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002691 return;
2692 }
2693 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002694 SkRRect rrect;
2695 rrect.setRectXY(r, rx, ry);
2696 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002697 } else {
2698 this->drawRect(r, paint);
2699 }
2700}
2701
reed@android.com8a1c16f2008-12-17 15:59:43 +00002702void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2703 SkScalar sweepAngle, bool useCenter,
2704 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002705 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002706 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2707 this->drawOval(oval, paint);
2708 } else {
2709 SkPath path;
2710 if (useCenter) {
2711 path.moveTo(oval.centerX(), oval.centerY());
2712 }
2713 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2714 if (useCenter) {
2715 path.close();
2716 }
2717 this->drawPath(path, paint);
2718 }
2719}
2720
2721void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2722 const SkPath& path, SkScalar hOffset,
2723 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002724 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002725 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002726
reed@android.com8a1c16f2008-12-17 15:59:43 +00002727 matrix.setTranslate(hOffset, vOffset);
2728 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2729}
2730
reed@android.comf76bacf2009-05-13 14:00:33 +00002731///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07002732
2733/**
2734 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2735 * against the playback cost of recursing into the subpicture to get at its actual ops.
2736 *
2737 * For now we pick a conservatively small value, though measurement (and other heuristics like
2738 * the type of ops contained) may justify changing this value.
2739 */
2740#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07002741
reedd5fa1a42014-08-09 11:08:05 -07002742void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reed1c2c4412015-04-30 13:09:24 -07002743 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
bsalomon49f085d2014-09-05 13:34:00 -07002744 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002745 if (matrix && matrix->isIdentity()) {
2746 matrix = NULL;
2747 }
reed1c2c4412015-04-30 13:09:24 -07002748 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2749 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2750 picture->playback(this);
2751 } else {
2752 this->onDrawPicture(picture, matrix, paint);
2753 }
reedd5fa1a42014-08-09 11:08:05 -07002754 }
2755}
robertphillips9b14f262014-06-04 05:40:44 -07002756
reedd5fa1a42014-08-09 11:08:05 -07002757void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2758 const SkPaint* paint) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002759 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002760 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002761 // Canvas has to first give the device the opportunity to render
2762 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002763 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002764 return; // the device has rendered the entire picture
2765 }
2766 }
2767
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002768 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07002769 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002770}
2771
reed@android.com8a1c16f2008-12-17 15:59:43 +00002772///////////////////////////////////////////////////////////////////////////////
2773///////////////////////////////////////////////////////////////////////////////
2774
2775SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002776 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002777
2778 SkASSERT(canvas);
2779
2780 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2781 fDone = !fImpl->next();
2782}
2783
2784SkCanvas::LayerIter::~LayerIter() {
2785 fImpl->~SkDrawIter();
2786}
2787
2788void SkCanvas::LayerIter::next() {
2789 fDone = !fImpl->next();
2790}
2791
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002792SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002793 return fImpl->getDevice();
2794}
2795
2796const SkMatrix& SkCanvas::LayerIter::matrix() const {
2797 return fImpl->getMatrix();
2798}
2799
2800const SkPaint& SkCanvas::LayerIter::paint() const {
2801 const SkPaint* paint = fImpl->getPaint();
2802 if (NULL == paint) {
2803 paint = &fDefaultPaint;
2804 }
2805 return *paint;
2806}
2807
2808const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2809int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2810int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002811
2812///////////////////////////////////////////////////////////////////////////////
2813
fmalitac3b589a2014-06-05 12:40:07 -07002814SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002815
2816///////////////////////////////////////////////////////////////////////////////
2817
2818static bool supported_for_raster_canvas(const SkImageInfo& info) {
2819 switch (info.alphaType()) {
2820 case kPremul_SkAlphaType:
2821 case kOpaque_SkAlphaType:
2822 break;
2823 default:
2824 return false;
2825 }
2826
2827 switch (info.colorType()) {
2828 case kAlpha_8_SkColorType:
2829 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002830 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002831 break;
2832 default:
2833 return false;
2834 }
2835
2836 return true;
2837}
2838
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002839SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2840 if (!supported_for_raster_canvas(info)) {
2841 return NULL;
2842 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002843
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002844 SkBitmap bitmap;
2845 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2846 return NULL;
2847 }
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002848 return SkNEW_ARGS(SkCanvas, (bitmap));
2849}
reedd5fa1a42014-08-09 11:08:05 -07002850
2851///////////////////////////////////////////////////////////////////////////////
2852
2853SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002854 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07002855 : fCanvas(canvas)
2856 , fSaveCount(canvas->getSaveCount())
2857{
bsalomon49f085d2014-09-05 13:34:00 -07002858 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002859 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07002860 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002861 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07002862 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002863 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07002864 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002865 canvas->save();
2866 }
mtklein6cfa73a2014-08-13 13:33:49 -07002867
bsalomon49f085d2014-09-05 13:34:00 -07002868 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002869 canvas->concat(*matrix);
2870 }
2871}
2872
2873SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
2874 fCanvas->restoreToCount(fSaveCount);
2875}