blob: 294562ad92356d78e716ee44cd0c32aa66ea0956 [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00003 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00004 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00006 */
7
8#include "SkCanvas.h"
reed6a070dc2014-11-11 19:36:09 -08009#include "SkCanvasDrawable.h"
reedd5fa1a42014-08-09 11:08:05 -070010#include "SkCanvasPriv.h"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000011#include "SkBitmapDevice.h"
senorblanco@chromium.org9c397442012-09-27 21:57:45 +000012#include "SkDeviceImageFilterProxy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013#include "SkDraw.h"
14#include "SkDrawFilter.h"
15#include "SkDrawLooper.h"
piotaixrb5fae932014-09-24 13:03:30 -070016#include "SkImage.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000017#include "SkMetaData.h"
caryclark@google.com45a75fb2013-04-25 13:34:40 +000018#include "SkPathOps.h"
dandovb3c9d1c2014-08-12 08:34:29 -070019#include "SkPatchUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000020#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000021#include "SkRasterClip.h"
reed96472de2014-12-10 09:53:42 -080022#include "SkReadPixelsRec.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000023#include "SkRRect.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000024#include "SkSmallAllocator.h"
reed@google.com97af1a62012-08-28 12:19:02 +000025#include "SkSurface_Base.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000026#include "SkTemplates.h"
fmalita7ba7aa72014-08-29 09:46:36 -070027#include "SkTextBlob.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000028#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000029#include "SkTLazy.h"
danakj8f757f52014-11-04 11:48:43 -080030#include "SkTraceEvent.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000031#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000032
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000033#if SK_SUPPORT_GPU
34#include "GrRenderTarget.h"
35#endif
36
reed@google.comda17f752012-08-16 18:27:05 +000037// experimental for faster tiled drawing...
38//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000039
reed@android.com8a1c16f2008-12-17 15:59:43 +000040//#define SK_TRACE_SAVERESTORE
41
42#ifdef SK_TRACE_SAVERESTORE
43 static int gLayerCounter;
44 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
45 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
46
47 static int gRecCounter;
48 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
49 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
50
51 static int gCanvasCounter;
52 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
53 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
54#else
55 #define inc_layer()
56 #define dec_layer()
57 #define inc_rec()
58 #define dec_rec()
59 #define inc_canvas()
60 #define dec_canvas()
61#endif
62
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000063typedef SkTLazy<SkPaint> SkLazyPaint;
64
reed@google.com97af1a62012-08-28 12:19:02 +000065void SkCanvas::predrawNotify() {
66 if (fSurfaceBase) {
commit-bot@chromium.orgc4c98702013-04-22 14:28:01 +000067 fSurfaceBase->aboutToDraw(SkSurface::kRetain_ContentChangeMode);
reed@google.com97af1a62012-08-28 12:19:02 +000068 }
69}
70
reed@android.com8a1c16f2008-12-17 15:59:43 +000071///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000072
reed4a8126e2014-09-22 07:29:03 -070073static uint32_t filter_paint_flags(const SkSurfaceProps& props, uint32_t flags) {
74 const uint32_t propFlags = props.flags();
75 if (propFlags & SkSurfaceProps::kDisallowDither_Flag) {
76 flags &= ~SkPaint::kDither_Flag;
77 }
78 if (propFlags & SkSurfaceProps::kDisallowAntiAlias_Flag) {
79 flags &= ~SkPaint::kAntiAlias_Flag;
80 }
81 return flags;
82}
83
84///////////////////////////////////////////////////////////////////////////////
85
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000086/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +000087 The clip/matrix/proc are fields that reflect the top of the save/restore
88 stack. Whenever the canvas changes, it marks a dirty flag, and then before
89 these are used (assuming we're not on a layer) we rebuild these cache
90 values: they reflect the top of the save stack, but translated and clipped
91 by the device's XY offset and bitmap-bounds.
92*/
93struct DeviceCM {
94 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000095 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +000096 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +000097 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +000098 SkPaint* fPaint; // may be null (in the future)
reed@android.com8a1c16f2008-12-17 15:59:43 +000099
reedd9544982014-09-09 18:46:22 -0700100 DeviceCM(SkBaseDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas,
101 bool conservativeRasterClip)
102 : fNext(NULL)
103 , fClip(conservativeRasterClip)
104 {
105 if (NULL != device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000106 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000107 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000108 }
reed@google.com4b226022011-01-11 18:32:13 +0000109 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000110 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000111 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000112
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000113 ~DeviceCM() {
bsalomon49f085d2014-09-05 13:34:00 -0700114 if (fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000115 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000116 fDevice->unref();
117 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000118 SkDELETE(fPaint);
119 }
reed@google.com4b226022011-01-11 18:32:13 +0000120
reed@google.com045e62d2011-10-24 12:19:46 +0000121 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
122 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000123 int x = fDevice->getOrigin().x();
124 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125 int width = fDevice->width();
126 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000127
reed@android.com8a1c16f2008-12-17 15:59:43 +0000128 if ((x | y) == 0) {
129 fMatrix = &totalMatrix;
130 fClip = totalClip;
131 } else {
132 fMatrixStorage = totalMatrix;
133 fMatrixStorage.postTranslate(SkIntToScalar(-x),
134 SkIntToScalar(-y));
135 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000136
reed@android.com8a1c16f2008-12-17 15:59:43 +0000137 totalClip.translate(-x, -y, &fClip);
138 }
139
reed@google.com045e62d2011-10-24 12:19:46 +0000140 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000141
142 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000143
reed@android.com8a1c16f2008-12-17 15:59:43 +0000144 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000145 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000146 SkRegion::kDifference_Op);
147 }
reed@google.com4b226022011-01-11 18:32:13 +0000148
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000149 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
150
reed@android.com8a1c16f2008-12-17 15:59:43 +0000151#ifdef SK_DEBUG
152 if (!fClip.isEmpty()) {
153 SkIRect deviceR;
154 deviceR.set(0, 0, width, height);
155 SkASSERT(deviceR.contains(fClip.getBounds()));
156 }
157#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000158 }
159
reed@android.com8a1c16f2008-12-17 15:59:43 +0000160private:
bsalomon@google.com0e354aa2012-10-08 20:44:25 +0000161 SkMatrix fMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000162};
163
164/* This is the record we keep for each save/restore level in the stack.
165 Since a level optionally copies the matrix and/or stack, we have pointers
166 for these fields. If the value is copied for this level, the copy is
167 stored in the ...Storage field, and the pointer points to that. If the
168 value is not copied for this level, we ignore ...Storage, and just point
169 at the corresponding value in the previous level in the stack.
170*/
171class SkCanvas::MCRec {
172public:
reed1f836ee2014-07-07 07:49:34 -0700173 SkDrawFilter* fFilter; // the current filter (or null)
reedd9544982014-09-09 18:46:22 -0700174 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000175 /* If there are any layers in the stack, this points to the top-most
176 one that is at or below this level in the stack (so we know what
177 bitmap/device to draw into from this level. This value is NOT
178 reference counted, since the real owner is either our fLayer field,
179 or a previous one in a lower level.)
180 */
reed2ff1fce2014-12-11 07:07:37 -0800181 DeviceCM* fTopLayer;
182 SkRasterClip fRasterClip;
183 SkMatrix fMatrix;
184 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000185
reedd9544982014-09-09 18:46:22 -0700186 MCRec(bool conservativeRasterClip) : fRasterClip(conservativeRasterClip) {
reedd9544982014-09-09 18:46:22 -0700187 fFilter = NULL;
188 fLayer = NULL;
189 fTopLayer = NULL;
reed2ff1fce2014-12-11 07:07:37 -0800190 fMatrix.reset();
191 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700192
reedd9544982014-09-09 18:46:22 -0700193 // don't bother initializing fNext
194 inc_rec();
195 }
reed2ff1fce2014-12-11 07:07:37 -0800196 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
reedd9544982014-09-09 18:46:22 -0700197 fFilter = SkSafeRef(prev.fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000198 fLayer = NULL;
reedd9544982014-09-09 18:46:22 -0700199 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800200 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700201
reed@android.com8a1c16f2008-12-17 15:59:43 +0000202 // don't bother initializing fNext
203 inc_rec();
204 }
205 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000206 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000207 SkDELETE(fLayer);
208 dec_rec();
209 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000210};
211
212class SkDrawIter : public SkDraw {
213public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000214 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000215 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000216 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217 canvas->updateDeviceCMCache();
218
reed@google.com90c07ea2012-04-13 13:50:27 +0000219 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000220 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000221 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000222 }
reed@google.com4b226022011-01-11 18:32:13 +0000223
reed@android.com8a1c16f2008-12-17 15:59:43 +0000224 bool next() {
225 // skip over recs with empty clips
226 if (fSkipEmptyClips) {
227 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
228 fCurrLayer = fCurrLayer->fNext;
229 }
230 }
231
reed@google.comf68c5e22012-02-24 16:38:58 +0000232 const DeviceCM* rec = fCurrLayer;
233 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000234
235 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000236 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
237 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000238 fDevice = rec->fDevice;
239 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000241 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242
243 fCurrLayer = rec->fNext;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000245
reed@android.com8a1c16f2008-12-17 15:59:43 +0000246 return true;
247 }
248 return false;
249 }
reed@google.com4b226022011-01-11 18:32:13 +0000250
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000251 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000252 int getX() const { return fDevice->getOrigin().x(); }
253 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000254 const SkMatrix& getMatrix() const { return *fMatrix; }
255 const SkRegion& getClip() const { return *fClip; }
256 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000257
reed@android.com8a1c16f2008-12-17 15:59:43 +0000258private:
259 SkCanvas* fCanvas;
260 const DeviceCM* fCurrLayer;
261 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262 SkBool8 fSkipEmptyClips;
263
264 typedef SkDraw INHERITED;
265};
266
267/////////////////////////////////////////////////////////////////////////////
268
269class AutoDrawLooper {
270public:
reed4a8126e2014-09-22 07:29:03 -0700271 AutoDrawLooper(SkCanvas* canvas, const SkSurfaceProps& props, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000272 bool skipLayerForImageFilter = false,
273 const SkRect* bounds = NULL) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000274 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000275 fFilter = canvas->getDrawFilter();
reed4a8126e2014-09-22 07:29:03 -0700276 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000277 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000278 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000279 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000280
reed@google.com8926b162012-03-23 15:36:36 +0000281 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
282 SkPaint tmp;
283 tmp.setImageFilter(fOrigPaint.getImageFilter());
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000284 (void)canvas->internalSaveLayer(bounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag,
285 true, SkCanvas::kFullLayer_SaveLayerStrategy);
reed@google.com8926b162012-03-23 15:36:36 +0000286 // we'll clear the imageFilter for the actual draws in next(), so
287 // it will only be applied during the restore().
288 fDoClearImageFilter = true;
289 }
290
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000291 if (SkDrawLooper* looper = paint.getLooper()) {
292 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
293 looper->contextSize());
294 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000295 fIsSimple = false;
296 } else {
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000297 fLooperContext = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000298 // can we be marked as simple?
299 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000300 }
piotaixrb5fae932014-09-24 13:03:30 -0700301
reed4a8126e2014-09-22 07:29:03 -0700302 uint32_t oldFlags = paint.getFlags();
303 fNewPaintFlags = filter_paint_flags(props, oldFlags);
304 if (fIsSimple && (fNewPaintFlags != oldFlags)) {
305 SkPaint* paint = fLazyPaint.set(fOrigPaint);
306 paint->setFlags(fNewPaintFlags);
307 fPaint = paint;
308 // if we're not simple, doNext() will take care of calling setFlags()
309 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000310 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000311
reed@android.com8a1c16f2008-12-17 15:59:43 +0000312 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000313 if (fDoClearImageFilter) {
314 fCanvas->internalRestore();
315 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000316 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000317 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000318
reed@google.com4e2b3d32011-04-07 14:18:59 +0000319 const SkPaint& paint() const {
320 SkASSERT(fPaint);
321 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000322 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000323
reed@google.com129ec222012-05-15 13:24:09 +0000324 bool next(SkDrawFilter::Type drawType) {
325 if (fDone) {
326 return false;
327 } else if (fIsSimple) {
328 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000329 return !fPaint->nothingToDraw();
330 } else {
331 return this->doNext(drawType);
332 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000333 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000334
reed@android.com8a1c16f2008-12-17 15:59:43 +0000335private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000336 SkLazyPaint fLazyPaint;
337 SkCanvas* fCanvas;
338 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000339 SkDrawFilter* fFilter;
340 const SkPaint* fPaint;
341 int fSaveCount;
reed4a8126e2014-09-22 07:29:03 -0700342 uint32_t fNewPaintFlags;
reed@google.com8926b162012-03-23 15:36:36 +0000343 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000344 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000345 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000346 SkDrawLooper::Context* fLooperContext;
347 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000348
349 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000350};
351
reed@google.com129ec222012-05-15 13:24:09 +0000352bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000353 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000354 SkASSERT(!fIsSimple);
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000355 SkASSERT(fLooperContext || fFilter || fDoClearImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000356
357 SkPaint* paint = fLazyPaint.set(fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700358 paint->setFlags(fNewPaintFlags);
reed@google.com129ec222012-05-15 13:24:09 +0000359
360 if (fDoClearImageFilter) {
361 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000362 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000363
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000364 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000365 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000366 return false;
367 }
368 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000369 if (!fFilter->filter(paint, drawType)) {
370 fDone = true;
371 return false;
372 }
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000373 if (NULL == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000374 // no looper means we only draw once
375 fDone = true;
376 }
377 }
378 fPaint = paint;
379
380 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000381 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000382 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000383 }
384
385 // call this after any possible paint modifiers
386 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000387 fPaint = NULL;
388 return false;
389 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000390 return true;
391}
392
reed@android.com8a1c16f2008-12-17 15:59:43 +0000393////////// macros to place around the internal draw calls //////////////////
394
reed@google.com8926b162012-03-23 15:36:36 +0000395#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000396 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700397 AutoDrawLooper looper(this, fProps, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000398 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000399 SkDrawIter iter(this);
400
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000401#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000402 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700403 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000404 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000405 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000406
reed@google.com4e2b3d32011-04-07 14:18:59 +0000407#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000408
409////////////////////////////////////////////////////////////////////////////
410
reedd9544982014-09-09 18:46:22 -0700411SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
412 fConservativeRasterClip = SkToBool(flags & kConservativeRasterClip_InitFlag);
reed@google.comc0784db2013-12-13 21:16:12 +0000413 fCachedLocalClipBounds.setEmpty();
414 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000415 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000416 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700417 fDeviceCMDirty = true;
reed2ff1fce2014-12-11 07:07:37 -0800418 fSaveCount = 1;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000419 fSaveLayerCount = 0;
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +0000420 fCullCount = 0;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000421 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000422
423 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700424 new (fMCRec) MCRec(fConservativeRasterClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000425
reedd9544982014-09-09 18:46:22 -0700426 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL, fConservativeRasterClip));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000427 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000428
reed@google.com97af1a62012-08-28 12:19:02 +0000429 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000430
reedf92c8662014-08-18 08:02:43 -0700431 if (device) {
reedb2db8982014-11-13 12:41:02 -0800432 device->initForRootLayer(fProps.pixelGeometry());
reed4a8126e2014-09-22 07:29:03 -0700433 if (device->forceConservativeRasterClip()) {
434 fConservativeRasterClip = true;
435 }
reedf92c8662014-08-18 08:02:43 -0700436 device->onAttachToCanvas(this);
437 fMCRec->fLayer->fDevice = SkRef(device);
reed78e27682014-11-19 08:04:34 -0800438 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
reedf92c8662014-08-18 08:02:43 -0700439 }
440 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000441}
442
reed@google.comcde92112011-07-06 20:00:52 +0000443SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000444 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700445 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000446{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000447 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000448
reedd9544982014-09-09 18:46:22 -0700449 this->init(NULL, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000450}
451
reedd9544982014-09-09 18:46:22 -0700452static SkBitmap make_nopixels(int width, int height) {
453 SkBitmap bitmap;
454 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
455 return bitmap;
456}
457
458class SkNoPixelsBitmapDevice : public SkBitmapDevice {
459public:
reed78e27682014-11-19 08:04:34 -0800460 SkNoPixelsBitmapDevice(const SkIRect& bounds)
461 : INHERITED(make_nopixels(bounds.width(), bounds.height()))
462 {
463 this->setOrigin(bounds.x(), bounds.y());
464 }
reedd9544982014-09-09 18:46:22 -0700465
466private:
piotaixrb5fae932014-09-24 13:03:30 -0700467
reedd9544982014-09-09 18:46:22 -0700468 typedef SkBitmapDevice INHERITED;
469};
470
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000471SkCanvas::SkCanvas(int width, int height)
472 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700473 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000474{
475 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700476
reed78e27682014-11-19 08:04:34 -0800477 this->init(SkNEW_ARGS(SkNoPixelsBitmapDevice,
478 (SkIRect::MakeWH(width, height))), kDefault_InitFlags)->unref();
reedd9544982014-09-09 18:46:22 -0700479}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000480
reed78e27682014-11-19 08:04:34 -0800481SkCanvas::SkCanvas(const SkIRect& bounds, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700482 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700483 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reedd9544982014-09-09 18:46:22 -0700484{
485 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700486
reed78e27682014-11-19 08:04:34 -0800487 this->init(SkNEW_ARGS(SkNoPixelsBitmapDevice, (bounds)), flags)->unref();
reedd9544982014-09-09 18:46:22 -0700488}
489
reed4a8126e2014-09-22 07:29:03 -0700490SkCanvas::SkCanvas(SkBaseDevice* device, const SkSurfaceProps* props, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700491 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700492 , fProps(SkSurfacePropsCopyOrDefault(props))
reedd9544982014-09-09 18:46:22 -0700493{
494 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700495
reedd9544982014-09-09 18:46:22 -0700496 this->init(device, flags);
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000497}
498
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000499SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000500 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700501 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000502{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000503 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700504
reedd9544982014-09-09 18:46:22 -0700505 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000506}
507
reed4a8126e2014-09-22 07:29:03 -0700508SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700509 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700510 , fProps(props)
reed3716fd02014-09-21 09:39:55 -0700511{
512 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700513
reed4a8126e2014-09-22 07:29:03 -0700514 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap)));
515 this->init(device, kDefault_InitFlags);
516}
reed29c857d2014-09-21 10:25:07 -0700517
reed4a8126e2014-09-22 07:29:03 -0700518SkCanvas::SkCanvas(const SkBitmap& bitmap)
519 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
520 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
521{
522 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700523
reed4a8126e2014-09-22 07:29:03 -0700524 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap)));
525 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000526}
527
528SkCanvas::~SkCanvas() {
529 // free up the contents of our deque
530 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000531 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000532
reed@android.com8a1c16f2008-12-17 15:59:43 +0000533 this->internalRestore(); // restore the last, since we're going away
534
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000535 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000536
reed@android.com8a1c16f2008-12-17 15:59:43 +0000537 dec_canvas();
538}
539
reed@android.com8a1c16f2008-12-17 15:59:43 +0000540SkDrawFilter* SkCanvas::getDrawFilter() const {
541 return fMCRec->fFilter;
542}
543
544SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
545 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
546 return filter;
547}
548
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000549SkMetaData& SkCanvas::getMetaData() {
550 // metadata users are rare, so we lazily allocate it. If that changes we
551 // can decide to just make it a field in the device (rather than a ptr)
552 if (NULL == fMetaData) {
553 fMetaData = new SkMetaData;
554 }
555 return *fMetaData;
556}
557
reed@android.com8a1c16f2008-12-17 15:59:43 +0000558///////////////////////////////////////////////////////////////////////////////
559
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000560void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000561 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000562 if (device) {
563 device->flush();
564 }
565}
566
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000567SkISize SkCanvas::getTopLayerSize() const {
568 SkBaseDevice* d = this->getTopDevice();
569 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
570}
571
572SkIPoint SkCanvas::getTopLayerOrigin() const {
573 SkBaseDevice* d = this->getTopDevice();
574 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
575}
576
577SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000578 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000579 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
580}
581
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000582SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000583 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000584 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000585 SkASSERT(rec && rec->fLayer);
586 return rec->fLayer->fDevice;
587}
588
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000589SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000590 if (updateMatrixClip) {
591 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
592 }
reed@google.com9266fed2011-03-30 00:18:03 +0000593 return fMCRec->fTopLayer->fDevice;
594}
595
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000596SkBaseDevice* SkCanvas::setRootDevice(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000597 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000598 SkDeque::F2BIter iter(fMCStack);
599 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000600 SkASSERT(rec && rec->fLayer);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000601 SkBaseDevice* rootDevice = rec->fLayer->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000602
603 if (rootDevice == device) {
604 return device;
605 }
reed@google.com4b226022011-01-11 18:32:13 +0000606
reed@android.com8a1c16f2008-12-17 15:59:43 +0000607 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000608 device->onAttachToCanvas(this);
reedb2db8982014-11-13 12:41:02 -0800609 device->initForRootLayer(fProps.pixelGeometry());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000610 }
611 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000612 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000613 }
614
615 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
616 rootDevice = device;
617
618 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000619
reed@android.com8a1c16f2008-12-17 15:59:43 +0000620 /* Now we update our initial region to have the bounds of the new device,
621 and then intersect all of the clips in our stack with these bounds,
622 to ensure that we can't draw outside of the device's bounds (and trash
623 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000624
reed@android.com8a1c16f2008-12-17 15:59:43 +0000625 NOTE: this is only a partial-fix, since if the new device is larger than
626 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000627 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000628 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
629 reconstruct the correct clips, so this approximation will have to do.
630 The caller really needs to restore() back to the base if they want to
631 accurately take advantage of the new device bounds.
632 */
633
reed@google.com42aea282012-03-28 16:19:15 +0000634 SkIRect bounds;
635 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000636 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000637 } else {
638 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000639 }
reed@google.com42aea282012-03-28 16:19:15 +0000640 // now jam our 1st clip to be bounds, and intersect the rest with that
reed1f836ee2014-07-07 07:49:34 -0700641 rec->fRasterClip.setRect(bounds);
reed@google.com42aea282012-03-28 16:19:15 +0000642 while ((rec = (MCRec*)iter.next()) != NULL) {
reed1f836ee2014-07-07 07:49:34 -0700643 (void)rec->fRasterClip.op(bounds, SkRegion::kIntersect_Op);
reed@google.com42aea282012-03-28 16:19:15 +0000644 }
645
reed@android.com8a1c16f2008-12-17 15:59:43 +0000646 return device;
647}
648
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000649bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
650 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
651 return false;
652 }
653
654 bool weAllocated = false;
655 if (NULL == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700656 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000657 return false;
658 }
659 weAllocated = true;
660 }
661
662 SkBitmap bm(*bitmap);
663 bm.lockPixels();
664 if (bm.getPixels() && this->readPixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y)) {
665 return true;
666 }
667
668 if (weAllocated) {
669 bitmap->setPixelRef(NULL);
670 }
671 return false;
672}
reed@google.com51df9e32010-12-23 19:29:18 +0000673
bsalomon@google.comc6980972011-11-02 19:57:21 +0000674bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000675 SkIRect r = srcRect;
676 const SkISize size = this->getBaseLayerSize();
677 if (!r.intersect(0, 0, size.width(), size.height())) {
678 bitmap->reset();
679 return false;
680 }
681
reed84825042014-09-02 12:50:45 -0700682 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000683 // bitmap will already be reset.
684 return false;
685 }
686 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
687 bitmap->reset();
688 return false;
689 }
690 return true;
691}
692
reed96472de2014-12-10 09:53:42 -0800693bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000694 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000695 if (!device) {
696 return false;
697 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000698 const SkISize size = this->getBaseLayerSize();
reed96472de2014-12-10 09:53:42 -0800699
700 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
701 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000702 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000703 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000704
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000705 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800706 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000707}
708
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000709bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
710 if (bitmap.getTexture()) {
711 return false;
712 }
713 SkBitmap bm(bitmap);
714 bm.lockPixels();
715 if (bm.getPixels()) {
716 return this->writePixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y);
717 }
718 return false;
719}
720
721bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
722 int x, int y) {
723 switch (origInfo.colorType()) {
724 case kUnknown_SkColorType:
725 case kIndex_8_SkColorType:
726 return false;
727 default:
728 break;
729 }
730 if (NULL == pixels || rowBytes < origInfo.minRowBytes()) {
731 return false;
732 }
733
734 const SkISize size = this->getBaseLayerSize();
735 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
736 if (!target.intersect(0, 0, size.width(), size.height())) {
737 return false;
738 }
739
740 SkBaseDevice* device = this->getDevice();
741 if (!device) {
742 return false;
743 }
744
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000745 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700746 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000747
748 // if x or y are negative, then we have to adjust pixels
749 if (x > 0) {
750 x = 0;
751 }
752 if (y > 0) {
753 y = 0;
754 }
755 // here x,y are either 0 or negative
756 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
757
reed4af35f32014-06-27 17:47:49 -0700758 // Tell our owning surface to bump its generation ID
759 this->predrawNotify();
760
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000761 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000762 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000763}
reed@google.com51df9e32010-12-23 19:29:18 +0000764
junov@google.com4370aed2012-01-18 16:21:08 +0000765SkCanvas* SkCanvas::canvasForDrawIter() {
766 return this;
767}
768
reed@android.com8a1c16f2008-12-17 15:59:43 +0000769//////////////////////////////////////////////////////////////////////////////
770
reed@android.com8a1c16f2008-12-17 15:59:43 +0000771void SkCanvas::updateDeviceCMCache() {
772 if (fDeviceCMDirty) {
773 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700774 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000775 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000776
reed@android.com8a1c16f2008-12-17 15:59:43 +0000777 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000778 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000779 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000780 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000781 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000782 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000783 } while ((layer = layer->fNext) != NULL);
784 }
785 fDeviceCMDirty = false;
786 }
787}
788
reed@android.com8a1c16f2008-12-17 15:59:43 +0000789///////////////////////////////////////////////////////////////////////////////
790
reed2ff1fce2014-12-11 07:07:37 -0800791void SkCanvas::checkForDeferredSave() {
792 if (fMCRec->fDeferredSaveCount > 0) {
793 fMCRec->fDeferredSaveCount -= 1;
794 this->doSave();
795 }
796}
797
reedf0090cb2014-11-26 08:55:51 -0800798int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800799#ifdef SK_DEBUG
800 int count = 0;
801 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
802 for (;;) {
803 const MCRec* rec = (const MCRec*)iter.next();
804 if (!rec) {
805 break;
806 }
807 count += 1 + rec->fDeferredSaveCount;
808 }
809 SkASSERT(count == fSaveCount);
810#endif
811 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -0800812}
813
814int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -0800815 fSaveCount += 1;
816 fMCRec->fDeferredSaveCount += 1;
817 return this->getSaveCount() - 1; // return our prev value
818}
819
820void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -0800821 this->willSave();
reed2ff1fce2014-12-11 07:07:37 -0800822 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -0800823}
824
825void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -0800826 if (fMCRec->fDeferredSaveCount > 0) {
827 SkASSERT(fSaveCount > 1);
828 fSaveCount -= 1;
829 fMCRec->fDeferredSaveCount -= 1;
830 } else {
831 // check for underflow
832 if (fMCStack.count() > 1) {
833 this->willRestore();
834 SkASSERT(fSaveCount > 1);
835 fSaveCount -= 1;
836 this->internalRestore();
837 this->didRestore();
838 }
reedf0090cb2014-11-26 08:55:51 -0800839 }
840}
841
842void SkCanvas::restoreToCount(int count) {
843 // sanity check
844 if (count < 1) {
845 count = 1;
846 }
847
848 int n = this->getSaveCount() - count;
849 for (int i = 0; i < n; ++i) {
850 this->restore();
851 }
852}
853
reed2ff1fce2014-12-11 07:07:37 -0800854void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000855 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700856 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000857 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000858
Florin Malita5f6102d2014-06-30 10:13:28 -0400859 fClipStack.save();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000860}
861
reed@android.com8a1c16f2008-12-17 15:59:43 +0000862static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +0000863#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000864 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +0000865#else
866 return true;
867#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000868}
869
junov@chromium.orga907ac32012-02-24 21:54:07 +0000870bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000871 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000872 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000873 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000874 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000875 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000876 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000877
878 if (imageFilter) {
reed1f836ee2014-07-07 07:49:34 -0700879 imageFilter->filterBounds(clipBounds, fMCRec->fMatrix, &clipBounds);
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000880 // Filters may grow the bounds beyond the device bounds.
881 op = SkRegion::kReplace_Op;
882 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000883 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -0700884 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000885 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000886
reed@android.com8a1c16f2008-12-17 15:59:43 +0000887 this->getTotalMatrix().mapRect(&r, *bounds);
888 r.roundOut(&ir);
889 // early exit if the layer's bounds are clipped out
890 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000891 if (bounds_affects_clip(flags)) {
reed1f836ee2014-07-07 07:49:34 -0700892 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000893 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000894 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000895 }
896 } else { // no user bounds, so just use the clip
897 ir = clipBounds;
898 }
899
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000900 if (bounds_affects_clip(flags)) {
901 fClipStack.clipDevRect(ir, op);
902 // early exit if the clip is now empty
reed1f836ee2014-07-07 07:49:34 -0700903 if (!fMCRec->fRasterClip.op(ir, op)) {
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000904 return false;
905 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000906 }
907
908 if (intersection) {
909 *intersection = ir;
910 }
911 return true;
912}
913
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000914int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
915 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag);
reed2ff1fce2014-12-11 07:07:37 -0800916 fSaveCount += 1;
917 this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, false, strategy);
918 return this->getSaveCount() - 1;
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000919}
920
reed2ff1fce2014-12-11 07:07:37 -0800921int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000922 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
reed2ff1fce2014-12-11 07:07:37 -0800923 fSaveCount += 1;
924 this->internalSaveLayer(bounds, paint, flags, false, strategy);
925 return this->getSaveCount() - 1;
reed@google.com8926b162012-03-23 15:36:36 +0000926}
927
reed2ff1fce2014-12-11 07:07:37 -0800928void SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000929 bool justForImageFilter, SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +0000930#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
commit-bot@chromium.org2a5cd602014-05-30 20:41:20 +0000931 flags |= kClipToLayer_SaveFlag;
reed@google.comb93ba452014-03-10 19:47:58 +0000932#endif
933
junov@chromium.orga907ac32012-02-24 21:54:07 +0000934 // do this before we create the layer. We don't call the public save() since
935 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -0800936 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +0000937
938 fDeviceCMDirty = true;
939
940 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000941 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed2ff1fce2014-12-11 07:07:37 -0800942 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000943 }
944
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000945 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
946 // the clipRectBounds() call above?
947 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -0800948 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000949 }
950
reed@google.comb55deeb2012-01-06 14:43:09 +0000951 // Kill the imagefilter if our device doesn't allow it
952 SkLazyPaint lazyP;
953 if (paint && paint->getImageFilter()) {
954 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000955 if (justForImageFilter) {
956 // early exit if the layer was just for the imageFilter
reed2ff1fce2014-12-11 07:07:37 -0800957 return;
reed@google.com8926b162012-03-23 15:36:36 +0000958 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000959 SkPaint* p = lazyP.set(*paint);
960 p->setImageFilter(NULL);
961 paint = p;
962 }
963 }
964
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000965 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
966 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
967 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000968
reedb2db8982014-11-13 12:41:02 -0800969 SkBaseDevice* device = this->getTopDevice();
970 if (NULL == device) {
971 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -0800972 return;
reed@google.com76dd2772012-01-05 21:15:07 +0000973 }
reedb2db8982014-11-13 12:41:02 -0800974
975 SkBaseDevice::Usage usage = SkBaseDevice::kSaveLayer_Usage;
976 if (paint && paint->getImageFilter()) {
977 usage = SkBaseDevice::kImageFilter_Usage;
978 }
979 device = device->onCreateCompatibleDevice(SkBaseDevice::CreateInfo(info, usage,
980 fProps.pixelGeometry()));
bungeman@google.come25c6842011-08-17 14:53:54 +0000981 if (NULL == device) {
982 SkDebugf("Unable to create device for layer.");
reed2ff1fce2014-12-11 07:07:37 -0800983 return;
bungeman@google.come25c6842011-08-17 14:53:54 +0000984 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000985
reed@google.com6f8f2922011-03-04 22:27:10 +0000986 device->setOrigin(ir.fLeft, ir.fTop);
reedd9544982014-09-09 18:46:22 -0700987 DeviceCM* layer = SkNEW_ARGS(DeviceCM,
988 (device, ir.fLeft, ir.fTop, paint, this, fConservativeRasterClip));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000989 device->unref();
990
991 layer->fNext = fMCRec->fTopLayer;
992 fMCRec->fLayer = layer;
993 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
994
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000995 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000996}
997
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000998int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
999 return this->saveLayerAlpha(bounds, alpha, kARGB_ClipLayer_SaveFlag);
1000}
1001
reed@android.com8a1c16f2008-12-17 15:59:43 +00001002int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
1003 SaveFlags flags) {
1004 if (0xFF == alpha) {
1005 return this->saveLayer(bounds, NULL, flags);
1006 } else {
1007 SkPaint tmpPaint;
1008 tmpPaint.setAlpha(alpha);
1009 return this->saveLayer(bounds, &tmpPaint, flags);
1010 }
1011}
1012
reed@android.com8a1c16f2008-12-17 15:59:43 +00001013void SkCanvas::internalRestore() {
1014 SkASSERT(fMCStack.count() != 0);
1015
1016 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001017 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001018
Florin Malita5f6102d2014-06-30 10:13:28 -04001019 fClipStack.restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001020
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001021 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001022 DeviceCM* layer = fMCRec->fLayer; // may be null
1023 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
1024 fMCRec->fLayer = NULL;
1025
1026 // now do the normal restore()
1027 fMCRec->~MCRec(); // balanced in save()
1028 fMCStack.pop_back();
1029 fMCRec = (MCRec*)fMCStack.back();
1030
1031 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1032 since if we're being recorded, we don't want to record this (the
1033 recorder will have already recorded the restore).
1034 */
bsalomon49f085d2014-09-05 13:34:00 -07001035 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001036 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001037 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001038 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
1039 layer->fPaint);
1040 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001041 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +00001042
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001043 SkASSERT(fSaveLayerCount > 0);
1044 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001045 }
1046 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001047 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001048}
1049
reed@google.com7c202932011-12-14 18:48:05 +00001050bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001051 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +00001052}
1053
reed4a8126e2014-09-22 07:29:03 -07001054SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
1055 if (NULL == props) {
1056 props = &fProps;
1057 }
1058 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001059}
1060
reed4a8126e2014-09-22 07:29:03 -07001061SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001062 SkBaseDevice* dev = this->getDevice();
reed4a8126e2014-09-22 07:29:03 -07001063 return dev ? dev->newSurface(info, props) : NULL;
reed@google.com76f10a32014-02-05 15:32:21 +00001064}
1065
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001066SkImageInfo SkCanvas::imageInfo() const {
1067 SkBaseDevice* dev = this->getDevice();
1068 if (dev) {
1069 return dev->imageInfo();
1070 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001071 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001072 }
1073}
1074
1075const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
1076 return this->onPeekPixels(info, rowBytes);
1077}
1078
1079const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
1080 SkBaseDevice* dev = this->getDevice();
1081 return dev ? dev->peekPixels(info, rowBytes) : NULL;
1082}
1083
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001084void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
1085 void* pixels = this->onAccessTopLayerPixels(info, rowBytes);
1086 if (pixels && origin) {
1087 *origin = this->getTopDevice(false)->getOrigin();
1088 }
1089 return pixels;
reed@google.com9c135db2014-03-12 18:28:35 +00001090}
1091
1092void* SkCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
1093 SkBaseDevice* dev = this->getTopDevice();
1094 return dev ? dev->accessPixels(info, rowBytes) : NULL;
1095}
1096
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001097SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1098 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1099 if (NULL == fAddr) {
1100 fInfo = canvas->imageInfo();
reed84825042014-09-02 12:50:45 -07001101 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.tryAllocPixels(fInfo)) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001102 return; // failure, fAddr is NULL
1103 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001104 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1105 return; // failure, fAddr is NULL
1106 }
1107 fAddr = fBitmap.getPixels();
1108 fRowBytes = fBitmap.rowBytes();
1109 }
1110 SkASSERT(fAddr); // success
1111}
1112
1113bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1114 if (fAddr) {
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +00001115 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001116 } else {
1117 bitmap->reset();
1118 return false;
1119 }
1120}
1121
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +00001122void SkCanvas::onPushCull(const SkRect& cullRect) {
1123 // do nothing. Subclasses may do something
1124}
1125
1126void SkCanvas::onPopCull() {
1127 // do nothing. Subclasses may do something
1128}
1129
reed@android.com8a1c16f2008-12-17 15:59:43 +00001130/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org520cf8b2014-03-20 20:25:14 +00001131#ifdef SK_DEBUG
1132// Ensure that cull rects are monotonically nested in device space.
1133void SkCanvas::validateCull(const SkIRect& devCull) {
1134 if (fCullStack.isEmpty()
1135 || devCull.isEmpty()
1136 || fCullStack.top().contains(devCull)) {
1137 return;
1138 }
1139
1140 SkDEBUGF(("Invalid cull: [%d %d %d %d] (previous cull: [%d %d %d %d])\n",
1141 devCull.x(), devCull.y(), devCull.right(), devCull.bottom(),
1142 fCullStack.top().x(), fCullStack.top().y(),
1143 fCullStack.top().right(), fCullStack.top().bottom()));
1144
1145#ifdef ASSERT_NESTED_CULLING
1146 SkDEBUGFAIL("Invalid cull.");
1147#endif
1148}
1149#endif
1150
1151void SkCanvas::pushCull(const SkRect& cullRect) {
reed2ff1fce2014-12-11 07:07:37 -08001152 this->checkForDeferredSave();
commit-bot@chromium.org520cf8b2014-03-20 20:25:14 +00001153 ++fCullCount;
1154 this->onPushCull(cullRect);
1155
1156#ifdef SK_DEBUG
1157 // Map the cull rect into device space.
1158 SkRect mappedCull;
1159 this->getTotalMatrix().mapRect(&mappedCull, cullRect);
1160
1161 // Take clipping into account.
1162 SkIRect devClip, devCull;
1163 mappedCull.roundOut(&devCull);
1164 this->getClipDeviceBounds(&devClip);
1165 if (!devCull.intersect(devClip)) {
1166 devCull.setEmpty();
1167 }
1168
1169 this->validateCull(devCull);
1170 fCullStack.push(devCull); // balanced in popCull
1171#endif
1172}
1173
1174void SkCanvas::popCull() {
1175 SkASSERT(fCullStack.count() == fCullCount);
1176
1177 if (fCullCount > 0) {
1178 --fCullCount;
1179 this->onPopCull();
1180
1181 SkDEBUGCODE(fCullStack.pop());
1182 }
1183}
1184
1185/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001186
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001187void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001188 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001189 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001190 return;
1191 }
1192
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001193 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001194 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001195 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001196 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001197
1198 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001199
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001200 SkRect storage;
1201 const SkRect* bounds = NULL;
1202 if (paint && paint->canComputeFastBounds()) {
1203 bitmap.getBounds(&storage);
1204 matrix.mapRect(&storage);
1205 bounds = &paint->computeFastBounds(storage, &storage);
1206 }
1207
1208 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001209
1210 while (iter.next()) {
1211 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1212 }
1213
1214 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001215}
1216
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001217void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001218 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001219 SkPaint tmp;
1220 if (NULL == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001221 paint = &tmp;
1222 }
reed@google.com4b226022011-01-11 18:32:13 +00001223
reed@google.com8926b162012-03-23 15:36:36 +00001224 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001225 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001226 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001227 paint = &looper.paint();
1228 SkImageFilter* filter = paint->getImageFilter();
1229 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001230 if (filter && !dstDev->canHandleImageFilter(filter)) {
fmalita2d97bc12014-11-20 10:44:58 -08001231 SkDeviceImageFilterProxy proxy(dstDev, fProps);
reed@google.com76dd2772012-01-05 21:15:07 +00001232 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001233 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001234 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001235 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001236 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001237 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblancobe129b22014-08-08 07:14:35 -07001238 SkAutoTUnref<SkImageFilter::Cache> cache(dstDev->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001239 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001240 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001241 SkPaint tmpUnfiltered(*paint);
1242 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001243 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1244 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001245 }
1246 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001247 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001248 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001249 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001250 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001251}
1252
reed@google.com8926b162012-03-23 15:36:36 +00001253void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1254 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08001255 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawSprite()");
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001256 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001257 return;
1258 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001259 SkDEBUGCODE(bitmap.validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001260
reed@google.com8926b162012-03-23 15:36:36 +00001261 SkPaint tmp;
1262 if (NULL == paint) {
1263 paint = &tmp;
1264 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001265
reed@google.com8926b162012-03-23 15:36:36 +00001266 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001267
reed@google.com8926b162012-03-23 15:36:36 +00001268 while (iter.next()) {
1269 paint = &looper.paint();
1270 SkImageFilter* filter = paint->getImageFilter();
1271 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1272 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
fmalita2d97bc12014-11-20 10:44:58 -08001273 SkDeviceImageFilterProxy proxy(iter.fDevice, fProps);
reed@google.com8926b162012-03-23 15:36:36 +00001274 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001275 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001276 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001277 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
halcanaryf622a6c2014-10-24 12:54:53 -07001278 const SkIRect clipBounds = bitmap.bounds();
senorblancobe129b22014-08-08 07:14:35 -07001279 SkAutoTUnref<SkImageFilter::Cache> cache(iter.fDevice->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001280 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001281 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001282 SkPaint tmpUnfiltered(*paint);
1283 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001284 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001285 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001286 }
1287 } else {
1288 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1289 }
1290 }
1291 LOOPER_END
1292}
1293
reed@android.com8a1c16f2008-12-17 15:59:43 +00001294/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001295void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001296 SkMatrix m;
1297 m.setTranslate(dx, dy);
1298 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001299}
1300
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001301void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001302 SkMatrix m;
1303 m.setScale(sx, sy);
1304 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001305}
1306
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001307void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001308 SkMatrix m;
1309 m.setRotate(degrees);
1310 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001311}
1312
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001313void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001314 SkMatrix m;
1315 m.setSkew(sx, sy);
1316 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001317}
1318
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001319void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001320 if (matrix.isIdentity()) {
1321 return;
1322 }
1323
reed2ff1fce2014-12-11 07:07:37 -08001324 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001325 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001326 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001327 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001328
1329 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001330}
1331
reed@android.com8a1c16f2008-12-17 15:59:43 +00001332void SkCanvas::setMatrix(const SkMatrix& matrix) {
reed2ff1fce2014-12-11 07:07:37 -08001333 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001334 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001335 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001336 fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001337 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001338}
1339
reed@android.com8a1c16f2008-12-17 15:59:43 +00001340void SkCanvas::resetMatrix() {
1341 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001342
reed@android.com8a1c16f2008-12-17 15:59:43 +00001343 matrix.reset();
1344 this->setMatrix(matrix);
1345}
1346
1347//////////////////////////////////////////////////////////////////////////////
1348
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001349void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001350 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001351 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1352 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001353}
1354
1355void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001356#ifdef SK_ENABLE_CLIP_QUICKREJECT
1357 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001358 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001359 return false;
1360 }
1361
reed@google.com3b3e8952012-08-16 20:53:31 +00001362 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001363 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001364 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001365
1366 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001367 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001368 }
1369 }
1370#endif
1371
reed@google.com5c3d1472011-02-22 19:12:23 +00001372 AutoValidateClip avc(this);
1373
reed@android.com8a1c16f2008-12-17 15:59:43 +00001374 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001375 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001376 if (!fAllowSoftClip) {
1377 edgeStyle = kHard_ClipEdgeStyle;
1378 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001379
reed1f836ee2014-07-07 07:49:34 -07001380 if (fMCRec->fMatrix.rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001381 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001382 // the matrix. This means we don't have to a) make a path, and b) tell
1383 // the region code to scan-convert the path, only to discover that it
1384 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001385 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001386
reed1f836ee2014-07-07 07:49:34 -07001387 fMCRec->fMatrix.mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001388 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reedd9544982014-09-09 18:46:22 -07001389 fMCRec->fRasterClip.op(r, this->getBaseLayerSize(), op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001390 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001391 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001392 // and clip against that, since it can handle any matrix. However, to
1393 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1394 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001395 SkPath path;
1396
1397 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001398 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001399 }
1400}
1401
reed73e714e2014-09-04 09:02:23 -07001402static void rasterclip_path(SkRasterClip* rc, const SkCanvas* canvas, const SkPath& devPath,
1403 SkRegion::Op op, bool doAA) {
reedd64c9482014-09-05 17:37:38 -07001404 rc->op(devPath, canvas->getBaseLayerSize(), op, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001405}
1406
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001407void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001408 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001409 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001410 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001411 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1412 } else {
1413 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001414 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001415}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001416
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001417void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001418 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001419 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001420 AutoValidateClip avc(this);
1421
1422 fDeviceCMDirty = true;
1423 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001424 if (!fAllowSoftClip) {
1425 edgeStyle = kHard_ClipEdgeStyle;
1426 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001427
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001428 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001429
1430 SkPath devPath;
1431 devPath.addRRect(transformedRRect);
1432
reed73e714e2014-09-04 09:02:23 -07001433 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001434 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001435 }
1436
1437 SkPath path;
1438 path.addRRect(rrect);
1439 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001440 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001441}
1442
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001443void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001444 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001445 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1446 SkRect r;
1447 if (!path.isInverseFillType() && path.isRect(&r)) {
1448 this->onClipRect(r, op, edgeStyle);
1449 } else {
1450 this->onClipPath(path, op, edgeStyle);
1451 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001452}
1453
1454void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001455#ifdef SK_ENABLE_CLIP_QUICKREJECT
1456 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001457 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001458 return false;
1459 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001460
reed@google.com3b3e8952012-08-16 20:53:31 +00001461 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001462 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001463 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001464
reed@google.comda17f752012-08-16 18:27:05 +00001465 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001466 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001467 }
1468 }
1469#endif
1470
reed@google.com5c3d1472011-02-22 19:12:23 +00001471 AutoValidateClip avc(this);
1472
reed@android.com8a1c16f2008-12-17 15:59:43 +00001473 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001474 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001475 if (!fAllowSoftClip) {
1476 edgeStyle = kHard_ClipEdgeStyle;
1477 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001478
1479 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001480 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001481
reed@google.comfe701122011-11-08 19:41:23 +00001482 // Check if the transfomation, or the original path itself
1483 // made us empty. Note this can also happen if we contained NaN
1484 // values. computing the bounds detects this, and will set our
1485 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1486 if (devPath.getBounds().isEmpty()) {
1487 // resetting the path will remove any NaN or other wanky values
1488 // that might upset our scan converter.
1489 devPath.reset();
1490 }
1491
reed@google.com5c3d1472011-02-22 19:12:23 +00001492 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001493 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001494
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001495 if (fAllowSimplifyClip) {
1496 devPath.reset();
1497 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1498 const SkClipStack* clipStack = getClipStack();
1499 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1500 const SkClipStack::Element* element;
1501 while ((element = iter.next())) {
1502 SkClipStack::Element::Type type = element->getType();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001503 SkPath operand;
commit-bot@chromium.org2a67e122014-05-19 13:53:10 +00001504 if (type != SkClipStack::Element::kEmpty_Type) {
1505 element->asPath(&operand);
1506 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001507 SkRegion::Op elementOp = element->getOp();
1508 if (elementOp == SkRegion::kReplace_Op) {
1509 devPath = operand;
1510 } else {
1511 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1512 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001513 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1514 // perhaps we need an API change to avoid this sort of mixed-signals about
1515 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001516 if (element->isAA()) {
1517 edgeStyle = kSoft_ClipEdgeStyle;
1518 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001519 }
1520 op = SkRegion::kReplace_Op;
1521 }
1522
reed73e714e2014-09-04 09:02:23 -07001523 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001524}
1525
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001526void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed2ff1fce2014-12-11 07:07:37 -08001527 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001528 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001529}
1530
1531void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001532 AutoValidateClip avc(this);
1533
reed@android.com8a1c16f2008-12-17 15:59:43 +00001534 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001535 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001536
reed@google.com5c3d1472011-02-22 19:12:23 +00001537 // todo: signal fClipStack that we have a region, and therefore (I guess)
1538 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001539 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001540
reed1f836ee2014-07-07 07:49:34 -07001541 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001542}
1543
reed@google.com819c9212011-02-23 18:56:55 +00001544#ifdef SK_DEBUG
1545void SkCanvas::validateClip() const {
1546 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001547 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001548 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001549 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001550 return;
1551 }
1552
reed@google.com819c9212011-02-23 18:56:55 +00001553 SkIRect ir;
1554 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001555 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001556
robertphillips@google.com80214e22012-07-20 15:33:18 +00001557 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001558 const SkClipStack::Element* element;
1559 while ((element = iter.next()) != NULL) {
1560 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001561 case SkClipStack::Element::kRect_Type:
1562 element->getRect().round(&ir);
1563 tmpClip.op(ir, element->getOp());
1564 break;
1565 case SkClipStack::Element::kEmpty_Type:
1566 tmpClip.setEmpty();
1567 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001568 default: {
1569 SkPath path;
1570 element->asPath(&path);
reed73e714e2014-09-04 09:02:23 -07001571 rasterclip_path(&tmpClip, this, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001572 break;
1573 }
reed@google.com819c9212011-02-23 18:56:55 +00001574 }
1575 }
reed@google.com819c9212011-02-23 18:56:55 +00001576}
1577#endif
1578
reed@google.com90c07ea2012-04-13 13:50:27 +00001579void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001580 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001581 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001582
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001583 while ((element = iter.next()) != NULL) {
fmalitac3b589a2014-06-05 12:40:07 -07001584 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001585 }
1586}
1587
reed@google.com5c3d1472011-02-22 19:12:23 +00001588///////////////////////////////////////////////////////////////////////////////
1589
reed@google.com754de5f2014-02-24 19:38:20 +00001590bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001591 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001592}
1593
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001594bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001595 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001596}
1597
reed@google.com3b3e8952012-08-16 20:53:31 +00001598bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001599 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001600 return true;
1601
reed1f836ee2014-07-07 07:49:34 -07001602 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001603 return true;
1604 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001605
reed1f836ee2014-07-07 07:49:34 -07001606 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001607 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001608 fMCRec->fMatrix.mapRect(&dst, rect);
reedb07a94f2014-11-19 05:03:18 -08001609 return !SkIRect::Intersects(dst.roundOut(), fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001610 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001611 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001612
reed@android.coma380ae42009-07-21 01:17:02 +00001613 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001614 // TODO: should we use | instead, or compare all 4 at once?
1615 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001616 return true;
1617 }
reed@google.comc0784db2013-12-13 21:16:12 +00001618 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001619 return true;
1620 }
1621 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001622 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001623}
1624
reed@google.com3b3e8952012-08-16 20:53:31 +00001625bool SkCanvas::quickReject(const SkPath& path) const {
1626 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001627}
1628
reed@google.com3b3e8952012-08-16 20:53:31 +00001629bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001630 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001631 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001632 return false;
1633 }
1634
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001635 SkMatrix inverse;
1636 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001637 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001638 if (bounds) {
1639 bounds->setEmpty();
1640 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001641 return false;
1642 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001643
bsalomon49f085d2014-09-05 13:34:00 -07001644 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001645 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001646 // adjust it outwards in case we are antialiasing
1647 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001648
reed@google.com8f4d2302013-12-17 16:44:46 +00001649 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1650 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001651 inverse.mapRect(bounds, r);
1652 }
1653 return true;
1654}
1655
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001656bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001657 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001658 if (clip.isEmpty()) {
1659 if (bounds) {
1660 bounds->setEmpty();
1661 }
1662 return false;
1663 }
1664
bsalomon49f085d2014-09-05 13:34:00 -07001665 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001666 *bounds = clip.getBounds();
1667 }
1668 return true;
1669}
1670
reed@android.com8a1c16f2008-12-17 15:59:43 +00001671const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001672 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001673}
1674
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001675const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001676 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001677}
1678
reed@google.com9c135db2014-03-12 18:28:35 +00001679GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1680 SkBaseDevice* dev = this->getTopDevice();
1681 return dev ? dev->accessRenderTarget() : NULL;
1682}
1683
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001684GrContext* SkCanvas::getGrContext() {
1685#if SK_SUPPORT_GPU
1686 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07001687 if (device) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001688 GrRenderTarget* renderTarget = device->accessRenderTarget();
bsalomon49f085d2014-09-05 13:34:00 -07001689 if (renderTarget) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001690 return renderTarget->getContext();
1691 }
1692 }
1693#endif
1694
1695 return NULL;
1696
1697}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001698
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001699void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1700 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001701 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001702 if (outer.isEmpty()) {
1703 return;
1704 }
1705 if (inner.isEmpty()) {
1706 this->drawRRect(outer, paint);
1707 return;
1708 }
1709
1710 // We don't have this method (yet), but technically this is what we should
1711 // be able to assert...
1712 // SkASSERT(outer.contains(inner));
1713 //
1714 // For now at least check for containment of bounds
1715 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1716
1717 this->onDrawDRRect(outer, inner, paint);
1718}
1719
reed@android.com8a1c16f2008-12-17 15:59:43 +00001720//////////////////////////////////////////////////////////////////////////////
1721// These are the virtual drawing methods
1722//////////////////////////////////////////////////////////////////////////////
1723
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001724void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07001725 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001726 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1727 }
1728}
1729
reed@android.com8a1c16f2008-12-17 15:59:43 +00001730void SkCanvas::drawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001731 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001732 this->internalDrawPaint(paint);
1733}
1734
1735void SkCanvas::internalDrawPaint(const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001736 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001737
1738 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001739 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001740 }
1741
reed@google.com4e2b3d32011-04-07 14:18:59 +00001742 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001743}
1744
1745void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1746 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001747 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001748 if ((long)count <= 0) {
1749 return;
1750 }
1751
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001752 SkRect r, storage;
1753 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001754 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001755 // special-case 2 points (common for drawing a single line)
1756 if (2 == count) {
1757 r.set(pts[0], pts[1]);
1758 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001759 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001760 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001761 bounds = &paint.computeFastStrokeBounds(r, &storage);
1762 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001763 return;
1764 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001765 }
reed@google.coma584aed2012-05-16 14:06:02 +00001766
reed@android.com8a1c16f2008-12-17 15:59:43 +00001767 SkASSERT(pts != NULL);
1768
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001769 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001770
reed@android.com8a1c16f2008-12-17 15:59:43 +00001771 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001772 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001773 }
reed@google.com4b226022011-01-11 18:32:13 +00001774
reed@google.com4e2b3d32011-04-07 14:18:59 +00001775 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001776}
1777
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001778void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001779 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001780 SkRect storage;
1781 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001782 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001783 bounds = &paint.computeFastBounds(r, &storage);
1784 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001785 return;
1786 }
1787 }
reed@google.com4b226022011-01-11 18:32:13 +00001788
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001789 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001790
1791 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001792 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001793 }
1794
reed@google.com4e2b3d32011-04-07 14:18:59 +00001795 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001796}
1797
reed@google.com4ed0fb72012-12-12 20:48:18 +00001798void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001799 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001800 SkRect storage;
1801 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001802 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001803 bounds = &paint.computeFastBounds(oval, &storage);
1804 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001805 return;
1806 }
1807 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001808
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001809 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001810
1811 while (iter.next()) {
1812 iter.fDevice->drawOval(iter, oval, looper.paint());
1813 }
1814
1815 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001816}
1817
1818void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001819 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001820 SkRect storage;
1821 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001822 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001823 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1824 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001825 return;
1826 }
1827 }
1828
1829 if (rrect.isRect()) {
1830 // call the non-virtual version
1831 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001832 return;
1833 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001834 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001835 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1836 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001837 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001838
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001839 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001840
1841 while (iter.next()) {
1842 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1843 }
1844
1845 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001846}
1847
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001848void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1849 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001850 SkRect storage;
1851 const SkRect* bounds = NULL;
1852 if (paint.canComputeFastBounds()) {
1853 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1854 if (this->quickReject(*bounds)) {
1855 return;
1856 }
1857 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001858
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001859 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001860
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001861 while (iter.next()) {
1862 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1863 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001864
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001865 LOOPER_END
1866}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001867
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001868void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001869 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00001870 if (!path.isFinite()) {
1871 return;
1872 }
1873
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001874 SkRect storage;
1875 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001876 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001877 const SkRect& pathBounds = path.getBounds();
1878 bounds = &paint.computeFastBounds(pathBounds, &storage);
1879 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001880 return;
1881 }
1882 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00001883
1884 const SkRect& r = path.getBounds();
1885 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00001886 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001887 this->internalDrawPaint(paint);
1888 }
1889 return;
1890 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001891
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001892 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001893
1894 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001895 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001896 }
1897
reed@google.com4e2b3d32011-04-07 14:18:59 +00001898 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001899}
1900
piotaixrb5fae932014-09-24 13:03:30 -07001901void SkCanvas::drawImage(const SkImage* image, SkScalar left, SkScalar top,
1902 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08001903 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
piotaixrb5fae932014-09-24 13:03:30 -07001904 image->draw(this, left, top, paint);
1905}
1906
1907void SkCanvas::drawImageRect(const SkImage* image, const SkRect* src,
1908 const SkRect& dst,
1909 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08001910 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
piotaixr5ceff912014-09-26 07:36:26 -07001911 image->drawRect(this, src, dst, paint);
piotaixrb5fae932014-09-24 13:03:30 -07001912}
1913
reed@android.com8a1c16f2008-12-17 15:59:43 +00001914void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1915 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08001916 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00001917 SkDEBUGCODE(bitmap.validate();)
1918
reed@google.com3d608122011-11-21 15:16:16 +00001919 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001920 SkRect bounds = {
1921 x, y,
1922 x + SkIntToScalar(bitmap.width()),
1923 y + SkIntToScalar(bitmap.height())
1924 };
1925 if (paint) {
1926 (void)paint->computeFastBounds(bounds, &bounds);
1927 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001928 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001929 return;
1930 }
1931 }
reed@google.com4b226022011-01-11 18:32:13 +00001932
reed@android.com8a1c16f2008-12-17 15:59:43 +00001933 SkMatrix matrix;
1934 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001935 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001936}
1937
reed@google.com9987ec32011-09-07 11:57:52 +00001938// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001939void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001940 const SkRect& dst, const SkPaint* paint,
1941 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001942 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001943 return;
1944 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001945
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001946 SkRect storage;
1947 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00001948 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001949 if (paint) {
1950 bounds = &paint->computeFastBounds(dst, &storage);
1951 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001952 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001953 return;
1954 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001955 }
reed@google.com3d608122011-11-21 15:16:16 +00001956
reed@google.com33535f32012-09-25 15:37:50 +00001957 SkLazyPaint lazy;
1958 if (NULL == paint) {
1959 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001960 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001961
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001962 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001963
reed@google.com33535f32012-09-25 15:37:50 +00001964 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001965 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001966 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001967
reed@google.com33535f32012-09-25 15:37:50 +00001968 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001969}
1970
reed@google.com71121732012-09-18 15:14:33 +00001971void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001972 const SkRect& dst, const SkPaint* paint,
1973 DrawBitmapRectFlags flags) {
danakj9881d632014-11-26 12:41:06 -08001974 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00001975 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001976 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001977}
1978
reed@google.com9987ec32011-09-07 11:57:52 +00001979void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1980 const SkIRect& center, const SkRect& dst,
1981 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001982 if (bitmap.drawsNothing()) {
1983 return;
1984 }
reed@google.com3d608122011-11-21 15:16:16 +00001985 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001986 SkRect storage;
1987 const SkRect* bounds = &dst;
1988 if (paint) {
1989 bounds = &paint->computeFastBounds(dst, &storage);
1990 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001991 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001992 return;
1993 }
1994 }
1995
reed@google.com9987ec32011-09-07 11:57:52 +00001996 const int32_t w = bitmap.width();
1997 const int32_t h = bitmap.height();
1998
1999 SkIRect c = center;
2000 // pin center to the bounds of the bitmap
2001 c.fLeft = SkMax32(0, center.fLeft);
2002 c.fTop = SkMax32(0, center.fTop);
2003 c.fRight = SkPin32(center.fRight, c.fLeft, w);
2004 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
2005
reed@google.com71121732012-09-18 15:14:33 +00002006 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002007 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00002008 };
2009 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002010 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00002011 };
reed@google.com9987ec32011-09-07 11:57:52 +00002012 SkScalar dstX[4] = {
2013 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
2014 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
2015 };
2016 SkScalar dstY[4] = {
2017 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2018 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2019 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002020
reed@google.com9987ec32011-09-07 11:57:52 +00002021 if (dstX[1] > dstX[2]) {
2022 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2023 dstX[2] = dstX[1];
2024 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002025
reed@google.com9987ec32011-09-07 11:57:52 +00002026 if (dstY[1] > dstY[2]) {
2027 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2028 dstY[2] = dstY[1];
2029 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002030
reed@google.com9987ec32011-09-07 11:57:52 +00002031 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002032 SkRect s, d;
2033
reed@google.com9987ec32011-09-07 11:57:52 +00002034 s.fTop = srcY[y];
2035 s.fBottom = srcY[y+1];
2036 d.fTop = dstY[y];
2037 d.fBottom = dstY[y+1];
2038 for (int x = 0; x < 3; x++) {
2039 s.fLeft = srcX[x];
2040 s.fRight = srcX[x+1];
2041 d.fLeft = dstX[x];
2042 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002043 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002044 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002045 }
2046 }
2047}
2048
2049void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
2050 const SkRect& dst, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002051 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002052 SkDEBUGCODE(bitmap.validate();)
2053
2054 // Need a device entry-point, so gpu can use a mesh
2055 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2056}
2057
reed@google.comf67e4cf2011-03-15 20:56:58 +00002058class SkDeviceFilteredPaint {
2059public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002060 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002061 uint32_t filteredFlags = device->filterTextFlags(paint);
2062 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002063 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002064 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002065 fPaint = newPaint;
2066 } else {
2067 fPaint = &paint;
2068 }
2069 }
2070
reed@google.comf67e4cf2011-03-15 20:56:58 +00002071 const SkPaint& paint() const { return *fPaint; }
2072
2073private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002074 const SkPaint* fPaint;
2075 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002076};
2077
bungeman@google.com52c748b2011-08-22 21:30:43 +00002078void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2079 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002080 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002081 draw.fDevice->drawRect(draw, r, paint);
2082 } else {
2083 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002084 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002085 draw.fDevice->drawRect(draw, r, p);
2086 }
2087}
2088
2089void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2090 const char text[], size_t byteLength,
2091 SkScalar x, SkScalar y) {
2092 SkASSERT(byteLength == 0 || text != NULL);
2093
2094 // nothing to draw
2095 if (text == NULL || byteLength == 0 ||
2096 draw.fClip->isEmpty() ||
2097 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2098 return;
2099 }
2100
2101 SkScalar width = 0;
2102 SkPoint start;
2103
2104 start.set(0, 0); // to avoid warning
2105 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2106 SkPaint::kStrikeThruText_Flag)) {
2107 width = paint.measureText(text, byteLength);
2108
2109 SkScalar offsetX = 0;
2110 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2111 offsetX = SkScalarHalf(width);
2112 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2113 offsetX = width;
2114 }
2115 start.set(x - offsetX, y);
2116 }
2117
2118 if (0 == width) {
2119 return;
2120 }
2121
2122 uint32_t flags = paint.getFlags();
2123
2124 if (flags & (SkPaint::kUnderlineText_Flag |
2125 SkPaint::kStrikeThruText_Flag)) {
2126 SkScalar textSize = paint.getTextSize();
2127 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2128 SkRect r;
2129
2130 r.fLeft = start.fX;
2131 r.fRight = start.fX + width;
2132
2133 if (flags & SkPaint::kUnderlineText_Flag) {
2134 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2135 start.fY);
2136 r.fTop = offset;
2137 r.fBottom = offset + height;
2138 DrawRect(draw, paint, r, textSize);
2139 }
2140 if (flags & SkPaint::kStrikeThruText_Flag) {
2141 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2142 start.fY);
2143 r.fTop = offset;
2144 r.fBottom = offset + height;
2145 DrawRect(draw, paint, r, textSize);
2146 }
2147 }
2148}
2149
reed@google.come0d9ce82014-04-23 04:00:17 +00002150void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2151 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002152 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002153
2154 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002155 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002156 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002157 DrawTextDecorations(iter, dfp.paint(),
2158 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002159 }
2160
reed@google.com4e2b3d32011-04-07 14:18:59 +00002161 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002162}
2163
reed@google.come0d9ce82014-04-23 04:00:17 +00002164void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2165 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002166 SkPoint textOffset = SkPoint::Make(0, 0);
2167
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002168 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002169
reed@android.com8a1c16f2008-12-17 15:59:43 +00002170 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002171 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002172 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002173 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002174 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002175
reed@google.com4e2b3d32011-04-07 14:18:59 +00002176 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002177}
2178
reed@google.come0d9ce82014-04-23 04:00:17 +00002179void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2180 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002181
2182 SkPoint textOffset = SkPoint::Make(0, constY);
2183
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002184 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002185
reed@android.com8a1c16f2008-12-17 15:59:43 +00002186 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002187 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002188 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002189 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002190 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002191
reed@google.com4e2b3d32011-04-07 14:18:59 +00002192 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002193}
2194
reed@google.come0d9ce82014-04-23 04:00:17 +00002195void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2196 const SkMatrix* matrix, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002197 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002198
reed@android.com8a1c16f2008-12-17 15:59:43 +00002199 while (iter.next()) {
2200 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002201 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002202 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002203
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002204 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002205}
2206
fmalita00d5c2c2014-08-21 08:53:26 -07002207void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2208 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002209
fmalita19653d12014-10-16 11:53:30 -07002210 if (paint.canComputeFastBounds()) {
fmalita7ba7aa72014-08-29 09:46:36 -07002211 SkRect storage;
2212
2213 if (this->quickReject(paint.computeFastBounds(blob->bounds().makeOffset(x, y), &storage))) {
2214 return;
2215 }
2216 }
2217
fmalitaaa1b9122014-08-28 14:32:24 -07002218 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
fmalita00d5c2c2014-08-21 08:53:26 -07002219
fmalitaaa1b9122014-08-28 14:32:24 -07002220 while (iter.next()) {
2221 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
2222 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint());
fmalita00d5c2c2014-08-21 08:53:26 -07002223 }
2224
fmalitaaa1b9122014-08-28 14:32:24 -07002225 LOOPER_END
fmalita00d5c2c2014-08-21 08:53:26 -07002226}
2227
reed@google.come0d9ce82014-04-23 04:00:17 +00002228// These will become non-virtual, so they always call the (virtual) onDraw... method
2229void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2230 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002231 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002232 this->onDrawText(text, byteLength, x, y, paint);
2233}
2234void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2235 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002236 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002237 this->onDrawPosText(text, byteLength, pos, paint);
2238}
2239void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2240 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002241 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002242 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2243}
2244void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2245 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002246 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002247 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2248}
fmalita00d5c2c2014-08-21 08:53:26 -07002249void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2250 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002251 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
bsalomon49f085d2014-09-05 13:34:00 -07002252 if (blob) {
fmalita00d5c2c2014-08-21 08:53:26 -07002253 this->onDrawTextBlob(blob, x, y, paint);
2254 }
2255}
reed@google.come0d9ce82014-04-23 04:00:17 +00002256
reed@android.com8a1c16f2008-12-17 15:59:43 +00002257void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2258 const SkPoint verts[], const SkPoint texs[],
2259 const SkColor colors[], SkXfermode* xmode,
2260 const uint16_t indices[], int indexCount,
2261 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002262 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002263 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002264
reed@android.com8a1c16f2008-12-17 15:59:43 +00002265 while (iter.next()) {
2266 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002267 colors, xmode, indices, indexCount,
2268 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002269 }
reed@google.com4b226022011-01-11 18:32:13 +00002270
reed@google.com4e2b3d32011-04-07 14:18:59 +00002271 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002272}
2273
dandovb3c9d1c2014-08-12 08:34:29 -07002274void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2275 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002276 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
dandovb3c9d1c2014-08-12 08:34:29 -07002277 if (NULL == cubics) {
2278 return;
2279 }
mtklein6cfa73a2014-08-13 13:33:49 -07002280
dandovecfff212014-08-04 10:02:00 -07002281 // Since a patch is always within the convex hull of the control points, we discard it when its
2282 // bounding rectangle is completely outside the current clip.
2283 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002284 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002285 if (this->quickReject(bounds)) {
2286 return;
2287 }
mtklein6cfa73a2014-08-13 13:33:49 -07002288
dandovb3c9d1c2014-08-12 08:34:29 -07002289 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2290}
2291
2292void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2293 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2294
dandovecfff212014-08-04 10:02:00 -07002295 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
mtklein6cfa73a2014-08-13 13:33:49 -07002296
dandovecfff212014-08-04 10:02:00 -07002297 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002298 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002299 }
mtklein6cfa73a2014-08-13 13:33:49 -07002300
dandovecfff212014-08-04 10:02:00 -07002301 LOOPER_END
2302}
2303
reed6a070dc2014-11-11 19:36:09 -08002304void SkCanvas::EXPERIMENTAL_drawDrawable(SkCanvasDrawable* dr) {
reed6be2aa92014-11-18 11:08:05 -08002305 if (dr && !this->quickReject(dr->getBounds())) {
2306 this->onDrawDrawable(dr);
reed6a070dc2014-11-11 19:36:09 -08002307 }
2308}
2309
2310void SkCanvas::onDrawDrawable(SkCanvasDrawable* dr) {
2311 dr->draw(this);
2312}
2313
reed@android.com8a1c16f2008-12-17 15:59:43 +00002314//////////////////////////////////////////////////////////////////////////////
2315// These methods are NOT virtual, and therefore must call back into virtual
2316// methods, rather than actually drawing themselves.
2317//////////////////////////////////////////////////////////////////////////////
2318
2319void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002320 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002321 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002322 SkPaint paint;
2323
2324 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002325 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002326 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002327 }
2328 this->drawPaint(paint);
2329}
2330
reed@android.com845fdac2009-06-23 03:01:32 +00002331void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002332 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002333 SkPaint paint;
2334
2335 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002336 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002337 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002338 }
2339 this->drawPaint(paint);
2340}
2341
2342void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002343 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002344 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002345
reed@android.com8a1c16f2008-12-17 15:59:43 +00002346 pt.set(x, y);
2347 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2348}
2349
2350void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002351 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002352 SkPoint pt;
2353 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002354
reed@android.com8a1c16f2008-12-17 15:59:43 +00002355 pt.set(x, y);
2356 paint.setColor(color);
2357 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2358}
2359
2360void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2361 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002362 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002363 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002364
reed@android.com8a1c16f2008-12-17 15:59:43 +00002365 pts[0].set(x0, y0);
2366 pts[1].set(x1, y1);
2367 this->drawPoints(kLines_PointMode, 2, pts, paint);
2368}
2369
2370void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2371 SkScalar right, SkScalar bottom,
2372 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002373 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002374 SkRect r;
2375
2376 r.set(left, top, right, bottom);
2377 this->drawRect(r, paint);
2378}
2379
2380void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2381 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002382 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002383 if (radius < 0) {
2384 radius = 0;
2385 }
2386
2387 SkRect r;
2388 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002389 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002390}
2391
2392void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2393 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002394 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002395 if (rx > 0 && ry > 0) {
2396 if (paint.canComputeFastBounds()) {
2397 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002398 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002399 return;
2400 }
2401 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002402 SkRRect rrect;
2403 rrect.setRectXY(r, rx, ry);
2404 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002405 } else {
2406 this->drawRect(r, paint);
2407 }
2408}
2409
reed@android.com8a1c16f2008-12-17 15:59:43 +00002410void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2411 SkScalar sweepAngle, bool useCenter,
2412 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002413 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002414 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2415 this->drawOval(oval, paint);
2416 } else {
2417 SkPath path;
2418 if (useCenter) {
2419 path.moveTo(oval.centerX(), oval.centerY());
2420 }
2421 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2422 if (useCenter) {
2423 path.close();
2424 }
2425 this->drawPath(path, paint);
2426 }
2427}
2428
2429void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2430 const SkPath& path, SkScalar hOffset,
2431 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002432 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002433 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002434
reed@android.com8a1c16f2008-12-17 15:59:43 +00002435 matrix.setTranslate(hOffset, vOffset);
2436 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2437}
2438
reed@android.comf76bacf2009-05-13 14:00:33 +00002439///////////////////////////////////////////////////////////////////////////////
robertphillips9b14f262014-06-04 05:40:44 -07002440void SkCanvas::drawPicture(const SkPicture* picture) {
danakj9881d632014-11-26 12:41:06 -08002441 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
bsalomon49f085d2014-09-05 13:34:00 -07002442 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002443 this->onDrawPicture(picture, NULL, NULL);
robertphillips9b14f262014-06-04 05:40:44 -07002444 }
2445}
2446
reedd5fa1a42014-08-09 11:08:05 -07002447void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002448 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture(SkMatrix, SkPaint)");
bsalomon49f085d2014-09-05 13:34:00 -07002449 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002450 if (matrix && matrix->isIdentity()) {
2451 matrix = NULL;
2452 }
2453 this->onDrawPicture(picture, matrix, paint);
2454 }
2455}
robertphillips9b14f262014-06-04 05:40:44 -07002456
reedd5fa1a42014-08-09 11:08:05 -07002457void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2458 const SkPaint* paint) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002459 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002460 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002461 // Canvas has to first give the device the opportunity to render
2462 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002463 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002464 return; // the device has rendered the entire picture
2465 }
2466 }
2467
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002468 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
reedd5fa1a42014-08-09 11:08:05 -07002469
robertphillipsc5ba71d2014-09-04 08:42:50 -07002470 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002471}
2472
reed@android.com8a1c16f2008-12-17 15:59:43 +00002473///////////////////////////////////////////////////////////////////////////////
2474///////////////////////////////////////////////////////////////////////////////
2475
2476SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002477 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002478
2479 SkASSERT(canvas);
2480
2481 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2482 fDone = !fImpl->next();
2483}
2484
2485SkCanvas::LayerIter::~LayerIter() {
2486 fImpl->~SkDrawIter();
2487}
2488
2489void SkCanvas::LayerIter::next() {
2490 fDone = !fImpl->next();
2491}
2492
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002493SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002494 return fImpl->getDevice();
2495}
2496
2497const SkMatrix& SkCanvas::LayerIter::matrix() const {
2498 return fImpl->getMatrix();
2499}
2500
2501const SkPaint& SkCanvas::LayerIter::paint() const {
2502 const SkPaint* paint = fImpl->getPaint();
2503 if (NULL == paint) {
2504 paint = &fDefaultPaint;
2505 }
2506 return *paint;
2507}
2508
2509const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2510int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2511int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002512
2513///////////////////////////////////////////////////////////////////////////////
2514
fmalitac3b589a2014-06-05 12:40:07 -07002515SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002516
2517///////////////////////////////////////////////////////////////////////////////
2518
2519static bool supported_for_raster_canvas(const SkImageInfo& info) {
2520 switch (info.alphaType()) {
2521 case kPremul_SkAlphaType:
2522 case kOpaque_SkAlphaType:
2523 break;
2524 default:
2525 return false;
2526 }
2527
2528 switch (info.colorType()) {
2529 case kAlpha_8_SkColorType:
2530 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002531 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002532 break;
2533 default:
2534 return false;
2535 }
2536
2537 return true;
2538}
2539
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002540SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2541 if (!supported_for_raster_canvas(info)) {
2542 return NULL;
2543 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002544
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002545 SkBitmap bitmap;
2546 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2547 return NULL;
2548 }
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002549 return SkNEW_ARGS(SkCanvas, (bitmap));
2550}
reedd5fa1a42014-08-09 11:08:05 -07002551
2552///////////////////////////////////////////////////////////////////////////////
2553
2554SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002555 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07002556 : fCanvas(canvas)
2557 , fSaveCount(canvas->getSaveCount())
2558{
bsalomon49f085d2014-09-05 13:34:00 -07002559 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002560 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07002561 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002562 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07002563 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002564 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07002565 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002566 canvas->save();
2567 }
mtklein6cfa73a2014-08-13 13:33:49 -07002568
bsalomon49f085d2014-09-05 13:34:00 -07002569 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002570 canvas->concat(*matrix);
2571 }
2572}
2573
2574SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
2575 fCanvas->restoreToCount(fSaveCount);
2576}