blob: 1443f5e575551bea03157e3be8635dda09011ae4 [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
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000670bool SkCanvas::readPixels(SkBitmap* bitmap,
671 int x, int y,
672 Config8888 config8888) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000673 SkBaseDevice* device = this->getDevice();
reed@google.com51df9e32010-12-23 19:29:18 +0000674 if (!device) {
675 return false;
676 }
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000677 return device->readPixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000678}
679
bsalomon@google.comc6980972011-11-02 19:57:21 +0000680bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000681 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000682 if (!device) {
683 return false;
684 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000685
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000686 SkIRect bounds;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000687 bounds.set(0, 0, device->width(), device->height());
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000688 if (!bounds.intersect(srcRect)) {
689 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000690 }
691
692 SkBitmap tmp;
693 tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
694 bounds.height());
695 if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
696 bitmap->swap(tmp);
697 return true;
698 } else {
reed@google.com51df9e32010-12-23 19:29:18 +0000699 return false;
700 }
reed@google.com51df9e32010-12-23 19:29:18 +0000701}
702
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000703#ifdef SK_SUPPORT_LEGACY_WRITEPIXELSCONFIG
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000704void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
705 Config8888 config8888) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000706 SkBaseDevice* device = this->getDevice();
reed@google.com51df9e32010-12-23 19:29:18 +0000707 if (device) {
bsalomon@google.com405d0f42012-08-29 21:26:13 +0000708 if (SkIRect::Intersects(SkIRect::MakeSize(this->getDeviceSize()),
709 SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height()))) {
710 device->accessBitmap(true);
711 device->writePixels(bitmap, x, y, config8888);
712 }
reed@google.com51df9e32010-12-23 19:29:18 +0000713 }
714}
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000715#endif
716
717bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
718 if (bitmap.getTexture()) {
719 return false;
720 }
721 SkBitmap bm(bitmap);
722 bm.lockPixels();
723 if (bm.getPixels()) {
724 return this->writePixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y);
725 }
726 return false;
727}
728
729bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
730 int x, int y) {
731 switch (origInfo.colorType()) {
732 case kUnknown_SkColorType:
733 case kIndex_8_SkColorType:
734 return false;
735 default:
736 break;
737 }
738 if (NULL == pixels || rowBytes < origInfo.minRowBytes()) {
739 return false;
740 }
741
742 const SkISize size = this->getBaseLayerSize();
743 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
744 if (!target.intersect(0, 0, size.width(), size.height())) {
745 return false;
746 }
747
748 SkBaseDevice* device = this->getDevice();
749 if (!device) {
750 return false;
751 }
752
753 SkImageInfo info = origInfo;
754 // the intersect may have shrunk info's logical size
755 info.fWidth = target.width();
756 info.fHeight = target.height();
757
758 // if x or y are negative, then we have to adjust pixels
759 if (x > 0) {
760 x = 0;
761 }
762 if (y > 0) {
763 y = 0;
764 }
765 // here x,y are either 0 or negative
766 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
767
768 // The device can assert that the requested area is always contained in its bounds
769 return device->writePixelsDirect(info, pixels, rowBytes, target.x(), target.y());
770}
reed@google.com51df9e32010-12-23 19:29:18 +0000771
junov@google.com4370aed2012-01-18 16:21:08 +0000772SkCanvas* SkCanvas::canvasForDrawIter() {
773 return this;
774}
775
reed@android.com8a1c16f2008-12-17 15:59:43 +0000776//////////////////////////////////////////////////////////////////////////////
777
reed@android.com8a1c16f2008-12-17 15:59:43 +0000778void SkCanvas::updateDeviceCMCache() {
779 if (fDeviceCMDirty) {
780 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000781 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000782 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000783
reed@android.com8a1c16f2008-12-17 15:59:43 +0000784 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000785 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000786 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000787 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000788 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000789 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000790 } while ((layer = layer->fNext) != NULL);
791 }
792 fDeviceCMDirty = false;
793 }
794}
795
reed@android.com8a1c16f2008-12-17 15:59:43 +0000796///////////////////////////////////////////////////////////////////////////////
797
798int SkCanvas::internalSave(SaveFlags flags) {
799 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000800
reed@android.com8a1c16f2008-12-17 15:59:43 +0000801 MCRec* newTop = (MCRec*)fMCStack.push_back();
802 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000803
reed@android.com8a1c16f2008-12-17 15:59:43 +0000804 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000805
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000806 if (SkCanvas::kClip_SaveFlag & flags) {
807 fClipStack.save();
808 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000809
reed@android.com8a1c16f2008-12-17 15:59:43 +0000810 return saveCount;
811}
812
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000813void SkCanvas::willSave(SaveFlags) {
814 // Do nothing. Subclasses may do something.
815}
816
reed@android.com8a1c16f2008-12-17 15:59:43 +0000817int SkCanvas::save(SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000818 this->willSave(flags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000819 // call shared impl
820 return this->internalSave(flags);
821}
822
reed@android.com8a1c16f2008-12-17 15:59:43 +0000823static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +0000824#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000825 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +0000826#else
827 return true;
828#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000829}
830
junov@chromium.orga907ac32012-02-24 21:54:07 +0000831bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000832 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000833 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000834 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000835 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000836 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000837 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000838
839 if (imageFilter) {
840 imageFilter->filterBounds(clipBounds, *fMCRec->fMatrix, &clipBounds);
841 // Filters may grow the bounds beyond the device bounds.
842 op = SkRegion::kReplace_Op;
843 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000844 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000845 if (NULL != bounds) {
846 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000847
reed@android.com8a1c16f2008-12-17 15:59:43 +0000848 this->getTotalMatrix().mapRect(&r, *bounds);
849 r.roundOut(&ir);
850 // early exit if the layer's bounds are clipped out
851 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000852 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000853 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000854 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000855 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000856 }
857 } else { // no user bounds, so just use the clip
858 ir = clipBounds;
859 }
860
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000861 if (bounds_affects_clip(flags)) {
862 fClipStack.clipDevRect(ir, op);
863 // early exit if the clip is now empty
864 if (!fMCRec->fRasterClip->op(ir, op)) {
865 return false;
866 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000867 }
868
869 if (intersection) {
870 *intersection = ir;
871 }
872 return true;
873}
874
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000875SkCanvas::SaveLayerStrategy SkCanvas::willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) {
876
877 // Do nothing. Subclasses may do something.
878 return kFullLayer_SaveLayerStrategy;
879}
880
junov@chromium.orga907ac32012-02-24 21:54:07 +0000881int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
882 SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000883 // Overriding classes may return false to signal that we don't need to create a layer.
884 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
885 return this->internalSaveLayer(bounds, paint, flags, false, strategy);
reed@google.com8926b162012-03-23 15:36:36 +0000886}
887
reed@google.com76f10a32014-02-05 15:32:21 +0000888static SkBaseDevice* createCompatibleDevice(SkCanvas* canvas,
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000889 const SkImageInfo& info) {
reed@google.com76f10a32014-02-05 15:32:21 +0000890 SkBaseDevice* device = canvas->getDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000891 return device ? device->createCompatibleDevice(info) : NULL;
reed@google.com76f10a32014-02-05 15:32:21 +0000892}
893
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000894int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
895 bool justForImageFilter, SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +0000896#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
897 flags = (SaveFlags)(flags | kClipToLayer_SaveFlag);
898#endif
899
junov@chromium.orga907ac32012-02-24 21:54:07 +0000900 // do this before we create the layer. We don't call the public save() since
901 // that would invoke a possibly overridden virtual
902 int count = this->internalSave(flags);
903
904 fDeviceCMDirty = true;
905
906 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000907 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000908 return count;
909 }
910
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000911 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
912 // the clipRectBounds() call above?
913 if (kNoLayer_SaveLayerStrategy == strategy) {
914 return count;
915 }
916
reed@google.comb55deeb2012-01-06 14:43:09 +0000917 // Kill the imagefilter if our device doesn't allow it
918 SkLazyPaint lazyP;
919 if (paint && paint->getImageFilter()) {
920 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000921 if (justForImageFilter) {
922 // early exit if the layer was just for the imageFilter
923 return count;
924 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000925 SkPaint* p = lazyP.set(*paint);
926 p->setImageFilter(NULL);
927 paint = p;
928 }
929 }
930
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000931 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
932 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
933 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000934
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000935 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000936 if (paint && paint->getImageFilter()) {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000937 device = createCompatibleDevice(this, info);
reed@google.com76dd2772012-01-05 21:15:07 +0000938 } else {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000939 device = this->createLayerDevice(info);
reed@google.com76dd2772012-01-05 21:15:07 +0000940 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000941 if (NULL == device) {
942 SkDebugf("Unable to create device for layer.");
943 return count;
944 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000945
reed@google.com6f8f2922011-03-04 22:27:10 +0000946 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000947 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000948 device->unref();
949
950 layer->fNext = fMCRec->fTopLayer;
951 fMCRec->fLayer = layer;
952 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
953
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000954 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000955 return count;
956}
957
958int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
959 SaveFlags flags) {
960 if (0xFF == alpha) {
961 return this->saveLayer(bounds, NULL, flags);
962 } else {
963 SkPaint tmpPaint;
964 tmpPaint.setAlpha(alpha);
965 return this->saveLayer(bounds, &tmpPaint, flags);
966 }
967}
968
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000969void SkCanvas::willRestore() {
970 // Do nothing. Subclasses may do something.
971}
972
reed@android.com8a1c16f2008-12-17 15:59:43 +0000973void SkCanvas::restore() {
974 // check for underflow
975 if (fMCStack.count() > 1) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000976 this->willRestore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000977 this->internalRestore();
978 }
979}
980
981void SkCanvas::internalRestore() {
982 SkASSERT(fMCStack.count() != 0);
983
984 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +0000985 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000986
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000987 if (SkCanvas::kClip_SaveFlag & fMCRec->fFlags) {
988 fClipStack.restore();
989 }
990
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000991 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000992 DeviceCM* layer = fMCRec->fLayer; // may be null
993 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
994 fMCRec->fLayer = NULL;
995
996 // now do the normal restore()
997 fMCRec->~MCRec(); // balanced in save()
998 fMCStack.pop_back();
999 fMCRec = (MCRec*)fMCStack.back();
1000
1001 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1002 since if we're being recorded, we don't want to record this (the
1003 recorder will have already recorded the restore).
1004 */
1005 if (NULL != layer) {
1006 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001007 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001008 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
1009 layer->fPaint);
1010 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001011 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +00001012
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001013 SkASSERT(fSaveLayerCount > 0);
1014 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001015 }
1016 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001017 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001018}
1019
1020int SkCanvas::getSaveCount() const {
1021 return fMCStack.count();
1022}
1023
1024void SkCanvas::restoreToCount(int count) {
1025 // sanity check
1026 if (count < 1) {
1027 count = 1;
1028 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001029
reed@google.comb9d1c6a2011-11-28 16:09:24 +00001030 int n = this->getSaveCount() - count;
1031 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001032 this->restore();
1033 }
1034}
1035
reed@google.com7c202932011-12-14 18:48:05 +00001036bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001037 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +00001038}
1039
reed@google.com76f10a32014-02-05 15:32:21 +00001040SkSurface* SkCanvas::newSurface(const SkImageInfo& info) {
1041 return this->onNewSurface(info);
1042}
1043
1044SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info) {
1045 SkBaseDevice* dev = this->getDevice();
1046 return dev ? dev->newSurface(info) : NULL;
1047}
1048
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001049SkImageInfo SkCanvas::imageInfo() const {
1050 SkBaseDevice* dev = this->getDevice();
1051 if (dev) {
1052 return dev->imageInfo();
1053 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001054 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001055 }
1056}
1057
1058const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
1059 return this->onPeekPixels(info, rowBytes);
1060}
1061
1062const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
1063 SkBaseDevice* dev = this->getDevice();
1064 return dev ? dev->peekPixels(info, rowBytes) : NULL;
1065}
1066
reed@google.com9c135db2014-03-12 18:28:35 +00001067void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
1068 return this->onAccessTopLayerPixels(info, rowBytes);
1069}
1070
1071void* SkCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
1072 SkBaseDevice* dev = this->getTopDevice();
1073 return dev ? dev->accessPixels(info, rowBytes) : NULL;
1074}
1075
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001076SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1077 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1078 if (NULL == fAddr) {
1079 fInfo = canvas->imageInfo();
1080 if (kUnknown_SkColorType == fInfo.colorType() ||
1081 !fBitmap.allocPixels(fInfo))
1082 {
1083 return; // failure, fAddr is NULL
1084 }
1085 fBitmap.lockPixels();
1086 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1087 return; // failure, fAddr is NULL
1088 }
1089 fAddr = fBitmap.getPixels();
1090 fRowBytes = fBitmap.rowBytes();
1091 }
1092 SkASSERT(fAddr); // success
1093}
1094
1095bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1096 if (fAddr) {
1097 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes,
1098 NULL, NULL);
1099 } else {
1100 bitmap->reset();
1101 return false;
1102 }
1103}
1104
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +00001105void SkCanvas::onPushCull(const SkRect& cullRect) {
1106 // do nothing. Subclasses may do something
1107}
1108
1109void SkCanvas::onPopCull() {
1110 // do nothing. Subclasses may do something
1111}
1112
reed@android.com8a1c16f2008-12-17 15:59:43 +00001113/////////////////////////////////////////////////////////////////////////////
1114
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001115void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001116 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001117 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001118 return;
1119 }
1120
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001121 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001122 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001123 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001124 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001125
1126 SkDEBUGCODE(bitmap.validate();)
1127 CHECK_LOCKCOUNT_BALANCE(bitmap);
1128
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001129 SkRect storage;
1130 const SkRect* bounds = NULL;
1131 if (paint && paint->canComputeFastBounds()) {
1132 bitmap.getBounds(&storage);
1133 matrix.mapRect(&storage);
1134 bounds = &paint->computeFastBounds(storage, &storage);
1135 }
1136
1137 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001138
1139 while (iter.next()) {
1140 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1141 }
1142
1143 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001144}
1145
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001146void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001147 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001148 SkPaint tmp;
1149 if (NULL == paint) {
1150 tmp.setDither(true);
1151 paint = &tmp;
1152 }
reed@google.com4b226022011-01-11 18:32:13 +00001153
reed@google.com8926b162012-03-23 15:36:36 +00001154 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001155 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001156 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001157 paint = &looper.paint();
1158 SkImageFilter* filter = paint->getImageFilter();
1159 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001160 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001161 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001162 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001163 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001164 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001165 SkMatrix matrix = *iter.fMatrix;
1166 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001167 if (filter->filterImage(&proxy, src, matrix, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001168 SkPaint tmpUnfiltered(*paint);
1169 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001170 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1171 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001172 }
1173 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001174 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001175 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001176 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001177 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001178}
1179
reed@google.com8926b162012-03-23 15:36:36 +00001180void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1181 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001182 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001183 return;
1184 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001185 SkDEBUGCODE(bitmap.validate();)
1186 CHECK_LOCKCOUNT_BALANCE(bitmap);
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001187
reed@google.com8926b162012-03-23 15:36:36 +00001188 SkPaint tmp;
1189 if (NULL == paint) {
1190 paint = &tmp;
1191 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001192
reed@google.com8926b162012-03-23 15:36:36 +00001193 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001194
reed@google.com8926b162012-03-23 15:36:36 +00001195 while (iter.next()) {
1196 paint = &looper.paint();
1197 SkImageFilter* filter = paint->getImageFilter();
1198 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1199 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001200 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001201 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001202 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001203 SkMatrix matrix = *iter.fMatrix;
1204 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001205 if (filter->filterImage(&proxy, bitmap, matrix, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001206 SkPaint tmpUnfiltered(*paint);
1207 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001208 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001209 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001210 }
1211 } else {
1212 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1213 }
1214 }
1215 LOOPER_END
1216}
1217
reed@android.com8a1c16f2008-12-17 15:59:43 +00001218/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001219void SkCanvas::didTranslate(SkScalar, SkScalar) {
1220 // Do nothing. Subclasses may do something.
1221}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001222
1223bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
1224 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001225 fCachedLocalClipBoundsDirty = true;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001226 bool res = fMCRec->fMatrix->preTranslate(dx, dy);
1227
1228 this->didTranslate(dx, dy);
1229 return res;
1230}
1231
1232void SkCanvas::didScale(SkScalar, SkScalar) {
1233 // Do nothing. Subclasses may do something.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001234}
1235
1236bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
1237 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001238 fCachedLocalClipBoundsDirty = true;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001239 bool res = fMCRec->fMatrix->preScale(sx, sy);
1240
1241 this->didScale(sx, sy);
1242 return res;
1243}
1244
1245void SkCanvas::didRotate(SkScalar) {
1246 // Do nothing. Subclasses may do something.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001247}
1248
1249bool SkCanvas::rotate(SkScalar degrees) {
1250 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001251 fCachedLocalClipBoundsDirty = true;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001252 bool res = fMCRec->fMatrix->preRotate(degrees);
1253
1254 this->didRotate(degrees);
1255 return res;
1256}
1257
1258void SkCanvas::didSkew(SkScalar, SkScalar) {
1259 // Do nothing. Subclasses may do something.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001260}
1261
1262bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
1263 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001264 fCachedLocalClipBoundsDirty = true;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001265 bool res = fMCRec->fMatrix->preSkew(sx, sy);
1266
1267 this->didSkew(sx, sy);
1268 return res;
1269}
1270
1271void SkCanvas::didConcat(const SkMatrix&) {
1272 // Do nothing. Subclasses may do something.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001273}
1274
1275bool SkCanvas::concat(const SkMatrix& matrix) {
1276 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001277 fCachedLocalClipBoundsDirty = true;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001278 bool res = fMCRec->fMatrix->preConcat(matrix);
1279
1280 this->didConcat(matrix);
1281 return res;
1282}
1283
1284void SkCanvas::didSetMatrix(const SkMatrix&) {
1285 // Do nothing. Subclasses may do something.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001286}
1287
1288void SkCanvas::setMatrix(const SkMatrix& matrix) {
1289 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001290 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001291 *fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001292 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001293}
1294
reed@android.com8a1c16f2008-12-17 15:59:43 +00001295void SkCanvas::resetMatrix() {
1296 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001297
reed@android.com8a1c16f2008-12-17 15:59:43 +00001298 matrix.reset();
1299 this->setMatrix(matrix);
1300}
1301
1302//////////////////////////////////////////////////////////////////////////////
1303
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001304void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001305 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1306 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001307}
1308
1309void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001310#ifdef SK_ENABLE_CLIP_QUICKREJECT
1311 if (SkRegion::kIntersect_Op == op) {
1312 if (fMCRec->fRasterClip->isEmpty()) {
1313 return false;
1314 }
1315
reed@google.com3b3e8952012-08-16 20:53:31 +00001316 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001317 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001318 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001319
1320 fClipStack.clipEmpty();
1321 return fMCRec->fRasterClip->setEmpty();
1322 }
1323 }
1324#endif
1325
reed@google.com5c3d1472011-02-22 19:12:23 +00001326 AutoValidateClip avc(this);
1327
reed@android.com8a1c16f2008-12-17 15:59:43 +00001328 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001329 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001330 if (!fAllowSoftClip) {
1331 edgeStyle = kHard_ClipEdgeStyle;
1332 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001333
1334 if (fMCRec->fMatrix->rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001335 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001336 // the matrix. This means we don't have to a) make a path, and b) tell
1337 // the region code to scan-convert the path, only to discover that it
1338 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001339 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001340
1341 fMCRec->fMatrix->mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001342 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
1343 fMCRec->fRasterClip->op(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001344 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001345 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001346 // and clip against that, since it can handle any matrix. However, to
1347 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1348 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001349 SkPath path;
1350
1351 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001352 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001353 }
1354}
1355
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001356static void clip_path_helper(const SkCanvas* canvas, SkRasterClip* currClip,
1357 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001358 // base is used to limit the size (and therefore memory allocation) of the
1359 // region that results from scan converting devPath.
1360 SkRegion base;
1361
reed@google.com819c9212011-02-23 18:56:55 +00001362 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001363 // since we are intersect, we can do better (tighter) with currRgn's
1364 // bounds, than just using the device. However, if currRgn is complex,
1365 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001366 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001367 // FIXME: we should also be able to do this when currClip->isBW(),
1368 // but relaxing the test above triggers GM asserts in
1369 // SkRgnBuilder::blitH(). We need to investigate what's going on.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001370 currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001371 } else {
reed@google.com00177082011-10-12 14:34:30 +00001372 base.setRect(currClip->getBounds());
1373 SkRasterClip clip;
1374 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001375 currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001376 }
reed@google.com819c9212011-02-23 18:56:55 +00001377 } else {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001378 const SkBaseDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001379 if (!device) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001380 currClip->setEmpty();
1381 return;
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001382 }
1383
junov@chromium.orga907ac32012-02-24 21:54:07 +00001384 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001385
1386 if (SkRegion::kReplace_Op == op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001387 currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001388 } else {
reed@google.com00177082011-10-12 14:34:30 +00001389 SkRasterClip clip;
1390 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001391 currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001392 }
1393 }
1394}
1395
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001396void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001397 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001398 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001399 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1400 } else {
1401 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001402 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001403}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001404
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001405void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001406 SkRRect transformedRRect;
1407 if (rrect.transform(*fMCRec->fMatrix, &transformedRRect)) {
1408 AutoValidateClip avc(this);
1409
1410 fDeviceCMDirty = true;
1411 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001412 if (!fAllowSoftClip) {
1413 edgeStyle = kHard_ClipEdgeStyle;
1414 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001415
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001416 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001417
1418 SkPath devPath;
1419 devPath.addRRect(transformedRRect);
1420
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001421 clip_path_helper(this, fMCRec->fRasterClip, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
1422 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001423 }
1424
1425 SkPath path;
1426 path.addRRect(rrect);
1427 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001428 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001429}
1430
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001431void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001432 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1433 SkRect r;
1434 if (!path.isInverseFillType() && path.isRect(&r)) {
1435 this->onClipRect(r, op, edgeStyle);
1436 } else {
1437 this->onClipPath(path, op, edgeStyle);
1438 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001439}
1440
1441void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001442#ifdef SK_ENABLE_CLIP_QUICKREJECT
1443 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1444 if (fMCRec->fRasterClip->isEmpty()) {
1445 return false;
1446 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001447
reed@google.com3b3e8952012-08-16 20:53:31 +00001448 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001449 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001450 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001451
reed@google.comda17f752012-08-16 18:27:05 +00001452 fClipStack.clipEmpty();
1453 return fMCRec->fRasterClip->setEmpty();
1454 }
1455 }
1456#endif
1457
reed@google.com5c3d1472011-02-22 19:12:23 +00001458 AutoValidateClip avc(this);
1459
reed@android.com8a1c16f2008-12-17 15:59:43 +00001460 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001461 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001462 if (!fAllowSoftClip) {
1463 edgeStyle = kHard_ClipEdgeStyle;
1464 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001465
1466 SkPath devPath;
1467 path.transform(*fMCRec->fMatrix, &devPath);
1468
reed@google.comfe701122011-11-08 19:41:23 +00001469 // Check if the transfomation, or the original path itself
1470 // made us empty. Note this can also happen if we contained NaN
1471 // values. computing the bounds detects this, and will set our
1472 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1473 if (devPath.getBounds().isEmpty()) {
1474 // resetting the path will remove any NaN or other wanky values
1475 // that might upset our scan converter.
1476 devPath.reset();
1477 }
1478
reed@google.com5c3d1472011-02-22 19:12:23 +00001479 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001480 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001481
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001482 if (fAllowSimplifyClip) {
1483 devPath.reset();
1484 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1485 const SkClipStack* clipStack = getClipStack();
1486 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1487 const SkClipStack::Element* element;
1488 while ((element = iter.next())) {
1489 SkClipStack::Element::Type type = element->getType();
1490 if (type == SkClipStack::Element::kEmpty_Type) {
1491 continue;
1492 }
1493 SkPath operand;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001494 element->asPath(&operand);
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001495 SkRegion::Op elementOp = element->getOp();
1496 if (elementOp == SkRegion::kReplace_Op) {
1497 devPath = operand;
1498 } else {
1499 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1500 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001501 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1502 // perhaps we need an API change to avoid this sort of mixed-signals about
1503 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001504 if (element->isAA()) {
1505 edgeStyle = kSoft_ClipEdgeStyle;
1506 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001507 }
1508 op = SkRegion::kReplace_Op;
1509 }
1510
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001511 clip_path_helper(this, fMCRec->fRasterClip, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001512}
1513
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001514void SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001515 bool inverseFilled) {
1516 // This is for updating the clip conservatively using only bounds
1517 // information.
1518 // Contract:
1519 // The current clip must contain the true clip. The true
1520 // clip is the clip that would have normally been computed
1521 // by calls to clipPath and clipRRect
1522 // Objective:
1523 // Keep the current clip as small as possible without
1524 // breaking the contract, using only clip bounding rectangles
1525 // (for performance).
1526
1527 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1528 // don't have to worry about getting caught in a loop. Thus anywhere
1529 // we call a virtual method, we explicitly prefix it with
1530 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001531
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001532 if (inverseFilled) {
1533 switch (op) {
1534 case SkRegion::kIntersect_Op:
1535 case SkRegion::kDifference_Op:
1536 // These ops can only shrink the current clip. So leaving
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001537 // the clip unchanged conservatively respects the contract.
1538 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001539 case SkRegion::kUnion_Op:
1540 case SkRegion::kReplace_Op:
1541 case SkRegion::kReverseDifference_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001542 case SkRegion::kXOR_Op: {
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001543 // These ops can grow the current clip up to the extents of
1544 // the input clip, which is inverse filled, so we just set
1545 // the current clip to the device bounds.
1546 SkRect deviceBounds;
1547 SkIRect deviceIBounds;
1548 this->getDevice()->getGlobalBounds(&deviceIBounds);
reed@google.com44699382013-10-31 17:28:30 +00001549 deviceBounds = SkRect::Make(deviceIBounds);
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001550 this->SkCanvas::save(SkCanvas::kMatrix_SaveFlag);
1551 // set the clip in device space
1552 this->SkCanvas::setMatrix(SkMatrix::I());
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001553 this->SkCanvas::onClipRect(deviceBounds, SkRegion::kReplace_Op,
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001554 kHard_ClipEdgeStyle);
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001555 this->SkCanvas::restore(); //pop the matrix, but keep the clip
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001556 break;
1557 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001558 default:
1559 SkASSERT(0); // unhandled op?
1560 }
1561 } else {
1562 // Not inverse filled
1563 switch (op) {
1564 case SkRegion::kIntersect_Op:
1565 case SkRegion::kUnion_Op:
1566 case SkRegion::kReplace_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001567 this->SkCanvas::onClipRect(bounds, op, kHard_ClipEdgeStyle);
1568 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001569 case SkRegion::kDifference_Op:
1570 // Difference can only shrink the current clip.
1571 // Leaving clip unchanged conservatively fullfills the contract.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001572 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001573 case SkRegion::kReverseDifference_Op:
1574 // To reverse, we swap in the bounds with a replace op.
1575 // As with difference, leave it unchanged.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001576 this->SkCanvas::onClipRect(bounds, SkRegion::kReplace_Op, kHard_ClipEdgeStyle);
1577 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001578 case SkRegion::kXOR_Op:
1579 // Be conservative, based on (A XOR B) always included in (A union B),
1580 // which is always included in (bounds(A) union bounds(B))
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001581 this->SkCanvas::onClipRect(bounds, SkRegion::kUnion_Op, kHard_ClipEdgeStyle);
1582 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001583 default:
1584 SkASSERT(0); // unhandled op?
1585 }
1586 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001587}
1588
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001589void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001590 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001591}
1592
1593void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001594 AutoValidateClip avc(this);
1595
reed@android.com8a1c16f2008-12-17 15:59:43 +00001596 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001597 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001598
reed@google.com5c3d1472011-02-22 19:12:23 +00001599 // todo: signal fClipStack that we have a region, and therefore (I guess)
1600 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001601 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001602
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001603 fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001604}
1605
reed@google.com819c9212011-02-23 18:56:55 +00001606#ifdef SK_DEBUG
1607void SkCanvas::validateClip() const {
1608 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001609 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001610 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001611 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001612 return;
1613 }
1614
reed@google.com819c9212011-02-23 18:56:55 +00001615 SkIRect ir;
1616 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001617 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001618
robertphillips@google.com80214e22012-07-20 15:33:18 +00001619 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001620 const SkClipStack::Element* element;
1621 while ((element = iter.next()) != NULL) {
1622 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001623 case SkClipStack::Element::kRect_Type:
1624 element->getRect().round(&ir);
1625 tmpClip.op(ir, element->getOp());
1626 break;
1627 case SkClipStack::Element::kEmpty_Type:
1628 tmpClip.setEmpty();
1629 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001630 default: {
1631 SkPath path;
1632 element->asPath(&path);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001633 clip_path_helper(this, &tmpClip, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001634 break;
1635 }
reed@google.com819c9212011-02-23 18:56:55 +00001636 }
1637 }
reed@google.com819c9212011-02-23 18:56:55 +00001638}
1639#endif
1640
reed@google.com90c07ea2012-04-13 13:50:27 +00001641void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001642 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001643 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001644
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001645 static const SkRect kEmpty = { 0, 0, 0, 0 };
1646 while ((element = iter.next()) != NULL) {
1647 switch (element->getType()) {
1648 case SkClipStack::Element::kPath_Type:
1649 visitor->clipPath(element->getPath(), element->getOp(), element->isAA());
1650 break;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001651 case SkClipStack::Element::kRRect_Type:
1652 visitor->clipRRect(element->getRRect(), element->getOp(), element->isAA());
1653 break;
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001654 case SkClipStack::Element::kRect_Type:
1655 visitor->clipRect(element->getRect(), element->getOp(), element->isAA());
1656 break;
1657 case SkClipStack::Element::kEmpty_Type:
1658 visitor->clipRect(kEmpty, SkRegion::kIntersect_Op, false);
1659 break;
reed@google.com90c07ea2012-04-13 13:50:27 +00001660 }
1661 }
1662}
1663
reed@google.com5c3d1472011-02-22 19:12:23 +00001664///////////////////////////////////////////////////////////////////////////////
1665
reed@google.com754de5f2014-02-24 19:38:20 +00001666bool SkCanvas::isClipEmpty() const {
1667 return fMCRec->fRasterClip->isEmpty();
1668}
1669
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001670bool SkCanvas::isClipRect() const {
1671 return fMCRec->fRasterClip->isRect();
1672}
1673
reed@google.com3b3e8952012-08-16 20:53:31 +00001674bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001675
reed@google.com16078632011-12-06 18:56:37 +00001676 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001677 return true;
1678
reed@google.com00177082011-10-12 14:34:30 +00001679 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001680 return true;
1681 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001682
tomhudson@google.com8d430182011-06-06 19:11:19 +00001683 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001684 SkRect dst;
1685 fMCRec->fMatrix->mapRect(&dst, rect);
1686 SkIRect idst;
1687 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001688 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001689 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001690 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001691
reed@android.coma380ae42009-07-21 01:17:02 +00001692 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001693 // TODO: should we use | instead, or compare all 4 at once?
1694 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001695 return true;
1696 }
reed@google.comc0784db2013-12-13 21:16:12 +00001697 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001698 return true;
1699 }
1700 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001701 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001702}
1703
reed@google.com3b3e8952012-08-16 20:53:31 +00001704bool SkCanvas::quickReject(const SkPath& path) const {
1705 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001706}
1707
reed@google.com3b3e8952012-08-16 20:53:31 +00001708bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001709 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001710 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001711 return false;
1712 }
1713
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001714 SkMatrix inverse;
1715 // if we can't invert the CTM, we can't return local clip bounds
1716 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001717 if (bounds) {
1718 bounds->setEmpty();
1719 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001720 return false;
1721 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001722
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001723 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001724 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001725 // adjust it outwards in case we are antialiasing
1726 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001727
reed@google.com8f4d2302013-12-17 16:44:46 +00001728 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1729 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001730 inverse.mapRect(bounds, r);
1731 }
1732 return true;
1733}
1734
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001735bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001736 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001737 if (clip.isEmpty()) {
1738 if (bounds) {
1739 bounds->setEmpty();
1740 }
1741 return false;
1742 }
1743
1744 if (NULL != bounds) {
1745 *bounds = clip.getBounds();
1746 }
1747 return true;
1748}
1749
reed@android.com8a1c16f2008-12-17 15:59:43 +00001750const SkMatrix& SkCanvas::getTotalMatrix() const {
1751 return *fMCRec->fMatrix;
1752}
1753
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001754#ifdef SK_SUPPORT_LEGACY_GETCLIPTYPE
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001755SkCanvas::ClipType SkCanvas::getClipType() const {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001756 if (fMCRec->fRasterClip->isEmpty()) {
1757 return kEmpty_ClipType;
1758 }
1759 if (fMCRec->fRasterClip->isRect()) {
1760 return kRect_ClipType;
1761 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001762 return kComplex_ClipType;
1763}
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001764#endif
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001765
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001766#ifdef SK_SUPPORT_LEGACY_GETTOTALCLIP
reed@android.com8a1c16f2008-12-17 15:59:43 +00001767const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001768 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001769}
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001770#endif
1771
1772const SkRegion& SkCanvas::internal_private_getTotalClip() const {
1773 return fMCRec->fRasterClip->forceGetBW();
1774}
1775
1776void SkCanvas::internal_private_getTotalClipAsPath(SkPath* path) const {
1777 path->reset();
1778
1779 const SkRegion& rgn = fMCRec->fRasterClip->forceGetBW();
1780 if (rgn.isEmpty()) {
1781 return;
1782 }
1783 (void)rgn.getBoundaryPath(path);
1784}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001785
reed@google.com9c135db2014-03-12 18:28:35 +00001786GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1787 SkBaseDevice* dev = this->getTopDevice();
1788 return dev ? dev->accessRenderTarget() : NULL;
1789}
1790
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001791SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001792 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001793 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001794}
1795
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001796GrContext* SkCanvas::getGrContext() {
1797#if SK_SUPPORT_GPU
1798 SkBaseDevice* device = this->getTopDevice();
1799 if (NULL != device) {
1800 GrRenderTarget* renderTarget = device->accessRenderTarget();
1801 if (NULL != renderTarget) {
1802 return renderTarget->getContext();
1803 }
1804 }
1805#endif
1806
1807 return NULL;
1808
1809}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001810
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001811void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1812 const SkPaint& paint) {
1813 if (outer.isEmpty()) {
1814 return;
1815 }
1816 if (inner.isEmpty()) {
1817 this->drawRRect(outer, paint);
1818 return;
1819 }
1820
1821 // We don't have this method (yet), but technically this is what we should
1822 // be able to assert...
1823 // SkASSERT(outer.contains(inner));
1824 //
1825 // For now at least check for containment of bounds
1826 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1827
1828 this->onDrawDRRect(outer, inner, paint);
1829}
1830
reed@android.com8a1c16f2008-12-17 15:59:43 +00001831//////////////////////////////////////////////////////////////////////////////
1832// These are the virtual drawing methods
1833//////////////////////////////////////////////////////////////////////////////
1834
reed@google.com2a981812011-04-14 18:59:28 +00001835void SkCanvas::clear(SkColor color) {
1836 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001837 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001838 while (iter.next()) {
1839 iter.fDevice->clear(color);
1840 }
1841}
1842
reed@android.com8a1c16f2008-12-17 15:59:43 +00001843void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001844 this->internalDrawPaint(paint);
1845}
1846
1847void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001848 CHECK_SHADER_NOSETCONTEXT(paint);
1849
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001850 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001851
1852 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001853 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001854 }
1855
reed@google.com4e2b3d32011-04-07 14:18:59 +00001856 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001857}
1858
1859void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1860 const SkPaint& paint) {
1861 if ((long)count <= 0) {
1862 return;
1863 }
1864
reed@google.comea033602012-12-14 13:13:55 +00001865 CHECK_SHADER_NOSETCONTEXT(paint);
1866
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001867 SkRect r, storage;
1868 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001869 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001870 // special-case 2 points (common for drawing a single line)
1871 if (2 == count) {
1872 r.set(pts[0], pts[1]);
1873 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001874 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001875 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001876 bounds = &paint.computeFastStrokeBounds(r, &storage);
1877 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001878 return;
1879 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001880 }
reed@google.coma584aed2012-05-16 14:06:02 +00001881
reed@android.com8a1c16f2008-12-17 15:59:43 +00001882 SkASSERT(pts != NULL);
1883
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001884 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001885
reed@android.com8a1c16f2008-12-17 15:59:43 +00001886 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001887 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001888 }
reed@google.com4b226022011-01-11 18:32:13 +00001889
reed@google.com4e2b3d32011-04-07 14:18:59 +00001890 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001891}
1892
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001893void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001894 CHECK_SHADER_NOSETCONTEXT(paint);
1895
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001896 SkRect storage;
1897 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001898 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001899 bounds = &paint.computeFastBounds(r, &storage);
1900 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001901 return;
1902 }
1903 }
reed@google.com4b226022011-01-11 18:32:13 +00001904
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001905 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001906
1907 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001908 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001909 }
1910
reed@google.com4e2b3d32011-04-07 14:18:59 +00001911 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001912}
1913
reed@google.com4ed0fb72012-12-12 20:48:18 +00001914void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001915 CHECK_SHADER_NOSETCONTEXT(paint);
1916
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001917 SkRect storage;
1918 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001919 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001920 bounds = &paint.computeFastBounds(oval, &storage);
1921 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001922 return;
1923 }
1924 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001925
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001926 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001927
1928 while (iter.next()) {
1929 iter.fDevice->drawOval(iter, oval, looper.paint());
1930 }
1931
1932 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001933}
1934
1935void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001936 CHECK_SHADER_NOSETCONTEXT(paint);
1937
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001938 SkRect storage;
1939 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001940 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001941 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1942 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001943 return;
1944 }
1945 }
1946
1947 if (rrect.isRect()) {
1948 // call the non-virtual version
1949 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001950 return;
1951 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001952 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001953 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1954 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001955 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001956
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001957 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001958
1959 while (iter.next()) {
1960 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1961 }
1962
1963 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001964}
1965
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001966void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1967 const SkPaint& paint) {
1968 CHECK_SHADER_NOSETCONTEXT(paint);
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001969
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001970 SkRect storage;
1971 const SkRect* bounds = NULL;
1972 if (paint.canComputeFastBounds()) {
1973 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1974 if (this->quickReject(*bounds)) {
1975 return;
1976 }
1977 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001978
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001979 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001980
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001981 while (iter.next()) {
1982 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1983 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001984
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001985 LOOPER_END
1986}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001987
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001988void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00001989 CHECK_SHADER_NOSETCONTEXT(paint);
1990
reed@google.com93645112012-07-26 16:11:47 +00001991 if (!path.isFinite()) {
1992 return;
1993 }
1994
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001995 SkRect storage;
1996 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001997 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001998 const SkRect& pathBounds = path.getBounds();
1999 bounds = &paint.computeFastBounds(pathBounds, &storage);
2000 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002001 return;
2002 }
2003 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002004
2005 const SkRect& r = path.getBounds();
2006 if (r.width() <= 0 && r.height() <= 0) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002007 if (path.isInverseFillType()) {
2008 this->internalDrawPaint(paint);
2009 }
2010 return;
2011 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002012
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002013 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002014
2015 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002016 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002017 }
2018
reed@google.com4e2b3d32011-04-07 14:18:59 +00002019 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002020}
2021
2022void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
2023 const SkPaint* paint) {
2024 SkDEBUGCODE(bitmap.validate();)
2025
reed@google.com3d608122011-11-21 15:16:16 +00002026 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002027 SkRect bounds = {
2028 x, y,
2029 x + SkIntToScalar(bitmap.width()),
2030 y + SkIntToScalar(bitmap.height())
2031 };
2032 if (paint) {
2033 (void)paint->computeFastBounds(bounds, &bounds);
2034 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002035 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002036 return;
2037 }
2038 }
reed@google.com4b226022011-01-11 18:32:13 +00002039
reed@android.com8a1c16f2008-12-17 15:59:43 +00002040 SkMatrix matrix;
2041 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00002042 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002043}
2044
reed@google.com9987ec32011-09-07 11:57:52 +00002045// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002046void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002047 const SkRect& dst, const SkPaint* paint,
2048 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002049 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002050 return;
2051 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002052
reed@google.comea033602012-12-14 13:13:55 +00002053 CHECK_LOCKCOUNT_BALANCE(bitmap);
2054
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002055 SkRect storage;
2056 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00002057 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002058 if (paint) {
2059 bounds = &paint->computeFastBounds(dst, &storage);
2060 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002061 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002062 return;
2063 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002064 }
reed@google.com3d608122011-11-21 15:16:16 +00002065
reed@google.com33535f32012-09-25 15:37:50 +00002066 SkLazyPaint lazy;
2067 if (NULL == paint) {
2068 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002069 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002070
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002071 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002072
reed@google.com33535f32012-09-25 15:37:50 +00002073 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002074 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00002075 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002076
reed@google.com33535f32012-09-25 15:37:50 +00002077 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002078}
2079
reed@google.com71121732012-09-18 15:14:33 +00002080void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002081 const SkRect& dst, const SkPaint* paint,
2082 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00002083 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002084 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00002085}
2086
reed@android.com8a1c16f2008-12-17 15:59:43 +00002087void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
2088 const SkPaint* paint) {
2089 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00002090 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002091}
2092
reed@google.com9987ec32011-09-07 11:57:52 +00002093void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
2094 const SkIRect& center, const SkRect& dst,
2095 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002096 if (bitmap.drawsNothing()) {
2097 return;
2098 }
reed@google.com3d608122011-11-21 15:16:16 +00002099 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00002100 SkRect storage;
2101 const SkRect* bounds = &dst;
2102 if (paint) {
2103 bounds = &paint->computeFastBounds(dst, &storage);
2104 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002105 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002106 return;
2107 }
2108 }
2109
reed@google.com9987ec32011-09-07 11:57:52 +00002110 const int32_t w = bitmap.width();
2111 const int32_t h = bitmap.height();
2112
2113 SkIRect c = center;
2114 // pin center to the bounds of the bitmap
2115 c.fLeft = SkMax32(0, center.fLeft);
2116 c.fTop = SkMax32(0, center.fTop);
2117 c.fRight = SkPin32(center.fRight, c.fLeft, w);
2118 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
2119
reed@google.com71121732012-09-18 15:14:33 +00002120 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002121 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00002122 };
2123 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002124 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00002125 };
reed@google.com9987ec32011-09-07 11:57:52 +00002126 SkScalar dstX[4] = {
2127 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
2128 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
2129 };
2130 SkScalar dstY[4] = {
2131 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2132 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2133 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002134
reed@google.com9987ec32011-09-07 11:57:52 +00002135 if (dstX[1] > dstX[2]) {
2136 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2137 dstX[2] = dstX[1];
2138 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002139
reed@google.com9987ec32011-09-07 11:57:52 +00002140 if (dstY[1] > dstY[2]) {
2141 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2142 dstY[2] = dstY[1];
2143 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002144
reed@google.com9987ec32011-09-07 11:57:52 +00002145 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002146 SkRect s, d;
2147
reed@google.com9987ec32011-09-07 11:57:52 +00002148 s.fTop = srcY[y];
2149 s.fBottom = srcY[y+1];
2150 d.fTop = dstY[y];
2151 d.fBottom = dstY[y+1];
2152 for (int x = 0; x < 3; x++) {
2153 s.fLeft = srcX[x];
2154 s.fRight = srcX[x+1];
2155 d.fLeft = dstX[x];
2156 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002157 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002158 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002159 }
2160 }
2161}
2162
2163void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
2164 const SkRect& dst, const SkPaint* paint) {
2165 SkDEBUGCODE(bitmap.validate();)
2166
2167 // Need a device entry-point, so gpu can use a mesh
2168 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2169}
2170
reed@google.comf67e4cf2011-03-15 20:56:58 +00002171class SkDeviceFilteredPaint {
2172public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002173 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
2174 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002175 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002176 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002177 newPaint->setFlags(flags.fFlags);
2178 newPaint->setHinting(flags.fHinting);
2179 fPaint = newPaint;
2180 } else {
2181 fPaint = &paint;
2182 }
2183 }
2184
reed@google.comf67e4cf2011-03-15 20:56:58 +00002185 const SkPaint& paint() const { return *fPaint; }
2186
2187private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002188 const SkPaint* fPaint;
2189 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002190};
2191
bungeman@google.com52c748b2011-08-22 21:30:43 +00002192void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2193 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002194 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002195 draw.fDevice->drawRect(draw, r, paint);
2196 } else {
2197 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002198 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002199 draw.fDevice->drawRect(draw, r, p);
2200 }
2201}
2202
2203void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2204 const char text[], size_t byteLength,
2205 SkScalar x, SkScalar y) {
2206 SkASSERT(byteLength == 0 || text != NULL);
2207
2208 // nothing to draw
2209 if (text == NULL || byteLength == 0 ||
2210 draw.fClip->isEmpty() ||
2211 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2212 return;
2213 }
2214
2215 SkScalar width = 0;
2216 SkPoint start;
2217
2218 start.set(0, 0); // to avoid warning
2219 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2220 SkPaint::kStrikeThruText_Flag)) {
2221 width = paint.measureText(text, byteLength);
2222
2223 SkScalar offsetX = 0;
2224 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2225 offsetX = SkScalarHalf(width);
2226 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2227 offsetX = width;
2228 }
2229 start.set(x - offsetX, y);
2230 }
2231
2232 if (0 == width) {
2233 return;
2234 }
2235
2236 uint32_t flags = paint.getFlags();
2237
2238 if (flags & (SkPaint::kUnderlineText_Flag |
2239 SkPaint::kStrikeThruText_Flag)) {
2240 SkScalar textSize = paint.getTextSize();
2241 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2242 SkRect r;
2243
2244 r.fLeft = start.fX;
2245 r.fRight = start.fX + width;
2246
2247 if (flags & SkPaint::kUnderlineText_Flag) {
2248 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2249 start.fY);
2250 r.fTop = offset;
2251 r.fBottom = offset + height;
2252 DrawRect(draw, paint, r, textSize);
2253 }
2254 if (flags & SkPaint::kStrikeThruText_Flag) {
2255 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2256 start.fY);
2257 r.fTop = offset;
2258 r.fBottom = offset + height;
2259 DrawRect(draw, paint, r, textSize);
2260 }
2261 }
2262}
2263
reed@android.com8a1c16f2008-12-17 15:59:43 +00002264void SkCanvas::drawText(const void* text, size_t byteLength,
2265 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002266 CHECK_SHADER_NOSETCONTEXT(paint);
2267
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002268 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002269
2270 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002271 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002272 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002273 DrawTextDecorations(iter, dfp.paint(),
2274 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002275 }
2276
reed@google.com4e2b3d32011-04-07 14:18:59 +00002277 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002278}
2279
2280void SkCanvas::drawPosText(const void* text, size_t byteLength,
2281 const SkPoint pos[], const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002282 CHECK_SHADER_NOSETCONTEXT(paint);
2283
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002284 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002285
reed@android.com8a1c16f2008-12-17 15:59:43 +00002286 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002287 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002288 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002289 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002290 }
reed@google.com4b226022011-01-11 18:32:13 +00002291
reed@google.com4e2b3d32011-04-07 14:18:59 +00002292 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002293}
2294
2295void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
2296 const SkScalar xpos[], SkScalar constY,
2297 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002298 CHECK_SHADER_NOSETCONTEXT(paint);
2299
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002300 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002301
reed@android.com8a1c16f2008-12-17 15:59:43 +00002302 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002303 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002304 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002305 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002306 }
reed@google.com4b226022011-01-11 18:32:13 +00002307
reed@google.com4e2b3d32011-04-07 14:18:59 +00002308 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002309}
2310
2311void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
2312 const SkPath& path, const SkMatrix* matrix,
2313 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002314 CHECK_SHADER_NOSETCONTEXT(paint);
2315
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002316 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002317
2318 while (iter.next()) {
2319 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002320 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002321 }
2322
reed@google.com4e2b3d32011-04-07 14:18:59 +00002323 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002324}
2325
2326void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2327 const SkPoint verts[], const SkPoint texs[],
2328 const SkColor colors[], SkXfermode* xmode,
2329 const uint16_t indices[], int indexCount,
2330 const SkPaint& paint) {
reed@google.comea033602012-12-14 13:13:55 +00002331 CHECK_SHADER_NOSETCONTEXT(paint);
2332
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002333 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002334
reed@android.com8a1c16f2008-12-17 15:59:43 +00002335 while (iter.next()) {
2336 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002337 colors, xmode, indices, indexCount,
2338 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002339 }
reed@google.com4b226022011-01-11 18:32:13 +00002340
reed@google.com4e2b3d32011-04-07 14:18:59 +00002341 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002342}
2343
2344//////////////////////////////////////////////////////////////////////////////
2345// These methods are NOT virtual, and therefore must call back into virtual
2346// methods, rather than actually drawing themselves.
2347//////////////////////////////////////////////////////////////////////////////
2348
2349void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002350 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002351 SkPaint paint;
2352
2353 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002354 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002355 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002356 }
2357 this->drawPaint(paint);
2358}
2359
reed@android.com845fdac2009-06-23 03:01:32 +00002360void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002361 SkPaint paint;
2362
2363 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002364 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002365 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002366 }
2367 this->drawPaint(paint);
2368}
2369
2370void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2371 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002372
reed@android.com8a1c16f2008-12-17 15:59:43 +00002373 pt.set(x, y);
2374 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2375}
2376
2377void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2378 SkPoint pt;
2379 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002380
reed@android.com8a1c16f2008-12-17 15:59:43 +00002381 pt.set(x, y);
2382 paint.setColor(color);
2383 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2384}
2385
2386void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2387 const SkPaint& paint) {
2388 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002389
reed@android.com8a1c16f2008-12-17 15:59:43 +00002390 pts[0].set(x0, y0);
2391 pts[1].set(x1, y1);
2392 this->drawPoints(kLines_PointMode, 2, pts, paint);
2393}
2394
2395void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2396 SkScalar right, SkScalar bottom,
2397 const SkPaint& paint) {
2398 SkRect r;
2399
2400 r.set(left, top, right, bottom);
2401 this->drawRect(r, paint);
2402}
2403
2404void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2405 const SkPaint& paint) {
2406 if (radius < 0) {
2407 radius = 0;
2408 }
2409
2410 SkRect r;
2411 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002412 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002413}
2414
2415void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2416 const SkPaint& paint) {
2417 if (rx > 0 && ry > 0) {
2418 if (paint.canComputeFastBounds()) {
2419 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002420 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002421 return;
2422 }
2423 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002424 SkRRect rrect;
2425 rrect.setRectXY(r, rx, ry);
2426 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002427 } else {
2428 this->drawRect(r, paint);
2429 }
2430}
2431
reed@android.com8a1c16f2008-12-17 15:59:43 +00002432void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2433 SkScalar sweepAngle, bool useCenter,
2434 const SkPaint& paint) {
2435 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2436 this->drawOval(oval, paint);
2437 } else {
2438 SkPath path;
2439 if (useCenter) {
2440 path.moveTo(oval.centerX(), oval.centerY());
2441 }
2442 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2443 if (useCenter) {
2444 path.close();
2445 }
2446 this->drawPath(path, paint);
2447 }
2448}
2449
2450void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2451 const SkPath& path, SkScalar hOffset,
2452 SkScalar vOffset, const SkPaint& paint) {
2453 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002454
reed@android.com8a1c16f2008-12-17 15:59:43 +00002455 matrix.setTranslate(hOffset, vOffset);
2456 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2457}
2458
reed@android.comf76bacf2009-05-13 14:00:33 +00002459///////////////////////////////////////////////////////////////////////////////
2460
reed@android.com8a1c16f2008-12-17 15:59:43 +00002461void SkCanvas::drawPicture(SkPicture& picture) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002462 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002463}
2464
reed@android.com8a1c16f2008-12-17 15:59:43 +00002465///////////////////////////////////////////////////////////////////////////////
2466///////////////////////////////////////////////////////////////////////////////
2467
2468SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002469 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002470
2471 SkASSERT(canvas);
2472
2473 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2474 fDone = !fImpl->next();
2475}
2476
2477SkCanvas::LayerIter::~LayerIter() {
2478 fImpl->~SkDrawIter();
2479}
2480
2481void SkCanvas::LayerIter::next() {
2482 fDone = !fImpl->next();
2483}
2484
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002485SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002486 return fImpl->getDevice();
2487}
2488
2489const SkMatrix& SkCanvas::LayerIter::matrix() const {
2490 return fImpl->getMatrix();
2491}
2492
2493const SkPaint& SkCanvas::LayerIter::paint() const {
2494 const SkPaint* paint = fImpl->getPaint();
2495 if (NULL == paint) {
2496 paint = &fDefaultPaint;
2497 }
2498 return *paint;
2499}
2500
2501const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2502int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2503int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002504
2505///////////////////////////////////////////////////////////////////////////////
2506
2507SkCanvas::ClipVisitor::~ClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002508
2509///////////////////////////////////////////////////////////////////////////////
2510
2511static bool supported_for_raster_canvas(const SkImageInfo& info) {
2512 switch (info.alphaType()) {
2513 case kPremul_SkAlphaType:
2514 case kOpaque_SkAlphaType:
2515 break;
2516 default:
2517 return false;
2518 }
2519
2520 switch (info.colorType()) {
2521 case kAlpha_8_SkColorType:
2522 case kRGB_565_SkColorType:
2523 case kPMColor_SkColorType:
2524 break;
2525 default:
2526 return false;
2527 }
2528
2529 return true;
2530}
2531
2532SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) {
2533 if (!supported_for_raster_canvas(info)) {
2534 return NULL;
2535 }
2536
2537 SkBitmap bitmap;
2538 if (!bitmap.allocPixels(info)) {
2539 return NULL;
2540 }
2541
2542 // should this functionality be moved into allocPixels()?
2543 if (!bitmap.info().isOpaque()) {
2544 bitmap.eraseColor(0);
2545 }
2546 return SkNEW_ARGS(SkCanvas, (bitmap));
2547}