blob: 342f79f9d99a0b0efa6f55df9687fded9b961798 [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00003 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00004 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00006 */
7
bungemand3ebb482015-08-05 13:57:49 -07008#include "SkBitmapDevice.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +00009#include "SkCanvas.h"
reedd5fa1a42014-08-09 11:08:05 -070010#include "SkCanvasPriv.h"
bungemand3ebb482015-08-05 13:57:49 -070011#include "SkClipStack.h"
reeddbc3cef2015-04-29 12:18:57 -070012#include "SkColorFilter.h"
bungemand3ebb482015-08-05 13:57:49 -070013#include "SkDevice.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000014#include "SkDraw.h"
reed3cb38402015-02-06 08:36:15 -080015#include "SkDrawable.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000016#include "SkDrawFilter.h"
17#include "SkDrawLooper.h"
joshualitt5f5a8d72015-02-25 14:09:45 -080018#include "SkErrorInternals.h"
piotaixrb5fae932014-09-24 13:03:30 -070019#include "SkImage.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000020#include "SkMetaData.h"
reed4c21dc52015-06-25 12:32:03 -070021#include "SkNinePatchIter.h"
reedc83a2972015-07-16 07:40:45 -070022#include "SkPaintPriv.h"
dandovb3c9d1c2014-08-12 08:34:29 -070023#include "SkPatchUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000024#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000025#include "SkRasterClip.h"
reed96472de2014-12-10 09:53:42 -080026#include "SkReadPixelsRec.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000027#include "SkRRect.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000028#include "SkSmallAllocator.h"
reed@google.com97af1a62012-08-28 12:19:02 +000029#include "SkSurface_Base.h"
fmalita7ba7aa72014-08-29 09:46:36 -070030#include "SkTextBlob.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000031#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000032#include "SkTLazy.h"
danakj8f757f52014-11-04 11:48:43 -080033#include "SkTraceEvent.h"
bungemand3ebb482015-08-05 13:57:49 -070034
35#include <new>
reed@android.com8a1c16f2008-12-17 15:59:43 +000036
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000037#if SK_SUPPORT_GPU
38#include "GrRenderTarget.h"
39#endif
40
reedc83a2972015-07-16 07:40:45 -070041/*
42 * Return true if the drawing this rect would hit every pixels in the canvas.
43 *
44 * Returns false if
45 * - rect does not contain the canvas' bounds
46 * - paint is not fill
47 * - paint would blur or otherwise change the coverage of the rect
48 */
49bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
50 ShaderOverrideOpacity overrideOpacity) const {
51 SK_COMPILE_ASSERT((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
52 (int)kNone_ShaderOverrideOpacity,
53 need_matching_enums0);
54 SK_COMPILE_ASSERT((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
55 (int)kOpaque_ShaderOverrideOpacity,
56 need_matching_enums1);
57 SK_COMPILE_ASSERT((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
58 (int)kNotOpaque_ShaderOverrideOpacity,
59 need_matching_enums2);
60
61 const SkISize size = this->getBaseLayerSize();
62 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
63 if (!this->getClipStack()->quickContains(bounds)) {
64 return false;
65 }
66
67 if (rect) {
68 if (!this->getTotalMatrix().rectStaysRect()) {
69 return false; // conservative
70 }
71
72 SkRect devRect;
73 this->getTotalMatrix().mapRect(&devRect, *rect);
fmalita8c0144c2015-07-22 05:56:16 -070074 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -070075 return false;
76 }
77 }
78
79 if (paint) {
80 SkPaint::Style paintStyle = paint->getStyle();
81 if (!(paintStyle == SkPaint::kFill_Style ||
82 paintStyle == SkPaint::kStrokeAndFill_Style)) {
83 return false;
84 }
85 if (paint->getMaskFilter() || paint->getLooper()
86 || paint->getPathEffect() || paint->getImageFilter()) {
87 return false; // conservative
88 }
89 }
90 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
91}
92
93///////////////////////////////////////////////////////////////////////////////////////////////////
94
reedd990e2f2014-12-22 11:58:30 -080095static bool gIgnoreSaveLayerBounds;
96void SkCanvas::Internal_Private_SetIgnoreSaveLayerBounds(bool ignore) {
97 gIgnoreSaveLayerBounds = ignore;
98}
99bool SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds() {
100 return gIgnoreSaveLayerBounds;
101}
102
reed0acf1b42014-12-22 16:12:38 -0800103static bool gTreatSpriteAsBitmap;
104void SkCanvas::Internal_Private_SetTreatSpriteAsBitmap(bool spriteAsBitmap) {
105 gTreatSpriteAsBitmap = spriteAsBitmap;
106}
107bool SkCanvas::Internal_Private_GetTreatSpriteAsBitmap() {
108 return gTreatSpriteAsBitmap;
109}
110
reed@google.comda17f752012-08-16 18:27:05 +0000111// experimental for faster tiled drawing...
112//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +0000113
reed@android.com8a1c16f2008-12-17 15:59:43 +0000114//#define SK_TRACE_SAVERESTORE
115
116#ifdef SK_TRACE_SAVERESTORE
117 static int gLayerCounter;
118 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
119 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
120
121 static int gRecCounter;
122 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
123 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
124
125 static int gCanvasCounter;
126 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
127 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
128#else
129 #define inc_layer()
130 #define dec_layer()
131 #define inc_rec()
132 #define dec_rec()
133 #define inc_canvas()
134 #define dec_canvas()
135#endif
136
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000137typedef SkTLazy<SkPaint> SkLazyPaint;
138
reedc83a2972015-07-16 07:40:45 -0700139void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000140 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700141 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
142 ? SkSurface::kDiscard_ContentChangeMode
143 : SkSurface::kRetain_ContentChangeMode);
144 }
145}
146
147void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
148 ShaderOverrideOpacity overrideOpacity) {
149 if (fSurfaceBase) {
150 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
151 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
152 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
153 // and therefore we don't care which mode we're in.
154 //
155 if (fSurfaceBase->outstandingImageSnapshot()) {
156 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
157 mode = SkSurface::kDiscard_ContentChangeMode;
158 }
159 }
160 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000161 }
162}
163
reed@android.com8a1c16f2008-12-17 15:59:43 +0000164///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000165
reed4a8126e2014-09-22 07:29:03 -0700166static uint32_t filter_paint_flags(const SkSurfaceProps& props, uint32_t flags) {
167 const uint32_t propFlags = props.flags();
168 if (propFlags & SkSurfaceProps::kDisallowDither_Flag) {
169 flags &= ~SkPaint::kDither_Flag;
170 }
171 if (propFlags & SkSurfaceProps::kDisallowAntiAlias_Flag) {
172 flags &= ~SkPaint::kAntiAlias_Flag;
173 }
174 return flags;
175}
176
177///////////////////////////////////////////////////////////////////////////////
178
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000179/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180 The clip/matrix/proc are fields that reflect the top of the save/restore
181 stack. Whenever the canvas changes, it marks a dirty flag, and then before
182 these are used (assuming we're not on a layer) we rebuild these cache
183 values: they reflect the top of the save stack, but translated and clipped
184 by the device's XY offset and bitmap-bounds.
185*/
186struct DeviceCM {
187 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000188 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000189 SkRasterClip fClip;
reed@google.com6f8f2922011-03-04 22:27:10 +0000190 SkPaint* fPaint; // may be null (in the future)
reed61f501f2015-04-29 08:34:00 -0700191 const SkMatrix* fMatrix;
192 SkMatrix fMatrixStorage;
193 const bool fDeviceIsBitmapDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194
reed96e657d2015-03-10 17:30:07 -0700195 DeviceCM(SkBaseDevice* device, const SkPaint* paint, SkCanvas* canvas,
reed86a17e72015-05-14 12:25:22 -0700196 bool conservativeRasterClip, bool deviceIsBitmapDevice)
reedd9544982014-09-09 18:46:22 -0700197 : fNext(NULL)
198 , fClip(conservativeRasterClip)
reed61f501f2015-04-29 08:34:00 -0700199 , fDeviceIsBitmapDevice(deviceIsBitmapDevice)
reedd9544982014-09-09 18:46:22 -0700200 {
201 if (NULL != device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000202 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000203 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204 }
reed@google.com4b226022011-01-11 18:32:13 +0000205 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000206 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000207 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000208
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000209 ~DeviceCM() {
bsalomon49f085d2014-09-05 13:34:00 -0700210 if (fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000211 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000212 fDevice->unref();
213 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000214 SkDELETE(fPaint);
215 }
reed@google.com4b226022011-01-11 18:32:13 +0000216
mtkleinfeaadee2015-04-08 11:25:48 -0700217 void reset(const SkIRect& bounds) {
218 SkASSERT(!fPaint);
219 SkASSERT(!fNext);
220 SkASSERT(fDevice);
221 fClip.setRect(bounds);
222 }
223
reed@google.com045e62d2011-10-24 12:19:46 +0000224 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
225 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000226 int x = fDevice->getOrigin().x();
227 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000228 int width = fDevice->width();
229 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000230
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231 if ((x | y) == 0) {
232 fMatrix = &totalMatrix;
233 fClip = totalClip;
234 } else {
235 fMatrixStorage = totalMatrix;
236 fMatrixStorage.postTranslate(SkIntToScalar(-x),
237 SkIntToScalar(-y));
238 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000239
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240 totalClip.translate(-x, -y, &fClip);
241 }
242
reed@google.com045e62d2011-10-24 12:19:46 +0000243 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244
245 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000246
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000248 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000249 SkRegion::kDifference_Op);
250 }
reed@google.com4b226022011-01-11 18:32:13 +0000251
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000252 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
253
reed@android.com8a1c16f2008-12-17 15:59:43 +0000254#ifdef SK_DEBUG
255 if (!fClip.isEmpty()) {
256 SkIRect deviceR;
257 deviceR.set(0, 0, width, height);
258 SkASSERT(deviceR.contains(fClip.getBounds()));
259 }
260#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000261 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262};
263
264/* This is the record we keep for each save/restore level in the stack.
265 Since a level optionally copies the matrix and/or stack, we have pointers
266 for these fields. If the value is copied for this level, the copy is
267 stored in the ...Storage field, and the pointer points to that. If the
268 value is not copied for this level, we ignore ...Storage, and just point
269 at the corresponding value in the previous level in the stack.
270*/
271class SkCanvas::MCRec {
272public:
reed1f836ee2014-07-07 07:49:34 -0700273 SkDrawFilter* fFilter; // the current filter (or null)
reedd9544982014-09-09 18:46:22 -0700274 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000275 /* If there are any layers in the stack, this points to the top-most
276 one that is at or below this level in the stack (so we know what
277 bitmap/device to draw into from this level. This value is NOT
278 reference counted, since the real owner is either our fLayer field,
279 or a previous one in a lower level.)
280 */
reed2ff1fce2014-12-11 07:07:37 -0800281 DeviceCM* fTopLayer;
282 SkRasterClip fRasterClip;
283 SkMatrix fMatrix;
284 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285
reedd9544982014-09-09 18:46:22 -0700286 MCRec(bool conservativeRasterClip) : fRasterClip(conservativeRasterClip) {
reedd9544982014-09-09 18:46:22 -0700287 fFilter = NULL;
288 fLayer = NULL;
289 fTopLayer = NULL;
reed2ff1fce2014-12-11 07:07:37 -0800290 fMatrix.reset();
291 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700292
reedd9544982014-09-09 18:46:22 -0700293 // don't bother initializing fNext
294 inc_rec();
295 }
reed2ff1fce2014-12-11 07:07:37 -0800296 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
reedd9544982014-09-09 18:46:22 -0700297 fFilter = SkSafeRef(prev.fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298 fLayer = NULL;
reedd9544982014-09-09 18:46:22 -0700299 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800300 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700301
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302 // don't bother initializing fNext
303 inc_rec();
304 }
305 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000306 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000307 SkDELETE(fLayer);
308 dec_rec();
309 }
mtkleinfeaadee2015-04-08 11:25:48 -0700310
311 void reset(const SkIRect& bounds) {
312 SkASSERT(fLayer);
313 SkASSERT(fDeferredSaveCount == 0);
314
315 fMatrix.reset();
316 fRasterClip.setRect(bounds);
317 fLayer->reset(bounds);
318 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319};
320
321class SkDrawIter : public SkDraw {
322public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000323 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000324 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000325 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000326 canvas->updateDeviceCMCache();
327
reed687fa1c2015-04-07 08:00:56 -0700328 fClipStack = canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000329 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000330 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000331 }
reed@google.com4b226022011-01-11 18:32:13 +0000332
reed@android.com8a1c16f2008-12-17 15:59:43 +0000333 bool next() {
334 // skip over recs with empty clips
335 if (fSkipEmptyClips) {
336 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
337 fCurrLayer = fCurrLayer->fNext;
338 }
339 }
340
reed@google.comf68c5e22012-02-24 16:38:58 +0000341 const DeviceCM* rec = fCurrLayer;
342 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000343
344 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000345 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
346 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347 fDevice = rec->fDevice;
reed41e010c2015-06-09 12:16:53 -0700348 if (!fDevice->accessPixels(&fDst)) {
349 fDst.reset(fDevice->imageInfo(), NULL, 0);
350 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000351 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000352 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000353
354 fCurrLayer = rec->fNext;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000355 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000356
reed@android.com8a1c16f2008-12-17 15:59:43 +0000357 return true;
358 }
359 return false;
360 }
reed@google.com4b226022011-01-11 18:32:13 +0000361
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000362 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000363 int getX() const { return fDevice->getOrigin().x(); }
364 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000365 const SkMatrix& getMatrix() const { return *fMatrix; }
366 const SkRegion& getClip() const { return *fClip; }
367 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000368
reed@android.com8a1c16f2008-12-17 15:59:43 +0000369private:
370 SkCanvas* fCanvas;
371 const DeviceCM* fCurrLayer;
372 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000373 SkBool8 fSkipEmptyClips;
374
375 typedef SkDraw INHERITED;
376};
377
378/////////////////////////////////////////////////////////////////////////////
379
reeddbc3cef2015-04-29 12:18:57 -0700380static SkPaint* set_if_needed(SkLazyPaint* lazy, const SkPaint& orig) {
381 return lazy->isValid() ? lazy->get() : lazy->set(orig);
382}
383
384/**
385 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
386 * colorfilter, else return NULL.
387 */
388static SkColorFilter* image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700389 SkImageFilter* imgf = paint.getImageFilter();
390 if (!imgf) {
391 return NULL;
392 }
393
394 SkColorFilter* imgCF;
395 if (!imgf->asAColorFilter(&imgCF)) {
396 return NULL;
397 }
398
399 SkColorFilter* paintCF = paint.getColorFilter();
400 if (NULL == paintCF) {
401 // there is no existing paint colorfilter, so we can just return the imagefilter's
402 return imgCF;
403 }
404
405 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
406 // and we need to combine them into a single colorfilter.
407 SkAutoTUnref<SkColorFilter> autoImgCF(imgCF);
408 return SkColorFilter::CreateComposeFilter(imgCF, paintCF);
reeddbc3cef2015-04-29 12:18:57 -0700409}
410
reed@android.com8a1c16f2008-12-17 15:59:43 +0000411class AutoDrawLooper {
412public:
reed4a8126e2014-09-22 07:29:03 -0700413 AutoDrawLooper(SkCanvas* canvas, const SkSurfaceProps& props, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000414 bool skipLayerForImageFilter = false,
415 const SkRect* bounds = NULL) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000416 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000417 fFilter = canvas->getDrawFilter();
reed4a8126e2014-09-22 07:29:03 -0700418 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000419 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700420 fTempLayerForImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000421 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000422
reeddbc3cef2015-04-29 12:18:57 -0700423 SkColorFilter* simplifiedCF = image_to_color_filter(fOrigPaint);
424 if (simplifiedCF) {
425 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
426 paint->setColorFilter(simplifiedCF)->unref();
427 paint->setImageFilter(NULL);
428 fPaint = paint;
429 }
430
431 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700432 /**
433 * We implement ImageFilters for a given draw by creating a layer, then applying the
434 * imagefilter to the pixels of that layer (its backing surface/image), and then
435 * we call restore() to xfer that layer to the main canvas.
436 *
437 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
438 * 2. Generate the src pixels:
439 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
440 * return (fPaint). We then draw the primitive (using srcover) into a cleared
441 * buffer/surface.
442 * 3. Restore the layer created in #1
443 * The imagefilter is passed the buffer/surface from the layer (now filled with the
444 * src pixels of the primitive). It returns a new "filtered" buffer, which we
445 * draw onto the previous layer using the xfermode from the original paint.
446 */
reed@google.com8926b162012-03-23 15:36:36 +0000447 SkPaint tmp;
reeddbc3cef2015-04-29 12:18:57 -0700448 tmp.setImageFilter(fPaint->getImageFilter());
449 tmp.setXfermode(fPaint->getXfermode());
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000450 (void)canvas->internalSaveLayer(bounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag,
reed76033be2015-03-14 10:54:31 -0700451 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700452 fTempLayerForImageFilter = true;
453 // we remove the imagefilter/xfermode inside doNext()
reed@google.com8926b162012-03-23 15:36:36 +0000454 }
455
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000456 if (SkDrawLooper* looper = paint.getLooper()) {
457 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
458 looper->contextSize());
459 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000460 fIsSimple = false;
461 } else {
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000462 fLooperContext = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000463 // can we be marked as simple?
reed5c476fb2015-04-20 08:04:21 -0700464 fIsSimple = !fFilter && !fTempLayerForImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000465 }
piotaixrb5fae932014-09-24 13:03:30 -0700466
reed4a8126e2014-09-22 07:29:03 -0700467 uint32_t oldFlags = paint.getFlags();
468 fNewPaintFlags = filter_paint_flags(props, oldFlags);
469 if (fIsSimple && (fNewPaintFlags != oldFlags)) {
reeddbc3cef2015-04-29 12:18:57 -0700470 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700471 paint->setFlags(fNewPaintFlags);
472 fPaint = paint;
473 // if we're not simple, doNext() will take care of calling setFlags()
474 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000475 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000476
reed@android.com8a1c16f2008-12-17 15:59:43 +0000477 ~AutoDrawLooper() {
reed5c476fb2015-04-20 08:04:21 -0700478 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000479 fCanvas->internalRestore();
480 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000481 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000482 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000483
reed@google.com4e2b3d32011-04-07 14:18:59 +0000484 const SkPaint& paint() const {
485 SkASSERT(fPaint);
486 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000487 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000488
reed@google.com129ec222012-05-15 13:24:09 +0000489 bool next(SkDrawFilter::Type drawType) {
490 if (fDone) {
491 return false;
492 } else if (fIsSimple) {
493 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000494 return !fPaint->nothingToDraw();
495 } else {
496 return this->doNext(drawType);
497 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000498 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000499
reed@android.com8a1c16f2008-12-17 15:59:43 +0000500private:
reeddbc3cef2015-04-29 12:18:57 -0700501 SkLazyPaint fLazyPaintInit; // base paint storage in case we need to modify it
502 SkLazyPaint fLazyPaintPerLooper; // per-draw-looper storage, so the looper can modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000503 SkCanvas* fCanvas;
504 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000505 SkDrawFilter* fFilter;
506 const SkPaint* fPaint;
507 int fSaveCount;
reed4a8126e2014-09-22 07:29:03 -0700508 uint32_t fNewPaintFlags;
reed5c476fb2015-04-20 08:04:21 -0700509 bool fTempLayerForImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000510 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000511 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000512 SkDrawLooper::Context* fLooperContext;
513 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000514
515 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000516};
517
reed@google.com129ec222012-05-15 13:24:09 +0000518bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000519 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000520 SkASSERT(!fIsSimple);
reed5c476fb2015-04-20 08:04:21 -0700521 SkASSERT(fLooperContext || fFilter || fTempLayerForImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000522
reeddbc3cef2015-04-29 12:18:57 -0700523 SkPaint* paint = fLazyPaintPerLooper.set(fLazyPaintInit.isValid() ?
524 *fLazyPaintInit.get() : fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700525 paint->setFlags(fNewPaintFlags);
reed@google.com129ec222012-05-15 13:24:09 +0000526
reed5c476fb2015-04-20 08:04:21 -0700527 if (fTempLayerForImageFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000528 paint->setImageFilter(NULL);
reed5c476fb2015-04-20 08:04:21 -0700529 paint->setXfermode(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000530 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000531
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000532 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000533 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000534 return false;
535 }
536 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000537 if (!fFilter->filter(paint, drawType)) {
538 fDone = true;
539 return false;
540 }
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000541 if (NULL == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000542 // no looper means we only draw once
543 fDone = true;
544 }
545 }
546 fPaint = paint;
547
548 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000549 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000550 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000551 }
552
553 // call this after any possible paint modifiers
554 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000555 fPaint = NULL;
556 return false;
557 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000558 return true;
559}
560
reed@android.com8a1c16f2008-12-17 15:59:43 +0000561////////// macros to place around the internal draw calls //////////////////
562
reed@google.com8926b162012-03-23 15:36:36 +0000563#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000564 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700565 AutoDrawLooper looper(this, fProps, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000566 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000567 SkDrawIter iter(this);
568
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000569#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000570 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700571 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000572 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000573 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000574
reedc83a2972015-07-16 07:40:45 -0700575#define LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, type, bounds, auxOpaque) \
576 this->predrawNotify(bounds, &paint, auxOpaque); \
577 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
578 while (looper.next(type)) { \
579 SkDrawIter iter(this);
580
reed@google.com4e2b3d32011-04-07 14:18:59 +0000581#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000582
583////////////////////////////////////////////////////////////////////////////
584
mtkleinfeaadee2015-04-08 11:25:48 -0700585void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
586 this->restoreToCount(1);
587 fCachedLocalClipBounds.setEmpty();
588 fCachedLocalClipBoundsDirty = true;
589 fClipStack->reset();
590 fMCRec->reset(bounds);
591
592 // We're peering through a lot of structs here. Only at this scope do we
593 // know that the device is an SkBitmapDevice (really an SkNoPixelsBitmapDevice).
594 static_cast<SkBitmapDevice*>(fMCRec->fLayer->fDevice)->setNewSize(bounds.size());
595}
596
reedd9544982014-09-09 18:46:22 -0700597SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
598 fConservativeRasterClip = SkToBool(flags & kConservativeRasterClip_InitFlag);
reed@google.comc0784db2013-12-13 21:16:12 +0000599 fCachedLocalClipBounds.setEmpty();
600 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000601 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000602 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700603 fDeviceCMDirty = true;
reed2ff1fce2014-12-11 07:07:37 -0800604 fSaveCount = 1;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000605 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606
reed687fa1c2015-04-07 08:00:56 -0700607 fClipStack.reset(SkNEW(SkClipStack));
608
reed@android.com8a1c16f2008-12-17 15:59:43 +0000609 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700610 new (fMCRec) MCRec(fConservativeRasterClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000611
reeda499f902015-05-01 09:34:31 -0700612 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
613 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
reed86a17e72015-05-14 12:25:22 -0700614 new (fDeviceCMStorage) DeviceCM(NULL, NULL, NULL, fConservativeRasterClip, false);
reedb679ca82015-04-07 04:40:48 -0700615
reed@android.com8a1c16f2008-12-17 15:59:43 +0000616 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000617
reed@google.com97af1a62012-08-28 12:19:02 +0000618 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000619
reedf92c8662014-08-18 08:02:43 -0700620 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700621 // The root device and the canvas should always have the same pixel geometry
622 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reed4a8126e2014-09-22 07:29:03 -0700623 if (device->forceConservativeRasterClip()) {
624 fConservativeRasterClip = true;
625 }
reedf92c8662014-08-18 08:02:43 -0700626 device->onAttachToCanvas(this);
627 fMCRec->fLayer->fDevice = SkRef(device);
reed78e27682014-11-19 08:04:34 -0800628 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
reedf92c8662014-08-18 08:02:43 -0700629 }
630 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000631}
632
reed@google.comcde92112011-07-06 20:00:52 +0000633SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000634 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700635 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000636{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000637 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000638
reedd9544982014-09-09 18:46:22 -0700639 this->init(NULL, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000640}
641
reedd9544982014-09-09 18:46:22 -0700642static SkBitmap make_nopixels(int width, int height) {
643 SkBitmap bitmap;
644 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
645 return bitmap;
646}
647
648class SkNoPixelsBitmapDevice : public SkBitmapDevice {
649public:
robertphillipsfcf78292015-06-19 11:49:52 -0700650 SkNoPixelsBitmapDevice(const SkIRect& bounds, const SkSurfaceProps& surfaceProps)
651 : INHERITED(make_nopixels(bounds.width(), bounds.height()), surfaceProps)
reed78e27682014-11-19 08:04:34 -0800652 {
653 this->setOrigin(bounds.x(), bounds.y());
654 }
reedd9544982014-09-09 18:46:22 -0700655
656private:
piotaixrb5fae932014-09-24 13:03:30 -0700657
reedd9544982014-09-09 18:46:22 -0700658 typedef SkBitmapDevice INHERITED;
659};
660
reed96a857e2015-01-25 10:33:58 -0800661SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000662 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800663 , fProps(SkSurfacePropsCopyOrDefault(props))
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000664{
665 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700666
reed78e27682014-11-19 08:04:34 -0800667 this->init(SkNEW_ARGS(SkNoPixelsBitmapDevice,
robertphillipsfcf78292015-06-19 11:49:52 -0700668 (SkIRect::MakeWH(width, height), fProps)), kDefault_InitFlags)->unref();
reedd9544982014-09-09 18:46:22 -0700669}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000670
reed78e27682014-11-19 08:04:34 -0800671SkCanvas::SkCanvas(const SkIRect& bounds, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700672 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700673 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reedd9544982014-09-09 18:46:22 -0700674{
675 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700676
robertphillipsfcf78292015-06-19 11:49:52 -0700677 this->init(SkNEW_ARGS(SkNoPixelsBitmapDevice, (bounds, fProps)), flags)->unref();
reedd9544982014-09-09 18:46:22 -0700678}
679
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000680SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000681 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700682 , fProps(device->surfaceProps())
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000683{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000684 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700685
reedd9544982014-09-09 18:46:22 -0700686 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000687}
688
robertphillipsfcf78292015-06-19 11:49:52 -0700689SkCanvas::SkCanvas(SkBaseDevice* device, InitFlags flags)
690 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700691 , fProps(device->surfaceProps())
robertphillipsfcf78292015-06-19 11:49:52 -0700692{
693 inc_canvas();
694
695 this->init(device, flags);
696}
697
reed4a8126e2014-09-22 07:29:03 -0700698SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700699 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700700 , fProps(props)
reed3716fd02014-09-21 09:39:55 -0700701{
702 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700703
robertphillipsfcf78292015-06-19 11:49:52 -0700704 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap, fProps)));
reed4a8126e2014-09-22 07:29:03 -0700705 this->init(device, kDefault_InitFlags);
706}
reed29c857d2014-09-21 10:25:07 -0700707
reed4a8126e2014-09-22 07:29:03 -0700708SkCanvas::SkCanvas(const SkBitmap& bitmap)
709 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
710 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
711{
712 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700713
robertphillipsfcf78292015-06-19 11:49:52 -0700714 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap, fProps)));
reed4a8126e2014-09-22 07:29:03 -0700715 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000716}
717
718SkCanvas::~SkCanvas() {
719 // free up the contents of our deque
720 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000721
reed@android.com8a1c16f2008-12-17 15:59:43 +0000722 this->internalRestore(); // restore the last, since we're going away
723
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000724 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000725
reed@android.com8a1c16f2008-12-17 15:59:43 +0000726 dec_canvas();
727}
728
reed@android.com8a1c16f2008-12-17 15:59:43 +0000729SkDrawFilter* SkCanvas::getDrawFilter() const {
730 return fMCRec->fFilter;
731}
732
733SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
reed51985e32015-04-11 08:04:56 -0700734 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000735 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
736 return filter;
737}
738
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000739SkMetaData& SkCanvas::getMetaData() {
740 // metadata users are rare, so we lazily allocate it. If that changes we
741 // can decide to just make it a field in the device (rather than a ptr)
742 if (NULL == fMetaData) {
743 fMetaData = new SkMetaData;
744 }
745 return *fMetaData;
746}
747
reed@android.com8a1c16f2008-12-17 15:59:43 +0000748///////////////////////////////////////////////////////////////////////////////
749
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000750void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000751 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000752 if (device) {
753 device->flush();
754 }
755}
756
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000757SkISize SkCanvas::getTopLayerSize() const {
758 SkBaseDevice* d = this->getTopDevice();
759 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
760}
761
762SkIPoint SkCanvas::getTopLayerOrigin() const {
763 SkBaseDevice* d = this->getTopDevice();
764 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
765}
766
767SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000768 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000769 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
770}
771
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000772SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000773 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000774 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000775 SkASSERT(rec && rec->fLayer);
776 return rec->fLayer->fDevice;
777}
778
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000779SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000780 if (updateMatrixClip) {
781 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
782 }
reed@google.com9266fed2011-03-30 00:18:03 +0000783 return fMCRec->fTopLayer->fDevice;
784}
785
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000786bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
787 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
788 return false;
789 }
790
791 bool weAllocated = false;
792 if (NULL == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700793 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000794 return false;
795 }
796 weAllocated = true;
797 }
798
reedcf01e312015-05-23 19:14:51 -0700799 SkAutoPixmapUnlock unlocker;
800 if (bitmap->requestLock(&unlocker)) {
801 const SkPixmap& pm = unlocker.pixmap();
802 if (this->readPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), x, y)) {
803 return true;
804 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000805 }
806
807 if (weAllocated) {
808 bitmap->setPixelRef(NULL);
809 }
810 return false;
811}
reed@google.com51df9e32010-12-23 19:29:18 +0000812
bsalomon@google.comc6980972011-11-02 19:57:21 +0000813bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000814 SkIRect r = srcRect;
815 const SkISize size = this->getBaseLayerSize();
816 if (!r.intersect(0, 0, size.width(), size.height())) {
817 bitmap->reset();
818 return false;
819 }
820
reed84825042014-09-02 12:50:45 -0700821 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000822 // bitmap will already be reset.
823 return false;
824 }
825 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
826 bitmap->reset();
827 return false;
828 }
829 return true;
830}
831
reed96472de2014-12-10 09:53:42 -0800832bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000833 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000834 if (!device) {
835 return false;
836 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000837 const SkISize size = this->getBaseLayerSize();
mtkleinf0f14112014-12-12 08:46:25 -0800838
reed96472de2014-12-10 09:53:42 -0800839 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
840 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000841 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000842 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000843
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000844 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800845 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000846}
847
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000848bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
849 if (bitmap.getTexture()) {
850 return false;
851 }
reedcf01e312015-05-23 19:14:51 -0700852
853 SkAutoPixmapUnlock unlocker;
854 if (bitmap.requestLock(&unlocker)) {
855 const SkPixmap& pm = unlocker.pixmap();
856 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000857 }
858 return false;
859}
860
861bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
862 int x, int y) {
863 switch (origInfo.colorType()) {
864 case kUnknown_SkColorType:
865 case kIndex_8_SkColorType:
866 return false;
867 default:
868 break;
869 }
870 if (NULL == pixels || rowBytes < origInfo.minRowBytes()) {
871 return false;
872 }
873
874 const SkISize size = this->getBaseLayerSize();
875 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
876 if (!target.intersect(0, 0, size.width(), size.height())) {
877 return false;
878 }
879
880 SkBaseDevice* device = this->getDevice();
881 if (!device) {
882 return false;
883 }
884
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000885 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700886 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000887
888 // if x or y are negative, then we have to adjust pixels
889 if (x > 0) {
890 x = 0;
891 }
892 if (y > 0) {
893 y = 0;
894 }
895 // here x,y are either 0 or negative
896 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
897
reed4af35f32014-06-27 17:47:49 -0700898 // Tell our owning surface to bump its generation ID
reedc83a2972015-07-16 07:40:45 -0700899 const bool completeOverwrite = info.dimensions() == size;
900 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700901
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000902 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000903 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000904}
reed@google.com51df9e32010-12-23 19:29:18 +0000905
junov@google.com4370aed2012-01-18 16:21:08 +0000906SkCanvas* SkCanvas::canvasForDrawIter() {
907 return this;
908}
909
reed@android.com8a1c16f2008-12-17 15:59:43 +0000910//////////////////////////////////////////////////////////////////////////////
911
reed@android.com8a1c16f2008-12-17 15:59:43 +0000912void SkCanvas::updateDeviceCMCache() {
913 if (fDeviceCMDirty) {
914 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700915 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000916 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000917
reed@android.com8a1c16f2008-12-17 15:59:43 +0000918 if (NULL == layer->fNext) { // only one layer
reed687fa1c2015-04-07 08:00:56 -0700919 layer->updateMC(totalMatrix, totalClip, *fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000920 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000921 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000922 do {
reed687fa1c2015-04-07 08:00:56 -0700923 layer->updateMC(totalMatrix, clip, *fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000924 } while ((layer = layer->fNext) != NULL);
925 }
926 fDeviceCMDirty = false;
927 }
928}
929
reed@android.com8a1c16f2008-12-17 15:59:43 +0000930///////////////////////////////////////////////////////////////////////////////
931
reed2ff1fce2014-12-11 07:07:37 -0800932void SkCanvas::checkForDeferredSave() {
933 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800934 this->doSave();
935 }
936}
937
reedf0090cb2014-11-26 08:55:51 -0800938int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800939#ifdef SK_DEBUG
940 int count = 0;
941 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
942 for (;;) {
943 const MCRec* rec = (const MCRec*)iter.next();
944 if (!rec) {
945 break;
946 }
947 count += 1 + rec->fDeferredSaveCount;
948 }
949 SkASSERT(count == fSaveCount);
950#endif
951 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -0800952}
953
954int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -0800955 fSaveCount += 1;
956 fMCRec->fDeferredSaveCount += 1;
957 return this->getSaveCount() - 1; // return our prev value
958}
959
960void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -0800961 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -0700962
963 SkASSERT(fMCRec->fDeferredSaveCount > 0);
964 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800965 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -0800966}
967
968void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -0800969 if (fMCRec->fDeferredSaveCount > 0) {
970 SkASSERT(fSaveCount > 1);
971 fSaveCount -= 1;
972 fMCRec->fDeferredSaveCount -= 1;
973 } else {
974 // check for underflow
975 if (fMCStack.count() > 1) {
976 this->willRestore();
977 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -0700978 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800979 this->internalRestore();
980 this->didRestore();
981 }
reedf0090cb2014-11-26 08:55:51 -0800982 }
983}
984
985void SkCanvas::restoreToCount(int count) {
986 // sanity check
987 if (count < 1) {
988 count = 1;
989 }
mtkleinf0f14112014-12-12 08:46:25 -0800990
reedf0090cb2014-11-26 08:55:51 -0800991 int n = this->getSaveCount() - count;
992 for (int i = 0; i < n; ++i) {
993 this->restore();
994 }
995}
996
reed2ff1fce2014-12-11 07:07:37 -0800997void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000998 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700999 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +00001000 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +00001001
reed687fa1c2015-04-07 08:00:56 -07001002 fClipStack->save();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001003}
1004
reed@android.com8a1c16f2008-12-17 15:59:43 +00001005static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +00001006#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +00001007 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +00001008#else
1009 return true;
1010#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001011}
1012
junov@chromium.orga907ac32012-02-24 21:54:07 +00001013bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
reed9b3aa542015-03-11 08:47:12 -07001014 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001015 SkIRect clipBounds;
1016 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001017 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +00001018 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001019
reed96e657d2015-03-10 17:30:07 -07001020 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
1021
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001022 if (imageFilter) {
reed96e657d2015-03-10 17:30:07 -07001023 imageFilter->filterBounds(clipBounds, ctm, &clipBounds);
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001024 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001025 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -07001026 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001027 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +00001028
reed96e657d2015-03-10 17:30:07 -07001029 ctm.mapRect(&r, *bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001030 r.roundOut(&ir);
1031 // early exit if the layer's bounds are clipped out
1032 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001033 if (bounds_affects_clip(flags)) {
reed9b3aa542015-03-11 08:47:12 -07001034 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001035 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001036 }
junov@chromium.orga907ac32012-02-24 21:54:07 +00001037 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001038 }
1039 } else { // no user bounds, so just use the clip
1040 ir = clipBounds;
1041 }
reed180aec42015-03-11 10:39:04 -07001042 SkASSERT(!ir.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001043
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +00001044 if (bounds_affects_clip(flags)) {
reed180aec42015-03-11 10:39:04 -07001045 // Simplify the current clips since they will be applied properly during restore()
reed9b3aa542015-03-11 08:47:12 -07001046 fCachedLocalClipBoundsDirty = true;
reed687fa1c2015-04-07 08:00:56 -07001047 fClipStack->clipDevRect(ir, SkRegion::kReplace_Op);
reed180aec42015-03-11 10:39:04 -07001048 fMCRec->fRasterClip.setRect(ir);
junov@chromium.orga907ac32012-02-24 21:54:07 +00001049 }
1050
1051 if (intersection) {
1052 *intersection = ir;
1053 }
1054 return true;
1055}
1056
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001057int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
reedd990e2f2014-12-22 11:58:30 -08001058 if (gIgnoreSaveLayerBounds) {
1059 bounds = NULL;
1060 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001061 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag);
reeda6441162015-03-26 13:40:09 -07001062 fSaveCount += 1;
reed76033be2015-03-14 10:54:31 -07001063 this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, strategy);
reed2ff1fce2014-12-11 07:07:37 -08001064 return this->getSaveCount() - 1;
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001065}
1066
reed2ff1fce2014-12-11 07:07:37 -08001067int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags) {
reedd990e2f2014-12-22 11:58:30 -08001068 if (gIgnoreSaveLayerBounds) {
1069 bounds = NULL;
1070 }
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001071 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
reeda6441162015-03-26 13:40:09 -07001072 fSaveCount += 1;
reed76033be2015-03-14 10:54:31 -07001073 this->internalSaveLayer(bounds, paint, flags, strategy);
reed2ff1fce2014-12-11 07:07:37 -08001074 return this->getSaveCount() - 1;
reed@google.com8926b162012-03-23 15:36:36 +00001075}
1076
reed2ff1fce2014-12-11 07:07:37 -08001077void SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
reed76033be2015-03-14 10:54:31 -07001078 SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +00001079#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
commit-bot@chromium.org2a5cd602014-05-30 20:41:20 +00001080 flags |= kClipToLayer_SaveFlag;
reed@google.comb93ba452014-03-10 19:47:58 +00001081#endif
1082
junov@chromium.orga907ac32012-02-24 21:54:07 +00001083 // do this before we create the layer. We don't call the public save() since
1084 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001085 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001086
1087 fDeviceCMDirty = true;
1088
1089 SkIRect ir;
reeddaa57bf2015-05-15 10:39:17 -07001090 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed2ff1fce2014-12-11 07:07:37 -08001091 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001092 }
1093
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001094 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1095 // the clipRectBounds() call above?
1096 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001097 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001098 }
1099
reed76033be2015-03-14 10:54:31 -07001100 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
reed8dc0ccb2015-03-20 06:32:52 -07001101 SkPixelGeometry geo = fProps.pixelGeometry();
1102 if (paint) {
reed76033be2015-03-14 10:54:31 -07001103 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001104 if (paint->getImageFilter() || paint->getColorFilter()) {
reed76033be2015-03-14 10:54:31 -07001105 isOpaque = false;
reed8dc0ccb2015-03-20 06:32:52 -07001106 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001107 }
1108 }
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001109 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
1110 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001111
reedb2db8982014-11-13 12:41:02 -08001112 SkBaseDevice* device = this->getTopDevice();
1113 if (NULL == device) {
1114 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001115 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001116 }
reedb2db8982014-11-13 12:41:02 -08001117
reed61f501f2015-04-29 08:34:00 -07001118 bool forceSpriteOnRestore = false;
1119 {
reeddaa57bf2015-05-15 10:39:17 -07001120 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
reed61f501f2015-04-29 08:34:00 -07001121 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo);
1122 SkBaseDevice* newDev = device->onCreateDevice(createInfo, paint);
1123 if (NULL == newDev) {
1124 // If onCreateDevice didn't succeed, try raster (e.g. PDF couldn't handle the paint)
robertphillips9a53fd72015-06-22 09:46:59 -07001125 const SkSurfaceProps surfaceProps(fProps.flags(), createInfo.fPixelGeometry);
1126 newDev = SkBitmapDevice::Create(createInfo.fInfo, surfaceProps);
reed61f501f2015-04-29 08:34:00 -07001127 if (NULL == newDev) {
1128 SkErrorInternals::SetError(kInternalError_SkError,
1129 "Unable to create device for layer.");
1130 return;
1131 }
1132 forceSpriteOnRestore = true;
1133 }
1134 device = newDev;
bungeman@google.come25c6842011-08-17 14:53:54 +00001135 }
bsalomon@google.come97f0852011-06-17 13:10:25 +00001136
reed@google.com6f8f2922011-03-04 22:27:10 +00001137 device->setOrigin(ir.fLeft, ir.fTop);
reed61f501f2015-04-29 08:34:00 -07001138 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, paint, this, fConservativeRasterClip,
reed86a17e72015-05-14 12:25:22 -07001139 forceSpriteOnRestore));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001140 device->unref();
1141
1142 layer->fNext = fMCRec->fTopLayer;
1143 fMCRec->fLayer = layer;
1144 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reed@android.com8a1c16f2008-12-17 15:59:43 +00001145}
1146
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001147int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
1148 return this->saveLayerAlpha(bounds, alpha, kARGB_ClipLayer_SaveFlag);
1149}
1150
reed@android.com8a1c16f2008-12-17 15:59:43 +00001151int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
1152 SaveFlags flags) {
1153 if (0xFF == alpha) {
1154 return this->saveLayer(bounds, NULL, flags);
1155 } else {
1156 SkPaint tmpPaint;
1157 tmpPaint.setAlpha(alpha);
1158 return this->saveLayer(bounds, &tmpPaint, flags);
1159 }
1160}
1161
reed@android.com8a1c16f2008-12-17 15:59:43 +00001162void SkCanvas::internalRestore() {
1163 SkASSERT(fMCStack.count() != 0);
1164
1165 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001166 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001167
reed687fa1c2015-04-07 08:00:56 -07001168 fClipStack->restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001169
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001170 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001171 DeviceCM* layer = fMCRec->fLayer; // may be null
1172 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
1173 fMCRec->fLayer = NULL;
1174
1175 // now do the normal restore()
1176 fMCRec->~MCRec(); // balanced in save()
1177 fMCStack.pop_back();
1178 fMCRec = (MCRec*)fMCStack.back();
1179
1180 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1181 since if we're being recorded, we don't want to record this (the
1182 recorder will have already recorded the restore).
1183 */
bsalomon49f085d2014-09-05 13:34:00 -07001184 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001185 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001186 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001187 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
reed61f501f2015-04-29 08:34:00 -07001188 layer->fPaint, layer->fDeviceIsBitmapDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001189 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001190 fDeviceCMDirty = true;
reedb679ca82015-04-07 04:40:48 -07001191 SkDELETE(layer);
1192 } else {
1193 // we're at the root
reeda499f902015-05-01 09:34:31 -07001194 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001195 layer->~DeviceCM();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001196 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001197 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001198}
1199
reed4a8126e2014-09-22 07:29:03 -07001200SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
1201 if (NULL == props) {
1202 props = &fProps;
1203 }
1204 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001205}
1206
reed4a8126e2014-09-22 07:29:03 -07001207SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001208 SkBaseDevice* dev = this->getDevice();
reed4a8126e2014-09-22 07:29:03 -07001209 return dev ? dev->newSurface(info, props) : NULL;
reed@google.com76f10a32014-02-05 15:32:21 +00001210}
1211
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001212SkImageInfo SkCanvas::imageInfo() const {
1213 SkBaseDevice* dev = this->getDevice();
1214 if (dev) {
1215 return dev->imageInfo();
1216 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001217 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001218 }
1219}
1220
1221const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
reed884e97c2015-05-26 11:31:54 -07001222 SkPixmap pmap;
1223 if (!this->onPeekPixels(&pmap)) {
1224 return NULL;
1225 }
1226 if (info) {
1227 *info = pmap.info();
1228 }
1229 if (rowBytes) {
1230 *rowBytes = pmap.rowBytes();
1231 }
1232 return pmap.addr();
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001233}
1234
reed884e97c2015-05-26 11:31:54 -07001235bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001236 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001237 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001238}
1239
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001240void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001241 SkPixmap pmap;
1242 if (!this->onAccessTopLayerPixels(&pmap)) {
1243 return NULL;
1244 }
1245 if (info) {
1246 *info = pmap.info();
1247 }
1248 if (rowBytes) {
1249 *rowBytes = pmap.rowBytes();
1250 }
1251 if (origin) {
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001252 *origin = this->getTopDevice(false)->getOrigin();
1253 }
reed884e97c2015-05-26 11:31:54 -07001254 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001255}
1256
reed884e97c2015-05-26 11:31:54 -07001257bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001258 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001259 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001260}
1261
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001262SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1263 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1264 if (NULL == fAddr) {
1265 fInfo = canvas->imageInfo();
reed84825042014-09-02 12:50:45 -07001266 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.tryAllocPixels(fInfo)) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001267 return; // failure, fAddr is NULL
1268 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001269 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1270 return; // failure, fAddr is NULL
1271 }
1272 fAddr = fBitmap.getPixels();
1273 fRowBytes = fBitmap.rowBytes();
1274 }
1275 SkASSERT(fAddr); // success
1276}
1277
1278bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1279 if (fAddr) {
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +00001280 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001281 } else {
1282 bitmap->reset();
1283 return false;
1284 }
1285}
1286
reed@android.com8a1c16f2008-12-17 15:59:43 +00001287/////////////////////////////////////////////////////////////////////////////
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001288void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001289 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001290 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001291 return;
1292 }
1293
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001294 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001295 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001296 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001297 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001298
1299 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001300
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001301 SkRect storage;
1302 const SkRect* bounds = NULL;
1303 if (paint && paint->canComputeFastBounds()) {
1304 bitmap.getBounds(&storage);
1305 matrix.mapRect(&storage);
1306 bounds = &paint->computeFastBounds(storage, &storage);
1307 }
1308
1309 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001310
1311 while (iter.next()) {
1312 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1313 }
1314
1315 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001316}
1317
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001318void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed61f501f2015-04-29 08:34:00 -07001319 const SkPaint* paint, bool deviceIsBitmapDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001320 SkPaint tmp;
1321 if (NULL == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001322 paint = &tmp;
1323 }
reed@google.com4b226022011-01-11 18:32:13 +00001324
reed@google.com8926b162012-03-23 15:36:36 +00001325 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001326 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001327 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001328 paint = &looper.paint();
1329 SkImageFilter* filter = paint->getImageFilter();
1330 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001331 if (filter && !dstDev->canHandleImageFilter(filter)) {
robertphillipsefbffed2015-06-22 12:06:08 -07001332 SkImageFilter::Proxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001333 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001334 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001335 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001336 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001337 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001338 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblancobe129b22014-08-08 07:14:35 -07001339 SkAutoTUnref<SkImageFilter::Cache> cache(dstDev->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001340 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001341 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001342 SkPaint tmpUnfiltered(*paint);
1343 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001344 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1345 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001346 }
reed61f501f2015-04-29 08:34:00 -07001347 } else if (deviceIsBitmapDevice) {
reed9572a102015-05-26 19:22:17 -07001348 const SkBitmap& src = static_cast<SkBitmapDevice*>(srcDev)->fBitmap;
reed61f501f2015-04-29 08:34:00 -07001349 dstDev->drawSprite(iter, src, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001350 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001351 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001352 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001353 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001354 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001355}
1356
reed41af9662015-01-05 07:49:08 -08001357void SkCanvas::onDrawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint* paint) {
reed0acf1b42014-12-22 16:12:38 -08001358 if (gTreatSpriteAsBitmap) {
1359 this->save();
1360 this->resetMatrix();
1361 this->drawBitmap(bitmap, SkIntToScalar(x), SkIntToScalar(y), paint);
1362 this->restore();
1363 return;
1364 }
1365
danakj9881d632014-11-26 12:41:06 -08001366 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawSprite()");
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001367 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001368 return;
1369 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001370 SkDEBUGCODE(bitmap.validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001371
reed@google.com8926b162012-03-23 15:36:36 +00001372 SkPaint tmp;
1373 if (NULL == paint) {
1374 paint = &tmp;
1375 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001376
reed@google.com8926b162012-03-23 15:36:36 +00001377 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001378
reed@google.com8926b162012-03-23 15:36:36 +00001379 while (iter.next()) {
1380 paint = &looper.paint();
1381 SkImageFilter* filter = paint->getImageFilter();
1382 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1383 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
robertphillipsefbffed2015-06-22 12:06:08 -07001384 SkImageFilter::Proxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001385 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001386 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001387 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001388 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
halcanaryf622a6c2014-10-24 12:54:53 -07001389 const SkIRect clipBounds = bitmap.bounds();
senorblancobe129b22014-08-08 07:14:35 -07001390 SkAutoTUnref<SkImageFilter::Cache> cache(iter.fDevice->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001391 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001392 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001393 SkPaint tmpUnfiltered(*paint);
1394 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001395 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001396 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001397 }
1398 } else {
1399 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1400 }
1401 }
1402 LOOPER_END
1403}
1404
reed@android.com8a1c16f2008-12-17 15:59:43 +00001405/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001406void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001407 SkMatrix m;
1408 m.setTranslate(dx, dy);
1409 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001410}
1411
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001412void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001413 SkMatrix m;
1414 m.setScale(sx, sy);
1415 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001416}
1417
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001418void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001419 SkMatrix m;
1420 m.setRotate(degrees);
1421 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001422}
1423
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001424void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001425 SkMatrix m;
1426 m.setSkew(sx, sy);
1427 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001428}
1429
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001430void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001431 if (matrix.isIdentity()) {
1432 return;
1433 }
1434
reed2ff1fce2014-12-11 07:07:37 -08001435 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001436 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001437 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001438 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001439
1440 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001441}
1442
reed86a17e72015-05-14 12:25:22 -07001443void SkCanvas::setMatrix(const SkMatrix& matrix) {
1444 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001445 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001446 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001447 fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001448 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001449}
1450
reed@android.com8a1c16f2008-12-17 15:59:43 +00001451void SkCanvas::resetMatrix() {
1452 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001453
reed@android.com8a1c16f2008-12-17 15:59:43 +00001454 matrix.reset();
1455 this->setMatrix(matrix);
1456}
1457
1458//////////////////////////////////////////////////////////////////////////////
1459
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001460void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001461 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001462 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1463 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001464}
1465
1466void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001467#ifdef SK_ENABLE_CLIP_QUICKREJECT
1468 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001469 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001470 return false;
1471 }
1472
reed@google.com3b3e8952012-08-16 20:53:31 +00001473 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001474 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001475 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001476
reed687fa1c2015-04-07 08:00:56 -07001477 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001478 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001479 }
1480 }
1481#endif
1482
reed@google.com5c3d1472011-02-22 19:12:23 +00001483 AutoValidateClip avc(this);
1484
reed@android.com8a1c16f2008-12-17 15:59:43 +00001485 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001486 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001487 if (!fAllowSoftClip) {
1488 edgeStyle = kHard_ClipEdgeStyle;
1489 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001490
reed1f836ee2014-07-07 07:49:34 -07001491 if (fMCRec->fMatrix.rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001492 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001493 // the matrix. This means we don't have to a) make a path, and b) tell
1494 // the region code to scan-convert the path, only to discover that it
1495 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001496 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001497
reed1f836ee2014-07-07 07:49:34 -07001498 fMCRec->fMatrix.mapRect(&r, rect);
reed687fa1c2015-04-07 08:00:56 -07001499 fClipStack->clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reedd9544982014-09-09 18:46:22 -07001500 fMCRec->fRasterClip.op(r, this->getBaseLayerSize(), op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001501 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001502 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001503 // and clip against that, since it can handle any matrix. However, to
1504 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1505 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001506 SkPath path;
1507
1508 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001509 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001510 }
1511}
1512
reed73e714e2014-09-04 09:02:23 -07001513static void rasterclip_path(SkRasterClip* rc, const SkCanvas* canvas, const SkPath& devPath,
1514 SkRegion::Op op, bool doAA) {
reedd64c9482014-09-05 17:37:38 -07001515 rc->op(devPath, canvas->getBaseLayerSize(), op, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001516}
1517
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001518void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001519 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001520 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001521 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001522 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1523 } else {
1524 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001525 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001526}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001527
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001528void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001529 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001530 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001531 AutoValidateClip avc(this);
1532
1533 fDeviceCMDirty = true;
1534 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001535 if (!fAllowSoftClip) {
1536 edgeStyle = kHard_ClipEdgeStyle;
1537 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001538
reed687fa1c2015-04-07 08:00:56 -07001539 fClipStack->clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001540
1541 SkPath devPath;
1542 devPath.addRRect(transformedRRect);
1543
reed73e714e2014-09-04 09:02:23 -07001544 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001545 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001546 }
1547
1548 SkPath path;
1549 path.addRRect(rrect);
1550 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001551 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001552}
1553
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001554void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001555 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001556 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1557 SkRect r;
1558 if (!path.isInverseFillType() && path.isRect(&r)) {
1559 this->onClipRect(r, op, edgeStyle);
1560 } else {
1561 this->onClipPath(path, op, edgeStyle);
1562 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001563}
1564
1565void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001566#ifdef SK_ENABLE_CLIP_QUICKREJECT
1567 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001568 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001569 return false;
1570 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001571
reed@google.com3b3e8952012-08-16 20:53:31 +00001572 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001573 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001574 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001575
reed687fa1c2015-04-07 08:00:56 -07001576 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001577 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001578 }
1579 }
1580#endif
1581
reed@google.com5c3d1472011-02-22 19:12:23 +00001582 AutoValidateClip avc(this);
1583
reed@android.com8a1c16f2008-12-17 15:59:43 +00001584 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001585 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001586 if (!fAllowSoftClip) {
1587 edgeStyle = kHard_ClipEdgeStyle;
1588 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001589
1590 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001591 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001592
reed@google.comfe701122011-11-08 19:41:23 +00001593 // Check if the transfomation, or the original path itself
1594 // made us empty. Note this can also happen if we contained NaN
1595 // values. computing the bounds detects this, and will set our
1596 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1597 if (devPath.getBounds().isEmpty()) {
1598 // resetting the path will remove any NaN or other wanky values
1599 // that might upset our scan converter.
1600 devPath.reset();
1601 }
1602
reed@google.com5c3d1472011-02-22 19:12:23 +00001603 // if we called path.swap() we could avoid a deep copy of this path
reed687fa1c2015-04-07 08:00:56 -07001604 fClipStack->clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001605
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001606 if (fAllowSimplifyClip) {
fmalita1a481fe2015-02-04 07:39:34 -08001607 bool clipIsAA = getClipStack()->asPath(&devPath);
1608 if (clipIsAA) {
1609 edgeStyle = kSoft_ClipEdgeStyle;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001610 }
fmalita1a481fe2015-02-04 07:39:34 -08001611
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001612 op = SkRegion::kReplace_Op;
1613 }
1614
reed73e714e2014-09-04 09:02:23 -07001615 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001616}
1617
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001618void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed2ff1fce2014-12-11 07:07:37 -08001619 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001620 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001621}
1622
1623void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001624 AutoValidateClip avc(this);
1625
reed@android.com8a1c16f2008-12-17 15:59:43 +00001626 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001627 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001628
reed@google.com5c3d1472011-02-22 19:12:23 +00001629 // todo: signal fClipStack that we have a region, and therefore (I guess)
1630 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001631 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001632
reed1f836ee2014-07-07 07:49:34 -07001633 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001634}
1635
reed@google.com819c9212011-02-23 18:56:55 +00001636#ifdef SK_DEBUG
1637void SkCanvas::validateClip() const {
1638 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001639 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001640 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001641 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001642 return;
1643 }
1644
reed@google.com819c9212011-02-23 18:56:55 +00001645 SkIRect ir;
1646 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001647 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001648
reed687fa1c2015-04-07 08:00:56 -07001649 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001650 const SkClipStack::Element* element;
1651 while ((element = iter.next()) != NULL) {
1652 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001653 case SkClipStack::Element::kRect_Type:
1654 element->getRect().round(&ir);
1655 tmpClip.op(ir, element->getOp());
1656 break;
1657 case SkClipStack::Element::kEmpty_Type:
1658 tmpClip.setEmpty();
1659 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001660 default: {
1661 SkPath path;
1662 element->asPath(&path);
reed73e714e2014-09-04 09:02:23 -07001663 rasterclip_path(&tmpClip, this, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001664 break;
1665 }
reed@google.com819c9212011-02-23 18:56:55 +00001666 }
1667 }
reed@google.com819c9212011-02-23 18:56:55 +00001668}
1669#endif
1670
reed@google.com90c07ea2012-04-13 13:50:27 +00001671void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001672 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001673 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001674
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001675 while ((element = iter.next()) != NULL) {
fmalitac3b589a2014-06-05 12:40:07 -07001676 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001677 }
1678}
1679
reed@google.com5c3d1472011-02-22 19:12:23 +00001680///////////////////////////////////////////////////////////////////////////////
1681
reed@google.com754de5f2014-02-24 19:38:20 +00001682bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001683 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001684}
1685
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001686bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001687 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001688}
1689
reed@google.com3b3e8952012-08-16 20:53:31 +00001690bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001691 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001692 return true;
1693
reed1f836ee2014-07-07 07:49:34 -07001694 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001695 return true;
1696 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001697
reed1f836ee2014-07-07 07:49:34 -07001698 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001699 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001700 fMCRec->fMatrix.mapRect(&dst, rect);
reedb07a94f2014-11-19 05:03:18 -08001701 return !SkIRect::Intersects(dst.roundOut(), fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001702 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001703 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001704
reed@android.coma380ae42009-07-21 01:17:02 +00001705 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001706 // TODO: should we use | instead, or compare all 4 at once?
1707 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001708 return true;
1709 }
reed@google.comc0784db2013-12-13 21:16:12 +00001710 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001711 return true;
1712 }
1713 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001714 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001715}
1716
reed@google.com3b3e8952012-08-16 20:53:31 +00001717bool SkCanvas::quickReject(const SkPath& path) const {
1718 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001719}
1720
reed@google.com3b3e8952012-08-16 20:53:31 +00001721bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001722 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001723 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001724 return false;
1725 }
1726
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001727 SkMatrix inverse;
1728 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001729 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001730 if (bounds) {
1731 bounds->setEmpty();
1732 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001733 return false;
1734 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001735
bsalomon49f085d2014-09-05 13:34:00 -07001736 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001737 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001738 // adjust it outwards in case we are antialiasing
1739 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001740
reed@google.com8f4d2302013-12-17 16:44:46 +00001741 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1742 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001743 inverse.mapRect(bounds, r);
1744 }
1745 return true;
1746}
1747
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001748bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001749 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001750 if (clip.isEmpty()) {
1751 if (bounds) {
1752 bounds->setEmpty();
1753 }
1754 return false;
1755 }
1756
bsalomon49f085d2014-09-05 13:34:00 -07001757 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001758 *bounds = clip.getBounds();
1759 }
1760 return true;
1761}
1762
reed@android.com8a1c16f2008-12-17 15:59:43 +00001763const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001764 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001765}
1766
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001767const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001768 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001769}
1770
reed@google.com9c135db2014-03-12 18:28:35 +00001771GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1772 SkBaseDevice* dev = this->getTopDevice();
1773 return dev ? dev->accessRenderTarget() : NULL;
1774}
1775
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001776GrContext* SkCanvas::getGrContext() {
1777#if SK_SUPPORT_GPU
1778 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07001779 if (device) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001780 GrRenderTarget* renderTarget = device->accessRenderTarget();
bsalomon49f085d2014-09-05 13:34:00 -07001781 if (renderTarget) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001782 return renderTarget->getContext();
1783 }
1784 }
1785#endif
1786
1787 return NULL;
1788
1789}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001790
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001791void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1792 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001793 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001794 if (outer.isEmpty()) {
1795 return;
1796 }
1797 if (inner.isEmpty()) {
1798 this->drawRRect(outer, paint);
1799 return;
1800 }
1801
1802 // We don't have this method (yet), but technically this is what we should
1803 // be able to assert...
1804 // SkASSERT(outer.contains(inner));
1805 //
1806 // For now at least check for containment of bounds
1807 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1808
1809 this->onDrawDRRect(outer, inner, paint);
1810}
1811
reed41af9662015-01-05 07:49:08 -08001812// These need to stop being virtual -- clients need to override the onDraw... versions
1813
1814void SkCanvas::drawPaint(const SkPaint& paint) {
1815 this->onDrawPaint(paint);
1816}
1817
1818void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1819 this->onDrawRect(r, paint);
1820}
1821
1822void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1823 this->onDrawOval(r, paint);
1824}
1825
1826void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1827 this->onDrawRRect(rrect, paint);
1828}
1829
1830void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1831 this->onDrawPoints(mode, count, pts, paint);
1832}
1833
1834void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
1835 const SkPoint texs[], const SkColor colors[], SkXfermode* xmode,
1836 const uint16_t indices[], int indexCount, const SkPaint& paint) {
1837 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
1838 indices, indexCount, paint);
1839}
1840
1841void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1842 this->onDrawPath(path, paint);
1843}
1844
reeda85d4d02015-05-06 12:56:48 -07001845void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
1846 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001847}
1848
1849void SkCanvas::drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07001850 const SkPaint* paint, SrcRectConstraint constraint) {
reeda85d4d02015-05-06 12:56:48 -07001851 if (dst.isEmpty()) {
1852 return;
1853 }
reed562fe472015-07-28 07:35:14 -07001854 this->onDrawImageRect(image, src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08001855}
1856
reed84984ef2015-07-17 07:09:43 -07001857void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1858 const SkPaint* paint, SrcRectConstraint constraint) {
1859 SkRect src = SkRect::Make(isrc);
1860 this->drawImageRect(image, &src, dst, paint, constraint);
1861}
1862
reed4c21dc52015-06-25 12:32:03 -07001863void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1864 const SkPaint* paint) {
1865 if (dst.isEmpty()) {
1866 return;
1867 }
1868 if (!SkNinePatchIter::Valid(image->width(), image->height(), center)) {
1869 this->drawImageRect(image, NULL, dst, paint);
1870 }
1871 this->onDrawImageNine(image, center, dst, paint);
1872}
1873
reed41af9662015-01-05 07:49:08 -08001874void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001875 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001876 return;
1877 }
reed41af9662015-01-05 07:49:08 -08001878 this->onDrawBitmap(bitmap, dx, dy, paint);
1879}
1880
reeda5517e22015-07-14 10:54:12 -07001881void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
1882 const SkPaint* paint, SrcRectConstraint constraint) {
1883 if (bitmap.drawsNothing() || dst.isEmpty()) {
1884 return;
1885 }
reed562fe472015-07-28 07:35:14 -07001886 this->onDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08001887}
1888
reed84984ef2015-07-17 07:09:43 -07001889void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
1890 const SkPaint* paint, SrcRectConstraint constraint) {
1891 SkRect src = SkRect::Make(isrc);
1892 this->drawBitmapRect(bitmap, &src, dst, paint, constraint);
1893}
1894
reed41af9662015-01-05 07:49:08 -08001895void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
1896 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001897 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001898 return;
1899 }
reed4c21dc52015-06-25 12:32:03 -07001900 if (!SkNinePatchIter::Valid(bitmap.width(), bitmap.height(), center)) {
reeda5517e22015-07-14 10:54:12 -07001901 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001902 }
reed41af9662015-01-05 07:49:08 -08001903 this->onDrawBitmapNine(bitmap, center, dst, paint);
1904}
1905
1906void SkCanvas::drawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001907 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001908 return;
1909 }
reed41af9662015-01-05 07:49:08 -08001910 this->onDrawSprite(bitmap, left, top, paint);
1911}
1912
reed71c3c762015-06-24 10:29:17 -07001913void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
1914 const SkColor colors[], int count, SkXfermode::Mode mode,
1915 const SkRect* cull, const SkPaint* paint) {
1916 if (count <= 0) {
1917 return;
1918 }
1919 SkASSERT(atlas);
1920 SkASSERT(xform);
1921 SkASSERT(tex);
1922 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
1923}
1924
reed@android.com8a1c16f2008-12-17 15:59:43 +00001925//////////////////////////////////////////////////////////////////////////////
1926// These are the virtual drawing methods
1927//////////////////////////////////////////////////////////////////////////////
1928
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001929void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07001930 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001931 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1932 }
1933}
1934
reed41af9662015-01-05 07:49:08 -08001935void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001936 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001937 this->internalDrawPaint(paint);
1938}
1939
1940void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reedc83a2972015-07-16 07:40:45 -07001941 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, NULL, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001942
1943 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001944 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001945 }
1946
reed@google.com4e2b3d32011-04-07 14:18:59 +00001947 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001948}
1949
reed41af9662015-01-05 07:49:08 -08001950void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
1951 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001952 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001953 if ((long)count <= 0) {
1954 return;
1955 }
1956
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001957 SkRect r, storage;
1958 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001959 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001960 // special-case 2 points (common for drawing a single line)
1961 if (2 == count) {
1962 r.set(pts[0], pts[1]);
1963 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001964 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001965 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001966 bounds = &paint.computeFastStrokeBounds(r, &storage);
1967 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001968 return;
1969 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001970 }
reed@google.coma584aed2012-05-16 14:06:02 +00001971
reed@android.com8a1c16f2008-12-17 15:59:43 +00001972 SkASSERT(pts != NULL);
1973
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001974 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001975
reed@android.com8a1c16f2008-12-17 15:59:43 +00001976 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001977 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001978 }
reed@google.com4b226022011-01-11 18:32:13 +00001979
reed@google.com4e2b3d32011-04-07 14:18:59 +00001980 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001981}
1982
reed41af9662015-01-05 07:49:08 -08001983void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001984 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001985 SkRect storage;
1986 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001987 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08001988 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
1989 // To prevent accidental rejecting at this stage, we have to sort it before we check.
1990 SkRect tmp(r);
1991 tmp.sort();
1992
1993 bounds = &paint.computeFastBounds(tmp, &storage);
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001994 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001995 return;
1996 }
1997 }
reed@google.com4b226022011-01-11 18:32:13 +00001998
reedc83a2972015-07-16 07:40:45 -07001999 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002000
2001 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002002 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002003 }
2004
reed@google.com4e2b3d32011-04-07 14:18:59 +00002005 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002006}
2007
reed41af9662015-01-05 07:49:08 -08002008void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002009 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002010 SkRect storage;
2011 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002012 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002013 bounds = &paint.computeFastBounds(oval, &storage);
2014 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002015 return;
2016 }
2017 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002018
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002019 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002020
2021 while (iter.next()) {
2022 iter.fDevice->drawOval(iter, oval, looper.paint());
2023 }
2024
2025 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002026}
2027
reed41af9662015-01-05 07:49:08 -08002028void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002029 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002030 SkRect storage;
2031 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002032 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002033 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
2034 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002035 return;
2036 }
2037 }
2038
2039 if (rrect.isRect()) {
2040 // call the non-virtual version
2041 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002042 return;
2043 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002044 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002045 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2046 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002047 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002048
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002049 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002050
2051 while (iter.next()) {
2052 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2053 }
2054
2055 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002056}
2057
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002058void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2059 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002060 SkRect storage;
2061 const SkRect* bounds = NULL;
2062 if (paint.canComputeFastBounds()) {
2063 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
2064 if (this->quickReject(*bounds)) {
2065 return;
2066 }
2067 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002068
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002069 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002070
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002071 while (iter.next()) {
2072 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2073 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002074
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002075 LOOPER_END
2076}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002077
reed41af9662015-01-05 07:49:08 -08002078void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002079 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002080 if (!path.isFinite()) {
2081 return;
2082 }
2083
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002084 SkRect storage;
2085 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002086 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002087 const SkRect& pathBounds = path.getBounds();
2088 bounds = &paint.computeFastBounds(pathBounds, &storage);
2089 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002090 return;
2091 }
2092 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002093
2094 const SkRect& r = path.getBounds();
2095 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002096 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002097 this->internalDrawPaint(paint);
2098 }
2099 return;
2100 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002101
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002102 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002103
2104 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002105 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002106 }
2107
reed@google.com4e2b3d32011-04-07 14:18:59 +00002108 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002109}
2110
reeda85d4d02015-05-06 12:56:48 -07002111void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002112 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002113 SkRect bounds = SkRect::MakeXYWH(x, y,
2114 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
2115 if (NULL == paint || paint->canComputeFastBounds()) {
2116 if (paint) {
2117 paint->computeFastBounds(bounds, &bounds);
2118 }
2119 if (this->quickReject(bounds)) {
2120 return;
2121 }
2122 }
2123
2124 SkLazyPaint lazy;
2125 if (NULL == paint) {
2126 paint = lazy.init();
2127 }
2128
2129 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &bounds)
2130
2131 while (iter.next()) {
2132 iter.fDevice->drawImage(iter, image, x, y, looper.paint());
2133 }
2134
2135 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002136}
2137
reed41af9662015-01-05 07:49:08 -08002138void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002139 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002140 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
reeda85d4d02015-05-06 12:56:48 -07002141 SkRect storage;
2142 const SkRect* bounds = &dst;
2143 if (NULL == paint || paint->canComputeFastBounds()) {
2144 if (paint) {
2145 bounds = &paint->computeFastBounds(dst, &storage);
2146 }
2147 if (this->quickReject(*bounds)) {
2148 return;
2149 }
2150 }
2151 SkLazyPaint lazy;
2152 if (NULL == paint) {
2153 paint = lazy.init();
2154 }
2155
reedc83a2972015-07-16 07:40:45 -07002156 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, bounds,
2157 image->isOpaque())
reeda85d4d02015-05-06 12:56:48 -07002158
2159 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002160 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002161 }
2162
2163 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002164}
2165
reed41af9662015-01-05 07:49:08 -08002166void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002167 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002168 SkDEBUGCODE(bitmap.validate();)
2169
reed@google.com3d608122011-11-21 15:16:16 +00002170 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002171 SkRect bounds = {
2172 x, y,
2173 x + SkIntToScalar(bitmap.width()),
2174 y + SkIntToScalar(bitmap.height())
2175 };
2176 if (paint) {
2177 (void)paint->computeFastBounds(bounds, &bounds);
2178 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002179 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002180 return;
2181 }
2182 }
reed@google.com4b226022011-01-11 18:32:13 +00002183
reed@android.com8a1c16f2008-12-17 15:59:43 +00002184 SkMatrix matrix;
2185 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00002186 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002187}
2188
reed@google.com9987ec32011-09-07 11:57:52 +00002189// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002190void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002191 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002192 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002193 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002194 return;
2195 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002196
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002197 SkRect storage;
2198 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00002199 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002200 if (paint) {
2201 bounds = &paint->computeFastBounds(dst, &storage);
2202 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002203 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002204 return;
2205 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002206 }
reed@google.com3d608122011-11-21 15:16:16 +00002207
reed@google.com33535f32012-09-25 15:37:50 +00002208 SkLazyPaint lazy;
2209 if (NULL == paint) {
2210 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002211 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002212
reedc83a2972015-07-16 07:40:45 -07002213 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, bounds,
2214 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002215
reed@google.com33535f32012-09-25 15:37:50 +00002216 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002217 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002218 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002219
reed@google.com33535f32012-09-25 15:37:50 +00002220 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002221}
2222
reed41af9662015-01-05 07:49:08 -08002223void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002224 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002225 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002226 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002227 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002228}
2229
reed4c21dc52015-06-25 12:32:03 -07002230void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2231 const SkPaint* paint) {
2232 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
2233
2234 SkRect storage;
2235 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00002236 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00002237 if (paint) {
2238 bounds = &paint->computeFastBounds(dst, &storage);
2239 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002240 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002241 return;
2242 }
2243 }
reed4c21dc52015-06-25 12:32:03 -07002244
2245 SkLazyPaint lazy;
2246 if (NULL == paint) {
2247 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002248 }
reed4c21dc52015-06-25 12:32:03 -07002249
2250 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
2251
2252 while (iter.next()) {
2253 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002254 }
reed4c21dc52015-06-25 12:32:03 -07002255
2256 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002257}
2258
reed41af9662015-01-05 07:49:08 -08002259void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2260 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002261 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002262 SkDEBUGCODE(bitmap.validate();)
2263
reed4c21dc52015-06-25 12:32:03 -07002264 SkRect storage;
2265 const SkRect* bounds = &dst;
2266 if (NULL == paint || paint->canComputeFastBounds()) {
2267 if (paint) {
2268 bounds = &paint->computeFastBounds(dst, &storage);
2269 }
2270 if (this->quickReject(*bounds)) {
2271 return;
2272 }
2273 }
2274
2275 SkLazyPaint lazy;
2276 if (NULL == paint) {
2277 paint = lazy.init();
2278 }
2279
2280 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
2281
2282 while (iter.next()) {
2283 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2284 }
2285
2286 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002287}
2288
reed@google.comf67e4cf2011-03-15 20:56:58 +00002289class SkDeviceFilteredPaint {
2290public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002291 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002292 uint32_t filteredFlags = device->filterTextFlags(paint);
2293 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002294 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002295 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002296 fPaint = newPaint;
2297 } else {
2298 fPaint = &paint;
2299 }
2300 }
2301
reed@google.comf67e4cf2011-03-15 20:56:58 +00002302 const SkPaint& paint() const { return *fPaint; }
2303
2304private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002305 const SkPaint* fPaint;
2306 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002307};
2308
bungeman@google.com52c748b2011-08-22 21:30:43 +00002309void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2310 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002311 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002312 draw.fDevice->drawRect(draw, r, paint);
2313 } else {
2314 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002315 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002316 draw.fDevice->drawRect(draw, r, p);
2317 }
2318}
2319
2320void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2321 const char text[], size_t byteLength,
2322 SkScalar x, SkScalar y) {
2323 SkASSERT(byteLength == 0 || text != NULL);
2324
2325 // nothing to draw
2326 if (text == NULL || byteLength == 0 ||
2327 draw.fClip->isEmpty() ||
2328 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2329 return;
2330 }
2331
2332 SkScalar width = 0;
2333 SkPoint start;
2334
2335 start.set(0, 0); // to avoid warning
2336 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2337 SkPaint::kStrikeThruText_Flag)) {
2338 width = paint.measureText(text, byteLength);
2339
2340 SkScalar offsetX = 0;
2341 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2342 offsetX = SkScalarHalf(width);
2343 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2344 offsetX = width;
2345 }
2346 start.set(x - offsetX, y);
2347 }
2348
2349 if (0 == width) {
2350 return;
2351 }
2352
2353 uint32_t flags = paint.getFlags();
2354
2355 if (flags & (SkPaint::kUnderlineText_Flag |
2356 SkPaint::kStrikeThruText_Flag)) {
2357 SkScalar textSize = paint.getTextSize();
2358 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2359 SkRect r;
2360
2361 r.fLeft = start.fX;
2362 r.fRight = start.fX + width;
2363
2364 if (flags & SkPaint::kUnderlineText_Flag) {
2365 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2366 start.fY);
2367 r.fTop = offset;
2368 r.fBottom = offset + height;
2369 DrawRect(draw, paint, r, textSize);
2370 }
2371 if (flags & SkPaint::kStrikeThruText_Flag) {
2372 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2373 start.fY);
2374 r.fTop = offset;
2375 r.fBottom = offset + height;
2376 DrawRect(draw, paint, r, textSize);
2377 }
2378 }
2379}
2380
reed@google.come0d9ce82014-04-23 04:00:17 +00002381void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2382 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002383 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002384
2385 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002386 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002387 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002388 DrawTextDecorations(iter, dfp.paint(),
2389 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002390 }
2391
reed@google.com4e2b3d32011-04-07 14:18:59 +00002392 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002393}
2394
reed@google.come0d9ce82014-04-23 04:00:17 +00002395void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2396 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002397 SkPoint textOffset = SkPoint::Make(0, 0);
2398
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002399 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002400
reed@android.com8a1c16f2008-12-17 15:59:43 +00002401 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002402 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002403 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002404 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002405 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002406
reed@google.com4e2b3d32011-04-07 14:18:59 +00002407 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002408}
2409
reed@google.come0d9ce82014-04-23 04:00:17 +00002410void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2411 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002412
2413 SkPoint textOffset = SkPoint::Make(0, constY);
2414
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002415 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002416
reed@android.com8a1c16f2008-12-17 15:59:43 +00002417 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002418 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002419 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002420 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002421 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002422
reed@google.com4e2b3d32011-04-07 14:18:59 +00002423 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002424}
2425
reed@google.come0d9ce82014-04-23 04:00:17 +00002426void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2427 const SkMatrix* matrix, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002428 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002429
reed@android.com8a1c16f2008-12-17 15:59:43 +00002430 while (iter.next()) {
2431 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002432 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002433 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002434
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002435 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002436}
2437
fmalita00d5c2c2014-08-21 08:53:26 -07002438void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2439 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002440
fmalita85d5eb92015-03-04 11:20:12 -08002441 SkRect storage;
2442 const SkRect* bounds = NULL;
fmalita19653d12014-10-16 11:53:30 -07002443 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002444 storage = blob->bounds().makeOffset(x, y);
2445 bounds = &paint.computeFastBounds(storage, &storage);
fmalita7ba7aa72014-08-29 09:46:36 -07002446
fmalita85d5eb92015-03-04 11:20:12 -08002447 if (this->quickReject(*bounds)) {
fmalita7ba7aa72014-08-29 09:46:36 -07002448 return;
2449 }
2450 }
2451
fmalita024f9962015-03-03 19:08:17 -08002452 // We cannot filter in the looper as we normally do, because the paint is
2453 // incomplete at this point (text-related attributes are embedded within blob run paints).
2454 SkDrawFilter* drawFilter = fMCRec->fFilter;
2455 fMCRec->fFilter = NULL;
2456
fmalita85d5eb92015-03-04 11:20:12 -08002457 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002458
fmalitaaa1b9122014-08-28 14:32:24 -07002459 while (iter.next()) {
2460 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002461 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002462 }
2463
fmalitaaa1b9122014-08-28 14:32:24 -07002464 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002465
2466 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002467}
2468
reed@google.come0d9ce82014-04-23 04:00:17 +00002469// These will become non-virtual, so they always call the (virtual) onDraw... method
2470void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2471 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002472 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002473 this->onDrawText(text, byteLength, x, y, paint);
2474}
2475void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2476 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002477 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002478 this->onDrawPosText(text, byteLength, pos, paint);
2479}
2480void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2481 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002482 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002483 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2484}
2485void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2486 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002487 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002488 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2489}
fmalita00d5c2c2014-08-21 08:53:26 -07002490void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2491 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002492 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
bsalomon49f085d2014-09-05 13:34:00 -07002493 if (blob) {
fmalita00d5c2c2014-08-21 08:53:26 -07002494 this->onDrawTextBlob(blob, x, y, paint);
2495 }
2496}
reed@google.come0d9ce82014-04-23 04:00:17 +00002497
reed41af9662015-01-05 07:49:08 -08002498void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2499 const SkPoint verts[], const SkPoint texs[],
2500 const SkColor colors[], SkXfermode* xmode,
2501 const uint16_t indices[], int indexCount,
2502 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002503 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002504 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002505
reed@android.com8a1c16f2008-12-17 15:59:43 +00002506 while (iter.next()) {
2507 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002508 colors, xmode, indices, indexCount,
2509 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002510 }
reed@google.com4b226022011-01-11 18:32:13 +00002511
reed@google.com4e2b3d32011-04-07 14:18:59 +00002512 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002513}
2514
dandovb3c9d1c2014-08-12 08:34:29 -07002515void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2516 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002517 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
dandovb3c9d1c2014-08-12 08:34:29 -07002518 if (NULL == cubics) {
2519 return;
2520 }
mtklein6cfa73a2014-08-13 13:33:49 -07002521
dandovecfff212014-08-04 10:02:00 -07002522 // Since a patch is always within the convex hull of the control points, we discard it when its
2523 // bounding rectangle is completely outside the current clip.
2524 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002525 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002526 if (this->quickReject(bounds)) {
2527 return;
2528 }
mtklein6cfa73a2014-08-13 13:33:49 -07002529
dandovb3c9d1c2014-08-12 08:34:29 -07002530 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2531}
2532
2533void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2534 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2535
dandovecfff212014-08-04 10:02:00 -07002536 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
mtklein6cfa73a2014-08-13 13:33:49 -07002537
dandovecfff212014-08-04 10:02:00 -07002538 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002539 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002540 }
mtklein6cfa73a2014-08-13 13:33:49 -07002541
dandovecfff212014-08-04 10:02:00 -07002542 LOOPER_END
2543}
2544
reeda8db7282015-07-07 10:22:31 -07002545void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
2546 if (dr) {
2547 if (x || y) {
2548 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2549 this->onDrawDrawable(dr, &matrix);
2550 } else {
2551 this->onDrawDrawable(dr, NULL);
2552 }
reed6a070dc2014-11-11 19:36:09 -08002553 }
2554}
2555
reeda8db7282015-07-07 10:22:31 -07002556void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2557 if (dr) {
2558 if (matrix && matrix->isIdentity()) {
2559 matrix = NULL;
2560 }
2561 this->onDrawDrawable(dr, matrix);
2562 }
2563}
2564
2565void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2566 SkRect bounds = dr->getBounds();
2567 if (matrix) {
2568 matrix->mapRect(&bounds);
2569 }
2570 if (this->quickReject(bounds)) {
2571 return;
2572 }
2573 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002574}
2575
reed71c3c762015-06-24 10:29:17 -07002576void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2577 const SkColor colors[], int count, SkXfermode::Mode mode,
2578 const SkRect* cull, const SkPaint* paint) {
2579 if (cull && this->quickReject(*cull)) {
2580 return;
2581 }
2582
2583 SkPaint pnt;
2584 if (paint) {
2585 pnt = *paint;
2586 }
2587
2588 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, NULL)
2589 while (iter.next()) {
2590 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, mode, pnt);
2591 }
2592 LOOPER_END
2593}
2594
reed@android.com8a1c16f2008-12-17 15:59:43 +00002595//////////////////////////////////////////////////////////////////////////////
2596// These methods are NOT virtual, and therefore must call back into virtual
2597// methods, rather than actually drawing themselves.
2598//////////////////////////////////////////////////////////////////////////////
2599
2600void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002601 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002602 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002603 SkPaint paint;
2604
2605 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002606 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002607 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002608 }
2609 this->drawPaint(paint);
2610}
2611
reed@android.com845fdac2009-06-23 03:01:32 +00002612void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002613 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002614 SkPaint paint;
2615
2616 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002617 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002618 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002619 }
2620 this->drawPaint(paint);
2621}
2622
2623void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002624 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002625 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002626
reed@android.com8a1c16f2008-12-17 15:59:43 +00002627 pt.set(x, y);
2628 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2629}
2630
2631void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002632 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002633 SkPoint pt;
2634 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002635
reed@android.com8a1c16f2008-12-17 15:59:43 +00002636 pt.set(x, y);
2637 paint.setColor(color);
2638 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2639}
2640
2641void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2642 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002643 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002644 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002645
reed@android.com8a1c16f2008-12-17 15:59:43 +00002646 pts[0].set(x0, y0);
2647 pts[1].set(x1, y1);
2648 this->drawPoints(kLines_PointMode, 2, pts, paint);
2649}
2650
2651void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2652 SkScalar right, SkScalar bottom,
2653 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002654 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002655 SkRect r;
2656
2657 r.set(left, top, right, bottom);
2658 this->drawRect(r, paint);
2659}
2660
2661void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2662 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002663 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002664 if (radius < 0) {
2665 radius = 0;
2666 }
2667
2668 SkRect r;
2669 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002670 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002671}
2672
2673void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2674 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002675 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002676 if (rx > 0 && ry > 0) {
2677 if (paint.canComputeFastBounds()) {
2678 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002679 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002680 return;
2681 }
2682 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002683 SkRRect rrect;
2684 rrect.setRectXY(r, rx, ry);
2685 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002686 } else {
2687 this->drawRect(r, paint);
2688 }
2689}
2690
reed@android.com8a1c16f2008-12-17 15:59:43 +00002691void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2692 SkScalar sweepAngle, bool useCenter,
2693 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002694 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002695 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2696 this->drawOval(oval, paint);
2697 } else {
2698 SkPath path;
2699 if (useCenter) {
2700 path.moveTo(oval.centerX(), oval.centerY());
2701 }
2702 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2703 if (useCenter) {
2704 path.close();
2705 }
2706 this->drawPath(path, paint);
2707 }
2708}
2709
2710void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2711 const SkPath& path, SkScalar hOffset,
2712 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002713 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002714 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002715
reed@android.com8a1c16f2008-12-17 15:59:43 +00002716 matrix.setTranslate(hOffset, vOffset);
2717 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2718}
2719
reed@android.comf76bacf2009-05-13 14:00:33 +00002720///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07002721
2722/**
2723 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2724 * against the playback cost of recursing into the subpicture to get at its actual ops.
2725 *
2726 * For now we pick a conservatively small value, though measurement (and other heuristics like
2727 * the type of ops contained) may justify changing this value.
2728 */
2729#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07002730
reedd5fa1a42014-08-09 11:08:05 -07002731void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reed1c2c4412015-04-30 13:09:24 -07002732 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
bsalomon49f085d2014-09-05 13:34:00 -07002733 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002734 if (matrix && matrix->isIdentity()) {
2735 matrix = NULL;
2736 }
reed1c2c4412015-04-30 13:09:24 -07002737 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2738 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2739 picture->playback(this);
2740 } else {
2741 this->onDrawPicture(picture, matrix, paint);
2742 }
reedd5fa1a42014-08-09 11:08:05 -07002743 }
2744}
robertphillips9b14f262014-06-04 05:40:44 -07002745
reedd5fa1a42014-08-09 11:08:05 -07002746void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2747 const SkPaint* paint) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002748 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002749 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002750 // Canvas has to first give the device the opportunity to render
2751 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002752 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002753 return; // the device has rendered the entire picture
2754 }
2755 }
2756
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002757 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07002758 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002759}
2760
reed@android.com8a1c16f2008-12-17 15:59:43 +00002761///////////////////////////////////////////////////////////////////////////////
2762///////////////////////////////////////////////////////////////////////////////
2763
2764SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002765 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002766
2767 SkASSERT(canvas);
2768
2769 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2770 fDone = !fImpl->next();
2771}
2772
2773SkCanvas::LayerIter::~LayerIter() {
2774 fImpl->~SkDrawIter();
2775}
2776
2777void SkCanvas::LayerIter::next() {
2778 fDone = !fImpl->next();
2779}
2780
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002781SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002782 return fImpl->getDevice();
2783}
2784
2785const SkMatrix& SkCanvas::LayerIter::matrix() const {
2786 return fImpl->getMatrix();
2787}
2788
2789const SkPaint& SkCanvas::LayerIter::paint() const {
2790 const SkPaint* paint = fImpl->getPaint();
2791 if (NULL == paint) {
2792 paint = &fDefaultPaint;
2793 }
2794 return *paint;
2795}
2796
2797const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2798int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2799int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002800
2801///////////////////////////////////////////////////////////////////////////////
2802
fmalitac3b589a2014-06-05 12:40:07 -07002803SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002804
2805///////////////////////////////////////////////////////////////////////////////
2806
2807static bool supported_for_raster_canvas(const SkImageInfo& info) {
2808 switch (info.alphaType()) {
2809 case kPremul_SkAlphaType:
2810 case kOpaque_SkAlphaType:
2811 break;
2812 default:
2813 return false;
2814 }
2815
2816 switch (info.colorType()) {
2817 case kAlpha_8_SkColorType:
2818 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002819 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002820 break;
2821 default:
2822 return false;
2823 }
2824
2825 return true;
2826}
2827
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002828SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2829 if (!supported_for_raster_canvas(info)) {
2830 return NULL;
2831 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002832
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002833 SkBitmap bitmap;
2834 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2835 return NULL;
2836 }
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002837 return SkNEW_ARGS(SkCanvas, (bitmap));
2838}
reedd5fa1a42014-08-09 11:08:05 -07002839
2840///////////////////////////////////////////////////////////////////////////////
2841
2842SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002843 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07002844 : fCanvas(canvas)
2845 , fSaveCount(canvas->getSaveCount())
2846{
bsalomon49f085d2014-09-05 13:34:00 -07002847 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002848 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07002849 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002850 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07002851 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002852 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07002853 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002854 canvas->save();
2855 }
mtklein6cfa73a2014-08-13 13:33:49 -07002856
bsalomon49f085d2014-09-05 13:34:00 -07002857 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002858 canvas->concat(*matrix);
2859 }
2860}
2861
2862SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
2863 fCanvas->restoreToCount(fSaveCount);
2864}