blob: 70b705e1e0144141bec58a26a16bd970db97de71 [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00003 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00004 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00006 */
7
8#include "SkCanvas.h"
reedd5fa1a42014-08-09 11:08:05 -07009#include "SkCanvasPriv.h"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000010#include "SkBitmapDevice.h"
senorblanco@chromium.org9c397442012-09-27 21:57:45 +000011#include "SkDeviceImageFilterProxy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkDraw.h"
13#include "SkDrawFilter.h"
14#include "SkDrawLooper.h"
piotaixrb5fae932014-09-24 13:03:30 -070015#include "SkImage.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000016#include "SkMetaData.h"
caryclark@google.com45a75fb2013-04-25 13:34:40 +000017#include "SkPathOps.h"
dandovb3c9d1c2014-08-12 08:34:29 -070018#include "SkPatchUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000019#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000020#include "SkRasterClip.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000021#include "SkRRect.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000022#include "SkSmallAllocator.h"
reed@google.com97af1a62012-08-28 12:19:02 +000023#include "SkSurface_Base.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000024#include "SkTemplates.h"
fmalita7ba7aa72014-08-29 09:46:36 -070025#include "SkTextBlob.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000026#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000027#include "SkTLazy.h"
danakj8f757f52014-11-04 11:48:43 -080028#include "SkTraceEvent.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000029#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000030
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000031#if SK_SUPPORT_GPU
32#include "GrRenderTarget.h"
33#endif
34
reed@google.comda17f752012-08-16 18:27:05 +000035// experimental for faster tiled drawing...
36//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000037
reed@android.com8a1c16f2008-12-17 15:59:43 +000038//#define SK_TRACE_SAVERESTORE
39
40#ifdef SK_TRACE_SAVERESTORE
41 static int gLayerCounter;
42 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
43 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
44
45 static int gRecCounter;
46 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
47 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
48
49 static int gCanvasCounter;
50 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
51 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
52#else
53 #define inc_layer()
54 #define dec_layer()
55 #define inc_rec()
56 #define dec_rec()
57 #define inc_canvas()
58 #define dec_canvas()
59#endif
60
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000061typedef SkTLazy<SkPaint> SkLazyPaint;
62
reed@google.com97af1a62012-08-28 12:19:02 +000063void SkCanvas::predrawNotify() {
64 if (fSurfaceBase) {
commit-bot@chromium.orgc4c98702013-04-22 14:28:01 +000065 fSurfaceBase->aboutToDraw(SkSurface::kRetain_ContentChangeMode);
reed@google.com97af1a62012-08-28 12:19:02 +000066 }
67}
68
reed@android.com8a1c16f2008-12-17 15:59:43 +000069///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000070
reed4a8126e2014-09-22 07:29:03 -070071static uint32_t filter_paint_flags(const SkSurfaceProps& props, uint32_t flags) {
72 const uint32_t propFlags = props.flags();
73 if (propFlags & SkSurfaceProps::kDisallowDither_Flag) {
74 flags &= ~SkPaint::kDither_Flag;
75 }
76 if (propFlags & SkSurfaceProps::kDisallowAntiAlias_Flag) {
77 flags &= ~SkPaint::kAntiAlias_Flag;
78 }
79 return flags;
80}
81
82///////////////////////////////////////////////////////////////////////////////
83
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000084/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +000085 The clip/matrix/proc are fields that reflect the top of the save/restore
86 stack. Whenever the canvas changes, it marks a dirty flag, and then before
87 these are used (assuming we're not on a layer) we rebuild these cache
88 values: they reflect the top of the save stack, but translated and clipped
89 by the device's XY offset and bitmap-bounds.
90*/
91struct DeviceCM {
92 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000093 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +000094 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +000095 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +000096 SkPaint* fPaint; // may be null (in the future)
reed@android.com8a1c16f2008-12-17 15:59:43 +000097
reedd9544982014-09-09 18:46:22 -070098 DeviceCM(SkBaseDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas,
99 bool conservativeRasterClip)
100 : fNext(NULL)
101 , fClip(conservativeRasterClip)
102 {
103 if (NULL != device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000104 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000105 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000106 }
reed@google.com4b226022011-01-11 18:32:13 +0000107 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000108 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000109 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000110
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000111 ~DeviceCM() {
bsalomon49f085d2014-09-05 13:34:00 -0700112 if (fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000113 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000114 fDevice->unref();
115 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000116 SkDELETE(fPaint);
117 }
reed@google.com4b226022011-01-11 18:32:13 +0000118
reed@google.com045e62d2011-10-24 12:19:46 +0000119 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
120 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000121 int x = fDevice->getOrigin().x();
122 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000123 int width = fDevice->width();
124 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000125
reed@android.com8a1c16f2008-12-17 15:59:43 +0000126 if ((x | y) == 0) {
127 fMatrix = &totalMatrix;
128 fClip = totalClip;
129 } else {
130 fMatrixStorage = totalMatrix;
131 fMatrixStorage.postTranslate(SkIntToScalar(-x),
132 SkIntToScalar(-y));
133 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000134
reed@android.com8a1c16f2008-12-17 15:59:43 +0000135 totalClip.translate(-x, -y, &fClip);
136 }
137
reed@google.com045e62d2011-10-24 12:19:46 +0000138 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000139
140 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000141
reed@android.com8a1c16f2008-12-17 15:59:43 +0000142 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000143 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000144 SkRegion::kDifference_Op);
145 }
reed@google.com4b226022011-01-11 18:32:13 +0000146
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000147 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
148
reed@android.com8a1c16f2008-12-17 15:59:43 +0000149#ifdef SK_DEBUG
150 if (!fClip.isEmpty()) {
151 SkIRect deviceR;
152 deviceR.set(0, 0, width, height);
153 SkASSERT(deviceR.contains(fClip.getBounds()));
154 }
155#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000156 }
157
reed@android.com8a1c16f2008-12-17 15:59:43 +0000158private:
bsalomon@google.com0e354aa2012-10-08 20:44:25 +0000159 SkMatrix fMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000160};
161
162/* This is the record we keep for each save/restore level in the stack.
163 Since a level optionally copies the matrix and/or stack, we have pointers
164 for these fields. If the value is copied for this level, the copy is
165 stored in the ...Storage field, and the pointer points to that. If the
166 value is not copied for this level, we ignore ...Storage, and just point
167 at the corresponding value in the previous level in the stack.
168*/
169class SkCanvas::MCRec {
170public:
reed6f097092014-09-09 12:51:10 -0700171 SkRasterClip fRasterClip;
reedd9544982014-09-09 18:46:22 -0700172 SkMatrix fMatrix;
reed1f836ee2014-07-07 07:49:34 -0700173 SkDrawFilter* fFilter; // the current filter (or null)
reedd9544982014-09-09 18:46:22 -0700174 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000175 /* If there are any layers in the stack, this points to the top-most
176 one that is at or below this level in the stack (so we know what
177 bitmap/device to draw into from this level. This value is NOT
178 reference counted, since the real owner is either our fLayer field,
179 or a previous one in a lower level.)
180 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000181 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000182
reedd9544982014-09-09 18:46:22 -0700183 MCRec(bool conservativeRasterClip) : fRasterClip(conservativeRasterClip) {
184 fMatrix.reset();
185 fFilter = NULL;
186 fLayer = NULL;
187 fTopLayer = NULL;
piotaixrb5fae932014-09-24 13:03:30 -0700188
reedd9544982014-09-09 18:46:22 -0700189 // don't bother initializing fNext
190 inc_rec();
191 }
192 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip) {
193 fMatrix = prev.fMatrix;
194 fFilter = SkSafeRef(prev.fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000195 fLayer = NULL;
reedd9544982014-09-09 18:46:22 -0700196 fTopLayer = prev.fTopLayer;
piotaixrb5fae932014-09-24 13:03:30 -0700197
reed@android.com8a1c16f2008-12-17 15:59:43 +0000198 // don't bother initializing fNext
199 inc_rec();
200 }
201 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000202 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000203 SkDELETE(fLayer);
204 dec_rec();
205 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000206};
207
208class SkDrawIter : public SkDraw {
209public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000210 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000211 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000212 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000213 canvas->updateDeviceCMCache();
214
reed@google.com90c07ea2012-04-13 13:50:27 +0000215 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000216 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000217 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000218 }
reed@google.com4b226022011-01-11 18:32:13 +0000219
reed@android.com8a1c16f2008-12-17 15:59:43 +0000220 bool next() {
221 // skip over recs with empty clips
222 if (fSkipEmptyClips) {
223 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
224 fCurrLayer = fCurrLayer->fNext;
225 }
226 }
227
reed@google.comf68c5e22012-02-24 16:38:58 +0000228 const DeviceCM* rec = fCurrLayer;
229 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000230
231 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000232 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
233 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000234 fDevice = rec->fDevice;
235 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000236 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000237 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000238
239 fCurrLayer = rec->fNext;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000241
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242 return true;
243 }
244 return false;
245 }
reed@google.com4b226022011-01-11 18:32:13 +0000246
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000247 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000248 int getX() const { return fDevice->getOrigin().x(); }
249 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250 const SkMatrix& getMatrix() const { return *fMatrix; }
251 const SkRegion& getClip() const { return *fClip; }
252 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000253
reed@android.com8a1c16f2008-12-17 15:59:43 +0000254private:
255 SkCanvas* fCanvas;
256 const DeviceCM* fCurrLayer;
257 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000258 SkBool8 fSkipEmptyClips;
259
260 typedef SkDraw INHERITED;
261};
262
263/////////////////////////////////////////////////////////////////////////////
264
265class AutoDrawLooper {
266public:
reed4a8126e2014-09-22 07:29:03 -0700267 AutoDrawLooper(SkCanvas* canvas, const SkSurfaceProps& props, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000268 bool skipLayerForImageFilter = false,
269 const SkRect* bounds = NULL) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000270 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000271 fFilter = canvas->getDrawFilter();
reed4a8126e2014-09-22 07:29:03 -0700272 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000273 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000274 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000275 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000276
reed@google.com8926b162012-03-23 15:36:36 +0000277 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
278 SkPaint tmp;
279 tmp.setImageFilter(fOrigPaint.getImageFilter());
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000280 (void)canvas->internalSaveLayer(bounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag,
281 true, SkCanvas::kFullLayer_SaveLayerStrategy);
reed@google.com8926b162012-03-23 15:36:36 +0000282 // we'll clear the imageFilter for the actual draws in next(), so
283 // it will only be applied during the restore().
284 fDoClearImageFilter = true;
285 }
286
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000287 if (SkDrawLooper* looper = paint.getLooper()) {
288 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
289 looper->contextSize());
290 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000291 fIsSimple = false;
292 } else {
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000293 fLooperContext = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000294 // can we be marked as simple?
295 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000296 }
piotaixrb5fae932014-09-24 13:03:30 -0700297
reed4a8126e2014-09-22 07:29:03 -0700298 uint32_t oldFlags = paint.getFlags();
299 fNewPaintFlags = filter_paint_flags(props, oldFlags);
300 if (fIsSimple && (fNewPaintFlags != oldFlags)) {
301 SkPaint* paint = fLazyPaint.set(fOrigPaint);
302 paint->setFlags(fNewPaintFlags);
303 fPaint = paint;
304 // if we're not simple, doNext() will take care of calling setFlags()
305 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000306 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000307
reed@android.com8a1c16f2008-12-17 15:59:43 +0000308 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000309 if (fDoClearImageFilter) {
310 fCanvas->internalRestore();
311 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000312 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000313 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000314
reed@google.com4e2b3d32011-04-07 14:18:59 +0000315 const SkPaint& paint() const {
316 SkASSERT(fPaint);
317 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000318 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000319
reed@google.com129ec222012-05-15 13:24:09 +0000320 bool next(SkDrawFilter::Type drawType) {
321 if (fDone) {
322 return false;
323 } else if (fIsSimple) {
324 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000325 return !fPaint->nothingToDraw();
326 } else {
327 return this->doNext(drawType);
328 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000329 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000330
reed@android.com8a1c16f2008-12-17 15:59:43 +0000331private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000332 SkLazyPaint fLazyPaint;
333 SkCanvas* fCanvas;
334 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000335 SkDrawFilter* fFilter;
336 const SkPaint* fPaint;
337 int fSaveCount;
reed4a8126e2014-09-22 07:29:03 -0700338 uint32_t fNewPaintFlags;
reed@google.com8926b162012-03-23 15:36:36 +0000339 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000340 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000341 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000342 SkDrawLooper::Context* fLooperContext;
343 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000344
345 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000346};
347
reed@google.com129ec222012-05-15 13:24:09 +0000348bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000349 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000350 SkASSERT(!fIsSimple);
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000351 SkASSERT(fLooperContext || fFilter || fDoClearImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000352
353 SkPaint* paint = fLazyPaint.set(fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700354 paint->setFlags(fNewPaintFlags);
reed@google.com129ec222012-05-15 13:24:09 +0000355
356 if (fDoClearImageFilter) {
357 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000358 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000359
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000360 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000361 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000362 return false;
363 }
364 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000365 if (!fFilter->filter(paint, drawType)) {
366 fDone = true;
367 return false;
368 }
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000369 if (NULL == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000370 // no looper means we only draw once
371 fDone = true;
372 }
373 }
374 fPaint = paint;
375
376 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000377 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000378 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000379 }
380
381 // call this after any possible paint modifiers
382 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000383 fPaint = NULL;
384 return false;
385 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000386 return true;
387}
388
reed@android.com8a1c16f2008-12-17 15:59:43 +0000389////////// macros to place around the internal draw calls //////////////////
390
reed@google.com8926b162012-03-23 15:36:36 +0000391#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000392 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700393 AutoDrawLooper looper(this, fProps, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000394 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000395 SkDrawIter iter(this);
396
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000397#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000398 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700399 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000400 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000401 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000402
reed@google.com4e2b3d32011-04-07 14:18:59 +0000403#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000404
405////////////////////////////////////////////////////////////////////////////
406
reed4a8126e2014-09-22 07:29:03 -0700407void SkCanvas::setupDevice(SkBaseDevice* device) {
408 device->setPixelGeometry(fProps.pixelGeometry());
409}
410
reedd9544982014-09-09 18:46:22 -0700411SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
412 fConservativeRasterClip = SkToBool(flags & kConservativeRasterClip_InitFlag);
reed@google.comc0784db2013-12-13 21:16:12 +0000413 fCachedLocalClipBounds.setEmpty();
414 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000415 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000416 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700417 fDeviceCMDirty = true;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000418 fSaveLayerCount = 0;
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +0000419 fCullCount = 0;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000420 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000421
422 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700423 new (fMCRec) MCRec(fConservativeRasterClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000424
reedd9544982014-09-09 18:46:22 -0700425 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL, fConservativeRasterClip));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000426 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000427
reed@google.com97af1a62012-08-28 12:19:02 +0000428 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000429
reedf92c8662014-08-18 08:02:43 -0700430 if (device) {
reed4a8126e2014-09-22 07:29:03 -0700431 this->setupDevice(device);
432 if (device->forceConservativeRasterClip()) {
433 fConservativeRasterClip = true;
434 }
reedf92c8662014-08-18 08:02:43 -0700435 device->onAttachToCanvas(this);
436 fMCRec->fLayer->fDevice = SkRef(device);
437 fMCRec->fRasterClip.setRect(SkIRect::MakeWH(device->width(), device->height()));
438 }
439 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000440}
441
reed@google.comcde92112011-07-06 20:00:52 +0000442SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000443 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700444 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000445{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000446 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000447
reedd9544982014-09-09 18:46:22 -0700448 this->init(NULL, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000449}
450
reedd9544982014-09-09 18:46:22 -0700451static SkBitmap make_nopixels(int width, int height) {
452 SkBitmap bitmap;
453 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
454 return bitmap;
455}
456
457class SkNoPixelsBitmapDevice : public SkBitmapDevice {
458public:
459 SkNoPixelsBitmapDevice(int width, int height) : INHERITED(make_nopixels(width, height)) {}
460
461private:
piotaixrb5fae932014-09-24 13:03:30 -0700462
reedd9544982014-09-09 18:46:22 -0700463 typedef SkBitmapDevice INHERITED;
464};
465
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000466SkCanvas::SkCanvas(int width, int height)
467 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700468 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000469{
470 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700471
reedd9544982014-09-09 18:46:22 -0700472 this->init(SkNEW_ARGS(SkNoPixelsBitmapDevice, (width, height)), kDefault_InitFlags)->unref();
473}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000474
reedd9544982014-09-09 18:46:22 -0700475SkCanvas::SkCanvas(int width, int height, InitFlags flags)
476 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700477 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reedd9544982014-09-09 18:46:22 -0700478{
479 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700480
reedd9544982014-09-09 18:46:22 -0700481 this->init(SkNEW_ARGS(SkNoPixelsBitmapDevice, (width, height)), flags)->unref();
482}
483
reed4a8126e2014-09-22 07:29:03 -0700484SkCanvas::SkCanvas(SkBaseDevice* device, const SkSurfaceProps* props, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700485 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700486 , fProps(SkSurfacePropsCopyOrDefault(props))
reedd9544982014-09-09 18:46:22 -0700487{
488 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700489
reedd9544982014-09-09 18:46:22 -0700490 this->init(device, flags);
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000491}
492
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000493SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000494 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700495 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000496{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000497 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700498
reedd9544982014-09-09 18:46:22 -0700499 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000500}
501
reed4a8126e2014-09-22 07:29:03 -0700502SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700503 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700504 , fProps(props)
reed3716fd02014-09-21 09:39:55 -0700505{
506 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700507
reed4a8126e2014-09-22 07:29:03 -0700508 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap)));
509 this->init(device, kDefault_InitFlags);
510}
reed29c857d2014-09-21 10:25:07 -0700511
reed4a8126e2014-09-22 07:29:03 -0700512SkCanvas::SkCanvas(const SkBitmap& bitmap)
513 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
514 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
515{
516 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700517
reed4a8126e2014-09-22 07:29:03 -0700518 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap)));
519 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000520}
521
522SkCanvas::~SkCanvas() {
523 // free up the contents of our deque
524 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000525 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000526
reed@android.com8a1c16f2008-12-17 15:59:43 +0000527 this->internalRestore(); // restore the last, since we're going away
528
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000529 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000530
reed@android.com8a1c16f2008-12-17 15:59:43 +0000531 dec_canvas();
532}
533
reed@android.com8a1c16f2008-12-17 15:59:43 +0000534SkDrawFilter* SkCanvas::getDrawFilter() const {
535 return fMCRec->fFilter;
536}
537
538SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
539 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
540 return filter;
541}
542
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000543SkMetaData& SkCanvas::getMetaData() {
544 // metadata users are rare, so we lazily allocate it. If that changes we
545 // can decide to just make it a field in the device (rather than a ptr)
546 if (NULL == fMetaData) {
547 fMetaData = new SkMetaData;
548 }
549 return *fMetaData;
550}
551
reed@android.com8a1c16f2008-12-17 15:59:43 +0000552///////////////////////////////////////////////////////////////////////////////
553
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000554void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000555 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000556 if (device) {
557 device->flush();
558 }
559}
560
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000561SkISize SkCanvas::getTopLayerSize() const {
562 SkBaseDevice* d = this->getTopDevice();
563 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
564}
565
566SkIPoint SkCanvas::getTopLayerOrigin() const {
567 SkBaseDevice* d = this->getTopDevice();
568 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
569}
570
571SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000572 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000573 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
574}
575
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000576SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000577 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000578 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000579 SkASSERT(rec && rec->fLayer);
580 return rec->fLayer->fDevice;
581}
582
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000583SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000584 if (updateMatrixClip) {
585 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
586 }
reed@google.com9266fed2011-03-30 00:18:03 +0000587 return fMCRec->fTopLayer->fDevice;
588}
589
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000590SkBaseDevice* SkCanvas::setRootDevice(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000591 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000592 SkDeque::F2BIter iter(fMCStack);
593 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000594 SkASSERT(rec && rec->fLayer);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000595 SkBaseDevice* rootDevice = rec->fLayer->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000596
597 if (rootDevice == device) {
598 return device;
599 }
reed@google.com4b226022011-01-11 18:32:13 +0000600
reed@android.com8a1c16f2008-12-17 15:59:43 +0000601 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000602 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000603 }
604 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000605 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606 }
607
608 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
609 rootDevice = device;
reed4a8126e2014-09-22 07:29:03 -0700610 this->setupDevice(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000611
612 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000613
reed@android.com8a1c16f2008-12-17 15:59:43 +0000614 /* Now we update our initial region to have the bounds of the new device,
615 and then intersect all of the clips in our stack with these bounds,
616 to ensure that we can't draw outside of the device's bounds (and trash
617 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000618
reed@android.com8a1c16f2008-12-17 15:59:43 +0000619 NOTE: this is only a partial-fix, since if the new device is larger than
620 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000621 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000622 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
623 reconstruct the correct clips, so this approximation will have to do.
624 The caller really needs to restore() back to the base if they want to
625 accurately take advantage of the new device bounds.
626 */
627
reed@google.com42aea282012-03-28 16:19:15 +0000628 SkIRect bounds;
629 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000630 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000631 } else {
632 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000633 }
reed@google.com42aea282012-03-28 16:19:15 +0000634 // now jam our 1st clip to be bounds, and intersect the rest with that
reed1f836ee2014-07-07 07:49:34 -0700635 rec->fRasterClip.setRect(bounds);
reed@google.com42aea282012-03-28 16:19:15 +0000636 while ((rec = (MCRec*)iter.next()) != NULL) {
reed1f836ee2014-07-07 07:49:34 -0700637 (void)rec->fRasterClip.op(bounds, SkRegion::kIntersect_Op);
reed@google.com42aea282012-03-28 16:19:15 +0000638 }
639
reed@android.com8a1c16f2008-12-17 15:59:43 +0000640 return device;
641}
642
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000643bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
644 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
645 return false;
646 }
647
648 bool weAllocated = false;
649 if (NULL == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700650 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000651 return false;
652 }
653 weAllocated = true;
654 }
655
656 SkBitmap bm(*bitmap);
657 bm.lockPixels();
658 if (bm.getPixels() && this->readPixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y)) {
659 return true;
660 }
661
662 if (weAllocated) {
663 bitmap->setPixelRef(NULL);
664 }
665 return false;
666}
reed@google.com51df9e32010-12-23 19:29:18 +0000667
bsalomon@google.comc6980972011-11-02 19:57:21 +0000668bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000669 SkIRect r = srcRect;
670 const SkISize size = this->getBaseLayerSize();
671 if (!r.intersect(0, 0, size.width(), size.height())) {
672 bitmap->reset();
673 return false;
674 }
675
reed84825042014-09-02 12:50:45 -0700676 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000677 // bitmap will already be reset.
678 return false;
679 }
680 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
681 bitmap->reset();
682 return false;
683 }
684 return true;
685}
686
687bool SkCanvas::readPixels(const SkImageInfo& origInfo, void* dstP, size_t rowBytes, int x, int y) {
688 switch (origInfo.colorType()) {
689 case kUnknown_SkColorType:
690 case kIndex_8_SkColorType:
691 return false;
692 default:
693 break;
694 }
695 if (NULL == dstP || rowBytes < origInfo.minRowBytes()) {
696 return false;
697 }
698 if (0 == origInfo.width() || 0 == origInfo.height()) {
699 return false;
700 }
701
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000702 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000703 if (!device) {
704 return false;
705 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000706
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000707 const SkISize size = this->getBaseLayerSize();
708 SkIRect srcR = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
709 if (!srcR.intersect(0, 0, size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000710 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000711 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000712
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000713 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700714 const SkImageInfo info = origInfo.makeWH(srcR.width(), srcR.height());
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000715
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000716 // if x or y are negative, then we have to adjust pixels
717 if (x > 0) {
718 x = 0;
reed@google.com51df9e32010-12-23 19:29:18 +0000719 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000720 if (y > 0) {
721 y = 0;
722 }
723 // here x,y are either 0 or negative
724 dstP = ((char*)dstP - y * rowBytes - x * info.bytesPerPixel());
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000725
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000726 // The device can assert that the requested area is always contained in its bounds
727 return device->readPixels(info, dstP, rowBytes, srcR.x(), srcR.y());
reed@google.com51df9e32010-12-23 19:29:18 +0000728}
729
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000730bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
731 if (bitmap.getTexture()) {
732 return false;
733 }
734 SkBitmap bm(bitmap);
735 bm.lockPixels();
736 if (bm.getPixels()) {
737 return this->writePixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y);
738 }
739 return false;
740}
741
742bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
743 int x, int y) {
744 switch (origInfo.colorType()) {
745 case kUnknown_SkColorType:
746 case kIndex_8_SkColorType:
747 return false;
748 default:
749 break;
750 }
751 if (NULL == pixels || rowBytes < origInfo.minRowBytes()) {
752 return false;
753 }
754
755 const SkISize size = this->getBaseLayerSize();
756 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
757 if (!target.intersect(0, 0, size.width(), size.height())) {
758 return false;
759 }
760
761 SkBaseDevice* device = this->getDevice();
762 if (!device) {
763 return false;
764 }
765
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000766 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700767 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000768
769 // if x or y are negative, then we have to adjust pixels
770 if (x > 0) {
771 x = 0;
772 }
773 if (y > 0) {
774 y = 0;
775 }
776 // here x,y are either 0 or negative
777 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
778
reed4af35f32014-06-27 17:47:49 -0700779 // Tell our owning surface to bump its generation ID
780 this->predrawNotify();
781
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000782 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000783 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000784}
reed@google.com51df9e32010-12-23 19:29:18 +0000785
junov@google.com4370aed2012-01-18 16:21:08 +0000786SkCanvas* SkCanvas::canvasForDrawIter() {
787 return this;
788}
789
reed@android.com8a1c16f2008-12-17 15:59:43 +0000790//////////////////////////////////////////////////////////////////////////////
791
reed@android.com8a1c16f2008-12-17 15:59:43 +0000792void SkCanvas::updateDeviceCMCache() {
793 if (fDeviceCMDirty) {
794 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700795 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000796 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000797
reed@android.com8a1c16f2008-12-17 15:59:43 +0000798 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000799 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000800 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000801 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000802 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000803 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000804 } while ((layer = layer->fNext) != NULL);
805 }
806 fDeviceCMDirty = false;
807 }
808}
809
reed@android.com8a1c16f2008-12-17 15:59:43 +0000810///////////////////////////////////////////////////////////////////////////////
811
Florin Malita5f6102d2014-06-30 10:13:28 -0400812int SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000813 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000814
reed@android.com8a1c16f2008-12-17 15:59:43 +0000815 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700816 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000817 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000818
Florin Malita5f6102d2014-06-30 10:13:28 -0400819 fClipStack.save();
reed@google.com5c3d1472011-02-22 19:12:23 +0000820
reed@android.com8a1c16f2008-12-17 15:59:43 +0000821 return saveCount;
822}
823
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000824int SkCanvas::save() {
Florin Malita5f6102d2014-06-30 10:13:28 -0400825 this->willSave();
826 return this->internalSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000827}
828
reed@android.com8a1c16f2008-12-17 15:59:43 +0000829static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +0000830#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000831 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +0000832#else
833 return true;
834#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000835}
836
junov@chromium.orga907ac32012-02-24 21:54:07 +0000837bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000838 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000839 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000840 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000841 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000842 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000843 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000844
845 if (imageFilter) {
reed1f836ee2014-07-07 07:49:34 -0700846 imageFilter->filterBounds(clipBounds, fMCRec->fMatrix, &clipBounds);
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000847 // Filters may grow the bounds beyond the device bounds.
848 op = SkRegion::kReplace_Op;
849 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000850 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -0700851 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000852 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000853
reed@android.com8a1c16f2008-12-17 15:59:43 +0000854 this->getTotalMatrix().mapRect(&r, *bounds);
855 r.roundOut(&ir);
856 // early exit if the layer's bounds are clipped out
857 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000858 if (bounds_affects_clip(flags)) {
reed1f836ee2014-07-07 07:49:34 -0700859 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000860 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000861 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000862 }
863 } else { // no user bounds, so just use the clip
864 ir = clipBounds;
865 }
866
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000867 if (bounds_affects_clip(flags)) {
868 fClipStack.clipDevRect(ir, op);
869 // early exit if the clip is now empty
reed1f836ee2014-07-07 07:49:34 -0700870 if (!fMCRec->fRasterClip.op(ir, op)) {
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000871 return false;
872 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000873 }
874
875 if (intersection) {
876 *intersection = ir;
877 }
878 return true;
879}
880
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000881int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
882 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag);
883 return this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, false, strategy);
884}
885
junov@chromium.orga907ac32012-02-24 21:54:07 +0000886int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
887 SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000888 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
889 return this->internalSaveLayer(bounds, paint, flags, false, strategy);
reed@google.com8926b162012-03-23 15:36:36 +0000890}
891
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000892int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
893 bool justForImageFilter, SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +0000894#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
commit-bot@chromium.org2a5cd602014-05-30 20:41:20 +0000895 flags |= kClipToLayer_SaveFlag;
reed@google.comb93ba452014-03-10 19:47:58 +0000896#endif
897
junov@chromium.orga907ac32012-02-24 21:54:07 +0000898 // do this before we create the layer. We don't call the public save() since
899 // that would invoke a possibly overridden virtual
Florin Malita5f6102d2014-06-30 10:13:28 -0400900 int count = this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +0000901
902 fDeviceCMDirty = true;
903
904 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000905 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000906 return count;
907 }
908
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000909 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
910 // the clipRectBounds() call above?
911 if (kNoLayer_SaveLayerStrategy == strategy) {
912 return count;
913 }
914
reed@google.comb55deeb2012-01-06 14:43:09 +0000915 // Kill the imagefilter if our device doesn't allow it
916 SkLazyPaint lazyP;
917 if (paint && paint->getImageFilter()) {
918 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000919 if (justForImageFilter) {
920 // early exit if the layer was just for the imageFilter
921 return count;
922 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000923 SkPaint* p = lazyP.set(*paint);
924 p->setImageFilter(NULL);
925 paint = p;
926 }
927 }
928
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000929 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
930 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
931 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000932
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000933 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000934 if (paint && paint->getImageFilter()) {
reed52d9ac62014-06-30 09:05:34 -0700935 device = this->getDevice();
936 if (device) {
senorblancob0e89dc2014-10-20 14:03:12 -0700937 device = device->createCompatibleDeviceForImageFilter(info);
reed52d9ac62014-06-30 09:05:34 -0700938 }
reed@google.com76dd2772012-01-05 21:15:07 +0000939 } else {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000940 device = this->createLayerDevice(info);
reed@google.com76dd2772012-01-05 21:15:07 +0000941 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000942 if (NULL == device) {
943 SkDebugf("Unable to create device for layer.");
944 return count;
945 }
reed4a8126e2014-09-22 07:29:03 -0700946 this->setupDevice(device);
bsalomon@google.come97f0852011-06-17 13:10:25 +0000947
reed@google.com6f8f2922011-03-04 22:27:10 +0000948 device->setOrigin(ir.fLeft, ir.fTop);
reedd9544982014-09-09 18:46:22 -0700949 DeviceCM* layer = SkNEW_ARGS(DeviceCM,
950 (device, ir.fLeft, ir.fTop, paint, this, fConservativeRasterClip));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000951 device->unref();
952
953 layer->fNext = fMCRec->fTopLayer;
954 fMCRec->fLayer = layer;
955 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
956
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000957 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000958 return count;
959}
960
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000961int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
962 return this->saveLayerAlpha(bounds, alpha, kARGB_ClipLayer_SaveFlag);
963}
964
reed@android.com8a1c16f2008-12-17 15:59:43 +0000965int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
966 SaveFlags flags) {
967 if (0xFF == alpha) {
968 return this->saveLayer(bounds, NULL, flags);
969 } else {
970 SkPaint tmpPaint;
971 tmpPaint.setAlpha(alpha);
972 return this->saveLayer(bounds, &tmpPaint, flags);
973 }
974}
975
976void SkCanvas::restore() {
977 // check for underflow
978 if (fMCStack.count() > 1) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000979 this->willRestore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000980 this->internalRestore();
mtklein6cfa73a2014-08-13 13:33:49 -0700981 this->didRestore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000982 }
983}
984
985void SkCanvas::internalRestore() {
986 SkASSERT(fMCStack.count() != 0);
987
988 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +0000989 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000990
Florin Malita5f6102d2014-06-30 10:13:28 -0400991 fClipStack.restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000992
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000993 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000994 DeviceCM* layer = fMCRec->fLayer; // may be null
995 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
996 fMCRec->fLayer = NULL;
997
998 // now do the normal restore()
999 fMCRec->~MCRec(); // balanced in save()
1000 fMCStack.pop_back();
1001 fMCRec = (MCRec*)fMCStack.back();
1002
1003 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1004 since if we're being recorded, we don't want to record this (the
1005 recorder will have already recorded the restore).
1006 */
bsalomon49f085d2014-09-05 13:34:00 -07001007 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001008 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001009 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001010 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
1011 layer->fPaint);
1012 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001013 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +00001014
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001015 SkASSERT(fSaveLayerCount > 0);
1016 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001017 }
1018 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001019 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001020}
1021
1022int SkCanvas::getSaveCount() const {
1023 return fMCStack.count();
1024}
1025
1026void SkCanvas::restoreToCount(int count) {
1027 // sanity check
1028 if (count < 1) {
1029 count = 1;
1030 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001031
reed@google.comb9d1c6a2011-11-28 16:09:24 +00001032 int n = this->getSaveCount() - count;
1033 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001034 this->restore();
1035 }
1036}
1037
reed@google.com7c202932011-12-14 18:48:05 +00001038bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001039 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +00001040}
1041
reed4a8126e2014-09-22 07:29:03 -07001042SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
1043 if (NULL == props) {
1044 props = &fProps;
1045 }
1046 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001047}
1048
reed4a8126e2014-09-22 07:29:03 -07001049SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001050 SkBaseDevice* dev = this->getDevice();
reed4a8126e2014-09-22 07:29:03 -07001051 return dev ? dev->newSurface(info, props) : NULL;
reed@google.com76f10a32014-02-05 15:32:21 +00001052}
1053
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001054SkImageInfo SkCanvas::imageInfo() const {
1055 SkBaseDevice* dev = this->getDevice();
1056 if (dev) {
1057 return dev->imageInfo();
1058 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001059 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001060 }
1061}
1062
1063const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
1064 return this->onPeekPixels(info, rowBytes);
1065}
1066
1067const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
1068 SkBaseDevice* dev = this->getDevice();
1069 return dev ? dev->peekPixels(info, rowBytes) : NULL;
1070}
1071
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001072void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
1073 void* pixels = this->onAccessTopLayerPixels(info, rowBytes);
1074 if (pixels && origin) {
1075 *origin = this->getTopDevice(false)->getOrigin();
1076 }
1077 return pixels;
reed@google.com9c135db2014-03-12 18:28:35 +00001078}
1079
1080void* SkCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
1081 SkBaseDevice* dev = this->getTopDevice();
1082 return dev ? dev->accessPixels(info, rowBytes) : NULL;
1083}
1084
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001085SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1086 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1087 if (NULL == fAddr) {
1088 fInfo = canvas->imageInfo();
reed84825042014-09-02 12:50:45 -07001089 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.tryAllocPixels(fInfo)) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001090 return; // failure, fAddr is NULL
1091 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001092 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1093 return; // failure, fAddr is NULL
1094 }
1095 fAddr = fBitmap.getPixels();
1096 fRowBytes = fBitmap.rowBytes();
1097 }
1098 SkASSERT(fAddr); // success
1099}
1100
1101bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1102 if (fAddr) {
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +00001103 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001104 } else {
1105 bitmap->reset();
1106 return false;
1107 }
1108}
1109
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +00001110void SkCanvas::onPushCull(const SkRect& cullRect) {
1111 // do nothing. Subclasses may do something
1112}
1113
1114void SkCanvas::onPopCull() {
1115 // do nothing. Subclasses may do something
1116}
1117
reed@android.com8a1c16f2008-12-17 15:59:43 +00001118/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org520cf8b2014-03-20 20:25:14 +00001119#ifdef SK_DEBUG
1120// Ensure that cull rects are monotonically nested in device space.
1121void SkCanvas::validateCull(const SkIRect& devCull) {
1122 if (fCullStack.isEmpty()
1123 || devCull.isEmpty()
1124 || fCullStack.top().contains(devCull)) {
1125 return;
1126 }
1127
1128 SkDEBUGF(("Invalid cull: [%d %d %d %d] (previous cull: [%d %d %d %d])\n",
1129 devCull.x(), devCull.y(), devCull.right(), devCull.bottom(),
1130 fCullStack.top().x(), fCullStack.top().y(),
1131 fCullStack.top().right(), fCullStack.top().bottom()));
1132
1133#ifdef ASSERT_NESTED_CULLING
1134 SkDEBUGFAIL("Invalid cull.");
1135#endif
1136}
1137#endif
1138
1139void SkCanvas::pushCull(const SkRect& cullRect) {
1140 ++fCullCount;
1141 this->onPushCull(cullRect);
1142
1143#ifdef SK_DEBUG
1144 // Map the cull rect into device space.
1145 SkRect mappedCull;
1146 this->getTotalMatrix().mapRect(&mappedCull, cullRect);
1147
1148 // Take clipping into account.
1149 SkIRect devClip, devCull;
1150 mappedCull.roundOut(&devCull);
1151 this->getClipDeviceBounds(&devClip);
1152 if (!devCull.intersect(devClip)) {
1153 devCull.setEmpty();
1154 }
1155
1156 this->validateCull(devCull);
1157 fCullStack.push(devCull); // balanced in popCull
1158#endif
1159}
1160
1161void SkCanvas::popCull() {
1162 SkASSERT(fCullStack.count() == fCullCount);
1163
1164 if (fCullCount > 0) {
1165 --fCullCount;
1166 this->onPopCull();
1167
1168 SkDEBUGCODE(fCullStack.pop());
1169 }
1170}
1171
1172/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001173
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001174void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001175 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001176 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001177 return;
1178 }
1179
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001180 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001181 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001182 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001183 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001184
1185 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001186
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001187 SkRect storage;
1188 const SkRect* bounds = NULL;
1189 if (paint && paint->canComputeFastBounds()) {
1190 bitmap.getBounds(&storage);
1191 matrix.mapRect(&storage);
1192 bounds = &paint->computeFastBounds(storage, &storage);
1193 }
1194
1195 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001196
1197 while (iter.next()) {
1198 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1199 }
1200
1201 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001202}
1203
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001204void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001205 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001206 SkPaint tmp;
1207 if (NULL == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001208 paint = &tmp;
1209 }
reed@google.com4b226022011-01-11 18:32:13 +00001210
reed@google.com8926b162012-03-23 15:36:36 +00001211 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001212 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001213 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001214 paint = &looper.paint();
1215 SkImageFilter* filter = paint->getImageFilter();
1216 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001217 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001218 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001219 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001220 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001221 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001222 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001223 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001224 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblancobe129b22014-08-08 07:14:35 -07001225 SkAutoTUnref<SkImageFilter::Cache> cache(dstDev->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001226 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001227 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001228 SkPaint tmpUnfiltered(*paint);
1229 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001230 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1231 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001232 }
1233 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001234 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001235 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001236 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001237 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001238}
1239
reed@google.com8926b162012-03-23 15:36:36 +00001240void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1241 const SkPaint* paint) {
danakj8f757f52014-11-04 11:48:43 -08001242 TRACE_EVENT0("skia", "SkCanvas::drawSprite()");
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001243 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001244 return;
1245 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001246 SkDEBUGCODE(bitmap.validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001247
reed@google.com8926b162012-03-23 15:36:36 +00001248 SkPaint tmp;
1249 if (NULL == paint) {
1250 paint = &tmp;
1251 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001252
reed@google.com8926b162012-03-23 15:36:36 +00001253 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001254
reed@google.com8926b162012-03-23 15:36:36 +00001255 while (iter.next()) {
1256 paint = &looper.paint();
1257 SkImageFilter* filter = paint->getImageFilter();
1258 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1259 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001260 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001261 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001262 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001263 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001264 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
halcanaryf622a6c2014-10-24 12:54:53 -07001265 const SkIRect clipBounds = bitmap.bounds();
senorblancobe129b22014-08-08 07:14:35 -07001266 SkAutoTUnref<SkImageFilter::Cache> cache(iter.fDevice->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001267 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001268 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001269 SkPaint tmpUnfiltered(*paint);
1270 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001271 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001272 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001273 }
1274 } else {
1275 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1276 }
1277 }
1278 LOOPER_END
1279}
1280
reed@android.com8a1c16f2008-12-17 15:59:43 +00001281/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001282void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001283 SkMatrix m;
1284 m.setTranslate(dx, dy);
1285 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001286}
1287
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001288void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001289 SkMatrix m;
1290 m.setScale(sx, sy);
1291 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001292}
1293
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001294void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001295 SkMatrix m;
1296 m.setRotate(degrees);
1297 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001298}
1299
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001300void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001301 SkMatrix m;
1302 m.setSkew(sx, sy);
1303 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001304}
1305
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001306void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001307 if (matrix.isIdentity()) {
1308 return;
1309 }
1310
reed@android.com8a1c16f2008-12-17 15:59:43 +00001311 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001312 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001313 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001314
1315 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001316}
1317
reed@android.com8a1c16f2008-12-17 15:59:43 +00001318void SkCanvas::setMatrix(const SkMatrix& matrix) {
1319 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001320 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001321 fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001322 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001323}
1324
reed@android.com8a1c16f2008-12-17 15:59:43 +00001325void SkCanvas::resetMatrix() {
1326 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001327
reed@android.com8a1c16f2008-12-17 15:59:43 +00001328 matrix.reset();
1329 this->setMatrix(matrix);
1330}
1331
1332//////////////////////////////////////////////////////////////////////////////
1333
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001334void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001335 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1336 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001337}
1338
1339void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001340#ifdef SK_ENABLE_CLIP_QUICKREJECT
1341 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001342 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001343 return false;
1344 }
1345
reed@google.com3b3e8952012-08-16 20:53:31 +00001346 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001347 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001348 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001349
1350 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001351 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001352 }
1353 }
1354#endif
1355
reed@google.com5c3d1472011-02-22 19:12:23 +00001356 AutoValidateClip avc(this);
1357
reed@android.com8a1c16f2008-12-17 15:59:43 +00001358 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001359 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001360 if (!fAllowSoftClip) {
1361 edgeStyle = kHard_ClipEdgeStyle;
1362 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001363
reed1f836ee2014-07-07 07:49:34 -07001364 if (fMCRec->fMatrix.rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001365 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001366 // the matrix. This means we don't have to a) make a path, and b) tell
1367 // the region code to scan-convert the path, only to discover that it
1368 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001369 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001370
reed1f836ee2014-07-07 07:49:34 -07001371 fMCRec->fMatrix.mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001372 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reedd9544982014-09-09 18:46:22 -07001373 fMCRec->fRasterClip.op(r, this->getBaseLayerSize(), op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001374 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001375 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001376 // and clip against that, since it can handle any matrix. However, to
1377 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1378 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001379 SkPath path;
1380
1381 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001382 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001383 }
1384}
1385
reed73e714e2014-09-04 09:02:23 -07001386static void rasterclip_path(SkRasterClip* rc, const SkCanvas* canvas, const SkPath& devPath,
1387 SkRegion::Op op, bool doAA) {
reedd64c9482014-09-05 17:37:38 -07001388 rc->op(devPath, canvas->getBaseLayerSize(), op, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001389}
1390
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001391void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001392 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001393 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001394 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1395 } else {
1396 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001397 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001398}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001399
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001400void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001401 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001402 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001403 AutoValidateClip avc(this);
1404
1405 fDeviceCMDirty = true;
1406 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001407 if (!fAllowSoftClip) {
1408 edgeStyle = kHard_ClipEdgeStyle;
1409 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001410
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001411 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001412
1413 SkPath devPath;
1414 devPath.addRRect(transformedRRect);
1415
reed73e714e2014-09-04 09:02:23 -07001416 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001417 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001418 }
1419
1420 SkPath path;
1421 path.addRRect(rrect);
1422 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001423 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001424}
1425
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001426void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001427 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1428 SkRect r;
1429 if (!path.isInverseFillType() && path.isRect(&r)) {
1430 this->onClipRect(r, op, edgeStyle);
1431 } else {
1432 this->onClipPath(path, op, edgeStyle);
1433 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001434}
1435
1436void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001437#ifdef SK_ENABLE_CLIP_QUICKREJECT
1438 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001439 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001440 return false;
1441 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001442
reed@google.com3b3e8952012-08-16 20:53:31 +00001443 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001444 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001445 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001446
reed@google.comda17f752012-08-16 18:27:05 +00001447 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001448 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001449 }
1450 }
1451#endif
1452
reed@google.com5c3d1472011-02-22 19:12:23 +00001453 AutoValidateClip avc(this);
1454
reed@android.com8a1c16f2008-12-17 15:59:43 +00001455 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001456 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001457 if (!fAllowSoftClip) {
1458 edgeStyle = kHard_ClipEdgeStyle;
1459 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001460
1461 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001462 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001463
reed@google.comfe701122011-11-08 19:41:23 +00001464 // Check if the transfomation, or the original path itself
1465 // made us empty. Note this can also happen if we contained NaN
1466 // values. computing the bounds detects this, and will set our
1467 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1468 if (devPath.getBounds().isEmpty()) {
1469 // resetting the path will remove any NaN or other wanky values
1470 // that might upset our scan converter.
1471 devPath.reset();
1472 }
1473
reed@google.com5c3d1472011-02-22 19:12:23 +00001474 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001475 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001476
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001477 if (fAllowSimplifyClip) {
1478 devPath.reset();
1479 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1480 const SkClipStack* clipStack = getClipStack();
1481 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1482 const SkClipStack::Element* element;
1483 while ((element = iter.next())) {
1484 SkClipStack::Element::Type type = element->getType();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001485 SkPath operand;
commit-bot@chromium.org2a67e122014-05-19 13:53:10 +00001486 if (type != SkClipStack::Element::kEmpty_Type) {
1487 element->asPath(&operand);
1488 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001489 SkRegion::Op elementOp = element->getOp();
1490 if (elementOp == SkRegion::kReplace_Op) {
1491 devPath = operand;
1492 } else {
1493 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1494 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001495 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1496 // perhaps we need an API change to avoid this sort of mixed-signals about
1497 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001498 if (element->isAA()) {
1499 edgeStyle = kSoft_ClipEdgeStyle;
1500 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001501 }
1502 op = SkRegion::kReplace_Op;
1503 }
1504
reed73e714e2014-09-04 09:02:23 -07001505 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001506}
1507
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001508void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001509 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001510}
1511
1512void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001513 AutoValidateClip avc(this);
1514
reed@android.com8a1c16f2008-12-17 15:59:43 +00001515 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001516 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001517
reed@google.com5c3d1472011-02-22 19:12:23 +00001518 // todo: signal fClipStack that we have a region, and therefore (I guess)
1519 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001520 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001521
reed1f836ee2014-07-07 07:49:34 -07001522 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001523}
1524
reed@google.com819c9212011-02-23 18:56:55 +00001525#ifdef SK_DEBUG
1526void SkCanvas::validateClip() const {
1527 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001528 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001529 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001530 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001531 return;
1532 }
1533
reed@google.com819c9212011-02-23 18:56:55 +00001534 SkIRect ir;
1535 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001536 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001537
robertphillips@google.com80214e22012-07-20 15:33:18 +00001538 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001539 const SkClipStack::Element* element;
1540 while ((element = iter.next()) != NULL) {
1541 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001542 case SkClipStack::Element::kRect_Type:
1543 element->getRect().round(&ir);
1544 tmpClip.op(ir, element->getOp());
1545 break;
1546 case SkClipStack::Element::kEmpty_Type:
1547 tmpClip.setEmpty();
1548 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001549 default: {
1550 SkPath path;
1551 element->asPath(&path);
reed73e714e2014-09-04 09:02:23 -07001552 rasterclip_path(&tmpClip, this, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001553 break;
1554 }
reed@google.com819c9212011-02-23 18:56:55 +00001555 }
1556 }
reed@google.com819c9212011-02-23 18:56:55 +00001557}
1558#endif
1559
reed@google.com90c07ea2012-04-13 13:50:27 +00001560void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001561 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001562 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001563
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001564 while ((element = iter.next()) != NULL) {
fmalitac3b589a2014-06-05 12:40:07 -07001565 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001566 }
1567}
1568
reed@google.com5c3d1472011-02-22 19:12:23 +00001569///////////////////////////////////////////////////////////////////////////////
1570
reed@google.com754de5f2014-02-24 19:38:20 +00001571bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001572 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001573}
1574
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001575bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001576 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001577}
1578
reed@google.com3b3e8952012-08-16 20:53:31 +00001579bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001580 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001581 return true;
1582
reed1f836ee2014-07-07 07:49:34 -07001583 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001584 return true;
1585 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001586
reed1f836ee2014-07-07 07:49:34 -07001587 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001588 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001589 fMCRec->fMatrix.mapRect(&dst, rect);
reed@android.coma380ae42009-07-21 01:17:02 +00001590 SkIRect idst;
1591 dst.roundOut(&idst);
reed1f836ee2014-07-07 07:49:34 -07001592 return !SkIRect::Intersects(idst, fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001593 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001594 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001595
reed@android.coma380ae42009-07-21 01:17:02 +00001596 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001597 // TODO: should we use | instead, or compare all 4 at once?
1598 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001599 return true;
1600 }
reed@google.comc0784db2013-12-13 21:16:12 +00001601 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001602 return true;
1603 }
1604 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001605 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001606}
1607
reed@google.com3b3e8952012-08-16 20:53:31 +00001608bool SkCanvas::quickReject(const SkPath& path) const {
1609 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001610}
1611
reed@google.com3b3e8952012-08-16 20:53:31 +00001612bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001613 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001614 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001615 return false;
1616 }
1617
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001618 SkMatrix inverse;
1619 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001620 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001621 if (bounds) {
1622 bounds->setEmpty();
1623 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001624 return false;
1625 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001626
bsalomon49f085d2014-09-05 13:34:00 -07001627 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001628 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001629 // adjust it outwards in case we are antialiasing
1630 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001631
reed@google.com8f4d2302013-12-17 16:44:46 +00001632 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1633 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001634 inverse.mapRect(bounds, r);
1635 }
1636 return true;
1637}
1638
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001639bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001640 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001641 if (clip.isEmpty()) {
1642 if (bounds) {
1643 bounds->setEmpty();
1644 }
1645 return false;
1646 }
1647
bsalomon49f085d2014-09-05 13:34:00 -07001648 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001649 *bounds = clip.getBounds();
1650 }
1651 return true;
1652}
1653
reed@android.com8a1c16f2008-12-17 15:59:43 +00001654const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001655 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001656}
1657
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001658const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001659 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001660}
1661
reed@google.com9c135db2014-03-12 18:28:35 +00001662GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1663 SkBaseDevice* dev = this->getTopDevice();
1664 return dev ? dev->accessRenderTarget() : NULL;
1665}
1666
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001667SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001668 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001669 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001670}
1671
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001672GrContext* SkCanvas::getGrContext() {
1673#if SK_SUPPORT_GPU
1674 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07001675 if (device) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001676 GrRenderTarget* renderTarget = device->accessRenderTarget();
bsalomon49f085d2014-09-05 13:34:00 -07001677 if (renderTarget) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001678 return renderTarget->getContext();
1679 }
1680 }
1681#endif
1682
1683 return NULL;
1684
1685}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001686
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001687void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1688 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08001689 TRACE_EVENT0("skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001690 if (outer.isEmpty()) {
1691 return;
1692 }
1693 if (inner.isEmpty()) {
1694 this->drawRRect(outer, paint);
1695 return;
1696 }
1697
1698 // We don't have this method (yet), but technically this is what we should
1699 // be able to assert...
1700 // SkASSERT(outer.contains(inner));
1701 //
1702 // For now at least check for containment of bounds
1703 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1704
1705 this->onDrawDRRect(outer, inner, paint);
1706}
1707
reed@android.com8a1c16f2008-12-17 15:59:43 +00001708//////////////////////////////////////////////////////////////////////////////
1709// These are the virtual drawing methods
1710//////////////////////////////////////////////////////////////////////////////
1711
reed@google.com2a981812011-04-14 18:59:28 +00001712void SkCanvas::clear(SkColor color) {
1713 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001714 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001715 while (iter.next()) {
1716 iter.fDevice->clear(color);
1717 }
1718}
1719
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001720void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07001721 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001722 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1723 }
1724}
1725
reed@android.com8a1c16f2008-12-17 15:59:43 +00001726void SkCanvas::drawPaint(const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08001727 TRACE_EVENT0("skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001728 this->internalDrawPaint(paint);
1729}
1730
1731void SkCanvas::internalDrawPaint(const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001732 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001733
1734 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001735 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001736 }
1737
reed@google.com4e2b3d32011-04-07 14:18:59 +00001738 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001739}
1740
1741void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1742 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08001743 TRACE_EVENT1("skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001744 if ((long)count <= 0) {
1745 return;
1746 }
1747
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001748 SkRect r, storage;
1749 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001750 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001751 // special-case 2 points (common for drawing a single line)
1752 if (2 == count) {
1753 r.set(pts[0], pts[1]);
1754 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001755 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001756 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001757 bounds = &paint.computeFastStrokeBounds(r, &storage);
1758 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001759 return;
1760 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001761 }
reed@google.coma584aed2012-05-16 14:06:02 +00001762
reed@android.com8a1c16f2008-12-17 15:59:43 +00001763 SkASSERT(pts != NULL);
1764
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001765 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001766
reed@android.com8a1c16f2008-12-17 15:59:43 +00001767 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001768 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001769 }
reed@google.com4b226022011-01-11 18:32:13 +00001770
reed@google.com4e2b3d32011-04-07 14:18:59 +00001771 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001772}
1773
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001774void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08001775 TRACE_EVENT0("skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001776 SkRect storage;
1777 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001778 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001779 bounds = &paint.computeFastBounds(r, &storage);
1780 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001781 return;
1782 }
1783 }
reed@google.com4b226022011-01-11 18:32:13 +00001784
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001785 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001786
1787 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001788 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001789 }
1790
reed@google.com4e2b3d32011-04-07 14:18:59 +00001791 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001792}
1793
reed@google.com4ed0fb72012-12-12 20:48:18 +00001794void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08001795 TRACE_EVENT0("skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001796 SkRect storage;
1797 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001798 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001799 bounds = &paint.computeFastBounds(oval, &storage);
1800 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001801 return;
1802 }
1803 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001804
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001805 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001806
1807 while (iter.next()) {
1808 iter.fDevice->drawOval(iter, oval, looper.paint());
1809 }
1810
1811 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001812}
1813
1814void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08001815 TRACE_EVENT0("skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001816 SkRect storage;
1817 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001818 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001819 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1820 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001821 return;
1822 }
1823 }
1824
1825 if (rrect.isRect()) {
1826 // call the non-virtual version
1827 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001828 return;
1829 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001830 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001831 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1832 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001833 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001834
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001835 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001836
1837 while (iter.next()) {
1838 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1839 }
1840
1841 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001842}
1843
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001844void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1845 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001846 SkRect storage;
1847 const SkRect* bounds = NULL;
1848 if (paint.canComputeFastBounds()) {
1849 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1850 if (this->quickReject(*bounds)) {
1851 return;
1852 }
1853 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001854
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001855 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001856
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001857 while (iter.next()) {
1858 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1859 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001860
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001861 LOOPER_END
1862}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001863
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001864void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08001865 TRACE_EVENT0("skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00001866 if (!path.isFinite()) {
1867 return;
1868 }
1869
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001870 SkRect storage;
1871 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001872 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001873 const SkRect& pathBounds = path.getBounds();
1874 bounds = &paint.computeFastBounds(pathBounds, &storage);
1875 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001876 return;
1877 }
1878 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00001879
1880 const SkRect& r = path.getBounds();
1881 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00001882 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001883 this->internalDrawPaint(paint);
1884 }
1885 return;
1886 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001887
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001888 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001889
1890 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001891 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001892 }
1893
reed@google.com4e2b3d32011-04-07 14:18:59 +00001894 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001895}
1896
piotaixrb5fae932014-09-24 13:03:30 -07001897void SkCanvas::drawImage(const SkImage* image, SkScalar left, SkScalar top,
1898 const SkPaint* paint) {
danakj8f757f52014-11-04 11:48:43 -08001899 TRACE_EVENT0("skia", "SkCanvas::drawImage()");
piotaixrb5fae932014-09-24 13:03:30 -07001900 image->draw(this, left, top, paint);
1901}
1902
1903void SkCanvas::drawImageRect(const SkImage* image, const SkRect* src,
1904 const SkRect& dst,
1905 const SkPaint* paint) {
danakj8f757f52014-11-04 11:48:43 -08001906 TRACE_EVENT0("skia", "SkCanvas::drawImageRect()");
piotaixr5ceff912014-09-26 07:36:26 -07001907 image->drawRect(this, src, dst, paint);
piotaixrb5fae932014-09-24 13:03:30 -07001908}
1909
reed@android.com8a1c16f2008-12-17 15:59:43 +00001910void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1911 const SkPaint* paint) {
danakj8f757f52014-11-04 11:48:43 -08001912 TRACE_EVENT0("skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00001913 SkDEBUGCODE(bitmap.validate();)
1914
reed@google.com3d608122011-11-21 15:16:16 +00001915 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001916 SkRect bounds = {
1917 x, y,
1918 x + SkIntToScalar(bitmap.width()),
1919 y + SkIntToScalar(bitmap.height())
1920 };
1921 if (paint) {
1922 (void)paint->computeFastBounds(bounds, &bounds);
1923 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001924 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001925 return;
1926 }
1927 }
reed@google.com4b226022011-01-11 18:32:13 +00001928
reed@android.com8a1c16f2008-12-17 15:59:43 +00001929 SkMatrix matrix;
1930 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001931 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001932}
1933
reed@google.com9987ec32011-09-07 11:57:52 +00001934// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001935void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001936 const SkRect& dst, const SkPaint* paint,
1937 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001938 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001939 return;
1940 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001941
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001942 SkRect storage;
1943 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00001944 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001945 if (paint) {
1946 bounds = &paint->computeFastBounds(dst, &storage);
1947 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001948 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001949 return;
1950 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001951 }
reed@google.com3d608122011-11-21 15:16:16 +00001952
reed@google.com33535f32012-09-25 15:37:50 +00001953 SkLazyPaint lazy;
1954 if (NULL == paint) {
1955 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001956 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001957
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001958 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001959
reed@google.com33535f32012-09-25 15:37:50 +00001960 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001961 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001962 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001963
reed@google.com33535f32012-09-25 15:37:50 +00001964 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001965}
1966
reed@google.com71121732012-09-18 15:14:33 +00001967void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001968 const SkRect& dst, const SkPaint* paint,
1969 DrawBitmapRectFlags flags) {
danakj8f757f52014-11-04 11:48:43 -08001970 TRACE_EVENT0("skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00001971 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001972 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001973}
1974
reed@android.com8a1c16f2008-12-17 15:59:43 +00001975void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1976 const SkPaint* paint) {
danakj8f757f52014-11-04 11:48:43 -08001977 TRACE_EVENT0("skia", "SkCanvas::drawBitmapMatrix()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00001978 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001979 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001980}
1981
reed@google.com9987ec32011-09-07 11:57:52 +00001982void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1983 const SkIRect& center, const SkRect& dst,
1984 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001985 if (bitmap.drawsNothing()) {
1986 return;
1987 }
reed@google.com3d608122011-11-21 15:16:16 +00001988 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001989 SkRect storage;
1990 const SkRect* bounds = &dst;
1991 if (paint) {
1992 bounds = &paint->computeFastBounds(dst, &storage);
1993 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001994 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001995 return;
1996 }
1997 }
1998
reed@google.com9987ec32011-09-07 11:57:52 +00001999 const int32_t w = bitmap.width();
2000 const int32_t h = bitmap.height();
2001
2002 SkIRect c = center;
2003 // pin center to the bounds of the bitmap
2004 c.fLeft = SkMax32(0, center.fLeft);
2005 c.fTop = SkMax32(0, center.fTop);
2006 c.fRight = SkPin32(center.fRight, c.fLeft, w);
2007 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
2008
reed@google.com71121732012-09-18 15:14:33 +00002009 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002010 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00002011 };
2012 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002013 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00002014 };
reed@google.com9987ec32011-09-07 11:57:52 +00002015 SkScalar dstX[4] = {
2016 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
2017 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
2018 };
2019 SkScalar dstY[4] = {
2020 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2021 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2022 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002023
reed@google.com9987ec32011-09-07 11:57:52 +00002024 if (dstX[1] > dstX[2]) {
2025 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2026 dstX[2] = dstX[1];
2027 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002028
reed@google.com9987ec32011-09-07 11:57:52 +00002029 if (dstY[1] > dstY[2]) {
2030 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2031 dstY[2] = dstY[1];
2032 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002033
reed@google.com9987ec32011-09-07 11:57:52 +00002034 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002035 SkRect s, d;
2036
reed@google.com9987ec32011-09-07 11:57:52 +00002037 s.fTop = srcY[y];
2038 s.fBottom = srcY[y+1];
2039 d.fTop = dstY[y];
2040 d.fBottom = dstY[y+1];
2041 for (int x = 0; x < 3; x++) {
2042 s.fLeft = srcX[x];
2043 s.fRight = srcX[x+1];
2044 d.fLeft = dstX[x];
2045 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002046 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002047 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002048 }
2049 }
2050}
2051
2052void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
2053 const SkRect& dst, const SkPaint* paint) {
danakj8f757f52014-11-04 11:48:43 -08002054 TRACE_EVENT0("skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002055 SkDEBUGCODE(bitmap.validate();)
2056
2057 // Need a device entry-point, so gpu can use a mesh
2058 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2059}
2060
reed@google.comf67e4cf2011-03-15 20:56:58 +00002061class SkDeviceFilteredPaint {
2062public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002063 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
2064 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002065 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002066 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002067 newPaint->setFlags(flags.fFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002068 fPaint = newPaint;
2069 } else {
2070 fPaint = &paint;
2071 }
2072 }
2073
reed@google.comf67e4cf2011-03-15 20:56:58 +00002074 const SkPaint& paint() const { return *fPaint; }
2075
2076private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002077 const SkPaint* fPaint;
2078 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002079};
2080
bungeman@google.com52c748b2011-08-22 21:30:43 +00002081void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2082 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002083 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002084 draw.fDevice->drawRect(draw, r, paint);
2085 } else {
2086 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002087 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002088 draw.fDevice->drawRect(draw, r, p);
2089 }
2090}
2091
2092void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2093 const char text[], size_t byteLength,
2094 SkScalar x, SkScalar y) {
2095 SkASSERT(byteLength == 0 || text != NULL);
2096
2097 // nothing to draw
2098 if (text == NULL || byteLength == 0 ||
2099 draw.fClip->isEmpty() ||
2100 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2101 return;
2102 }
2103
2104 SkScalar width = 0;
2105 SkPoint start;
2106
2107 start.set(0, 0); // to avoid warning
2108 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2109 SkPaint::kStrikeThruText_Flag)) {
2110 width = paint.measureText(text, byteLength);
2111
2112 SkScalar offsetX = 0;
2113 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2114 offsetX = SkScalarHalf(width);
2115 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2116 offsetX = width;
2117 }
2118 start.set(x - offsetX, y);
2119 }
2120
2121 if (0 == width) {
2122 return;
2123 }
2124
2125 uint32_t flags = paint.getFlags();
2126
2127 if (flags & (SkPaint::kUnderlineText_Flag |
2128 SkPaint::kStrikeThruText_Flag)) {
2129 SkScalar textSize = paint.getTextSize();
2130 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2131 SkRect r;
2132
2133 r.fLeft = start.fX;
2134 r.fRight = start.fX + width;
2135
2136 if (flags & SkPaint::kUnderlineText_Flag) {
2137 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2138 start.fY);
2139 r.fTop = offset;
2140 r.fBottom = offset + height;
2141 DrawRect(draw, paint, r, textSize);
2142 }
2143 if (flags & SkPaint::kStrikeThruText_Flag) {
2144 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2145 start.fY);
2146 r.fTop = offset;
2147 r.fBottom = offset + height;
2148 DrawRect(draw, paint, r, textSize);
2149 }
2150 }
2151}
2152
reed@google.come0d9ce82014-04-23 04:00:17 +00002153void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2154 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002155 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002156
2157 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002158 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002159 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002160 DrawTextDecorations(iter, dfp.paint(),
2161 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002162 }
2163
reed@google.com4e2b3d32011-04-07 14:18:59 +00002164 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002165}
2166
reed@google.come0d9ce82014-04-23 04:00:17 +00002167void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2168 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002169 SkPoint textOffset = SkPoint::Make(0, 0);
2170
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002171 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002172
reed@android.com8a1c16f2008-12-17 15:59:43 +00002173 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002174 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002175 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002176 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002177 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002178
reed@google.com4e2b3d32011-04-07 14:18:59 +00002179 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002180}
2181
reed@google.come0d9ce82014-04-23 04:00:17 +00002182void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2183 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002184
2185 SkPoint textOffset = SkPoint::Make(0, constY);
2186
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002187 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002188
reed@android.com8a1c16f2008-12-17 15:59:43 +00002189 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002190 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002191 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002192 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002193 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002194
reed@google.com4e2b3d32011-04-07 14:18:59 +00002195 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002196}
2197
reed@google.come0d9ce82014-04-23 04:00:17 +00002198void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2199 const SkMatrix* matrix, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002200 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002201
reed@android.com8a1c16f2008-12-17 15:59:43 +00002202 while (iter.next()) {
2203 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002204 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002205 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002206
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002207 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002208}
2209
fmalita00d5c2c2014-08-21 08:53:26 -07002210void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2211 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002212
fmalita19653d12014-10-16 11:53:30 -07002213 if (paint.canComputeFastBounds()) {
fmalita7ba7aa72014-08-29 09:46:36 -07002214 SkRect storage;
2215
2216 if (this->quickReject(paint.computeFastBounds(blob->bounds().makeOffset(x, y), &storage))) {
2217 return;
2218 }
2219 }
2220
fmalitaaa1b9122014-08-28 14:32:24 -07002221 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
fmalita00d5c2c2014-08-21 08:53:26 -07002222
fmalitaaa1b9122014-08-28 14:32:24 -07002223 while (iter.next()) {
2224 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
2225 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint());
fmalita00d5c2c2014-08-21 08:53:26 -07002226 }
2227
fmalitaaa1b9122014-08-28 14:32:24 -07002228 LOOPER_END
fmalita00d5c2c2014-08-21 08:53:26 -07002229}
2230
reed@google.come0d9ce82014-04-23 04:00:17 +00002231// These will become non-virtual, so they always call the (virtual) onDraw... method
2232void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2233 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002234 TRACE_EVENT0("skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002235 this->onDrawText(text, byteLength, x, y, paint);
2236}
2237void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2238 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002239 TRACE_EVENT0("skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002240 this->onDrawPosText(text, byteLength, pos, paint);
2241}
2242void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2243 SkScalar constY, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002244 TRACE_EVENT0("skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002245 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2246}
2247void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2248 const SkMatrix* matrix, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002249 TRACE_EVENT0("skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002250 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2251}
fmalita00d5c2c2014-08-21 08:53:26 -07002252void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2253 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002254 TRACE_EVENT0("skia", "SkCanvas::drawTextBlob()");
bsalomon49f085d2014-09-05 13:34:00 -07002255 if (blob) {
fmalita00d5c2c2014-08-21 08:53:26 -07002256 this->onDrawTextBlob(blob, x, y, paint);
2257 }
2258}
reed@google.come0d9ce82014-04-23 04:00:17 +00002259
reed@android.com8a1c16f2008-12-17 15:59:43 +00002260void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2261 const SkPoint verts[], const SkPoint texs[],
2262 const SkColor colors[], SkXfermode* xmode,
2263 const uint16_t indices[], int indexCount,
2264 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002265 TRACE_EVENT0("skia", "SkCanvas::drawVertices()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002266 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002267
reed@android.com8a1c16f2008-12-17 15:59:43 +00002268 while (iter.next()) {
2269 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002270 colors, xmode, indices, indexCount,
2271 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002272 }
reed@google.com4b226022011-01-11 18:32:13 +00002273
reed@google.com4e2b3d32011-04-07 14:18:59 +00002274 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002275}
2276
dandovb3c9d1c2014-08-12 08:34:29 -07002277void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2278 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002279 TRACE_EVENT0("skia", "SkCanvas::drawPatch()");
dandovb3c9d1c2014-08-12 08:34:29 -07002280 if (NULL == cubics) {
2281 return;
2282 }
mtklein6cfa73a2014-08-13 13:33:49 -07002283
dandovecfff212014-08-04 10:02:00 -07002284 // Since a patch is always within the convex hull of the control points, we discard it when its
2285 // bounding rectangle is completely outside the current clip.
2286 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002287 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002288 if (this->quickReject(bounds)) {
2289 return;
2290 }
mtklein6cfa73a2014-08-13 13:33:49 -07002291
dandovb3c9d1c2014-08-12 08:34:29 -07002292 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2293}
2294
2295void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2296 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2297
dandovecfff212014-08-04 10:02:00 -07002298 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
mtklein6cfa73a2014-08-13 13:33:49 -07002299
dandovecfff212014-08-04 10:02:00 -07002300 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002301 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002302 }
mtklein6cfa73a2014-08-13 13:33:49 -07002303
dandovecfff212014-08-04 10:02:00 -07002304 LOOPER_END
2305}
2306
reed@android.com8a1c16f2008-12-17 15:59:43 +00002307//////////////////////////////////////////////////////////////////////////////
2308// These methods are NOT virtual, and therefore must call back into virtual
2309// methods, rather than actually drawing themselves.
2310//////////////////////////////////////////////////////////////////////////////
2311
2312void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002313 SkXfermode::Mode mode) {
danakj8f757f52014-11-04 11:48:43 -08002314 TRACE_EVENT0("skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002315 SkPaint paint;
2316
2317 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002318 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002319 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002320 }
2321 this->drawPaint(paint);
2322}
2323
reed@android.com845fdac2009-06-23 03:01:32 +00002324void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj8f757f52014-11-04 11:48:43 -08002325 TRACE_EVENT0("skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002326 SkPaint paint;
2327
2328 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002329 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002330 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002331 }
2332 this->drawPaint(paint);
2333}
2334
2335void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002336 TRACE_EVENT0("skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002337 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002338
reed@android.com8a1c16f2008-12-17 15:59:43 +00002339 pt.set(x, y);
2340 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2341}
2342
2343void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj8f757f52014-11-04 11:48:43 -08002344 TRACE_EVENT0("skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002345 SkPoint pt;
2346 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002347
reed@android.com8a1c16f2008-12-17 15:59:43 +00002348 pt.set(x, y);
2349 paint.setColor(color);
2350 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2351}
2352
2353void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2354 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002355 TRACE_EVENT0("skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002356 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002357
reed@android.com8a1c16f2008-12-17 15:59:43 +00002358 pts[0].set(x0, y0);
2359 pts[1].set(x1, y1);
2360 this->drawPoints(kLines_PointMode, 2, pts, paint);
2361}
2362
2363void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2364 SkScalar right, SkScalar bottom,
2365 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002366 TRACE_EVENT0("skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002367 SkRect r;
2368
2369 r.set(left, top, right, bottom);
2370 this->drawRect(r, paint);
2371}
2372
2373void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2374 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002375 TRACE_EVENT0("skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002376 if (radius < 0) {
2377 radius = 0;
2378 }
2379
2380 SkRect r;
2381 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002382 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002383}
2384
2385void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2386 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002387 TRACE_EVENT0("skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002388 if (rx > 0 && ry > 0) {
2389 if (paint.canComputeFastBounds()) {
2390 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002391 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002392 return;
2393 }
2394 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002395 SkRRect rrect;
2396 rrect.setRectXY(r, rx, ry);
2397 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002398 } else {
2399 this->drawRect(r, paint);
2400 }
2401}
2402
reed@android.com8a1c16f2008-12-17 15:59:43 +00002403void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2404 SkScalar sweepAngle, bool useCenter,
2405 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002406 TRACE_EVENT0("skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002407 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2408 this->drawOval(oval, paint);
2409 } else {
2410 SkPath path;
2411 if (useCenter) {
2412 path.moveTo(oval.centerX(), oval.centerY());
2413 }
2414 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2415 if (useCenter) {
2416 path.close();
2417 }
2418 this->drawPath(path, paint);
2419 }
2420}
2421
2422void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2423 const SkPath& path, SkScalar hOffset,
2424 SkScalar vOffset, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002425 TRACE_EVENT0("skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002426 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002427
reed@android.com8a1c16f2008-12-17 15:59:43 +00002428 matrix.setTranslate(hOffset, vOffset);
2429 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2430}
2431
reed@android.comf76bacf2009-05-13 14:00:33 +00002432///////////////////////////////////////////////////////////////////////////////
robertphillips9b14f262014-06-04 05:40:44 -07002433void SkCanvas::EXPERIMENTAL_optimize(const SkPicture* picture) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002434 SkBaseDevice* device = this->getDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002435 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002436 device->EXPERIMENTAL_optimize(picture);
2437 }
2438}
reed@android.comf76bacf2009-05-13 14:00:33 +00002439
robertphillips9b14f262014-06-04 05:40:44 -07002440void SkCanvas::drawPicture(const SkPicture* picture) {
danakj8f757f52014-11-04 11:48:43 -08002441 TRACE_EVENT0("skia", "SkCanvas::drawPicture()");
bsalomon49f085d2014-09-05 13:34:00 -07002442 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002443 this->onDrawPicture(picture, NULL, NULL);
robertphillips9b14f262014-06-04 05:40:44 -07002444 }
2445}
2446
reedd5fa1a42014-08-09 11:08:05 -07002447void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
danakj8f757f52014-11-04 11:48:43 -08002448 TRACE_EVENT0("skia", "SkCanvas::drawPicture(SkMatrix, SkPaint)");
bsalomon49f085d2014-09-05 13:34:00 -07002449 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002450 if (matrix && matrix->isIdentity()) {
2451 matrix = NULL;
2452 }
2453 this->onDrawPicture(picture, matrix, paint);
2454 }
2455}
robertphillips9b14f262014-06-04 05:40:44 -07002456
reedd5fa1a42014-08-09 11:08:05 -07002457void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2458 const SkPaint* paint) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002459 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002460 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002461 // Canvas has to first give the device the opportunity to render
2462 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002463 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002464 return; // the device has rendered the entire picture
2465 }
2466 }
2467
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002468 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
reedd5fa1a42014-08-09 11:08:05 -07002469
robertphillipsc5ba71d2014-09-04 08:42:50 -07002470 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002471}
2472
reed@android.com8a1c16f2008-12-17 15:59:43 +00002473///////////////////////////////////////////////////////////////////////////////
2474///////////////////////////////////////////////////////////////////////////////
2475
2476SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002477 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002478
2479 SkASSERT(canvas);
2480
2481 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2482 fDone = !fImpl->next();
2483}
2484
2485SkCanvas::LayerIter::~LayerIter() {
2486 fImpl->~SkDrawIter();
2487}
2488
2489void SkCanvas::LayerIter::next() {
2490 fDone = !fImpl->next();
2491}
2492
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002493SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002494 return fImpl->getDevice();
2495}
2496
2497const SkMatrix& SkCanvas::LayerIter::matrix() const {
2498 return fImpl->getMatrix();
2499}
2500
2501const SkPaint& SkCanvas::LayerIter::paint() const {
2502 const SkPaint* paint = fImpl->getPaint();
2503 if (NULL == paint) {
2504 paint = &fDefaultPaint;
2505 }
2506 return *paint;
2507}
2508
2509const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2510int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2511int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002512
2513///////////////////////////////////////////////////////////////////////////////
2514
fmalitac3b589a2014-06-05 12:40:07 -07002515SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002516
2517///////////////////////////////////////////////////////////////////////////////
2518
2519static bool supported_for_raster_canvas(const SkImageInfo& info) {
2520 switch (info.alphaType()) {
2521 case kPremul_SkAlphaType:
2522 case kOpaque_SkAlphaType:
2523 break;
2524 default:
2525 return false;
2526 }
2527
2528 switch (info.colorType()) {
2529 case kAlpha_8_SkColorType:
2530 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002531 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002532 break;
2533 default:
2534 return false;
2535 }
2536
2537 return true;
2538}
2539
2540SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) {
2541 if (!supported_for_raster_canvas(info)) {
2542 return NULL;
2543 }
2544
2545 SkBitmap bitmap;
reed84825042014-09-02 12:50:45 -07002546 if (!bitmap.tryAllocPixels(info)) {
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002547 return NULL;
2548 }
2549
2550 // should this functionality be moved into allocPixels()?
2551 if (!bitmap.info().isOpaque()) {
2552 bitmap.eraseColor(0);
2553 }
2554 return SkNEW_ARGS(SkCanvas, (bitmap));
2555}
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002556
2557SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2558 if (!supported_for_raster_canvas(info)) {
2559 return NULL;
2560 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002561
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002562 SkBitmap bitmap;
2563 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2564 return NULL;
2565 }
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002566 return SkNEW_ARGS(SkCanvas, (bitmap));
2567}
reedd5fa1a42014-08-09 11:08:05 -07002568
2569///////////////////////////////////////////////////////////////////////////////
2570
2571SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002572 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07002573 : fCanvas(canvas)
2574 , fSaveCount(canvas->getSaveCount())
2575{
bsalomon49f085d2014-09-05 13:34:00 -07002576 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002577 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07002578 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002579 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07002580 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002581 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07002582 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002583 canvas->save();
2584 }
mtklein6cfa73a2014-08-13 13:33:49 -07002585
bsalomon49f085d2014-09-05 13:34:00 -07002586 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002587 canvas->concat(*matrix);
2588 }
2589}
2590
2591SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
2592 fCanvas->restoreToCount(fSaveCount);
2593}