blob: a72b58c7cbc8d407b34b0796119bf4a66dcb91bd [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;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000420 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000421
422 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700423 new (fMCRec) MCRec(fConservativeRasterClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000424
reedd9544982014-09-09 18:46:22 -0700425 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL, fConservativeRasterClip));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000426 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000427
reed@google.com97af1a62012-08-28 12:19:02 +0000428 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000429
reedf92c8662014-08-18 08:02:43 -0700430 if (device) {
reedb2db8982014-11-13 12:41:02 -0800431 device->initForRootLayer(fProps.pixelGeometry());
reed4a8126e2014-09-22 07:29:03 -0700432 if (device->forceConservativeRasterClip()) {
433 fConservativeRasterClip = true;
434 }
reedf92c8662014-08-18 08:02:43 -0700435 device->onAttachToCanvas(this);
436 fMCRec->fLayer->fDevice = SkRef(device);
reed78e27682014-11-19 08:04:34 -0800437 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
reedf92c8662014-08-18 08:02:43 -0700438 }
439 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000440}
441
reed@google.comcde92112011-07-06 20:00:52 +0000442SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000443 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700444 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000445{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000446 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000447
reedd9544982014-09-09 18:46:22 -0700448 this->init(NULL, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000449}
450
reedd9544982014-09-09 18:46:22 -0700451static SkBitmap make_nopixels(int width, int height) {
452 SkBitmap bitmap;
453 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
454 return bitmap;
455}
456
457class SkNoPixelsBitmapDevice : public SkBitmapDevice {
458public:
reed78e27682014-11-19 08:04:34 -0800459 SkNoPixelsBitmapDevice(const SkIRect& bounds)
460 : INHERITED(make_nopixels(bounds.width(), bounds.height()))
461 {
462 this->setOrigin(bounds.x(), bounds.y());
463 }
reedd9544982014-09-09 18:46:22 -0700464
465private:
piotaixrb5fae932014-09-24 13:03:30 -0700466
reedd9544982014-09-09 18:46:22 -0700467 typedef SkBitmapDevice INHERITED;
468};
469
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000470SkCanvas::SkCanvas(int width, int height)
471 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700472 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000473{
474 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700475
reed78e27682014-11-19 08:04:34 -0800476 this->init(SkNEW_ARGS(SkNoPixelsBitmapDevice,
477 (SkIRect::MakeWH(width, height))), kDefault_InitFlags)->unref();
reedd9544982014-09-09 18:46:22 -0700478}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000479
reed78e27682014-11-19 08:04:34 -0800480SkCanvas::SkCanvas(const SkIRect& bounds, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700481 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700482 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reedd9544982014-09-09 18:46:22 -0700483{
484 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700485
reed78e27682014-11-19 08:04:34 -0800486 this->init(SkNEW_ARGS(SkNoPixelsBitmapDevice, (bounds)), flags)->unref();
reedd9544982014-09-09 18:46:22 -0700487}
488
reed4a8126e2014-09-22 07:29:03 -0700489SkCanvas::SkCanvas(SkBaseDevice* device, const SkSurfaceProps* props, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700490 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700491 , fProps(SkSurfacePropsCopyOrDefault(props))
reedd9544982014-09-09 18:46:22 -0700492{
493 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700494
reedd9544982014-09-09 18:46:22 -0700495 this->init(device, flags);
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000496}
497
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000498SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000499 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700500 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000501{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000502 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700503
reedd9544982014-09-09 18:46:22 -0700504 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000505}
506
reed4a8126e2014-09-22 07:29:03 -0700507SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700508 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700509 , fProps(props)
reed3716fd02014-09-21 09:39:55 -0700510{
511 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700512
reed4a8126e2014-09-22 07:29:03 -0700513 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap)));
514 this->init(device, kDefault_InitFlags);
515}
reed29c857d2014-09-21 10:25:07 -0700516
reed4a8126e2014-09-22 07:29:03 -0700517SkCanvas::SkCanvas(const SkBitmap& bitmap)
518 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
519 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
520{
521 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700522
reed4a8126e2014-09-22 07:29:03 -0700523 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap)));
524 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000525}
526
527SkCanvas::~SkCanvas() {
528 // free up the contents of our deque
529 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000530 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000531
reed@android.com8a1c16f2008-12-17 15:59:43 +0000532 this->internalRestore(); // restore the last, since we're going away
533
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000534 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000535
reed@android.com8a1c16f2008-12-17 15:59:43 +0000536 dec_canvas();
537}
538
reed@android.com8a1c16f2008-12-17 15:59:43 +0000539SkDrawFilter* SkCanvas::getDrawFilter() const {
540 return fMCRec->fFilter;
541}
542
543SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
544 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
545 return filter;
546}
547
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000548SkMetaData& SkCanvas::getMetaData() {
549 // metadata users are rare, so we lazily allocate it. If that changes we
550 // can decide to just make it a field in the device (rather than a ptr)
551 if (NULL == fMetaData) {
552 fMetaData = new SkMetaData;
553 }
554 return *fMetaData;
555}
556
reed@android.com8a1c16f2008-12-17 15:59:43 +0000557///////////////////////////////////////////////////////////////////////////////
558
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000559void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000560 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000561 if (device) {
562 device->flush();
563 }
564}
565
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000566SkISize SkCanvas::getTopLayerSize() const {
567 SkBaseDevice* d = this->getTopDevice();
568 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
569}
570
571SkIPoint SkCanvas::getTopLayerOrigin() const {
572 SkBaseDevice* d = this->getTopDevice();
573 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
574}
575
576SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000577 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000578 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
579}
580
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000581SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000582 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000583 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000584 SkASSERT(rec && rec->fLayer);
585 return rec->fLayer->fDevice;
586}
587
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000588SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000589 if (updateMatrixClip) {
590 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
591 }
reed@google.com9266fed2011-03-30 00:18:03 +0000592 return fMCRec->fTopLayer->fDevice;
593}
594
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000595SkBaseDevice* SkCanvas::setRootDevice(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000596 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000597 SkDeque::F2BIter iter(fMCStack);
598 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000599 SkASSERT(rec && rec->fLayer);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000600 SkBaseDevice* rootDevice = rec->fLayer->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000601
602 if (rootDevice == device) {
603 return device;
604 }
reed@google.com4b226022011-01-11 18:32:13 +0000605
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000607 device->onAttachToCanvas(this);
reedb2db8982014-11-13 12:41:02 -0800608 device->initForRootLayer(fProps.pixelGeometry());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000609 }
610 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000611 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000612 }
613
614 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
615 rootDevice = device;
616
617 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000618
reed@android.com8a1c16f2008-12-17 15:59:43 +0000619 /* Now we update our initial region to have the bounds of the new device,
620 and then intersect all of the clips in our stack with these bounds,
621 to ensure that we can't draw outside of the device's bounds (and trash
622 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000623
reed@android.com8a1c16f2008-12-17 15:59:43 +0000624 NOTE: this is only a partial-fix, since if the new device is larger than
625 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000626 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000627 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
628 reconstruct the correct clips, so this approximation will have to do.
629 The caller really needs to restore() back to the base if they want to
630 accurately take advantage of the new device bounds.
631 */
632
reed@google.com42aea282012-03-28 16:19:15 +0000633 SkIRect bounds;
634 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000635 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000636 } else {
637 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000638 }
reed@google.com42aea282012-03-28 16:19:15 +0000639 // now jam our 1st clip to be bounds, and intersect the rest with that
reed1f836ee2014-07-07 07:49:34 -0700640 rec->fRasterClip.setRect(bounds);
reed@google.com42aea282012-03-28 16:19:15 +0000641 while ((rec = (MCRec*)iter.next()) != NULL) {
reed1f836ee2014-07-07 07:49:34 -0700642 (void)rec->fRasterClip.op(bounds, SkRegion::kIntersect_Op);
reed@google.com42aea282012-03-28 16:19:15 +0000643 }
644
reed@android.com8a1c16f2008-12-17 15:59:43 +0000645 return device;
646}
647
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000648bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
649 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
650 return false;
651 }
652
653 bool weAllocated = false;
654 if (NULL == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700655 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000656 return false;
657 }
658 weAllocated = true;
659 }
660
661 SkBitmap bm(*bitmap);
662 bm.lockPixels();
663 if (bm.getPixels() && this->readPixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y)) {
664 return true;
665 }
666
667 if (weAllocated) {
668 bitmap->setPixelRef(NULL);
669 }
670 return false;
671}
reed@google.com51df9e32010-12-23 19:29:18 +0000672
bsalomon@google.comc6980972011-11-02 19:57:21 +0000673bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000674 SkIRect r = srcRect;
675 const SkISize size = this->getBaseLayerSize();
676 if (!r.intersect(0, 0, size.width(), size.height())) {
677 bitmap->reset();
678 return false;
679 }
680
reed84825042014-09-02 12:50:45 -0700681 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000682 // bitmap will already be reset.
683 return false;
684 }
685 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
686 bitmap->reset();
687 return false;
688 }
689 return true;
690}
691
reed96472de2014-12-10 09:53:42 -0800692bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000693 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000694 if (!device) {
695 return false;
696 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000697 const SkISize size = this->getBaseLayerSize();
mtkleinf0f14112014-12-12 08:46:25 -0800698
reed96472de2014-12-10 09:53:42 -0800699 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
700 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000701 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000702 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000703
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000704 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800705 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000706}
707
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000708bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
709 if (bitmap.getTexture()) {
710 return false;
711 }
712 SkBitmap bm(bitmap);
713 bm.lockPixels();
714 if (bm.getPixels()) {
715 return this->writePixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y);
716 }
717 return false;
718}
719
720bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
721 int x, int y) {
722 switch (origInfo.colorType()) {
723 case kUnknown_SkColorType:
724 case kIndex_8_SkColorType:
725 return false;
726 default:
727 break;
728 }
729 if (NULL == pixels || rowBytes < origInfo.minRowBytes()) {
730 return false;
731 }
732
733 const SkISize size = this->getBaseLayerSize();
734 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
735 if (!target.intersect(0, 0, size.width(), size.height())) {
736 return false;
737 }
738
739 SkBaseDevice* device = this->getDevice();
740 if (!device) {
741 return false;
742 }
743
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000744 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700745 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000746
747 // if x or y are negative, then we have to adjust pixels
748 if (x > 0) {
749 x = 0;
750 }
751 if (y > 0) {
752 y = 0;
753 }
754 // here x,y are either 0 or negative
755 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
756
reed4af35f32014-06-27 17:47:49 -0700757 // Tell our owning surface to bump its generation ID
758 this->predrawNotify();
759
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000760 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000761 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000762}
reed@google.com51df9e32010-12-23 19:29:18 +0000763
junov@google.com4370aed2012-01-18 16:21:08 +0000764SkCanvas* SkCanvas::canvasForDrawIter() {
765 return this;
766}
767
reed@android.com8a1c16f2008-12-17 15:59:43 +0000768//////////////////////////////////////////////////////////////////////////////
769
reed@android.com8a1c16f2008-12-17 15:59:43 +0000770void SkCanvas::updateDeviceCMCache() {
771 if (fDeviceCMDirty) {
772 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700773 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000774 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000775
reed@android.com8a1c16f2008-12-17 15:59:43 +0000776 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000777 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000778 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000779 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000780 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000781 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000782 } while ((layer = layer->fNext) != NULL);
783 }
784 fDeviceCMDirty = false;
785 }
786}
787
reed@android.com8a1c16f2008-12-17 15:59:43 +0000788///////////////////////////////////////////////////////////////////////////////
789
reed2ff1fce2014-12-11 07:07:37 -0800790void SkCanvas::checkForDeferredSave() {
791 if (fMCRec->fDeferredSaveCount > 0) {
792 fMCRec->fDeferredSaveCount -= 1;
793 this->doSave();
794 }
795}
796
reedf0090cb2014-11-26 08:55:51 -0800797int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800798#ifdef SK_DEBUG
799 int count = 0;
800 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
801 for (;;) {
802 const MCRec* rec = (const MCRec*)iter.next();
803 if (!rec) {
804 break;
805 }
806 count += 1 + rec->fDeferredSaveCount;
807 }
808 SkASSERT(count == fSaveCount);
809#endif
810 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -0800811}
812
813int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -0800814 fSaveCount += 1;
815 fMCRec->fDeferredSaveCount += 1;
816 return this->getSaveCount() - 1; // return our prev value
817}
818
819void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -0800820 this->willSave();
reed2ff1fce2014-12-11 07:07:37 -0800821 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -0800822}
823
824void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -0800825 if (fMCRec->fDeferredSaveCount > 0) {
826 SkASSERT(fSaveCount > 1);
827 fSaveCount -= 1;
828 fMCRec->fDeferredSaveCount -= 1;
829 } else {
830 // check for underflow
831 if (fMCStack.count() > 1) {
832 this->willRestore();
833 SkASSERT(fSaveCount > 1);
834 fSaveCount -= 1;
835 this->internalRestore();
836 this->didRestore();
837 }
reedf0090cb2014-11-26 08:55:51 -0800838 }
839}
840
841void SkCanvas::restoreToCount(int count) {
842 // sanity check
843 if (count < 1) {
844 count = 1;
845 }
mtkleinf0f14112014-12-12 08:46:25 -0800846
reedf0090cb2014-11-26 08:55:51 -0800847 int n = this->getSaveCount() - count;
848 for (int i = 0; i < n; ++i) {
849 this->restore();
850 }
851}
852
reed2ff1fce2014-12-11 07:07:37 -0800853void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000854 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700855 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000856 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000857
Florin Malita5f6102d2014-06-30 10:13:28 -0400858 fClipStack.save();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000859}
860
reed@android.com8a1c16f2008-12-17 15:59:43 +0000861static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +0000862#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000863 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +0000864#else
865 return true;
866#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000867}
868
junov@chromium.orga907ac32012-02-24 21:54:07 +0000869bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000870 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000871 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000872 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000873 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000874 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000875 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000876
877 if (imageFilter) {
reed1f836ee2014-07-07 07:49:34 -0700878 imageFilter->filterBounds(clipBounds, fMCRec->fMatrix, &clipBounds);
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000879 // Filters may grow the bounds beyond the device bounds.
880 op = SkRegion::kReplace_Op;
881 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000882 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -0700883 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000884 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000885
reed@android.com8a1c16f2008-12-17 15:59:43 +0000886 this->getTotalMatrix().mapRect(&r, *bounds);
887 r.roundOut(&ir);
888 // early exit if the layer's bounds are clipped out
889 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000890 if (bounds_affects_clip(flags)) {
reed1f836ee2014-07-07 07:49:34 -0700891 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000892 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000893 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000894 }
895 } else { // no user bounds, so just use the clip
896 ir = clipBounds;
897 }
898
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000899 if (bounds_affects_clip(flags)) {
900 fClipStack.clipDevRect(ir, op);
901 // early exit if the clip is now empty
reed1f836ee2014-07-07 07:49:34 -0700902 if (!fMCRec->fRasterClip.op(ir, op)) {
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000903 return false;
904 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000905 }
906
907 if (intersection) {
908 *intersection = ir;
909 }
910 return true;
911}
912
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000913int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
914 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag);
reed2ff1fce2014-12-11 07:07:37 -0800915 fSaveCount += 1;
916 this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, false, strategy);
917 return this->getSaveCount() - 1;
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000918}
919
reed2ff1fce2014-12-11 07:07:37 -0800920int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000921 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
reed2ff1fce2014-12-11 07:07:37 -0800922 fSaveCount += 1;
923 this->internalSaveLayer(bounds, paint, flags, false, strategy);
924 return this->getSaveCount() - 1;
reed@google.com8926b162012-03-23 15:36:36 +0000925}
926
reed2ff1fce2014-12-11 07:07:37 -0800927void SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000928 bool justForImageFilter, SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +0000929#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
commit-bot@chromium.org2a5cd602014-05-30 20:41:20 +0000930 flags |= kClipToLayer_SaveFlag;
reed@google.comb93ba452014-03-10 19:47:58 +0000931#endif
932
junov@chromium.orga907ac32012-02-24 21:54:07 +0000933 // do this before we create the layer. We don't call the public save() since
934 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -0800935 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +0000936
937 fDeviceCMDirty = true;
938
939 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000940 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed2ff1fce2014-12-11 07:07:37 -0800941 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000942 }
943
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000944 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
945 // the clipRectBounds() call above?
946 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -0800947 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000948 }
949
reed@google.comb55deeb2012-01-06 14:43:09 +0000950 // Kill the imagefilter if our device doesn't allow it
951 SkLazyPaint lazyP;
952 if (paint && paint->getImageFilter()) {
953 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000954 if (justForImageFilter) {
955 // early exit if the layer was just for the imageFilter
reed2ff1fce2014-12-11 07:07:37 -0800956 return;
reed@google.com8926b162012-03-23 15:36:36 +0000957 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000958 SkPaint* p = lazyP.set(*paint);
959 p->setImageFilter(NULL);
960 paint = p;
961 }
962 }
963
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000964 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
965 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
966 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000967
reedb2db8982014-11-13 12:41:02 -0800968 SkBaseDevice* device = this->getTopDevice();
969 if (NULL == device) {
970 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -0800971 return;
reed@google.com76dd2772012-01-05 21:15:07 +0000972 }
reedb2db8982014-11-13 12:41:02 -0800973
974 SkBaseDevice::Usage usage = SkBaseDevice::kSaveLayer_Usage;
975 if (paint && paint->getImageFilter()) {
976 usage = SkBaseDevice::kImageFilter_Usage;
977 }
978 device = device->onCreateCompatibleDevice(SkBaseDevice::CreateInfo(info, usage,
979 fProps.pixelGeometry()));
bungeman@google.come25c6842011-08-17 14:53:54 +0000980 if (NULL == device) {
981 SkDebugf("Unable to create device for layer.");
reed2ff1fce2014-12-11 07:07:37 -0800982 return;
bungeman@google.come25c6842011-08-17 14:53:54 +0000983 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000984
reed@google.com6f8f2922011-03-04 22:27:10 +0000985 device->setOrigin(ir.fLeft, ir.fTop);
reedd9544982014-09-09 18:46:22 -0700986 DeviceCM* layer = SkNEW_ARGS(DeviceCM,
987 (device, ir.fLeft, ir.fTop, paint, this, fConservativeRasterClip));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000988 device->unref();
989
990 layer->fNext = fMCRec->fTopLayer;
991 fMCRec->fLayer = layer;
992 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
993
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000994 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000995}
996
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000997int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
998 return this->saveLayerAlpha(bounds, alpha, kARGB_ClipLayer_SaveFlag);
999}
1000
reed@android.com8a1c16f2008-12-17 15:59:43 +00001001int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
1002 SaveFlags flags) {
1003 if (0xFF == alpha) {
1004 return this->saveLayer(bounds, NULL, flags);
1005 } else {
1006 SkPaint tmpPaint;
1007 tmpPaint.setAlpha(alpha);
1008 return this->saveLayer(bounds, &tmpPaint, flags);
1009 }
1010}
1011
reed@android.com8a1c16f2008-12-17 15:59:43 +00001012void SkCanvas::internalRestore() {
1013 SkASSERT(fMCStack.count() != 0);
1014
1015 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001016 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001017
Florin Malita5f6102d2014-06-30 10:13:28 -04001018 fClipStack.restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001019
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001020 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001021 DeviceCM* layer = fMCRec->fLayer; // may be null
1022 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
1023 fMCRec->fLayer = NULL;
1024
1025 // now do the normal restore()
1026 fMCRec->~MCRec(); // balanced in save()
1027 fMCStack.pop_back();
1028 fMCRec = (MCRec*)fMCStack.back();
1029
1030 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1031 since if we're being recorded, we don't want to record this (the
1032 recorder will have already recorded the restore).
1033 */
bsalomon49f085d2014-09-05 13:34:00 -07001034 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001035 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001036 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001037 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
1038 layer->fPaint);
1039 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001040 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +00001041
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001042 SkASSERT(fSaveLayerCount > 0);
1043 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001044 }
1045 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001046 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001047}
1048
reed@google.com7c202932011-12-14 18:48:05 +00001049bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001050 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +00001051}
1052
reed4a8126e2014-09-22 07:29:03 -07001053SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
1054 if (NULL == props) {
1055 props = &fProps;
1056 }
1057 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001058}
1059
reed4a8126e2014-09-22 07:29:03 -07001060SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001061 SkBaseDevice* dev = this->getDevice();
reed4a8126e2014-09-22 07:29:03 -07001062 return dev ? dev->newSurface(info, props) : NULL;
reed@google.com76f10a32014-02-05 15:32:21 +00001063}
1064
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001065SkImageInfo SkCanvas::imageInfo() const {
1066 SkBaseDevice* dev = this->getDevice();
1067 if (dev) {
1068 return dev->imageInfo();
1069 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001070 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001071 }
1072}
1073
1074const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
1075 return this->onPeekPixels(info, rowBytes);
1076}
1077
1078const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
1079 SkBaseDevice* dev = this->getDevice();
1080 return dev ? dev->peekPixels(info, rowBytes) : NULL;
1081}
1082
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001083void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
1084 void* pixels = this->onAccessTopLayerPixels(info, rowBytes);
1085 if (pixels && origin) {
1086 *origin = this->getTopDevice(false)->getOrigin();
1087 }
1088 return pixels;
reed@google.com9c135db2014-03-12 18:28:35 +00001089}
1090
1091void* SkCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
1092 SkBaseDevice* dev = this->getTopDevice();
1093 return dev ? dev->accessPixels(info, rowBytes) : NULL;
1094}
1095
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001096SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1097 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1098 if (NULL == fAddr) {
1099 fInfo = canvas->imageInfo();
reed84825042014-09-02 12:50:45 -07001100 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.tryAllocPixels(fInfo)) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001101 return; // failure, fAddr is NULL
1102 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001103 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1104 return; // failure, fAddr is NULL
1105 }
1106 fAddr = fBitmap.getPixels();
1107 fRowBytes = fBitmap.rowBytes();
1108 }
1109 SkASSERT(fAddr); // success
1110}
1111
1112bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1113 if (fAddr) {
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +00001114 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001115 } else {
1116 bitmap->reset();
1117 return false;
1118 }
1119}
1120
reed@android.com8a1c16f2008-12-17 15:59:43 +00001121/////////////////////////////////////////////////////////////////////////////
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001122void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001123 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001124 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001125 return;
1126 }
1127
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001128 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001129 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001130 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001131 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001132
1133 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001134
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001135 SkRect storage;
1136 const SkRect* bounds = NULL;
1137 if (paint && paint->canComputeFastBounds()) {
1138 bitmap.getBounds(&storage);
1139 matrix.mapRect(&storage);
1140 bounds = &paint->computeFastBounds(storage, &storage);
1141 }
1142
1143 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001144
1145 while (iter.next()) {
1146 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1147 }
1148
1149 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001150}
1151
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001152void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001153 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001154 SkPaint tmp;
1155 if (NULL == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001156 paint = &tmp;
1157 }
reed@google.com4b226022011-01-11 18:32:13 +00001158
reed@google.com8926b162012-03-23 15:36:36 +00001159 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001160 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001161 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001162 paint = &looper.paint();
1163 SkImageFilter* filter = paint->getImageFilter();
1164 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001165 if (filter && !dstDev->canHandleImageFilter(filter)) {
fmalita2d97bc12014-11-20 10:44:58 -08001166 SkDeviceImageFilterProxy proxy(dstDev, fProps);
reed@google.com76dd2772012-01-05 21:15:07 +00001167 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001168 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001169 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001170 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001171 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001172 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblancobe129b22014-08-08 07:14:35 -07001173 SkAutoTUnref<SkImageFilter::Cache> cache(dstDev->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001174 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001175 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001176 SkPaint tmpUnfiltered(*paint);
1177 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001178 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1179 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001180 }
1181 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001182 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001183 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001184 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001185 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001186}
1187
reed@google.com8926b162012-03-23 15:36:36 +00001188void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1189 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08001190 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawSprite()");
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001191 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001192 return;
1193 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001194 SkDEBUGCODE(bitmap.validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001195
reed@google.com8926b162012-03-23 15:36:36 +00001196 SkPaint tmp;
1197 if (NULL == paint) {
1198 paint = &tmp;
1199 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001200
reed@google.com8926b162012-03-23 15:36:36 +00001201 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001202
reed@google.com8926b162012-03-23 15:36:36 +00001203 while (iter.next()) {
1204 paint = &looper.paint();
1205 SkImageFilter* filter = paint->getImageFilter();
1206 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1207 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
fmalita2d97bc12014-11-20 10:44:58 -08001208 SkDeviceImageFilterProxy proxy(iter.fDevice, fProps);
reed@google.com8926b162012-03-23 15:36:36 +00001209 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001210 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001211 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001212 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
halcanaryf622a6c2014-10-24 12:54:53 -07001213 const SkIRect clipBounds = bitmap.bounds();
senorblancobe129b22014-08-08 07:14:35 -07001214 SkAutoTUnref<SkImageFilter::Cache> cache(iter.fDevice->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001215 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001216 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001217 SkPaint tmpUnfiltered(*paint);
1218 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001219 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001220 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001221 }
1222 } else {
1223 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1224 }
1225 }
1226 LOOPER_END
1227}
1228
reed@android.com8a1c16f2008-12-17 15:59:43 +00001229/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001230void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001231 SkMatrix m;
1232 m.setTranslate(dx, dy);
1233 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001234}
1235
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001236void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001237 SkMatrix m;
1238 m.setScale(sx, sy);
1239 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001240}
1241
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001242void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001243 SkMatrix m;
1244 m.setRotate(degrees);
1245 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001246}
1247
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001248void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001249 SkMatrix m;
1250 m.setSkew(sx, sy);
1251 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001252}
1253
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001254void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001255 if (matrix.isIdentity()) {
1256 return;
1257 }
1258
reed2ff1fce2014-12-11 07:07:37 -08001259 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001260 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001261 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001262 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001263
1264 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001265}
1266
reed@android.com8a1c16f2008-12-17 15:59:43 +00001267void SkCanvas::setMatrix(const SkMatrix& matrix) {
reed2ff1fce2014-12-11 07:07:37 -08001268 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001269 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001270 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001271 fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001272 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001273}
1274
reed@android.com8a1c16f2008-12-17 15:59:43 +00001275void SkCanvas::resetMatrix() {
1276 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001277
reed@android.com8a1c16f2008-12-17 15:59:43 +00001278 matrix.reset();
1279 this->setMatrix(matrix);
1280}
1281
1282//////////////////////////////////////////////////////////////////////////////
1283
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001284void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001285 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001286 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1287 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001288}
1289
1290void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001291#ifdef SK_ENABLE_CLIP_QUICKREJECT
1292 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001293 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001294 return false;
1295 }
1296
reed@google.com3b3e8952012-08-16 20:53:31 +00001297 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001298 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001299 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001300
1301 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001302 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001303 }
1304 }
1305#endif
1306
reed@google.com5c3d1472011-02-22 19:12:23 +00001307 AutoValidateClip avc(this);
1308
reed@android.com8a1c16f2008-12-17 15:59:43 +00001309 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001310 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001311 if (!fAllowSoftClip) {
1312 edgeStyle = kHard_ClipEdgeStyle;
1313 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001314
reed1f836ee2014-07-07 07:49:34 -07001315 if (fMCRec->fMatrix.rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001316 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001317 // the matrix. This means we don't have to a) make a path, and b) tell
1318 // the region code to scan-convert the path, only to discover that it
1319 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001320 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001321
reed1f836ee2014-07-07 07:49:34 -07001322 fMCRec->fMatrix.mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001323 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reedd9544982014-09-09 18:46:22 -07001324 fMCRec->fRasterClip.op(r, this->getBaseLayerSize(), op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001325 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001326 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001327 // and clip against that, since it can handle any matrix. However, to
1328 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1329 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001330 SkPath path;
1331
1332 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001333 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001334 }
1335}
1336
reed73e714e2014-09-04 09:02:23 -07001337static void rasterclip_path(SkRasterClip* rc, const SkCanvas* canvas, const SkPath& devPath,
1338 SkRegion::Op op, bool doAA) {
reedd64c9482014-09-05 17:37:38 -07001339 rc->op(devPath, canvas->getBaseLayerSize(), op, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001340}
1341
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001342void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001343 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001344 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001345 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001346 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1347 } else {
1348 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001349 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001350}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001351
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001352void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001353 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001354 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001355 AutoValidateClip avc(this);
1356
1357 fDeviceCMDirty = true;
1358 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001359 if (!fAllowSoftClip) {
1360 edgeStyle = kHard_ClipEdgeStyle;
1361 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001362
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001363 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001364
1365 SkPath devPath;
1366 devPath.addRRect(transformedRRect);
1367
reed73e714e2014-09-04 09:02:23 -07001368 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001369 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001370 }
1371
1372 SkPath path;
1373 path.addRRect(rrect);
1374 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001375 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001376}
1377
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001378void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001379 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001380 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1381 SkRect r;
1382 if (!path.isInverseFillType() && path.isRect(&r)) {
1383 this->onClipRect(r, op, edgeStyle);
1384 } else {
1385 this->onClipPath(path, op, edgeStyle);
1386 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001387}
1388
1389void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001390#ifdef SK_ENABLE_CLIP_QUICKREJECT
1391 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001392 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001393 return false;
1394 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001395
reed@google.com3b3e8952012-08-16 20:53:31 +00001396 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001397 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001398 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001399
reed@google.comda17f752012-08-16 18:27:05 +00001400 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001401 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001402 }
1403 }
1404#endif
1405
reed@google.com5c3d1472011-02-22 19:12:23 +00001406 AutoValidateClip avc(this);
1407
reed@android.com8a1c16f2008-12-17 15:59:43 +00001408 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001409 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001410 if (!fAllowSoftClip) {
1411 edgeStyle = kHard_ClipEdgeStyle;
1412 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001413
1414 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001415 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001416
reed@google.comfe701122011-11-08 19:41:23 +00001417 // Check if the transfomation, or the original path itself
1418 // made us empty. Note this can also happen if we contained NaN
1419 // values. computing the bounds detects this, and will set our
1420 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1421 if (devPath.getBounds().isEmpty()) {
1422 // resetting the path will remove any NaN or other wanky values
1423 // that might upset our scan converter.
1424 devPath.reset();
1425 }
1426
reed@google.com5c3d1472011-02-22 19:12:23 +00001427 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001428 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001429
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001430 if (fAllowSimplifyClip) {
1431 devPath.reset();
1432 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1433 const SkClipStack* clipStack = getClipStack();
1434 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1435 const SkClipStack::Element* element;
1436 while ((element = iter.next())) {
1437 SkClipStack::Element::Type type = element->getType();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001438 SkPath operand;
commit-bot@chromium.org2a67e122014-05-19 13:53:10 +00001439 if (type != SkClipStack::Element::kEmpty_Type) {
1440 element->asPath(&operand);
1441 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001442 SkRegion::Op elementOp = element->getOp();
1443 if (elementOp == SkRegion::kReplace_Op) {
1444 devPath = operand;
1445 } else {
1446 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1447 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001448 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1449 // perhaps we need an API change to avoid this sort of mixed-signals about
1450 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001451 if (element->isAA()) {
1452 edgeStyle = kSoft_ClipEdgeStyle;
1453 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001454 }
1455 op = SkRegion::kReplace_Op;
1456 }
1457
reed73e714e2014-09-04 09:02:23 -07001458 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001459}
1460
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001461void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed2ff1fce2014-12-11 07:07:37 -08001462 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001463 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001464}
1465
1466void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001467 AutoValidateClip avc(this);
1468
reed@android.com8a1c16f2008-12-17 15:59:43 +00001469 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001470 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001471
reed@google.com5c3d1472011-02-22 19:12:23 +00001472 // todo: signal fClipStack that we have a region, and therefore (I guess)
1473 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001474 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001475
reed1f836ee2014-07-07 07:49:34 -07001476 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001477}
1478
reed@google.com819c9212011-02-23 18:56:55 +00001479#ifdef SK_DEBUG
1480void SkCanvas::validateClip() const {
1481 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001482 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001483 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001484 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001485 return;
1486 }
1487
reed@google.com819c9212011-02-23 18:56:55 +00001488 SkIRect ir;
1489 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001490 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001491
robertphillips@google.com80214e22012-07-20 15:33:18 +00001492 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001493 const SkClipStack::Element* element;
1494 while ((element = iter.next()) != NULL) {
1495 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001496 case SkClipStack::Element::kRect_Type:
1497 element->getRect().round(&ir);
1498 tmpClip.op(ir, element->getOp());
1499 break;
1500 case SkClipStack::Element::kEmpty_Type:
1501 tmpClip.setEmpty();
1502 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001503 default: {
1504 SkPath path;
1505 element->asPath(&path);
reed73e714e2014-09-04 09:02:23 -07001506 rasterclip_path(&tmpClip, this, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001507 break;
1508 }
reed@google.com819c9212011-02-23 18:56:55 +00001509 }
1510 }
reed@google.com819c9212011-02-23 18:56:55 +00001511}
1512#endif
1513
reed@google.com90c07ea2012-04-13 13:50:27 +00001514void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001515 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001516 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001517
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001518 while ((element = iter.next()) != NULL) {
fmalitac3b589a2014-06-05 12:40:07 -07001519 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001520 }
1521}
1522
reed@google.com5c3d1472011-02-22 19:12:23 +00001523///////////////////////////////////////////////////////////////////////////////
1524
reed@google.com754de5f2014-02-24 19:38:20 +00001525bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001526 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001527}
1528
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001529bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001530 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001531}
1532
reed@google.com3b3e8952012-08-16 20:53:31 +00001533bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001534 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001535 return true;
1536
reed1f836ee2014-07-07 07:49:34 -07001537 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001538 return true;
1539 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001540
reed1f836ee2014-07-07 07:49:34 -07001541 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001542 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001543 fMCRec->fMatrix.mapRect(&dst, rect);
reedb07a94f2014-11-19 05:03:18 -08001544 return !SkIRect::Intersects(dst.roundOut(), fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001545 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001546 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001547
reed@android.coma380ae42009-07-21 01:17:02 +00001548 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001549 // TODO: should we use | instead, or compare all 4 at once?
1550 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001551 return true;
1552 }
reed@google.comc0784db2013-12-13 21:16:12 +00001553 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001554 return true;
1555 }
1556 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001557 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001558}
1559
reed@google.com3b3e8952012-08-16 20:53:31 +00001560bool SkCanvas::quickReject(const SkPath& path) const {
1561 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001562}
1563
reed@google.com3b3e8952012-08-16 20:53:31 +00001564bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001565 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001566 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001567 return false;
1568 }
1569
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001570 SkMatrix inverse;
1571 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001572 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001573 if (bounds) {
1574 bounds->setEmpty();
1575 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001576 return false;
1577 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001578
bsalomon49f085d2014-09-05 13:34:00 -07001579 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001580 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001581 // adjust it outwards in case we are antialiasing
1582 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001583
reed@google.com8f4d2302013-12-17 16:44:46 +00001584 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1585 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001586 inverse.mapRect(bounds, r);
1587 }
1588 return true;
1589}
1590
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001591bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001592 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001593 if (clip.isEmpty()) {
1594 if (bounds) {
1595 bounds->setEmpty();
1596 }
1597 return false;
1598 }
1599
bsalomon49f085d2014-09-05 13:34:00 -07001600 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001601 *bounds = clip.getBounds();
1602 }
1603 return true;
1604}
1605
reed@android.com8a1c16f2008-12-17 15:59:43 +00001606const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001607 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001608}
1609
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001610const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001611 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001612}
1613
reed@google.com9c135db2014-03-12 18:28:35 +00001614GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1615 SkBaseDevice* dev = this->getTopDevice();
1616 return dev ? dev->accessRenderTarget() : NULL;
1617}
1618
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001619GrContext* SkCanvas::getGrContext() {
1620#if SK_SUPPORT_GPU
1621 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07001622 if (device) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001623 GrRenderTarget* renderTarget = device->accessRenderTarget();
bsalomon49f085d2014-09-05 13:34:00 -07001624 if (renderTarget) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001625 return renderTarget->getContext();
1626 }
1627 }
1628#endif
1629
1630 return NULL;
1631
1632}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001633
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001634void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1635 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001636 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001637 if (outer.isEmpty()) {
1638 return;
1639 }
1640 if (inner.isEmpty()) {
1641 this->drawRRect(outer, paint);
1642 return;
1643 }
1644
1645 // We don't have this method (yet), but technically this is what we should
1646 // be able to assert...
1647 // SkASSERT(outer.contains(inner));
1648 //
1649 // For now at least check for containment of bounds
1650 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1651
1652 this->onDrawDRRect(outer, inner, paint);
1653}
1654
reed@android.com8a1c16f2008-12-17 15:59:43 +00001655//////////////////////////////////////////////////////////////////////////////
1656// These are the virtual drawing methods
1657//////////////////////////////////////////////////////////////////////////////
1658
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001659void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07001660 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001661 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1662 }
1663}
1664
reed@android.com8a1c16f2008-12-17 15:59:43 +00001665void SkCanvas::drawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001666 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001667 this->internalDrawPaint(paint);
1668}
1669
1670void SkCanvas::internalDrawPaint(const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001671 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001672
1673 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001674 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001675 }
1676
reed@google.com4e2b3d32011-04-07 14:18:59 +00001677 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001678}
1679
1680void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1681 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001682 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001683 if ((long)count <= 0) {
1684 return;
1685 }
1686
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001687 SkRect r, storage;
1688 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001689 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001690 // special-case 2 points (common for drawing a single line)
1691 if (2 == count) {
1692 r.set(pts[0], pts[1]);
1693 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001694 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001695 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001696 bounds = &paint.computeFastStrokeBounds(r, &storage);
1697 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001698 return;
1699 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001700 }
reed@google.coma584aed2012-05-16 14:06:02 +00001701
reed@android.com8a1c16f2008-12-17 15:59:43 +00001702 SkASSERT(pts != NULL);
1703
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001704 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001705
reed@android.com8a1c16f2008-12-17 15:59:43 +00001706 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001707 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001708 }
reed@google.com4b226022011-01-11 18:32:13 +00001709
reed@google.com4e2b3d32011-04-07 14:18:59 +00001710 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001711}
1712
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001713void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001714 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001715 SkRect storage;
1716 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001717 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001718 bounds = &paint.computeFastBounds(r, &storage);
1719 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001720 return;
1721 }
1722 }
reed@google.com4b226022011-01-11 18:32:13 +00001723
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001724 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001725
1726 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001727 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001728 }
1729
reed@google.com4e2b3d32011-04-07 14:18:59 +00001730 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001731}
1732
reed@google.com4ed0fb72012-12-12 20:48:18 +00001733void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001734 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001735 SkRect storage;
1736 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001737 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001738 bounds = &paint.computeFastBounds(oval, &storage);
1739 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001740 return;
1741 }
1742 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001743
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001744 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001745
1746 while (iter.next()) {
1747 iter.fDevice->drawOval(iter, oval, looper.paint());
1748 }
1749
1750 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001751}
1752
1753void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001754 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001755 SkRect storage;
1756 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001757 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001758 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1759 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001760 return;
1761 }
1762 }
1763
1764 if (rrect.isRect()) {
1765 // call the non-virtual version
1766 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001767 return;
1768 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001769 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001770 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1771 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001772 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001773
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001774 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001775
1776 while (iter.next()) {
1777 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1778 }
1779
1780 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001781}
1782
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001783void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1784 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001785 SkRect storage;
1786 const SkRect* bounds = NULL;
1787 if (paint.canComputeFastBounds()) {
1788 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1789 if (this->quickReject(*bounds)) {
1790 return;
1791 }
1792 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001793
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001794 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001795
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001796 while (iter.next()) {
1797 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1798 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001799
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001800 LOOPER_END
1801}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001802
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001803void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001804 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00001805 if (!path.isFinite()) {
1806 return;
1807 }
1808
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001809 SkRect storage;
1810 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001811 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001812 const SkRect& pathBounds = path.getBounds();
1813 bounds = &paint.computeFastBounds(pathBounds, &storage);
1814 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001815 return;
1816 }
1817 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00001818
1819 const SkRect& r = path.getBounds();
1820 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00001821 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001822 this->internalDrawPaint(paint);
1823 }
1824 return;
1825 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001826
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001827 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001828
1829 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001830 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001831 }
1832
reed@google.com4e2b3d32011-04-07 14:18:59 +00001833 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001834}
1835
piotaixrb5fae932014-09-24 13:03:30 -07001836void SkCanvas::drawImage(const SkImage* image, SkScalar left, SkScalar top,
1837 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08001838 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
piotaixrb5fae932014-09-24 13:03:30 -07001839 image->draw(this, left, top, paint);
1840}
1841
1842void SkCanvas::drawImageRect(const SkImage* image, const SkRect* src,
1843 const SkRect& dst,
1844 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08001845 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
piotaixr5ceff912014-09-26 07:36:26 -07001846 image->drawRect(this, src, dst, paint);
piotaixrb5fae932014-09-24 13:03:30 -07001847}
1848
reed@android.com8a1c16f2008-12-17 15:59:43 +00001849void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1850 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08001851 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00001852 SkDEBUGCODE(bitmap.validate();)
1853
reed@google.com3d608122011-11-21 15:16:16 +00001854 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001855 SkRect bounds = {
1856 x, y,
1857 x + SkIntToScalar(bitmap.width()),
1858 y + SkIntToScalar(bitmap.height())
1859 };
1860 if (paint) {
1861 (void)paint->computeFastBounds(bounds, &bounds);
1862 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001863 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001864 return;
1865 }
1866 }
reed@google.com4b226022011-01-11 18:32:13 +00001867
reed@android.com8a1c16f2008-12-17 15:59:43 +00001868 SkMatrix matrix;
1869 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001870 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001871}
1872
reed@google.com9987ec32011-09-07 11:57:52 +00001873// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001874void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001875 const SkRect& dst, const SkPaint* paint,
1876 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001877 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001878 return;
1879 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001880
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001881 SkRect storage;
1882 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00001883 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001884 if (paint) {
1885 bounds = &paint->computeFastBounds(dst, &storage);
1886 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001887 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001888 return;
1889 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001890 }
reed@google.com3d608122011-11-21 15:16:16 +00001891
reed@google.com33535f32012-09-25 15:37:50 +00001892 SkLazyPaint lazy;
1893 if (NULL == paint) {
1894 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001895 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001896
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001897 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001898
reed@google.com33535f32012-09-25 15:37:50 +00001899 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001900 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001901 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001902
reed@google.com33535f32012-09-25 15:37:50 +00001903 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001904}
1905
reed@google.com71121732012-09-18 15:14:33 +00001906void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001907 const SkRect& dst, const SkPaint* paint,
1908 DrawBitmapRectFlags flags) {
danakj9881d632014-11-26 12:41:06 -08001909 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00001910 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001911 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001912}
1913
reed@google.com9987ec32011-09-07 11:57:52 +00001914void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1915 const SkIRect& center, const SkRect& dst,
1916 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001917 if (bitmap.drawsNothing()) {
1918 return;
1919 }
reed@google.com3d608122011-11-21 15:16:16 +00001920 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001921 SkRect storage;
1922 const SkRect* bounds = &dst;
1923 if (paint) {
1924 bounds = &paint->computeFastBounds(dst, &storage);
1925 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001926 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001927 return;
1928 }
1929 }
1930
reed@google.com9987ec32011-09-07 11:57:52 +00001931 const int32_t w = bitmap.width();
1932 const int32_t h = bitmap.height();
1933
1934 SkIRect c = center;
1935 // pin center to the bounds of the bitmap
1936 c.fLeft = SkMax32(0, center.fLeft);
1937 c.fTop = SkMax32(0, center.fTop);
1938 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1939 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1940
reed@google.com71121732012-09-18 15:14:33 +00001941 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001942 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00001943 };
1944 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001945 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00001946 };
reed@google.com9987ec32011-09-07 11:57:52 +00001947 SkScalar dstX[4] = {
1948 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1949 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1950 };
1951 SkScalar dstY[4] = {
1952 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1953 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1954 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001955
reed@google.com9987ec32011-09-07 11:57:52 +00001956 if (dstX[1] > dstX[2]) {
1957 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1958 dstX[2] = dstX[1];
1959 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001960
reed@google.com9987ec32011-09-07 11:57:52 +00001961 if (dstY[1] > dstY[2]) {
1962 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1963 dstY[2] = dstY[1];
1964 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001965
reed@google.com9987ec32011-09-07 11:57:52 +00001966 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00001967 SkRect s, d;
1968
reed@google.com9987ec32011-09-07 11:57:52 +00001969 s.fTop = srcY[y];
1970 s.fBottom = srcY[y+1];
1971 d.fTop = dstY[y];
1972 d.fBottom = dstY[y+1];
1973 for (int x = 0; x < 3; x++) {
1974 s.fLeft = srcX[x];
1975 s.fRight = srcX[x+1];
1976 d.fLeft = dstX[x];
1977 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001978 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00001979 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00001980 }
1981 }
1982}
1983
1984void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1985 const SkRect& dst, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08001986 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00001987 SkDEBUGCODE(bitmap.validate();)
1988
1989 // Need a device entry-point, so gpu can use a mesh
1990 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1991}
1992
reed@google.comf67e4cf2011-03-15 20:56:58 +00001993class SkDeviceFilteredPaint {
1994public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001995 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08001996 uint32_t filteredFlags = device->filterTextFlags(paint);
1997 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001998 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08001999 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002000 fPaint = newPaint;
2001 } else {
2002 fPaint = &paint;
2003 }
2004 }
2005
reed@google.comf67e4cf2011-03-15 20:56:58 +00002006 const SkPaint& paint() const { return *fPaint; }
2007
2008private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002009 const SkPaint* fPaint;
2010 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002011};
2012
bungeman@google.com52c748b2011-08-22 21:30:43 +00002013void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2014 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002015 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002016 draw.fDevice->drawRect(draw, r, paint);
2017 } else {
2018 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002019 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002020 draw.fDevice->drawRect(draw, r, p);
2021 }
2022}
2023
2024void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2025 const char text[], size_t byteLength,
2026 SkScalar x, SkScalar y) {
2027 SkASSERT(byteLength == 0 || text != NULL);
2028
2029 // nothing to draw
2030 if (text == NULL || byteLength == 0 ||
2031 draw.fClip->isEmpty() ||
2032 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2033 return;
2034 }
2035
2036 SkScalar width = 0;
2037 SkPoint start;
2038
2039 start.set(0, 0); // to avoid warning
2040 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2041 SkPaint::kStrikeThruText_Flag)) {
2042 width = paint.measureText(text, byteLength);
2043
2044 SkScalar offsetX = 0;
2045 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2046 offsetX = SkScalarHalf(width);
2047 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2048 offsetX = width;
2049 }
2050 start.set(x - offsetX, y);
2051 }
2052
2053 if (0 == width) {
2054 return;
2055 }
2056
2057 uint32_t flags = paint.getFlags();
2058
2059 if (flags & (SkPaint::kUnderlineText_Flag |
2060 SkPaint::kStrikeThruText_Flag)) {
2061 SkScalar textSize = paint.getTextSize();
2062 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2063 SkRect r;
2064
2065 r.fLeft = start.fX;
2066 r.fRight = start.fX + width;
2067
2068 if (flags & SkPaint::kUnderlineText_Flag) {
2069 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2070 start.fY);
2071 r.fTop = offset;
2072 r.fBottom = offset + height;
2073 DrawRect(draw, paint, r, textSize);
2074 }
2075 if (flags & SkPaint::kStrikeThruText_Flag) {
2076 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2077 start.fY);
2078 r.fTop = offset;
2079 r.fBottom = offset + height;
2080 DrawRect(draw, paint, r, textSize);
2081 }
2082 }
2083}
2084
reed@google.come0d9ce82014-04-23 04:00:17 +00002085void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2086 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002087 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002088
2089 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002090 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002091 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002092 DrawTextDecorations(iter, dfp.paint(),
2093 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002094 }
2095
reed@google.com4e2b3d32011-04-07 14:18:59 +00002096 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002097}
2098
reed@google.come0d9ce82014-04-23 04:00:17 +00002099void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2100 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002101 SkPoint textOffset = SkPoint::Make(0, 0);
2102
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002103 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002104
reed@android.com8a1c16f2008-12-17 15:59:43 +00002105 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002106 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002107 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002108 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002109 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002110
reed@google.com4e2b3d32011-04-07 14:18:59 +00002111 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002112}
2113
reed@google.come0d9ce82014-04-23 04:00:17 +00002114void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2115 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002116
2117 SkPoint textOffset = SkPoint::Make(0, constY);
2118
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002119 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002120
reed@android.com8a1c16f2008-12-17 15:59:43 +00002121 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002122 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002123 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002124 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002125 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002126
reed@google.com4e2b3d32011-04-07 14:18:59 +00002127 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002128}
2129
reed@google.come0d9ce82014-04-23 04:00:17 +00002130void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2131 const SkMatrix* matrix, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002132 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002133
reed@android.com8a1c16f2008-12-17 15:59:43 +00002134 while (iter.next()) {
2135 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002136 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002137 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002138
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002139 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002140}
2141
fmalita00d5c2c2014-08-21 08:53:26 -07002142void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2143 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002144
fmalita19653d12014-10-16 11:53:30 -07002145 if (paint.canComputeFastBounds()) {
fmalita7ba7aa72014-08-29 09:46:36 -07002146 SkRect storage;
2147
2148 if (this->quickReject(paint.computeFastBounds(blob->bounds().makeOffset(x, y), &storage))) {
2149 return;
2150 }
2151 }
2152
fmalitaaa1b9122014-08-28 14:32:24 -07002153 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
fmalita00d5c2c2014-08-21 08:53:26 -07002154
fmalitaaa1b9122014-08-28 14:32:24 -07002155 while (iter.next()) {
2156 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
2157 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint());
fmalita00d5c2c2014-08-21 08:53:26 -07002158 }
2159
fmalitaaa1b9122014-08-28 14:32:24 -07002160 LOOPER_END
fmalita00d5c2c2014-08-21 08:53:26 -07002161}
2162
reed@google.come0d9ce82014-04-23 04:00:17 +00002163// These will become non-virtual, so they always call the (virtual) onDraw... method
2164void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2165 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002166 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002167 this->onDrawText(text, byteLength, x, y, paint);
2168}
2169void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2170 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002171 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002172 this->onDrawPosText(text, byteLength, pos, paint);
2173}
2174void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2175 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002176 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002177 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2178}
2179void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2180 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002181 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002182 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2183}
fmalita00d5c2c2014-08-21 08:53:26 -07002184void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2185 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002186 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
bsalomon49f085d2014-09-05 13:34:00 -07002187 if (blob) {
fmalita00d5c2c2014-08-21 08:53:26 -07002188 this->onDrawTextBlob(blob, x, y, paint);
2189 }
2190}
reed@google.come0d9ce82014-04-23 04:00:17 +00002191
reed@android.com8a1c16f2008-12-17 15:59:43 +00002192void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2193 const SkPoint verts[], const SkPoint texs[],
2194 const SkColor colors[], SkXfermode* xmode,
2195 const uint16_t indices[], int indexCount,
2196 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002197 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002198 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002199
reed@android.com8a1c16f2008-12-17 15:59:43 +00002200 while (iter.next()) {
2201 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002202 colors, xmode, indices, indexCount,
2203 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002204 }
reed@google.com4b226022011-01-11 18:32:13 +00002205
reed@google.com4e2b3d32011-04-07 14:18:59 +00002206 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002207}
2208
dandovb3c9d1c2014-08-12 08:34:29 -07002209void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2210 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002211 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
dandovb3c9d1c2014-08-12 08:34:29 -07002212 if (NULL == cubics) {
2213 return;
2214 }
mtklein6cfa73a2014-08-13 13:33:49 -07002215
dandovecfff212014-08-04 10:02:00 -07002216 // Since a patch is always within the convex hull of the control points, we discard it when its
2217 // bounding rectangle is completely outside the current clip.
2218 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002219 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002220 if (this->quickReject(bounds)) {
2221 return;
2222 }
mtklein6cfa73a2014-08-13 13:33:49 -07002223
dandovb3c9d1c2014-08-12 08:34:29 -07002224 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2225}
2226
2227void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2228 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2229
dandovecfff212014-08-04 10:02:00 -07002230 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
mtklein6cfa73a2014-08-13 13:33:49 -07002231
dandovecfff212014-08-04 10:02:00 -07002232 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002233 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002234 }
mtklein6cfa73a2014-08-13 13:33:49 -07002235
dandovecfff212014-08-04 10:02:00 -07002236 LOOPER_END
2237}
2238
reed6a070dc2014-11-11 19:36:09 -08002239void SkCanvas::EXPERIMENTAL_drawDrawable(SkCanvasDrawable* dr) {
reed6be2aa92014-11-18 11:08:05 -08002240 if (dr && !this->quickReject(dr->getBounds())) {
2241 this->onDrawDrawable(dr);
reed6a070dc2014-11-11 19:36:09 -08002242 }
2243}
2244
2245void SkCanvas::onDrawDrawable(SkCanvasDrawable* dr) {
2246 dr->draw(this);
2247}
2248
reed@android.com8a1c16f2008-12-17 15:59:43 +00002249//////////////////////////////////////////////////////////////////////////////
2250// These methods are NOT virtual, and therefore must call back into virtual
2251// methods, rather than actually drawing themselves.
2252//////////////////////////////////////////////////////////////////////////////
2253
2254void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002255 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002256 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002257 SkPaint paint;
2258
2259 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002260 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002261 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002262 }
2263 this->drawPaint(paint);
2264}
2265
reed@android.com845fdac2009-06-23 03:01:32 +00002266void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002267 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002268 SkPaint paint;
2269
2270 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002271 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002272 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002273 }
2274 this->drawPaint(paint);
2275}
2276
2277void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002278 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002279 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002280
reed@android.com8a1c16f2008-12-17 15:59:43 +00002281 pt.set(x, y);
2282 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2283}
2284
2285void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002286 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002287 SkPoint pt;
2288 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002289
reed@android.com8a1c16f2008-12-17 15:59:43 +00002290 pt.set(x, y);
2291 paint.setColor(color);
2292 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2293}
2294
2295void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2296 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002297 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002298 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002299
reed@android.com8a1c16f2008-12-17 15:59:43 +00002300 pts[0].set(x0, y0);
2301 pts[1].set(x1, y1);
2302 this->drawPoints(kLines_PointMode, 2, pts, paint);
2303}
2304
2305void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2306 SkScalar right, SkScalar bottom,
2307 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002308 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002309 SkRect r;
2310
2311 r.set(left, top, right, bottom);
2312 this->drawRect(r, paint);
2313}
2314
2315void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2316 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002317 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002318 if (radius < 0) {
2319 radius = 0;
2320 }
2321
2322 SkRect r;
2323 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002324 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002325}
2326
2327void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2328 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002329 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002330 if (rx > 0 && ry > 0) {
2331 if (paint.canComputeFastBounds()) {
2332 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002333 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002334 return;
2335 }
2336 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002337 SkRRect rrect;
2338 rrect.setRectXY(r, rx, ry);
2339 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002340 } else {
2341 this->drawRect(r, paint);
2342 }
2343}
2344
reed@android.com8a1c16f2008-12-17 15:59:43 +00002345void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2346 SkScalar sweepAngle, bool useCenter,
2347 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002348 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002349 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2350 this->drawOval(oval, paint);
2351 } else {
2352 SkPath path;
2353 if (useCenter) {
2354 path.moveTo(oval.centerX(), oval.centerY());
2355 }
2356 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2357 if (useCenter) {
2358 path.close();
2359 }
2360 this->drawPath(path, paint);
2361 }
2362}
2363
2364void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2365 const SkPath& path, SkScalar hOffset,
2366 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002367 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002368 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002369
reed@android.com8a1c16f2008-12-17 15:59:43 +00002370 matrix.setTranslate(hOffset, vOffset);
2371 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2372}
2373
reed@android.comf76bacf2009-05-13 14:00:33 +00002374///////////////////////////////////////////////////////////////////////////////
robertphillips9b14f262014-06-04 05:40:44 -07002375void SkCanvas::drawPicture(const SkPicture* picture) {
danakj9881d632014-11-26 12:41:06 -08002376 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
bsalomon49f085d2014-09-05 13:34:00 -07002377 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002378 this->onDrawPicture(picture, NULL, NULL);
robertphillips9b14f262014-06-04 05:40:44 -07002379 }
2380}
2381
reedd5fa1a42014-08-09 11:08:05 -07002382void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002383 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture(SkMatrix, SkPaint)");
bsalomon49f085d2014-09-05 13:34:00 -07002384 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002385 if (matrix && matrix->isIdentity()) {
2386 matrix = NULL;
2387 }
2388 this->onDrawPicture(picture, matrix, paint);
2389 }
2390}
robertphillips9b14f262014-06-04 05:40:44 -07002391
reedd5fa1a42014-08-09 11:08:05 -07002392void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2393 const SkPaint* paint) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002394 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002395 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002396 // Canvas has to first give the device the opportunity to render
2397 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002398 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002399 return; // the device has rendered the entire picture
2400 }
2401 }
2402
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002403 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
reedd5fa1a42014-08-09 11:08:05 -07002404
robertphillipsc5ba71d2014-09-04 08:42:50 -07002405 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002406}
2407
reed@android.com8a1c16f2008-12-17 15:59:43 +00002408///////////////////////////////////////////////////////////////////////////////
2409///////////////////////////////////////////////////////////////////////////////
2410
2411SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002412 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002413
2414 SkASSERT(canvas);
2415
2416 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2417 fDone = !fImpl->next();
2418}
2419
2420SkCanvas::LayerIter::~LayerIter() {
2421 fImpl->~SkDrawIter();
2422}
2423
2424void SkCanvas::LayerIter::next() {
2425 fDone = !fImpl->next();
2426}
2427
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002428SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002429 return fImpl->getDevice();
2430}
2431
2432const SkMatrix& SkCanvas::LayerIter::matrix() const {
2433 return fImpl->getMatrix();
2434}
2435
2436const SkPaint& SkCanvas::LayerIter::paint() const {
2437 const SkPaint* paint = fImpl->getPaint();
2438 if (NULL == paint) {
2439 paint = &fDefaultPaint;
2440 }
2441 return *paint;
2442}
2443
2444const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2445int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2446int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002447
2448///////////////////////////////////////////////////////////////////////////////
2449
fmalitac3b589a2014-06-05 12:40:07 -07002450SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002451
2452///////////////////////////////////////////////////////////////////////////////
2453
2454static bool supported_for_raster_canvas(const SkImageInfo& info) {
2455 switch (info.alphaType()) {
2456 case kPremul_SkAlphaType:
2457 case kOpaque_SkAlphaType:
2458 break;
2459 default:
2460 return false;
2461 }
2462
2463 switch (info.colorType()) {
2464 case kAlpha_8_SkColorType:
2465 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002466 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002467 break;
2468 default:
2469 return false;
2470 }
2471
2472 return true;
2473}
2474
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002475SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2476 if (!supported_for_raster_canvas(info)) {
2477 return NULL;
2478 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002479
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002480 SkBitmap bitmap;
2481 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2482 return NULL;
2483 }
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002484 return SkNEW_ARGS(SkCanvas, (bitmap));
2485}
reedd5fa1a42014-08-09 11:08:05 -07002486
2487///////////////////////////////////////////////////////////////////////////////
2488
2489SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002490 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07002491 : fCanvas(canvas)
2492 , fSaveCount(canvas->getSaveCount())
2493{
bsalomon49f085d2014-09-05 13:34:00 -07002494 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002495 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07002496 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002497 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07002498 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002499 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07002500 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002501 canvas->save();
2502 }
mtklein6cfa73a2014-08-13 13:33:49 -07002503
bsalomon49f085d2014-09-05 13:34:00 -07002504 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002505 canvas->concat(*matrix);
2506 }
2507}
2508
2509SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
2510 fCanvas->restoreToCount(fSaveCount);
2511}