blob: 2308151b40f55b422f131ac027f784e1349d7945 [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);
fmalita8c0144c2015-07-22 05:56:16 -070073 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -070074 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 }
reed562fe472015-07-28 07:35:14 -07001853 this->onDrawImageRect(image, src, dst, paint, 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
reeda5517e22015-07-14 10:54:12 -07001880void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
1881 const SkPaint* paint, SrcRectConstraint constraint) {
1882 if (bitmap.drawsNothing() || dst.isEmpty()) {
1883 return;
1884 }
reed562fe472015-07-28 07:35:14 -07001885 this->onDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08001886}
1887
reed84984ef2015-07-17 07:09:43 -07001888void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
1889 const SkPaint* paint, SrcRectConstraint constraint) {
1890 SkRect src = SkRect::Make(isrc);
1891 this->drawBitmapRect(bitmap, &src, dst, paint, constraint);
1892}
1893
reed41af9662015-01-05 07:49:08 -08001894void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
1895 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001896 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001897 return;
1898 }
reed4c21dc52015-06-25 12:32:03 -07001899 if (!SkNinePatchIter::Valid(bitmap.width(), bitmap.height(), center)) {
reeda5517e22015-07-14 10:54:12 -07001900 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001901 }
reed41af9662015-01-05 07:49:08 -08001902 this->onDrawBitmapNine(bitmap, center, dst, paint);
1903}
1904
1905void SkCanvas::drawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001906 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001907 return;
1908 }
reed41af9662015-01-05 07:49:08 -08001909 this->onDrawSprite(bitmap, left, top, paint);
1910}
1911
reed71c3c762015-06-24 10:29:17 -07001912void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
1913 const SkColor colors[], int count, SkXfermode::Mode mode,
1914 const SkRect* cull, const SkPaint* paint) {
1915 if (count <= 0) {
1916 return;
1917 }
1918 SkASSERT(atlas);
1919 SkASSERT(xform);
1920 SkASSERT(tex);
1921 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
1922}
1923
reed@android.com8a1c16f2008-12-17 15:59:43 +00001924//////////////////////////////////////////////////////////////////////////////
1925// These are the virtual drawing methods
1926//////////////////////////////////////////////////////////////////////////////
1927
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001928void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07001929 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001930 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1931 }
1932}
1933
reed41af9662015-01-05 07:49:08 -08001934void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001935 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001936 this->internalDrawPaint(paint);
1937}
1938
1939void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reedc83a2972015-07-16 07:40:45 -07001940 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, NULL, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001941
1942 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001943 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001944 }
1945
reed@google.com4e2b3d32011-04-07 14:18:59 +00001946 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001947}
1948
reed41af9662015-01-05 07:49:08 -08001949void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
1950 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001951 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001952 if ((long)count <= 0) {
1953 return;
1954 }
1955
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001956 SkRect r, storage;
1957 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001958 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001959 // special-case 2 points (common for drawing a single line)
1960 if (2 == count) {
1961 r.set(pts[0], pts[1]);
1962 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001963 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001964 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001965 bounds = &paint.computeFastStrokeBounds(r, &storage);
1966 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001967 return;
1968 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001969 }
reed@google.coma584aed2012-05-16 14:06:02 +00001970
reed@android.com8a1c16f2008-12-17 15:59:43 +00001971 SkASSERT(pts != NULL);
1972
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001973 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001974
reed@android.com8a1c16f2008-12-17 15:59:43 +00001975 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001976 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001977 }
reed@google.com4b226022011-01-11 18:32:13 +00001978
reed@google.com4e2b3d32011-04-07 14:18:59 +00001979 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001980}
1981
reed41af9662015-01-05 07:49:08 -08001982void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001983 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001984 SkRect storage;
1985 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001986 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08001987 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
1988 // To prevent accidental rejecting at this stage, we have to sort it before we check.
1989 SkRect tmp(r);
1990 tmp.sort();
1991
1992 bounds = &paint.computeFastBounds(tmp, &storage);
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001993 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001994 return;
1995 }
1996 }
reed@google.com4b226022011-01-11 18:32:13 +00001997
reedc83a2972015-07-16 07:40:45 -07001998 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001999
2000 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002001 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002002 }
2003
reed@google.com4e2b3d32011-04-07 14:18:59 +00002004 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002005}
2006
reed41af9662015-01-05 07:49:08 -08002007void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002008 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002009 SkRect storage;
2010 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002011 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002012 bounds = &paint.computeFastBounds(oval, &storage);
2013 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002014 return;
2015 }
2016 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002017
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002018 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002019
2020 while (iter.next()) {
2021 iter.fDevice->drawOval(iter, oval, looper.paint());
2022 }
2023
2024 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002025}
2026
reed41af9662015-01-05 07:49:08 -08002027void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002028 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002029 SkRect storage;
2030 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002031 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002032 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
2033 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002034 return;
2035 }
2036 }
2037
2038 if (rrect.isRect()) {
2039 // call the non-virtual version
2040 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002041 return;
2042 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002043 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002044 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2045 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002046 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002047
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002048 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002049
2050 while (iter.next()) {
2051 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2052 }
2053
2054 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002055}
2056
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002057void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2058 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002059 SkRect storage;
2060 const SkRect* bounds = NULL;
2061 if (paint.canComputeFastBounds()) {
2062 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
2063 if (this->quickReject(*bounds)) {
2064 return;
2065 }
2066 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002067
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002068 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002069
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002070 while (iter.next()) {
2071 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2072 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002073
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002074 LOOPER_END
2075}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002076
reed41af9662015-01-05 07:49:08 -08002077void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002078 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002079 if (!path.isFinite()) {
2080 return;
2081 }
2082
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002083 SkRect storage;
2084 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002085 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002086 const SkRect& pathBounds = path.getBounds();
2087 bounds = &paint.computeFastBounds(pathBounds, &storage);
2088 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002089 return;
2090 }
2091 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002092
2093 const SkRect& r = path.getBounds();
2094 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002095 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002096 this->internalDrawPaint(paint);
2097 }
2098 return;
2099 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002100
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002101 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002102
2103 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002104 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002105 }
2106
reed@google.com4e2b3d32011-04-07 14:18:59 +00002107 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002108}
2109
reeda85d4d02015-05-06 12:56:48 -07002110void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002111 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002112 SkRect bounds = SkRect::MakeXYWH(x, y,
2113 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
2114 if (NULL == paint || paint->canComputeFastBounds()) {
2115 if (paint) {
2116 paint->computeFastBounds(bounds, &bounds);
2117 }
2118 if (this->quickReject(bounds)) {
2119 return;
2120 }
2121 }
2122
2123 SkLazyPaint lazy;
2124 if (NULL == paint) {
2125 paint = lazy.init();
2126 }
2127
2128 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &bounds)
2129
2130 while (iter.next()) {
2131 iter.fDevice->drawImage(iter, image, x, y, looper.paint());
2132 }
2133
2134 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002135}
2136
reed41af9662015-01-05 07:49:08 -08002137void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002138 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002139 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
reeda85d4d02015-05-06 12:56:48 -07002140 SkRect storage;
2141 const SkRect* bounds = &dst;
2142 if (NULL == paint || paint->canComputeFastBounds()) {
2143 if (paint) {
2144 bounds = &paint->computeFastBounds(dst, &storage);
2145 }
2146 if (this->quickReject(*bounds)) {
2147 return;
2148 }
2149 }
2150 SkLazyPaint lazy;
2151 if (NULL == paint) {
2152 paint = lazy.init();
2153 }
2154
reedc83a2972015-07-16 07:40:45 -07002155 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, bounds,
2156 image->isOpaque())
reeda85d4d02015-05-06 12:56:48 -07002157
2158 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002159 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002160 }
2161
2162 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002163}
2164
reed41af9662015-01-05 07:49:08 -08002165void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002166 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002167 SkDEBUGCODE(bitmap.validate();)
2168
reed@google.com3d608122011-11-21 15:16:16 +00002169 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002170 SkRect bounds = {
2171 x, y,
2172 x + SkIntToScalar(bitmap.width()),
2173 y + SkIntToScalar(bitmap.height())
2174 };
2175 if (paint) {
2176 (void)paint->computeFastBounds(bounds, &bounds);
2177 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002178 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002179 return;
2180 }
2181 }
reed@google.com4b226022011-01-11 18:32:13 +00002182
reed@android.com8a1c16f2008-12-17 15:59:43 +00002183 SkMatrix matrix;
2184 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00002185 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002186}
2187
reed@google.com9987ec32011-09-07 11:57:52 +00002188// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002189void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002190 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002191 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002192 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002193 return;
2194 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002195
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002196 SkRect storage;
2197 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00002198 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002199 if (paint) {
2200 bounds = &paint->computeFastBounds(dst, &storage);
2201 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002202 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002203 return;
2204 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002205 }
reed@google.com3d608122011-11-21 15:16:16 +00002206
reed@google.com33535f32012-09-25 15:37:50 +00002207 SkLazyPaint lazy;
2208 if (NULL == paint) {
2209 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002210 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002211
reedc83a2972015-07-16 07:40:45 -07002212 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, bounds,
2213 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002214
reed@google.com33535f32012-09-25 15:37:50 +00002215 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002216 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002217 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002218
reed@google.com33535f32012-09-25 15:37:50 +00002219 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002220}
2221
reed41af9662015-01-05 07:49:08 -08002222void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002223 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002224 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002225 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002226 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002227}
2228
reed4c21dc52015-06-25 12:32:03 -07002229void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2230 const SkPaint* paint) {
2231 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
2232
2233 SkRect storage;
2234 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00002235 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00002236 if (paint) {
2237 bounds = &paint->computeFastBounds(dst, &storage);
2238 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002239 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002240 return;
2241 }
2242 }
reed4c21dc52015-06-25 12:32:03 -07002243
2244 SkLazyPaint lazy;
2245 if (NULL == paint) {
2246 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002247 }
reed4c21dc52015-06-25 12:32:03 -07002248
2249 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
2250
2251 while (iter.next()) {
2252 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002253 }
reed4c21dc52015-06-25 12:32:03 -07002254
2255 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002256}
2257
reed41af9662015-01-05 07:49:08 -08002258void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2259 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002260 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002261 SkDEBUGCODE(bitmap.validate();)
2262
reed4c21dc52015-06-25 12:32:03 -07002263 SkRect storage;
2264 const SkRect* bounds = &dst;
2265 if (NULL == paint || paint->canComputeFastBounds()) {
2266 if (paint) {
2267 bounds = &paint->computeFastBounds(dst, &storage);
2268 }
2269 if (this->quickReject(*bounds)) {
2270 return;
2271 }
2272 }
2273
2274 SkLazyPaint lazy;
2275 if (NULL == paint) {
2276 paint = lazy.init();
2277 }
2278
2279 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
2280
2281 while (iter.next()) {
2282 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2283 }
2284
2285 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002286}
2287
reed@google.comf67e4cf2011-03-15 20:56:58 +00002288class SkDeviceFilteredPaint {
2289public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002290 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002291 uint32_t filteredFlags = device->filterTextFlags(paint);
2292 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002293 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002294 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002295 fPaint = newPaint;
2296 } else {
2297 fPaint = &paint;
2298 }
2299 }
2300
reed@google.comf67e4cf2011-03-15 20:56:58 +00002301 const SkPaint& paint() const { return *fPaint; }
2302
2303private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002304 const SkPaint* fPaint;
2305 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002306};
2307
bungeman@google.com52c748b2011-08-22 21:30:43 +00002308void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2309 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002310 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002311 draw.fDevice->drawRect(draw, r, paint);
2312 } else {
2313 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002314 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002315 draw.fDevice->drawRect(draw, r, p);
2316 }
2317}
2318
2319void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2320 const char text[], size_t byteLength,
2321 SkScalar x, SkScalar y) {
2322 SkASSERT(byteLength == 0 || text != NULL);
2323
2324 // nothing to draw
2325 if (text == NULL || byteLength == 0 ||
2326 draw.fClip->isEmpty() ||
2327 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2328 return;
2329 }
2330
2331 SkScalar width = 0;
2332 SkPoint start;
2333
2334 start.set(0, 0); // to avoid warning
2335 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2336 SkPaint::kStrikeThruText_Flag)) {
2337 width = paint.measureText(text, byteLength);
2338
2339 SkScalar offsetX = 0;
2340 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2341 offsetX = SkScalarHalf(width);
2342 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2343 offsetX = width;
2344 }
2345 start.set(x - offsetX, y);
2346 }
2347
2348 if (0 == width) {
2349 return;
2350 }
2351
2352 uint32_t flags = paint.getFlags();
2353
2354 if (flags & (SkPaint::kUnderlineText_Flag |
2355 SkPaint::kStrikeThruText_Flag)) {
2356 SkScalar textSize = paint.getTextSize();
2357 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2358 SkRect r;
2359
2360 r.fLeft = start.fX;
2361 r.fRight = start.fX + width;
2362
2363 if (flags & SkPaint::kUnderlineText_Flag) {
2364 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2365 start.fY);
2366 r.fTop = offset;
2367 r.fBottom = offset + height;
2368 DrawRect(draw, paint, r, textSize);
2369 }
2370 if (flags & SkPaint::kStrikeThruText_Flag) {
2371 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2372 start.fY);
2373 r.fTop = offset;
2374 r.fBottom = offset + height;
2375 DrawRect(draw, paint, r, textSize);
2376 }
2377 }
2378}
2379
reed@google.come0d9ce82014-04-23 04:00:17 +00002380void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2381 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002382 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002383
2384 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002385 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002386 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002387 DrawTextDecorations(iter, dfp.paint(),
2388 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002389 }
2390
reed@google.com4e2b3d32011-04-07 14:18:59 +00002391 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002392}
2393
reed@google.come0d9ce82014-04-23 04:00:17 +00002394void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2395 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002396 SkPoint textOffset = SkPoint::Make(0, 0);
2397
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002398 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002399
reed@android.com8a1c16f2008-12-17 15:59:43 +00002400 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002401 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002402 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002403 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002404 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002405
reed@google.com4e2b3d32011-04-07 14:18:59 +00002406 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002407}
2408
reed@google.come0d9ce82014-04-23 04:00:17 +00002409void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2410 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002411
2412 SkPoint textOffset = SkPoint::Make(0, constY);
2413
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002414 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002415
reed@android.com8a1c16f2008-12-17 15:59:43 +00002416 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002417 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002418 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002419 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002420 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002421
reed@google.com4e2b3d32011-04-07 14:18:59 +00002422 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002423}
2424
reed@google.come0d9ce82014-04-23 04:00:17 +00002425void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2426 const SkMatrix* matrix, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002427 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002428
reed@android.com8a1c16f2008-12-17 15:59:43 +00002429 while (iter.next()) {
2430 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002431 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002432 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002433
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002434 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002435}
2436
fmalita00d5c2c2014-08-21 08:53:26 -07002437void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2438 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002439
fmalita85d5eb92015-03-04 11:20:12 -08002440 SkRect storage;
2441 const SkRect* bounds = NULL;
fmalita19653d12014-10-16 11:53:30 -07002442 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002443 storage = blob->bounds().makeOffset(x, y);
2444 bounds = &paint.computeFastBounds(storage, &storage);
fmalita7ba7aa72014-08-29 09:46:36 -07002445
fmalita85d5eb92015-03-04 11:20:12 -08002446 if (this->quickReject(*bounds)) {
fmalita7ba7aa72014-08-29 09:46:36 -07002447 return;
2448 }
2449 }
2450
fmalita024f9962015-03-03 19:08:17 -08002451 // We cannot filter in the looper as we normally do, because the paint is
2452 // incomplete at this point (text-related attributes are embedded within blob run paints).
2453 SkDrawFilter* drawFilter = fMCRec->fFilter;
2454 fMCRec->fFilter = NULL;
2455
fmalita85d5eb92015-03-04 11:20:12 -08002456 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002457
fmalitaaa1b9122014-08-28 14:32:24 -07002458 while (iter.next()) {
2459 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002460 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002461 }
2462
fmalitaaa1b9122014-08-28 14:32:24 -07002463 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002464
2465 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002466}
2467
reed@google.come0d9ce82014-04-23 04:00:17 +00002468// These will become non-virtual, so they always call the (virtual) onDraw... method
2469void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2470 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002471 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002472 this->onDrawText(text, byteLength, x, y, paint);
2473}
2474void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2475 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002476 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002477 this->onDrawPosText(text, byteLength, pos, paint);
2478}
2479void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2480 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002481 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002482 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2483}
2484void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2485 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002486 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002487 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2488}
fmalita00d5c2c2014-08-21 08:53:26 -07002489void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2490 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002491 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
bsalomon49f085d2014-09-05 13:34:00 -07002492 if (blob) {
fmalita00d5c2c2014-08-21 08:53:26 -07002493 this->onDrawTextBlob(blob, x, y, paint);
2494 }
2495}
reed@google.come0d9ce82014-04-23 04:00:17 +00002496
reed41af9662015-01-05 07:49:08 -08002497void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2498 const SkPoint verts[], const SkPoint texs[],
2499 const SkColor colors[], SkXfermode* xmode,
2500 const uint16_t indices[], int indexCount,
2501 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002502 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002503 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002504
reed@android.com8a1c16f2008-12-17 15:59:43 +00002505 while (iter.next()) {
2506 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002507 colors, xmode, indices, indexCount,
2508 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002509 }
reed@google.com4b226022011-01-11 18:32:13 +00002510
reed@google.com4e2b3d32011-04-07 14:18:59 +00002511 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002512}
2513
dandovb3c9d1c2014-08-12 08:34:29 -07002514void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2515 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002516 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
dandovb3c9d1c2014-08-12 08:34:29 -07002517 if (NULL == cubics) {
2518 return;
2519 }
mtklein6cfa73a2014-08-13 13:33:49 -07002520
dandovecfff212014-08-04 10:02:00 -07002521 // Since a patch is always within the convex hull of the control points, we discard it when its
2522 // bounding rectangle is completely outside the current clip.
2523 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002524 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002525 if (this->quickReject(bounds)) {
2526 return;
2527 }
mtklein6cfa73a2014-08-13 13:33:49 -07002528
dandovb3c9d1c2014-08-12 08:34:29 -07002529 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2530}
2531
2532void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2533 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2534
dandovecfff212014-08-04 10:02:00 -07002535 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
mtklein6cfa73a2014-08-13 13:33:49 -07002536
dandovecfff212014-08-04 10:02:00 -07002537 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002538 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002539 }
mtklein6cfa73a2014-08-13 13:33:49 -07002540
dandovecfff212014-08-04 10:02:00 -07002541 LOOPER_END
2542}
2543
reeda8db7282015-07-07 10:22:31 -07002544void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
2545 if (dr) {
2546 if (x || y) {
2547 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2548 this->onDrawDrawable(dr, &matrix);
2549 } else {
2550 this->onDrawDrawable(dr, NULL);
2551 }
reed6a070dc2014-11-11 19:36:09 -08002552 }
2553}
2554
reeda8db7282015-07-07 10:22:31 -07002555void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2556 if (dr) {
2557 if (matrix && matrix->isIdentity()) {
2558 matrix = NULL;
2559 }
2560 this->onDrawDrawable(dr, matrix);
2561 }
2562}
2563
2564void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2565 SkRect bounds = dr->getBounds();
2566 if (matrix) {
2567 matrix->mapRect(&bounds);
2568 }
2569 if (this->quickReject(bounds)) {
2570 return;
2571 }
2572 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002573}
2574
reed71c3c762015-06-24 10:29:17 -07002575void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2576 const SkColor colors[], int count, SkXfermode::Mode mode,
2577 const SkRect* cull, const SkPaint* paint) {
2578 if (cull && this->quickReject(*cull)) {
2579 return;
2580 }
2581
2582 SkPaint pnt;
2583 if (paint) {
2584 pnt = *paint;
2585 }
2586
2587 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, NULL)
2588 while (iter.next()) {
2589 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, mode, pnt);
2590 }
2591 LOOPER_END
2592}
2593
reed@android.com8a1c16f2008-12-17 15:59:43 +00002594//////////////////////////////////////////////////////////////////////////////
2595// These methods are NOT virtual, and therefore must call back into virtual
2596// methods, rather than actually drawing themselves.
2597//////////////////////////////////////////////////////////////////////////////
2598
2599void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002600 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002601 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002602 SkPaint paint;
2603
2604 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002605 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002606 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002607 }
2608 this->drawPaint(paint);
2609}
2610
reed@android.com845fdac2009-06-23 03:01:32 +00002611void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002612 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002613 SkPaint paint;
2614
2615 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002616 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002617 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002618 }
2619 this->drawPaint(paint);
2620}
2621
2622void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002623 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002624 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002625
reed@android.com8a1c16f2008-12-17 15:59:43 +00002626 pt.set(x, y);
2627 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2628}
2629
2630void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002631 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002632 SkPoint pt;
2633 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002634
reed@android.com8a1c16f2008-12-17 15:59:43 +00002635 pt.set(x, y);
2636 paint.setColor(color);
2637 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2638}
2639
2640void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2641 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002642 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002643 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002644
reed@android.com8a1c16f2008-12-17 15:59:43 +00002645 pts[0].set(x0, y0);
2646 pts[1].set(x1, y1);
2647 this->drawPoints(kLines_PointMode, 2, pts, paint);
2648}
2649
2650void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2651 SkScalar right, SkScalar bottom,
2652 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002653 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002654 SkRect r;
2655
2656 r.set(left, top, right, bottom);
2657 this->drawRect(r, paint);
2658}
2659
2660void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2661 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002662 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002663 if (radius < 0) {
2664 radius = 0;
2665 }
2666
2667 SkRect r;
2668 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002669 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002670}
2671
2672void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2673 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002674 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002675 if (rx > 0 && ry > 0) {
2676 if (paint.canComputeFastBounds()) {
2677 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002678 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002679 return;
2680 }
2681 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002682 SkRRect rrect;
2683 rrect.setRectXY(r, rx, ry);
2684 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002685 } else {
2686 this->drawRect(r, paint);
2687 }
2688}
2689
reed@android.com8a1c16f2008-12-17 15:59:43 +00002690void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2691 SkScalar sweepAngle, bool useCenter,
2692 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002693 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002694 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2695 this->drawOval(oval, paint);
2696 } else {
2697 SkPath path;
2698 if (useCenter) {
2699 path.moveTo(oval.centerX(), oval.centerY());
2700 }
2701 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2702 if (useCenter) {
2703 path.close();
2704 }
2705 this->drawPath(path, paint);
2706 }
2707}
2708
2709void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2710 const SkPath& path, SkScalar hOffset,
2711 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002712 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002713 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002714
reed@android.com8a1c16f2008-12-17 15:59:43 +00002715 matrix.setTranslate(hOffset, vOffset);
2716 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2717}
2718
reed@android.comf76bacf2009-05-13 14:00:33 +00002719///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07002720
2721/**
2722 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2723 * against the playback cost of recursing into the subpicture to get at its actual ops.
2724 *
2725 * For now we pick a conservatively small value, though measurement (and other heuristics like
2726 * the type of ops contained) may justify changing this value.
2727 */
2728#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07002729
reedd5fa1a42014-08-09 11:08:05 -07002730void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reed1c2c4412015-04-30 13:09:24 -07002731 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
bsalomon49f085d2014-09-05 13:34:00 -07002732 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002733 if (matrix && matrix->isIdentity()) {
2734 matrix = NULL;
2735 }
reed1c2c4412015-04-30 13:09:24 -07002736 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2737 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2738 picture->playback(this);
2739 } else {
2740 this->onDrawPicture(picture, matrix, paint);
2741 }
reedd5fa1a42014-08-09 11:08:05 -07002742 }
2743}
robertphillips9b14f262014-06-04 05:40:44 -07002744
reedd5fa1a42014-08-09 11:08:05 -07002745void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2746 const SkPaint* paint) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002747 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002748 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002749 // Canvas has to first give the device the opportunity to render
2750 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002751 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002752 return; // the device has rendered the entire picture
2753 }
2754 }
2755
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002756 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07002757 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002758}
2759
reed@android.com8a1c16f2008-12-17 15:59:43 +00002760///////////////////////////////////////////////////////////////////////////////
2761///////////////////////////////////////////////////////////////////////////////
2762
2763SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002764 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002765
2766 SkASSERT(canvas);
2767
2768 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2769 fDone = !fImpl->next();
2770}
2771
2772SkCanvas::LayerIter::~LayerIter() {
2773 fImpl->~SkDrawIter();
2774}
2775
2776void SkCanvas::LayerIter::next() {
2777 fDone = !fImpl->next();
2778}
2779
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002780SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002781 return fImpl->getDevice();
2782}
2783
2784const SkMatrix& SkCanvas::LayerIter::matrix() const {
2785 return fImpl->getMatrix();
2786}
2787
2788const SkPaint& SkCanvas::LayerIter::paint() const {
2789 const SkPaint* paint = fImpl->getPaint();
2790 if (NULL == paint) {
2791 paint = &fDefaultPaint;
2792 }
2793 return *paint;
2794}
2795
2796const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2797int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2798int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002799
2800///////////////////////////////////////////////////////////////////////////////
2801
fmalitac3b589a2014-06-05 12:40:07 -07002802SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002803
2804///////////////////////////////////////////////////////////////////////////////
2805
2806static bool supported_for_raster_canvas(const SkImageInfo& info) {
2807 switch (info.alphaType()) {
2808 case kPremul_SkAlphaType:
2809 case kOpaque_SkAlphaType:
2810 break;
2811 default:
2812 return false;
2813 }
2814
2815 switch (info.colorType()) {
2816 case kAlpha_8_SkColorType:
2817 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002818 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002819 break;
2820 default:
2821 return false;
2822 }
2823
2824 return true;
2825}
2826
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002827SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2828 if (!supported_for_raster_canvas(info)) {
2829 return NULL;
2830 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002831
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002832 SkBitmap bitmap;
2833 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2834 return NULL;
2835 }
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002836 return SkNEW_ARGS(SkCanvas, (bitmap));
2837}
reedd5fa1a42014-08-09 11:08:05 -07002838
2839///////////////////////////////////////////////////////////////////////////////
2840
2841SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002842 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07002843 : fCanvas(canvas)
2844 , fSaveCount(canvas->getSaveCount())
2845{
bsalomon49f085d2014-09-05 13:34:00 -07002846 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002847 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07002848 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002849 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07002850 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002851 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07002852 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002853 canvas->save();
2854 }
mtklein6cfa73a2014-08-13 13:33:49 -07002855
bsalomon49f085d2014-09-05 13:34:00 -07002856 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002857 canvas->concat(*matrix);
2858 }
2859}
2860
2861SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
2862 fCanvas->restoreToCount(fSaveCount);
2863}