blob: 57f11df9f202d4a43996b28ee9d3b2e65d941b10 [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"
13#include "SkDraw.h"
14#include "SkDrawFilter.h"
15#include "SkDrawLooper.h"
16#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000017#include "SkRasterClip.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000018#include "SkScalarCompare.h"
19#include "SkTemplates.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000020#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000021#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000022#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000023
reed@google.com82ce2b82012-06-26 17:43:26 +000024SK_DEFINE_INST_COUNT(SkBounder)
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000025SK_DEFINE_INST_COUNT(SkCanvas)
reed@google.com82ce2b82012-06-26 17:43:26 +000026SK_DEFINE_INST_COUNT(SkDrawFilter)
27
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000028
reed@android.com8a1c16f2008-12-17 15:59:43 +000029//#define SK_TRACE_SAVERESTORE
30
31#ifdef SK_TRACE_SAVERESTORE
32 static int gLayerCounter;
33 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
34 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
35
36 static int gRecCounter;
37 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
38 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
39
40 static int gCanvasCounter;
41 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
42 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
43#else
44 #define inc_layer()
45 #define dec_layer()
46 #define inc_rec()
47 #define dec_rec()
48 #define inc_canvas()
49 #define dec_canvas()
50#endif
51
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000052typedef SkTLazy<SkPaint> SkLazyPaint;
53
reed@android.com8a1c16f2008-12-17 15:59:43 +000054///////////////////////////////////////////////////////////////////////////////
55// Helpers for computing fast bounds for quickReject tests
56
57static SkCanvas::EdgeType paint2EdgeType(const SkPaint* paint) {
58 return paint != NULL && paint->isAntiAlias() ?
59 SkCanvas::kAA_EdgeType : SkCanvas::kBW_EdgeType;
60}
61
62///////////////////////////////////////////////////////////////////////////////
63
64/* This is the record we keep for each SkDevice that the user installs.
65 The clip/matrix/proc are fields that reflect the top of the save/restore
66 stack. Whenever the canvas changes, it marks a dirty flag, and then before
67 these are used (assuming we're not on a layer) we rebuild these cache
68 values: they reflect the top of the save stack, but translated and clipped
69 by the device's XY offset and bitmap-bounds.
70*/
71struct DeviceCM {
72 DeviceCM* fNext;
73 SkDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +000074 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +000075 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +000076 SkPaint* fPaint; // may be null (in the future)
reed@android.comf2b98d62010-12-20 18:26:13 +000077 // optional, related to canvas' external matrix
78 const SkMatrix* fMVMatrix;
79 const SkMatrix* fExtMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +000080
robertphillips@google.com40a1ae42012-07-13 15:36:15 +000081 DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas)
reed@android.com8a1c16f2008-12-17 15:59:43 +000082 : fNext(NULL) {
83 if (NULL != device) {
84 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +000085 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +000086 }
reed@google.com4b226022011-01-11 18:32:13 +000087 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +000088 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +000089 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000090
bungeman@google.com88edf1e2011-08-08 19:41:56 +000091 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000092 if (NULL != fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +000093 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +000094 fDevice->unref();
95 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +000096 SkDELETE(fPaint);
97 }
reed@google.com4b226022011-01-11 18:32:13 +000098
reed@google.com045e62d2011-10-24 12:19:46 +000099 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
100 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000101 int x = fDevice->getOrigin().x();
102 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000103 int width = fDevice->width();
104 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000105
reed@android.com8a1c16f2008-12-17 15:59:43 +0000106 if ((x | y) == 0) {
107 fMatrix = &totalMatrix;
108 fClip = totalClip;
109 } else {
110 fMatrixStorage = totalMatrix;
111 fMatrixStorage.postTranslate(SkIntToScalar(-x),
112 SkIntToScalar(-y));
113 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000114
reed@android.com8a1c16f2008-12-17 15:59:43 +0000115 totalClip.translate(-x, -y, &fClip);
116 }
117
reed@google.com045e62d2011-10-24 12:19:46 +0000118 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000119
120 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000121
reed@android.com8a1c16f2008-12-17 15:59:43 +0000122 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000123 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000124 SkRegion::kDifference_Op);
125 }
reed@google.com4b226022011-01-11 18:32:13 +0000126
reed@google.com045e62d2011-10-24 12:19:46 +0000127 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000128
129#ifdef SK_DEBUG
130 if (!fClip.isEmpty()) {
131 SkIRect deviceR;
132 deviceR.set(0, 0, width, height);
133 SkASSERT(deviceR.contains(fClip.getBounds()));
134 }
135#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000136 // default is to assume no external matrix
137 fMVMatrix = NULL;
138 fExtMatrix = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000139 }
reed@android.comf2b98d62010-12-20 18:26:13 +0000140
141 // can only be called after calling updateMC()
142 void updateExternalMatrix(const SkMatrix& extM, const SkMatrix& extI) {
143 fMVMatrixStorage.setConcat(extI, *fMatrix);
144 fMVMatrix = &fMVMatrixStorage;
145 fExtMatrix = &extM; // assumes extM has long life-time (owned by canvas)
146 }
147
reed@android.com8a1c16f2008-12-17 15:59:43 +0000148private:
reed@android.comf2b98d62010-12-20 18:26:13 +0000149 SkMatrix fMatrixStorage, fMVMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000150};
151
152/* This is the record we keep for each save/restore level in the stack.
153 Since a level optionally copies the matrix and/or stack, we have pointers
154 for these fields. If the value is copied for this level, the copy is
155 stored in the ...Storage field, and the pointer points to that. If the
156 value is not copied for this level, we ignore ...Storage, and just point
157 at the corresponding value in the previous level in the stack.
158*/
159class SkCanvas::MCRec {
160public:
161 MCRec* fNext;
reed@google.com00177082011-10-12 14:34:30 +0000162 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
163 SkRasterClip* fRasterClip; // points to either fRegionStorage or prev MCRec
164 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000165
reed@android.com8a1c16f2008-12-17 15:59:43 +0000166 DeviceCM* fLayer;
167 /* If there are any layers in the stack, this points to the top-most
168 one that is at or below this level in the stack (so we know what
169 bitmap/device to draw into from this level. This value is NOT
170 reference counted, since the real owner is either our fLayer field,
171 or a previous one in a lower level.)
172 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000173 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000174
175 MCRec(const MCRec* prev, int flags) {
176 if (NULL != prev) {
177 if (flags & SkCanvas::kMatrix_SaveFlag) {
178 fMatrixStorage = *prev->fMatrix;
179 fMatrix = &fMatrixStorage;
180 } else {
181 fMatrix = prev->fMatrix;
182 }
reed@google.com4b226022011-01-11 18:32:13 +0000183
reed@android.com8a1c16f2008-12-17 15:59:43 +0000184 if (flags & SkCanvas::kClip_SaveFlag) {
reed@google.com00177082011-10-12 14:34:30 +0000185 fRasterClipStorage = *prev->fRasterClip;
186 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000187 } else {
reed@google.com00177082011-10-12 14:34:30 +0000188 fRasterClip = prev->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000189 }
190
191 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000192 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000193
194 fTopLayer = prev->fTopLayer;
195 } else { // no prev
196 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000197
reed@android.com8a1c16f2008-12-17 15:59:43 +0000198 fMatrix = &fMatrixStorage;
reed@google.com00177082011-10-12 14:34:30 +0000199 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000200 fFilter = NULL;
201 fTopLayer = NULL;
202 }
203 fLayer = NULL;
204
205 // don't bother initializing fNext
206 inc_rec();
207 }
208 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000209 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000210 SkDELETE(fLayer);
211 dec_rec();
212 }
reed@google.com4b226022011-01-11 18:32:13 +0000213
reed@android.com8a1c16f2008-12-17 15:59:43 +0000214private:
reed@google.com00177082011-10-12 14:34:30 +0000215 SkMatrix fMatrixStorage;
216 SkRasterClip fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217};
218
219class SkDrawIter : public SkDraw {
220public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000221 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000222 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000223 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000224 canvas->updateDeviceCMCache();
225
reed@google.com90c07ea2012-04-13 13:50:27 +0000226 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000227 fBounder = canvas->getBounder();
228 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000229 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000230 }
reed@google.com4b226022011-01-11 18:32:13 +0000231
reed@android.com8a1c16f2008-12-17 15:59:43 +0000232 bool next() {
233 // skip over recs with empty clips
234 if (fSkipEmptyClips) {
235 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
236 fCurrLayer = fCurrLayer->fNext;
237 }
238 }
239
reed@google.comf68c5e22012-02-24 16:38:58 +0000240 const DeviceCM* rec = fCurrLayer;
241 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242
243 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000244 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
245 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000246 fDevice = rec->fDevice;
247 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000249 fMVMatrix = rec->fMVMatrix;
250 fExtMatrix = rec->fExtMatrix;
251 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252
253 fCurrLayer = rec->fNext;
254 if (fBounder) {
255 fBounder->setClip(fClip);
256 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000258
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000259 fCanvas->prepareForDeviceDraw(fDevice, *fMatrix, *fClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260 return true;
261 }
262 return false;
263 }
reed@google.com4b226022011-01-11 18:32:13 +0000264
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265 SkDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000266 int getX() const { return fDevice->getOrigin().x(); }
267 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000268 const SkMatrix& getMatrix() const { return *fMatrix; }
269 const SkRegion& getClip() const { return *fClip; }
270 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000271
reed@android.com8a1c16f2008-12-17 15:59:43 +0000272private:
273 SkCanvas* fCanvas;
274 const DeviceCM* fCurrLayer;
275 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000276 SkBool8 fSkipEmptyClips;
277
278 typedef SkDraw INHERITED;
279};
280
281/////////////////////////////////////////////////////////////////////////////
282
283class AutoDrawLooper {
284public:
reed@google.com8926b162012-03-23 15:36:36 +0000285 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint,
286 bool skipLayerForImageFilter = false) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000287 fCanvas = canvas;
288 fLooper = paint.getLooper();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000289 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000290 fPaint = NULL;
291 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000292 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000293 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000294
reed@google.com8926b162012-03-23 15:36:36 +0000295 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
296 SkPaint tmp;
297 tmp.setImageFilter(fOrigPaint.getImageFilter());
298 // it would be nice if we had a guess at the bounds, instead of null
299 (void)canvas->internalSaveLayer(NULL, &tmp,
300 SkCanvas::kARGB_ClipLayer_SaveFlag, true);
301 // we'll clear the imageFilter for the actual draws in next(), so
302 // it will only be applied during the restore().
303 fDoClearImageFilter = true;
304 }
305
reed@google.com4e2b3d32011-04-07 14:18:59 +0000306 if (fLooper) {
307 fLooper->init(canvas);
reed@google.com129ec222012-05-15 13:24:09 +0000308 fIsSimple = false;
309 } else {
310 // can we be marked as simple?
311 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000312 }
313 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000314
reed@android.com8a1c16f2008-12-17 15:59:43 +0000315 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000316 if (fDoClearImageFilter) {
317 fCanvas->internalRestore();
318 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000319 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000320 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000321
reed@google.com4e2b3d32011-04-07 14:18:59 +0000322 const SkPaint& paint() const {
323 SkASSERT(fPaint);
324 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000325 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000326
reed@google.com129ec222012-05-15 13:24:09 +0000327 bool next(SkDrawFilter::Type drawType) {
328 if (fDone) {
329 return false;
330 } else if (fIsSimple) {
331 fDone = true;
332 fPaint = &fOrigPaint;
333 return !fPaint->nothingToDraw();
334 } else {
335 return this->doNext(drawType);
336 }
337 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000338
reed@android.com8a1c16f2008-12-17 15:59:43 +0000339private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000340 SkLazyPaint fLazyPaint;
341 SkCanvas* fCanvas;
342 const SkPaint& fOrigPaint;
343 SkDrawLooper* fLooper;
344 SkDrawFilter* fFilter;
345 const SkPaint* fPaint;
346 int fSaveCount;
reed@google.com8926b162012-03-23 15:36:36 +0000347 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000348 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000349 bool fIsSimple;
350
351 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000352};
353
reed@google.com129ec222012-05-15 13:24:09 +0000354bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000355 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000356 SkASSERT(!fIsSimple);
357 SkASSERT(fLooper || fFilter || fDoClearImageFilter);
358
359 SkPaint* paint = fLazyPaint.set(fOrigPaint);
360
361 if (fDoClearImageFilter) {
362 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000363 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000364
reed@google.com129ec222012-05-15 13:24:09 +0000365 if (fLooper && !fLooper->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000366 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000367 return false;
368 }
369 if (fFilter) {
370 fFilter->filter(paint, drawType);
371 if (NULL == fLooper) {
372 // no looper means we only draw once
373 fDone = true;
374 }
375 }
376 fPaint = paint;
377
378 // if we only came in here for the imagefilter, mark us as done
379 if (!fLooper && !fFilter) {
380 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000381 }
382
383 // call this after any possible paint modifiers
384 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000385 fPaint = NULL;
386 return false;
387 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000388 return true;
389}
390
reed@android.com8a1c16f2008-12-17 15:59:43 +0000391/* Stack helper for managing a SkBounder. In the destructor, if we were
392 given a bounder, we call its commit() method, signifying that we are
393 done accumulating bounds for that draw.
394*/
395class SkAutoBounderCommit {
396public:
397 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
398 ~SkAutoBounderCommit() {
399 if (NULL != fBounder) {
400 fBounder->commit();
401 }
402 }
403private:
404 SkBounder* fBounder;
405};
406
407#include "SkColorPriv.h"
408
409class AutoValidator {
410public:
411 AutoValidator(SkDevice* device) : fDevice(device) {}
412 ~AutoValidator() {
413#ifdef SK_DEBUG
414 const SkBitmap& bm = fDevice->accessBitmap(false);
415 if (bm.config() == SkBitmap::kARGB_4444_Config) {
416 for (int y = 0; y < bm.height(); y++) {
417 const SkPMColor16* p = bm.getAddr16(0, y);
418 for (int x = 0; x < bm.width(); x++) {
419 SkPMColor16 c = p[x];
420 SkPMColor16Assert(c);
421 }
422 }
423 }
424#endif
425 }
426private:
427 SkDevice* fDevice;
428};
429
430////////// macros to place around the internal draw calls //////////////////
431
reed@google.com8926b162012-03-23 15:36:36 +0000432#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
433/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
434 AutoDrawLooper looper(this, paint, true); \
435 while (looper.next(type)) { \
436 SkAutoBounderCommit ac(fBounder); \
437 SkDrawIter iter(this);
438
reed@google.com4e2b3d32011-04-07 14:18:59 +0000439#define LOOPER_BEGIN(paint, type) \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000440/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000441 AutoDrawLooper looper(this, paint); \
442 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000443 SkAutoBounderCommit ac(fBounder); \
444 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000445
reed@google.com4e2b3d32011-04-07 14:18:59 +0000446#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000447
448////////////////////////////////////////////////////////////////////////////
449
450SkDevice* SkCanvas::init(SkDevice* device) {
451 fBounder = NULL;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000452 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000453 fLocalBoundsCompareTypeDirty = true;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000454 fLocalBoundsCompareTypeBW.setEmpty();
reed@android.comba09de42010-02-05 20:46:05 +0000455 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com199f1082009-06-10 02:12:47 +0000456 fLastDeviceToGainFocus = NULL;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000457 fDeviceCMDirty = false;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000458 fSaveLayerCount = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000459
460 fMCRec = (MCRec*)fMCStack.push_back();
461 new (fMCRec) MCRec(NULL, 0);
462
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000463 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000464 fMCRec->fTopLayer = fMCRec->fLayer;
465 fMCRec->fNext = NULL;
466
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000467 fExternalMatrix.reset();
468 fExternalInverse.reset();
reed@android.comf2b98d62010-12-20 18:26:13 +0000469 fUseExternalMatrix = false;
470
reed@android.com8a1c16f2008-12-17 15:59:43 +0000471 return this->setDevice(device);
472}
473
reed@google.comcde92112011-07-06 20:00:52 +0000474SkCanvas::SkCanvas()
475: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000476 inc_canvas();
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000477
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000478 this->init(NULL);
479}
480
reed@android.com8a1c16f2008-12-17 15:59:43 +0000481SkCanvas::SkCanvas(SkDevice* device)
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000482 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000483 inc_canvas();
484
485 this->init(device);
486}
487
488SkCanvas::SkCanvas(const SkBitmap& bitmap)
489 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
490 inc_canvas();
491
reed@google.comcde92112011-07-06 20:00:52 +0000492 this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000493}
494
495SkCanvas::~SkCanvas() {
496 // free up the contents of our deque
497 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000498 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000499
reed@android.com8a1c16f2008-12-17 15:59:43 +0000500 this->internalRestore(); // restore the last, since we're going away
501
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000502 SkSafeUnref(fBounder);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000503
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504 dec_canvas();
505}
506
507SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
508 SkRefCnt_SafeAssign(fBounder, bounder);
509 return bounder;
510}
511
512SkDrawFilter* SkCanvas::getDrawFilter() const {
513 return fMCRec->fFilter;
514}
515
516SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
517 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
518 return filter;
519}
520
521///////////////////////////////////////////////////////////////////////////////
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
reed@google.comaf951c92011-06-16 19:10:39 +0000601SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) {
602 SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (bitmap)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000603 device->unref();
604 return device;
605}
606
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000607bool SkCanvas::readPixels(SkBitmap* bitmap,
608 int x, int y,
609 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000610 SkDevice* device = this->getDevice();
611 if (!device) {
612 return false;
613 }
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000614 return device->readPixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000615}
616
bsalomon@google.comc6980972011-11-02 19:57:21 +0000617bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
bsalomon@google.comace7bd52011-11-02 19:39:51 +0000618 SkDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000619 if (!device) {
620 return false;
621 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000622
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000623 SkIRect bounds;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000624 bounds.set(0, 0, device->width(), device->height());
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000625 if (!bounds.intersect(srcRect)) {
626 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000627 }
628
629 SkBitmap tmp;
630 tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
631 bounds.height());
632 if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
633 bitmap->swap(tmp);
634 return true;
635 } else {
reed@google.com51df9e32010-12-23 19:29:18 +0000636 return false;
637 }
reed@google.com51df9e32010-12-23 19:29:18 +0000638}
639
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000640void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
641 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000642 SkDevice* device = this->getDevice();
643 if (device) {
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000644 device->writePixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000645 }
646}
647
junov@google.com4370aed2012-01-18 16:21:08 +0000648SkCanvas* SkCanvas::canvasForDrawIter() {
649 return this;
650}
651
reed@android.com8a1c16f2008-12-17 15:59:43 +0000652//////////////////////////////////////////////////////////////////////////////
653
reed@android.com8a1c16f2008-12-17 15:59:43 +0000654void SkCanvas::updateDeviceCMCache() {
655 if (fDeviceCMDirty) {
656 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000657 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000658 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000659
reed@android.com8a1c16f2008-12-17 15:59:43 +0000660 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000661 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000662 if (fUseExternalMatrix) {
663 layer->updateExternalMatrix(fExternalMatrix,
664 fExternalInverse);
665 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000666 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000667 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000668 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000669 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.comf2b98d62010-12-20 18:26:13 +0000670 if (fUseExternalMatrix) {
671 layer->updateExternalMatrix(fExternalMatrix,
672 fExternalInverse);
673 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000674 } while ((layer = layer->fNext) != NULL);
675 }
676 fDeviceCMDirty = false;
677 }
678}
679
reed@android.comf2b98d62010-12-20 18:26:13 +0000680void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000681 const SkRegion& clip) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000682 SkASSERT(device);
reed@android.com199f1082009-06-10 02:12:47 +0000683 if (fLastDeviceToGainFocus != device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000684 device->gainFocus(matrix, clip);
reed@android.com199f1082009-06-10 02:12:47 +0000685 fLastDeviceToGainFocus = device;
686 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000687}
688
689///////////////////////////////////////////////////////////////////////////////
690
691int SkCanvas::internalSave(SaveFlags flags) {
692 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000693
reed@android.com8a1c16f2008-12-17 15:59:43 +0000694 MCRec* newTop = (MCRec*)fMCStack.push_back();
695 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000696
reed@android.com8a1c16f2008-12-17 15:59:43 +0000697 newTop->fNext = fMCRec;
698 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000699
reed@google.com5c3d1472011-02-22 19:12:23 +0000700 fClipStack.save();
701 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
702
reed@android.com8a1c16f2008-12-17 15:59:43 +0000703 return saveCount;
704}
705
706int SkCanvas::save(SaveFlags flags) {
707 // call shared impl
708 return this->internalSave(flags);
709}
710
711#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
712#define C16MASK (1 << SkBitmap::kRGB_565_Config)
713#define C8MASK (1 << SkBitmap::kA8_Config)
714
715static SkBitmap::Config resolve_config(SkCanvas* canvas,
716 const SkIRect& bounds,
717 SkCanvas::SaveFlags flags,
718 bool* isOpaque) {
719 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
720
721#if 0
722 // loop through and union all the configs we may draw into
723 uint32_t configMask = 0;
724 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
725 {
726 SkDevice* device = canvas->getLayerDevice(i);
727 if (device->intersects(bounds))
728 configMask |= 1 << device->config();
729 }
730
731 // if the caller wants alpha or fullcolor, we can't return 565
732 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
733 SkCanvas::kHasAlphaLayer_SaveFlag))
734 configMask &= ~C16MASK;
735
736 switch (configMask) {
737 case C8MASK: // if we only have A8, return that
738 return SkBitmap::kA8_Config;
739
740 case C16MASK: // if we only have 565, return that
741 return SkBitmap::kRGB_565_Config;
742
743 default:
744 return SkBitmap::kARGB_8888_Config; // default answer
745 }
746#else
747 return SkBitmap::kARGB_8888_Config; // default answer
748#endif
749}
750
751static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
752 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
753}
754
junov@chromium.orga907ac32012-02-24 21:54:07 +0000755bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
756 SkIRect* intersection) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000757 SkIRect clipBounds;
758 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000759 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000760 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000761 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000762 if (NULL != bounds) {
763 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000764
reed@android.com8a1c16f2008-12-17 15:59:43 +0000765 this->getTotalMatrix().mapRect(&r, *bounds);
766 r.roundOut(&ir);
767 // early exit if the layer's bounds are clipped out
768 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000769 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000770 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000771 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000772 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000773 }
774 } else { // no user bounds, so just use the clip
775 ir = clipBounds;
776 }
777
reed@google.com5c3d1472011-02-22 19:12:23 +0000778 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000779
reed@android.com8a1c16f2008-12-17 15:59:43 +0000780 // early exit if the clip is now empty
781 if (bounds_affects_clip(flags) &&
reed@google.com00177082011-10-12 14:34:30 +0000782 !fMCRec->fRasterClip->op(ir, SkRegion::kIntersect_Op)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000783 return false;
784 }
785
786 if (intersection) {
787 *intersection = ir;
788 }
789 return true;
790}
791
792int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
793 SaveFlags flags) {
reed@google.com8926b162012-03-23 15:36:36 +0000794 return this->internalSaveLayer(bounds, paint, flags, false);
795}
796
797int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
798 SaveFlags flags, bool justForImageFilter) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000799 // do this before we create the layer. We don't call the public save() since
800 // that would invoke a possibly overridden virtual
801 int count = this->internalSave(flags);
802
803 fDeviceCMDirty = true;
804
805 SkIRect ir;
806 if (!this->clipRectBounds(bounds, flags, &ir)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000807 return count;
808 }
809
reed@google.comb55deeb2012-01-06 14:43:09 +0000810 // Kill the imagefilter if our device doesn't allow it
811 SkLazyPaint lazyP;
812 if (paint && paint->getImageFilter()) {
813 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000814 if (justForImageFilter) {
815 // early exit if the layer was just for the imageFilter
816 return count;
817 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000818 SkPaint* p = lazyP.set(*paint);
819 p->setImageFilter(NULL);
820 paint = p;
821 }
822 }
823
reed@android.com8a1c16f2008-12-17 15:59:43 +0000824 bool isOpaque;
825 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
826
reed@google.com76dd2772012-01-05 21:15:07 +0000827 SkDevice* device;
828 if (paint && paint->getImageFilter()) {
829 device = this->createCompatibleDevice(config, ir.width(), ir.height(),
830 isOpaque);
831 } else {
832 device = this->createLayerDevice(config, ir.width(), ir.height(),
833 isOpaque);
834 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000835 if (NULL == device) {
836 SkDebugf("Unable to create device for layer.");
837 return count;
838 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000839
reed@google.com6f8f2922011-03-04 22:27:10 +0000840 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000841 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000842 device->unref();
843
844 layer->fNext = fMCRec->fTopLayer;
845 fMCRec->fLayer = layer;
846 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
847
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000848 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000849 return count;
850}
851
852int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
853 SaveFlags flags) {
854 if (0xFF == alpha) {
855 return this->saveLayer(bounds, NULL, flags);
856 } else {
857 SkPaint tmpPaint;
858 tmpPaint.setAlpha(alpha);
859 return this->saveLayer(bounds, &tmpPaint, flags);
860 }
861}
862
863void SkCanvas::restore() {
864 // check for underflow
865 if (fMCStack.count() > 1) {
866 this->internalRestore();
867 }
868}
869
870void SkCanvas::internalRestore() {
871 SkASSERT(fMCStack.count() != 0);
872
873 fDeviceCMDirty = true;
874 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000875 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000876
reed@google.com5c3d1472011-02-22 19:12:23 +0000877 fClipStack.restore();
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000878 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000879 DeviceCM* layer = fMCRec->fLayer; // may be null
880 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
881 fMCRec->fLayer = NULL;
882
883 // now do the normal restore()
884 fMCRec->~MCRec(); // balanced in save()
885 fMCStack.pop_back();
886 fMCRec = (MCRec*)fMCStack.back();
887
888 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
889 since if we're being recorded, we don't want to record this (the
890 recorder will have already recorded the restore).
891 */
892 if (NULL != layer) {
893 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000894 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000895 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
896 layer->fPaint);
897 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000898 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000899
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000900 SkASSERT(fSaveLayerCount > 0);
901 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000902 }
903 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000904 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000905
906 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000907}
908
909int SkCanvas::getSaveCount() const {
910 return fMCStack.count();
911}
912
913void SkCanvas::restoreToCount(int count) {
914 // sanity check
915 if (count < 1) {
916 count = 1;
917 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000918
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000919 int n = this->getSaveCount() - count;
920 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000921 this->restore();
922 }
923}
924
reed@google.com7c202932011-12-14 18:48:05 +0000925bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000926 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000927}
928
reed@android.com8a1c16f2008-12-17 15:59:43 +0000929/////////////////////////////////////////////////////////////////////////////
930
931// can't draw it if its empty, or its too big for a fixed-point width or height
932static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000933 return bitmap.width() <= 0 || bitmap.height() <= 0
934#ifndef SK_ALLOW_OVER_32K_BITMAPS
935 || bitmap.width() > 32767 || bitmap.height() > 32767
936#endif
937 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000938}
939
reed@android.comf2b98d62010-12-20 18:26:13 +0000940void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000941 const SkMatrix& matrix, const SkPaint* paint) {
942 if (reject_bitmap(bitmap)) {
943 return;
944 }
945
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000946 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000947 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000948 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000949 }
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000950 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000951}
952
reed@google.com76dd2772012-01-05 21:15:07 +0000953#include "SkImageFilter.h"
954
955class DeviceImageFilterProxy : public SkImageFilter::Proxy {
956public:
957 DeviceImageFilterProxy(SkDevice* device) : fDevice(device) {}
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000958
reed@google.com8926b162012-03-23 15:36:36 +0000959 virtual SkDevice* createDevice(int w, int h) SK_OVERRIDE {
960 return fDevice->createCompatibleDevice(SkBitmap::kARGB_8888_Config,
961 w, h, false);
962 }
963 virtual bool canHandleImageFilter(SkImageFilter* filter) SK_OVERRIDE {
964 return fDevice->canHandleImageFilter(filter);
965 }
966 virtual bool filterImage(SkImageFilter* filter, const SkBitmap& src,
reed@google.com76dd2772012-01-05 21:15:07 +0000967 const SkMatrix& ctm,
reed@google.com8926b162012-03-23 15:36:36 +0000968 SkBitmap* result, SkIPoint* offset) SK_OVERRIDE {
969 return fDevice->filterImage(filter, src, ctm, result, offset);
970 }
971
reed@google.com76dd2772012-01-05 21:15:07 +0000972private:
973 SkDevice* fDevice;
974};
975
reed@google.com8926b162012-03-23 15:36:36 +0000976void SkCanvas::internalDrawDevice(SkDevice* srcDev, int x, int y,
977 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000978 SkPaint tmp;
979 if (NULL == paint) {
980 tmp.setDither(true);
981 paint = &tmp;
982 }
reed@google.com4b226022011-01-11 18:32:13 +0000983
reed@google.com8926b162012-03-23 15:36:36 +0000984 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000985 while (iter.next()) {
reed@google.comb55deeb2012-01-06 14:43:09 +0000986 SkDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +0000987 paint = &looper.paint();
988 SkImageFilter* filter = paint->getImageFilter();
989 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +0000990 if (filter && !dstDev->canHandleImageFilter(filter)) {
reed@google.comb55deeb2012-01-06 14:43:09 +0000991 DeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +0000992 SkBitmap dst;
reed@google.comb55deeb2012-01-06 14:43:09 +0000993 const SkBitmap& src = srcDev->accessBitmap(false);
reed@google.com76dd2772012-01-05 21:15:07 +0000994 if (filter->filterImage(&proxy, src, *iter.fMatrix, &dst, &pos)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +0000995 SkPaint tmpUnfiltered(*paint);
996 tmpUnfiltered.setImageFilter(NULL);
997 dstDev->drawSprite(iter, dst, pos.x(), pos.y(), tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +0000998 }
999 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001000 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001001 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001002 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001003 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001004}
1005
reed@google.com8926b162012-03-23 15:36:36 +00001006void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1007 const SkPaint* paint) {
1008 SkDEBUGCODE(bitmap.validate();)
1009
1010 if (reject_bitmap(bitmap)) {
1011 return;
1012 }
1013
1014 SkPaint tmp;
1015 if (NULL == paint) {
1016 paint = &tmp;
1017 }
1018
1019 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
1020
1021 while (iter.next()) {
1022 paint = &looper.paint();
1023 SkImageFilter* filter = paint->getImageFilter();
1024 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1025 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
1026 DeviceImageFilterProxy proxy(iter.fDevice);
1027 SkBitmap dst;
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001028 if (filter->filterImage(&proxy, bitmap, *iter.fMatrix,
1029 &dst, &pos)) {
1030 SkPaint tmpUnfiltered(*paint);
1031 tmpUnfiltered.setImageFilter(NULL);
1032 iter.fDevice->drawSprite(iter, dst, pos.x(), pos.y(),
1033 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001034 }
1035 } else {
1036 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1037 }
1038 }
1039 LOOPER_END
1040}
1041
reed@android.com8a1c16f2008-12-17 15:59:43 +00001042/////////////////////////////////////////////////////////////////////////////
1043
1044bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
1045 fDeviceCMDirty = true;
1046 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001047 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001048 return fMCRec->fMatrix->preTranslate(dx, dy);
1049}
1050
1051bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
1052 fDeviceCMDirty = true;
1053 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001054 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001055 return fMCRec->fMatrix->preScale(sx, sy);
1056}
1057
1058bool SkCanvas::rotate(SkScalar degrees) {
1059 fDeviceCMDirty = true;
1060 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001061 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001062 return fMCRec->fMatrix->preRotate(degrees);
1063}
1064
1065bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
1066 fDeviceCMDirty = true;
1067 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001068 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001069 return fMCRec->fMatrix->preSkew(sx, sy);
1070}
1071
1072bool SkCanvas::concat(const SkMatrix& matrix) {
1073 fDeviceCMDirty = true;
1074 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001075 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001076 return fMCRec->fMatrix->preConcat(matrix);
1077}
1078
1079void SkCanvas::setMatrix(const SkMatrix& matrix) {
1080 fDeviceCMDirty = true;
1081 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001082 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001083 *fMCRec->fMatrix = matrix;
1084}
1085
1086// this is not virtual, so it must call a virtual method so that subclasses
1087// will see its action
1088void SkCanvas::resetMatrix() {
1089 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001090
reed@android.com8a1c16f2008-12-17 15:59:43 +00001091 matrix.reset();
1092 this->setMatrix(matrix);
1093}
1094
1095//////////////////////////////////////////////////////////////////////////////
1096
reed@google.comc42d35d2011-10-12 11:57:42 +00001097bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001098 AutoValidateClip avc(this);
1099
reed@android.com8a1c16f2008-12-17 15:59:43 +00001100 fDeviceCMDirty = true;
1101 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001102 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001103
1104 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +00001105 // for these simpler matrices, we can stay a rect ever after applying
1106 // the matrix. This means we don't have to a) make a path, and b) tell
1107 // the region code to scan-convert the path, only to discover that it
1108 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001109 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001110
1111 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +00001112 fClipStack.clipDevRect(r, op, doAA);
1113 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001114 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +00001115 // since we're rotate or some such thing, we convert the rect to a path
1116 // and clip against that, since it can handle any matrix. However, to
1117 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1118 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001119 SkPath path;
1120
1121 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +00001122 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001123 }
1124}
1125
reed@google.com00177082011-10-12 14:34:30 +00001126static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +00001127 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001128 // base is used to limit the size (and therefore memory allocation) of the
1129 // region that results from scan converting devPath.
1130 SkRegion base;
1131
reed@google.com819c9212011-02-23 18:56:55 +00001132 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001133 // since we are intersect, we can do better (tighter) with currRgn's
1134 // bounds, than just using the device. However, if currRgn is complex,
1135 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001136 if (currClip->isRect()) {
1137 return currClip->setPath(devPath, *currClip, doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001138 } else {
reed@google.com00177082011-10-12 14:34:30 +00001139 base.setRect(currClip->getBounds());
1140 SkRasterClip clip;
1141 clip.setPath(devPath, base, doAA);
1142 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001143 }
reed@google.com819c9212011-02-23 18:56:55 +00001144 } else {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001145 const SkDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001146 if (!device) {
1147 return currClip->setEmpty();
1148 }
1149
junov@chromium.orga907ac32012-02-24 21:54:07 +00001150 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001151
1152 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +00001153 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001154 } else {
reed@google.com00177082011-10-12 14:34:30 +00001155 SkRasterClip clip;
1156 clip.setPath(devPath, base, doAA);
1157 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001158 }
1159 }
1160}
1161
reed@google.comc42d35d2011-10-12 11:57:42 +00001162bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001163 AutoValidateClip avc(this);
1164
reed@android.com8a1c16f2008-12-17 15:59:43 +00001165 fDeviceCMDirty = true;
1166 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001167 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001168
1169 SkPath devPath;
1170 path.transform(*fMCRec->fMatrix, &devPath);
1171
reed@google.comfe701122011-11-08 19:41:23 +00001172 // Check if the transfomation, or the original path itself
1173 // made us empty. Note this can also happen if we contained NaN
1174 // values. computing the bounds detects this, and will set our
1175 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1176 if (devPath.getBounds().isEmpty()) {
1177 // resetting the path will remove any NaN or other wanky values
1178 // that might upset our scan converter.
1179 devPath.reset();
1180 }
1181
reed@google.com5c3d1472011-02-22 19:12:23 +00001182 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +00001183 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +00001184
reed@google.com00177082011-10-12 14:34:30 +00001185 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001186}
1187
1188bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001189 AutoValidateClip avc(this);
1190
reed@android.com8a1c16f2008-12-17 15:59:43 +00001191 fDeviceCMDirty = true;
1192 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001193 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001194
reed@google.com5c3d1472011-02-22 19:12:23 +00001195 // todo: signal fClipStack that we have a region, and therefore (I guess)
1196 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001197 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001198
reed@google.com00177082011-10-12 14:34:30 +00001199 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001200}
1201
reed@google.com819c9212011-02-23 18:56:55 +00001202#ifdef SK_DEBUG
1203void SkCanvas::validateClip() const {
1204 // construct clipRgn from the clipstack
1205 const SkDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001206 if (!device) {
1207 SkASSERT(this->getTotalClip().isEmpty());
1208 return;
1209 }
1210
reed@google.com819c9212011-02-23 18:56:55 +00001211 SkIRect ir;
1212 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001213 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001214
robertphillips@google.com80214e22012-07-20 15:33:18 +00001215 SkClipStack::B2TIter iter(fClipStack);
1216 const SkClipStack::B2TIter::Clip* clip;
reed@google.com819c9212011-02-23 18:56:55 +00001217 while ((clip = iter.next()) != NULL) {
1218 if (clip->fPath) {
reed@google.com00177082011-10-12 14:34:30 +00001219 clipPathHelper(this, &tmpClip, *clip->fPath, clip->fOp, clip->fDoAA);
reed@google.com819c9212011-02-23 18:56:55 +00001220 } else if (clip->fRect) {
1221 clip->fRect->round(&ir);
reed@google.com00177082011-10-12 14:34:30 +00001222 tmpClip.op(ir, clip->fOp);
reed@google.com819c9212011-02-23 18:56:55 +00001223 } else {
reed@google.com00177082011-10-12 14:34:30 +00001224 tmpClip.setEmpty();
reed@google.com819c9212011-02-23 18:56:55 +00001225 }
1226 }
1227
reed@google.com6f8f2922011-03-04 22:27:10 +00001228#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001229 // now compare against the current rgn
1230 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001231 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001232#endif
reed@google.com819c9212011-02-23 18:56:55 +00001233}
1234#endif
1235
reed@google.com90c07ea2012-04-13 13:50:27 +00001236void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001237 SkClipStack::B2TIter iter(fClipStack);
1238 const SkClipStack::B2TIter::Clip* clip;
reed@google.com90c07ea2012-04-13 13:50:27 +00001239
robertphillips@google.com7460b372012-04-25 16:54:51 +00001240 SkRect empty = { 0, 0, 0, 0 };
reed@google.com90c07ea2012-04-13 13:50:27 +00001241 while ((clip = iter.next()) != NULL) {
1242 if (clip->fPath) {
1243 visitor->clipPath(*clip->fPath, clip->fOp, clip->fDoAA);
1244 } else if (clip->fRect) {
1245 visitor->clipRect(*clip->fRect, clip->fOp, clip->fDoAA);
1246 } else {
1247 visitor->clipRect(empty, SkRegion::kIntersect_Op, false);
1248 }
1249 }
1250}
1251
reed@google.com5c3d1472011-02-22 19:12:23 +00001252///////////////////////////////////////////////////////////////////////////////
1253
reed@android.comba09de42010-02-05 20:46:05 +00001254void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001255 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001256 SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
1257 fLocalBoundsCompareTypeBW;
1258
1259 if (!this->getClipBounds(&r, et)) {
1260 rCompare.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001261 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001262 rCompare.set(SkScalarToCompareType(r.fLeft),
1263 SkScalarToCompareType(r.fTop),
1264 SkScalarToCompareType(r.fRight),
1265 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001266 }
1267}
1268
reed@android.comd252db02009-04-01 18:31:44 +00001269/* current impl ignores edgetype, and relies on
1270 getLocalClipBoundsCompareType(), which always returns a value assuming
1271 antialiasing (worst case)
1272 */
reed@android.comba09de42010-02-05 20:46:05 +00001273bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) 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@android.comba09de42010-02-05 20:46:05 +00001289 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
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
1306bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +00001307 return path.isEmpty() || this->quickReject(path.getBounds(), et);
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@android.com8a1c16f2008-12-17 15:59:43 +00001321bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) 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@android.com8a1c16f2008-12-17 15:59:43 +00001338 // adjust it outwards if we are antialiasing
1339 int inset = (kAA_EdgeType == et);
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),
1348 pinIntForScalar(ibounds.fRight + inset),
1349 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
reed@android.comf2b98d62010-12-20 18:26:13 +00001384void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1385 if (NULL == matrix || matrix->isIdentity()) {
1386 if (fUseExternalMatrix) {
1387 fDeviceCMDirty = true;
1388 }
1389 fUseExternalMatrix = false;
1390 } else {
mike@reedtribe.org90bf4272012-04-14 19:06:16 +00001391 if (matrix->invert(&fExternalInverse)) {
1392 fExternalMatrix = *matrix;
1393 fUseExternalMatrix = true;
1394 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.comfc9a3be2012-04-12 13:52:14 +00001395 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001396 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001397}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001398
bsalomon@google.come97f0852011-06-17 13:10:25 +00001399SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
1400 int width, int height,
1401 bool isOpaque) {
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001402 SkDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001403 if (device) {
1404 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1405 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001406 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001407 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001408 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001409}
1410
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001411SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
bsalomon@google.come97f0852011-06-17 13:10:25 +00001412 int width, int height,
1413 bool isOpaque) {
1414 SkDevice* device = this->getDevice();
1415 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001416 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001417 } else {
1418 return NULL;
1419 }
1420}
1421
1422
reed@android.com8a1c16f2008-12-17 15:59:43 +00001423//////////////////////////////////////////////////////////////////////////////
1424// These are the virtual drawing methods
1425//////////////////////////////////////////////////////////////////////////////
1426
reed@google.com2a981812011-04-14 18:59:28 +00001427void SkCanvas::clear(SkColor color) {
1428 SkDrawIter iter(this);
1429
1430 while (iter.next()) {
1431 iter.fDevice->clear(color);
1432 }
1433}
1434
reed@android.com8a1c16f2008-12-17 15:59:43 +00001435void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001436 this->internalDrawPaint(paint);
1437}
1438
1439void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001440 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001441
1442 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001443 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001444 }
1445
reed@google.com4e2b3d32011-04-07 14:18:59 +00001446 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001447}
1448
1449void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1450 const SkPaint& paint) {
1451 if ((long)count <= 0) {
1452 return;
1453 }
1454
reed@google.coma584aed2012-05-16 14:06:02 +00001455 if (paint.canComputeFastBounds()) {
1456 SkRect r;
1457 // special-case 2 points (common for drawing a single line)
1458 if (2 == count) {
1459 r.set(pts[0], pts[1]);
1460 } else {
1461 r.set(pts, count);
1462 }
1463 SkRect storage;
1464 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage),
1465 paint2EdgeType(&paint))) {
1466 return;
1467 }
1468 }
1469
reed@android.com8a1c16f2008-12-17 15:59:43 +00001470 SkASSERT(pts != NULL);
1471
reed@google.com4e2b3d32011-04-07 14:18:59 +00001472 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001473
reed@android.com8a1c16f2008-12-17 15:59:43 +00001474 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001475 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001476 }
reed@google.com4b226022011-01-11 18:32:13 +00001477
reed@google.com4e2b3d32011-04-07 14:18:59 +00001478 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001479}
1480
1481void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1482 if (paint.canComputeFastBounds()) {
1483 SkRect storage;
1484 if (this->quickReject(paint.computeFastBounds(r, &storage),
1485 paint2EdgeType(&paint))) {
1486 return;
1487 }
1488 }
reed@google.com4b226022011-01-11 18:32:13 +00001489
reed@google.com4e2b3d32011-04-07 14:18:59 +00001490 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001491
1492 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001493 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001494 }
1495
reed@google.com4e2b3d32011-04-07 14:18:59 +00001496 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001497}
1498
1499void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00001500 if (!path.isFinite()) {
1501 return;
1502 }
1503
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001504 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001505 SkRect storage;
1506 const SkRect& bounds = path.getBounds();
1507 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001508 paint2EdgeType(&paint))) {
1509 return;
1510 }
1511 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001512 if (path.isEmpty()) {
1513 if (path.isInverseFillType()) {
1514 this->internalDrawPaint(paint);
1515 }
1516 return;
1517 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001518
reed@google.com4e2b3d32011-04-07 14:18:59 +00001519 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001520
1521 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001522 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001523 }
1524
reed@google.com4e2b3d32011-04-07 14:18:59 +00001525 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001526}
1527
1528void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1529 const SkPaint* paint) {
1530 SkDEBUGCODE(bitmap.validate();)
1531
reed@google.com3d608122011-11-21 15:16:16 +00001532 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001533 SkRect bounds = {
1534 x, y,
1535 x + SkIntToScalar(bitmap.width()),
1536 y + SkIntToScalar(bitmap.height())
1537 };
1538 if (paint) {
1539 (void)paint->computeFastBounds(bounds, &bounds);
1540 }
1541 if (this->quickReject(bounds, paint2EdgeType(paint))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001542 return;
1543 }
1544 }
reed@google.com4b226022011-01-11 18:32:13 +00001545
reed@android.com8a1c16f2008-12-17 15:59:43 +00001546 SkMatrix matrix;
1547 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001548 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001549}
1550
reed@google.com9987ec32011-09-07 11:57:52 +00001551// this one is non-virtual, so it can be called safely by other canvas apis
1552void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1553 const SkRect& dst, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001554 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1555 return;
1556 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001557
reed@android.com8a1c16f2008-12-17 15:59:43 +00001558 // do this now, to avoid the cost of calling extract for RLE bitmaps
reed@google.com3d608122011-11-21 15:16:16 +00001559 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001560 SkRect storage;
1561 const SkRect* bounds = &dst;
1562 if (paint) {
1563 bounds = &paint->computeFastBounds(dst, &storage);
1564 }
1565 if (this->quickReject(*bounds, paint2EdgeType(paint))) {
reed@google.com3d608122011-11-21 15:16:16 +00001566 return;
1567 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001568 }
reed@google.com3d608122011-11-21 15:16:16 +00001569
reed@android.com8a1c16f2008-12-17 15:59:43 +00001570 const SkBitmap* bitmapPtr = &bitmap;
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001571
reed@android.com8a1c16f2008-12-17 15:59:43 +00001572 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001573 SkRect tmpSrc;
1574 if (src) {
1575 tmpSrc.set(*src);
1576 // if the extract process clipped off the top or left of the
1577 // original, we adjust for that here to get the position right.
1578 if (tmpSrc.fLeft > 0) {
1579 tmpSrc.fRight -= tmpSrc.fLeft;
1580 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001581 }
reed@android.com87899992009-10-16 14:48:38 +00001582 if (tmpSrc.fTop > 0) {
1583 tmpSrc.fBottom -= tmpSrc.fTop;
1584 tmpSrc.fTop = 0;
1585 }
1586 } else {
1587 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1588 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001589 }
reed@android.com87899992009-10-16 14:48:38 +00001590 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001591
reed@android.comf2b98d62010-12-20 18:26:13 +00001592 // ensure that src is "valid" before we pass it to our internal routines
1593 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1594 SkIRect tmpISrc;
1595 if (src) {
1596 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
reed@google.com2ade0862011-03-17 17:48:04 +00001597 if (!tmpISrc.intersect(*src)) {
1598 return;
1599 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001600 src = &tmpISrc;
1601 }
1602 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001603}
1604
reed@google.com9987ec32011-09-07 11:57:52 +00001605void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1606 const SkRect& dst, const SkPaint* paint) {
1607 SkDEBUGCODE(bitmap.validate();)
1608 this->internalDrawBitmapRect(bitmap, src, dst, paint);
1609}
1610
reed@android.com8a1c16f2008-12-17 15:59:43 +00001611void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1612 const SkPaint* paint) {
1613 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001614 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001615}
1616
reed@android.comf2b98d62010-12-20 18:26:13 +00001617void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1618 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001619 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001620
reed@google.com4e2b3d32011-04-07 14:18:59 +00001621 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001622
reed@android.com8a1c16f2008-12-17 15:59:43 +00001623 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001624 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001625 }
reed@android.com9b039062009-02-11 15:09:58 +00001626
reed@google.com4e2b3d32011-04-07 14:18:59 +00001627 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001628}
1629
reed@google.com9987ec32011-09-07 11:57:52 +00001630void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1631 const SkIRect& center, const SkRect& dst,
1632 const SkPaint* paint) {
reed@google.com3d608122011-11-21 15:16:16 +00001633 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001634 SkRect storage;
1635 const SkRect* bounds = &dst;
1636 if (paint) {
1637 bounds = &paint->computeFastBounds(dst, &storage);
1638 }
1639 if (this->quickReject(*bounds, paint2EdgeType(paint))) {
reed@google.com3d608122011-11-21 15:16:16 +00001640 return;
1641 }
1642 }
1643
reed@google.com9987ec32011-09-07 11:57:52 +00001644 const int32_t w = bitmap.width();
1645 const int32_t h = bitmap.height();
1646
1647 SkIRect c = center;
1648 // pin center to the bounds of the bitmap
1649 c.fLeft = SkMax32(0, center.fLeft);
1650 c.fTop = SkMax32(0, center.fTop);
1651 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1652 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1653
1654 const int32_t srcX[4] = { 0, c.fLeft, c.fRight, w };
1655 const int32_t srcY[4] = { 0, c.fTop, c.fBottom, h };
1656 SkScalar dstX[4] = {
1657 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1658 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1659 };
1660 SkScalar dstY[4] = {
1661 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1662 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1663 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001664
reed@google.com9987ec32011-09-07 11:57:52 +00001665 if (dstX[1] > dstX[2]) {
1666 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1667 dstX[2] = dstX[1];
1668 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001669
reed@google.com9987ec32011-09-07 11:57:52 +00001670 if (dstY[1] > dstY[2]) {
1671 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1672 dstY[2] = dstY[1];
1673 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001674
reed@google.com9987ec32011-09-07 11:57:52 +00001675 SkIRect s;
1676 SkRect d;
1677 for (int y = 0; y < 3; y++) {
1678 s.fTop = srcY[y];
1679 s.fBottom = srcY[y+1];
1680 d.fTop = dstY[y];
1681 d.fBottom = dstY[y+1];
1682 for (int x = 0; x < 3; x++) {
1683 s.fLeft = srcX[x];
1684 s.fRight = srcX[x+1];
1685 d.fLeft = dstX[x];
1686 d.fRight = dstX[x+1];
1687 this->internalDrawBitmapRect(bitmap, &s, d, paint);
1688 }
1689 }
1690}
1691
1692void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1693 const SkRect& dst, const SkPaint* paint) {
1694 SkDEBUGCODE(bitmap.validate();)
1695
1696 // Need a device entry-point, so gpu can use a mesh
1697 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1698}
1699
reed@google.comf67e4cf2011-03-15 20:56:58 +00001700class SkDeviceFilteredPaint {
1701public:
1702 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1703 SkDevice::TextFlags flags;
1704 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001705 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001706 newPaint->setFlags(flags.fFlags);
1707 newPaint->setHinting(flags.fHinting);
1708 fPaint = newPaint;
1709 } else {
1710 fPaint = &paint;
1711 }
1712 }
1713
reed@google.comf67e4cf2011-03-15 20:56:58 +00001714 const SkPaint& paint() const { return *fPaint; }
1715
1716private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001717 const SkPaint* fPaint;
1718 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001719};
1720
bungeman@google.com52c748b2011-08-22 21:30:43 +00001721void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1722 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001723 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001724 draw.fDevice->drawRect(draw, r, paint);
1725 } else {
1726 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001727 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001728 draw.fDevice->drawRect(draw, r, p);
1729 }
1730}
1731
1732void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1733 const char text[], size_t byteLength,
1734 SkScalar x, SkScalar y) {
1735 SkASSERT(byteLength == 0 || text != NULL);
1736
1737 // nothing to draw
1738 if (text == NULL || byteLength == 0 ||
1739 draw.fClip->isEmpty() ||
1740 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1741 return;
1742 }
1743
1744 SkScalar width = 0;
1745 SkPoint start;
1746
1747 start.set(0, 0); // to avoid warning
1748 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1749 SkPaint::kStrikeThruText_Flag)) {
1750 width = paint.measureText(text, byteLength);
1751
1752 SkScalar offsetX = 0;
1753 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1754 offsetX = SkScalarHalf(width);
1755 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1756 offsetX = width;
1757 }
1758 start.set(x - offsetX, y);
1759 }
1760
1761 if (0 == width) {
1762 return;
1763 }
1764
1765 uint32_t flags = paint.getFlags();
1766
1767 if (flags & (SkPaint::kUnderlineText_Flag |
1768 SkPaint::kStrikeThruText_Flag)) {
1769 SkScalar textSize = paint.getTextSize();
1770 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1771 SkRect r;
1772
1773 r.fLeft = start.fX;
1774 r.fRight = start.fX + width;
1775
1776 if (flags & SkPaint::kUnderlineText_Flag) {
1777 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1778 start.fY);
1779 r.fTop = offset;
1780 r.fBottom = offset + height;
1781 DrawRect(draw, paint, r, textSize);
1782 }
1783 if (flags & SkPaint::kStrikeThruText_Flag) {
1784 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1785 start.fY);
1786 r.fTop = offset;
1787 r.fBottom = offset + height;
1788 DrawRect(draw, paint, r, textSize);
1789 }
1790 }
1791}
1792
reed@android.com8a1c16f2008-12-17 15:59:43 +00001793void SkCanvas::drawText(const void* text, size_t byteLength,
1794 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001795 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001796
1797 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001798 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001799 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001800 DrawTextDecorations(iter, dfp.paint(),
1801 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001802 }
1803
reed@google.com4e2b3d32011-04-07 14:18:59 +00001804 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001805}
1806
1807void SkCanvas::drawPosText(const void* text, size_t byteLength,
1808 const SkPoint pos[], const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001809 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001810
reed@android.com8a1c16f2008-12-17 15:59:43 +00001811 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001812 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001813 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001814 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001815 }
reed@google.com4b226022011-01-11 18:32:13 +00001816
reed@google.com4e2b3d32011-04-07 14:18:59 +00001817 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001818}
1819
1820void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1821 const SkScalar xpos[], SkScalar constY,
1822 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001823 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001824
reed@android.com8a1c16f2008-12-17 15:59:43 +00001825 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001826 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001827 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001828 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001829 }
reed@google.com4b226022011-01-11 18:32:13 +00001830
reed@google.com4e2b3d32011-04-07 14:18:59 +00001831 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001832}
1833
1834void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1835 const SkPath& path, const SkMatrix* matrix,
1836 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001837 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001838
1839 while (iter.next()) {
1840 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001841 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001842 }
1843
reed@google.com4e2b3d32011-04-07 14:18:59 +00001844 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001845}
1846
djsollen@google.com56c69772011-11-08 19:00:26 +00001847#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001848void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1849 const SkPoint pos[], const SkPaint& paint,
1850 const SkPath& path, const SkMatrix* matrix) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001851 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001852
1853 while (iter.next()) {
1854 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001855 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001856 }
1857
reed@google.com4e2b3d32011-04-07 14:18:59 +00001858 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001859}
1860#endif
1861
reed@android.com8a1c16f2008-12-17 15:59:43 +00001862void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1863 const SkPoint verts[], const SkPoint texs[],
1864 const SkColor colors[], SkXfermode* xmode,
1865 const uint16_t indices[], int indexCount,
1866 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001867 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001868
reed@android.com8a1c16f2008-12-17 15:59:43 +00001869 while (iter.next()) {
1870 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001871 colors, xmode, indices, indexCount,
1872 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001873 }
reed@google.com4b226022011-01-11 18:32:13 +00001874
reed@google.com4e2b3d32011-04-07 14:18:59 +00001875 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001876}
1877
reed@android.comcb608442009-12-04 21:32:27 +00001878void SkCanvas::drawData(const void* data, size_t length) {
1879 // do nothing. Subclasses may do something with the data
1880}
1881
reed@android.com8a1c16f2008-12-17 15:59:43 +00001882//////////////////////////////////////////////////////////////////////////////
1883// These methods are NOT virtual, and therefore must call back into virtual
1884// methods, rather than actually drawing themselves.
1885//////////////////////////////////////////////////////////////////////////////
1886
1887void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001888 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001889 SkPaint paint;
1890
1891 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001892 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001893 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001894 }
1895 this->drawPaint(paint);
1896}
1897
reed@android.com845fdac2009-06-23 03:01:32 +00001898void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001899 SkPaint paint;
1900
1901 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001902 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001903 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001904 }
1905 this->drawPaint(paint);
1906}
1907
1908void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1909 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001910
reed@android.com8a1c16f2008-12-17 15:59:43 +00001911 pt.set(x, y);
1912 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1913}
1914
1915void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1916 SkPoint pt;
1917 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001918
reed@android.com8a1c16f2008-12-17 15:59:43 +00001919 pt.set(x, y);
1920 paint.setColor(color);
1921 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1922}
1923
1924void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1925 const SkPaint& paint) {
1926 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001927
reed@android.com8a1c16f2008-12-17 15:59:43 +00001928 pts[0].set(x0, y0);
1929 pts[1].set(x1, y1);
1930 this->drawPoints(kLines_PointMode, 2, pts, paint);
1931}
1932
1933void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1934 SkScalar right, SkScalar bottom,
1935 const SkPaint& paint) {
1936 SkRect r;
1937
1938 r.set(left, top, right, bottom);
1939 this->drawRect(r, paint);
1940}
1941
1942void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1943 const SkPaint& paint) {
1944 if (radius < 0) {
1945 radius = 0;
1946 }
1947
1948 SkRect r;
1949 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001950
reed@android.com8a1c16f2008-12-17 15:59:43 +00001951 if (paint.canComputeFastBounds()) {
1952 SkRect storage;
1953 if (this->quickReject(paint.computeFastBounds(r, &storage),
1954 paint2EdgeType(&paint))) {
1955 return;
1956 }
1957 }
reed@google.com4b226022011-01-11 18:32:13 +00001958
reed@android.com8a1c16f2008-12-17 15:59:43 +00001959 SkPath path;
1960 path.addOval(r);
1961 this->drawPath(path, paint);
1962}
1963
1964void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1965 const SkPaint& paint) {
1966 if (rx > 0 && ry > 0) {
1967 if (paint.canComputeFastBounds()) {
1968 SkRect storage;
1969 if (this->quickReject(paint.computeFastBounds(r, &storage),
1970 paint2EdgeType(&paint))) {
1971 return;
1972 }
1973 }
1974
1975 SkPath path;
1976 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1977 this->drawPath(path, paint);
1978 } else {
1979 this->drawRect(r, paint);
1980 }
1981}
1982
1983void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1984 if (paint.canComputeFastBounds()) {
1985 SkRect storage;
1986 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1987 paint2EdgeType(&paint))) {
1988 return;
1989 }
1990 }
1991
1992 SkPath path;
1993 path.addOval(oval);
1994 this->drawPath(path, paint);
1995}
1996
1997void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1998 SkScalar sweepAngle, bool useCenter,
1999 const SkPaint& paint) {
2000 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2001 this->drawOval(oval, paint);
2002 } else {
2003 SkPath path;
2004 if (useCenter) {
2005 path.moveTo(oval.centerX(), oval.centerY());
2006 }
2007 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2008 if (useCenter) {
2009 path.close();
2010 }
2011 this->drawPath(path, paint);
2012 }
2013}
2014
2015void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2016 const SkPath& path, SkScalar hOffset,
2017 SkScalar vOffset, const SkPaint& paint) {
2018 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002019
reed@android.com8a1c16f2008-12-17 15:59:43 +00002020 matrix.setTranslate(hOffset, vOffset);
2021 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2022}
2023
reed@android.comf76bacf2009-05-13 14:00:33 +00002024///////////////////////////////////////////////////////////////////////////////
2025
reed@android.com8a1c16f2008-12-17 15:59:43 +00002026void SkCanvas::drawPicture(SkPicture& picture) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002027 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002028}
2029
reed@android.com8a1c16f2008-12-17 15:59:43 +00002030///////////////////////////////////////////////////////////////////////////////
2031///////////////////////////////////////////////////////////////////////////////
2032
2033SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002034 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002035
2036 SkASSERT(canvas);
2037
2038 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2039 fDone = !fImpl->next();
2040}
2041
2042SkCanvas::LayerIter::~LayerIter() {
2043 fImpl->~SkDrawIter();
2044}
2045
2046void SkCanvas::LayerIter::next() {
2047 fDone = !fImpl->next();
2048}
2049
2050SkDevice* SkCanvas::LayerIter::device() const {
2051 return fImpl->getDevice();
2052}
2053
2054const SkMatrix& SkCanvas::LayerIter::matrix() const {
2055 return fImpl->getMatrix();
2056}
2057
2058const SkPaint& SkCanvas::LayerIter::paint() const {
2059 const SkPaint* paint = fImpl->getPaint();
2060 if (NULL == paint) {
2061 paint = &fDefaultPaint;
2062 }
2063 return *paint;
2064}
2065
2066const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2067int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2068int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002069
2070///////////////////////////////////////////////////////////////////////////////
2071
2072SkCanvas::ClipVisitor::~ClipVisitor() { }