blob: c7fb2dd47fd0889990e98192c04e06f8b5129b1d [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
reedd990e2f2014-12-22 11:58:30 -080037static bool gIgnoreSaveLayerBounds;
38void SkCanvas::Internal_Private_SetIgnoreSaveLayerBounds(bool ignore) {
39 gIgnoreSaveLayerBounds = ignore;
40}
41bool SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds() {
42 return gIgnoreSaveLayerBounds;
43}
44
reed@google.comda17f752012-08-16 18:27:05 +000045// experimental for faster tiled drawing...
46//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000047
reed@android.com8a1c16f2008-12-17 15:59:43 +000048//#define SK_TRACE_SAVERESTORE
49
50#ifdef SK_TRACE_SAVERESTORE
51 static int gLayerCounter;
52 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
53 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
54
55 static int gRecCounter;
56 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
57 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
58
59 static int gCanvasCounter;
60 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
61 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
62#else
63 #define inc_layer()
64 #define dec_layer()
65 #define inc_rec()
66 #define dec_rec()
67 #define inc_canvas()
68 #define dec_canvas()
69#endif
70
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000071typedef SkTLazy<SkPaint> SkLazyPaint;
72
reed@google.com97af1a62012-08-28 12:19:02 +000073void SkCanvas::predrawNotify() {
74 if (fSurfaceBase) {
commit-bot@chromium.orgc4c98702013-04-22 14:28:01 +000075 fSurfaceBase->aboutToDraw(SkSurface::kRetain_ContentChangeMode);
reed@google.com97af1a62012-08-28 12:19:02 +000076 }
77}
78
reed@android.com8a1c16f2008-12-17 15:59:43 +000079///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000080
reed4a8126e2014-09-22 07:29:03 -070081static uint32_t filter_paint_flags(const SkSurfaceProps& props, uint32_t flags) {
82 const uint32_t propFlags = props.flags();
83 if (propFlags & SkSurfaceProps::kDisallowDither_Flag) {
84 flags &= ~SkPaint::kDither_Flag;
85 }
86 if (propFlags & SkSurfaceProps::kDisallowAntiAlias_Flag) {
87 flags &= ~SkPaint::kAntiAlias_Flag;
88 }
89 return flags;
90}
91
92///////////////////////////////////////////////////////////////////////////////
93
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000094/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +000095 The clip/matrix/proc are fields that reflect the top of the save/restore
96 stack. Whenever the canvas changes, it marks a dirty flag, and then before
97 these are used (assuming we're not on a layer) we rebuild these cache
98 values: they reflect the top of the save stack, but translated and clipped
99 by the device's XY offset and bitmap-bounds.
100*/
101struct DeviceCM {
102 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000103 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000104 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000105 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +0000106 SkPaint* fPaint; // may be null (in the future)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000107
reedd9544982014-09-09 18:46:22 -0700108 DeviceCM(SkBaseDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas,
109 bool conservativeRasterClip)
110 : fNext(NULL)
111 , fClip(conservativeRasterClip)
112 {
113 if (NULL != device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000114 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000115 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000116 }
reed@google.com4b226022011-01-11 18:32:13 +0000117 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000118 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000119 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000120
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000121 ~DeviceCM() {
bsalomon49f085d2014-09-05 13:34:00 -0700122 if (fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000123 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000124 fDevice->unref();
125 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000126 SkDELETE(fPaint);
127 }
reed@google.com4b226022011-01-11 18:32:13 +0000128
reed@google.com045e62d2011-10-24 12:19:46 +0000129 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
130 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000131 int x = fDevice->getOrigin().x();
132 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000133 int width = fDevice->width();
134 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000135
reed@android.com8a1c16f2008-12-17 15:59:43 +0000136 if ((x | y) == 0) {
137 fMatrix = &totalMatrix;
138 fClip = totalClip;
139 } else {
140 fMatrixStorage = totalMatrix;
141 fMatrixStorage.postTranslate(SkIntToScalar(-x),
142 SkIntToScalar(-y));
143 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000144
reed@android.com8a1c16f2008-12-17 15:59:43 +0000145 totalClip.translate(-x, -y, &fClip);
146 }
147
reed@google.com045e62d2011-10-24 12:19:46 +0000148 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000149
150 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000151
reed@android.com8a1c16f2008-12-17 15:59:43 +0000152 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000153 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000154 SkRegion::kDifference_Op);
155 }
reed@google.com4b226022011-01-11 18:32:13 +0000156
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000157 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
158
reed@android.com8a1c16f2008-12-17 15:59:43 +0000159#ifdef SK_DEBUG
160 if (!fClip.isEmpty()) {
161 SkIRect deviceR;
162 deviceR.set(0, 0, width, height);
163 SkASSERT(deviceR.contains(fClip.getBounds()));
164 }
165#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000166 }
167
reed@android.com8a1c16f2008-12-17 15:59:43 +0000168private:
bsalomon@google.com0e354aa2012-10-08 20:44:25 +0000169 SkMatrix fMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000170};
171
172/* This is the record we keep for each save/restore level in the stack.
173 Since a level optionally copies the matrix and/or stack, we have pointers
174 for these fields. If the value is copied for this level, the copy is
175 stored in the ...Storage field, and the pointer points to that. If the
176 value is not copied for this level, we ignore ...Storage, and just point
177 at the corresponding value in the previous level in the stack.
178*/
179class SkCanvas::MCRec {
180public:
reed1f836ee2014-07-07 07:49:34 -0700181 SkDrawFilter* fFilter; // the current filter (or null)
reedd9544982014-09-09 18:46:22 -0700182 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000183 /* If there are any layers in the stack, this points to the top-most
184 one that is at or below this level in the stack (so we know what
185 bitmap/device to draw into from this level. This value is NOT
186 reference counted, since the real owner is either our fLayer field,
187 or a previous one in a lower level.)
188 */
reed2ff1fce2014-12-11 07:07:37 -0800189 DeviceCM* fTopLayer;
190 SkRasterClip fRasterClip;
191 SkMatrix fMatrix;
192 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000193
reedd9544982014-09-09 18:46:22 -0700194 MCRec(bool conservativeRasterClip) : fRasterClip(conservativeRasterClip) {
reedd9544982014-09-09 18:46:22 -0700195 fFilter = NULL;
196 fLayer = NULL;
197 fTopLayer = NULL;
reed2ff1fce2014-12-11 07:07:37 -0800198 fMatrix.reset();
199 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700200
reedd9544982014-09-09 18:46:22 -0700201 // don't bother initializing fNext
202 inc_rec();
203 }
reed2ff1fce2014-12-11 07:07:37 -0800204 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
reedd9544982014-09-09 18:46:22 -0700205 fFilter = SkSafeRef(prev.fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000206 fLayer = NULL;
reedd9544982014-09-09 18:46:22 -0700207 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800208 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700209
reed@android.com8a1c16f2008-12-17 15:59:43 +0000210 // don't bother initializing fNext
211 inc_rec();
212 }
213 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000214 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215 SkDELETE(fLayer);
216 dec_rec();
217 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000218};
219
220class SkDrawIter : public SkDraw {
221public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000222 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000223 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000224 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225 canvas->updateDeviceCMCache();
226
reed@google.com90c07ea2012-04-13 13:50:27 +0000227 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000228 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000229 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000230 }
reed@google.com4b226022011-01-11 18:32:13 +0000231
reed@android.com8a1c16f2008-12-17 15:59:43 +0000232 bool next() {
233 // skip over recs with empty clips
234 if (fSkipEmptyClips) {
235 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
236 fCurrLayer = fCurrLayer->fNext;
237 }
238 }
239
reed@google.comf68c5e22012-02-24 16:38:58 +0000240 const DeviceCM* rec = fCurrLayer;
241 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242
243 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000244 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
245 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000246 fDevice = rec->fDevice;
247 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000249 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250
251 fCurrLayer = rec->fNext;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000253
reed@android.com8a1c16f2008-12-17 15:59:43 +0000254 return true;
255 }
256 return false;
257 }
reed@google.com4b226022011-01-11 18:32:13 +0000258
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000259 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000260 int getX() const { return fDevice->getOrigin().x(); }
261 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262 const SkMatrix& getMatrix() const { return *fMatrix; }
263 const SkRegion& getClip() const { return *fClip; }
264 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000265
reed@android.com8a1c16f2008-12-17 15:59:43 +0000266private:
267 SkCanvas* fCanvas;
268 const DeviceCM* fCurrLayer;
269 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000270 SkBool8 fSkipEmptyClips;
271
272 typedef SkDraw INHERITED;
273};
274
275/////////////////////////////////////////////////////////////////////////////
276
277class AutoDrawLooper {
278public:
reed4a8126e2014-09-22 07:29:03 -0700279 AutoDrawLooper(SkCanvas* canvas, const SkSurfaceProps& props, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000280 bool skipLayerForImageFilter = false,
281 const SkRect* bounds = NULL) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000282 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000283 fFilter = canvas->getDrawFilter();
reed4a8126e2014-09-22 07:29:03 -0700284 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000285 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000286 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000287 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288
reed@google.com8926b162012-03-23 15:36:36 +0000289 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
290 SkPaint tmp;
291 tmp.setImageFilter(fOrigPaint.getImageFilter());
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000292 (void)canvas->internalSaveLayer(bounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag,
293 true, SkCanvas::kFullLayer_SaveLayerStrategy);
reed@google.com8926b162012-03-23 15:36:36 +0000294 // we'll clear the imageFilter for the actual draws in next(), so
295 // it will only be applied during the restore().
296 fDoClearImageFilter = true;
297 }
298
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000299 if (SkDrawLooper* looper = paint.getLooper()) {
300 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
301 looper->contextSize());
302 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000303 fIsSimple = false;
304 } else {
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000305 fLooperContext = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000306 // can we be marked as simple?
307 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000308 }
piotaixrb5fae932014-09-24 13:03:30 -0700309
reed4a8126e2014-09-22 07:29:03 -0700310 uint32_t oldFlags = paint.getFlags();
311 fNewPaintFlags = filter_paint_flags(props, oldFlags);
312 if (fIsSimple && (fNewPaintFlags != oldFlags)) {
313 SkPaint* paint = fLazyPaint.set(fOrigPaint);
314 paint->setFlags(fNewPaintFlags);
315 fPaint = paint;
316 // if we're not simple, doNext() will take care of calling setFlags()
317 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000318 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000319
reed@android.com8a1c16f2008-12-17 15:59:43 +0000320 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000321 if (fDoClearImageFilter) {
322 fCanvas->internalRestore();
323 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000324 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000325 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000326
reed@google.com4e2b3d32011-04-07 14:18:59 +0000327 const SkPaint& paint() const {
328 SkASSERT(fPaint);
329 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000330 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000331
reed@google.com129ec222012-05-15 13:24:09 +0000332 bool next(SkDrawFilter::Type drawType) {
333 if (fDone) {
334 return false;
335 } else if (fIsSimple) {
336 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000337 return !fPaint->nothingToDraw();
338 } else {
339 return this->doNext(drawType);
340 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000341 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000342
reed@android.com8a1c16f2008-12-17 15:59:43 +0000343private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000344 SkLazyPaint fLazyPaint;
345 SkCanvas* fCanvas;
346 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000347 SkDrawFilter* fFilter;
348 const SkPaint* fPaint;
349 int fSaveCount;
reed4a8126e2014-09-22 07:29:03 -0700350 uint32_t fNewPaintFlags;
reed@google.com8926b162012-03-23 15:36:36 +0000351 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000352 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000353 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000354 SkDrawLooper::Context* fLooperContext;
355 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000356
357 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000358};
359
reed@google.com129ec222012-05-15 13:24:09 +0000360bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000361 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000362 SkASSERT(!fIsSimple);
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000363 SkASSERT(fLooperContext || fFilter || fDoClearImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000364
365 SkPaint* paint = fLazyPaint.set(fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700366 paint->setFlags(fNewPaintFlags);
reed@google.com129ec222012-05-15 13:24:09 +0000367
368 if (fDoClearImageFilter) {
369 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000370 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000371
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000372 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000373 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000374 return false;
375 }
376 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000377 if (!fFilter->filter(paint, drawType)) {
378 fDone = true;
379 return false;
380 }
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000381 if (NULL == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000382 // no looper means we only draw once
383 fDone = true;
384 }
385 }
386 fPaint = paint;
387
388 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000389 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000390 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000391 }
392
393 // call this after any possible paint modifiers
394 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000395 fPaint = NULL;
396 return false;
397 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000398 return true;
399}
400
reed@android.com8a1c16f2008-12-17 15:59:43 +0000401////////// macros to place around the internal draw calls //////////////////
402
reed@google.com8926b162012-03-23 15:36:36 +0000403#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000404 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700405 AutoDrawLooper looper(this, fProps, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000406 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000407 SkDrawIter iter(this);
408
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000409#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000410 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700411 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000412 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000413 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000414
reed@google.com4e2b3d32011-04-07 14:18:59 +0000415#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000416
417////////////////////////////////////////////////////////////////////////////
418
reedd9544982014-09-09 18:46:22 -0700419SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
420 fConservativeRasterClip = SkToBool(flags & kConservativeRasterClip_InitFlag);
reed@google.comc0784db2013-12-13 21:16:12 +0000421 fCachedLocalClipBounds.setEmpty();
422 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000423 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000424 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700425 fDeviceCMDirty = true;
reed2ff1fce2014-12-11 07:07:37 -0800426 fSaveCount = 1;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000427 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000428
429 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700430 new (fMCRec) MCRec(fConservativeRasterClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000431
reedd9544982014-09-09 18:46:22 -0700432 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL, fConservativeRasterClip));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000433 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000434
reed@google.com97af1a62012-08-28 12:19:02 +0000435 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000436
reedf92c8662014-08-18 08:02:43 -0700437 if (device) {
reedb2db8982014-11-13 12:41:02 -0800438 device->initForRootLayer(fProps.pixelGeometry());
reed4a8126e2014-09-22 07:29:03 -0700439 if (device->forceConservativeRasterClip()) {
440 fConservativeRasterClip = true;
441 }
reedf92c8662014-08-18 08:02:43 -0700442 device->onAttachToCanvas(this);
443 fMCRec->fLayer->fDevice = SkRef(device);
reed78e27682014-11-19 08:04:34 -0800444 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
reedf92c8662014-08-18 08:02:43 -0700445 }
446 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000447}
448
reed@google.comcde92112011-07-06 20:00:52 +0000449SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000450 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700451 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000452{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000453 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000454
reedd9544982014-09-09 18:46:22 -0700455 this->init(NULL, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000456}
457
reedd9544982014-09-09 18:46:22 -0700458static SkBitmap make_nopixels(int width, int height) {
459 SkBitmap bitmap;
460 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
461 return bitmap;
462}
463
464class SkNoPixelsBitmapDevice : public SkBitmapDevice {
465public:
reed78e27682014-11-19 08:04:34 -0800466 SkNoPixelsBitmapDevice(const SkIRect& bounds)
467 : INHERITED(make_nopixels(bounds.width(), bounds.height()))
468 {
469 this->setOrigin(bounds.x(), bounds.y());
470 }
reedd9544982014-09-09 18:46:22 -0700471
472private:
piotaixrb5fae932014-09-24 13:03:30 -0700473
reedd9544982014-09-09 18:46:22 -0700474 typedef SkBitmapDevice INHERITED;
475};
476
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000477SkCanvas::SkCanvas(int width, int height)
478 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700479 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000480{
481 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700482
reed78e27682014-11-19 08:04:34 -0800483 this->init(SkNEW_ARGS(SkNoPixelsBitmapDevice,
484 (SkIRect::MakeWH(width, height))), kDefault_InitFlags)->unref();
reedd9544982014-09-09 18:46:22 -0700485}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000486
reed78e27682014-11-19 08:04:34 -0800487SkCanvas::SkCanvas(const SkIRect& bounds, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700488 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700489 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reedd9544982014-09-09 18:46:22 -0700490{
491 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700492
reed78e27682014-11-19 08:04:34 -0800493 this->init(SkNEW_ARGS(SkNoPixelsBitmapDevice, (bounds)), flags)->unref();
reedd9544982014-09-09 18:46:22 -0700494}
495
reed4a8126e2014-09-22 07:29:03 -0700496SkCanvas::SkCanvas(SkBaseDevice* device, const SkSurfaceProps* props, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700497 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700498 , fProps(SkSurfacePropsCopyOrDefault(props))
reedd9544982014-09-09 18:46:22 -0700499{
500 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700501
reedd9544982014-09-09 18:46:22 -0700502 this->init(device, flags);
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000503}
504
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000505SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000506 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700507 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000508{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000509 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700510
reedd9544982014-09-09 18:46:22 -0700511 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000512}
513
reed4a8126e2014-09-22 07:29:03 -0700514SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700515 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700516 , fProps(props)
reed3716fd02014-09-21 09:39:55 -0700517{
518 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700519
reed4a8126e2014-09-22 07:29:03 -0700520 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap)));
521 this->init(device, kDefault_InitFlags);
522}
reed29c857d2014-09-21 10:25:07 -0700523
reed4a8126e2014-09-22 07:29:03 -0700524SkCanvas::SkCanvas(const SkBitmap& bitmap)
525 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
526 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
527{
528 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700529
reed4a8126e2014-09-22 07:29:03 -0700530 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap)));
531 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000532}
533
534SkCanvas::~SkCanvas() {
535 // free up the contents of our deque
536 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000537
reed@android.com8a1c16f2008-12-17 15:59:43 +0000538 this->internalRestore(); // restore the last, since we're going away
539
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000540 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000541
reed@android.com8a1c16f2008-12-17 15:59:43 +0000542 dec_canvas();
543}
544
reed@android.com8a1c16f2008-12-17 15:59:43 +0000545SkDrawFilter* SkCanvas::getDrawFilter() const {
546 return fMCRec->fFilter;
547}
548
549SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
550 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
551 return filter;
552}
553
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000554SkMetaData& SkCanvas::getMetaData() {
555 // metadata users are rare, so we lazily allocate it. If that changes we
556 // can decide to just make it a field in the device (rather than a ptr)
557 if (NULL == fMetaData) {
558 fMetaData = new SkMetaData;
559 }
560 return *fMetaData;
561}
562
reed@android.com8a1c16f2008-12-17 15:59:43 +0000563///////////////////////////////////////////////////////////////////////////////
564
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000565void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000566 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000567 if (device) {
568 device->flush();
569 }
570}
571
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000572SkISize SkCanvas::getTopLayerSize() const {
573 SkBaseDevice* d = this->getTopDevice();
574 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
575}
576
577SkIPoint SkCanvas::getTopLayerOrigin() const {
578 SkBaseDevice* d = this->getTopDevice();
579 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
580}
581
582SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000583 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000584 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
585}
586
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000587SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000588 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000589 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000590 SkASSERT(rec && rec->fLayer);
591 return rec->fLayer->fDevice;
592}
593
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000594SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000595 if (updateMatrixClip) {
596 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
597 }
reed@google.com9266fed2011-03-30 00:18:03 +0000598 return fMCRec->fTopLayer->fDevice;
599}
600
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000601SkBaseDevice* SkCanvas::setRootDevice(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000602 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000603 SkDeque::F2BIter iter(fMCStack);
604 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605 SkASSERT(rec && rec->fLayer);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000606 SkBaseDevice* rootDevice = rec->fLayer->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000607
608 if (rootDevice == device) {
609 return device;
610 }
reed@google.com4b226022011-01-11 18:32:13 +0000611
reed@android.com8a1c16f2008-12-17 15:59:43 +0000612 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000613 device->onAttachToCanvas(this);
reedb2db8982014-11-13 12:41:02 -0800614 device->initForRootLayer(fProps.pixelGeometry());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000615 }
616 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000617 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000618 }
619
620 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
621 rootDevice = device;
622
623 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000624
reed@android.com8a1c16f2008-12-17 15:59:43 +0000625 /* Now we update our initial region to have the bounds of the new device,
626 and then intersect all of the clips in our stack with these bounds,
627 to ensure that we can't draw outside of the device's bounds (and trash
628 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000629
reed@android.com8a1c16f2008-12-17 15:59:43 +0000630 NOTE: this is only a partial-fix, since if the new device is larger than
631 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000632 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000633 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
634 reconstruct the correct clips, so this approximation will have to do.
635 The caller really needs to restore() back to the base if they want to
636 accurately take advantage of the new device bounds.
637 */
638
reed@google.com42aea282012-03-28 16:19:15 +0000639 SkIRect bounds;
640 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000641 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000642 } else {
643 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000644 }
reed@google.com42aea282012-03-28 16:19:15 +0000645 // now jam our 1st clip to be bounds, and intersect the rest with that
reed1f836ee2014-07-07 07:49:34 -0700646 rec->fRasterClip.setRect(bounds);
reed@google.com42aea282012-03-28 16:19:15 +0000647 while ((rec = (MCRec*)iter.next()) != NULL) {
reed1f836ee2014-07-07 07:49:34 -0700648 (void)rec->fRasterClip.op(bounds, SkRegion::kIntersect_Op);
reed@google.com42aea282012-03-28 16:19:15 +0000649 }
650
reed@android.com8a1c16f2008-12-17 15:59:43 +0000651 return device;
652}
653
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000654bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
655 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
656 return false;
657 }
658
659 bool weAllocated = false;
660 if (NULL == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700661 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000662 return false;
663 }
664 weAllocated = true;
665 }
666
667 SkBitmap bm(*bitmap);
668 bm.lockPixels();
669 if (bm.getPixels() && this->readPixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y)) {
670 return true;
671 }
672
673 if (weAllocated) {
674 bitmap->setPixelRef(NULL);
675 }
676 return false;
677}
reed@google.com51df9e32010-12-23 19:29:18 +0000678
bsalomon@google.comc6980972011-11-02 19:57:21 +0000679bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000680 SkIRect r = srcRect;
681 const SkISize size = this->getBaseLayerSize();
682 if (!r.intersect(0, 0, size.width(), size.height())) {
683 bitmap->reset();
684 return false;
685 }
686
reed84825042014-09-02 12:50:45 -0700687 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000688 // bitmap will already be reset.
689 return false;
690 }
691 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
692 bitmap->reset();
693 return false;
694 }
695 return true;
696}
697
reed96472de2014-12-10 09:53:42 -0800698bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000699 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000700 if (!device) {
701 return false;
702 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000703 const SkISize size = this->getBaseLayerSize();
mtkleinf0f14112014-12-12 08:46:25 -0800704
reed96472de2014-12-10 09:53:42 -0800705 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
706 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000707 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000708 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000709
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000710 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800711 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000712}
713
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000714bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
715 if (bitmap.getTexture()) {
716 return false;
717 }
718 SkBitmap bm(bitmap);
719 bm.lockPixels();
720 if (bm.getPixels()) {
721 return this->writePixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y);
722 }
723 return false;
724}
725
726bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
727 int x, int y) {
728 switch (origInfo.colorType()) {
729 case kUnknown_SkColorType:
730 case kIndex_8_SkColorType:
731 return false;
732 default:
733 break;
734 }
735 if (NULL == pixels || rowBytes < origInfo.minRowBytes()) {
736 return false;
737 }
738
739 const SkISize size = this->getBaseLayerSize();
740 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
741 if (!target.intersect(0, 0, size.width(), size.height())) {
742 return false;
743 }
744
745 SkBaseDevice* device = this->getDevice();
746 if (!device) {
747 return false;
748 }
749
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000750 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700751 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000752
753 // if x or y are negative, then we have to adjust pixels
754 if (x > 0) {
755 x = 0;
756 }
757 if (y > 0) {
758 y = 0;
759 }
760 // here x,y are either 0 or negative
761 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
762
reed4af35f32014-06-27 17:47:49 -0700763 // Tell our owning surface to bump its generation ID
764 this->predrawNotify();
765
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000766 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000767 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000768}
reed@google.com51df9e32010-12-23 19:29:18 +0000769
junov@google.com4370aed2012-01-18 16:21:08 +0000770SkCanvas* SkCanvas::canvasForDrawIter() {
771 return this;
772}
773
reed@android.com8a1c16f2008-12-17 15:59:43 +0000774//////////////////////////////////////////////////////////////////////////////
775
reed@android.com8a1c16f2008-12-17 15:59:43 +0000776void SkCanvas::updateDeviceCMCache() {
777 if (fDeviceCMDirty) {
778 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700779 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000780 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000781
reed@android.com8a1c16f2008-12-17 15:59:43 +0000782 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000783 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000784 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000785 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000786 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000787 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000788 } while ((layer = layer->fNext) != NULL);
789 }
790 fDeviceCMDirty = false;
791 }
792}
793
reed@android.com8a1c16f2008-12-17 15:59:43 +0000794///////////////////////////////////////////////////////////////////////////////
795
reed2ff1fce2014-12-11 07:07:37 -0800796void SkCanvas::checkForDeferredSave() {
797 if (fMCRec->fDeferredSaveCount > 0) {
798 fMCRec->fDeferredSaveCount -= 1;
799 this->doSave();
800 }
801}
802
reedf0090cb2014-11-26 08:55:51 -0800803int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800804#ifdef SK_DEBUG
805 int count = 0;
806 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
807 for (;;) {
808 const MCRec* rec = (const MCRec*)iter.next();
809 if (!rec) {
810 break;
811 }
812 count += 1 + rec->fDeferredSaveCount;
813 }
814 SkASSERT(count == fSaveCount);
815#endif
816 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -0800817}
818
819int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -0800820 fSaveCount += 1;
821 fMCRec->fDeferredSaveCount += 1;
822 return this->getSaveCount() - 1; // return our prev value
823}
824
825void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -0800826 this->willSave();
reed2ff1fce2014-12-11 07:07:37 -0800827 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -0800828}
829
830void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -0800831 if (fMCRec->fDeferredSaveCount > 0) {
832 SkASSERT(fSaveCount > 1);
833 fSaveCount -= 1;
834 fMCRec->fDeferredSaveCount -= 1;
835 } else {
836 // check for underflow
837 if (fMCStack.count() > 1) {
838 this->willRestore();
839 SkASSERT(fSaveCount > 1);
840 fSaveCount -= 1;
841 this->internalRestore();
842 this->didRestore();
843 }
reedf0090cb2014-11-26 08:55:51 -0800844 }
845}
846
847void SkCanvas::restoreToCount(int count) {
848 // sanity check
849 if (count < 1) {
850 count = 1;
851 }
mtkleinf0f14112014-12-12 08:46:25 -0800852
reedf0090cb2014-11-26 08:55:51 -0800853 int n = this->getSaveCount() - count;
854 for (int i = 0; i < n; ++i) {
855 this->restore();
856 }
857}
858
reed2ff1fce2014-12-11 07:07:37 -0800859void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000860 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700861 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000862 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000863
Florin Malita5f6102d2014-06-30 10:13:28 -0400864 fClipStack.save();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000865}
866
reed@android.com8a1c16f2008-12-17 15:59:43 +0000867static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +0000868#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000869 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +0000870#else
871 return true;
872#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000873}
874
junov@chromium.orga907ac32012-02-24 21:54:07 +0000875bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000876 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000877 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000878 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000879 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000880 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000881 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000882
883 if (imageFilter) {
reed1f836ee2014-07-07 07:49:34 -0700884 imageFilter->filterBounds(clipBounds, fMCRec->fMatrix, &clipBounds);
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000885 // Filters may grow the bounds beyond the device bounds.
886 op = SkRegion::kReplace_Op;
887 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000888 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -0700889 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000890 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000891
reed@android.com8a1c16f2008-12-17 15:59:43 +0000892 this->getTotalMatrix().mapRect(&r, *bounds);
893 r.roundOut(&ir);
894 // early exit if the layer's bounds are clipped out
895 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000896 if (bounds_affects_clip(flags)) {
reed1f836ee2014-07-07 07:49:34 -0700897 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000898 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000899 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000900 }
901 } else { // no user bounds, so just use the clip
902 ir = clipBounds;
903 }
904
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000905 if (bounds_affects_clip(flags)) {
906 fClipStack.clipDevRect(ir, op);
907 // early exit if the clip is now empty
reed1f836ee2014-07-07 07:49:34 -0700908 if (!fMCRec->fRasterClip.op(ir, op)) {
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000909 return false;
910 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000911 }
912
913 if (intersection) {
914 *intersection = ir;
915 }
916 return true;
917}
918
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000919int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
reedd990e2f2014-12-22 11:58:30 -0800920 if (gIgnoreSaveLayerBounds) {
921 bounds = NULL;
922 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000923 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag);
reed2ff1fce2014-12-11 07:07:37 -0800924 fSaveCount += 1;
925 this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, false, strategy);
926 return this->getSaveCount() - 1;
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000927}
928
reed2ff1fce2014-12-11 07:07:37 -0800929int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags) {
reedd990e2f2014-12-22 11:58:30 -0800930 if (gIgnoreSaveLayerBounds) {
931 bounds = NULL;
932 }
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000933 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
reed2ff1fce2014-12-11 07:07:37 -0800934 fSaveCount += 1;
935 this->internalSaveLayer(bounds, paint, flags, false, strategy);
936 return this->getSaveCount() - 1;
reed@google.com8926b162012-03-23 15:36:36 +0000937}
938
reed2ff1fce2014-12-11 07:07:37 -0800939void SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000940 bool justForImageFilter, SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +0000941#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
commit-bot@chromium.org2a5cd602014-05-30 20:41:20 +0000942 flags |= kClipToLayer_SaveFlag;
reed@google.comb93ba452014-03-10 19:47:58 +0000943#endif
944
junov@chromium.orga907ac32012-02-24 21:54:07 +0000945 // do this before we create the layer. We don't call the public save() since
946 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -0800947 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +0000948
949 fDeviceCMDirty = true;
950
951 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000952 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed2ff1fce2014-12-11 07:07:37 -0800953 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000954 }
955
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000956 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
957 // the clipRectBounds() call above?
958 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -0800959 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000960 }
961
reed@google.comb55deeb2012-01-06 14:43:09 +0000962 // Kill the imagefilter if our device doesn't allow it
963 SkLazyPaint lazyP;
964 if (paint && paint->getImageFilter()) {
965 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000966 if (justForImageFilter) {
967 // early exit if the layer was just for the imageFilter
reed2ff1fce2014-12-11 07:07:37 -0800968 return;
reed@google.com8926b162012-03-23 15:36:36 +0000969 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000970 SkPaint* p = lazyP.set(*paint);
971 p->setImageFilter(NULL);
972 paint = p;
973 }
974 }
975
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000976 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
977 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
978 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000979
reedb2db8982014-11-13 12:41:02 -0800980 SkBaseDevice* device = this->getTopDevice();
981 if (NULL == device) {
982 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -0800983 return;
reed@google.com76dd2772012-01-05 21:15:07 +0000984 }
reedb2db8982014-11-13 12:41:02 -0800985
986 SkBaseDevice::Usage usage = SkBaseDevice::kSaveLayer_Usage;
987 if (paint && paint->getImageFilter()) {
988 usage = SkBaseDevice::kImageFilter_Usage;
989 }
990 device = device->onCreateCompatibleDevice(SkBaseDevice::CreateInfo(info, usage,
991 fProps.pixelGeometry()));
bungeman@google.come25c6842011-08-17 14:53:54 +0000992 if (NULL == device) {
993 SkDebugf("Unable to create device for layer.");
reed2ff1fce2014-12-11 07:07:37 -0800994 return;
bungeman@google.come25c6842011-08-17 14:53:54 +0000995 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000996
reed@google.com6f8f2922011-03-04 22:27:10 +0000997 device->setOrigin(ir.fLeft, ir.fTop);
reedd9544982014-09-09 18:46:22 -0700998 DeviceCM* layer = SkNEW_ARGS(DeviceCM,
999 (device, ir.fLeft, ir.fTop, paint, this, fConservativeRasterClip));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001000 device->unref();
1001
1002 layer->fNext = fMCRec->fTopLayer;
1003 fMCRec->fLayer = layer;
1004 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reed@android.com8a1c16f2008-12-17 15:59:43 +00001005}
1006
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001007int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
1008 return this->saveLayerAlpha(bounds, alpha, kARGB_ClipLayer_SaveFlag);
1009}
1010
reed@android.com8a1c16f2008-12-17 15:59:43 +00001011int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
1012 SaveFlags flags) {
1013 if (0xFF == alpha) {
1014 return this->saveLayer(bounds, NULL, flags);
1015 } else {
1016 SkPaint tmpPaint;
1017 tmpPaint.setAlpha(alpha);
1018 return this->saveLayer(bounds, &tmpPaint, flags);
1019 }
1020}
1021
reed@android.com8a1c16f2008-12-17 15:59:43 +00001022void SkCanvas::internalRestore() {
1023 SkASSERT(fMCStack.count() != 0);
1024
1025 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001026 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001027
Florin Malita5f6102d2014-06-30 10:13:28 -04001028 fClipStack.restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001029
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001030 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001031 DeviceCM* layer = fMCRec->fLayer; // may be null
1032 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
1033 fMCRec->fLayer = NULL;
1034
1035 // now do the normal restore()
1036 fMCRec->~MCRec(); // balanced in save()
1037 fMCStack.pop_back();
1038 fMCRec = (MCRec*)fMCStack.back();
1039
1040 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1041 since if we're being recorded, we don't want to record this (the
1042 recorder will have already recorded the restore).
1043 */
bsalomon49f085d2014-09-05 13:34:00 -07001044 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001045 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001046 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001047 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
1048 layer->fPaint);
1049 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001050 fDeviceCMDirty = true;
1051 }
1052 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001053 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001054}
1055
reed4a8126e2014-09-22 07:29:03 -07001056SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
1057 if (NULL == props) {
1058 props = &fProps;
1059 }
1060 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001061}
1062
reed4a8126e2014-09-22 07:29:03 -07001063SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001064 SkBaseDevice* dev = this->getDevice();
reed4a8126e2014-09-22 07:29:03 -07001065 return dev ? dev->newSurface(info, props) : NULL;
reed@google.com76f10a32014-02-05 15:32:21 +00001066}
1067
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001068SkImageInfo SkCanvas::imageInfo() const {
1069 SkBaseDevice* dev = this->getDevice();
1070 if (dev) {
1071 return dev->imageInfo();
1072 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001073 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001074 }
1075}
1076
1077const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
1078 return this->onPeekPixels(info, rowBytes);
1079}
1080
1081const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
1082 SkBaseDevice* dev = this->getDevice();
1083 return dev ? dev->peekPixels(info, rowBytes) : NULL;
1084}
1085
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001086void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
1087 void* pixels = this->onAccessTopLayerPixels(info, rowBytes);
1088 if (pixels && origin) {
1089 *origin = this->getTopDevice(false)->getOrigin();
1090 }
1091 return pixels;
reed@google.com9c135db2014-03-12 18:28:35 +00001092}
1093
1094void* SkCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
1095 SkBaseDevice* dev = this->getTopDevice();
1096 return dev ? dev->accessPixels(info, rowBytes) : NULL;
1097}
1098
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001099SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1100 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1101 if (NULL == fAddr) {
1102 fInfo = canvas->imageInfo();
reed84825042014-09-02 12:50:45 -07001103 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.tryAllocPixels(fInfo)) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001104 return; // failure, fAddr is NULL
1105 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001106 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1107 return; // failure, fAddr is NULL
1108 }
1109 fAddr = fBitmap.getPixels();
1110 fRowBytes = fBitmap.rowBytes();
1111 }
1112 SkASSERT(fAddr); // success
1113}
1114
1115bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1116 if (fAddr) {
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +00001117 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001118 } else {
1119 bitmap->reset();
1120 return false;
1121 }
1122}
1123
reed@android.com8a1c16f2008-12-17 15:59:43 +00001124/////////////////////////////////////////////////////////////////////////////
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001125void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001126 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001127 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001128 return;
1129 }
1130
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001131 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001132 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001133 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001134 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001135
1136 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001137
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001138 SkRect storage;
1139 const SkRect* bounds = NULL;
1140 if (paint && paint->canComputeFastBounds()) {
1141 bitmap.getBounds(&storage);
1142 matrix.mapRect(&storage);
1143 bounds = &paint->computeFastBounds(storage, &storage);
1144 }
1145
1146 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001147
1148 while (iter.next()) {
1149 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1150 }
1151
1152 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001153}
1154
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001155void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001156 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001157 SkPaint tmp;
1158 if (NULL == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001159 paint = &tmp;
1160 }
reed@google.com4b226022011-01-11 18:32:13 +00001161
reed@google.com8926b162012-03-23 15:36:36 +00001162 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001163 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001164 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001165 paint = &looper.paint();
1166 SkImageFilter* filter = paint->getImageFilter();
1167 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001168 if (filter && !dstDev->canHandleImageFilter(filter)) {
fmalita2d97bc12014-11-20 10:44:58 -08001169 SkDeviceImageFilterProxy proxy(dstDev, fProps);
reed@google.com76dd2772012-01-05 21:15:07 +00001170 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001171 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001172 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001173 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001174 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001175 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblancobe129b22014-08-08 07:14:35 -07001176 SkAutoTUnref<SkImageFilter::Cache> cache(dstDev->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001177 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001178 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001179 SkPaint tmpUnfiltered(*paint);
1180 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001181 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1182 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001183 }
1184 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001185 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001186 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001187 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001188 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001189}
1190
reed@google.com8926b162012-03-23 15:36:36 +00001191void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1192 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08001193 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawSprite()");
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001194 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001195 return;
1196 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001197 SkDEBUGCODE(bitmap.validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001198
reed@google.com8926b162012-03-23 15:36:36 +00001199 SkPaint tmp;
1200 if (NULL == paint) {
1201 paint = &tmp;
1202 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001203
reed@google.com8926b162012-03-23 15:36:36 +00001204 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001205
reed@google.com8926b162012-03-23 15:36:36 +00001206 while (iter.next()) {
1207 paint = &looper.paint();
1208 SkImageFilter* filter = paint->getImageFilter();
1209 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1210 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
fmalita2d97bc12014-11-20 10:44:58 -08001211 SkDeviceImageFilterProxy proxy(iter.fDevice, fProps);
reed@google.com8926b162012-03-23 15:36:36 +00001212 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001213 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001214 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001215 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
halcanaryf622a6c2014-10-24 12:54:53 -07001216 const SkIRect clipBounds = bitmap.bounds();
senorblancobe129b22014-08-08 07:14:35 -07001217 SkAutoTUnref<SkImageFilter::Cache> cache(iter.fDevice->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001218 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001219 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001220 SkPaint tmpUnfiltered(*paint);
1221 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001222 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001223 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001224 }
1225 } else {
1226 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1227 }
1228 }
1229 LOOPER_END
1230}
1231
reed@android.com8a1c16f2008-12-17 15:59:43 +00001232/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001233void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001234 SkMatrix m;
1235 m.setTranslate(dx, dy);
1236 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001237}
1238
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001239void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001240 SkMatrix m;
1241 m.setScale(sx, sy);
1242 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001243}
1244
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001245void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001246 SkMatrix m;
1247 m.setRotate(degrees);
1248 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001249}
1250
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001251void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001252 SkMatrix m;
1253 m.setSkew(sx, sy);
1254 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001255}
1256
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001257void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001258 if (matrix.isIdentity()) {
1259 return;
1260 }
1261
reed2ff1fce2014-12-11 07:07:37 -08001262 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001263 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001264 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001265 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001266
1267 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001268}
1269
reed@android.com8a1c16f2008-12-17 15:59:43 +00001270void SkCanvas::setMatrix(const SkMatrix& matrix) {
reed2ff1fce2014-12-11 07:07:37 -08001271 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001272 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001273 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001274 fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001275 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001276}
1277
reed@android.com8a1c16f2008-12-17 15:59:43 +00001278void SkCanvas::resetMatrix() {
1279 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001280
reed@android.com8a1c16f2008-12-17 15:59:43 +00001281 matrix.reset();
1282 this->setMatrix(matrix);
1283}
1284
1285//////////////////////////////////////////////////////////////////////////////
1286
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001287void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001288 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001289 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1290 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001291}
1292
1293void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001294#ifdef SK_ENABLE_CLIP_QUICKREJECT
1295 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001296 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001297 return false;
1298 }
1299
reed@google.com3b3e8952012-08-16 20:53:31 +00001300 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001301 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001302 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001303
1304 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001305 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001306 }
1307 }
1308#endif
1309
reed@google.com5c3d1472011-02-22 19:12:23 +00001310 AutoValidateClip avc(this);
1311
reed@android.com8a1c16f2008-12-17 15:59:43 +00001312 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001313 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001314 if (!fAllowSoftClip) {
1315 edgeStyle = kHard_ClipEdgeStyle;
1316 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001317
reed1f836ee2014-07-07 07:49:34 -07001318 if (fMCRec->fMatrix.rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001319 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001320 // the matrix. This means we don't have to a) make a path, and b) tell
1321 // the region code to scan-convert the path, only to discover that it
1322 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001323 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001324
reed1f836ee2014-07-07 07:49:34 -07001325 fMCRec->fMatrix.mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001326 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reedd9544982014-09-09 18:46:22 -07001327 fMCRec->fRasterClip.op(r, this->getBaseLayerSize(), op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001328 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001329 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001330 // and clip against that, since it can handle any matrix. However, to
1331 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1332 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001333 SkPath path;
1334
1335 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001336 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001337 }
1338}
1339
reed73e714e2014-09-04 09:02:23 -07001340static void rasterclip_path(SkRasterClip* rc, const SkCanvas* canvas, const SkPath& devPath,
1341 SkRegion::Op op, bool doAA) {
reedd64c9482014-09-05 17:37:38 -07001342 rc->op(devPath, canvas->getBaseLayerSize(), op, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001343}
1344
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001345void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001346 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001347 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001348 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001349 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1350 } else {
1351 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001352 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001353}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001354
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001355void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001356 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001357 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001358 AutoValidateClip avc(this);
1359
1360 fDeviceCMDirty = true;
1361 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001362 if (!fAllowSoftClip) {
1363 edgeStyle = kHard_ClipEdgeStyle;
1364 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001365
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001366 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001367
1368 SkPath devPath;
1369 devPath.addRRect(transformedRRect);
1370
reed73e714e2014-09-04 09:02:23 -07001371 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001372 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001373 }
1374
1375 SkPath path;
1376 path.addRRect(rrect);
1377 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001378 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001379}
1380
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001381void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001382 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001383 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1384 SkRect r;
1385 if (!path.isInverseFillType() && path.isRect(&r)) {
1386 this->onClipRect(r, op, edgeStyle);
1387 } else {
1388 this->onClipPath(path, op, edgeStyle);
1389 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001390}
1391
1392void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001393#ifdef SK_ENABLE_CLIP_QUICKREJECT
1394 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001395 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001396 return false;
1397 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001398
reed@google.com3b3e8952012-08-16 20:53:31 +00001399 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001400 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001401 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001402
reed@google.comda17f752012-08-16 18:27:05 +00001403 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001404 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001405 }
1406 }
1407#endif
1408
reed@google.com5c3d1472011-02-22 19:12:23 +00001409 AutoValidateClip avc(this);
1410
reed@android.com8a1c16f2008-12-17 15:59:43 +00001411 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001412 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001413 if (!fAllowSoftClip) {
1414 edgeStyle = kHard_ClipEdgeStyle;
1415 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001416
1417 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001418 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001419
reed@google.comfe701122011-11-08 19:41:23 +00001420 // Check if the transfomation, or the original path itself
1421 // made us empty. Note this can also happen if we contained NaN
1422 // values. computing the bounds detects this, and will set our
1423 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1424 if (devPath.getBounds().isEmpty()) {
1425 // resetting the path will remove any NaN or other wanky values
1426 // that might upset our scan converter.
1427 devPath.reset();
1428 }
1429
reed@google.com5c3d1472011-02-22 19:12:23 +00001430 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001431 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001432
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001433 if (fAllowSimplifyClip) {
1434 devPath.reset();
1435 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1436 const SkClipStack* clipStack = getClipStack();
1437 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1438 const SkClipStack::Element* element;
1439 while ((element = iter.next())) {
1440 SkClipStack::Element::Type type = element->getType();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001441 SkPath operand;
commit-bot@chromium.org2a67e122014-05-19 13:53:10 +00001442 if (type != SkClipStack::Element::kEmpty_Type) {
1443 element->asPath(&operand);
1444 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001445 SkRegion::Op elementOp = element->getOp();
1446 if (elementOp == SkRegion::kReplace_Op) {
1447 devPath = operand;
1448 } else {
1449 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1450 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001451 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1452 // perhaps we need an API change to avoid this sort of mixed-signals about
1453 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001454 if (element->isAA()) {
1455 edgeStyle = kSoft_ClipEdgeStyle;
1456 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001457 }
1458 op = SkRegion::kReplace_Op;
1459 }
1460
reed73e714e2014-09-04 09:02:23 -07001461 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001462}
1463
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001464void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed2ff1fce2014-12-11 07:07:37 -08001465 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001466 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001467}
1468
1469void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001470 AutoValidateClip avc(this);
1471
reed@android.com8a1c16f2008-12-17 15:59:43 +00001472 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001473 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001474
reed@google.com5c3d1472011-02-22 19:12:23 +00001475 // todo: signal fClipStack that we have a region, and therefore (I guess)
1476 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001477 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001478
reed1f836ee2014-07-07 07:49:34 -07001479 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001480}
1481
reed@google.com819c9212011-02-23 18:56:55 +00001482#ifdef SK_DEBUG
1483void SkCanvas::validateClip() const {
1484 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001485 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001486 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001487 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001488 return;
1489 }
1490
reed@google.com819c9212011-02-23 18:56:55 +00001491 SkIRect ir;
1492 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001493 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001494
robertphillips@google.com80214e22012-07-20 15:33:18 +00001495 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001496 const SkClipStack::Element* element;
1497 while ((element = iter.next()) != NULL) {
1498 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001499 case SkClipStack::Element::kRect_Type:
1500 element->getRect().round(&ir);
1501 tmpClip.op(ir, element->getOp());
1502 break;
1503 case SkClipStack::Element::kEmpty_Type:
1504 tmpClip.setEmpty();
1505 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001506 default: {
1507 SkPath path;
1508 element->asPath(&path);
reed73e714e2014-09-04 09:02:23 -07001509 rasterclip_path(&tmpClip, this, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001510 break;
1511 }
reed@google.com819c9212011-02-23 18:56:55 +00001512 }
1513 }
reed@google.com819c9212011-02-23 18:56:55 +00001514}
1515#endif
1516
reed@google.com90c07ea2012-04-13 13:50:27 +00001517void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001518 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001519 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001520
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001521 while ((element = iter.next()) != NULL) {
fmalitac3b589a2014-06-05 12:40:07 -07001522 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001523 }
1524}
1525
reed@google.com5c3d1472011-02-22 19:12:23 +00001526///////////////////////////////////////////////////////////////////////////////
1527
reed@google.com754de5f2014-02-24 19:38:20 +00001528bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001529 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001530}
1531
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001532bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001533 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001534}
1535
reed@google.com3b3e8952012-08-16 20:53:31 +00001536bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001537 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001538 return true;
1539
reed1f836ee2014-07-07 07:49:34 -07001540 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001541 return true;
1542 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001543
reed1f836ee2014-07-07 07:49:34 -07001544 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001545 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001546 fMCRec->fMatrix.mapRect(&dst, rect);
reedb07a94f2014-11-19 05:03:18 -08001547 return !SkIRect::Intersects(dst.roundOut(), fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001548 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001549 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001550
reed@android.coma380ae42009-07-21 01:17:02 +00001551 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001552 // TODO: should we use | instead, or compare all 4 at once?
1553 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001554 return true;
1555 }
reed@google.comc0784db2013-12-13 21:16:12 +00001556 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001557 return true;
1558 }
1559 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001560 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001561}
1562
reed@google.com3b3e8952012-08-16 20:53:31 +00001563bool SkCanvas::quickReject(const SkPath& path) const {
1564 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001565}
1566
reed@google.com3b3e8952012-08-16 20:53:31 +00001567bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001568 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001569 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001570 return false;
1571 }
1572
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001573 SkMatrix inverse;
1574 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001575 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001576 if (bounds) {
1577 bounds->setEmpty();
1578 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001579 return false;
1580 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001581
bsalomon49f085d2014-09-05 13:34:00 -07001582 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001583 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001584 // adjust it outwards in case we are antialiasing
1585 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001586
reed@google.com8f4d2302013-12-17 16:44:46 +00001587 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1588 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001589 inverse.mapRect(bounds, r);
1590 }
1591 return true;
1592}
1593
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001594bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001595 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001596 if (clip.isEmpty()) {
1597 if (bounds) {
1598 bounds->setEmpty();
1599 }
1600 return false;
1601 }
1602
bsalomon49f085d2014-09-05 13:34:00 -07001603 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001604 *bounds = clip.getBounds();
1605 }
1606 return true;
1607}
1608
reed@android.com8a1c16f2008-12-17 15:59:43 +00001609const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001610 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001611}
1612
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001613const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001614 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001615}
1616
reed@google.com9c135db2014-03-12 18:28:35 +00001617GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1618 SkBaseDevice* dev = this->getTopDevice();
1619 return dev ? dev->accessRenderTarget() : NULL;
1620}
1621
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001622GrContext* SkCanvas::getGrContext() {
1623#if SK_SUPPORT_GPU
1624 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07001625 if (device) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001626 GrRenderTarget* renderTarget = device->accessRenderTarget();
bsalomon49f085d2014-09-05 13:34:00 -07001627 if (renderTarget) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001628 return renderTarget->getContext();
1629 }
1630 }
1631#endif
1632
1633 return NULL;
1634
1635}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001636
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001637void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1638 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001639 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001640 if (outer.isEmpty()) {
1641 return;
1642 }
1643 if (inner.isEmpty()) {
1644 this->drawRRect(outer, paint);
1645 return;
1646 }
1647
1648 // We don't have this method (yet), but technically this is what we should
1649 // be able to assert...
1650 // SkASSERT(outer.contains(inner));
1651 //
1652 // For now at least check for containment of bounds
1653 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1654
1655 this->onDrawDRRect(outer, inner, paint);
1656}
1657
reed@android.com8a1c16f2008-12-17 15:59:43 +00001658//////////////////////////////////////////////////////////////////////////////
1659// These are the virtual drawing methods
1660//////////////////////////////////////////////////////////////////////////////
1661
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001662void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07001663 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001664 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1665 }
1666}
1667
reed@android.com8a1c16f2008-12-17 15:59:43 +00001668void SkCanvas::drawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001669 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001670 this->internalDrawPaint(paint);
1671}
1672
1673void SkCanvas::internalDrawPaint(const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001674 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001675
1676 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001677 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001678 }
1679
reed@google.com4e2b3d32011-04-07 14:18:59 +00001680 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001681}
1682
1683void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1684 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001685 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001686 if ((long)count <= 0) {
1687 return;
1688 }
1689
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001690 SkRect r, storage;
1691 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001692 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001693 // special-case 2 points (common for drawing a single line)
1694 if (2 == count) {
1695 r.set(pts[0], pts[1]);
1696 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001697 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001698 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001699 bounds = &paint.computeFastStrokeBounds(r, &storage);
1700 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001701 return;
1702 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001703 }
reed@google.coma584aed2012-05-16 14:06:02 +00001704
reed@android.com8a1c16f2008-12-17 15:59:43 +00001705 SkASSERT(pts != NULL);
1706
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001707 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001708
reed@android.com8a1c16f2008-12-17 15:59:43 +00001709 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001710 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001711 }
reed@google.com4b226022011-01-11 18:32:13 +00001712
reed@google.com4e2b3d32011-04-07 14:18:59 +00001713 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001714}
1715
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001716void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001717 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001718 SkRect storage;
1719 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001720 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001721 bounds = &paint.computeFastBounds(r, &storage);
1722 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001723 return;
1724 }
1725 }
reed@google.com4b226022011-01-11 18:32:13 +00001726
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001727 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001728
1729 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001730 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001731 }
1732
reed@google.com4e2b3d32011-04-07 14:18:59 +00001733 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001734}
1735
reed@google.com4ed0fb72012-12-12 20:48:18 +00001736void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001737 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001738 SkRect storage;
1739 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001740 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001741 bounds = &paint.computeFastBounds(oval, &storage);
1742 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001743 return;
1744 }
1745 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001746
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001747 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001748
1749 while (iter.next()) {
1750 iter.fDevice->drawOval(iter, oval, looper.paint());
1751 }
1752
1753 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001754}
1755
1756void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001757 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001758 SkRect storage;
1759 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001760 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001761 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1762 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001763 return;
1764 }
1765 }
1766
1767 if (rrect.isRect()) {
1768 // call the non-virtual version
1769 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001770 return;
1771 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001772 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001773 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1774 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001775 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001776
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001777 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001778
1779 while (iter.next()) {
1780 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1781 }
1782
1783 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001784}
1785
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001786void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1787 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001788 SkRect storage;
1789 const SkRect* bounds = NULL;
1790 if (paint.canComputeFastBounds()) {
1791 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1792 if (this->quickReject(*bounds)) {
1793 return;
1794 }
1795 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001796
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001797 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001798
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001799 while (iter.next()) {
1800 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1801 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001802
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001803 LOOPER_END
1804}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001805
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001806void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001807 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00001808 if (!path.isFinite()) {
1809 return;
1810 }
1811
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001812 SkRect storage;
1813 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001814 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001815 const SkRect& pathBounds = path.getBounds();
1816 bounds = &paint.computeFastBounds(pathBounds, &storage);
1817 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001818 return;
1819 }
1820 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00001821
1822 const SkRect& r = path.getBounds();
1823 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00001824 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001825 this->internalDrawPaint(paint);
1826 }
1827 return;
1828 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001829
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001830 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001831
1832 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001833 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001834 }
1835
reed@google.com4e2b3d32011-04-07 14:18:59 +00001836 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001837}
1838
piotaixrb5fae932014-09-24 13:03:30 -07001839void SkCanvas::drawImage(const SkImage* image, SkScalar left, SkScalar top,
1840 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08001841 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
piotaixrb5fae932014-09-24 13:03:30 -07001842 image->draw(this, left, top, paint);
1843}
1844
1845void SkCanvas::drawImageRect(const SkImage* image, const SkRect* src,
1846 const SkRect& dst,
1847 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08001848 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
piotaixr5ceff912014-09-26 07:36:26 -07001849 image->drawRect(this, src, dst, paint);
piotaixrb5fae932014-09-24 13:03:30 -07001850}
1851
reed@android.com8a1c16f2008-12-17 15:59:43 +00001852void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1853 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08001854 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00001855 SkDEBUGCODE(bitmap.validate();)
1856
reed@google.com3d608122011-11-21 15:16:16 +00001857 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001858 SkRect bounds = {
1859 x, y,
1860 x + SkIntToScalar(bitmap.width()),
1861 y + SkIntToScalar(bitmap.height())
1862 };
1863 if (paint) {
1864 (void)paint->computeFastBounds(bounds, &bounds);
1865 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001866 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001867 return;
1868 }
1869 }
reed@google.com4b226022011-01-11 18:32:13 +00001870
reed@android.com8a1c16f2008-12-17 15:59:43 +00001871 SkMatrix matrix;
1872 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001873 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001874}
1875
reed@google.com9987ec32011-09-07 11:57:52 +00001876// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001877void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001878 const SkRect& dst, const SkPaint* paint,
1879 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001880 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001881 return;
1882 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001883
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001884 SkRect storage;
1885 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00001886 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001887 if (paint) {
1888 bounds = &paint->computeFastBounds(dst, &storage);
1889 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001890 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001891 return;
1892 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001893 }
reed@google.com3d608122011-11-21 15:16:16 +00001894
reed@google.com33535f32012-09-25 15:37:50 +00001895 SkLazyPaint lazy;
1896 if (NULL == paint) {
1897 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001898 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001899
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001900 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001901
reed@google.com33535f32012-09-25 15:37:50 +00001902 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001903 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001904 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001905
reed@google.com33535f32012-09-25 15:37:50 +00001906 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001907}
1908
reed@google.com71121732012-09-18 15:14:33 +00001909void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001910 const SkRect& dst, const SkPaint* paint,
1911 DrawBitmapRectFlags flags) {
danakj9881d632014-11-26 12:41:06 -08001912 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00001913 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001914 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001915}
1916
reed@google.com9987ec32011-09-07 11:57:52 +00001917void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1918 const SkIRect& center, const SkRect& dst,
1919 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001920 if (bitmap.drawsNothing()) {
1921 return;
1922 }
reed@google.com3d608122011-11-21 15:16:16 +00001923 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001924 SkRect storage;
1925 const SkRect* bounds = &dst;
1926 if (paint) {
1927 bounds = &paint->computeFastBounds(dst, &storage);
1928 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001929 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001930 return;
1931 }
1932 }
1933
reed@google.com9987ec32011-09-07 11:57:52 +00001934 const int32_t w = bitmap.width();
1935 const int32_t h = bitmap.height();
1936
1937 SkIRect c = center;
1938 // pin center to the bounds of the bitmap
1939 c.fLeft = SkMax32(0, center.fLeft);
1940 c.fTop = SkMax32(0, center.fTop);
1941 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1942 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1943
reed@google.com71121732012-09-18 15:14:33 +00001944 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001945 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00001946 };
1947 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001948 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00001949 };
reed@google.com9987ec32011-09-07 11:57:52 +00001950 SkScalar dstX[4] = {
1951 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1952 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1953 };
1954 SkScalar dstY[4] = {
1955 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1956 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1957 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001958
reed@google.com9987ec32011-09-07 11:57:52 +00001959 if (dstX[1] > dstX[2]) {
1960 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1961 dstX[2] = dstX[1];
1962 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001963
reed@google.com9987ec32011-09-07 11:57:52 +00001964 if (dstY[1] > dstY[2]) {
1965 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1966 dstY[2] = dstY[1];
1967 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001968
reed@google.com9987ec32011-09-07 11:57:52 +00001969 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00001970 SkRect s, d;
1971
reed@google.com9987ec32011-09-07 11:57:52 +00001972 s.fTop = srcY[y];
1973 s.fBottom = srcY[y+1];
1974 d.fTop = dstY[y];
1975 d.fBottom = dstY[y+1];
1976 for (int x = 0; x < 3; x++) {
1977 s.fLeft = srcX[x];
1978 s.fRight = srcX[x+1];
1979 d.fLeft = dstX[x];
1980 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001981 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00001982 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00001983 }
1984 }
1985}
1986
1987void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1988 const SkRect& dst, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08001989 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00001990 SkDEBUGCODE(bitmap.validate();)
1991
1992 // Need a device entry-point, so gpu can use a mesh
1993 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1994}
1995
reed@google.comf67e4cf2011-03-15 20:56:58 +00001996class SkDeviceFilteredPaint {
1997public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001998 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08001999 uint32_t filteredFlags = device->filterTextFlags(paint);
2000 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002001 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002002 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002003 fPaint = newPaint;
2004 } else {
2005 fPaint = &paint;
2006 }
2007 }
2008
reed@google.comf67e4cf2011-03-15 20:56:58 +00002009 const SkPaint& paint() const { return *fPaint; }
2010
2011private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002012 const SkPaint* fPaint;
2013 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002014};
2015
bungeman@google.com52c748b2011-08-22 21:30:43 +00002016void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2017 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002018 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002019 draw.fDevice->drawRect(draw, r, paint);
2020 } else {
2021 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002022 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002023 draw.fDevice->drawRect(draw, r, p);
2024 }
2025}
2026
2027void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2028 const char text[], size_t byteLength,
2029 SkScalar x, SkScalar y) {
2030 SkASSERT(byteLength == 0 || text != NULL);
2031
2032 // nothing to draw
2033 if (text == NULL || byteLength == 0 ||
2034 draw.fClip->isEmpty() ||
2035 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2036 return;
2037 }
2038
2039 SkScalar width = 0;
2040 SkPoint start;
2041
2042 start.set(0, 0); // to avoid warning
2043 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2044 SkPaint::kStrikeThruText_Flag)) {
2045 width = paint.measureText(text, byteLength);
2046
2047 SkScalar offsetX = 0;
2048 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2049 offsetX = SkScalarHalf(width);
2050 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2051 offsetX = width;
2052 }
2053 start.set(x - offsetX, y);
2054 }
2055
2056 if (0 == width) {
2057 return;
2058 }
2059
2060 uint32_t flags = paint.getFlags();
2061
2062 if (flags & (SkPaint::kUnderlineText_Flag |
2063 SkPaint::kStrikeThruText_Flag)) {
2064 SkScalar textSize = paint.getTextSize();
2065 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2066 SkRect r;
2067
2068 r.fLeft = start.fX;
2069 r.fRight = start.fX + width;
2070
2071 if (flags & SkPaint::kUnderlineText_Flag) {
2072 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2073 start.fY);
2074 r.fTop = offset;
2075 r.fBottom = offset + height;
2076 DrawRect(draw, paint, r, textSize);
2077 }
2078 if (flags & SkPaint::kStrikeThruText_Flag) {
2079 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2080 start.fY);
2081 r.fTop = offset;
2082 r.fBottom = offset + height;
2083 DrawRect(draw, paint, r, textSize);
2084 }
2085 }
2086}
2087
reed@google.come0d9ce82014-04-23 04:00:17 +00002088void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2089 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002090 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002091
2092 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002093 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002094 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002095 DrawTextDecorations(iter, dfp.paint(),
2096 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002097 }
2098
reed@google.com4e2b3d32011-04-07 14:18:59 +00002099 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002100}
2101
reed@google.come0d9ce82014-04-23 04:00:17 +00002102void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2103 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002104 SkPoint textOffset = SkPoint::Make(0, 0);
2105
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002106 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002107
reed@android.com8a1c16f2008-12-17 15:59:43 +00002108 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002109 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002110 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002111 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002112 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002113
reed@google.com4e2b3d32011-04-07 14:18:59 +00002114 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002115}
2116
reed@google.come0d9ce82014-04-23 04:00:17 +00002117void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2118 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002119
2120 SkPoint textOffset = SkPoint::Make(0, constY);
2121
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002122 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002123
reed@android.com8a1c16f2008-12-17 15:59:43 +00002124 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002125 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002126 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002127 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002128 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002129
reed@google.com4e2b3d32011-04-07 14:18:59 +00002130 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002131}
2132
reed@google.come0d9ce82014-04-23 04:00:17 +00002133void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2134 const SkMatrix* matrix, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002135 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002136
reed@android.com8a1c16f2008-12-17 15:59:43 +00002137 while (iter.next()) {
2138 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002139 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002140 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002141
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002142 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002143}
2144
fmalita00d5c2c2014-08-21 08:53:26 -07002145void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2146 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002147
fmalita19653d12014-10-16 11:53:30 -07002148 if (paint.canComputeFastBounds()) {
fmalita7ba7aa72014-08-29 09:46:36 -07002149 SkRect storage;
2150
2151 if (this->quickReject(paint.computeFastBounds(blob->bounds().makeOffset(x, y), &storage))) {
2152 return;
2153 }
2154 }
2155
fmalitaaa1b9122014-08-28 14:32:24 -07002156 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
fmalita00d5c2c2014-08-21 08:53:26 -07002157
fmalitaaa1b9122014-08-28 14:32:24 -07002158 while (iter.next()) {
2159 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
2160 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint());
fmalita00d5c2c2014-08-21 08:53:26 -07002161 }
2162
fmalitaaa1b9122014-08-28 14:32:24 -07002163 LOOPER_END
fmalita00d5c2c2014-08-21 08:53:26 -07002164}
2165
reed@google.come0d9ce82014-04-23 04:00:17 +00002166// These will become non-virtual, so they always call the (virtual) onDraw... method
2167void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2168 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002169 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002170 this->onDrawText(text, byteLength, x, y, paint);
2171}
2172void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2173 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002174 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002175 this->onDrawPosText(text, byteLength, pos, paint);
2176}
2177void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2178 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002179 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002180 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2181}
2182void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2183 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002184 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002185 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2186}
fmalita00d5c2c2014-08-21 08:53:26 -07002187void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2188 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002189 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
bsalomon49f085d2014-09-05 13:34:00 -07002190 if (blob) {
fmalita00d5c2c2014-08-21 08:53:26 -07002191 this->onDrawTextBlob(blob, x, y, paint);
2192 }
2193}
reed@google.come0d9ce82014-04-23 04:00:17 +00002194
reed@android.com8a1c16f2008-12-17 15:59:43 +00002195void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2196 const SkPoint verts[], const SkPoint texs[],
2197 const SkColor colors[], SkXfermode* xmode,
2198 const uint16_t indices[], int indexCount,
2199 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002200 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002201 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002202
reed@android.com8a1c16f2008-12-17 15:59:43 +00002203 while (iter.next()) {
2204 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002205 colors, xmode, indices, indexCount,
2206 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002207 }
reed@google.com4b226022011-01-11 18:32:13 +00002208
reed@google.com4e2b3d32011-04-07 14:18:59 +00002209 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002210}
2211
dandovb3c9d1c2014-08-12 08:34:29 -07002212void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2213 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002214 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
dandovb3c9d1c2014-08-12 08:34:29 -07002215 if (NULL == cubics) {
2216 return;
2217 }
mtklein6cfa73a2014-08-13 13:33:49 -07002218
dandovecfff212014-08-04 10:02:00 -07002219 // Since a patch is always within the convex hull of the control points, we discard it when its
2220 // bounding rectangle is completely outside the current clip.
2221 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002222 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002223 if (this->quickReject(bounds)) {
2224 return;
2225 }
mtklein6cfa73a2014-08-13 13:33:49 -07002226
dandovb3c9d1c2014-08-12 08:34:29 -07002227 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2228}
2229
2230void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2231 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2232
dandovecfff212014-08-04 10:02:00 -07002233 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
mtklein6cfa73a2014-08-13 13:33:49 -07002234
dandovecfff212014-08-04 10:02:00 -07002235 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002236 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002237 }
mtklein6cfa73a2014-08-13 13:33:49 -07002238
dandovecfff212014-08-04 10:02:00 -07002239 LOOPER_END
2240}
2241
reed6a070dc2014-11-11 19:36:09 -08002242void SkCanvas::EXPERIMENTAL_drawDrawable(SkCanvasDrawable* dr) {
reed6be2aa92014-11-18 11:08:05 -08002243 if (dr && !this->quickReject(dr->getBounds())) {
2244 this->onDrawDrawable(dr);
reed6a070dc2014-11-11 19:36:09 -08002245 }
2246}
2247
2248void SkCanvas::onDrawDrawable(SkCanvasDrawable* dr) {
2249 dr->draw(this);
2250}
2251
reed@android.com8a1c16f2008-12-17 15:59:43 +00002252//////////////////////////////////////////////////////////////////////////////
2253// These methods are NOT virtual, and therefore must call back into virtual
2254// methods, rather than actually drawing themselves.
2255//////////////////////////////////////////////////////////////////////////////
2256
2257void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002258 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002259 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002260 SkPaint paint;
2261
2262 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002263 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002264 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002265 }
2266 this->drawPaint(paint);
2267}
2268
reed@android.com845fdac2009-06-23 03:01:32 +00002269void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002270 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002271 SkPaint paint;
2272
2273 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002274 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002275 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002276 }
2277 this->drawPaint(paint);
2278}
2279
2280void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002281 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002282 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002283
reed@android.com8a1c16f2008-12-17 15:59:43 +00002284 pt.set(x, y);
2285 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2286}
2287
2288void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002289 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002290 SkPoint pt;
2291 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002292
reed@android.com8a1c16f2008-12-17 15:59:43 +00002293 pt.set(x, y);
2294 paint.setColor(color);
2295 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2296}
2297
2298void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2299 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002300 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002301 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002302
reed@android.com8a1c16f2008-12-17 15:59:43 +00002303 pts[0].set(x0, y0);
2304 pts[1].set(x1, y1);
2305 this->drawPoints(kLines_PointMode, 2, pts, paint);
2306}
2307
2308void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2309 SkScalar right, SkScalar bottom,
2310 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002311 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002312 SkRect r;
2313
2314 r.set(left, top, right, bottom);
2315 this->drawRect(r, paint);
2316}
2317
2318void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2319 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002320 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002321 if (radius < 0) {
2322 radius = 0;
2323 }
2324
2325 SkRect r;
2326 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002327 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002328}
2329
2330void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2331 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002332 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002333 if (rx > 0 && ry > 0) {
2334 if (paint.canComputeFastBounds()) {
2335 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002336 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002337 return;
2338 }
2339 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002340 SkRRect rrect;
2341 rrect.setRectXY(r, rx, ry);
2342 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002343 } else {
2344 this->drawRect(r, paint);
2345 }
2346}
2347
reed@android.com8a1c16f2008-12-17 15:59:43 +00002348void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2349 SkScalar sweepAngle, bool useCenter,
2350 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002351 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002352 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2353 this->drawOval(oval, paint);
2354 } else {
2355 SkPath path;
2356 if (useCenter) {
2357 path.moveTo(oval.centerX(), oval.centerY());
2358 }
2359 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2360 if (useCenter) {
2361 path.close();
2362 }
2363 this->drawPath(path, paint);
2364 }
2365}
2366
2367void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2368 const SkPath& path, SkScalar hOffset,
2369 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002370 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002371 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002372
reed@android.com8a1c16f2008-12-17 15:59:43 +00002373 matrix.setTranslate(hOffset, vOffset);
2374 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2375}
2376
reed@android.comf76bacf2009-05-13 14:00:33 +00002377///////////////////////////////////////////////////////////////////////////////
robertphillips9b14f262014-06-04 05:40:44 -07002378void SkCanvas::drawPicture(const SkPicture* picture) {
danakj9881d632014-11-26 12:41:06 -08002379 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
bsalomon49f085d2014-09-05 13:34:00 -07002380 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002381 this->onDrawPicture(picture, NULL, NULL);
robertphillips9b14f262014-06-04 05:40:44 -07002382 }
2383}
2384
reedd5fa1a42014-08-09 11:08:05 -07002385void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002386 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture(SkMatrix, SkPaint)");
bsalomon49f085d2014-09-05 13:34:00 -07002387 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002388 if (matrix && matrix->isIdentity()) {
2389 matrix = NULL;
2390 }
2391 this->onDrawPicture(picture, matrix, paint);
2392 }
2393}
robertphillips9b14f262014-06-04 05:40:44 -07002394
reedd5fa1a42014-08-09 11:08:05 -07002395void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2396 const SkPaint* paint) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002397 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002398 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002399 // Canvas has to first give the device the opportunity to render
2400 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002401 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002402 return; // the device has rendered the entire picture
2403 }
2404 }
2405
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002406 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
reedd5fa1a42014-08-09 11:08:05 -07002407
robertphillipsc5ba71d2014-09-04 08:42:50 -07002408 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002409}
2410
reed@android.com8a1c16f2008-12-17 15:59:43 +00002411///////////////////////////////////////////////////////////////////////////////
2412///////////////////////////////////////////////////////////////////////////////
2413
2414SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002415 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002416
2417 SkASSERT(canvas);
2418
2419 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2420 fDone = !fImpl->next();
2421}
2422
2423SkCanvas::LayerIter::~LayerIter() {
2424 fImpl->~SkDrawIter();
2425}
2426
2427void SkCanvas::LayerIter::next() {
2428 fDone = !fImpl->next();
2429}
2430
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002431SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002432 return fImpl->getDevice();
2433}
2434
2435const SkMatrix& SkCanvas::LayerIter::matrix() const {
2436 return fImpl->getMatrix();
2437}
2438
2439const SkPaint& SkCanvas::LayerIter::paint() const {
2440 const SkPaint* paint = fImpl->getPaint();
2441 if (NULL == paint) {
2442 paint = &fDefaultPaint;
2443 }
2444 return *paint;
2445}
2446
2447const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2448int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2449int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002450
2451///////////////////////////////////////////////////////////////////////////////
2452
fmalitac3b589a2014-06-05 12:40:07 -07002453SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002454
2455///////////////////////////////////////////////////////////////////////////////
2456
2457static bool supported_for_raster_canvas(const SkImageInfo& info) {
2458 switch (info.alphaType()) {
2459 case kPremul_SkAlphaType:
2460 case kOpaque_SkAlphaType:
2461 break;
2462 default:
2463 return false;
2464 }
2465
2466 switch (info.colorType()) {
2467 case kAlpha_8_SkColorType:
2468 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002469 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002470 break;
2471 default:
2472 return false;
2473 }
2474
2475 return true;
2476}
2477
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002478SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2479 if (!supported_for_raster_canvas(info)) {
2480 return NULL;
2481 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002482
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002483 SkBitmap bitmap;
2484 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2485 return NULL;
2486 }
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002487 return SkNEW_ARGS(SkCanvas, (bitmap));
2488}
reedd5fa1a42014-08-09 11:08:05 -07002489
2490///////////////////////////////////////////////////////////////////////////////
2491
2492SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002493 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07002494 : fCanvas(canvas)
2495 , fSaveCount(canvas->getSaveCount())
2496{
bsalomon49f085d2014-09-05 13:34:00 -07002497 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002498 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07002499 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002500 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07002501 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002502 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07002503 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002504 canvas->save();
2505 }
mtklein6cfa73a2014-08-13 13:33:49 -07002506
bsalomon49f085d2014-09-05 13:34:00 -07002507 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002508 canvas->concat(*matrix);
2509 }
2510}
2511
2512SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
2513 fCanvas->restoreToCount(fSaveCount);
2514}