blob: 7d25c2848c51d4592af7f3369fc42e0563044696 [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"
11#include "SkBounder.h"
12#include "SkDevice.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"
reed@android.com8a1c16f2008-12-17 15:59:43 +000018#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000019#include "SkRasterClip.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000020#include "SkRRect.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000021#include "SkScalarCompare.h"
reed@google.com97af1a62012-08-28 12:19:02 +000022#include "SkSurface_Base.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000023#include "SkTemplates.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000024#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000025#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000026#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000027
reed@google.com82ce2b82012-06-26 17:43:26 +000028SK_DEFINE_INST_COUNT(SkBounder)
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000029SK_DEFINE_INST_COUNT(SkCanvas)
reed@google.com82ce2b82012-06-26 17:43:26 +000030SK_DEFINE_INST_COUNT(SkDrawFilter)
31
reed@google.comda17f752012-08-16 18:27:05 +000032// experimental for faster tiled drawing...
33//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000034
reed@android.com8a1c16f2008-12-17 15:59:43 +000035//#define SK_TRACE_SAVERESTORE
36
37#ifdef SK_TRACE_SAVERESTORE
38 static int gLayerCounter;
39 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
40 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
41
42 static int gRecCounter;
43 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
44 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
45
46 static int gCanvasCounter;
47 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
48 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
49#else
50 #define inc_layer()
51 #define dec_layer()
52 #define inc_rec()
53 #define dec_rec()
54 #define inc_canvas()
55 #define dec_canvas()
56#endif
57
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000058typedef SkTLazy<SkPaint> SkLazyPaint;
59
reed@google.com97af1a62012-08-28 12:19:02 +000060void SkCanvas::predrawNotify() {
61 if (fSurfaceBase) {
62 fSurfaceBase->aboutToDraw(this);
63 }
64}
65
reed@android.com8a1c16f2008-12-17 15:59:43 +000066///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000067
68/* This is the record we keep for each SkDevice that the user installs.
69 The clip/matrix/proc are fields that reflect the top of the save/restore
70 stack. Whenever the canvas changes, it marks a dirty flag, and then before
71 these are used (assuming we're not on a layer) we rebuild these cache
72 values: they reflect the top of the save stack, but translated and clipped
73 by the device's XY offset and bitmap-bounds.
74*/
75struct DeviceCM {
76 DeviceCM* fNext;
77 SkDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +000078 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +000079 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +000080 SkPaint* fPaint; // may be null (in the future)
reed@android.com8a1c16f2008-12-17 15:59:43 +000081
robertphillips@google.com40a1ae42012-07-13 15:36:15 +000082 DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas)
reed@android.com8a1c16f2008-12-17 15:59:43 +000083 : fNext(NULL) {
84 if (NULL != device) {
85 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +000086 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +000087 }
reed@google.com4b226022011-01-11 18:32:13 +000088 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +000089 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +000090 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000091
bungeman@google.com88edf1e2011-08-08 19:41:56 +000092 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000093 if (NULL != fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +000094 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +000095 fDevice->unref();
96 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +000097 SkDELETE(fPaint);
98 }
reed@google.com4b226022011-01-11 18:32:13 +000099
reed@google.com045e62d2011-10-24 12:19:46 +0000100 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
101 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000102 int x = fDevice->getOrigin().x();
103 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000104 int width = fDevice->width();
105 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000106
reed@android.com8a1c16f2008-12-17 15:59:43 +0000107 if ((x | y) == 0) {
108 fMatrix = &totalMatrix;
109 fClip = totalClip;
110 } else {
111 fMatrixStorage = totalMatrix;
112 fMatrixStorage.postTranslate(SkIntToScalar(-x),
113 SkIntToScalar(-y));
114 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000115
reed@android.com8a1c16f2008-12-17 15:59:43 +0000116 totalClip.translate(-x, -y, &fClip);
117 }
118
reed@google.com045e62d2011-10-24 12:19:46 +0000119 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000120
121 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000122
reed@android.com8a1c16f2008-12-17 15:59:43 +0000123 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000124 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125 SkRegion::kDifference_Op);
126 }
reed@google.com4b226022011-01-11 18:32:13 +0000127
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000128 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
129
reed@android.com8a1c16f2008-12-17 15:59:43 +0000130#ifdef SK_DEBUG
131 if (!fClip.isEmpty()) {
132 SkIRect deviceR;
133 deviceR.set(0, 0, width, height);
134 SkASSERT(deviceR.contains(fClip.getBounds()));
135 }
136#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000137 }
138
reed@android.com8a1c16f2008-12-17 15:59:43 +0000139private:
bsalomon@google.com0e354aa2012-10-08 20:44:25 +0000140 SkMatrix fMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000141};
142
143/* This is the record we keep for each save/restore level in the stack.
144 Since a level optionally copies the matrix and/or stack, we have pointers
145 for these fields. If the value is copied for this level, the copy is
146 stored in the ...Storage field, and the pointer points to that. If the
147 value is not copied for this level, we ignore ...Storage, and just point
148 at the corresponding value in the previous level in the stack.
149*/
150class SkCanvas::MCRec {
151public:
152 MCRec* fNext;
reed@google.com00177082011-10-12 14:34:30 +0000153 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
154 SkRasterClip* fRasterClip; // points to either fRegionStorage or prev MCRec
155 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000156
reed@android.com8a1c16f2008-12-17 15:59:43 +0000157 DeviceCM* fLayer;
158 /* If there are any layers in the stack, this points to the top-most
159 one that is at or below this level in the stack (so we know what
160 bitmap/device to draw into from this level. This value is NOT
161 reference counted, since the real owner is either our fLayer field,
162 or a previous one in a lower level.)
163 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000164 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000165
166 MCRec(const MCRec* prev, int flags) {
167 if (NULL != prev) {
168 if (flags & SkCanvas::kMatrix_SaveFlag) {
169 fMatrixStorage = *prev->fMatrix;
170 fMatrix = &fMatrixStorage;
171 } else {
172 fMatrix = prev->fMatrix;
173 }
reed@google.com4b226022011-01-11 18:32:13 +0000174
reed@android.com8a1c16f2008-12-17 15:59:43 +0000175 if (flags & SkCanvas::kClip_SaveFlag) {
reed@google.com00177082011-10-12 14:34:30 +0000176 fRasterClipStorage = *prev->fRasterClip;
177 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000178 } else {
reed@google.com00177082011-10-12 14:34:30 +0000179 fRasterClip = prev->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180 }
181
182 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000183 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000184
185 fTopLayer = prev->fTopLayer;
186 } else { // no prev
187 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000188
reed@android.com8a1c16f2008-12-17 15:59:43 +0000189 fMatrix = &fMatrixStorage;
reed@google.com00177082011-10-12 14:34:30 +0000190 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000191 fFilter = NULL;
192 fTopLayer = NULL;
193 }
194 fLayer = NULL;
195
196 // don't bother initializing fNext
197 inc_rec();
198 }
199 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000200 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000201 SkDELETE(fLayer);
202 dec_rec();
203 }
reed@google.com4b226022011-01-11 18:32:13 +0000204
reed@android.com8a1c16f2008-12-17 15:59:43 +0000205private:
reed@google.com00177082011-10-12 14:34:30 +0000206 SkMatrix fMatrixStorage;
207 SkRasterClip fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000208};
209
210class SkDrawIter : public SkDraw {
211public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000212 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000213 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000214 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215 canvas->updateDeviceCMCache();
216
reed@google.com90c07ea2012-04-13 13:50:27 +0000217 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000218 fBounder = canvas->getBounder();
219 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000220 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221 }
reed@google.com4b226022011-01-11 18:32:13 +0000222
reed@android.com8a1c16f2008-12-17 15:59:43 +0000223 bool next() {
224 // skip over recs with empty clips
225 if (fSkipEmptyClips) {
226 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
227 fCurrLayer = fCurrLayer->fNext;
228 }
229 }
230
reed@google.comf68c5e22012-02-24 16:38:58 +0000231 const DeviceCM* rec = fCurrLayer;
232 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000233
234 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000235 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
236 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000237 fDevice = rec->fDevice;
238 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000239 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000240 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000241
242 fCurrLayer = rec->fNext;
243 if (fBounder) {
244 fBounder->setClip(fClip);
245 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000246 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000247
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248 return true;
249 }
250 return false;
251 }
reed@google.com4b226022011-01-11 18:32:13 +0000252
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253 SkDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000254 int getX() const { return fDevice->getOrigin().x(); }
255 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000256 const SkMatrix& getMatrix() const { return *fMatrix; }
257 const SkRegion& getClip() const { return *fClip; }
258 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000259
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260private:
261 SkCanvas* fCanvas;
262 const DeviceCM* fCurrLayer;
263 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000264 SkBool8 fSkipEmptyClips;
265
266 typedef SkDraw INHERITED;
267};
268
269/////////////////////////////////////////////////////////////////////////////
270
271class AutoDrawLooper {
272public:
reed@google.com8926b162012-03-23 15:36:36 +0000273 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint,
274 bool skipLayerForImageFilter = false) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000275 fCanvas = canvas;
276 fLooper = paint.getLooper();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000277 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000278 fPaint = NULL;
279 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000280 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000281 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000282
reed@google.com8926b162012-03-23 15:36:36 +0000283 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
284 SkPaint tmp;
285 tmp.setImageFilter(fOrigPaint.getImageFilter());
286 // it would be nice if we had a guess at the bounds, instead of null
287 (void)canvas->internalSaveLayer(NULL, &tmp,
288 SkCanvas::kARGB_ClipLayer_SaveFlag, true);
289 // we'll clear the imageFilter for the actual draws in next(), so
290 // it will only be applied during the restore().
291 fDoClearImageFilter = true;
292 }
293
reed@google.com4e2b3d32011-04-07 14:18:59 +0000294 if (fLooper) {
295 fLooper->init(canvas);
reed@google.com129ec222012-05-15 13:24:09 +0000296 fIsSimple = false;
297 } else {
298 // can we be marked as simple?
299 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000300 }
301 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000302
reed@android.com8a1c16f2008-12-17 15:59:43 +0000303 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000304 if (fDoClearImageFilter) {
305 fCanvas->internalRestore();
306 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000307 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000308 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000309
reed@google.com4e2b3d32011-04-07 14:18:59 +0000310 const SkPaint& paint() const {
311 SkASSERT(fPaint);
312 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000313 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000314
reed@google.com129ec222012-05-15 13:24:09 +0000315 bool next(SkDrawFilter::Type drawType) {
316 if (fDone) {
317 return false;
318 } else if (fIsSimple) {
319 fDone = true;
320 fPaint = &fOrigPaint;
321 return !fPaint->nothingToDraw();
322 } else {
323 return this->doNext(drawType);
324 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000325 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000326
reed@android.com8a1c16f2008-12-17 15:59:43 +0000327private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000328 SkLazyPaint fLazyPaint;
329 SkCanvas* fCanvas;
330 const SkPaint& fOrigPaint;
331 SkDrawLooper* fLooper;
332 SkDrawFilter* fFilter;
333 const SkPaint* fPaint;
334 int fSaveCount;
reed@google.com8926b162012-03-23 15:36:36 +0000335 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000336 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000337 bool fIsSimple;
338
339 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000340};
341
reed@google.com129ec222012-05-15 13:24:09 +0000342bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000343 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000344 SkASSERT(!fIsSimple);
345 SkASSERT(fLooper || fFilter || fDoClearImageFilter);
346
347 SkPaint* paint = fLazyPaint.set(fOrigPaint);
348
349 if (fDoClearImageFilter) {
350 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000351 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000352
reed@google.com129ec222012-05-15 13:24:09 +0000353 if (fLooper && !fLooper->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000354 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000355 return false;
356 }
357 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000358 if (!fFilter->filter(paint, drawType)) {
359 fDone = true;
360 return false;
361 }
reed@google.com129ec222012-05-15 13:24:09 +0000362 if (NULL == fLooper) {
363 // no looper means we only draw once
364 fDone = true;
365 }
366 }
367 fPaint = paint;
368
369 // if we only came in here for the imagefilter, mark us as done
370 if (!fLooper && !fFilter) {
371 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000372 }
373
374 // call this after any possible paint modifiers
375 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000376 fPaint = NULL;
377 return false;
378 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000379 return true;
380}
381
reed@android.com8a1c16f2008-12-17 15:59:43 +0000382/* Stack helper for managing a SkBounder. In the destructor, if we were
383 given a bounder, we call its commit() method, signifying that we are
384 done accumulating bounds for that draw.
385*/
386class SkAutoBounderCommit {
387public:
388 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
389 ~SkAutoBounderCommit() {
390 if (NULL != fBounder) {
391 fBounder->commit();
392 }
393 }
394private:
395 SkBounder* fBounder;
396};
397
398#include "SkColorPriv.h"
399
400class AutoValidator {
401public:
402 AutoValidator(SkDevice* device) : fDevice(device) {}
403 ~AutoValidator() {
404#ifdef SK_DEBUG
405 const SkBitmap& bm = fDevice->accessBitmap(false);
406 if (bm.config() == SkBitmap::kARGB_4444_Config) {
407 for (int y = 0; y < bm.height(); y++) {
408 const SkPMColor16* p = bm.getAddr16(0, y);
409 for (int x = 0; x < bm.width(); x++) {
410 SkPMColor16 c = p[x];
411 SkPMColor16Assert(c);
412 }
413 }
414 }
415#endif
416 }
417private:
418 SkDevice* fDevice;
419};
420
421////////// macros to place around the internal draw calls //////////////////
422
reed@google.com8926b162012-03-23 15:36:36 +0000423#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
424/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
reed@google.com97af1a62012-08-28 12:19:02 +0000425 this->predrawNotify(); \
reed@google.com8926b162012-03-23 15:36:36 +0000426 AutoDrawLooper looper(this, paint, true); \
427 while (looper.next(type)) { \
428 SkAutoBounderCommit ac(fBounder); \
429 SkDrawIter iter(this);
430
reed@google.com4e2b3d32011-04-07 14:18:59 +0000431#define LOOPER_BEGIN(paint, type) \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000432/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
reed@google.com97af1a62012-08-28 12:19:02 +0000433 this->predrawNotify(); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000434 AutoDrawLooper looper(this, paint); \
435 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000436 SkAutoBounderCommit ac(fBounder); \
437 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000438
reed@google.com4e2b3d32011-04-07 14:18:59 +0000439#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000440
441////////////////////////////////////////////////////////////////////////////
442
443SkDevice* SkCanvas::init(SkDevice* device) {
444 fBounder = NULL;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000445 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000446 fLocalBoundsCompareTypeDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000447 fAllowSoftClip = true;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000448 fDeviceCMDirty = false;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000449 fSaveLayerCount = 0;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000450 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000451
452 fMCRec = (MCRec*)fMCStack.push_back();
453 new (fMCRec) MCRec(NULL, 0);
454
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000455 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000456 fMCRec->fTopLayer = fMCRec->fLayer;
457 fMCRec->fNext = NULL;
458
reed@google.com97af1a62012-08-28 12:19:02 +0000459 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000460
reed@android.com8a1c16f2008-12-17 15:59:43 +0000461 return this->setDevice(device);
462}
463
reed@google.comcde92112011-07-06 20:00:52 +0000464SkCanvas::SkCanvas()
465: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000466 inc_canvas();
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000467
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000468 this->init(NULL);
469}
470
reed@android.com8a1c16f2008-12-17 15:59:43 +0000471SkCanvas::SkCanvas(SkDevice* device)
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000472 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000473 inc_canvas();
474
475 this->init(device);
476}
477
478SkCanvas::SkCanvas(const SkBitmap& bitmap)
479 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
480 inc_canvas();
481
reed@google.comcde92112011-07-06 20:00:52 +0000482 this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000483}
484
485SkCanvas::~SkCanvas() {
486 // free up the contents of our deque
487 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000488 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000489
reed@android.com8a1c16f2008-12-17 15:59:43 +0000490 this->internalRestore(); // restore the last, since we're going away
491
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000492 SkSafeUnref(fBounder);
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000493 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000494
reed@android.com8a1c16f2008-12-17 15:59:43 +0000495 dec_canvas();
496}
497
498SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
499 SkRefCnt_SafeAssign(fBounder, bounder);
500 return bounder;
501}
502
503SkDrawFilter* SkCanvas::getDrawFilter() const {
504 return fMCRec->fFilter;
505}
506
507SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
508 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
509 return filter;
510}
511
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000512SkMetaData& SkCanvas::getMetaData() {
513 // metadata users are rare, so we lazily allocate it. If that changes we
514 // can decide to just make it a field in the device (rather than a ptr)
515 if (NULL == fMetaData) {
516 fMetaData = new SkMetaData;
517 }
518 return *fMetaData;
519}
520
reed@android.com8a1c16f2008-12-17 15:59:43 +0000521///////////////////////////////////////////////////////////////////////////////
522
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000523void SkCanvas::flush() {
524 SkDevice* device = this->getDevice();
525 if (device) {
526 device->flush();
527 }
528}
529
reed@google.com210ce002011-11-01 14:24:23 +0000530SkISize SkCanvas::getDeviceSize() const {
531 SkDevice* d = this->getDevice();
532 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
533}
534
reed@android.com8a1c16f2008-12-17 15:59:43 +0000535SkDevice* SkCanvas::getDevice() const {
536 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000537 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000538 SkASSERT(rec && rec->fLayer);
539 return rec->fLayer->fDevice;
540}
541
reed@google.com0b53d592012-03-19 18:26:34 +0000542SkDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
543 if (updateMatrixClip) {
544 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
545 }
reed@google.com9266fed2011-03-30 00:18:03 +0000546 return fMCRec->fTopLayer->fDevice;
547}
548
reed@android.com8a1c16f2008-12-17 15:59:43 +0000549SkDevice* SkCanvas::setDevice(SkDevice* device) {
550 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000551 SkDeque::F2BIter iter(fMCStack);
552 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000553 SkASSERT(rec && rec->fLayer);
554 SkDevice* rootDevice = rec->fLayer->fDevice;
555
556 if (rootDevice == device) {
557 return device;
558 }
reed@google.com4b226022011-01-11 18:32:13 +0000559
reed@android.com8a1c16f2008-12-17 15:59:43 +0000560 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000561 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000562 }
563 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000564 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000565 }
566
567 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
568 rootDevice = device;
569
570 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000571
reed@android.com8a1c16f2008-12-17 15:59:43 +0000572 /* Now we update our initial region to have the bounds of the new device,
573 and then intersect all of the clips in our stack with these bounds,
574 to ensure that we can't draw outside of the device's bounds (and trash
575 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000576
reed@android.com8a1c16f2008-12-17 15:59:43 +0000577 NOTE: this is only a partial-fix, since if the new device is larger than
578 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000579 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000580 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
581 reconstruct the correct clips, so this approximation will have to do.
582 The caller really needs to restore() back to the base if they want to
583 accurately take advantage of the new device bounds.
584 */
585
reed@google.com42aea282012-03-28 16:19:15 +0000586 SkIRect bounds;
587 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000588 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000589 } else {
590 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000591 }
reed@google.com42aea282012-03-28 16:19:15 +0000592 // now jam our 1st clip to be bounds, and intersect the rest with that
593 rec->fRasterClip->setRect(bounds);
594 while ((rec = (MCRec*)iter.next()) != NULL) {
595 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
596 }
597
reed@android.com8a1c16f2008-12-17 15:59:43 +0000598 return device;
599}
600
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000601bool SkCanvas::readPixels(SkBitmap* bitmap,
602 int x, int y,
603 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000604 SkDevice* device = this->getDevice();
605 if (!device) {
606 return false;
607 }
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000608 return device->readPixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000609}
610
bsalomon@google.comc6980972011-11-02 19:57:21 +0000611bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
bsalomon@google.comace7bd52011-11-02 19:39:51 +0000612 SkDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000613 if (!device) {
614 return false;
615 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000616
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000617 SkIRect bounds;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000618 bounds.set(0, 0, device->width(), device->height());
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000619 if (!bounds.intersect(srcRect)) {
620 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000621 }
622
623 SkBitmap tmp;
624 tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
625 bounds.height());
626 if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
627 bitmap->swap(tmp);
628 return true;
629 } else {
reed@google.com51df9e32010-12-23 19:29:18 +0000630 return false;
631 }
reed@google.com51df9e32010-12-23 19:29:18 +0000632}
633
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000634void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
635 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000636 SkDevice* device = this->getDevice();
637 if (device) {
bsalomon@google.com405d0f42012-08-29 21:26:13 +0000638 if (SkIRect::Intersects(SkIRect::MakeSize(this->getDeviceSize()),
639 SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height()))) {
640 device->accessBitmap(true);
641 device->writePixels(bitmap, x, y, config8888);
642 }
reed@google.com51df9e32010-12-23 19:29:18 +0000643 }
644}
645
junov@google.com4370aed2012-01-18 16:21:08 +0000646SkCanvas* SkCanvas::canvasForDrawIter() {
647 return this;
648}
649
reed@android.com8a1c16f2008-12-17 15:59:43 +0000650//////////////////////////////////////////////////////////////////////////////
651
reed@android.com8a1c16f2008-12-17 15:59:43 +0000652void SkCanvas::updateDeviceCMCache() {
653 if (fDeviceCMDirty) {
654 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000655 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000656 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000657
reed@android.com8a1c16f2008-12-17 15:59:43 +0000658 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000659 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000660 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000661 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000662 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000663 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000664 } while ((layer = layer->fNext) != NULL);
665 }
666 fDeviceCMDirty = false;
667 }
668}
669
reed@android.com8a1c16f2008-12-17 15:59:43 +0000670///////////////////////////////////////////////////////////////////////////////
671
672int SkCanvas::internalSave(SaveFlags flags) {
673 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000674
reed@android.com8a1c16f2008-12-17 15:59:43 +0000675 MCRec* newTop = (MCRec*)fMCStack.push_back();
676 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000677
reed@android.com8a1c16f2008-12-17 15:59:43 +0000678 newTop->fNext = fMCRec;
679 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000680
reed@google.com5c3d1472011-02-22 19:12:23 +0000681 fClipStack.save();
682 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
683
reed@android.com8a1c16f2008-12-17 15:59:43 +0000684 return saveCount;
685}
686
687int SkCanvas::save(SaveFlags flags) {
688 // call shared impl
689 return this->internalSave(flags);
690}
691
692#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
693#define C16MASK (1 << SkBitmap::kRGB_565_Config)
694#define C8MASK (1 << SkBitmap::kA8_Config)
695
696static SkBitmap::Config resolve_config(SkCanvas* canvas,
697 const SkIRect& bounds,
698 SkCanvas::SaveFlags flags,
699 bool* isOpaque) {
700 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
701
702#if 0
703 // loop through and union all the configs we may draw into
704 uint32_t configMask = 0;
705 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
706 {
707 SkDevice* device = canvas->getLayerDevice(i);
708 if (device->intersects(bounds))
709 configMask |= 1 << device->config();
710 }
711
712 // if the caller wants alpha or fullcolor, we can't return 565
713 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
714 SkCanvas::kHasAlphaLayer_SaveFlag))
715 configMask &= ~C16MASK;
716
717 switch (configMask) {
718 case C8MASK: // if we only have A8, return that
719 return SkBitmap::kA8_Config;
720
721 case C16MASK: // if we only have 565, return that
722 return SkBitmap::kRGB_565_Config;
723
724 default:
725 return SkBitmap::kARGB_8888_Config; // default answer
726 }
727#else
728 return SkBitmap::kARGB_8888_Config; // default answer
729#endif
730}
731
732static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
733 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
734}
735
junov@chromium.orga907ac32012-02-24 21:54:07 +0000736bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
737 SkIRect* intersection) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000738 SkIRect clipBounds;
739 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000740 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000741 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000742 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000743 if (NULL != bounds) {
744 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000745
reed@android.com8a1c16f2008-12-17 15:59:43 +0000746 this->getTotalMatrix().mapRect(&r, *bounds);
747 r.roundOut(&ir);
748 // early exit if the layer's bounds are clipped out
749 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000750 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000751 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000752 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000753 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000754 }
755 } else { // no user bounds, so just use the clip
756 ir = clipBounds;
757 }
758
reed@google.com5c3d1472011-02-22 19:12:23 +0000759 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000760
reed@android.com8a1c16f2008-12-17 15:59:43 +0000761 // early exit if the clip is now empty
762 if (bounds_affects_clip(flags) &&
reed@google.com00177082011-10-12 14:34:30 +0000763 !fMCRec->fRasterClip->op(ir, SkRegion::kIntersect_Op)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000764 return false;
765 }
766
767 if (intersection) {
768 *intersection = ir;
769 }
770 return true;
771}
772
773int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
774 SaveFlags flags) {
reed@google.com8926b162012-03-23 15:36:36 +0000775 return this->internalSaveLayer(bounds, paint, flags, false);
776}
777
778int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
779 SaveFlags flags, bool justForImageFilter) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000780 // do this before we create the layer. We don't call the public save() since
781 // that would invoke a possibly overridden virtual
782 int count = this->internalSave(flags);
783
784 fDeviceCMDirty = true;
785
786 SkIRect ir;
787 if (!this->clipRectBounds(bounds, flags, &ir)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000788 return count;
789 }
790
reed@google.comb55deeb2012-01-06 14:43:09 +0000791 // Kill the imagefilter if our device doesn't allow it
792 SkLazyPaint lazyP;
793 if (paint && paint->getImageFilter()) {
794 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000795 if (justForImageFilter) {
796 // early exit if the layer was just for the imageFilter
797 return count;
798 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000799 SkPaint* p = lazyP.set(*paint);
800 p->setImageFilter(NULL);
801 paint = p;
802 }
803 }
804
reed@android.com8a1c16f2008-12-17 15:59:43 +0000805 bool isOpaque;
806 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
807
reed@google.com76dd2772012-01-05 21:15:07 +0000808 SkDevice* device;
809 if (paint && paint->getImageFilter()) {
810 device = this->createCompatibleDevice(config, ir.width(), ir.height(),
811 isOpaque);
812 } else {
813 device = this->createLayerDevice(config, ir.width(), ir.height(),
814 isOpaque);
815 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000816 if (NULL == device) {
817 SkDebugf("Unable to create device for layer.");
818 return count;
819 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000820
reed@google.com6f8f2922011-03-04 22:27:10 +0000821 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000822 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000823 device->unref();
824
825 layer->fNext = fMCRec->fTopLayer;
826 fMCRec->fLayer = layer;
827 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
828
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000829 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000830 return count;
831}
832
833int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
834 SaveFlags flags) {
835 if (0xFF == alpha) {
836 return this->saveLayer(bounds, NULL, flags);
837 } else {
838 SkPaint tmpPaint;
839 tmpPaint.setAlpha(alpha);
840 return this->saveLayer(bounds, &tmpPaint, flags);
841 }
842}
843
844void SkCanvas::restore() {
845 // check for underflow
846 if (fMCStack.count() > 1) {
847 this->internalRestore();
848 }
849}
850
851void SkCanvas::internalRestore() {
852 SkASSERT(fMCStack.count() != 0);
853
854 fDeviceCMDirty = true;
855 fLocalBoundsCompareTypeDirty = true;
856
reed@google.com5c3d1472011-02-22 19:12:23 +0000857 fClipStack.restore();
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000858 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000859 DeviceCM* layer = fMCRec->fLayer; // may be null
860 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
861 fMCRec->fLayer = NULL;
862
863 // now do the normal restore()
864 fMCRec->~MCRec(); // balanced in save()
865 fMCStack.pop_back();
866 fMCRec = (MCRec*)fMCStack.back();
867
868 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
869 since if we're being recorded, we don't want to record this (the
870 recorder will have already recorded the restore).
871 */
872 if (NULL != layer) {
873 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000874 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000875 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
876 layer->fPaint);
877 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000878 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000879
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000880 SkASSERT(fSaveLayerCount > 0);
881 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000882 }
883 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000884 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000885
886 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000887}
888
889int SkCanvas::getSaveCount() const {
890 return fMCStack.count();
891}
892
893void SkCanvas::restoreToCount(int count) {
894 // sanity check
895 if (count < 1) {
896 count = 1;
897 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000898
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000899 int n = this->getSaveCount() - count;
900 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000901 this->restore();
902 }
903}
904
reed@google.com7c202932011-12-14 18:48:05 +0000905bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000906 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000907}
908
reed@android.com8a1c16f2008-12-17 15:59:43 +0000909/////////////////////////////////////////////////////////////////////////////
910
911// can't draw it if its empty, or its too big for a fixed-point width or height
912static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000913 return bitmap.width() <= 0 || bitmap.height() <= 0
914#ifndef SK_ALLOW_OVER_32K_BITMAPS
915 || bitmap.width() > 32767 || bitmap.height() > 32767
916#endif
917 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000918}
919
reed@android.comf2b98d62010-12-20 18:26:13 +0000920void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000921 const SkMatrix& matrix, const SkPaint* paint) {
922 if (reject_bitmap(bitmap)) {
923 return;
924 }
925
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000926 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000927 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000928 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000929 }
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000930 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000931}
932
reed@google.com8926b162012-03-23 15:36:36 +0000933void SkCanvas::internalDrawDevice(SkDevice* srcDev, int x, int y,
934 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000935 SkPaint tmp;
936 if (NULL == paint) {
937 tmp.setDither(true);
938 paint = &tmp;
939 }
reed@google.com4b226022011-01-11 18:32:13 +0000940
reed@google.com8926b162012-03-23 15:36:36 +0000941 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000942 while (iter.next()) {
reed@google.comb55deeb2012-01-06 14:43:09 +0000943 SkDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +0000944 paint = &looper.paint();
945 SkImageFilter* filter = paint->getImageFilter();
946 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +0000947 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +0000948 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +0000949 SkBitmap dst;
reed@google.comb55deeb2012-01-06 14:43:09 +0000950 const SkBitmap& src = srcDev->accessBitmap(false);
reed@google.com76dd2772012-01-05 21:15:07 +0000951 if (filter->filterImage(&proxy, src, *iter.fMatrix, &dst, &pos)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +0000952 SkPaint tmpUnfiltered(*paint);
953 tmpUnfiltered.setImageFilter(NULL);
954 dstDev->drawSprite(iter, dst, pos.x(), pos.y(), tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +0000955 }
956 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +0000957 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +0000958 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000959 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000960 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +0000961}
962
reed@google.com8926b162012-03-23 15:36:36 +0000963void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
964 const SkPaint* paint) {
965 SkDEBUGCODE(bitmap.validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000966
reed@google.com8926b162012-03-23 15:36:36 +0000967 if (reject_bitmap(bitmap)) {
968 return;
969 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000970
reed@google.com8926b162012-03-23 15:36:36 +0000971 SkPaint tmp;
972 if (NULL == paint) {
973 paint = &tmp;
974 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000975
reed@google.com8926b162012-03-23 15:36:36 +0000976 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000977
reed@google.com8926b162012-03-23 15:36:36 +0000978 while (iter.next()) {
979 paint = &looper.paint();
980 SkImageFilter* filter = paint->getImageFilter();
981 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
982 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +0000983 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +0000984 SkBitmap dst;
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +0000985 if (filter->filterImage(&proxy, bitmap, *iter.fMatrix,
986 &dst, &pos)) {
987 SkPaint tmpUnfiltered(*paint);
988 tmpUnfiltered.setImageFilter(NULL);
989 iter.fDevice->drawSprite(iter, dst, pos.x(), pos.y(),
990 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +0000991 }
992 } else {
993 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
994 }
995 }
996 LOOPER_END
997}
998
reed@android.com8a1c16f2008-12-17 15:59:43 +0000999/////////////////////////////////////////////////////////////////////////////
1000
1001bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
1002 fDeviceCMDirty = true;
1003 fLocalBoundsCompareTypeDirty = true;
1004 return fMCRec->fMatrix->preTranslate(dx, dy);
1005}
1006
1007bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
1008 fDeviceCMDirty = true;
1009 fLocalBoundsCompareTypeDirty = true;
1010 return fMCRec->fMatrix->preScale(sx, sy);
1011}
1012
1013bool SkCanvas::rotate(SkScalar degrees) {
1014 fDeviceCMDirty = true;
1015 fLocalBoundsCompareTypeDirty = true;
1016 return fMCRec->fMatrix->preRotate(degrees);
1017}
1018
1019bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
1020 fDeviceCMDirty = true;
1021 fLocalBoundsCompareTypeDirty = true;
1022 return fMCRec->fMatrix->preSkew(sx, sy);
1023}
1024
1025bool SkCanvas::concat(const SkMatrix& matrix) {
1026 fDeviceCMDirty = true;
1027 fLocalBoundsCompareTypeDirty = true;
1028 return fMCRec->fMatrix->preConcat(matrix);
1029}
1030
1031void SkCanvas::setMatrix(const SkMatrix& matrix) {
1032 fDeviceCMDirty = true;
1033 fLocalBoundsCompareTypeDirty = true;
1034 *fMCRec->fMatrix = matrix;
1035}
1036
1037// this is not virtual, so it must call a virtual method so that subclasses
1038// will see its action
1039void SkCanvas::resetMatrix() {
1040 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001041
reed@android.com8a1c16f2008-12-17 15:59:43 +00001042 matrix.reset();
1043 this->setMatrix(matrix);
1044}
1045
1046//////////////////////////////////////////////////////////////////////////////
1047
reed@google.comc42d35d2011-10-12 11:57:42 +00001048bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001049#ifdef SK_ENABLE_CLIP_QUICKREJECT
1050 if (SkRegion::kIntersect_Op == op) {
1051 if (fMCRec->fRasterClip->isEmpty()) {
1052 return false;
1053 }
1054
reed@google.com3b3e8952012-08-16 20:53:31 +00001055 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001056 fDeviceCMDirty = true;
1057 fLocalBoundsCompareTypeDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001058
1059 fClipStack.clipEmpty();
1060 return fMCRec->fRasterClip->setEmpty();
1061 }
1062 }
1063#endif
1064
reed@google.com5c3d1472011-02-22 19:12:23 +00001065 AutoValidateClip avc(this);
1066
reed@android.com8a1c16f2008-12-17 15:59:43 +00001067 fDeviceCMDirty = true;
1068 fLocalBoundsCompareTypeDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001069 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001070
1071 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +00001072 // for these simpler matrices, we can stay a rect ever after applying
1073 // the matrix. This means we don't have to a) make a path, and b) tell
1074 // the region code to scan-convert the path, only to discover that it
1075 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001076 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001077
1078 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +00001079 fClipStack.clipDevRect(r, op, doAA);
1080 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001081 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +00001082 // since we're rotate or some such thing, we convert the rect to a path
1083 // and clip against that, since it can handle any matrix. However, to
1084 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1085 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001086 SkPath path;
1087
1088 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +00001089 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001090 }
1091}
1092
reed@google.com00177082011-10-12 14:34:30 +00001093static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +00001094 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001095 // base is used to limit the size (and therefore memory allocation) of the
1096 // region that results from scan converting devPath.
1097 SkRegion base;
1098
reed@google.com819c9212011-02-23 18:56:55 +00001099 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001100 // since we are intersect, we can do better (tighter) with currRgn's
1101 // bounds, than just using the device. However, if currRgn is complex,
1102 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001103 if (currClip->isRect()) {
1104 return currClip->setPath(devPath, *currClip, doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001105 } else {
reed@google.com00177082011-10-12 14:34:30 +00001106 base.setRect(currClip->getBounds());
1107 SkRasterClip clip;
1108 clip.setPath(devPath, base, doAA);
1109 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001110 }
reed@google.com819c9212011-02-23 18:56:55 +00001111 } else {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001112 const SkDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001113 if (!device) {
1114 return currClip->setEmpty();
1115 }
1116
junov@chromium.orga907ac32012-02-24 21:54:07 +00001117 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001118
1119 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +00001120 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001121 } else {
reed@google.com00177082011-10-12 14:34:30 +00001122 SkRasterClip clip;
1123 clip.setPath(devPath, base, doAA);
1124 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001125 }
1126 }
1127}
1128
reed@google.com4ed0fb72012-12-12 20:48:18 +00001129bool SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
1130 if (rrect.isRect()) {
1131 // call the non-virtual version
1132 return this->SkCanvas::clipRect(rrect.getBounds(), op, doAA);
1133 } else {
1134 SkPath path;
1135 path.addRRect(rrect);
1136 // call the non-virtual version
1137 return this->SkCanvas::clipPath(path, op, doAA);
1138 }
1139}
1140
reed@google.comc42d35d2011-10-12 11:57:42 +00001141bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001142#ifdef SK_ENABLE_CLIP_QUICKREJECT
1143 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1144 if (fMCRec->fRasterClip->isEmpty()) {
1145 return false;
1146 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001147
reed@google.com3b3e8952012-08-16 20:53:31 +00001148 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001149 fDeviceCMDirty = true;
1150 fLocalBoundsCompareTypeDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001151
reed@google.comda17f752012-08-16 18:27:05 +00001152 fClipStack.clipEmpty();
1153 return fMCRec->fRasterClip->setEmpty();
1154 }
1155 }
1156#endif
1157
reed@google.com5c3d1472011-02-22 19:12:23 +00001158 AutoValidateClip avc(this);
1159
reed@android.com8a1c16f2008-12-17 15:59:43 +00001160 fDeviceCMDirty = true;
1161 fLocalBoundsCompareTypeDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001162 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001163
1164 SkPath devPath;
1165 path.transform(*fMCRec->fMatrix, &devPath);
1166
reed@google.comfe701122011-11-08 19:41:23 +00001167 // Check if the transfomation, or the original path itself
1168 // made us empty. Note this can also happen if we contained NaN
1169 // values. computing the bounds detects this, and will set our
1170 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1171 if (devPath.getBounds().isEmpty()) {
1172 // resetting the path will remove any NaN or other wanky values
1173 // that might upset our scan converter.
1174 devPath.reset();
1175 }
1176
reed@google.com5c3d1472011-02-22 19:12:23 +00001177 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +00001178 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +00001179
reed@google.com00177082011-10-12 14:34:30 +00001180 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001181}
1182
1183bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001184 AutoValidateClip avc(this);
1185
reed@android.com8a1c16f2008-12-17 15:59:43 +00001186 fDeviceCMDirty = true;
1187 fLocalBoundsCompareTypeDirty = true;
1188
reed@google.com5c3d1472011-02-22 19:12:23 +00001189 // todo: signal fClipStack that we have a region, and therefore (I guess)
1190 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001191 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001192
reed@google.com00177082011-10-12 14:34:30 +00001193 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001194}
1195
reed@google.com819c9212011-02-23 18:56:55 +00001196#ifdef SK_DEBUG
1197void SkCanvas::validateClip() const {
1198 // construct clipRgn from the clipstack
1199 const SkDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001200 if (!device) {
1201 SkASSERT(this->getTotalClip().isEmpty());
1202 return;
1203 }
1204
reed@google.com819c9212011-02-23 18:56:55 +00001205 SkIRect ir;
1206 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001207 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001208
robertphillips@google.com80214e22012-07-20 15:33:18 +00001209 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001210 const SkClipStack::Element* element;
1211 while ((element = iter.next()) != NULL) {
1212 switch (element->getType()) {
1213 case SkClipStack::Element::kPath_Type:
1214 clipPathHelper(this,
1215 &tmpClip,
1216 element->getPath(),
1217 element->getOp(),
1218 element->isAA());
1219 break;
1220 case SkClipStack::Element::kRect_Type:
1221 element->getRect().round(&ir);
1222 tmpClip.op(ir, element->getOp());
1223 break;
1224 case SkClipStack::Element::kEmpty_Type:
1225 tmpClip.setEmpty();
1226 break;
reed@google.com819c9212011-02-23 18:56:55 +00001227 }
1228 }
1229
reed@google.com6f8f2922011-03-04 22:27:10 +00001230#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001231 // now compare against the current rgn
1232 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001233 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001234#endif
reed@google.com819c9212011-02-23 18:56:55 +00001235}
1236#endif
1237
reed@google.com90c07ea2012-04-13 13:50:27 +00001238void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001239 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001240 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001241
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001242 static const SkRect kEmpty = { 0, 0, 0, 0 };
1243 while ((element = iter.next()) != NULL) {
1244 switch (element->getType()) {
1245 case SkClipStack::Element::kPath_Type:
1246 visitor->clipPath(element->getPath(), element->getOp(), element->isAA());
1247 break;
1248 case SkClipStack::Element::kRect_Type:
1249 visitor->clipRect(element->getRect(), element->getOp(), element->isAA());
1250 break;
1251 case SkClipStack::Element::kEmpty_Type:
1252 visitor->clipRect(kEmpty, SkRegion::kIntersect_Op, false);
1253 break;
reed@google.com90c07ea2012-04-13 13:50:27 +00001254 }
1255 }
1256}
1257
reed@google.com5c3d1472011-02-22 19:12:23 +00001258///////////////////////////////////////////////////////////////////////////////
1259
reed@google.com3b3e8952012-08-16 20:53:31 +00001260void SkCanvas::computeLocalClipBoundsCompareType() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001261 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001262
reed@google.com3b3e8952012-08-16 20:53:31 +00001263 if (!this->getClipBounds(&r)) {
1264 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001265 } else {
reed@google.com3b3e8952012-08-16 20:53:31 +00001266 fLocalBoundsCompareType.set(SkScalarToCompareType(r.fLeft),
1267 SkScalarToCompareType(r.fTop),
1268 SkScalarToCompareType(r.fRight),
1269 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001270 }
1271}
1272
reed@google.com3b3e8952012-08-16 20:53:31 +00001273bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001274
reed@google.com16078632011-12-06 18:56:37 +00001275 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001276 return true;
1277
reed@google.com00177082011-10-12 14:34:30 +00001278 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001279 return true;
1280 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001281
tomhudson@google.com8d430182011-06-06 19:11:19 +00001282 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001283 SkRect dst;
1284 fMCRec->fMatrix->mapRect(&dst, rect);
1285 SkIRect idst;
1286 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001287 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001288 } else {
reed@google.com3b3e8952012-08-16 20:53:31 +00001289 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
reed@android.comd252db02009-04-01 18:31:44 +00001290
reed@android.coma380ae42009-07-21 01:17:02 +00001291 // for speed, do the most likely reject compares first
1292 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1293 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1294 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1295 return true;
1296 }
1297 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1298 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1299 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1300 return true;
1301 }
1302 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001303 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001304}
1305
reed@google.com3b3e8952012-08-16 20:53:31 +00001306bool SkCanvas::quickReject(const SkPath& path) const {
1307 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001308}
1309
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001310static inline int pinIntForScalar(int x) {
1311#ifdef SK_SCALAR_IS_FIXED
1312 if (x < SK_MinS16) {
1313 x = SK_MinS16;
1314 } else if (x > SK_MaxS16) {
1315 x = SK_MaxS16;
1316 }
1317#endif
1318 return x;
1319}
1320
reed@google.com3b3e8952012-08-16 20:53:31 +00001321bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001322 SkIRect ibounds;
1323 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001324 return false;
1325 }
1326
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001327 SkMatrix inverse;
1328 // if we can't invert the CTM, we can't return local clip bounds
1329 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001330 if (bounds) {
1331 bounds->setEmpty();
1332 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001333 return false;
1334 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001335
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001336 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001337 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001338 // adjust it outwards in case we are antialiasing
1339 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001340
1341 // SkRect::iset() will correctly assert if we pass a value out of range
1342 // (when SkScalar==fixed), so we pin to legal values. This does not
1343 // really returnt the correct answer, but its the best we can do given
1344 // that we've promised to return SkRect (even though we support devices
1345 // that can be larger than 32K in width or height).
1346 r.iset(pinIntForScalar(ibounds.fLeft - inset),
1347 pinIntForScalar(ibounds.fTop - inset),
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001348 pinIntForScalar(ibounds.fRight + inset),
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001349 pinIntForScalar(ibounds.fBottom + inset));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001350 inverse.mapRect(bounds, r);
1351 }
1352 return true;
1353}
1354
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001355bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001356 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001357 if (clip.isEmpty()) {
1358 if (bounds) {
1359 bounds->setEmpty();
1360 }
1361 return false;
1362 }
1363
1364 if (NULL != bounds) {
1365 *bounds = clip.getBounds();
1366 }
1367 return true;
1368}
1369
reed@android.com8a1c16f2008-12-17 15:59:43 +00001370const SkMatrix& SkCanvas::getTotalMatrix() const {
1371 return *fMCRec->fMatrix;
1372}
1373
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001374SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001375 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1376 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001377 return kComplex_ClipType;
1378}
1379
reed@android.com8a1c16f2008-12-17 15:59:43 +00001380const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001381 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001382}
1383
bsalomon@google.come97f0852011-06-17 13:10:25 +00001384SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
1385 int width, int height,
1386 bool isOpaque) {
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001387 SkDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001388 if (device) {
1389 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1390 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001391 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001392 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001393 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001394}
1395
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001396SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
bsalomon@google.come97f0852011-06-17 13:10:25 +00001397 int width, int height,
1398 bool isOpaque) {
1399 SkDevice* device = this->getDevice();
1400 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001401 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001402 } else {
1403 return NULL;
1404 }
1405}
1406
1407
reed@android.com8a1c16f2008-12-17 15:59:43 +00001408//////////////////////////////////////////////////////////////////////////////
1409// These are the virtual drawing methods
1410//////////////////////////////////////////////////////////////////////////////
1411
reed@google.com2a981812011-04-14 18:59:28 +00001412void SkCanvas::clear(SkColor color) {
1413 SkDrawIter iter(this);
1414
1415 while (iter.next()) {
1416 iter.fDevice->clear(color);
1417 }
1418}
1419
reed@android.com8a1c16f2008-12-17 15:59:43 +00001420void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001421 this->internalDrawPaint(paint);
1422}
1423
1424void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001425 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001426
1427 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001428 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001429 }
1430
reed@google.com4e2b3d32011-04-07 14:18:59 +00001431 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001432}
1433
1434void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1435 const SkPaint& paint) {
1436 if ((long)count <= 0) {
1437 return;
1438 }
1439
reed@google.coma584aed2012-05-16 14:06:02 +00001440 if (paint.canComputeFastBounds()) {
1441 SkRect r;
1442 // special-case 2 points (common for drawing a single line)
1443 if (2 == count) {
1444 r.set(pts[0], pts[1]);
1445 } else {
1446 r.set(pts, count);
1447 }
1448 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001449 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
reed@google.coma584aed2012-05-16 14:06:02 +00001450 return;
1451 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001452 }
reed@google.coma584aed2012-05-16 14:06:02 +00001453
reed@android.com8a1c16f2008-12-17 15:59:43 +00001454 SkASSERT(pts != NULL);
1455
reed@google.com4e2b3d32011-04-07 14:18:59 +00001456 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001457
reed@android.com8a1c16f2008-12-17 15:59:43 +00001458 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001459 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001460 }
reed@google.com4b226022011-01-11 18:32:13 +00001461
reed@google.com4e2b3d32011-04-07 14:18:59 +00001462 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001463}
1464
1465void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1466 if (paint.canComputeFastBounds()) {
1467 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001468 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001469 return;
1470 }
1471 }
reed@google.com4b226022011-01-11 18:32:13 +00001472
reed@google.com4e2b3d32011-04-07 14:18:59 +00001473 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001474
1475 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001476 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001477 }
1478
reed@google.com4e2b3d32011-04-07 14:18:59 +00001479 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001480}
1481
reed@google.com4ed0fb72012-12-12 20:48:18 +00001482void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1483 if (paint.canComputeFastBounds()) {
1484 SkRect storage;
1485 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
1486 return;
1487 }
1488 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001489
reed@google.com4ed0fb72012-12-12 20:48:18 +00001490 SkPath path;
1491 path.addOval(oval);
1492 // call the non-virtual version
1493 this->SkCanvas::drawPath(path, paint);
1494}
1495
1496void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1497 if (paint.canComputeFastBounds()) {
1498 SkRect storage;
1499 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
1500 return;
1501 }
1502 }
1503
1504 if (rrect.isRect()) {
1505 // call the non-virtual version
1506 this->SkCanvas::drawRect(rrect.getBounds(), paint);
1507 } else {
1508 SkPath path;
1509 path.addRRect(rrect);
1510 // call the non-virtual version
1511 this->SkCanvas::drawPath(path, paint);
1512 }
1513}
1514
1515
reed@android.com8a1c16f2008-12-17 15:59:43 +00001516void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00001517 if (!path.isFinite()) {
1518 return;
1519 }
1520
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001521 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001522 SkRect storage;
1523 const SkRect& bounds = path.getBounds();
reed@google.com3b3e8952012-08-16 20:53:31 +00001524 if (this->quickReject(paint.computeFastBounds(bounds, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001525 return;
1526 }
1527 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001528 if (path.isEmpty()) {
1529 if (path.isInverseFillType()) {
1530 this->internalDrawPaint(paint);
1531 }
1532 return;
1533 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001534
reed@google.com4e2b3d32011-04-07 14:18:59 +00001535 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001536
1537 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001538 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001539 }
1540
reed@google.com4e2b3d32011-04-07 14:18:59 +00001541 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001542}
1543
1544void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1545 const SkPaint* paint) {
1546 SkDEBUGCODE(bitmap.validate();)
1547
reed@google.com3d608122011-11-21 15:16:16 +00001548 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001549 SkRect bounds = {
1550 x, y,
1551 x + SkIntToScalar(bitmap.width()),
1552 y + SkIntToScalar(bitmap.height())
1553 };
1554 if (paint) {
1555 (void)paint->computeFastBounds(bounds, &bounds);
1556 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001557 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001558 return;
1559 }
1560 }
reed@google.com4b226022011-01-11 18:32:13 +00001561
reed@android.com8a1c16f2008-12-17 15:59:43 +00001562 SkMatrix matrix;
1563 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001564 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001565}
1566
reed@google.com9987ec32011-09-07 11:57:52 +00001567// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001568void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
reed@google.com9987ec32011-09-07 11:57:52 +00001569 const SkRect& dst, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001570 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1571 return;
1572 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001573
reed@google.com3d608122011-11-21 15:16:16 +00001574 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001575 SkRect storage;
1576 const SkRect* bounds = &dst;
1577 if (paint) {
1578 bounds = &paint->computeFastBounds(dst, &storage);
1579 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001580 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001581 return;
1582 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001583 }
reed@google.com3d608122011-11-21 15:16:16 +00001584
reed@google.com33535f32012-09-25 15:37:50 +00001585 SkLazyPaint lazy;
1586 if (NULL == paint) {
1587 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001588 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001589
reed@google.com33535f32012-09-25 15:37:50 +00001590 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001591
reed@google.com33535f32012-09-25 15:37:50 +00001592 while (iter.next()) {
1593 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint());
reed@android.comf2b98d62010-12-20 18:26:13 +00001594 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001595
reed@google.com33535f32012-09-25 15:37:50 +00001596 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001597}
1598
reed@google.com71121732012-09-18 15:14:33 +00001599void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
1600 const SkRect& dst, const SkPaint* paint) {
reed@google.com9987ec32011-09-07 11:57:52 +00001601 SkDEBUGCODE(bitmap.validate();)
1602 this->internalDrawBitmapRect(bitmap, src, dst, paint);
1603}
1604
reed@android.com8a1c16f2008-12-17 15:59:43 +00001605void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1606 const SkPaint* paint) {
1607 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001608 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001609}
1610
reed@android.comf2b98d62010-12-20 18:26:13 +00001611void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1612 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001613 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001614
reed@google.com4e2b3d32011-04-07 14:18:59 +00001615 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001616
reed@android.com8a1c16f2008-12-17 15:59:43 +00001617 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001618 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001619 }
reed@android.com9b039062009-02-11 15:09:58 +00001620
reed@google.com4e2b3d32011-04-07 14:18:59 +00001621 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001622}
1623
reed@google.com9987ec32011-09-07 11:57:52 +00001624void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1625 const SkIRect& center, const SkRect& dst,
1626 const SkPaint* paint) {
reed@google.com3d608122011-11-21 15:16:16 +00001627 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001628 SkRect storage;
1629 const SkRect* bounds = &dst;
1630 if (paint) {
1631 bounds = &paint->computeFastBounds(dst, &storage);
1632 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001633 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001634 return;
1635 }
1636 }
1637
reed@google.com9987ec32011-09-07 11:57:52 +00001638 const int32_t w = bitmap.width();
1639 const int32_t h = bitmap.height();
1640
1641 SkIRect c = center;
1642 // pin center to the bounds of the bitmap
1643 c.fLeft = SkMax32(0, center.fLeft);
1644 c.fTop = SkMax32(0, center.fTop);
1645 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1646 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1647
reed@google.com71121732012-09-18 15:14:33 +00001648 const SkScalar srcX[4] = {
1649 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), w
1650 };
1651 const SkScalar srcY[4] = {
1652 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), h
1653 };
reed@google.com9987ec32011-09-07 11:57:52 +00001654 SkScalar dstX[4] = {
1655 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1656 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1657 };
1658 SkScalar dstY[4] = {
1659 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1660 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1661 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001662
reed@google.com9987ec32011-09-07 11:57:52 +00001663 if (dstX[1] > dstX[2]) {
1664 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1665 dstX[2] = dstX[1];
1666 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001667
reed@google.com9987ec32011-09-07 11:57:52 +00001668 if (dstY[1] > dstY[2]) {
1669 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1670 dstY[2] = dstY[1];
1671 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001672
reed@google.com9987ec32011-09-07 11:57:52 +00001673 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00001674 SkRect s, d;
1675
reed@google.com9987ec32011-09-07 11:57:52 +00001676 s.fTop = srcY[y];
1677 s.fBottom = srcY[y+1];
1678 d.fTop = dstY[y];
1679 d.fBottom = dstY[y+1];
1680 for (int x = 0; x < 3; x++) {
1681 s.fLeft = srcX[x];
1682 s.fRight = srcX[x+1];
1683 d.fLeft = dstX[x];
1684 d.fRight = dstX[x+1];
1685 this->internalDrawBitmapRect(bitmap, &s, d, paint);
1686 }
1687 }
1688}
1689
1690void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1691 const SkRect& dst, const SkPaint* paint) {
1692 SkDEBUGCODE(bitmap.validate();)
1693
1694 // Need a device entry-point, so gpu can use a mesh
1695 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1696}
1697
reed@google.comf67e4cf2011-03-15 20:56:58 +00001698class SkDeviceFilteredPaint {
1699public:
1700 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1701 SkDevice::TextFlags flags;
1702 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001703 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001704 newPaint->setFlags(flags.fFlags);
1705 newPaint->setHinting(flags.fHinting);
1706 fPaint = newPaint;
1707 } else {
1708 fPaint = &paint;
1709 }
1710 }
1711
reed@google.comf67e4cf2011-03-15 20:56:58 +00001712 const SkPaint& paint() const { return *fPaint; }
1713
1714private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001715 const SkPaint* fPaint;
1716 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001717};
1718
bungeman@google.com52c748b2011-08-22 21:30:43 +00001719void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1720 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001721 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001722 draw.fDevice->drawRect(draw, r, paint);
1723 } else {
1724 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001725 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001726 draw.fDevice->drawRect(draw, r, p);
1727 }
1728}
1729
1730void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1731 const char text[], size_t byteLength,
1732 SkScalar x, SkScalar y) {
1733 SkASSERT(byteLength == 0 || text != NULL);
1734
1735 // nothing to draw
1736 if (text == NULL || byteLength == 0 ||
1737 draw.fClip->isEmpty() ||
1738 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1739 return;
1740 }
1741
1742 SkScalar width = 0;
1743 SkPoint start;
1744
1745 start.set(0, 0); // to avoid warning
1746 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1747 SkPaint::kStrikeThruText_Flag)) {
1748 width = paint.measureText(text, byteLength);
1749
1750 SkScalar offsetX = 0;
1751 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1752 offsetX = SkScalarHalf(width);
1753 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1754 offsetX = width;
1755 }
1756 start.set(x - offsetX, y);
1757 }
1758
1759 if (0 == width) {
1760 return;
1761 }
1762
1763 uint32_t flags = paint.getFlags();
1764
1765 if (flags & (SkPaint::kUnderlineText_Flag |
1766 SkPaint::kStrikeThruText_Flag)) {
1767 SkScalar textSize = paint.getTextSize();
1768 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1769 SkRect r;
1770
1771 r.fLeft = start.fX;
1772 r.fRight = start.fX + width;
1773
1774 if (flags & SkPaint::kUnderlineText_Flag) {
1775 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1776 start.fY);
1777 r.fTop = offset;
1778 r.fBottom = offset + height;
1779 DrawRect(draw, paint, r, textSize);
1780 }
1781 if (flags & SkPaint::kStrikeThruText_Flag) {
1782 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1783 start.fY);
1784 r.fTop = offset;
1785 r.fBottom = offset + height;
1786 DrawRect(draw, paint, r, textSize);
1787 }
1788 }
1789}
1790
reed@android.com8a1c16f2008-12-17 15:59:43 +00001791void SkCanvas::drawText(const void* text, size_t byteLength,
1792 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001793 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001794
1795 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001796 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001797 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001798 DrawTextDecorations(iter, dfp.paint(),
1799 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001800 }
1801
reed@google.com4e2b3d32011-04-07 14:18:59 +00001802 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001803}
1804
1805void SkCanvas::drawPosText(const void* text, size_t byteLength,
1806 const SkPoint pos[], const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001807 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001808
reed@android.com8a1c16f2008-12-17 15:59:43 +00001809 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001810 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001811 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001812 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001813 }
reed@google.com4b226022011-01-11 18:32:13 +00001814
reed@google.com4e2b3d32011-04-07 14:18:59 +00001815 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001816}
1817
1818void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1819 const SkScalar xpos[], SkScalar constY,
1820 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001821 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001822
reed@android.com8a1c16f2008-12-17 15:59:43 +00001823 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001824 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001825 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001826 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001827 }
reed@google.com4b226022011-01-11 18:32:13 +00001828
reed@google.com4e2b3d32011-04-07 14:18:59 +00001829 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001830}
1831
1832void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1833 const SkPath& path, const SkMatrix* matrix,
1834 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001835 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001836
1837 while (iter.next()) {
1838 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001839 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001840 }
1841
reed@google.com4e2b3d32011-04-07 14:18:59 +00001842 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001843}
1844
djsollen@google.com56c69772011-11-08 19:00:26 +00001845#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001846void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1847 const SkPoint pos[], const SkPaint& paint,
1848 const SkPath& path, const SkMatrix* matrix) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001849 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001850
1851 while (iter.next()) {
1852 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001853 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001854 }
1855
reed@google.com4e2b3d32011-04-07 14:18:59 +00001856 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001857}
1858#endif
1859
reed@android.com8a1c16f2008-12-17 15:59:43 +00001860void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1861 const SkPoint verts[], const SkPoint texs[],
1862 const SkColor colors[], SkXfermode* xmode,
1863 const uint16_t indices[], int indexCount,
1864 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001865 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001866
reed@android.com8a1c16f2008-12-17 15:59:43 +00001867 while (iter.next()) {
1868 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001869 colors, xmode, indices, indexCount,
1870 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001871 }
reed@google.com4b226022011-01-11 18:32:13 +00001872
reed@google.com4e2b3d32011-04-07 14:18:59 +00001873 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001874}
1875
reed@android.comcb608442009-12-04 21:32:27 +00001876void SkCanvas::drawData(const void* data, size_t length) {
1877 // do nothing. Subclasses may do something with the data
1878}
1879
reed@android.com8a1c16f2008-12-17 15:59:43 +00001880//////////////////////////////////////////////////////////////////////////////
1881// These methods are NOT virtual, and therefore must call back into virtual
1882// methods, rather than actually drawing themselves.
1883//////////////////////////////////////////////////////////////////////////////
1884
1885void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001886 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001887 SkPaint paint;
1888
1889 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001890 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001891 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001892 }
1893 this->drawPaint(paint);
1894}
1895
reed@android.com845fdac2009-06-23 03:01:32 +00001896void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001897 SkPaint paint;
1898
1899 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001900 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001901 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001902 }
1903 this->drawPaint(paint);
1904}
1905
1906void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1907 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001908
reed@android.com8a1c16f2008-12-17 15:59:43 +00001909 pt.set(x, y);
1910 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1911}
1912
1913void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1914 SkPoint pt;
1915 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001916
reed@android.com8a1c16f2008-12-17 15:59:43 +00001917 pt.set(x, y);
1918 paint.setColor(color);
1919 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1920}
1921
1922void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1923 const SkPaint& paint) {
1924 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001925
reed@android.com8a1c16f2008-12-17 15:59:43 +00001926 pts[0].set(x0, y0);
1927 pts[1].set(x1, y1);
1928 this->drawPoints(kLines_PointMode, 2, pts, paint);
1929}
1930
1931void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1932 SkScalar right, SkScalar bottom,
1933 const SkPaint& paint) {
1934 SkRect r;
1935
1936 r.set(left, top, right, bottom);
1937 this->drawRect(r, paint);
1938}
1939
1940void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1941 const SkPaint& paint) {
1942 if (radius < 0) {
1943 radius = 0;
1944 }
1945
1946 SkRect r;
1947 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001948 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001949}
1950
1951void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1952 const SkPaint& paint) {
1953 if (rx > 0 && ry > 0) {
1954 if (paint.canComputeFastBounds()) {
1955 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001956 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001957 return;
1958 }
1959 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00001960 SkRRect rrect;
1961 rrect.setRectXY(r, rx, ry);
1962 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001963 } else {
1964 this->drawRect(r, paint);
1965 }
1966}
1967
reed@android.com8a1c16f2008-12-17 15:59:43 +00001968void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1969 SkScalar sweepAngle, bool useCenter,
1970 const SkPaint& paint) {
1971 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1972 this->drawOval(oval, paint);
1973 } else {
1974 SkPath path;
1975 if (useCenter) {
1976 path.moveTo(oval.centerX(), oval.centerY());
1977 }
1978 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1979 if (useCenter) {
1980 path.close();
1981 }
1982 this->drawPath(path, paint);
1983 }
1984}
1985
1986void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1987 const SkPath& path, SkScalar hOffset,
1988 SkScalar vOffset, const SkPaint& paint) {
1989 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001990
reed@android.com8a1c16f2008-12-17 15:59:43 +00001991 matrix.setTranslate(hOffset, vOffset);
1992 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1993}
1994
reed@android.comf76bacf2009-05-13 14:00:33 +00001995///////////////////////////////////////////////////////////////////////////////
1996
reed@android.com8a1c16f2008-12-17 15:59:43 +00001997void SkCanvas::drawPicture(SkPicture& picture) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001998 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001999}
2000
reed@android.com8a1c16f2008-12-17 15:59:43 +00002001///////////////////////////////////////////////////////////////////////////////
2002///////////////////////////////////////////////////////////////////////////////
2003
2004SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002005 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002006
2007 SkASSERT(canvas);
2008
2009 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2010 fDone = !fImpl->next();
2011}
2012
2013SkCanvas::LayerIter::~LayerIter() {
2014 fImpl->~SkDrawIter();
2015}
2016
2017void SkCanvas::LayerIter::next() {
2018 fDone = !fImpl->next();
2019}
2020
2021SkDevice* SkCanvas::LayerIter::device() const {
2022 return fImpl->getDevice();
2023}
2024
2025const SkMatrix& SkCanvas::LayerIter::matrix() const {
2026 return fImpl->getMatrix();
2027}
2028
2029const SkPaint& SkCanvas::LayerIter::paint() const {
2030 const SkPaint* paint = fImpl->getPaint();
2031 if (NULL == paint) {
2032 paint = &fDefaultPaint;
2033 }
2034 return *paint;
2035}
2036
2037const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2038int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2039int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002040
2041///////////////////////////////////////////////////////////////////////////////
2042
2043SkCanvas::ClipVisitor::~ClipVisitor() { }
reed@google.com97af1a62012-08-28 12:19:02 +00002044
2045