blob: cd2065bd30daaa135d98507edd2b1f5b48c0b091 [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/////////////////////////////////////////////////////////////////////////////
1165
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001166void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001167 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001168 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001169 return;
1170 }
1171
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001172 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001173 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001174 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001175 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001176
1177 SkDEBUGCODE(bitmap.validate();)
1178 CHECK_LOCKCOUNT_BALANCE(bitmap);
1179
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001180 SkRect storage;
1181 const SkRect* bounds = NULL;
1182 if (paint && paint->canComputeFastBounds()) {
1183 bitmap.getBounds(&storage);
1184 matrix.mapRect(&storage);
1185 bounds = &paint->computeFastBounds(storage, &storage);
1186 }
1187
1188 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001189
1190 while (iter.next()) {
1191 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1192 }
1193
1194 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001195}
1196
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001197void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001198 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001199 SkPaint tmp;
1200 if (NULL == paint) {
1201 tmp.setDither(true);
1202 paint = &tmp;
1203 }
reed@google.com4b226022011-01-11 18:32:13 +00001204
reed@google.com8926b162012-03-23 15:36:36 +00001205 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001206 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001207 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001208 paint = &looper.paint();
1209 SkImageFilter* filter = paint->getImageFilter();
1210 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001211 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001212 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001213 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001214 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001215 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001216 SkMatrix matrix = *iter.fMatrix;
1217 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001218 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
1219 SkImageFilter::Context ctx(matrix, clipBounds);
1220 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001221 SkPaint tmpUnfiltered(*paint);
1222 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001223 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1224 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001225 }
1226 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001227 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001228 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001229 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001230 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001231}
1232
reed@google.com8926b162012-03-23 15:36:36 +00001233void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1234 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001235 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001236 return;
1237 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001238 SkDEBUGCODE(bitmap.validate();)
1239 CHECK_LOCKCOUNT_BALANCE(bitmap);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001240
reed@google.com8926b162012-03-23 15:36:36 +00001241 SkPaint tmp;
1242 if (NULL == paint) {
1243 paint = &tmp;
1244 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001245
reed@google.com8926b162012-03-23 15:36:36 +00001246 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001247
reed@google.com8926b162012-03-23 15:36:36 +00001248 while (iter.next()) {
1249 paint = &looper.paint();
1250 SkImageFilter* filter = paint->getImageFilter();
1251 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1252 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001253 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001254 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001255 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001256 SkMatrix matrix = *iter.fMatrix;
1257 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001258 SkIRect clipBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
1259 SkImageFilter::Context ctx(matrix, clipBounds);
1260 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001261 SkPaint tmpUnfiltered(*paint);
1262 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001263 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001264 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001265 }
1266 } else {
1267 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1268 }
1269 }
1270 LOOPER_END
1271}
1272
reed@android.com8a1c16f2008-12-17 15:59:43 +00001273/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001274void SkCanvas::didTranslate(SkScalar, SkScalar) {
1275 // Do nothing. Subclasses may do something.
1276}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001277
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001278void SkCanvas::translate(SkScalar dx, SkScalar dy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001279 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001280 fCachedLocalClipBoundsDirty = true;
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001281 fMCRec->fMatrix->preTranslate(dx, dy);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001282
1283 this->didTranslate(dx, dy);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001284}
1285
1286void SkCanvas::didScale(SkScalar, SkScalar) {
1287 // Do nothing. Subclasses may do something.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001288}
1289
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001290void SkCanvas::scale(SkScalar sx, SkScalar sy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001291 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001292 fCachedLocalClipBoundsDirty = true;
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001293 fMCRec->fMatrix->preScale(sx, sy);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001294
1295 this->didScale(sx, sy);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001296}
1297
1298void SkCanvas::didRotate(SkScalar) {
1299 // Do nothing. Subclasses may do something.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001300}
1301
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001302void SkCanvas::rotate(SkScalar degrees) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001303 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001304 fCachedLocalClipBoundsDirty = true;
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001305 fMCRec->fMatrix->preRotate(degrees);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001306
1307 this->didRotate(degrees);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001308}
1309
1310void SkCanvas::didSkew(SkScalar, SkScalar) {
1311 // Do nothing. Subclasses may do something.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001312}
1313
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001314void SkCanvas::skew(SkScalar sx, SkScalar sy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001315 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001316 fCachedLocalClipBoundsDirty = true;
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001317 fMCRec->fMatrix->preSkew(sx, sy);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001318
1319 this->didSkew(sx, sy);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001320}
1321
1322void SkCanvas::didConcat(const SkMatrix&) {
1323 // Do nothing. Subclasses may do something.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001324}
1325
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001326void SkCanvas::concat(const SkMatrix& matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001327 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001328 fCachedLocalClipBoundsDirty = true;
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001329 fMCRec->fMatrix->preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001330
1331 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001332}
1333
1334void SkCanvas::didSetMatrix(const SkMatrix&) {
1335 // Do nothing. Subclasses may do something.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001336}
1337
1338void SkCanvas::setMatrix(const SkMatrix& matrix) {
1339 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001340 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001341 *fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001342 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001343}
1344
reed@android.com8a1c16f2008-12-17 15:59:43 +00001345void SkCanvas::resetMatrix() {
1346 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001347
reed@android.com8a1c16f2008-12-17 15:59:43 +00001348 matrix.reset();
1349 this->setMatrix(matrix);
1350}
1351
1352//////////////////////////////////////////////////////////////////////////////
1353
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001354void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001355 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1356 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001357}
1358
1359void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001360#ifdef SK_ENABLE_CLIP_QUICKREJECT
1361 if (SkRegion::kIntersect_Op == op) {
1362 if (fMCRec->fRasterClip->isEmpty()) {
1363 return false;
1364 }
1365
reed@google.com3b3e8952012-08-16 20:53:31 +00001366 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001367 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001368 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001369
1370 fClipStack.clipEmpty();
1371 return fMCRec->fRasterClip->setEmpty();
1372 }
1373 }
1374#endif
1375
reed@google.com5c3d1472011-02-22 19:12:23 +00001376 AutoValidateClip avc(this);
1377
reed@android.com8a1c16f2008-12-17 15:59:43 +00001378 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001379 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001380 if (!fAllowSoftClip) {
1381 edgeStyle = kHard_ClipEdgeStyle;
1382 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001383
1384 if (fMCRec->fMatrix->rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001385 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001386 // the matrix. This means we don't have to a) make a path, and b) tell
1387 // the region code to scan-convert the path, only to discover that it
1388 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001389 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001390
1391 fMCRec->fMatrix->mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001392 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
1393 fMCRec->fRasterClip->op(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001394 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001395 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001396 // and clip against that, since it can handle any matrix. However, to
1397 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1398 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001399 SkPath path;
1400
1401 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001402 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001403 }
1404}
1405
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001406static void clip_path_helper(const SkCanvas* canvas, SkRasterClip* currClip,
1407 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001408 // base is used to limit the size (and therefore memory allocation) of the
1409 // region that results from scan converting devPath.
1410 SkRegion base;
1411
reed@google.com819c9212011-02-23 18:56:55 +00001412 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001413 // since we are intersect, we can do better (tighter) with currRgn's
1414 // bounds, than just using the device. However, if currRgn is complex,
1415 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001416 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001417 // FIXME: we should also be able to do this when currClip->isBW(),
1418 // but relaxing the test above triggers GM asserts in
1419 // SkRgnBuilder::blitH(). We need to investigate what's going on.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001420 currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001421 } else {
reed@google.com00177082011-10-12 14:34:30 +00001422 base.setRect(currClip->getBounds());
1423 SkRasterClip clip;
1424 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001425 currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001426 }
reed@google.com819c9212011-02-23 18:56:55 +00001427 } else {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001428 const SkBaseDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001429 if (!device) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001430 currClip->setEmpty();
1431 return;
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001432 }
1433
junov@chromium.orga907ac32012-02-24 21:54:07 +00001434 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001435
1436 if (SkRegion::kReplace_Op == op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001437 currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001438 } else {
reed@google.com00177082011-10-12 14:34:30 +00001439 SkRasterClip clip;
1440 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001441 currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001442 }
1443 }
1444}
1445
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001446void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001447 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001448 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001449 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1450 } else {
1451 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001452 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001453}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001454
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001455void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001456 SkRRect transformedRRect;
1457 if (rrect.transform(*fMCRec->fMatrix, &transformedRRect)) {
1458 AutoValidateClip avc(this);
1459
1460 fDeviceCMDirty = true;
1461 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001462 if (!fAllowSoftClip) {
1463 edgeStyle = kHard_ClipEdgeStyle;
1464 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001465
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001466 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001467
1468 SkPath devPath;
1469 devPath.addRRect(transformedRRect);
1470
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001471 clip_path_helper(this, fMCRec->fRasterClip, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
1472 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001473 }
1474
1475 SkPath path;
1476 path.addRRect(rrect);
1477 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001478 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001479}
1480
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001481void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001482 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1483 SkRect r;
1484 if (!path.isInverseFillType() && path.isRect(&r)) {
1485 this->onClipRect(r, op, edgeStyle);
1486 } else {
1487 this->onClipPath(path, op, edgeStyle);
1488 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001489}
1490
1491void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001492#ifdef SK_ENABLE_CLIP_QUICKREJECT
1493 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1494 if (fMCRec->fRasterClip->isEmpty()) {
1495 return false;
1496 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001497
reed@google.com3b3e8952012-08-16 20:53:31 +00001498 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001499 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001500 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001501
reed@google.comda17f752012-08-16 18:27:05 +00001502 fClipStack.clipEmpty();
1503 return fMCRec->fRasterClip->setEmpty();
1504 }
1505 }
1506#endif
1507
reed@google.com5c3d1472011-02-22 19:12:23 +00001508 AutoValidateClip avc(this);
1509
reed@android.com8a1c16f2008-12-17 15:59:43 +00001510 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001511 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001512 if (!fAllowSoftClip) {
1513 edgeStyle = kHard_ClipEdgeStyle;
1514 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001515
1516 SkPath devPath;
1517 path.transform(*fMCRec->fMatrix, &devPath);
1518
reed@google.comfe701122011-11-08 19:41:23 +00001519 // Check if the transfomation, or the original path itself
1520 // made us empty. Note this can also happen if we contained NaN
1521 // values. computing the bounds detects this, and will set our
1522 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1523 if (devPath.getBounds().isEmpty()) {
1524 // resetting the path will remove any NaN or other wanky values
1525 // that might upset our scan converter.
1526 devPath.reset();
1527 }
1528
reed@google.com5c3d1472011-02-22 19:12:23 +00001529 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001530 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001531
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001532 if (fAllowSimplifyClip) {
1533 devPath.reset();
1534 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1535 const SkClipStack* clipStack = getClipStack();
1536 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1537 const SkClipStack::Element* element;
1538 while ((element = iter.next())) {
1539 SkClipStack::Element::Type type = element->getType();
1540 if (type == SkClipStack::Element::kEmpty_Type) {
1541 continue;
1542 }
1543 SkPath operand;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001544 element->asPath(&operand);
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001545 SkRegion::Op elementOp = element->getOp();
1546 if (elementOp == SkRegion::kReplace_Op) {
1547 devPath = operand;
1548 } else {
1549 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1550 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001551 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1552 // perhaps we need an API change to avoid this sort of mixed-signals about
1553 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001554 if (element->isAA()) {
1555 edgeStyle = kSoft_ClipEdgeStyle;
1556 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001557 }
1558 op = SkRegion::kReplace_Op;
1559 }
1560
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001561 clip_path_helper(this, fMCRec->fRasterClip, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001562}
1563
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001564void SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001565 bool inverseFilled) {
1566 // This is for updating the clip conservatively using only bounds
1567 // information.
1568 // Contract:
1569 // The current clip must contain the true clip. The true
1570 // clip is the clip that would have normally been computed
1571 // by calls to clipPath and clipRRect
1572 // Objective:
1573 // Keep the current clip as small as possible without
1574 // breaking the contract, using only clip bounding rectangles
1575 // (for performance).
1576
1577 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1578 // don't have to worry about getting caught in a loop. Thus anywhere
1579 // we call a virtual method, we explicitly prefix it with
1580 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001581
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001582 if (inverseFilled) {
1583 switch (op) {
1584 case SkRegion::kIntersect_Op:
1585 case SkRegion::kDifference_Op:
1586 // These ops can only shrink the current clip. So leaving
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001587 // the clip unchanged conservatively respects the contract.
1588 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001589 case SkRegion::kUnion_Op:
1590 case SkRegion::kReplace_Op:
1591 case SkRegion::kReverseDifference_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001592 case SkRegion::kXOR_Op: {
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001593 // These ops can grow the current clip up to the extents of
1594 // the input clip, which is inverse filled, so we just set
1595 // the current clip to the device bounds.
1596 SkRect deviceBounds;
1597 SkIRect deviceIBounds;
1598 this->getDevice()->getGlobalBounds(&deviceIBounds);
reed@google.com44699382013-10-31 17:28:30 +00001599 deviceBounds = SkRect::Make(deviceIBounds);
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001600 this->SkCanvas::save(SkCanvas::kMatrix_SaveFlag);
1601 // set the clip in device space
1602 this->SkCanvas::setMatrix(SkMatrix::I());
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001603 this->SkCanvas::onClipRect(deviceBounds, SkRegion::kReplace_Op,
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001604 kHard_ClipEdgeStyle);
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001605 this->SkCanvas::restore(); //pop the matrix, but keep the clip
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001606 break;
1607 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001608 default:
1609 SkASSERT(0); // unhandled op?
1610 }
1611 } else {
1612 // Not inverse filled
1613 switch (op) {
1614 case SkRegion::kIntersect_Op:
1615 case SkRegion::kUnion_Op:
1616 case SkRegion::kReplace_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001617 this->SkCanvas::onClipRect(bounds, op, kHard_ClipEdgeStyle);
1618 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001619 case SkRegion::kDifference_Op:
1620 // Difference can only shrink the current clip.
1621 // Leaving clip unchanged conservatively fullfills the contract.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001622 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001623 case SkRegion::kReverseDifference_Op:
1624 // To reverse, we swap in the bounds with a replace op.
1625 // As with difference, leave it unchanged.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001626 this->SkCanvas::onClipRect(bounds, SkRegion::kReplace_Op, kHard_ClipEdgeStyle);
1627 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001628 case SkRegion::kXOR_Op:
1629 // Be conservative, based on (A XOR B) always included in (A union B),
1630 // which is always included in (bounds(A) union bounds(B))
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001631 this->SkCanvas::onClipRect(bounds, SkRegion::kUnion_Op, kHard_ClipEdgeStyle);
1632 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001633 default:
1634 SkASSERT(0); // unhandled op?
1635 }
1636 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001637}
1638
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001639void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001640 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001641}
1642
1643void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001644 AutoValidateClip avc(this);
1645
reed@android.com8a1c16f2008-12-17 15:59:43 +00001646 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001647 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001648
reed@google.com5c3d1472011-02-22 19:12:23 +00001649 // todo: signal fClipStack that we have a region, and therefore (I guess)
1650 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001651 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001652
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001653 fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001654}
1655
reed@google.com819c9212011-02-23 18:56:55 +00001656#ifdef SK_DEBUG
1657void SkCanvas::validateClip() const {
1658 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001659 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001660 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001661 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001662 return;
1663 }
1664
reed@google.com819c9212011-02-23 18:56:55 +00001665 SkIRect ir;
1666 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001667 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001668
robertphillips@google.com80214e22012-07-20 15:33:18 +00001669 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001670 const SkClipStack::Element* element;
1671 while ((element = iter.next()) != NULL) {
1672 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001673 case SkClipStack::Element::kRect_Type:
1674 element->getRect().round(&ir);
1675 tmpClip.op(ir, element->getOp());
1676 break;
1677 case SkClipStack::Element::kEmpty_Type:
1678 tmpClip.setEmpty();
1679 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001680 default: {
1681 SkPath path;
1682 element->asPath(&path);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001683 clip_path_helper(this, &tmpClip, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001684 break;
1685 }
reed@google.com819c9212011-02-23 18:56:55 +00001686 }
1687 }
reed@google.com819c9212011-02-23 18:56:55 +00001688}
1689#endif
1690
reed@google.com90c07ea2012-04-13 13:50:27 +00001691void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001692 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001693 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001694
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001695 static const SkRect kEmpty = { 0, 0, 0, 0 };
1696 while ((element = iter.next()) != NULL) {
1697 switch (element->getType()) {
1698 case SkClipStack::Element::kPath_Type:
1699 visitor->clipPath(element->getPath(), element->getOp(), element->isAA());
1700 break;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001701 case SkClipStack::Element::kRRect_Type:
1702 visitor->clipRRect(element->getRRect(), element->getOp(), element->isAA());
1703 break;
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001704 case SkClipStack::Element::kRect_Type:
1705 visitor->clipRect(element->getRect(), element->getOp(), element->isAA());
1706 break;
1707 case SkClipStack::Element::kEmpty_Type:
1708 visitor->clipRect(kEmpty, SkRegion::kIntersect_Op, false);
1709 break;
reed@google.com90c07ea2012-04-13 13:50:27 +00001710 }
1711 }
1712}
1713
reed@google.com5c3d1472011-02-22 19:12:23 +00001714///////////////////////////////////////////////////////////////////////////////
1715
reed@google.com754de5f2014-02-24 19:38:20 +00001716bool SkCanvas::isClipEmpty() const {
1717 return fMCRec->fRasterClip->isEmpty();
1718}
1719
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001720bool SkCanvas::isClipRect() const {
1721 return fMCRec->fRasterClip->isRect();
1722}
1723
reed@google.com3b3e8952012-08-16 20:53:31 +00001724bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001725
reed@google.com16078632011-12-06 18:56:37 +00001726 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001727 return true;
1728
reed@google.com00177082011-10-12 14:34:30 +00001729 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001730 return true;
1731 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001732
tomhudson@google.com8d430182011-06-06 19:11:19 +00001733 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001734 SkRect dst;
1735 fMCRec->fMatrix->mapRect(&dst, rect);
1736 SkIRect idst;
1737 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001738 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001739 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001740 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001741
reed@android.coma380ae42009-07-21 01:17:02 +00001742 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001743 // TODO: should we use | instead, or compare all 4 at once?
1744 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001745 return true;
1746 }
reed@google.comc0784db2013-12-13 21:16:12 +00001747 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001748 return true;
1749 }
1750 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001751 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001752}
1753
reed@google.com3b3e8952012-08-16 20:53:31 +00001754bool SkCanvas::quickReject(const SkPath& path) const {
1755 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001756}
1757
reed@google.com3b3e8952012-08-16 20:53:31 +00001758bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001759 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001760 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001761 return false;
1762 }
1763
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001764 SkMatrix inverse;
1765 // if we can't invert the CTM, we can't return local clip bounds
1766 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001767 if (bounds) {
1768 bounds->setEmpty();
1769 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001770 return false;
1771 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001772
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001773 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001774 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001775 // adjust it outwards in case we are antialiasing
1776 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001777
reed@google.com8f4d2302013-12-17 16:44:46 +00001778 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1779 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001780 inverse.mapRect(bounds, r);
1781 }
1782 return true;
1783}
1784
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001785bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001786 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001787 if (clip.isEmpty()) {
1788 if (bounds) {
1789 bounds->setEmpty();
1790 }
1791 return false;
1792 }
1793
1794 if (NULL != bounds) {
1795 *bounds = clip.getBounds();
1796 }
1797 return true;
1798}
1799
reed@android.com8a1c16f2008-12-17 15:59:43 +00001800const SkMatrix& SkCanvas::getTotalMatrix() const {
1801 return *fMCRec->fMatrix;
1802}
1803
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001804#ifdef SK_SUPPORT_LEGACY_GETCLIPTYPE
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001805SkCanvas::ClipType SkCanvas::getClipType() const {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001806 if (fMCRec->fRasterClip->isEmpty()) {
1807 return kEmpty_ClipType;
1808 }
1809 if (fMCRec->fRasterClip->isRect()) {
1810 return kRect_ClipType;
1811 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001812 return kComplex_ClipType;
1813}
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001814#endif
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001815
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001816#ifdef SK_SUPPORT_LEGACY_GETTOTALCLIP
reed@android.com8a1c16f2008-12-17 15:59:43 +00001817const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001818 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001819}
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001820#endif
1821
1822const SkRegion& SkCanvas::internal_private_getTotalClip() const {
1823 return fMCRec->fRasterClip->forceGetBW();
1824}
1825
1826void SkCanvas::internal_private_getTotalClipAsPath(SkPath* path) const {
1827 path->reset();
1828
1829 const SkRegion& rgn = fMCRec->fRasterClip->forceGetBW();
1830 if (rgn.isEmpty()) {
1831 return;
1832 }
1833 (void)rgn.getBoundaryPath(path);
1834}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001835
reed@google.com9c135db2014-03-12 18:28:35 +00001836GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1837 SkBaseDevice* dev = this->getTopDevice();
1838 return dev ? dev->accessRenderTarget() : NULL;
1839}
1840
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001841SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001842 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001843 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001844}
1845
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001846GrContext* SkCanvas::getGrContext() {
1847#if SK_SUPPORT_GPU
1848 SkBaseDevice* device = this->getTopDevice();
1849 if (NULL != device) {
1850 GrRenderTarget* renderTarget = device->accessRenderTarget();
1851 if (NULL != renderTarget) {
1852 return renderTarget->getContext();
1853 }
1854 }
1855#endif
1856
1857 return NULL;
1858
1859}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001860
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001861void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1862 const SkPaint& paint) {
1863 if (outer.isEmpty()) {
1864 return;
1865 }
1866 if (inner.isEmpty()) {
1867 this->drawRRect(outer, paint);
1868 return;
1869 }
1870
1871 // We don't have this method (yet), but technically this is what we should
1872 // be able to assert...
1873 // SkASSERT(outer.contains(inner));
1874 //
1875 // For now at least check for containment of bounds
1876 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1877
1878 this->onDrawDRRect(outer, inner, paint);
1879}
1880
reed@android.com8a1c16f2008-12-17 15:59:43 +00001881//////////////////////////////////////////////////////////////////////////////
1882// These are the virtual drawing methods
1883//////////////////////////////////////////////////////////////////////////////
1884
reed@google.com2a981812011-04-14 18:59:28 +00001885void SkCanvas::clear(SkColor color) {
1886 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001887 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001888 while (iter.next()) {
1889 iter.fDevice->clear(color);
1890 }
1891}
1892
reed@android.com8a1c16f2008-12-17 15:59:43 +00001893void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001894 this->internalDrawPaint(paint);
1895}
1896
1897void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001898 CHECK_SHADER_NOSETCONTEXT(paint);
1899
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001900 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001901
1902 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001903 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001904 }
1905
reed@google.com4e2b3d32011-04-07 14:18:59 +00001906 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001907}
1908
1909void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1910 const SkPaint& paint) {
1911 if ((long)count <= 0) {
1912 return;
1913 }
1914
reed@google.comea033602012-12-14 13:13:55 +00001915 CHECK_SHADER_NOSETCONTEXT(paint);
1916
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001917 SkRect r, storage;
1918 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001919 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001920 // special-case 2 points (common for drawing a single line)
1921 if (2 == count) {
1922 r.set(pts[0], pts[1]);
1923 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001924 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001925 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001926 bounds = &paint.computeFastStrokeBounds(r, &storage);
1927 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001928 return;
1929 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001930 }
reed@google.coma584aed2012-05-16 14:06:02 +00001931
reed@android.com8a1c16f2008-12-17 15:59:43 +00001932 SkASSERT(pts != NULL);
1933
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001934 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001935
reed@android.com8a1c16f2008-12-17 15:59:43 +00001936 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001937 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001938 }
reed@google.com4b226022011-01-11 18:32:13 +00001939
reed@google.com4e2b3d32011-04-07 14:18:59 +00001940 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001941}
1942
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001943void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001944 CHECK_SHADER_NOSETCONTEXT(paint);
1945
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001946 SkRect storage;
1947 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001948 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001949 bounds = &paint.computeFastBounds(r, &storage);
1950 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001951 return;
1952 }
1953 }
reed@google.com4b226022011-01-11 18:32:13 +00001954
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001955 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001956
1957 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001958 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001959 }
1960
reed@google.com4e2b3d32011-04-07 14:18:59 +00001961 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001962}
1963
reed@google.com4ed0fb72012-12-12 20:48:18 +00001964void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001965 CHECK_SHADER_NOSETCONTEXT(paint);
1966
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001967 SkRect storage;
1968 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001969 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001970 bounds = &paint.computeFastBounds(oval, &storage);
1971 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001972 return;
1973 }
1974 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001975
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001976 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001977
1978 while (iter.next()) {
1979 iter.fDevice->drawOval(iter, oval, looper.paint());
1980 }
1981
1982 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001983}
1984
1985void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001986 CHECK_SHADER_NOSETCONTEXT(paint);
1987
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001988 SkRect storage;
1989 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001990 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001991 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1992 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001993 return;
1994 }
1995 }
1996
1997 if (rrect.isRect()) {
1998 // call the non-virtual version
1999 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002000 return;
2001 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002002 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002003 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2004 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002005 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002006
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002007 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002008
2009 while (iter.next()) {
2010 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2011 }
2012
2013 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002014}
2015
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002016void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2017 const SkPaint& paint) {
2018 CHECK_SHADER_NOSETCONTEXT(paint);
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002019
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002020 SkRect storage;
2021 const SkRect* bounds = NULL;
2022 if (paint.canComputeFastBounds()) {
2023 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
2024 if (this->quickReject(*bounds)) {
2025 return;
2026 }
2027 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002028
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002029 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002030
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002031 while (iter.next()) {
2032 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2033 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002034
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002035 LOOPER_END
2036}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002037
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00002038void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002039 CHECK_SHADER_NOSETCONTEXT(paint);
2040
reed@google.com93645112012-07-26 16:11:47 +00002041 if (!path.isFinite()) {
2042 return;
2043 }
2044
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002045 SkRect storage;
2046 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002047 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002048 const SkRect& pathBounds = path.getBounds();
2049 bounds = &paint.computeFastBounds(pathBounds, &storage);
2050 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002051 return;
2052 }
2053 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002054
2055 const SkRect& r = path.getBounds();
2056 if (r.width() <= 0 && r.height() <= 0) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002057 if (path.isInverseFillType()) {
2058 this->internalDrawPaint(paint);
2059 }
2060 return;
2061 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002062
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002063 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002064
2065 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002066 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002067 }
2068
reed@google.com4e2b3d32011-04-07 14:18:59 +00002069 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002070}
2071
2072void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
2073 const SkPaint* paint) {
2074 SkDEBUGCODE(bitmap.validate();)
2075
reed@google.com3d608122011-11-21 15:16:16 +00002076 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002077 SkRect bounds = {
2078 x, y,
2079 x + SkIntToScalar(bitmap.width()),
2080 y + SkIntToScalar(bitmap.height())
2081 };
2082 if (paint) {
2083 (void)paint->computeFastBounds(bounds, &bounds);
2084 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002085 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002086 return;
2087 }
2088 }
reed@google.com4b226022011-01-11 18:32:13 +00002089
reed@android.com8a1c16f2008-12-17 15:59:43 +00002090 SkMatrix matrix;
2091 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00002092 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002093}
2094
reed@google.com9987ec32011-09-07 11:57:52 +00002095// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002096void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002097 const SkRect& dst, const SkPaint* paint,
2098 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002099 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002100 return;
2101 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002102
reed@google.comea033602012-12-14 13:13:55 +00002103 CHECK_LOCKCOUNT_BALANCE(bitmap);
2104
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002105 SkRect storage;
2106 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00002107 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002108 if (paint) {
2109 bounds = &paint->computeFastBounds(dst, &storage);
2110 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002111 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002112 return;
2113 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002114 }
reed@google.com3d608122011-11-21 15:16:16 +00002115
reed@google.com33535f32012-09-25 15:37:50 +00002116 SkLazyPaint lazy;
2117 if (NULL == paint) {
2118 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002119 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002120
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002121 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002122
reed@google.com33535f32012-09-25 15:37:50 +00002123 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002124 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00002125 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002126
reed@google.com33535f32012-09-25 15:37:50 +00002127 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002128}
2129
reed@google.com71121732012-09-18 15:14:33 +00002130void SkCanvas::drawBitmapRectToRect(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) {
reed@google.com9987ec32011-09-07 11:57:52 +00002133 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002134 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00002135}
2136
reed@android.com8a1c16f2008-12-17 15:59:43 +00002137void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
2138 const SkPaint* paint) {
2139 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00002140 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002141}
2142
reed@google.com9987ec32011-09-07 11:57:52 +00002143void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
2144 const SkIRect& center, const SkRect& dst,
2145 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002146 if (bitmap.drawsNothing()) {
2147 return;
2148 }
reed@google.com3d608122011-11-21 15:16:16 +00002149 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00002150 SkRect storage;
2151 const SkRect* bounds = &dst;
2152 if (paint) {
2153 bounds = &paint->computeFastBounds(dst, &storage);
2154 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002155 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002156 return;
2157 }
2158 }
2159
reed@google.com9987ec32011-09-07 11:57:52 +00002160 const int32_t w = bitmap.width();
2161 const int32_t h = bitmap.height();
2162
2163 SkIRect c = center;
2164 // pin center to the bounds of the bitmap
2165 c.fLeft = SkMax32(0, center.fLeft);
2166 c.fTop = SkMax32(0, center.fTop);
2167 c.fRight = SkPin32(center.fRight, c.fLeft, w);
2168 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
2169
reed@google.com71121732012-09-18 15:14:33 +00002170 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002171 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00002172 };
2173 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002174 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00002175 };
reed@google.com9987ec32011-09-07 11:57:52 +00002176 SkScalar dstX[4] = {
2177 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
2178 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
2179 };
2180 SkScalar dstY[4] = {
2181 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2182 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2183 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002184
reed@google.com9987ec32011-09-07 11:57:52 +00002185 if (dstX[1] > dstX[2]) {
2186 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2187 dstX[2] = dstX[1];
2188 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002189
reed@google.com9987ec32011-09-07 11:57:52 +00002190 if (dstY[1] > dstY[2]) {
2191 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2192 dstY[2] = dstY[1];
2193 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002194
reed@google.com9987ec32011-09-07 11:57:52 +00002195 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002196 SkRect s, d;
2197
reed@google.com9987ec32011-09-07 11:57:52 +00002198 s.fTop = srcY[y];
2199 s.fBottom = srcY[y+1];
2200 d.fTop = dstY[y];
2201 d.fBottom = dstY[y+1];
2202 for (int x = 0; x < 3; x++) {
2203 s.fLeft = srcX[x];
2204 s.fRight = srcX[x+1];
2205 d.fLeft = dstX[x];
2206 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002207 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002208 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002209 }
2210 }
2211}
2212
2213void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
2214 const SkRect& dst, const SkPaint* paint) {
2215 SkDEBUGCODE(bitmap.validate();)
2216
2217 // Need a device entry-point, so gpu can use a mesh
2218 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2219}
2220
reed@google.comf67e4cf2011-03-15 20:56:58 +00002221class SkDeviceFilteredPaint {
2222public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002223 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
2224 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002225 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002226 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002227 newPaint->setFlags(flags.fFlags);
2228 newPaint->setHinting(flags.fHinting);
2229 fPaint = newPaint;
2230 } else {
2231 fPaint = &paint;
2232 }
2233 }
2234
reed@google.comf67e4cf2011-03-15 20:56:58 +00002235 const SkPaint& paint() const { return *fPaint; }
2236
2237private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002238 const SkPaint* fPaint;
2239 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002240};
2241
bungeman@google.com52c748b2011-08-22 21:30:43 +00002242void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2243 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002244 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002245 draw.fDevice->drawRect(draw, r, paint);
2246 } else {
2247 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002248 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002249 draw.fDevice->drawRect(draw, r, p);
2250 }
2251}
2252
2253void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2254 const char text[], size_t byteLength,
2255 SkScalar x, SkScalar y) {
2256 SkASSERT(byteLength == 0 || text != NULL);
2257
2258 // nothing to draw
2259 if (text == NULL || byteLength == 0 ||
2260 draw.fClip->isEmpty() ||
2261 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2262 return;
2263 }
2264
2265 SkScalar width = 0;
2266 SkPoint start;
2267
2268 start.set(0, 0); // to avoid warning
2269 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2270 SkPaint::kStrikeThruText_Flag)) {
2271 width = paint.measureText(text, byteLength);
2272
2273 SkScalar offsetX = 0;
2274 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2275 offsetX = SkScalarHalf(width);
2276 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2277 offsetX = width;
2278 }
2279 start.set(x - offsetX, y);
2280 }
2281
2282 if (0 == width) {
2283 return;
2284 }
2285
2286 uint32_t flags = paint.getFlags();
2287
2288 if (flags & (SkPaint::kUnderlineText_Flag |
2289 SkPaint::kStrikeThruText_Flag)) {
2290 SkScalar textSize = paint.getTextSize();
2291 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2292 SkRect r;
2293
2294 r.fLeft = start.fX;
2295 r.fRight = start.fX + width;
2296
2297 if (flags & SkPaint::kUnderlineText_Flag) {
2298 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2299 start.fY);
2300 r.fTop = offset;
2301 r.fBottom = offset + height;
2302 DrawRect(draw, paint, r, textSize);
2303 }
2304 if (flags & SkPaint::kStrikeThruText_Flag) {
2305 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2306 start.fY);
2307 r.fTop = offset;
2308 r.fBottom = offset + height;
2309 DrawRect(draw, paint, r, textSize);
2310 }
2311 }
2312}
2313
reed@android.com8a1c16f2008-12-17 15:59:43 +00002314void SkCanvas::drawText(const void* text, size_t byteLength,
2315 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002316 CHECK_SHADER_NOSETCONTEXT(paint);
2317
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002318 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002319
2320 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002321 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002322 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002323 DrawTextDecorations(iter, dfp.paint(),
2324 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002325 }
2326
reed@google.com4e2b3d32011-04-07 14:18:59 +00002327 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002328}
2329
2330void SkCanvas::drawPosText(const void* text, size_t byteLength,
2331 const SkPoint pos[], const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002332 CHECK_SHADER_NOSETCONTEXT(paint);
2333
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002334 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002335
reed@android.com8a1c16f2008-12-17 15:59:43 +00002336 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002337 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002338 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002339 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002340 }
reed@google.com4b226022011-01-11 18:32:13 +00002341
reed@google.com4e2b3d32011-04-07 14:18:59 +00002342 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002343}
2344
2345void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
2346 const SkScalar xpos[], SkScalar constY,
2347 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002348 CHECK_SHADER_NOSETCONTEXT(paint);
2349
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002350 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002351
reed@android.com8a1c16f2008-12-17 15:59:43 +00002352 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002353 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002354 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002355 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002356 }
reed@google.com4b226022011-01-11 18:32:13 +00002357
reed@google.com4e2b3d32011-04-07 14:18:59 +00002358 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002359}
2360
2361void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
2362 const SkPath& path, const SkMatrix* matrix,
2363 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002364 CHECK_SHADER_NOSETCONTEXT(paint);
2365
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002366 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002367
2368 while (iter.next()) {
2369 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002370 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002371 }
2372
reed@google.com4e2b3d32011-04-07 14:18:59 +00002373 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002374}
2375
2376void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2377 const SkPoint verts[], const SkPoint texs[],
2378 const SkColor colors[], SkXfermode* xmode,
2379 const uint16_t indices[], int indexCount,
2380 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002381 CHECK_SHADER_NOSETCONTEXT(paint);
2382
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002383 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002384
reed@android.com8a1c16f2008-12-17 15:59:43 +00002385 while (iter.next()) {
2386 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002387 colors, xmode, indices, indexCount,
2388 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002389 }
reed@google.com4b226022011-01-11 18:32:13 +00002390
reed@google.com4e2b3d32011-04-07 14:18:59 +00002391 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002392}
2393
2394//////////////////////////////////////////////////////////////////////////////
2395// These methods are NOT virtual, and therefore must call back into virtual
2396// methods, rather than actually drawing themselves.
2397//////////////////////////////////////////////////////////////////////////////
2398
2399void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002400 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002401 SkPaint paint;
2402
2403 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002404 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002405 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002406 }
2407 this->drawPaint(paint);
2408}
2409
reed@android.com845fdac2009-06-23 03:01:32 +00002410void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002411 SkPaint paint;
2412
2413 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002414 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002415 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002416 }
2417 this->drawPaint(paint);
2418}
2419
2420void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2421 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002422
reed@android.com8a1c16f2008-12-17 15:59:43 +00002423 pt.set(x, y);
2424 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2425}
2426
2427void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2428 SkPoint pt;
2429 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002430
reed@android.com8a1c16f2008-12-17 15:59:43 +00002431 pt.set(x, y);
2432 paint.setColor(color);
2433 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2434}
2435
2436void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2437 const SkPaint& paint) {
2438 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002439
reed@android.com8a1c16f2008-12-17 15:59:43 +00002440 pts[0].set(x0, y0);
2441 pts[1].set(x1, y1);
2442 this->drawPoints(kLines_PointMode, 2, pts, paint);
2443}
2444
2445void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2446 SkScalar right, SkScalar bottom,
2447 const SkPaint& paint) {
2448 SkRect r;
2449
2450 r.set(left, top, right, bottom);
2451 this->drawRect(r, paint);
2452}
2453
2454void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2455 const SkPaint& paint) {
2456 if (radius < 0) {
2457 radius = 0;
2458 }
2459
2460 SkRect r;
2461 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002462 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002463}
2464
2465void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2466 const SkPaint& paint) {
2467 if (rx > 0 && ry > 0) {
2468 if (paint.canComputeFastBounds()) {
2469 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002470 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002471 return;
2472 }
2473 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002474 SkRRect rrect;
2475 rrect.setRectXY(r, rx, ry);
2476 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002477 } else {
2478 this->drawRect(r, paint);
2479 }
2480}
2481
reed@android.com8a1c16f2008-12-17 15:59:43 +00002482void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2483 SkScalar sweepAngle, bool useCenter,
2484 const SkPaint& paint) {
2485 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2486 this->drawOval(oval, paint);
2487 } else {
2488 SkPath path;
2489 if (useCenter) {
2490 path.moveTo(oval.centerX(), oval.centerY());
2491 }
2492 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2493 if (useCenter) {
2494 path.close();
2495 }
2496 this->drawPath(path, paint);
2497 }
2498}
2499
2500void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2501 const SkPath& path, SkScalar hOffset,
2502 SkScalar vOffset, const SkPaint& paint) {
2503 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002504
reed@android.com8a1c16f2008-12-17 15:59:43 +00002505 matrix.setTranslate(hOffset, vOffset);
2506 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2507}
2508
reed@android.comf76bacf2009-05-13 14:00:33 +00002509///////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002510void SkCanvas::EXPERIMENTAL_optimize(SkPicture* picture) {
2511 SkBaseDevice* device = this->getDevice();
2512 if (NULL != device) {
2513 device->EXPERIMENTAL_optimize(picture);
2514 }
2515}
reed@android.comf76bacf2009-05-13 14:00:33 +00002516
reed@android.com8a1c16f2008-12-17 15:59:43 +00002517void SkCanvas::drawPicture(SkPicture& picture) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002518 SkBaseDevice* device = this->getTopDevice();
2519 if (NULL != device) {
2520 // Canvas has to first give the device the opportunity to render
2521 // the picture itself.
2522 if (device->EXPERIMENTAL_drawPicture(picture)) {
2523 return; // the device has rendered the entire picture
2524 }
2525 }
2526
reed@android.com8a1c16f2008-12-17 15:59:43 +00002527 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002528}
2529
reed@android.com8a1c16f2008-12-17 15:59:43 +00002530///////////////////////////////////////////////////////////////////////////////
2531///////////////////////////////////////////////////////////////////////////////
2532
2533SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002534 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002535
2536 SkASSERT(canvas);
2537
2538 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2539 fDone = !fImpl->next();
2540}
2541
2542SkCanvas::LayerIter::~LayerIter() {
2543 fImpl->~SkDrawIter();
2544}
2545
2546void SkCanvas::LayerIter::next() {
2547 fDone = !fImpl->next();
2548}
2549
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002550SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002551 return fImpl->getDevice();
2552}
2553
2554const SkMatrix& SkCanvas::LayerIter::matrix() const {
2555 return fImpl->getMatrix();
2556}
2557
2558const SkPaint& SkCanvas::LayerIter::paint() const {
2559 const SkPaint* paint = fImpl->getPaint();
2560 if (NULL == paint) {
2561 paint = &fDefaultPaint;
2562 }
2563 return *paint;
2564}
2565
2566const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2567int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2568int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002569
2570///////////////////////////////////////////////////////////////////////////////
2571
2572SkCanvas::ClipVisitor::~ClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002573
2574///////////////////////////////////////////////////////////////////////////////
2575
2576static bool supported_for_raster_canvas(const SkImageInfo& info) {
2577 switch (info.alphaType()) {
2578 case kPremul_SkAlphaType:
2579 case kOpaque_SkAlphaType:
2580 break;
2581 default:
2582 return false;
2583 }
2584
2585 switch (info.colorType()) {
2586 case kAlpha_8_SkColorType:
2587 case kRGB_565_SkColorType:
2588 case kPMColor_SkColorType:
2589 break;
2590 default:
2591 return false;
2592 }
2593
2594 return true;
2595}
2596
2597SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) {
2598 if (!supported_for_raster_canvas(info)) {
2599 return NULL;
2600 }
2601
2602 SkBitmap bitmap;
2603 if (!bitmap.allocPixels(info)) {
2604 return NULL;
2605 }
2606
2607 // should this functionality be moved into allocPixels()?
2608 if (!bitmap.info().isOpaque()) {
2609 bitmap.eraseColor(0);
2610 }
2611 return SkNEW_ARGS(SkCanvas, (bitmap));
2612}
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002613
2614SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2615 if (!supported_for_raster_canvas(info)) {
2616 return NULL;
2617 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002618
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002619 SkBitmap bitmap;
2620 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2621 return NULL;
2622 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002623
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002624 // should this functionality be moved into allocPixels()?
2625 if (!bitmap.info().isOpaque()) {
2626 bitmap.eraseColor(0);
2627 }
2628 return SkNEW_ARGS(SkCanvas, (bitmap));
2629}