blob: 45f5e074a9fb2abb9ba949cf5ba960c53193586d [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
reed@android.com8a1c16f2008-12-17 15:59:43 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00004 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00005 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00007 */
8
epoger@google.comec3ed6a2011-07-28 14:26:00 +00009
reed@android.com8a1c16f2008-12-17 15:59:43 +000010#include "SkCanvas.h"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000011#include "SkBitmapDevice.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkBounder.h"
senorblanco@chromium.org9c397442012-09-27 21:57:45 +000013#include "SkDeviceImageFilterProxy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000014#include "SkDraw.h"
15#include "SkDrawFilter.h"
16#include "SkDrawLooper.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000017#include "SkMetaData.h"
caryclark@google.com45a75fb2013-04-25 13:34:40 +000018#include "SkPathOps.h"
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"
bungeman@google.com52c748b2011-08-22 21:30:43 +000025#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000026#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000027#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000028
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000029#if SK_SUPPORT_GPU
30#include "GrRenderTarget.h"
31#endif
32
reed@google.comda17f752012-08-16 18:27:05 +000033// experimental for faster tiled drawing...
34//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000035
reed@android.com8a1c16f2008-12-17 15:59:43 +000036//#define SK_TRACE_SAVERESTORE
37
38#ifdef SK_TRACE_SAVERESTORE
39 static int gLayerCounter;
40 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
41 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
42
43 static int gRecCounter;
44 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
45 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
46
47 static int gCanvasCounter;
48 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
49 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
50#else
51 #define inc_layer()
52 #define dec_layer()
53 #define inc_rec()
54 #define dec_rec()
55 #define inc_canvas()
56 #define dec_canvas()
57#endif
58
reed@google.comea033602012-12-14 13:13:55 +000059#ifdef SK_DEBUG
60#include "SkPixelRef.h"
61
reed@google.comf53d0a92013-01-30 13:17:32 +000062/*
63 * Some pixelref subclasses can support being "locked" from another thread
64 * during the lock-scope of skia calling them. In these instances, this balance
65 * check will fail, but may not be indicative of a problem, so we allow a build
66 * flag to disable this check.
67 *
68 * Potentially another fix would be to have a (debug-only) virtual or flag on
69 * pixelref, which could tell us at runtime if this check is valid. That would
70 * eliminate the need for this heavy-handed build check.
71 */
72#ifdef SK_DISABLE_PIXELREF_LOCKCOUNT_BALANCE_CHECK
73class AutoCheckLockCountBalance {
74public:
75 AutoCheckLockCountBalance(const SkBitmap&) { /* do nothing */ }
76};
77#else
reed@google.comea033602012-12-14 13:13:55 +000078class AutoCheckLockCountBalance {
79public:
80 AutoCheckLockCountBalance(const SkBitmap& bm) : fPixelRef(bm.pixelRef()) {
81 fLockCount = fPixelRef ? fPixelRef->getLockCount() : 0;
82 }
83 ~AutoCheckLockCountBalance() {
84 const int count = fPixelRef ? fPixelRef->getLockCount() : 0;
85 SkASSERT(count == fLockCount);
86 }
87
88private:
89 const SkPixelRef* fPixelRef;
90 int fLockCount;
91};
reed@google.comf53d0a92013-01-30 13:17:32 +000092#endif
reed@google.comea033602012-12-14 13:13:55 +000093
94class AutoCheckNoSetContext {
95public:
96 AutoCheckNoSetContext(const SkPaint& paint) : fPaint(paint) {
97 this->assertNoSetContext(fPaint);
98 }
99 ~AutoCheckNoSetContext() {
100 this->assertNoSetContext(fPaint);
101 }
102
103private:
104 const SkPaint& fPaint;
105
106 void assertNoSetContext(const SkPaint& paint) {
107 SkShader* s = paint.getShader();
108 if (s) {
109 SkASSERT(!s->setContextHasBeenCalled());
110 }
111 }
112};
113
114#define CHECK_LOCKCOUNT_BALANCE(bitmap) AutoCheckLockCountBalance clcb(bitmap)
115#define CHECK_SHADER_NOSETCONTEXT(paint) AutoCheckNoSetContext cshsc(paint)
116
117#else
118 #define CHECK_LOCKCOUNT_BALANCE(bitmap)
119 #define CHECK_SHADER_NOSETCONTEXT(paint)
120#endif
121
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000122typedef SkTLazy<SkPaint> SkLazyPaint;
123
reed@google.com97af1a62012-08-28 12:19:02 +0000124void SkCanvas::predrawNotify() {
125 if (fSurfaceBase) {
commit-bot@chromium.orgc4c98702013-04-22 14:28:01 +0000126 fSurfaceBase->aboutToDraw(SkSurface::kRetain_ContentChangeMode);
reed@google.com97af1a62012-08-28 12:19:02 +0000127 }
128}
129
reed@android.com8a1c16f2008-12-17 15:59:43 +0000130///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000131
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000132/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000133 The clip/matrix/proc are fields that reflect the top of the save/restore
134 stack. Whenever the canvas changes, it marks a dirty flag, and then before
135 these are used (assuming we're not on a layer) we rebuild these cache
136 values: they reflect the top of the save stack, but translated and clipped
137 by the device's XY offset and bitmap-bounds.
138*/
139struct DeviceCM {
140 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000141 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000142 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000143 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +0000144 SkPaint* fPaint; // may be null (in the future)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000145
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000146 DeviceCM(SkBaseDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000147 : fNext(NULL) {
148 if (NULL != device) {
149 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000150 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000151 }
reed@google.com4b226022011-01-11 18:32:13 +0000152 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000153 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000154 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000155
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000156 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000157 if (NULL != fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000158 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000159 fDevice->unref();
160 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000161 SkDELETE(fPaint);
162 }
reed@google.com4b226022011-01-11 18:32:13 +0000163
reed@google.com045e62d2011-10-24 12:19:46 +0000164 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
165 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000166 int x = fDevice->getOrigin().x();
167 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000168 int width = fDevice->width();
169 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000170
reed@android.com8a1c16f2008-12-17 15:59:43 +0000171 if ((x | y) == 0) {
172 fMatrix = &totalMatrix;
173 fClip = totalClip;
174 } else {
175 fMatrixStorage = totalMatrix;
176 fMatrixStorage.postTranslate(SkIntToScalar(-x),
177 SkIntToScalar(-y));
178 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000179
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180 totalClip.translate(-x, -y, &fClip);
181 }
182
reed@google.com045e62d2011-10-24 12:19:46 +0000183 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000184
185 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000186
reed@android.com8a1c16f2008-12-17 15:59:43 +0000187 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000188 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000189 SkRegion::kDifference_Op);
190 }
reed@google.com4b226022011-01-11 18:32:13 +0000191
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000192 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
193
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194#ifdef SK_DEBUG
195 if (!fClip.isEmpty()) {
196 SkIRect deviceR;
197 deviceR.set(0, 0, width, height);
198 SkASSERT(deviceR.contains(fClip.getBounds()));
199 }
200#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000201 }
202
reed@android.com8a1c16f2008-12-17 15:59:43 +0000203private:
bsalomon@google.com0e354aa2012-10-08 20:44:25 +0000204 SkMatrix fMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000205};
206
207/* This is the record we keep for each save/restore level in the stack.
208 Since a level optionally copies the matrix and/or stack, we have pointers
209 for these fields. If the value is copied for this level, the copy is
210 stored in the ...Storage field, and the pointer points to that. If the
211 value is not copied for this level, we ignore ...Storage, and just point
212 at the corresponding value in the previous level in the stack.
213*/
214class SkCanvas::MCRec {
215public:
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000216 int fFlags;
reed@google.com00177082011-10-12 14:34:30 +0000217 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
218 SkRasterClip* fRasterClip; // points to either fRegionStorage or prev MCRec
219 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000220
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221 DeviceCM* fLayer;
222 /* If there are any layers in the stack, this points to the top-most
223 one that is at or below this level in the stack (so we know what
224 bitmap/device to draw into from this level. This value is NOT
225 reference counted, since the real owner is either our fLayer field,
226 or a previous one in a lower level.)
227 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000228 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000229
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000230 MCRec(const MCRec* prev, int flags) : fFlags(flags) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231 if (NULL != prev) {
232 if (flags & SkCanvas::kMatrix_SaveFlag) {
233 fMatrixStorage = *prev->fMatrix;
234 fMatrix = &fMatrixStorage;
235 } else {
236 fMatrix = prev->fMatrix;
237 }
reed@google.com4b226022011-01-11 18:32:13 +0000238
reed@android.com8a1c16f2008-12-17 15:59:43 +0000239 if (flags & SkCanvas::kClip_SaveFlag) {
reed@google.com00177082011-10-12 14:34:30 +0000240 fRasterClipStorage = *prev->fRasterClip;
241 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242 } else {
reed@google.com00177082011-10-12 14:34:30 +0000243 fRasterClip = prev->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244 }
245
246 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000247 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248
249 fTopLayer = prev->fTopLayer;
250 } else { // no prev
251 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000252
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253 fMatrix = &fMatrixStorage;
reed@google.com00177082011-10-12 14:34:30 +0000254 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255 fFilter = NULL;
256 fTopLayer = NULL;
257 }
258 fLayer = NULL;
259
260 // don't bother initializing fNext
261 inc_rec();
262 }
263 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000264 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265 SkDELETE(fLayer);
266 dec_rec();
267 }
reed@google.com4b226022011-01-11 18:32:13 +0000268
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269private:
reed@google.com00177082011-10-12 14:34:30 +0000270 SkMatrix fMatrixStorage;
271 SkRasterClip fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000272};
273
274class SkDrawIter : public SkDraw {
275public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000276 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000277 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000278 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279 canvas->updateDeviceCMCache();
280
reed@google.com90c07ea2012-04-13 13:50:27 +0000281 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000282 fBounder = canvas->getBounder();
283 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000284 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285 }
reed@google.com4b226022011-01-11 18:32:13 +0000286
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287 bool next() {
288 // skip over recs with empty clips
289 if (fSkipEmptyClips) {
290 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
291 fCurrLayer = fCurrLayer->fNext;
292 }
293 }
294
reed@google.comf68c5e22012-02-24 16:38:58 +0000295 const DeviceCM* rec = fCurrLayer;
296 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297
298 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000299 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
300 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000301 fDevice = rec->fDevice;
302 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000303 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000304 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000305
306 fCurrLayer = rec->fNext;
307 if (fBounder) {
308 fBounder->setClip(fClip);
309 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000310 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000311
reed@android.com8a1c16f2008-12-17 15:59:43 +0000312 return true;
313 }
314 return false;
315 }
reed@google.com4b226022011-01-11 18:32:13 +0000316
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000317 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000318 int getX() const { return fDevice->getOrigin().x(); }
319 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000320 const SkMatrix& getMatrix() const { return *fMatrix; }
321 const SkRegion& getClip() const { return *fClip; }
322 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000323
reed@android.com8a1c16f2008-12-17 15:59:43 +0000324private:
325 SkCanvas* fCanvas;
326 const DeviceCM* fCurrLayer;
327 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000328 SkBool8 fSkipEmptyClips;
329
330 typedef SkDraw INHERITED;
331};
332
333/////////////////////////////////////////////////////////////////////////////
334
335class AutoDrawLooper {
336public:
reed@google.com8926b162012-03-23 15:36:36 +0000337 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000338 bool skipLayerForImageFilter = false,
339 const SkRect* bounds = NULL) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000340 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000341 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000342 fPaint = NULL;
343 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000344 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000345 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000346
reed@google.com8926b162012-03-23 15:36:36 +0000347 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
348 SkPaint tmp;
349 tmp.setImageFilter(fOrigPaint.getImageFilter());
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000350 (void)canvas->internalSaveLayer(bounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag,
351 true, SkCanvas::kFullLayer_SaveLayerStrategy);
reed@google.com8926b162012-03-23 15:36:36 +0000352 // we'll clear the imageFilter for the actual draws in next(), so
353 // it will only be applied during the restore().
354 fDoClearImageFilter = true;
355 }
356
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000357 if (SkDrawLooper* looper = paint.getLooper()) {
358 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
359 looper->contextSize());
360 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000361 fIsSimple = false;
362 } else {
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000363 fLooperContext = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000364 // can we be marked as simple?
365 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000366 }
367 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000368
reed@android.com8a1c16f2008-12-17 15:59:43 +0000369 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000370 if (fDoClearImageFilter) {
371 fCanvas->internalRestore();
372 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000373 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000374 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000375
reed@google.com4e2b3d32011-04-07 14:18:59 +0000376 const SkPaint& paint() const {
377 SkASSERT(fPaint);
378 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000379 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000380
reed@google.com129ec222012-05-15 13:24:09 +0000381 bool next(SkDrawFilter::Type drawType) {
382 if (fDone) {
383 return false;
384 } else if (fIsSimple) {
385 fDone = true;
386 fPaint = &fOrigPaint;
387 return !fPaint->nothingToDraw();
388 } else {
389 return this->doNext(drawType);
390 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000391 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000392
reed@android.com8a1c16f2008-12-17 15:59:43 +0000393private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000394 SkLazyPaint fLazyPaint;
395 SkCanvas* fCanvas;
396 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000397 SkDrawFilter* fFilter;
398 const SkPaint* fPaint;
399 int fSaveCount;
reed@google.com8926b162012-03-23 15:36:36 +0000400 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000401 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000402 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000403 SkDrawLooper::Context* fLooperContext;
404 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000405
406 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000407};
408
reed@google.com129ec222012-05-15 13:24:09 +0000409bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000410 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000411 SkASSERT(!fIsSimple);
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000412 SkASSERT(fLooperContext || fFilter || fDoClearImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000413
414 SkPaint* paint = fLazyPaint.set(fOrigPaint);
415
416 if (fDoClearImageFilter) {
417 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000418 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000419
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000420 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000421 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000422 return false;
423 }
424 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000425 if (!fFilter->filter(paint, drawType)) {
426 fDone = true;
427 return false;
428 }
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000429 if (NULL == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000430 // no looper means we only draw once
431 fDone = true;
432 }
433 }
434 fPaint = paint;
435
436 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000437 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000438 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000439 }
440
441 // call this after any possible paint modifiers
442 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000443 fPaint = NULL;
444 return false;
445 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000446 return true;
447}
448
reed@android.com8a1c16f2008-12-17 15:59:43 +0000449/* Stack helper for managing a SkBounder. In the destructor, if we were
450 given a bounder, we call its commit() method, signifying that we are
451 done accumulating bounds for that draw.
452*/
453class SkAutoBounderCommit {
454public:
455 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
456 ~SkAutoBounderCommit() {
457 if (NULL != fBounder) {
458 fBounder->commit();
459 }
460 }
461private:
462 SkBounder* fBounder;
463};
commit-bot@chromium.orge61a86c2013-11-18 16:03:59 +0000464#define SkAutoBounderCommit(...) SK_REQUIRE_LOCAL_VAR(SkAutoBounderCommit)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000465
466#include "SkColorPriv.h"
467
reed@android.com8a1c16f2008-12-17 15:59:43 +0000468////////// macros to place around the internal draw calls //////////////////
469
reed@google.com8926b162012-03-23 15:36:36 +0000470#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000471 this->predrawNotify(); \
reed@google.com8926b162012-03-23 15:36:36 +0000472 AutoDrawLooper looper(this, paint, true); \
473 while (looper.next(type)) { \
474 SkAutoBounderCommit ac(fBounder); \
475 SkDrawIter iter(this);
476
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000477#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000478 this->predrawNotify(); \
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000479 AutoDrawLooper looper(this, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000480 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000481 SkAutoBounderCommit ac(fBounder); \
482 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000483
reed@google.com4e2b3d32011-04-07 14:18:59 +0000484#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000485
486////////////////////////////////////////////////////////////////////////////
487
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000488SkBaseDevice* SkCanvas::init(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000489 fBounder = NULL;
reed@google.comc0784db2013-12-13 21:16:12 +0000490 fCachedLocalClipBounds.setEmpty();
491 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000492 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000493 fAllowSimplifyClip = false;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000494 fDeviceCMDirty = false;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000495 fSaveLayerCount = 0;
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +0000496 fCullCount = 0;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000497 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000498
499 fMCRec = (MCRec*)fMCStack.push_back();
500 new (fMCRec) MCRec(NULL, 0);
501
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000502 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000503 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504
reed@google.com97af1a62012-08-28 12:19:02 +0000505 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000506
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000507 return this->setRootDevice(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000508}
509
reed@google.comcde92112011-07-06 20:00:52 +0000510SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000511 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
512{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000513 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000514
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000515 this->init(NULL);
516}
517
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000518SkCanvas::SkCanvas(int width, int height)
519 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
520{
521 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000522
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000523 SkBitmap bitmap;
reed@google.com900ecf22014-02-20 20:55:37 +0000524 bitmap.setConfig(SkImageInfo::MakeUnknown(width, height));
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000525 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
526}
527
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000528SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000529 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
530{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000531 inc_canvas();
532
533 this->init(device);
534}
535
536SkCanvas::SkCanvas(const SkBitmap& bitmap)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000537 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
538{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000539 inc_canvas();
540
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000541 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000542}
543
544SkCanvas::~SkCanvas() {
545 // free up the contents of our deque
546 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000547 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000548
reed@android.com8a1c16f2008-12-17 15:59:43 +0000549 this->internalRestore(); // restore the last, since we're going away
550
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000551 SkSafeUnref(fBounder);
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000552 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000553
reed@android.com8a1c16f2008-12-17 15:59:43 +0000554 dec_canvas();
555}
556
557SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
558 SkRefCnt_SafeAssign(fBounder, bounder);
559 return bounder;
560}
561
562SkDrawFilter* SkCanvas::getDrawFilter() const {
563 return fMCRec->fFilter;
564}
565
566SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
567 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
568 return filter;
569}
570
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000571SkMetaData& SkCanvas::getMetaData() {
572 // metadata users are rare, so we lazily allocate it. If that changes we
573 // can decide to just make it a field in the device (rather than a ptr)
574 if (NULL == fMetaData) {
575 fMetaData = new SkMetaData;
576 }
577 return *fMetaData;
578}
579
reed@android.com8a1c16f2008-12-17 15:59:43 +0000580///////////////////////////////////////////////////////////////////////////////
581
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000582void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000583 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000584 if (device) {
585 device->flush();
586 }
587}
588
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000589SkISize SkCanvas::getTopLayerSize() const {
590 SkBaseDevice* d = this->getTopDevice();
591 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
592}
593
594SkIPoint SkCanvas::getTopLayerOrigin() const {
595 SkBaseDevice* d = this->getTopDevice();
596 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
597}
598
599SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000600 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000601 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
602}
603
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000604SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000606 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000607 SkASSERT(rec && rec->fLayer);
608 return rec->fLayer->fDevice;
609}
610
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000611SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000612 if (updateMatrixClip) {
613 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
614 }
reed@google.com9266fed2011-03-30 00:18:03 +0000615 return fMCRec->fTopLayer->fDevice;
616}
617
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000618SkBaseDevice* SkCanvas::setRootDevice(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000619 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000620 SkDeque::F2BIter iter(fMCStack);
621 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000622 SkASSERT(rec && rec->fLayer);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000623 SkBaseDevice* rootDevice = rec->fLayer->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000624
625 if (rootDevice == device) {
626 return device;
627 }
reed@google.com4b226022011-01-11 18:32:13 +0000628
reed@android.com8a1c16f2008-12-17 15:59:43 +0000629 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000630 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000631 }
632 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000633 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000634 }
635
636 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
637 rootDevice = device;
638
639 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000640
reed@android.com8a1c16f2008-12-17 15:59:43 +0000641 /* Now we update our initial region to have the bounds of the new device,
642 and then intersect all of the clips in our stack with these bounds,
643 to ensure that we can't draw outside of the device's bounds (and trash
644 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000645
reed@android.com8a1c16f2008-12-17 15:59:43 +0000646 NOTE: this is only a partial-fix, since if the new device is larger than
647 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000648 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000649 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
650 reconstruct the correct clips, so this approximation will have to do.
651 The caller really needs to restore() back to the base if they want to
652 accurately take advantage of the new device bounds.
653 */
654
reed@google.com42aea282012-03-28 16:19:15 +0000655 SkIRect bounds;
656 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000657 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000658 } else {
659 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000660 }
reed@google.com42aea282012-03-28 16:19:15 +0000661 // now jam our 1st clip to be bounds, and intersect the rest with that
662 rec->fRasterClip->setRect(bounds);
663 while ((rec = (MCRec*)iter.next()) != NULL) {
664 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
665 }
666
reed@android.com8a1c16f2008-12-17 15:59:43 +0000667 return device;
668}
669
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000670#ifdef SK_SUPPORT_LEGACY_READPIXELSCONFIG
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000671bool SkCanvas::readPixels(SkBitmap* bitmap,
672 int x, int y,
673 Config8888 config8888) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000674 SkBaseDevice* device = this->getDevice();
reed@google.com51df9e32010-12-23 19:29:18 +0000675 if (!device) {
676 return false;
677 }
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000678 return device->readPixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000679}
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000680#endif
681
682bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
683 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
684 return false;
685 }
686
687 bool weAllocated = false;
688 if (NULL == bitmap->pixelRef()) {
689 if (!bitmap->allocPixels()) {
690 return false;
691 }
692 weAllocated = true;
693 }
694
695 SkBitmap bm(*bitmap);
696 bm.lockPixels();
697 if (bm.getPixels() && this->readPixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y)) {
698 return true;
699 }
700
701 if (weAllocated) {
702 bitmap->setPixelRef(NULL);
703 }
704 return false;
705}
reed@google.com51df9e32010-12-23 19:29:18 +0000706
bsalomon@google.comc6980972011-11-02 19:57:21 +0000707bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000708 SkIRect r = srcRect;
709 const SkISize size = this->getBaseLayerSize();
710 if (!r.intersect(0, 0, size.width(), size.height())) {
711 bitmap->reset();
712 return false;
713 }
714
715 if (!bitmap->allocN32Pixels(r.width(), r.height())) {
716 // bitmap will already be reset.
717 return false;
718 }
719 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
720 bitmap->reset();
721 return false;
722 }
723 return true;
724}
725
726bool SkCanvas::readPixels(const SkImageInfo& origInfo, void* dstP, size_t rowBytes, int x, int y) {
727 switch (origInfo.colorType()) {
728 case kUnknown_SkColorType:
729 case kIndex_8_SkColorType:
730 return false;
731 default:
732 break;
733 }
734 if (NULL == dstP || rowBytes < origInfo.minRowBytes()) {
735 return false;
736 }
737 if (0 == origInfo.width() || 0 == origInfo.height()) {
738 return false;
739 }
740
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000741 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000742 if (!device) {
743 return false;
744 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000745
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000746 const SkISize size = this->getBaseLayerSize();
747 SkIRect srcR = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
748 if (!srcR.intersect(0, 0, size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000749 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000750 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000751
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000752 SkImageInfo info = origInfo;
753 // the intersect may have shrunk info's logical size
754 info.fWidth = srcR.width();
755 info.fHeight = srcR.height();
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000756
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000757 // if x or y are negative, then we have to adjust pixels
758 if (x > 0) {
759 x = 0;
reed@google.com51df9e32010-12-23 19:29:18 +0000760 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000761 if (y > 0) {
762 y = 0;
763 }
764 // here x,y are either 0 or negative
765 dstP = ((char*)dstP - y * rowBytes - x * info.bytesPerPixel());
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000766
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000767 // The device can assert that the requested area is always contained in its bounds
768 return device->readPixels(info, dstP, rowBytes, srcR.x(), srcR.y());
reed@google.com51df9e32010-12-23 19:29:18 +0000769}
770
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000771bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
772 if (bitmap.getTexture()) {
773 return false;
774 }
775 SkBitmap bm(bitmap);
776 bm.lockPixels();
777 if (bm.getPixels()) {
778 return this->writePixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y);
779 }
780 return false;
781}
782
783bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
784 int x, int y) {
785 switch (origInfo.colorType()) {
786 case kUnknown_SkColorType:
787 case kIndex_8_SkColorType:
788 return false;
789 default:
790 break;
791 }
792 if (NULL == pixels || rowBytes < origInfo.minRowBytes()) {
793 return false;
794 }
795
796 const SkISize size = this->getBaseLayerSize();
797 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
798 if (!target.intersect(0, 0, size.width(), size.height())) {
799 return false;
800 }
801
802 SkBaseDevice* device = this->getDevice();
803 if (!device) {
804 return false;
805 }
806
807 SkImageInfo info = origInfo;
808 // the intersect may have shrunk info's logical size
809 info.fWidth = target.width();
810 info.fHeight = target.height();
811
812 // if x or y are negative, then we have to adjust pixels
813 if (x > 0) {
814 x = 0;
815 }
816 if (y > 0) {
817 y = 0;
818 }
819 // here x,y are either 0 or negative
820 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
821
822 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000823 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000824}
reed@google.com51df9e32010-12-23 19:29:18 +0000825
junov@google.com4370aed2012-01-18 16:21:08 +0000826SkCanvas* SkCanvas::canvasForDrawIter() {
827 return this;
828}
829
reed@android.com8a1c16f2008-12-17 15:59:43 +0000830//////////////////////////////////////////////////////////////////////////////
831
reed@android.com8a1c16f2008-12-17 15:59:43 +0000832void SkCanvas::updateDeviceCMCache() {
833 if (fDeviceCMDirty) {
834 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000835 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000836 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000837
reed@android.com8a1c16f2008-12-17 15:59:43 +0000838 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000839 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000840 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000841 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000842 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000843 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000844 } while ((layer = layer->fNext) != NULL);
845 }
846 fDeviceCMDirty = false;
847 }
848}
849
reed@android.com8a1c16f2008-12-17 15:59:43 +0000850///////////////////////////////////////////////////////////////////////////////
851
852int SkCanvas::internalSave(SaveFlags flags) {
853 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000854
reed@android.com8a1c16f2008-12-17 15:59:43 +0000855 MCRec* newTop = (MCRec*)fMCStack.push_back();
856 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000857
reed@android.com8a1c16f2008-12-17 15:59:43 +0000858 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000859
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000860 if (SkCanvas::kClip_SaveFlag & flags) {
861 fClipStack.save();
862 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000863
reed@android.com8a1c16f2008-12-17 15:59:43 +0000864 return saveCount;
865}
866
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000867void SkCanvas::willSave(SaveFlags) {
868 // Do nothing. Subclasses may do something.
869}
870
reed@android.com8a1c16f2008-12-17 15:59:43 +0000871int SkCanvas::save(SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000872 this->willSave(flags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000873 // call shared impl
874 return this->internalSave(flags);
875}
876
reed@android.com8a1c16f2008-12-17 15:59:43 +0000877static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +0000878#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000879 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +0000880#else
881 return true;
882#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000883}
884
junov@chromium.orga907ac32012-02-24 21:54:07 +0000885bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000886 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000887 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000888 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000889 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000890 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000891 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000892
893 if (imageFilter) {
894 imageFilter->filterBounds(clipBounds, *fMCRec->fMatrix, &clipBounds);
895 // Filters may grow the bounds beyond the device bounds.
896 op = SkRegion::kReplace_Op;
897 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000898 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000899 if (NULL != bounds) {
900 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000901
reed@android.com8a1c16f2008-12-17 15:59:43 +0000902 this->getTotalMatrix().mapRect(&r, *bounds);
903 r.roundOut(&ir);
904 // early exit if the layer's bounds are clipped out
905 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000906 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000907 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000908 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000909 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000910 }
911 } else { // no user bounds, so just use the clip
912 ir = clipBounds;
913 }
914
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000915 if (bounds_affects_clip(flags)) {
916 fClipStack.clipDevRect(ir, op);
917 // early exit if the clip is now empty
918 if (!fMCRec->fRasterClip->op(ir, op)) {
919 return false;
920 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000921 }
922
923 if (intersection) {
924 *intersection = ir;
925 }
926 return true;
927}
928
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000929SkCanvas::SaveLayerStrategy SkCanvas::willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) {
930
931 // Do nothing. Subclasses may do something.
932 return kFullLayer_SaveLayerStrategy;
933}
934
junov@chromium.orga907ac32012-02-24 21:54:07 +0000935int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
936 SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000937 // Overriding classes may return false to signal that we don't need to create a layer.
938 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
939 return this->internalSaveLayer(bounds, paint, flags, false, strategy);
reed@google.com8926b162012-03-23 15:36:36 +0000940}
941
reed@google.com76f10a32014-02-05 15:32:21 +0000942static SkBaseDevice* createCompatibleDevice(SkCanvas* canvas,
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000943 const SkImageInfo& info) {
reed@google.com76f10a32014-02-05 15:32:21 +0000944 SkBaseDevice* device = canvas->getDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000945 return device ? device->createCompatibleDevice(info) : NULL;
reed@google.com76f10a32014-02-05 15:32:21 +0000946}
947
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000948int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
949 bool justForImageFilter, SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +0000950#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
951 flags = (SaveFlags)(flags | kClipToLayer_SaveFlag);
952#endif
953
junov@chromium.orga907ac32012-02-24 21:54:07 +0000954 // do this before we create the layer. We don't call the public save() since
955 // that would invoke a possibly overridden virtual
956 int count = this->internalSave(flags);
957
958 fDeviceCMDirty = true;
959
960 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000961 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000962 return count;
963 }
964
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000965 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
966 // the clipRectBounds() call above?
967 if (kNoLayer_SaveLayerStrategy == strategy) {
968 return count;
969 }
970
reed@google.comb55deeb2012-01-06 14:43:09 +0000971 // Kill the imagefilter if our device doesn't allow it
972 SkLazyPaint lazyP;
973 if (paint && paint->getImageFilter()) {
974 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000975 if (justForImageFilter) {
976 // early exit if the layer was just for the imageFilter
977 return count;
978 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000979 SkPaint* p = lazyP.set(*paint);
980 p->setImageFilter(NULL);
981 paint = p;
982 }
983 }
984
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000985 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
986 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
987 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000988
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000989 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000990 if (paint && paint->getImageFilter()) {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000991 device = createCompatibleDevice(this, info);
reed@google.com76dd2772012-01-05 21:15:07 +0000992 } else {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000993 device = this->createLayerDevice(info);
reed@google.com76dd2772012-01-05 21:15:07 +0000994 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000995 if (NULL == device) {
996 SkDebugf("Unable to create device for layer.");
997 return count;
998 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000999
reed@google.com6f8f2922011-03-04 22:27:10 +00001000 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +00001001 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001002 device->unref();
1003
1004 layer->fNext = fMCRec->fTopLayer;
1005 fMCRec->fLayer = layer;
1006 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
1007
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001008 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001009 return count;
1010}
1011
1012int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
1013 SaveFlags flags) {
1014 if (0xFF == alpha) {
1015 return this->saveLayer(bounds, NULL, flags);
1016 } else {
1017 SkPaint tmpPaint;
1018 tmpPaint.setAlpha(alpha);
1019 return this->saveLayer(bounds, &tmpPaint, flags);
1020 }
1021}
1022
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001023void SkCanvas::willRestore() {
1024 // Do nothing. Subclasses may do something.
1025}
1026
reed@android.com8a1c16f2008-12-17 15:59:43 +00001027void SkCanvas::restore() {
1028 // check for underflow
1029 if (fMCStack.count() > 1) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001030 this->willRestore();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001031 this->internalRestore();
1032 }
1033}
1034
1035void SkCanvas::internalRestore() {
1036 SkASSERT(fMCStack.count() != 0);
1037
1038 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001039 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001040
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001041 if (SkCanvas::kClip_SaveFlag & fMCRec->fFlags) {
1042 fClipStack.restore();
1043 }
1044
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001045 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001046 DeviceCM* layer = fMCRec->fLayer; // may be null
1047 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
1048 fMCRec->fLayer = NULL;
1049
1050 // now do the normal restore()
1051 fMCRec->~MCRec(); // balanced in save()
1052 fMCStack.pop_back();
1053 fMCRec = (MCRec*)fMCStack.back();
1054
1055 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1056 since if we're being recorded, we don't want to record this (the
1057 recorder will have already recorded the restore).
1058 */
1059 if (NULL != layer) {
1060 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001061 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001062 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
1063 layer->fPaint);
1064 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001065 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +00001066
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001067 SkASSERT(fSaveLayerCount > 0);
1068 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001069 }
1070 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001071 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001072}
1073
1074int SkCanvas::getSaveCount() const {
1075 return fMCStack.count();
1076}
1077
1078void SkCanvas::restoreToCount(int count) {
1079 // sanity check
1080 if (count < 1) {
1081 count = 1;
1082 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001083
reed@google.comb9d1c6a2011-11-28 16:09:24 +00001084 int n = this->getSaveCount() - count;
1085 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001086 this->restore();
1087 }
1088}
1089
reed@google.com7c202932011-12-14 18:48:05 +00001090bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001091 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +00001092}
1093
reed@google.com76f10a32014-02-05 15:32:21 +00001094SkSurface* SkCanvas::newSurface(const SkImageInfo& info) {
1095 return this->onNewSurface(info);
1096}
1097
1098SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info) {
1099 SkBaseDevice* dev = this->getDevice();
1100 return dev ? dev->newSurface(info) : NULL;
1101}
1102
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001103SkImageInfo SkCanvas::imageInfo() const {
1104 SkBaseDevice* dev = this->getDevice();
1105 if (dev) {
1106 return dev->imageInfo();
1107 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001108 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001109 }
1110}
1111
1112const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
1113 return this->onPeekPixels(info, rowBytes);
1114}
1115
1116const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
1117 SkBaseDevice* dev = this->getDevice();
1118 return dev ? dev->peekPixels(info, rowBytes) : NULL;
1119}
1120
reed@google.com9c135db2014-03-12 18:28:35 +00001121void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
1122 return this->onAccessTopLayerPixels(info, rowBytes);
1123}
1124
1125void* SkCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
1126 SkBaseDevice* dev = this->getTopDevice();
1127 return dev ? dev->accessPixels(info, rowBytes) : NULL;
1128}
1129
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001130SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1131 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1132 if (NULL == fAddr) {
1133 fInfo = canvas->imageInfo();
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +00001134 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.allocPixels(fInfo)) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001135 return; // failure, fAddr is NULL
1136 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001137 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1138 return; // failure, fAddr is NULL
1139 }
1140 fAddr = fBitmap.getPixels();
1141 fRowBytes = fBitmap.rowBytes();
1142 }
1143 SkASSERT(fAddr); // success
1144}
1145
1146bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1147 if (fAddr) {
1148 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes,
1149 NULL, NULL);
1150 } else {
1151 bitmap->reset();
1152 return false;
1153 }
1154}
1155
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +00001156void SkCanvas::onPushCull(const SkRect& cullRect) {
1157 // do nothing. Subclasses may do something
1158}
1159
1160void SkCanvas::onPopCull() {
1161 // do nothing. Subclasses may do something
1162}
1163
reed@android.com8a1c16f2008-12-17 15:59:43 +00001164/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org520cf8b2014-03-20 20:25:14 +00001165#ifdef SK_DEBUG
1166// Ensure that cull rects are monotonically nested in device space.
1167void SkCanvas::validateCull(const SkIRect& devCull) {
1168 if (fCullStack.isEmpty()
1169 || devCull.isEmpty()
1170 || fCullStack.top().contains(devCull)) {
1171 return;
1172 }
1173
1174 SkDEBUGF(("Invalid cull: [%d %d %d %d] (previous cull: [%d %d %d %d])\n",
1175 devCull.x(), devCull.y(), devCull.right(), devCull.bottom(),
1176 fCullStack.top().x(), fCullStack.top().y(),
1177 fCullStack.top().right(), fCullStack.top().bottom()));
1178
1179#ifdef ASSERT_NESTED_CULLING
1180 SkDEBUGFAIL("Invalid cull.");
1181#endif
1182}
1183#endif
1184
1185void SkCanvas::pushCull(const SkRect& cullRect) {
1186 ++fCullCount;
1187 this->onPushCull(cullRect);
1188
1189#ifdef SK_DEBUG
1190 // Map the cull rect into device space.
1191 SkRect mappedCull;
1192 this->getTotalMatrix().mapRect(&mappedCull, cullRect);
1193
1194 // Take clipping into account.
1195 SkIRect devClip, devCull;
1196 mappedCull.roundOut(&devCull);
1197 this->getClipDeviceBounds(&devClip);
1198 if (!devCull.intersect(devClip)) {
1199 devCull.setEmpty();
1200 }
1201
1202 this->validateCull(devCull);
1203 fCullStack.push(devCull); // balanced in popCull
1204#endif
1205}
1206
1207void SkCanvas::popCull() {
1208 SkASSERT(fCullStack.count() == fCullCount);
1209
1210 if (fCullCount > 0) {
1211 --fCullCount;
1212 this->onPopCull();
1213
1214 SkDEBUGCODE(fCullStack.pop());
1215 }
1216}
1217
1218/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001219
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001220void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001221 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001222 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001223 return;
1224 }
1225
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001226 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001227 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001228 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001229 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001230
1231 SkDEBUGCODE(bitmap.validate();)
1232 CHECK_LOCKCOUNT_BALANCE(bitmap);
1233
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001234 SkRect storage;
1235 const SkRect* bounds = NULL;
1236 if (paint && paint->canComputeFastBounds()) {
1237 bitmap.getBounds(&storage);
1238 matrix.mapRect(&storage);
1239 bounds = &paint->computeFastBounds(storage, &storage);
1240 }
1241
1242 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001243
1244 while (iter.next()) {
1245 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1246 }
1247
1248 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001249}
1250
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001251void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001252 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001253 SkPaint tmp;
1254 if (NULL == paint) {
1255 tmp.setDither(true);
1256 paint = &tmp;
1257 }
reed@google.com4b226022011-01-11 18:32:13 +00001258
reed@google.com8926b162012-03-23 15:36:36 +00001259 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001260 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001261 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001262 paint = &looper.paint();
1263 SkImageFilter* filter = paint->getImageFilter();
1264 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001265 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001266 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001267 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001268 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001269 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001270 SkMatrix matrix = *iter.fMatrix;
1271 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001272 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
1273 SkImageFilter::Context ctx(matrix, clipBounds);
1274 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001275 SkPaint tmpUnfiltered(*paint);
1276 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001277 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1278 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001279 }
1280 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001281 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001282 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001283 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001284 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001285}
1286
reed@google.com8926b162012-03-23 15:36:36 +00001287void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1288 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001289 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001290 return;
1291 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001292 SkDEBUGCODE(bitmap.validate();)
1293 CHECK_LOCKCOUNT_BALANCE(bitmap);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001294
reed@google.com8926b162012-03-23 15:36:36 +00001295 SkPaint tmp;
1296 if (NULL == paint) {
1297 paint = &tmp;
1298 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001299
reed@google.com8926b162012-03-23 15:36:36 +00001300 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001301
reed@google.com8926b162012-03-23 15:36:36 +00001302 while (iter.next()) {
1303 paint = &looper.paint();
1304 SkImageFilter* filter = paint->getImageFilter();
1305 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1306 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001307 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001308 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001309 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001310 SkMatrix matrix = *iter.fMatrix;
1311 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001312 SkIRect clipBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
1313 SkImageFilter::Context ctx(matrix, clipBounds);
1314 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001315 SkPaint tmpUnfiltered(*paint);
1316 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001317 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001318 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001319 }
1320 } else {
1321 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1322 }
1323 }
1324 LOOPER_END
1325}
1326
reed@android.com8a1c16f2008-12-17 15:59:43 +00001327/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001328void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001329 SkMatrix m;
1330 m.setTranslate(dx, dy);
1331 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001332}
1333
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001334void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001335 SkMatrix m;
1336 m.setScale(sx, sy);
1337 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001338}
1339
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001340void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001341 SkMatrix m;
1342 m.setRotate(degrees);
1343 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001344}
1345
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001346void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001347 SkMatrix m;
1348 m.setSkew(sx, sy);
1349 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001350}
1351
1352void SkCanvas::didConcat(const SkMatrix&) {
1353 // Do nothing. Subclasses may do something.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001354}
1355
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001356void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001357 if (matrix.isIdentity()) {
1358 return;
1359 }
1360
reed@android.com8a1c16f2008-12-17 15:59:43 +00001361 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001362 fCachedLocalClipBoundsDirty = true;
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001363 fMCRec->fMatrix->preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001364
1365 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001366}
1367
1368void SkCanvas::didSetMatrix(const SkMatrix&) {
1369 // Do nothing. Subclasses may do something.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001370}
1371
1372void SkCanvas::setMatrix(const SkMatrix& matrix) {
1373 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001374 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001375 *fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001376 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001377}
1378
reed@android.com8a1c16f2008-12-17 15:59:43 +00001379void SkCanvas::resetMatrix() {
1380 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001381
reed@android.com8a1c16f2008-12-17 15:59:43 +00001382 matrix.reset();
1383 this->setMatrix(matrix);
1384}
1385
1386//////////////////////////////////////////////////////////////////////////////
1387
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001388void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001389 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1390 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001391}
1392
1393void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001394#ifdef SK_ENABLE_CLIP_QUICKREJECT
1395 if (SkRegion::kIntersect_Op == op) {
1396 if (fMCRec->fRasterClip->isEmpty()) {
1397 return false;
1398 }
1399
reed@google.com3b3e8952012-08-16 20:53:31 +00001400 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001401 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001402 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001403
1404 fClipStack.clipEmpty();
1405 return fMCRec->fRasterClip->setEmpty();
1406 }
1407 }
1408#endif
1409
reed@google.com5c3d1472011-02-22 19:12:23 +00001410 AutoValidateClip avc(this);
1411
reed@android.com8a1c16f2008-12-17 15:59:43 +00001412 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001413 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001414 if (!fAllowSoftClip) {
1415 edgeStyle = kHard_ClipEdgeStyle;
1416 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001417
1418 if (fMCRec->fMatrix->rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001419 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001420 // the matrix. This means we don't have to a) make a path, and b) tell
1421 // the region code to scan-convert the path, only to discover that it
1422 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001423 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001424
1425 fMCRec->fMatrix->mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001426 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
1427 fMCRec->fRasterClip->op(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001428 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001429 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001430 // and clip against that, since it can handle any matrix. However, to
1431 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1432 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001433 SkPath path;
1434
1435 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001436 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001437 }
1438}
1439
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001440static void clip_path_helper(const SkCanvas* canvas, SkRasterClip* currClip,
1441 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001442 // base is used to limit the size (and therefore memory allocation) of the
1443 // region that results from scan converting devPath.
1444 SkRegion base;
1445
reed@google.com819c9212011-02-23 18:56:55 +00001446 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001447 // since we are intersect, we can do better (tighter) with currRgn's
1448 // bounds, than just using the device. However, if currRgn is complex,
1449 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001450 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001451 // FIXME: we should also be able to do this when currClip->isBW(),
1452 // but relaxing the test above triggers GM asserts in
1453 // SkRgnBuilder::blitH(). We need to investigate what's going on.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001454 currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001455 } else {
reed@google.com00177082011-10-12 14:34:30 +00001456 base.setRect(currClip->getBounds());
1457 SkRasterClip clip;
1458 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001459 currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001460 }
reed@google.com819c9212011-02-23 18:56:55 +00001461 } else {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001462 const SkBaseDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001463 if (!device) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001464 currClip->setEmpty();
1465 return;
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001466 }
1467
junov@chromium.orga907ac32012-02-24 21:54:07 +00001468 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001469
1470 if (SkRegion::kReplace_Op == op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001471 currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001472 } else {
reed@google.com00177082011-10-12 14:34:30 +00001473 SkRasterClip clip;
1474 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001475 currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001476 }
1477 }
1478}
1479
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001480void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001481 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001482 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001483 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1484 } else {
1485 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001486 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001487}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001488
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001489void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001490 SkRRect transformedRRect;
1491 if (rrect.transform(*fMCRec->fMatrix, &transformedRRect)) {
1492 AutoValidateClip avc(this);
1493
1494 fDeviceCMDirty = true;
1495 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001496 if (!fAllowSoftClip) {
1497 edgeStyle = kHard_ClipEdgeStyle;
1498 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001499
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001500 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001501
1502 SkPath devPath;
1503 devPath.addRRect(transformedRRect);
1504
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001505 clip_path_helper(this, fMCRec->fRasterClip, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
1506 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001507 }
1508
1509 SkPath path;
1510 path.addRRect(rrect);
1511 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001512 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001513}
1514
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001515void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001516 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1517 SkRect r;
1518 if (!path.isInverseFillType() && path.isRect(&r)) {
1519 this->onClipRect(r, op, edgeStyle);
1520 } else {
1521 this->onClipPath(path, op, edgeStyle);
1522 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001523}
1524
1525void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001526#ifdef SK_ENABLE_CLIP_QUICKREJECT
1527 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1528 if (fMCRec->fRasterClip->isEmpty()) {
1529 return false;
1530 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001531
reed@google.com3b3e8952012-08-16 20:53:31 +00001532 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001533 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001534 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001535
reed@google.comda17f752012-08-16 18:27:05 +00001536 fClipStack.clipEmpty();
1537 return fMCRec->fRasterClip->setEmpty();
1538 }
1539 }
1540#endif
1541
reed@google.com5c3d1472011-02-22 19:12:23 +00001542 AutoValidateClip avc(this);
1543
reed@android.com8a1c16f2008-12-17 15:59:43 +00001544 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001545 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001546 if (!fAllowSoftClip) {
1547 edgeStyle = kHard_ClipEdgeStyle;
1548 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001549
1550 SkPath devPath;
1551 path.transform(*fMCRec->fMatrix, &devPath);
1552
reed@google.comfe701122011-11-08 19:41:23 +00001553 // Check if the transfomation, or the original path itself
1554 // made us empty. Note this can also happen if we contained NaN
1555 // values. computing the bounds detects this, and will set our
1556 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1557 if (devPath.getBounds().isEmpty()) {
1558 // resetting the path will remove any NaN or other wanky values
1559 // that might upset our scan converter.
1560 devPath.reset();
1561 }
1562
reed@google.com5c3d1472011-02-22 19:12:23 +00001563 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001564 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001565
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001566 if (fAllowSimplifyClip) {
1567 devPath.reset();
1568 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1569 const SkClipStack* clipStack = getClipStack();
1570 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1571 const SkClipStack::Element* element;
1572 while ((element = iter.next())) {
1573 SkClipStack::Element::Type type = element->getType();
1574 if (type == SkClipStack::Element::kEmpty_Type) {
1575 continue;
1576 }
1577 SkPath operand;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001578 element->asPath(&operand);
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001579 SkRegion::Op elementOp = element->getOp();
1580 if (elementOp == SkRegion::kReplace_Op) {
1581 devPath = operand;
1582 } else {
1583 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1584 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001585 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1586 // perhaps we need an API change to avoid this sort of mixed-signals about
1587 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001588 if (element->isAA()) {
1589 edgeStyle = kSoft_ClipEdgeStyle;
1590 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001591 }
1592 op = SkRegion::kReplace_Op;
1593 }
1594
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001595 clip_path_helper(this, fMCRec->fRasterClip, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001596}
1597
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001598void SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001599 bool inverseFilled) {
1600 // This is for updating the clip conservatively using only bounds
1601 // information.
1602 // Contract:
1603 // The current clip must contain the true clip. The true
1604 // clip is the clip that would have normally been computed
1605 // by calls to clipPath and clipRRect
1606 // Objective:
1607 // Keep the current clip as small as possible without
1608 // breaking the contract, using only clip bounding rectangles
1609 // (for performance).
1610
1611 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1612 // don't have to worry about getting caught in a loop. Thus anywhere
1613 // we call a virtual method, we explicitly prefix it with
1614 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001615
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001616 if (inverseFilled) {
1617 switch (op) {
1618 case SkRegion::kIntersect_Op:
1619 case SkRegion::kDifference_Op:
1620 // These ops can only shrink the current clip. So leaving
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001621 // the clip unchanged conservatively respects the contract.
1622 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001623 case SkRegion::kUnion_Op:
1624 case SkRegion::kReplace_Op:
1625 case SkRegion::kReverseDifference_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001626 case SkRegion::kXOR_Op: {
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001627 // These ops can grow the current clip up to the extents of
1628 // the input clip, which is inverse filled, so we just set
1629 // the current clip to the device bounds.
1630 SkRect deviceBounds;
1631 SkIRect deviceIBounds;
1632 this->getDevice()->getGlobalBounds(&deviceIBounds);
reed@google.com44699382013-10-31 17:28:30 +00001633 deviceBounds = SkRect::Make(deviceIBounds);
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001634 this->SkCanvas::save(SkCanvas::kMatrix_SaveFlag);
1635 // set the clip in device space
1636 this->SkCanvas::setMatrix(SkMatrix::I());
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001637 this->SkCanvas::onClipRect(deviceBounds, SkRegion::kReplace_Op,
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001638 kHard_ClipEdgeStyle);
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001639 this->SkCanvas::restore(); //pop the matrix, but keep the clip
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001640 break;
1641 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001642 default:
1643 SkASSERT(0); // unhandled op?
1644 }
1645 } else {
1646 // Not inverse filled
1647 switch (op) {
1648 case SkRegion::kIntersect_Op:
1649 case SkRegion::kUnion_Op:
1650 case SkRegion::kReplace_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001651 this->SkCanvas::onClipRect(bounds, op, kHard_ClipEdgeStyle);
1652 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001653 case SkRegion::kDifference_Op:
1654 // Difference can only shrink the current clip.
1655 // Leaving clip unchanged conservatively fullfills the contract.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001656 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001657 case SkRegion::kReverseDifference_Op:
1658 // To reverse, we swap in the bounds with a replace op.
1659 // As with difference, leave it unchanged.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001660 this->SkCanvas::onClipRect(bounds, SkRegion::kReplace_Op, kHard_ClipEdgeStyle);
1661 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001662 case SkRegion::kXOR_Op:
1663 // Be conservative, based on (A XOR B) always included in (A union B),
1664 // which is always included in (bounds(A) union bounds(B))
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001665 this->SkCanvas::onClipRect(bounds, SkRegion::kUnion_Op, kHard_ClipEdgeStyle);
1666 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001667 default:
1668 SkASSERT(0); // unhandled op?
1669 }
1670 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001671}
1672
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001673void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001674 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001675}
1676
1677void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001678 AutoValidateClip avc(this);
1679
reed@android.com8a1c16f2008-12-17 15:59:43 +00001680 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001681 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001682
reed@google.com5c3d1472011-02-22 19:12:23 +00001683 // todo: signal fClipStack that we have a region, and therefore (I guess)
1684 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001685 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001686
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001687 fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001688}
1689
reed@google.com819c9212011-02-23 18:56:55 +00001690#ifdef SK_DEBUG
1691void SkCanvas::validateClip() const {
1692 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001693 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001694 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001695 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001696 return;
1697 }
1698
reed@google.com819c9212011-02-23 18:56:55 +00001699 SkIRect ir;
1700 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001701 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001702
robertphillips@google.com80214e22012-07-20 15:33:18 +00001703 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001704 const SkClipStack::Element* element;
1705 while ((element = iter.next()) != NULL) {
1706 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001707 case SkClipStack::Element::kRect_Type:
1708 element->getRect().round(&ir);
1709 tmpClip.op(ir, element->getOp());
1710 break;
1711 case SkClipStack::Element::kEmpty_Type:
1712 tmpClip.setEmpty();
1713 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001714 default: {
1715 SkPath path;
1716 element->asPath(&path);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001717 clip_path_helper(this, &tmpClip, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001718 break;
1719 }
reed@google.com819c9212011-02-23 18:56:55 +00001720 }
1721 }
reed@google.com819c9212011-02-23 18:56:55 +00001722}
1723#endif
1724
reed@google.com90c07ea2012-04-13 13:50:27 +00001725void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001726 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001727 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001728
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001729 static const SkRect kEmpty = { 0, 0, 0, 0 };
1730 while ((element = iter.next()) != NULL) {
1731 switch (element->getType()) {
1732 case SkClipStack::Element::kPath_Type:
1733 visitor->clipPath(element->getPath(), element->getOp(), element->isAA());
1734 break;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001735 case SkClipStack::Element::kRRect_Type:
1736 visitor->clipRRect(element->getRRect(), element->getOp(), element->isAA());
1737 break;
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001738 case SkClipStack::Element::kRect_Type:
1739 visitor->clipRect(element->getRect(), element->getOp(), element->isAA());
1740 break;
1741 case SkClipStack::Element::kEmpty_Type:
1742 visitor->clipRect(kEmpty, SkRegion::kIntersect_Op, false);
1743 break;
reed@google.com90c07ea2012-04-13 13:50:27 +00001744 }
1745 }
1746}
1747
reed@google.com5c3d1472011-02-22 19:12:23 +00001748///////////////////////////////////////////////////////////////////////////////
1749
reed@google.com754de5f2014-02-24 19:38:20 +00001750bool SkCanvas::isClipEmpty() const {
1751 return fMCRec->fRasterClip->isEmpty();
1752}
1753
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001754bool SkCanvas::isClipRect() const {
1755 return fMCRec->fRasterClip->isRect();
1756}
1757
reed@google.com3b3e8952012-08-16 20:53:31 +00001758bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001759
reed@google.com16078632011-12-06 18:56:37 +00001760 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001761 return true;
1762
reed@google.com00177082011-10-12 14:34:30 +00001763 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001764 return true;
1765 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001766
tomhudson@google.com8d430182011-06-06 19:11:19 +00001767 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001768 SkRect dst;
1769 fMCRec->fMatrix->mapRect(&dst, rect);
1770 SkIRect idst;
1771 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001772 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001773 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001774 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001775
reed@android.coma380ae42009-07-21 01:17:02 +00001776 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001777 // TODO: should we use | instead, or compare all 4 at once?
1778 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001779 return true;
1780 }
reed@google.comc0784db2013-12-13 21:16:12 +00001781 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001782 return true;
1783 }
1784 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001785 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001786}
1787
reed@google.com3b3e8952012-08-16 20:53:31 +00001788bool SkCanvas::quickReject(const SkPath& path) const {
1789 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001790}
1791
reed@google.com3b3e8952012-08-16 20:53:31 +00001792bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001793 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001794 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001795 return false;
1796 }
1797
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001798 SkMatrix inverse;
1799 // if we can't invert the CTM, we can't return local clip bounds
1800 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001801 if (bounds) {
1802 bounds->setEmpty();
1803 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001804 return false;
1805 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001806
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001807 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001808 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001809 // adjust it outwards in case we are antialiasing
1810 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001811
reed@google.com8f4d2302013-12-17 16:44:46 +00001812 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1813 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001814 inverse.mapRect(bounds, r);
1815 }
1816 return true;
1817}
1818
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001819bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001820 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001821 if (clip.isEmpty()) {
1822 if (bounds) {
1823 bounds->setEmpty();
1824 }
1825 return false;
1826 }
1827
1828 if (NULL != bounds) {
1829 *bounds = clip.getBounds();
1830 }
1831 return true;
1832}
1833
reed@android.com8a1c16f2008-12-17 15:59:43 +00001834const SkMatrix& SkCanvas::getTotalMatrix() const {
1835 return *fMCRec->fMatrix;
1836}
1837
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001838#ifdef SK_SUPPORT_LEGACY_GETCLIPTYPE
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001839SkCanvas::ClipType SkCanvas::getClipType() const {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001840 if (fMCRec->fRasterClip->isEmpty()) {
1841 return kEmpty_ClipType;
1842 }
1843 if (fMCRec->fRasterClip->isRect()) {
1844 return kRect_ClipType;
1845 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001846 return kComplex_ClipType;
1847}
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001848#endif
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001849
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001850#ifdef SK_SUPPORT_LEGACY_GETTOTALCLIP
reed@android.com8a1c16f2008-12-17 15:59:43 +00001851const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001852 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001853}
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001854#endif
1855
1856const SkRegion& SkCanvas::internal_private_getTotalClip() const {
1857 return fMCRec->fRasterClip->forceGetBW();
1858}
1859
1860void SkCanvas::internal_private_getTotalClipAsPath(SkPath* path) const {
1861 path->reset();
1862
1863 const SkRegion& rgn = fMCRec->fRasterClip->forceGetBW();
1864 if (rgn.isEmpty()) {
1865 return;
1866 }
1867 (void)rgn.getBoundaryPath(path);
1868}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001869
reed@google.com9c135db2014-03-12 18:28:35 +00001870GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1871 SkBaseDevice* dev = this->getTopDevice();
1872 return dev ? dev->accessRenderTarget() : NULL;
1873}
1874
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001875SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001876 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001877 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001878}
1879
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001880GrContext* SkCanvas::getGrContext() {
1881#if SK_SUPPORT_GPU
1882 SkBaseDevice* device = this->getTopDevice();
1883 if (NULL != device) {
1884 GrRenderTarget* renderTarget = device->accessRenderTarget();
1885 if (NULL != renderTarget) {
1886 return renderTarget->getContext();
1887 }
1888 }
1889#endif
1890
1891 return NULL;
1892
1893}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001894
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001895void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1896 const SkPaint& paint) {
1897 if (outer.isEmpty()) {
1898 return;
1899 }
1900 if (inner.isEmpty()) {
1901 this->drawRRect(outer, paint);
1902 return;
1903 }
1904
1905 // We don't have this method (yet), but technically this is what we should
1906 // be able to assert...
1907 // SkASSERT(outer.contains(inner));
1908 //
1909 // For now at least check for containment of bounds
1910 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1911
1912 this->onDrawDRRect(outer, inner, paint);
1913}
1914
reed@android.com8a1c16f2008-12-17 15:59:43 +00001915//////////////////////////////////////////////////////////////////////////////
1916// These are the virtual drawing methods
1917//////////////////////////////////////////////////////////////////////////////
1918
reed@google.com2a981812011-04-14 18:59:28 +00001919void SkCanvas::clear(SkColor color) {
1920 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001921 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001922 while (iter.next()) {
1923 iter.fDevice->clear(color);
1924 }
1925}
1926
reed@android.com8a1c16f2008-12-17 15:59:43 +00001927void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001928 this->internalDrawPaint(paint);
1929}
1930
1931void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001932 CHECK_SHADER_NOSETCONTEXT(paint);
1933
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001934 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001935
1936 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001937 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001938 }
1939
reed@google.com4e2b3d32011-04-07 14:18:59 +00001940 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001941}
1942
1943void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1944 const SkPaint& paint) {
1945 if ((long)count <= 0) {
1946 return;
1947 }
1948
reed@google.comea033602012-12-14 13:13:55 +00001949 CHECK_SHADER_NOSETCONTEXT(paint);
1950
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001951 SkRect r, storage;
1952 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001953 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001954 // special-case 2 points (common for drawing a single line)
1955 if (2 == count) {
1956 r.set(pts[0], pts[1]);
1957 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001958 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001959 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001960 bounds = &paint.computeFastStrokeBounds(r, &storage);
1961 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001962 return;
1963 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001964 }
reed@google.coma584aed2012-05-16 14:06:02 +00001965
reed@android.com8a1c16f2008-12-17 15:59:43 +00001966 SkASSERT(pts != NULL);
1967
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001968 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001969
reed@android.com8a1c16f2008-12-17 15:59:43 +00001970 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001971 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001972 }
reed@google.com4b226022011-01-11 18:32:13 +00001973
reed@google.com4e2b3d32011-04-07 14:18:59 +00001974 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001975}
1976
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001977void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001978 CHECK_SHADER_NOSETCONTEXT(paint);
1979
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001980 SkRect storage;
1981 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001982 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001983 bounds = &paint.computeFastBounds(r, &storage);
1984 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001985 return;
1986 }
1987 }
reed@google.com4b226022011-01-11 18:32:13 +00001988
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001989 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001990
1991 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001992 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001993 }
1994
reed@google.com4e2b3d32011-04-07 14:18:59 +00001995 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001996}
1997
reed@google.com4ed0fb72012-12-12 20:48:18 +00001998void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001999 CHECK_SHADER_NOSETCONTEXT(paint);
2000
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002001 SkRect storage;
2002 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002003 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002004 bounds = &paint.computeFastBounds(oval, &storage);
2005 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002006 return;
2007 }
2008 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002009
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002010 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002011
2012 while (iter.next()) {
2013 iter.fDevice->drawOval(iter, oval, looper.paint());
2014 }
2015
2016 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002017}
2018
2019void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002020 CHECK_SHADER_NOSETCONTEXT(paint);
2021
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002022 SkRect storage;
2023 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002024 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002025 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
2026 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002027 return;
2028 }
2029 }
2030
2031 if (rrect.isRect()) {
2032 // call the non-virtual version
2033 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002034 return;
2035 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002036 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002037 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2038 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002039 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002040
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002041 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002042
2043 while (iter.next()) {
2044 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2045 }
2046
2047 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002048}
2049
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002050void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2051 const SkPaint& paint) {
2052 CHECK_SHADER_NOSETCONTEXT(paint);
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002053
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002054 SkRect storage;
2055 const SkRect* bounds = NULL;
2056 if (paint.canComputeFastBounds()) {
2057 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
2058 if (this->quickReject(*bounds)) {
2059 return;
2060 }
2061 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002062
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002063 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002064
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002065 while (iter.next()) {
2066 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2067 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002068
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002069 LOOPER_END
2070}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002071
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00002072void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002073 CHECK_SHADER_NOSETCONTEXT(paint);
2074
reed@google.com93645112012-07-26 16:11:47 +00002075 if (!path.isFinite()) {
2076 return;
2077 }
2078
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002079 SkRect storage;
2080 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002081 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002082 const SkRect& pathBounds = path.getBounds();
2083 bounds = &paint.computeFastBounds(pathBounds, &storage);
2084 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002085 return;
2086 }
2087 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002088
2089 const SkRect& r = path.getBounds();
2090 if (r.width() <= 0 && r.height() <= 0) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002091 if (path.isInverseFillType()) {
2092 this->internalDrawPaint(paint);
2093 }
2094 return;
2095 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002096
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002097 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002098
2099 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002100 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002101 }
2102
reed@google.com4e2b3d32011-04-07 14:18:59 +00002103 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002104}
2105
2106void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
2107 const SkPaint* paint) {
2108 SkDEBUGCODE(bitmap.validate();)
2109
reed@google.com3d608122011-11-21 15:16:16 +00002110 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002111 SkRect bounds = {
2112 x, y,
2113 x + SkIntToScalar(bitmap.width()),
2114 y + SkIntToScalar(bitmap.height())
2115 };
2116 if (paint) {
2117 (void)paint->computeFastBounds(bounds, &bounds);
2118 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002119 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002120 return;
2121 }
2122 }
reed@google.com4b226022011-01-11 18:32:13 +00002123
reed@android.com8a1c16f2008-12-17 15:59:43 +00002124 SkMatrix matrix;
2125 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00002126 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002127}
2128
reed@google.com9987ec32011-09-07 11:57:52 +00002129// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002130void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002131 const SkRect& dst, const SkPaint* paint,
2132 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002133 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002134 return;
2135 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002136
reed@google.comea033602012-12-14 13:13:55 +00002137 CHECK_LOCKCOUNT_BALANCE(bitmap);
2138
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002139 SkRect storage;
2140 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00002141 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002142 if (paint) {
2143 bounds = &paint->computeFastBounds(dst, &storage);
2144 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002145 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002146 return;
2147 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002148 }
reed@google.com3d608122011-11-21 15:16:16 +00002149
reed@google.com33535f32012-09-25 15:37:50 +00002150 SkLazyPaint lazy;
2151 if (NULL == paint) {
2152 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002153 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002154
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002155 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002156
reed@google.com33535f32012-09-25 15:37:50 +00002157 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002158 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00002159 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002160
reed@google.com33535f32012-09-25 15:37:50 +00002161 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002162}
2163
reed@google.com71121732012-09-18 15:14:33 +00002164void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002165 const SkRect& dst, const SkPaint* paint,
2166 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00002167 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002168 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00002169}
2170
reed@android.com8a1c16f2008-12-17 15:59:43 +00002171void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
2172 const SkPaint* paint) {
2173 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00002174 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002175}
2176
reed@google.com9987ec32011-09-07 11:57:52 +00002177void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
2178 const SkIRect& center, const SkRect& dst,
2179 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002180 if (bitmap.drawsNothing()) {
2181 return;
2182 }
reed@google.com3d608122011-11-21 15:16:16 +00002183 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00002184 SkRect storage;
2185 const SkRect* bounds = &dst;
2186 if (paint) {
2187 bounds = &paint->computeFastBounds(dst, &storage);
2188 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002189 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002190 return;
2191 }
2192 }
2193
reed@google.com9987ec32011-09-07 11:57:52 +00002194 const int32_t w = bitmap.width();
2195 const int32_t h = bitmap.height();
2196
2197 SkIRect c = center;
2198 // pin center to the bounds of the bitmap
2199 c.fLeft = SkMax32(0, center.fLeft);
2200 c.fTop = SkMax32(0, center.fTop);
2201 c.fRight = SkPin32(center.fRight, c.fLeft, w);
2202 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
2203
reed@google.com71121732012-09-18 15:14:33 +00002204 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002205 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00002206 };
2207 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002208 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00002209 };
reed@google.com9987ec32011-09-07 11:57:52 +00002210 SkScalar dstX[4] = {
2211 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
2212 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
2213 };
2214 SkScalar dstY[4] = {
2215 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2216 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2217 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002218
reed@google.com9987ec32011-09-07 11:57:52 +00002219 if (dstX[1] > dstX[2]) {
2220 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2221 dstX[2] = dstX[1];
2222 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002223
reed@google.com9987ec32011-09-07 11:57:52 +00002224 if (dstY[1] > dstY[2]) {
2225 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2226 dstY[2] = dstY[1];
2227 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002228
reed@google.com9987ec32011-09-07 11:57:52 +00002229 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002230 SkRect s, d;
2231
reed@google.com9987ec32011-09-07 11:57:52 +00002232 s.fTop = srcY[y];
2233 s.fBottom = srcY[y+1];
2234 d.fTop = dstY[y];
2235 d.fBottom = dstY[y+1];
2236 for (int x = 0; x < 3; x++) {
2237 s.fLeft = srcX[x];
2238 s.fRight = srcX[x+1];
2239 d.fLeft = dstX[x];
2240 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002241 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002242 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002243 }
2244 }
2245}
2246
2247void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
2248 const SkRect& dst, const SkPaint* paint) {
2249 SkDEBUGCODE(bitmap.validate();)
2250
2251 // Need a device entry-point, so gpu can use a mesh
2252 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2253}
2254
reed@google.comf67e4cf2011-03-15 20:56:58 +00002255class SkDeviceFilteredPaint {
2256public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002257 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
2258 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002259 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002260 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002261 newPaint->setFlags(flags.fFlags);
2262 newPaint->setHinting(flags.fHinting);
2263 fPaint = newPaint;
2264 } else {
2265 fPaint = &paint;
2266 }
2267 }
2268
reed@google.comf67e4cf2011-03-15 20:56:58 +00002269 const SkPaint& paint() const { return *fPaint; }
2270
2271private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002272 const SkPaint* fPaint;
2273 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002274};
2275
bungeman@google.com52c748b2011-08-22 21:30:43 +00002276void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2277 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002278 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002279 draw.fDevice->drawRect(draw, r, paint);
2280 } else {
2281 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002282 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002283 draw.fDevice->drawRect(draw, r, p);
2284 }
2285}
2286
2287void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2288 const char text[], size_t byteLength,
2289 SkScalar x, SkScalar y) {
2290 SkASSERT(byteLength == 0 || text != NULL);
2291
2292 // nothing to draw
2293 if (text == NULL || byteLength == 0 ||
2294 draw.fClip->isEmpty() ||
2295 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2296 return;
2297 }
2298
2299 SkScalar width = 0;
2300 SkPoint start;
2301
2302 start.set(0, 0); // to avoid warning
2303 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2304 SkPaint::kStrikeThruText_Flag)) {
2305 width = paint.measureText(text, byteLength);
2306
2307 SkScalar offsetX = 0;
2308 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2309 offsetX = SkScalarHalf(width);
2310 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2311 offsetX = width;
2312 }
2313 start.set(x - offsetX, y);
2314 }
2315
2316 if (0 == width) {
2317 return;
2318 }
2319
2320 uint32_t flags = paint.getFlags();
2321
2322 if (flags & (SkPaint::kUnderlineText_Flag |
2323 SkPaint::kStrikeThruText_Flag)) {
2324 SkScalar textSize = paint.getTextSize();
2325 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2326 SkRect r;
2327
2328 r.fLeft = start.fX;
2329 r.fRight = start.fX + width;
2330
2331 if (flags & SkPaint::kUnderlineText_Flag) {
2332 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2333 start.fY);
2334 r.fTop = offset;
2335 r.fBottom = offset + height;
2336 DrawRect(draw, paint, r, textSize);
2337 }
2338 if (flags & SkPaint::kStrikeThruText_Flag) {
2339 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2340 start.fY);
2341 r.fTop = offset;
2342 r.fBottom = offset + height;
2343 DrawRect(draw, paint, r, textSize);
2344 }
2345 }
2346}
2347
reed@android.com8a1c16f2008-12-17 15:59:43 +00002348void SkCanvas::drawText(const void* text, size_t byteLength,
2349 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002350 CHECK_SHADER_NOSETCONTEXT(paint);
2351
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002352 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002353
2354 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002355 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002356 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002357 DrawTextDecorations(iter, dfp.paint(),
2358 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002359 }
2360
reed@google.com4e2b3d32011-04-07 14:18:59 +00002361 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002362}
2363
2364void SkCanvas::drawPosText(const void* text, size_t byteLength,
2365 const SkPoint pos[], const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002366 CHECK_SHADER_NOSETCONTEXT(paint);
2367
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002368 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002369
reed@android.com8a1c16f2008-12-17 15:59:43 +00002370 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002371 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002372 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002373 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002374 }
reed@google.com4b226022011-01-11 18:32:13 +00002375
reed@google.com4e2b3d32011-04-07 14:18:59 +00002376 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002377}
2378
2379void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
2380 const SkScalar xpos[], SkScalar constY,
2381 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002382 CHECK_SHADER_NOSETCONTEXT(paint);
2383
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002384 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002385
reed@android.com8a1c16f2008-12-17 15:59:43 +00002386 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002387 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002388 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002389 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002390 }
reed@google.com4b226022011-01-11 18:32:13 +00002391
reed@google.com4e2b3d32011-04-07 14:18:59 +00002392 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002393}
2394
2395void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
2396 const SkPath& path, const SkMatrix* matrix,
2397 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002398 CHECK_SHADER_NOSETCONTEXT(paint);
2399
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002400 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002401
2402 while (iter.next()) {
2403 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002404 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002405 }
2406
reed@google.com4e2b3d32011-04-07 14:18:59 +00002407 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002408}
2409
2410void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2411 const SkPoint verts[], const SkPoint texs[],
2412 const SkColor colors[], SkXfermode* xmode,
2413 const uint16_t indices[], int indexCount,
2414 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002415 CHECK_SHADER_NOSETCONTEXT(paint);
2416
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002417 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002418
reed@android.com8a1c16f2008-12-17 15:59:43 +00002419 while (iter.next()) {
2420 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002421 colors, xmode, indices, indexCount,
2422 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002423 }
reed@google.com4b226022011-01-11 18:32:13 +00002424
reed@google.com4e2b3d32011-04-07 14:18:59 +00002425 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002426}
2427
2428//////////////////////////////////////////////////////////////////////////////
2429// These methods are NOT virtual, and therefore must call back into virtual
2430// methods, rather than actually drawing themselves.
2431//////////////////////////////////////////////////////////////////////////////
2432
2433void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002434 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002435 SkPaint paint;
2436
2437 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002438 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002439 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002440 }
2441 this->drawPaint(paint);
2442}
2443
reed@android.com845fdac2009-06-23 03:01:32 +00002444void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002445 SkPaint paint;
2446
2447 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002448 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002449 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002450 }
2451 this->drawPaint(paint);
2452}
2453
2454void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2455 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002456
reed@android.com8a1c16f2008-12-17 15:59:43 +00002457 pt.set(x, y);
2458 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2459}
2460
2461void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2462 SkPoint pt;
2463 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002464
reed@android.com8a1c16f2008-12-17 15:59:43 +00002465 pt.set(x, y);
2466 paint.setColor(color);
2467 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2468}
2469
2470void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2471 const SkPaint& paint) {
2472 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002473
reed@android.com8a1c16f2008-12-17 15:59:43 +00002474 pts[0].set(x0, y0);
2475 pts[1].set(x1, y1);
2476 this->drawPoints(kLines_PointMode, 2, pts, paint);
2477}
2478
2479void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2480 SkScalar right, SkScalar bottom,
2481 const SkPaint& paint) {
2482 SkRect r;
2483
2484 r.set(left, top, right, bottom);
2485 this->drawRect(r, paint);
2486}
2487
2488void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2489 const SkPaint& paint) {
2490 if (radius < 0) {
2491 radius = 0;
2492 }
2493
2494 SkRect r;
2495 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002496 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002497}
2498
2499void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2500 const SkPaint& paint) {
2501 if (rx > 0 && ry > 0) {
2502 if (paint.canComputeFastBounds()) {
2503 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002504 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002505 return;
2506 }
2507 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002508 SkRRect rrect;
2509 rrect.setRectXY(r, rx, ry);
2510 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002511 } else {
2512 this->drawRect(r, paint);
2513 }
2514}
2515
reed@android.com8a1c16f2008-12-17 15:59:43 +00002516void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2517 SkScalar sweepAngle, bool useCenter,
2518 const SkPaint& paint) {
2519 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2520 this->drawOval(oval, paint);
2521 } else {
2522 SkPath path;
2523 if (useCenter) {
2524 path.moveTo(oval.centerX(), oval.centerY());
2525 }
2526 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2527 if (useCenter) {
2528 path.close();
2529 }
2530 this->drawPath(path, paint);
2531 }
2532}
2533
2534void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2535 const SkPath& path, SkScalar hOffset,
2536 SkScalar vOffset, const SkPaint& paint) {
2537 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002538
reed@android.com8a1c16f2008-12-17 15:59:43 +00002539 matrix.setTranslate(hOffset, vOffset);
2540 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2541}
2542
reed@android.comf76bacf2009-05-13 14:00:33 +00002543///////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002544void SkCanvas::EXPERIMENTAL_optimize(SkPicture* picture) {
2545 SkBaseDevice* device = this->getDevice();
2546 if (NULL != device) {
2547 device->EXPERIMENTAL_optimize(picture);
2548 }
2549}
reed@android.comf76bacf2009-05-13 14:00:33 +00002550
reed@android.com8a1c16f2008-12-17 15:59:43 +00002551void SkCanvas::drawPicture(SkPicture& picture) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002552 SkBaseDevice* device = this->getTopDevice();
2553 if (NULL != device) {
2554 // Canvas has to first give the device the opportunity to render
2555 // the picture itself.
2556 if (device->EXPERIMENTAL_drawPicture(picture)) {
2557 return; // the device has rendered the entire picture
2558 }
2559 }
2560
reed@android.com8a1c16f2008-12-17 15:59:43 +00002561 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002562}
2563
reed@android.com8a1c16f2008-12-17 15:59:43 +00002564///////////////////////////////////////////////////////////////////////////////
2565///////////////////////////////////////////////////////////////////////////////
2566
2567SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002568 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002569
2570 SkASSERT(canvas);
2571
2572 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2573 fDone = !fImpl->next();
2574}
2575
2576SkCanvas::LayerIter::~LayerIter() {
2577 fImpl->~SkDrawIter();
2578}
2579
2580void SkCanvas::LayerIter::next() {
2581 fDone = !fImpl->next();
2582}
2583
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002584SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002585 return fImpl->getDevice();
2586}
2587
2588const SkMatrix& SkCanvas::LayerIter::matrix() const {
2589 return fImpl->getMatrix();
2590}
2591
2592const SkPaint& SkCanvas::LayerIter::paint() const {
2593 const SkPaint* paint = fImpl->getPaint();
2594 if (NULL == paint) {
2595 paint = &fDefaultPaint;
2596 }
2597 return *paint;
2598}
2599
2600const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2601int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2602int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002603
2604///////////////////////////////////////////////////////////////////////////////
2605
2606SkCanvas::ClipVisitor::~ClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002607
2608///////////////////////////////////////////////////////////////////////////////
2609
2610static bool supported_for_raster_canvas(const SkImageInfo& info) {
2611 switch (info.alphaType()) {
2612 case kPremul_SkAlphaType:
2613 case kOpaque_SkAlphaType:
2614 break;
2615 default:
2616 return false;
2617 }
2618
2619 switch (info.colorType()) {
2620 case kAlpha_8_SkColorType:
2621 case kRGB_565_SkColorType:
2622 case kPMColor_SkColorType:
2623 break;
2624 default:
2625 return false;
2626 }
2627
2628 return true;
2629}
2630
2631SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) {
2632 if (!supported_for_raster_canvas(info)) {
2633 return NULL;
2634 }
2635
2636 SkBitmap bitmap;
2637 if (!bitmap.allocPixels(info)) {
2638 return NULL;
2639 }
2640
2641 // should this functionality be moved into allocPixels()?
2642 if (!bitmap.info().isOpaque()) {
2643 bitmap.eraseColor(0);
2644 }
2645 return SkNEW_ARGS(SkCanvas, (bitmap));
2646}
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002647
2648SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2649 if (!supported_for_raster_canvas(info)) {
2650 return NULL;
2651 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002652
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002653 SkBitmap bitmap;
2654 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2655 return NULL;
2656 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002657
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002658 // should this functionality be moved into allocPixels()?
2659 if (!bitmap.info().isOpaque()) {
2660 bitmap.eraseColor(0);
2661 }
2662 return SkNEW_ARGS(SkCanvas, (bitmap));
2663}