blob: 567e5f5c15aa404d9b6b04bac7d674c32d8213cb [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
1215 SkClipStack::B2FIter iter(fClipStack);
1216 const SkClipStack::B2FIter::Clip* clip;
1217 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 {
1237 SkClipStack::B2FIter iter(fClipStack);
1238 const SkClipStack::B2FIter::Clip* clip;
1239
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) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001500 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001501 SkRect storage;
1502 const SkRect& bounds = path.getBounds();
1503 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001504 paint2EdgeType(&paint))) {
1505 return;
1506 }
1507 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001508 if (path.isEmpty()) {
1509 if (path.isInverseFillType()) {
1510 this->internalDrawPaint(paint);
1511 }
1512 return;
1513 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001514
reed@google.com4e2b3d32011-04-07 14:18:59 +00001515 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001516
1517 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001518 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001519 }
1520
reed@google.com4e2b3d32011-04-07 14:18:59 +00001521 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001522}
1523
1524void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1525 const SkPaint* paint) {
1526 SkDEBUGCODE(bitmap.validate();)
1527
reed@google.com3d608122011-11-21 15:16:16 +00001528 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001529 SkRect bounds = {
1530 x, y,
1531 x + SkIntToScalar(bitmap.width()),
1532 y + SkIntToScalar(bitmap.height())
1533 };
1534 if (paint) {
1535 (void)paint->computeFastBounds(bounds, &bounds);
1536 }
1537 if (this->quickReject(bounds, paint2EdgeType(paint))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001538 return;
1539 }
1540 }
reed@google.com4b226022011-01-11 18:32:13 +00001541
reed@android.com8a1c16f2008-12-17 15:59:43 +00001542 SkMatrix matrix;
1543 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001544 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001545}
1546
reed@google.com9987ec32011-09-07 11:57:52 +00001547// this one is non-virtual, so it can be called safely by other canvas apis
1548void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1549 const SkRect& dst, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001550 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1551 return;
1552 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001553
reed@android.com8a1c16f2008-12-17 15:59:43 +00001554 // do this now, to avoid the cost of calling extract for RLE bitmaps
reed@google.com3d608122011-11-21 15:16:16 +00001555 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001556 SkRect storage;
1557 const SkRect* bounds = &dst;
1558 if (paint) {
1559 bounds = &paint->computeFastBounds(dst, &storage);
1560 }
1561 if (this->quickReject(*bounds, paint2EdgeType(paint))) {
reed@google.com3d608122011-11-21 15:16:16 +00001562 return;
1563 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001564 }
reed@google.com3d608122011-11-21 15:16:16 +00001565
reed@android.com8a1c16f2008-12-17 15:59:43 +00001566 const SkBitmap* bitmapPtr = &bitmap;
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001567
reed@android.com8a1c16f2008-12-17 15:59:43 +00001568 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001569 SkRect tmpSrc;
1570 if (src) {
1571 tmpSrc.set(*src);
1572 // if the extract process clipped off the top or left of the
1573 // original, we adjust for that here to get the position right.
1574 if (tmpSrc.fLeft > 0) {
1575 tmpSrc.fRight -= tmpSrc.fLeft;
1576 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001577 }
reed@android.com87899992009-10-16 14:48:38 +00001578 if (tmpSrc.fTop > 0) {
1579 tmpSrc.fBottom -= tmpSrc.fTop;
1580 tmpSrc.fTop = 0;
1581 }
1582 } else {
1583 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1584 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001585 }
reed@android.com87899992009-10-16 14:48:38 +00001586 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001587
reed@android.comf2b98d62010-12-20 18:26:13 +00001588 // ensure that src is "valid" before we pass it to our internal routines
1589 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1590 SkIRect tmpISrc;
1591 if (src) {
1592 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
reed@google.com2ade0862011-03-17 17:48:04 +00001593 if (!tmpISrc.intersect(*src)) {
1594 return;
1595 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001596 src = &tmpISrc;
1597 }
1598 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001599}
1600
reed@google.com9987ec32011-09-07 11:57:52 +00001601void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1602 const SkRect& dst, const SkPaint* paint) {
1603 SkDEBUGCODE(bitmap.validate();)
1604 this->internalDrawBitmapRect(bitmap, src, dst, paint);
1605}
1606
reed@android.com8a1c16f2008-12-17 15:59:43 +00001607void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1608 const SkPaint* paint) {
1609 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001610 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001611}
1612
reed@android.comf2b98d62010-12-20 18:26:13 +00001613void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1614 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001615 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001616
reed@google.com4e2b3d32011-04-07 14:18:59 +00001617 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001618
reed@android.com8a1c16f2008-12-17 15:59:43 +00001619 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001620 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001621 }
reed@android.com9b039062009-02-11 15:09:58 +00001622
reed@google.com4e2b3d32011-04-07 14:18:59 +00001623 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001624}
1625
reed@google.com9987ec32011-09-07 11:57:52 +00001626void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1627 const SkIRect& center, const SkRect& dst,
1628 const SkPaint* paint) {
reed@google.com3d608122011-11-21 15:16:16 +00001629 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001630 SkRect storage;
1631 const SkRect* bounds = &dst;
1632 if (paint) {
1633 bounds = &paint->computeFastBounds(dst, &storage);
1634 }
1635 if (this->quickReject(*bounds, paint2EdgeType(paint))) {
reed@google.com3d608122011-11-21 15:16:16 +00001636 return;
1637 }
1638 }
1639
reed@google.com9987ec32011-09-07 11:57:52 +00001640 const int32_t w = bitmap.width();
1641 const int32_t h = bitmap.height();
1642
1643 SkIRect c = center;
1644 // pin center to the bounds of the bitmap
1645 c.fLeft = SkMax32(0, center.fLeft);
1646 c.fTop = SkMax32(0, center.fTop);
1647 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1648 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1649
1650 const int32_t srcX[4] = { 0, c.fLeft, c.fRight, w };
1651 const int32_t srcY[4] = { 0, c.fTop, c.fBottom, h };
1652 SkScalar dstX[4] = {
1653 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1654 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1655 };
1656 SkScalar dstY[4] = {
1657 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1658 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1659 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001660
reed@google.com9987ec32011-09-07 11:57:52 +00001661 if (dstX[1] > dstX[2]) {
1662 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1663 dstX[2] = dstX[1];
1664 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001665
reed@google.com9987ec32011-09-07 11:57:52 +00001666 if (dstY[1] > dstY[2]) {
1667 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1668 dstY[2] = dstY[1];
1669 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001670
reed@google.com9987ec32011-09-07 11:57:52 +00001671 SkIRect s;
1672 SkRect d;
1673 for (int y = 0; y < 3; y++) {
1674 s.fTop = srcY[y];
1675 s.fBottom = srcY[y+1];
1676 d.fTop = dstY[y];
1677 d.fBottom = dstY[y+1];
1678 for (int x = 0; x < 3; x++) {
1679 s.fLeft = srcX[x];
1680 s.fRight = srcX[x+1];
1681 d.fLeft = dstX[x];
1682 d.fRight = dstX[x+1];
1683 this->internalDrawBitmapRect(bitmap, &s, d, paint);
1684 }
1685 }
1686}
1687
1688void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1689 const SkRect& dst, const SkPaint* paint) {
1690 SkDEBUGCODE(bitmap.validate();)
1691
1692 // Need a device entry-point, so gpu can use a mesh
1693 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1694}
1695
reed@google.comf67e4cf2011-03-15 20:56:58 +00001696class SkDeviceFilteredPaint {
1697public:
1698 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1699 SkDevice::TextFlags flags;
1700 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001701 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001702 newPaint->setFlags(flags.fFlags);
1703 newPaint->setHinting(flags.fHinting);
1704 fPaint = newPaint;
1705 } else {
1706 fPaint = &paint;
1707 }
1708 }
1709
reed@google.comf67e4cf2011-03-15 20:56:58 +00001710 const SkPaint& paint() const { return *fPaint; }
1711
1712private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001713 const SkPaint* fPaint;
1714 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001715};
1716
bungeman@google.com52c748b2011-08-22 21:30:43 +00001717void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1718 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001719 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001720 draw.fDevice->drawRect(draw, r, paint);
1721 } else {
1722 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001723 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001724 draw.fDevice->drawRect(draw, r, p);
1725 }
1726}
1727
1728void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1729 const char text[], size_t byteLength,
1730 SkScalar x, SkScalar y) {
1731 SkASSERT(byteLength == 0 || text != NULL);
1732
1733 // nothing to draw
1734 if (text == NULL || byteLength == 0 ||
1735 draw.fClip->isEmpty() ||
1736 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1737 return;
1738 }
1739
1740 SkScalar width = 0;
1741 SkPoint start;
1742
1743 start.set(0, 0); // to avoid warning
1744 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1745 SkPaint::kStrikeThruText_Flag)) {
1746 width = paint.measureText(text, byteLength);
1747
1748 SkScalar offsetX = 0;
1749 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1750 offsetX = SkScalarHalf(width);
1751 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1752 offsetX = width;
1753 }
1754 start.set(x - offsetX, y);
1755 }
1756
1757 if (0 == width) {
1758 return;
1759 }
1760
1761 uint32_t flags = paint.getFlags();
1762
1763 if (flags & (SkPaint::kUnderlineText_Flag |
1764 SkPaint::kStrikeThruText_Flag)) {
1765 SkScalar textSize = paint.getTextSize();
1766 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1767 SkRect r;
1768
1769 r.fLeft = start.fX;
1770 r.fRight = start.fX + width;
1771
1772 if (flags & SkPaint::kUnderlineText_Flag) {
1773 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1774 start.fY);
1775 r.fTop = offset;
1776 r.fBottom = offset + height;
1777 DrawRect(draw, paint, r, textSize);
1778 }
1779 if (flags & SkPaint::kStrikeThruText_Flag) {
1780 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1781 start.fY);
1782 r.fTop = offset;
1783 r.fBottom = offset + height;
1784 DrawRect(draw, paint, r, textSize);
1785 }
1786 }
1787}
1788
reed@android.com8a1c16f2008-12-17 15:59:43 +00001789void SkCanvas::drawText(const void* text, size_t byteLength,
1790 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001791 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001792
1793 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001794 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001795 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001796 DrawTextDecorations(iter, dfp.paint(),
1797 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001798 }
1799
reed@google.com4e2b3d32011-04-07 14:18:59 +00001800 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001801}
1802
1803void SkCanvas::drawPosText(const void* text, size_t byteLength,
1804 const SkPoint pos[], const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001805 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001806
reed@android.com8a1c16f2008-12-17 15:59:43 +00001807 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001808 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001809 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001810 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001811 }
reed@google.com4b226022011-01-11 18:32:13 +00001812
reed@google.com4e2b3d32011-04-07 14:18:59 +00001813 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001814}
1815
1816void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1817 const SkScalar xpos[], SkScalar constY,
1818 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001819 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001820
reed@android.com8a1c16f2008-12-17 15:59:43 +00001821 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001822 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001823 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001824 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001825 }
reed@google.com4b226022011-01-11 18:32:13 +00001826
reed@google.com4e2b3d32011-04-07 14:18:59 +00001827 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001828}
1829
1830void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1831 const SkPath& path, const SkMatrix* matrix,
1832 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001833 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001834
1835 while (iter.next()) {
1836 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001837 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001838 }
1839
reed@google.com4e2b3d32011-04-07 14:18:59 +00001840 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001841}
1842
djsollen@google.com56c69772011-11-08 19:00:26 +00001843#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001844void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1845 const SkPoint pos[], const SkPaint& paint,
1846 const SkPath& path, const SkMatrix* matrix) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001847 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001848
1849 while (iter.next()) {
1850 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001851 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001852 }
1853
reed@google.com4e2b3d32011-04-07 14:18:59 +00001854 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001855}
1856#endif
1857
reed@android.com8a1c16f2008-12-17 15:59:43 +00001858void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1859 const SkPoint verts[], const SkPoint texs[],
1860 const SkColor colors[], SkXfermode* xmode,
1861 const uint16_t indices[], int indexCount,
1862 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001863 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001864
reed@android.com8a1c16f2008-12-17 15:59:43 +00001865 while (iter.next()) {
1866 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001867 colors, xmode, indices, indexCount,
1868 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001869 }
reed@google.com4b226022011-01-11 18:32:13 +00001870
reed@google.com4e2b3d32011-04-07 14:18:59 +00001871 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001872}
1873
reed@android.comcb608442009-12-04 21:32:27 +00001874void SkCanvas::drawData(const void* data, size_t length) {
1875 // do nothing. Subclasses may do something with the data
1876}
1877
reed@android.com8a1c16f2008-12-17 15:59:43 +00001878//////////////////////////////////////////////////////////////////////////////
1879// These methods are NOT virtual, and therefore must call back into virtual
1880// methods, rather than actually drawing themselves.
1881//////////////////////////////////////////////////////////////////////////////
1882
1883void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001884 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001885 SkPaint paint;
1886
1887 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001888 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001889 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001890 }
1891 this->drawPaint(paint);
1892}
1893
reed@android.com845fdac2009-06-23 03:01:32 +00001894void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001895 SkPaint paint;
1896
1897 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001898 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001899 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001900 }
1901 this->drawPaint(paint);
1902}
1903
1904void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1905 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001906
reed@android.com8a1c16f2008-12-17 15:59:43 +00001907 pt.set(x, y);
1908 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1909}
1910
1911void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1912 SkPoint pt;
1913 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001914
reed@android.com8a1c16f2008-12-17 15:59:43 +00001915 pt.set(x, y);
1916 paint.setColor(color);
1917 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1918}
1919
1920void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1921 const SkPaint& paint) {
1922 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001923
reed@android.com8a1c16f2008-12-17 15:59:43 +00001924 pts[0].set(x0, y0);
1925 pts[1].set(x1, y1);
1926 this->drawPoints(kLines_PointMode, 2, pts, paint);
1927}
1928
1929void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1930 SkScalar right, SkScalar bottom,
1931 const SkPaint& paint) {
1932 SkRect r;
1933
1934 r.set(left, top, right, bottom);
1935 this->drawRect(r, paint);
1936}
1937
1938void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1939 const SkPaint& paint) {
1940 if (radius < 0) {
1941 radius = 0;
1942 }
1943
1944 SkRect r;
1945 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001946
reed@android.com8a1c16f2008-12-17 15:59:43 +00001947 if (paint.canComputeFastBounds()) {
1948 SkRect storage;
1949 if (this->quickReject(paint.computeFastBounds(r, &storage),
1950 paint2EdgeType(&paint))) {
1951 return;
1952 }
1953 }
reed@google.com4b226022011-01-11 18:32:13 +00001954
reed@android.com8a1c16f2008-12-17 15:59:43 +00001955 SkPath path;
1956 path.addOval(r);
1957 this->drawPath(path, paint);
1958}
1959
1960void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1961 const SkPaint& paint) {
1962 if (rx > 0 && ry > 0) {
1963 if (paint.canComputeFastBounds()) {
1964 SkRect storage;
1965 if (this->quickReject(paint.computeFastBounds(r, &storage),
1966 paint2EdgeType(&paint))) {
1967 return;
1968 }
1969 }
1970
1971 SkPath path;
1972 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1973 this->drawPath(path, paint);
1974 } else {
1975 this->drawRect(r, paint);
1976 }
1977}
1978
1979void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1980 if (paint.canComputeFastBounds()) {
1981 SkRect storage;
1982 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1983 paint2EdgeType(&paint))) {
1984 return;
1985 }
1986 }
1987
1988 SkPath path;
1989 path.addOval(oval);
1990 this->drawPath(path, paint);
1991}
1992
1993void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1994 SkScalar sweepAngle, bool useCenter,
1995 const SkPaint& paint) {
1996 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1997 this->drawOval(oval, paint);
1998 } else {
1999 SkPath path;
2000 if (useCenter) {
2001 path.moveTo(oval.centerX(), oval.centerY());
2002 }
2003 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2004 if (useCenter) {
2005 path.close();
2006 }
2007 this->drawPath(path, paint);
2008 }
2009}
2010
2011void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2012 const SkPath& path, SkScalar hOffset,
2013 SkScalar vOffset, const SkPaint& paint) {
2014 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002015
reed@android.com8a1c16f2008-12-17 15:59:43 +00002016 matrix.setTranslate(hOffset, vOffset);
2017 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2018}
2019
reed@android.comf76bacf2009-05-13 14:00:33 +00002020///////////////////////////////////////////////////////////////////////////////
2021
reed@android.com8a1c16f2008-12-17 15:59:43 +00002022void SkCanvas::drawPicture(SkPicture& picture) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002023 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002024}
2025
reed@android.com8a1c16f2008-12-17 15:59:43 +00002026///////////////////////////////////////////////////////////////////////////////
2027///////////////////////////////////////////////////////////////////////////////
2028
2029SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002030 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002031
2032 SkASSERT(canvas);
2033
2034 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2035 fDone = !fImpl->next();
2036}
2037
2038SkCanvas::LayerIter::~LayerIter() {
2039 fImpl->~SkDrawIter();
2040}
2041
2042void SkCanvas::LayerIter::next() {
2043 fDone = !fImpl->next();
2044}
2045
2046SkDevice* SkCanvas::LayerIter::device() const {
2047 return fImpl->getDevice();
2048}
2049
2050const SkMatrix& SkCanvas::LayerIter::matrix() const {
2051 return fImpl->getMatrix();
2052}
2053
2054const SkPaint& SkCanvas::LayerIter::paint() const {
2055 const SkPaint* paint = fImpl->getPaint();
2056 if (NULL == paint) {
2057 paint = &fDefaultPaint;
2058 }
2059 return *paint;
2060}
2061
2062const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2063int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2064int SkCanvas::LayerIter::y() const { return fImpl->getY(); }