blob: 56ac2da6460e2d386c4847b712d63865c027d8ff [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
24//#define SK_TRACE_SAVERESTORE
25
26#ifdef SK_TRACE_SAVERESTORE
27 static int gLayerCounter;
28 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
29 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
30
31 static int gRecCounter;
32 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
33 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
34
35 static int gCanvasCounter;
36 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
37 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
38#else
39 #define inc_layer()
40 #define dec_layer()
41 #define inc_rec()
42 #define dec_rec()
43 #define inc_canvas()
44 #define dec_canvas()
45#endif
46
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000047typedef SkTLazy<SkPaint> SkLazyPaint;
48
reed@android.com8a1c16f2008-12-17 15:59:43 +000049///////////////////////////////////////////////////////////////////////////////
50// Helpers for computing fast bounds for quickReject tests
51
52static SkCanvas::EdgeType paint2EdgeType(const SkPaint* paint) {
53 return paint != NULL && paint->isAntiAlias() ?
54 SkCanvas::kAA_EdgeType : SkCanvas::kBW_EdgeType;
55}
56
57///////////////////////////////////////////////////////////////////////////////
58
59/* This is the record we keep for each SkDevice that the user installs.
60 The clip/matrix/proc are fields that reflect the top of the save/restore
61 stack. Whenever the canvas changes, it marks a dirty flag, and then before
62 these are used (assuming we're not on a layer) we rebuild these cache
63 values: they reflect the top of the save stack, but translated and clipped
64 by the device's XY offset and bitmap-bounds.
65*/
66struct DeviceCM {
67 DeviceCM* fNext;
68 SkDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +000069 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +000070 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +000071 SkPaint* fPaint; // may be null (in the future)
reed@android.comf2b98d62010-12-20 18:26:13 +000072 // optional, related to canvas' external matrix
73 const SkMatrix* fMVMatrix;
74 const SkMatrix* fExtMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +000075
bungeman@google.com88edf1e2011-08-08 19:41:56 +000076 DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint)
reed@android.com8a1c16f2008-12-17 15:59:43 +000077 : fNext(NULL) {
78 if (NULL != device) {
79 device->ref();
80 device->lockPixels();
81 }
reed@google.com4b226022011-01-11 18:32:13 +000082 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +000083 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +000084 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000085
bungeman@google.com88edf1e2011-08-08 19:41:56 +000086 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000087 if (NULL != fDevice) {
88 fDevice->unlockPixels();
89 fDevice->unref();
90 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +000091 SkDELETE(fPaint);
92 }
reed@google.com4b226022011-01-11 18:32:13 +000093
reed@google.com045e62d2011-10-24 12:19:46 +000094 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
95 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +000096 int x = fDevice->getOrigin().x();
97 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +000098 int width = fDevice->width();
99 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000100
reed@android.com8a1c16f2008-12-17 15:59:43 +0000101 if ((x | y) == 0) {
102 fMatrix = &totalMatrix;
103 fClip = totalClip;
104 } else {
105 fMatrixStorage = totalMatrix;
106 fMatrixStorage.postTranslate(SkIntToScalar(-x),
107 SkIntToScalar(-y));
108 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000109
reed@android.com8a1c16f2008-12-17 15:59:43 +0000110 totalClip.translate(-x, -y, &fClip);
111 }
112
reed@google.com045e62d2011-10-24 12:19:46 +0000113 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000114
115 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000116
reed@android.com8a1c16f2008-12-17 15:59:43 +0000117 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000118 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000119 SkRegion::kDifference_Op);
120 }
reed@google.com4b226022011-01-11 18:32:13 +0000121
reed@google.com045e62d2011-10-24 12:19:46 +0000122 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000123
124#ifdef SK_DEBUG
125 if (!fClip.isEmpty()) {
126 SkIRect deviceR;
127 deviceR.set(0, 0, width, height);
128 SkASSERT(deviceR.contains(fClip.getBounds()));
129 }
130#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000131 // default is to assume no external matrix
132 fMVMatrix = NULL;
133 fExtMatrix = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000134 }
reed@android.comf2b98d62010-12-20 18:26:13 +0000135
136 // can only be called after calling updateMC()
137 void updateExternalMatrix(const SkMatrix& extM, const SkMatrix& extI) {
138 fMVMatrixStorage.setConcat(extI, *fMatrix);
139 fMVMatrix = &fMVMatrixStorage;
140 fExtMatrix = &extM; // assumes extM has long life-time (owned by canvas)
141 }
142
reed@android.com8a1c16f2008-12-17 15:59:43 +0000143private:
reed@android.comf2b98d62010-12-20 18:26:13 +0000144 SkMatrix fMatrixStorage, fMVMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000145};
146
147/* This is the record we keep for each save/restore level in the stack.
148 Since a level optionally copies the matrix and/or stack, we have pointers
149 for these fields. If the value is copied for this level, the copy is
150 stored in the ...Storage field, and the pointer points to that. If the
151 value is not copied for this level, we ignore ...Storage, and just point
152 at the corresponding value in the previous level in the stack.
153*/
154class SkCanvas::MCRec {
155public:
156 MCRec* fNext;
reed@google.com00177082011-10-12 14:34:30 +0000157 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
158 SkRasterClip* fRasterClip; // points to either fRegionStorage or prev MCRec
159 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000160
reed@android.com8a1c16f2008-12-17 15:59:43 +0000161 DeviceCM* fLayer;
162 /* If there are any layers in the stack, this points to the top-most
163 one that is at or below this level in the stack (so we know what
164 bitmap/device to draw into from this level. This value is NOT
165 reference counted, since the real owner is either our fLayer field,
166 or a previous one in a lower level.)
167 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000168 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000169
170 MCRec(const MCRec* prev, int flags) {
171 if (NULL != prev) {
172 if (flags & SkCanvas::kMatrix_SaveFlag) {
173 fMatrixStorage = *prev->fMatrix;
174 fMatrix = &fMatrixStorage;
175 } else {
176 fMatrix = prev->fMatrix;
177 }
reed@google.com4b226022011-01-11 18:32:13 +0000178
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179 if (flags & SkCanvas::kClip_SaveFlag) {
reed@google.com00177082011-10-12 14:34:30 +0000180 fRasterClipStorage = *prev->fRasterClip;
181 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000182 } else {
reed@google.com00177082011-10-12 14:34:30 +0000183 fRasterClip = prev->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000184 }
185
186 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000187 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000188
189 fTopLayer = prev->fTopLayer;
190 } else { // no prev
191 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000192
reed@android.com8a1c16f2008-12-17 15:59:43 +0000193 fMatrix = &fMatrixStorage;
reed@google.com00177082011-10-12 14:34:30 +0000194 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000195 fFilter = NULL;
196 fTopLayer = NULL;
197 }
198 fLayer = NULL;
199
200 // don't bother initializing fNext
201 inc_rec();
202 }
203 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000204 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000205 SkDELETE(fLayer);
206 dec_rec();
207 }
reed@google.com4b226022011-01-11 18:32:13 +0000208
reed@android.com8a1c16f2008-12-17 15:59:43 +0000209private:
reed@google.com00177082011-10-12 14:34:30 +0000210 SkMatrix fMatrixStorage;
211 SkRasterClip fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000212};
213
214class SkDrawIter : public SkDraw {
215public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000216 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000217 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000218 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000219 canvas->updateDeviceCMCache();
220
reed@google.com90c07ea2012-04-13 13:50:27 +0000221 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000222 fBounder = canvas->getBounder();
223 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000224 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225 }
reed@google.com4b226022011-01-11 18:32:13 +0000226
reed@android.com8a1c16f2008-12-17 15:59:43 +0000227 bool next() {
228 // skip over recs with empty clips
229 if (fSkipEmptyClips) {
230 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
231 fCurrLayer = fCurrLayer->fNext;
232 }
233 }
234
reed@google.comf68c5e22012-02-24 16:38:58 +0000235 const DeviceCM* rec = fCurrLayer;
236 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000237
238 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000239 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
240 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000241 fDevice = rec->fDevice;
242 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000244 fMVMatrix = rec->fMVMatrix;
245 fExtMatrix = rec->fExtMatrix;
246 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247
248 fCurrLayer = rec->fNext;
249 if (fBounder) {
250 fBounder->setClip(fClip);
251 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000253
bsalomon@google.comd302f142011-03-03 13:54:13 +0000254 fCanvas->prepareForDeviceDraw(fDevice, *fMatrix, *fClip, *fClipStack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255 return true;
256 }
257 return false;
258 }
reed@google.com4b226022011-01-11 18:32:13 +0000259
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260 SkDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000261 int getX() const { return fDevice->getOrigin().x(); }
262 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000263 const SkMatrix& getMatrix() const { return *fMatrix; }
264 const SkRegion& getClip() const { return *fClip; }
265 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000266
reed@android.com8a1c16f2008-12-17 15:59:43 +0000267private:
268 SkCanvas* fCanvas;
269 const DeviceCM* fCurrLayer;
270 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000271 SkBool8 fSkipEmptyClips;
272
273 typedef SkDraw INHERITED;
274};
275
276/////////////////////////////////////////////////////////////////////////////
277
278class AutoDrawLooper {
279public:
reed@google.com8926b162012-03-23 15:36:36 +0000280 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint,
281 bool skipLayerForImageFilter = false) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000282 fCanvas = canvas;
283 fLooper = paint.getLooper();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000285 fPaint = NULL;
286 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000287 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000288 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000289
reed@google.com8926b162012-03-23 15:36:36 +0000290 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
291 SkPaint tmp;
292 tmp.setImageFilter(fOrigPaint.getImageFilter());
293 // it would be nice if we had a guess at the bounds, instead of null
294 (void)canvas->internalSaveLayer(NULL, &tmp,
295 SkCanvas::kARGB_ClipLayer_SaveFlag, true);
296 // we'll clear the imageFilter for the actual draws in next(), so
297 // it will only be applied during the restore().
298 fDoClearImageFilter = true;
299 }
300
reed@google.com4e2b3d32011-04-07 14:18:59 +0000301 if (fLooper) {
302 fLooper->init(canvas);
303 }
304 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000305
reed@android.com8a1c16f2008-12-17 15:59:43 +0000306 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000307 if (fDoClearImageFilter) {
308 fCanvas->internalRestore();
309 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000310 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000311 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000312
reed@google.com4e2b3d32011-04-07 14:18:59 +0000313 const SkPaint& paint() const {
314 SkASSERT(fPaint);
315 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000316 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000317
reed@google.com4e2b3d32011-04-07 14:18:59 +0000318 bool next(SkDrawFilter::Type drawType);
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000319
reed@android.com8a1c16f2008-12-17 15:59:43 +0000320private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000321 SkLazyPaint fLazyPaint;
322 SkCanvas* fCanvas;
323 const SkPaint& fOrigPaint;
324 SkDrawLooper* fLooper;
325 SkDrawFilter* fFilter;
326 const SkPaint* fPaint;
327 int fSaveCount;
reed@google.com8926b162012-03-23 15:36:36 +0000328 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000329 bool fDone;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000330};
331
reed@google.com4e2b3d32011-04-07 14:18:59 +0000332bool AutoDrawLooper::next(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000333 fPaint = NULL;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000334 if (fDone) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000335 return false;
336 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000337
reed@google.com8926b162012-03-23 15:36:36 +0000338 if (fLooper || fFilter || fDoClearImageFilter) {
reed@google.com632e1a22011-10-06 12:37:00 +0000339 SkPaint* paint = fLazyPaint.set(fOrigPaint);
reed@google.com8926b162012-03-23 15:36:36 +0000340
341 if (fDoClearImageFilter) {
342 paint->setImageFilter(NULL);
343 }
344
reed@google.com632e1a22011-10-06 12:37:00 +0000345 if (fLooper && !fLooper->next(fCanvas, paint)) {
346 fDone = true;
347 return false;
348 }
349 if (fFilter) {
350 fFilter->filter(paint, drawType);
351 if (NULL == fLooper) {
352 // no looper means we only draw once
353 fDone = true;
354 }
355 }
356 fPaint = paint;
reed@google.com8926b162012-03-23 15:36:36 +0000357
358 // if we only came in here for the imagefilter, mark us as done
359 if (!fLooper && !fFilter) {
360 fDone = true;
361 }
reed@google.com632e1a22011-10-06 12:37:00 +0000362 } else {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000363 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000364 fPaint = &fOrigPaint;
365 }
366
367 // call this after any possible paint modifiers
368 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000369 fPaint = NULL;
370 return false;
371 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000372 return true;
373}
374
reed@android.com8a1c16f2008-12-17 15:59:43 +0000375/* Stack helper for managing a SkBounder. In the destructor, if we were
376 given a bounder, we call its commit() method, signifying that we are
377 done accumulating bounds for that draw.
378*/
379class SkAutoBounderCommit {
380public:
381 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
382 ~SkAutoBounderCommit() {
383 if (NULL != fBounder) {
384 fBounder->commit();
385 }
386 }
387private:
388 SkBounder* fBounder;
389};
390
391#include "SkColorPriv.h"
392
393class AutoValidator {
394public:
395 AutoValidator(SkDevice* device) : fDevice(device) {}
396 ~AutoValidator() {
397#ifdef SK_DEBUG
398 const SkBitmap& bm = fDevice->accessBitmap(false);
399 if (bm.config() == SkBitmap::kARGB_4444_Config) {
400 for (int y = 0; y < bm.height(); y++) {
401 const SkPMColor16* p = bm.getAddr16(0, y);
402 for (int x = 0; x < bm.width(); x++) {
403 SkPMColor16 c = p[x];
404 SkPMColor16Assert(c);
405 }
406 }
407 }
408#endif
409 }
410private:
411 SkDevice* fDevice;
412};
413
414////////// macros to place around the internal draw calls //////////////////
415
reed@google.com8926b162012-03-23 15:36:36 +0000416#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
417/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
418 AutoDrawLooper looper(this, paint, true); \
419 while (looper.next(type)) { \
420 SkAutoBounderCommit ac(fBounder); \
421 SkDrawIter iter(this);
422
reed@google.com4e2b3d32011-04-07 14:18:59 +0000423#define LOOPER_BEGIN(paint, type) \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000424/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000425 AutoDrawLooper looper(this, paint); \
426 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000427 SkAutoBounderCommit ac(fBounder); \
428 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000429
reed@google.com4e2b3d32011-04-07 14:18:59 +0000430#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000431
432////////////////////////////////////////////////////////////////////////////
433
434SkDevice* SkCanvas::init(SkDevice* device) {
435 fBounder = NULL;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000436 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437 fLocalBoundsCompareTypeDirty = true;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000438 fLocalBoundsCompareTypeBW.setEmpty();
reed@android.comba09de42010-02-05 20:46:05 +0000439 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com199f1082009-06-10 02:12:47 +0000440 fLastDeviceToGainFocus = NULL;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000441 fDeviceCMDirty = false;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000442 fSaveLayerCount = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000443
444 fMCRec = (MCRec*)fMCStack.push_back();
445 new (fMCRec) MCRec(NULL, 0);
446
447 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL));
448 fMCRec->fTopLayer = fMCRec->fLayer;
449 fMCRec->fNext = NULL;
450
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000451 fExternalMatrix.reset();
452 fExternalInverse.reset();
reed@android.comf2b98d62010-12-20 18:26:13 +0000453 fUseExternalMatrix = false;
454
reed@android.com8a1c16f2008-12-17 15:59:43 +0000455 return this->setDevice(device);
456}
457
reed@google.comcde92112011-07-06 20:00:52 +0000458SkCanvas::SkCanvas()
459: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000460 inc_canvas();
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000461
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000462 this->init(NULL);
463}
464
reed@android.com8a1c16f2008-12-17 15:59:43 +0000465SkCanvas::SkCanvas(SkDevice* device)
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000466 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000467 inc_canvas();
468
469 this->init(device);
470}
471
472SkCanvas::SkCanvas(const SkBitmap& bitmap)
473 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
474 inc_canvas();
475
reed@google.comcde92112011-07-06 20:00:52 +0000476 this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000477}
478
479SkCanvas::~SkCanvas() {
480 // free up the contents of our deque
481 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000482 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000483
reed@android.com8a1c16f2008-12-17 15:59:43 +0000484 this->internalRestore(); // restore the last, since we're going away
485
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000486 SkSafeUnref(fBounder);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000487
reed@android.com8a1c16f2008-12-17 15:59:43 +0000488 dec_canvas();
489}
490
491SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
492 SkRefCnt_SafeAssign(fBounder, bounder);
493 return bounder;
494}
495
496SkDrawFilter* SkCanvas::getDrawFilter() const {
497 return fMCRec->fFilter;
498}
499
500SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
501 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
502 return filter;
503}
504
505///////////////////////////////////////////////////////////////////////////////
506
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000507void SkCanvas::flush() {
508 SkDevice* device = this->getDevice();
509 if (device) {
510 device->flush();
511 }
512}
513
reed@google.com210ce002011-11-01 14:24:23 +0000514SkISize SkCanvas::getDeviceSize() const {
515 SkDevice* d = this->getDevice();
516 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
517}
518
reed@android.com8a1c16f2008-12-17 15:59:43 +0000519SkDevice* SkCanvas::getDevice() const {
520 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000521 SkDeque::F2BIter iter(fMCStack);
522 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000523 SkASSERT(rec && rec->fLayer);
524 return rec->fLayer->fDevice;
525}
526
reed@google.com0b53d592012-03-19 18:26:34 +0000527SkDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
528 if (updateMatrixClip) {
529 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
530 }
reed@google.com9266fed2011-03-30 00:18:03 +0000531 return fMCRec->fTopLayer->fDevice;
532}
533
reed@android.com8a1c16f2008-12-17 15:59:43 +0000534SkDevice* SkCanvas::setDevice(SkDevice* device) {
535 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000536 SkDeque::F2BIter iter(fMCStack);
537 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000538 SkASSERT(rec && rec->fLayer);
539 SkDevice* rootDevice = rec->fLayer->fDevice;
540
541 if (rootDevice == device) {
542 return device;
543 }
reed@google.com4b226022011-01-11 18:32:13 +0000544
reed@android.com8a1c16f2008-12-17 15:59:43 +0000545 /* Notify the devices that they are going in/out of scope, so they can do
546 things like lock/unlock their pixels, etc.
547 */
548 if (device) {
549 device->lockPixels();
550 }
551 if (rootDevice) {
552 rootDevice->unlockPixels();
553 }
554
555 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
556 rootDevice = device;
557
558 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000559
reed@android.com8a1c16f2008-12-17 15:59:43 +0000560 /* Now we update our initial region to have the bounds of the new device,
561 and then intersect all of the clips in our stack with these bounds,
562 to ensure that we can't draw outside of the device's bounds (and trash
563 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000564
reed@android.com8a1c16f2008-12-17 15:59:43 +0000565 NOTE: this is only a partial-fix, since if the new device is larger than
566 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000567 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000568 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
569 reconstruct the correct clips, so this approximation will have to do.
570 The caller really needs to restore() back to the base if they want to
571 accurately take advantage of the new device bounds.
572 */
573
reed@google.com42aea282012-03-28 16:19:15 +0000574 SkIRect bounds;
575 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000576 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000577 } else {
578 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000579 }
reed@google.com42aea282012-03-28 16:19:15 +0000580 // now jam our 1st clip to be bounds, and intersect the rest with that
581 rec->fRasterClip->setRect(bounds);
582 while ((rec = (MCRec*)iter.next()) != NULL) {
583 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
584 }
585
reed@android.com8a1c16f2008-12-17 15:59:43 +0000586 return device;
587}
588
reed@google.comaf951c92011-06-16 19:10:39 +0000589SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) {
590 SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (bitmap)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000591 device->unref();
592 return device;
593}
594
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000595bool SkCanvas::readPixels(SkBitmap* bitmap,
596 int x, int y,
597 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000598 SkDevice* device = this->getDevice();
599 if (!device) {
600 return false;
601 }
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000602 return device->readPixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000603}
604
bsalomon@google.comc6980972011-11-02 19:57:21 +0000605bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
bsalomon@google.comace7bd52011-11-02 19:39:51 +0000606 SkDevice* device = this->getDevice();
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000607
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000608 SkIRect bounds;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000609 bounds.set(0, 0, device->width(), device->height());
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000610 if (!bounds.intersect(srcRect)) {
611 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000612 }
613
614 SkBitmap tmp;
615 tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
616 bounds.height());
617 if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
618 bitmap->swap(tmp);
619 return true;
620 } else {
reed@google.com51df9e32010-12-23 19:29:18 +0000621 return false;
622 }
reed@google.com51df9e32010-12-23 19:29:18 +0000623}
624
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000625void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
626 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000627 SkDevice* device = this->getDevice();
628 if (device) {
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000629 device->writePixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000630 }
631}
632
junov@google.com4370aed2012-01-18 16:21:08 +0000633SkCanvas* SkCanvas::canvasForDrawIter() {
634 return this;
635}
636
reed@android.com8a1c16f2008-12-17 15:59:43 +0000637//////////////////////////////////////////////////////////////////////////////
638
reed@android.com8a1c16f2008-12-17 15:59:43 +0000639void SkCanvas::updateDeviceCMCache() {
640 if (fDeviceCMDirty) {
641 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000642 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000643 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000644
reed@android.com8a1c16f2008-12-17 15:59:43 +0000645 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000646 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000647 if (fUseExternalMatrix) {
648 layer->updateExternalMatrix(fExternalMatrix,
649 fExternalInverse);
650 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000651 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000652 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000653 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000654 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.comf2b98d62010-12-20 18:26:13 +0000655 if (fUseExternalMatrix) {
656 layer->updateExternalMatrix(fExternalMatrix,
657 fExternalInverse);
658 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000659 } while ((layer = layer->fNext) != NULL);
660 }
661 fDeviceCMDirty = false;
662 }
663}
664
reed@android.comf2b98d62010-12-20 18:26:13 +0000665void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000666 const SkRegion& clip,
667 const SkClipStack& clipStack) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000668 SkASSERT(device);
reed@android.com199f1082009-06-10 02:12:47 +0000669 if (fLastDeviceToGainFocus != device) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000670 device->gainFocus(this, matrix, clip, clipStack);
reed@android.com199f1082009-06-10 02:12:47 +0000671 fLastDeviceToGainFocus = device;
672 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000673}
674
675///////////////////////////////////////////////////////////////////////////////
676
677int SkCanvas::internalSave(SaveFlags flags) {
678 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000679
reed@android.com8a1c16f2008-12-17 15:59:43 +0000680 MCRec* newTop = (MCRec*)fMCStack.push_back();
681 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000682
reed@android.com8a1c16f2008-12-17 15:59:43 +0000683 newTop->fNext = fMCRec;
684 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000685
reed@google.com5c3d1472011-02-22 19:12:23 +0000686 fClipStack.save();
687 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
688
reed@android.com8a1c16f2008-12-17 15:59:43 +0000689 return saveCount;
690}
691
692int SkCanvas::save(SaveFlags flags) {
693 // call shared impl
694 return this->internalSave(flags);
695}
696
697#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
698#define C16MASK (1 << SkBitmap::kRGB_565_Config)
699#define C8MASK (1 << SkBitmap::kA8_Config)
700
701static SkBitmap::Config resolve_config(SkCanvas* canvas,
702 const SkIRect& bounds,
703 SkCanvas::SaveFlags flags,
704 bool* isOpaque) {
705 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
706
707#if 0
708 // loop through and union all the configs we may draw into
709 uint32_t configMask = 0;
710 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
711 {
712 SkDevice* device = canvas->getLayerDevice(i);
713 if (device->intersects(bounds))
714 configMask |= 1 << device->config();
715 }
716
717 // if the caller wants alpha or fullcolor, we can't return 565
718 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
719 SkCanvas::kHasAlphaLayer_SaveFlag))
720 configMask &= ~C16MASK;
721
722 switch (configMask) {
723 case C8MASK: // if we only have A8, return that
724 return SkBitmap::kA8_Config;
725
726 case C16MASK: // if we only have 565, return that
727 return SkBitmap::kRGB_565_Config;
728
729 default:
730 return SkBitmap::kARGB_8888_Config; // default answer
731 }
732#else
733 return SkBitmap::kARGB_8888_Config; // default answer
734#endif
735}
736
737static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
738 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
739}
740
junov@chromium.orga907ac32012-02-24 21:54:07 +0000741bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
742 SkIRect* intersection) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000743 SkIRect clipBounds;
744 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000745 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000746 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000747 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000748 if (NULL != bounds) {
749 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000750
reed@android.com8a1c16f2008-12-17 15:59:43 +0000751 this->getTotalMatrix().mapRect(&r, *bounds);
752 r.roundOut(&ir);
753 // early exit if the layer's bounds are clipped out
754 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000755 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000756 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000757 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000758 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000759 }
760 } else { // no user bounds, so just use the clip
761 ir = clipBounds;
762 }
763
reed@google.com5c3d1472011-02-22 19:12:23 +0000764 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000765
reed@android.com8a1c16f2008-12-17 15:59:43 +0000766 // early exit if the clip is now empty
767 if (bounds_affects_clip(flags) &&
reed@google.com00177082011-10-12 14:34:30 +0000768 !fMCRec->fRasterClip->op(ir, SkRegion::kIntersect_Op)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000769 return false;
770 }
771
772 if (intersection) {
773 *intersection = ir;
774 }
775 return true;
776}
777
778int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
779 SaveFlags flags) {
reed@google.com8926b162012-03-23 15:36:36 +0000780 return this->internalSaveLayer(bounds, paint, flags, false);
781}
782
783int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
784 SaveFlags flags, bool justForImageFilter) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000785 // do this before we create the layer. We don't call the public save() since
786 // that would invoke a possibly overridden virtual
787 int count = this->internalSave(flags);
788
789 fDeviceCMDirty = true;
790
791 SkIRect ir;
792 if (!this->clipRectBounds(bounds, flags, &ir)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000793 return count;
794 }
795
reed@google.comb55deeb2012-01-06 14:43:09 +0000796 // Kill the imagefilter if our device doesn't allow it
797 SkLazyPaint lazyP;
798 if (paint && paint->getImageFilter()) {
799 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000800 if (justForImageFilter) {
801 // early exit if the layer was just for the imageFilter
802 return count;
803 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000804 SkPaint* p = lazyP.set(*paint);
805 p->setImageFilter(NULL);
806 paint = p;
807 }
808 }
809
reed@android.com8a1c16f2008-12-17 15:59:43 +0000810 bool isOpaque;
811 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
812
reed@google.com76dd2772012-01-05 21:15:07 +0000813 SkDevice* device;
814 if (paint && paint->getImageFilter()) {
815 device = this->createCompatibleDevice(config, ir.width(), ir.height(),
816 isOpaque);
817 } else {
818 device = this->createLayerDevice(config, ir.width(), ir.height(),
819 isOpaque);
820 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000821 if (NULL == device) {
822 SkDebugf("Unable to create device for layer.");
823 return count;
824 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000825
reed@google.com6f8f2922011-03-04 22:27:10 +0000826 device->setOrigin(ir.fLeft, ir.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000827 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
828 device->unref();
829
830 layer->fNext = fMCRec->fTopLayer;
831 fMCRec->fLayer = layer;
832 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
833
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000834 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000835 return count;
836}
837
838int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
839 SaveFlags flags) {
840 if (0xFF == alpha) {
841 return this->saveLayer(bounds, NULL, flags);
842 } else {
843 SkPaint tmpPaint;
844 tmpPaint.setAlpha(alpha);
845 return this->saveLayer(bounds, &tmpPaint, flags);
846 }
847}
848
849void SkCanvas::restore() {
850 // check for underflow
851 if (fMCStack.count() > 1) {
852 this->internalRestore();
853 }
854}
855
856void SkCanvas::internalRestore() {
857 SkASSERT(fMCStack.count() != 0);
858
859 fDeviceCMDirty = true;
860 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000861 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000862
reed@google.com5c3d1472011-02-22 19:12:23 +0000863 fClipStack.restore();
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000864 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000865 DeviceCM* layer = fMCRec->fLayer; // may be null
866 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
867 fMCRec->fLayer = NULL;
868
869 // now do the normal restore()
870 fMCRec->~MCRec(); // balanced in save()
871 fMCStack.pop_back();
872 fMCRec = (MCRec*)fMCStack.back();
873
874 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
875 since if we're being recorded, we don't want to record this (the
876 recorder will have already recorded the restore).
877 */
878 if (NULL != layer) {
879 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000880 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000881 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
882 layer->fPaint);
883 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000884 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000885
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000886 SkASSERT(fSaveLayerCount > 0);
887 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000888 }
889 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000890 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000891
892 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000893}
894
895int SkCanvas::getSaveCount() const {
896 return fMCStack.count();
897}
898
899void SkCanvas::restoreToCount(int count) {
900 // sanity check
901 if (count < 1) {
902 count = 1;
903 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000904
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000905 int n = this->getSaveCount() - count;
906 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000907 this->restore();
908 }
909}
910
reed@google.com7c202932011-12-14 18:48:05 +0000911bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000912 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000913}
914
reed@android.com8a1c16f2008-12-17 15:59:43 +0000915/////////////////////////////////////////////////////////////////////////////
916
917// can't draw it if its empty, or its too big for a fixed-point width or height
918static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000919 return bitmap.width() <= 0 || bitmap.height() <= 0
920#ifndef SK_ALLOW_OVER_32K_BITMAPS
921 || bitmap.width() > 32767 || bitmap.height() > 32767
922#endif
923 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000924}
925
reed@android.comf2b98d62010-12-20 18:26:13 +0000926void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000927 const SkMatrix& matrix, const SkPaint* paint) {
928 if (reject_bitmap(bitmap)) {
929 return;
930 }
931
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000932 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000933 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000934 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000935 }
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000936 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000937}
938
reed@google.com76dd2772012-01-05 21:15:07 +0000939#include "SkImageFilter.h"
940
941class DeviceImageFilterProxy : public SkImageFilter::Proxy {
942public:
943 DeviceImageFilterProxy(SkDevice* device) : fDevice(device) {}
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000944
reed@google.com8926b162012-03-23 15:36:36 +0000945 virtual SkDevice* createDevice(int w, int h) SK_OVERRIDE {
946 return fDevice->createCompatibleDevice(SkBitmap::kARGB_8888_Config,
947 w, h, false);
948 }
949 virtual bool canHandleImageFilter(SkImageFilter* filter) SK_OVERRIDE {
950 return fDevice->canHandleImageFilter(filter);
951 }
952 virtual bool filterImage(SkImageFilter* filter, const SkBitmap& src,
reed@google.com76dd2772012-01-05 21:15:07 +0000953 const SkMatrix& ctm,
reed@google.com8926b162012-03-23 15:36:36 +0000954 SkBitmap* result, SkIPoint* offset) SK_OVERRIDE {
955 return fDevice->filterImage(filter, src, ctm, result, offset);
956 }
957
reed@google.com76dd2772012-01-05 21:15:07 +0000958private:
959 SkDevice* fDevice;
960};
961
reed@google.com8926b162012-03-23 15:36:36 +0000962void SkCanvas::internalDrawDevice(SkDevice* srcDev, int x, int y,
963 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000964 SkPaint tmp;
965 if (NULL == paint) {
966 tmp.setDither(true);
967 paint = &tmp;
968 }
reed@google.com4b226022011-01-11 18:32:13 +0000969
reed@google.com8926b162012-03-23 15:36:36 +0000970 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000971 while (iter.next()) {
reed@google.comb55deeb2012-01-06 14:43:09 +0000972 SkDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +0000973 paint = &looper.paint();
974 SkImageFilter* filter = paint->getImageFilter();
975 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +0000976 if (filter && !dstDev->canHandleImageFilter(filter)) {
reed@google.comb55deeb2012-01-06 14:43:09 +0000977 DeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +0000978 SkBitmap dst;
reed@google.comb55deeb2012-01-06 14:43:09 +0000979 const SkBitmap& src = srcDev->accessBitmap(false);
reed@google.com76dd2772012-01-05 21:15:07 +0000980 if (filter->filterImage(&proxy, src, *iter.fMatrix, &dst, &pos)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +0000981 SkPaint tmpUnfiltered(*paint);
982 tmpUnfiltered.setImageFilter(NULL);
983 dstDev->drawSprite(iter, dst, pos.x(), pos.y(), tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +0000984 }
985 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +0000986 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +0000987 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000988 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000989 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +0000990}
991
reed@google.com8926b162012-03-23 15:36:36 +0000992void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
993 const SkPaint* paint) {
994 SkDEBUGCODE(bitmap.validate();)
995
996 if (reject_bitmap(bitmap)) {
997 return;
998 }
999
1000 SkPaint tmp;
1001 if (NULL == paint) {
1002 paint = &tmp;
1003 }
1004
1005 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
1006
1007 while (iter.next()) {
1008 paint = &looper.paint();
1009 SkImageFilter* filter = paint->getImageFilter();
1010 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1011 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
1012 DeviceImageFilterProxy proxy(iter.fDevice);
1013 SkBitmap dst;
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001014 if (filter->filterImage(&proxy, bitmap, *iter.fMatrix,
1015 &dst, &pos)) {
1016 SkPaint tmpUnfiltered(*paint);
1017 tmpUnfiltered.setImageFilter(NULL);
1018 iter.fDevice->drawSprite(iter, dst, pos.x(), pos.y(),
1019 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001020 }
1021 } else {
1022 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1023 }
1024 }
1025 LOOPER_END
1026}
1027
reed@android.com8a1c16f2008-12-17 15:59:43 +00001028/////////////////////////////////////////////////////////////////////////////
1029
1030bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
1031 fDeviceCMDirty = true;
1032 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001033 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001034 return fMCRec->fMatrix->preTranslate(dx, dy);
1035}
1036
1037bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
1038 fDeviceCMDirty = true;
1039 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001040 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001041 return fMCRec->fMatrix->preScale(sx, sy);
1042}
1043
1044bool SkCanvas::rotate(SkScalar degrees) {
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->preRotate(degrees);
1049}
1050
1051bool SkCanvas::skew(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->preSkew(sx, sy);
1056}
1057
1058bool SkCanvas::concat(const SkMatrix& matrix) {
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->preConcat(matrix);
1063}
1064
1065void SkCanvas::setMatrix(const SkMatrix& matrix) {
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 *fMCRec->fMatrix = matrix;
1070}
1071
1072// this is not virtual, so it must call a virtual method so that subclasses
1073// will see its action
1074void SkCanvas::resetMatrix() {
1075 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001076
reed@android.com8a1c16f2008-12-17 15:59:43 +00001077 matrix.reset();
1078 this->setMatrix(matrix);
1079}
1080
1081//////////////////////////////////////////////////////////////////////////////
1082
reed@google.comc42d35d2011-10-12 11:57:42 +00001083bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001084 AutoValidateClip avc(this);
1085
reed@android.com8a1c16f2008-12-17 15:59:43 +00001086 fDeviceCMDirty = true;
1087 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001088 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001089
1090 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +00001091 // for these simpler matrices, we can stay a rect ever after applying
1092 // the matrix. This means we don't have to a) make a path, and b) tell
1093 // the region code to scan-convert the path, only to discover that it
1094 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001095 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001096
1097 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +00001098 fClipStack.clipDevRect(r, op, doAA);
1099 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001100 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +00001101 // since we're rotate or some such thing, we convert the rect to a path
1102 // and clip against that, since it can handle any matrix. However, to
1103 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1104 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001105 SkPath path;
1106
1107 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +00001108 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001109 }
1110}
1111
reed@google.com00177082011-10-12 14:34:30 +00001112static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +00001113 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001114 // base is used to limit the size (and therefore memory allocation) of the
1115 // region that results from scan converting devPath.
1116 SkRegion base;
1117
reed@google.com819c9212011-02-23 18:56:55 +00001118 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001119 // since we are intersect, we can do better (tighter) with currRgn's
1120 // bounds, than just using the device. However, if currRgn is complex,
1121 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001122 if (currClip->isRect()) {
1123 return currClip->setPath(devPath, *currClip, doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001124 } else {
reed@google.com00177082011-10-12 14:34:30 +00001125 base.setRect(currClip->getBounds());
1126 SkRasterClip clip;
1127 clip.setPath(devPath, base, doAA);
1128 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001129 }
reed@google.com819c9212011-02-23 18:56:55 +00001130 } else {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001131 const SkDevice* device = canvas->getDevice();
1132 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001133
1134 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +00001135 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001136 } else {
reed@google.com00177082011-10-12 14:34:30 +00001137 SkRasterClip clip;
1138 clip.setPath(devPath, base, doAA);
1139 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001140 }
1141 }
1142}
1143
reed@google.comc42d35d2011-10-12 11:57:42 +00001144bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001145 AutoValidateClip avc(this);
1146
reed@android.com8a1c16f2008-12-17 15:59:43 +00001147 fDeviceCMDirty = true;
1148 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001149 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001150
1151 SkPath devPath;
1152 path.transform(*fMCRec->fMatrix, &devPath);
1153
reed@google.comfe701122011-11-08 19:41:23 +00001154 // Check if the transfomation, or the original path itself
1155 // made us empty. Note this can also happen if we contained NaN
1156 // values. computing the bounds detects this, and will set our
1157 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1158 if (devPath.getBounds().isEmpty()) {
1159 // resetting the path will remove any NaN or other wanky values
1160 // that might upset our scan converter.
1161 devPath.reset();
1162 }
1163
reed@google.com5c3d1472011-02-22 19:12:23 +00001164 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +00001165 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +00001166
reed@google.com00177082011-10-12 14:34:30 +00001167 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001168}
1169
1170bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001171 AutoValidateClip avc(this);
1172
reed@android.com8a1c16f2008-12-17 15:59:43 +00001173 fDeviceCMDirty = true;
1174 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001175 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001176
reed@google.com5c3d1472011-02-22 19:12:23 +00001177 // todo: signal fClipStack that we have a region, and therefore (I guess)
1178 // we have to ignore it, and use the region directly?
1179 fClipStack.clipDevRect(rgn.getBounds());
1180
reed@google.com00177082011-10-12 14:34:30 +00001181 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001182}
1183
reed@google.com819c9212011-02-23 18:56:55 +00001184#ifdef SK_DEBUG
1185void SkCanvas::validateClip() const {
1186 // construct clipRgn from the clipstack
1187 const SkDevice* device = this->getDevice();
1188 SkIRect ir;
1189 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001190 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001191
1192 SkClipStack::B2FIter iter(fClipStack);
1193 const SkClipStack::B2FIter::Clip* clip;
1194 while ((clip = iter.next()) != NULL) {
1195 if (clip->fPath) {
reed@google.com00177082011-10-12 14:34:30 +00001196 clipPathHelper(this, &tmpClip, *clip->fPath, clip->fOp, clip->fDoAA);
reed@google.com819c9212011-02-23 18:56:55 +00001197 } else if (clip->fRect) {
1198 clip->fRect->round(&ir);
reed@google.com00177082011-10-12 14:34:30 +00001199 tmpClip.op(ir, clip->fOp);
reed@google.com819c9212011-02-23 18:56:55 +00001200 } else {
reed@google.com00177082011-10-12 14:34:30 +00001201 tmpClip.setEmpty();
reed@google.com819c9212011-02-23 18:56:55 +00001202 }
1203 }
1204
reed@google.com6f8f2922011-03-04 22:27:10 +00001205#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001206 // now compare against the current rgn
1207 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001208 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001209#endif
reed@google.com819c9212011-02-23 18:56:55 +00001210}
1211#endif
1212
reed@google.com90c07ea2012-04-13 13:50:27 +00001213void SkCanvas::replayClips(ClipVisitor* visitor) const {
1214 SkClipStack::B2FIter iter(fClipStack);
1215 const SkClipStack::B2FIter::Clip* clip;
1216
1217 SkRect empty = {};
1218 while ((clip = iter.next()) != NULL) {
1219 if (clip->fPath) {
1220 visitor->clipPath(*clip->fPath, clip->fOp, clip->fDoAA);
1221 } else if (clip->fRect) {
1222 visitor->clipRect(*clip->fRect, clip->fOp, clip->fDoAA);
1223 } else {
1224 visitor->clipRect(empty, SkRegion::kIntersect_Op, false);
1225 }
1226 }
1227}
1228
reed@google.com5c3d1472011-02-22 19:12:23 +00001229///////////////////////////////////////////////////////////////////////////////
1230
reed@android.comba09de42010-02-05 20:46:05 +00001231void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001232 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001233 SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
1234 fLocalBoundsCompareTypeBW;
1235
1236 if (!this->getClipBounds(&r, et)) {
1237 rCompare.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001238 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001239 rCompare.set(SkScalarToCompareType(r.fLeft),
1240 SkScalarToCompareType(r.fTop),
1241 SkScalarToCompareType(r.fRight),
1242 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001243 }
1244}
1245
reed@android.comd252db02009-04-01 18:31:44 +00001246/* current impl ignores edgetype, and relies on
1247 getLocalClipBoundsCompareType(), which always returns a value assuming
1248 antialiasing (worst case)
1249 */
reed@android.comba09de42010-02-05 20:46:05 +00001250bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001251
reed@google.com16078632011-12-06 18:56:37 +00001252 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001253 return true;
1254
reed@google.com00177082011-10-12 14:34:30 +00001255 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001256 return true;
1257 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001258
tomhudson@google.com8d430182011-06-06 19:11:19 +00001259 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001260 SkRect dst;
1261 fMCRec->fMatrix->mapRect(&dst, rect);
1262 SkIRect idst;
1263 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001264 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001265 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001266 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
reed@android.comd252db02009-04-01 18:31:44 +00001267
reed@android.coma380ae42009-07-21 01:17:02 +00001268 // for speed, do the most likely reject compares first
1269 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1270 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1271 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1272 return true;
1273 }
1274 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1275 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1276 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1277 return true;
1278 }
1279 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001280 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001281}
1282
1283bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +00001284 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001285}
1286
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001287static inline int pinIntForScalar(int x) {
1288#ifdef SK_SCALAR_IS_FIXED
1289 if (x < SK_MinS16) {
1290 x = SK_MinS16;
1291 } else if (x > SK_MaxS16) {
1292 x = SK_MaxS16;
1293 }
1294#endif
1295 return x;
1296}
1297
reed@android.com8a1c16f2008-12-17 15:59:43 +00001298bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001299 SkIRect ibounds;
1300 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001301 return false;
1302 }
1303
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001304 SkMatrix inverse;
1305 // if we can't invert the CTM, we can't return local clip bounds
1306 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001307 if (bounds) {
1308 bounds->setEmpty();
1309 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001310 return false;
1311 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001312
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001313 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001314 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001315 // adjust it outwards if we are antialiasing
1316 int inset = (kAA_EdgeType == et);
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001317
1318 // SkRect::iset() will correctly assert if we pass a value out of range
1319 // (when SkScalar==fixed), so we pin to legal values. This does not
1320 // really returnt the correct answer, but its the best we can do given
1321 // that we've promised to return SkRect (even though we support devices
1322 // that can be larger than 32K in width or height).
1323 r.iset(pinIntForScalar(ibounds.fLeft - inset),
1324 pinIntForScalar(ibounds.fTop - inset),
1325 pinIntForScalar(ibounds.fRight + inset),
1326 pinIntForScalar(ibounds.fBottom + inset));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001327 inverse.mapRect(bounds, r);
1328 }
1329 return true;
1330}
1331
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001332bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001333 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001334 if (clip.isEmpty()) {
1335 if (bounds) {
1336 bounds->setEmpty();
1337 }
1338 return false;
1339 }
1340
1341 if (NULL != bounds) {
1342 *bounds = clip.getBounds();
1343 }
1344 return true;
1345}
1346
reed@android.com8a1c16f2008-12-17 15:59:43 +00001347const SkMatrix& SkCanvas::getTotalMatrix() const {
1348 return *fMCRec->fMatrix;
1349}
1350
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001351SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001352 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1353 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001354 return kComplex_ClipType;
1355}
1356
reed@android.com8a1c16f2008-12-17 15:59:43 +00001357const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001358 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001359}
1360
reed@android.comf2b98d62010-12-20 18:26:13 +00001361void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1362 if (NULL == matrix || matrix->isIdentity()) {
1363 if (fUseExternalMatrix) {
1364 fDeviceCMDirty = true;
1365 }
1366 fUseExternalMatrix = false;
1367 } else {
1368 fUseExternalMatrix = true;
1369 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.com4b226022011-01-11 18:32:13 +00001370
reed@android.comf2b98d62010-12-20 18:26:13 +00001371 fExternalMatrix = *matrix;
reed@google.comfc9a3be2012-04-12 13:52:14 +00001372 if (!matrix->invert(&fExternalInverse)) {
1373 fExternalInverse.reset();
1374 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001375 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001376}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001377
bsalomon@google.come97f0852011-06-17 13:10:25 +00001378SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
1379 int width, int height,
1380 bool isOpaque) {
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001381 SkDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001382 if (device) {
1383 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1384 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001385 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001386 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001387 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001388}
1389
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001390SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
bsalomon@google.come97f0852011-06-17 13:10:25 +00001391 int width, int height,
1392 bool isOpaque) {
1393 SkDevice* device = this->getDevice();
1394 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001395 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001396 } else {
1397 return NULL;
1398 }
1399}
1400
1401
reed@android.com8a1c16f2008-12-17 15:59:43 +00001402//////////////////////////////////////////////////////////////////////////////
1403// These are the virtual drawing methods
1404//////////////////////////////////////////////////////////////////////////////
1405
reed@google.com2a981812011-04-14 18:59:28 +00001406void SkCanvas::clear(SkColor color) {
1407 SkDrawIter iter(this);
1408
1409 while (iter.next()) {
1410 iter.fDevice->clear(color);
1411 }
1412}
1413
reed@android.com8a1c16f2008-12-17 15:59:43 +00001414void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001415 this->internalDrawPaint(paint);
1416}
1417
1418void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001419 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001420
1421 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001422 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001423 }
1424
reed@google.com4e2b3d32011-04-07 14:18:59 +00001425 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001426}
1427
1428void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1429 const SkPaint& paint) {
1430 if ((long)count <= 0) {
1431 return;
1432 }
1433
1434 SkASSERT(pts != NULL);
1435
reed@google.com4e2b3d32011-04-07 14:18:59 +00001436 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001437
reed@android.com8a1c16f2008-12-17 15:59:43 +00001438 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001439 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001440 }
reed@google.com4b226022011-01-11 18:32:13 +00001441
reed@google.com4e2b3d32011-04-07 14:18:59 +00001442 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001443}
1444
1445void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1446 if (paint.canComputeFastBounds()) {
1447 SkRect storage;
1448 if (this->quickReject(paint.computeFastBounds(r, &storage),
1449 paint2EdgeType(&paint))) {
1450 return;
1451 }
1452 }
reed@google.com4b226022011-01-11 18:32:13 +00001453
reed@google.com4e2b3d32011-04-07 14:18:59 +00001454 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001455
1456 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001457 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001458 }
1459
reed@google.com4e2b3d32011-04-07 14:18:59 +00001460 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001461}
1462
1463void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001464 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001465 SkRect storage;
1466 const SkRect& bounds = path.getBounds();
1467 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001468 paint2EdgeType(&paint))) {
1469 return;
1470 }
1471 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001472 if (path.isEmpty()) {
1473 if (path.isInverseFillType()) {
1474 this->internalDrawPaint(paint);
1475 }
1476 return;
1477 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001478
reed@google.com4e2b3d32011-04-07 14:18:59 +00001479 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001480
1481 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001482 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001483 }
1484
reed@google.com4e2b3d32011-04-07 14:18:59 +00001485 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001486}
1487
1488void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1489 const SkPaint* paint) {
1490 SkDEBUGCODE(bitmap.validate();)
1491
reed@google.com3d608122011-11-21 15:16:16 +00001492 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001493 SkRect bounds = {
1494 x, y,
1495 x + SkIntToScalar(bitmap.width()),
1496 y + SkIntToScalar(bitmap.height())
1497 };
1498 if (paint) {
1499 (void)paint->computeFastBounds(bounds, &bounds);
1500 }
1501 if (this->quickReject(bounds, paint2EdgeType(paint))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001502 return;
1503 }
1504 }
reed@google.com4b226022011-01-11 18:32:13 +00001505
reed@android.com8a1c16f2008-12-17 15:59:43 +00001506 SkMatrix matrix;
1507 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001508 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001509}
1510
reed@google.com9987ec32011-09-07 11:57:52 +00001511// this one is non-virtual, so it can be called safely by other canvas apis
1512void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1513 const SkRect& dst, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001514 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1515 return;
1516 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001517
reed@android.com8a1c16f2008-12-17 15:59:43 +00001518 // do this now, to avoid the cost of calling extract for RLE bitmaps
reed@google.com3d608122011-11-21 15:16:16 +00001519 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001520 SkRect storage;
1521 const SkRect* bounds = &dst;
1522 if (paint) {
1523 bounds = &paint->computeFastBounds(dst, &storage);
1524 }
1525 if (this->quickReject(*bounds, paint2EdgeType(paint))) {
reed@google.com3d608122011-11-21 15:16:16 +00001526 return;
1527 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001528 }
reed@google.com3d608122011-11-21 15:16:16 +00001529
reed@android.com8a1c16f2008-12-17 15:59:43 +00001530 const SkBitmap* bitmapPtr = &bitmap;
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001531
reed@android.com8a1c16f2008-12-17 15:59:43 +00001532 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001533 SkRect tmpSrc;
1534 if (src) {
1535 tmpSrc.set(*src);
1536 // if the extract process clipped off the top or left of the
1537 // original, we adjust for that here to get the position right.
1538 if (tmpSrc.fLeft > 0) {
1539 tmpSrc.fRight -= tmpSrc.fLeft;
1540 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001541 }
reed@android.com87899992009-10-16 14:48:38 +00001542 if (tmpSrc.fTop > 0) {
1543 tmpSrc.fBottom -= tmpSrc.fTop;
1544 tmpSrc.fTop = 0;
1545 }
1546 } else {
1547 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1548 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001549 }
reed@android.com87899992009-10-16 14:48:38 +00001550 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001551
reed@android.comf2b98d62010-12-20 18:26:13 +00001552 // ensure that src is "valid" before we pass it to our internal routines
1553 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1554 SkIRect tmpISrc;
1555 if (src) {
1556 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
reed@google.com2ade0862011-03-17 17:48:04 +00001557 if (!tmpISrc.intersect(*src)) {
1558 return;
1559 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001560 src = &tmpISrc;
1561 }
1562 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001563}
1564
reed@google.com9987ec32011-09-07 11:57:52 +00001565void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1566 const SkRect& dst, const SkPaint* paint) {
1567 SkDEBUGCODE(bitmap.validate();)
1568 this->internalDrawBitmapRect(bitmap, src, dst, paint);
1569}
1570
reed@android.com8a1c16f2008-12-17 15:59:43 +00001571void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1572 const SkPaint* paint) {
1573 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001574 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001575}
1576
reed@android.comf2b98d62010-12-20 18:26:13 +00001577void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1578 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001579 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001580
reed@google.com4e2b3d32011-04-07 14:18:59 +00001581 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001582
reed@android.com8a1c16f2008-12-17 15:59:43 +00001583 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001584 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001585 }
reed@android.com9b039062009-02-11 15:09:58 +00001586
reed@google.com4e2b3d32011-04-07 14:18:59 +00001587 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001588}
1589
reed@google.com9987ec32011-09-07 11:57:52 +00001590void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1591 const SkIRect& center, const SkRect& dst,
1592 const SkPaint* paint) {
reed@google.com3d608122011-11-21 15:16:16 +00001593 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001594 SkRect storage;
1595 const SkRect* bounds = &dst;
1596 if (paint) {
1597 bounds = &paint->computeFastBounds(dst, &storage);
1598 }
1599 if (this->quickReject(*bounds, paint2EdgeType(paint))) {
reed@google.com3d608122011-11-21 15:16:16 +00001600 return;
1601 }
1602 }
1603
reed@google.com9987ec32011-09-07 11:57:52 +00001604 const int32_t w = bitmap.width();
1605 const int32_t h = bitmap.height();
1606
1607 SkIRect c = center;
1608 // pin center to the bounds of the bitmap
1609 c.fLeft = SkMax32(0, center.fLeft);
1610 c.fTop = SkMax32(0, center.fTop);
1611 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1612 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1613
1614 const int32_t srcX[4] = { 0, c.fLeft, c.fRight, w };
1615 const int32_t srcY[4] = { 0, c.fTop, c.fBottom, h };
1616 SkScalar dstX[4] = {
1617 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1618 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1619 };
1620 SkScalar dstY[4] = {
1621 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1622 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1623 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001624
reed@google.com9987ec32011-09-07 11:57:52 +00001625 if (dstX[1] > dstX[2]) {
1626 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1627 dstX[2] = dstX[1];
1628 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001629
reed@google.com9987ec32011-09-07 11:57:52 +00001630 if (dstY[1] > dstY[2]) {
1631 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1632 dstY[2] = dstY[1];
1633 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001634
reed@google.com9987ec32011-09-07 11:57:52 +00001635 SkIRect s;
1636 SkRect d;
1637 for (int y = 0; y < 3; y++) {
1638 s.fTop = srcY[y];
1639 s.fBottom = srcY[y+1];
1640 d.fTop = dstY[y];
1641 d.fBottom = dstY[y+1];
1642 for (int x = 0; x < 3; x++) {
1643 s.fLeft = srcX[x];
1644 s.fRight = srcX[x+1];
1645 d.fLeft = dstX[x];
1646 d.fRight = dstX[x+1];
1647 this->internalDrawBitmapRect(bitmap, &s, d, paint);
1648 }
1649 }
1650}
1651
1652void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1653 const SkRect& dst, const SkPaint* paint) {
1654 SkDEBUGCODE(bitmap.validate();)
1655
1656 // Need a device entry-point, so gpu can use a mesh
1657 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1658}
1659
reed@google.comf67e4cf2011-03-15 20:56:58 +00001660class SkDeviceFilteredPaint {
1661public:
1662 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1663 SkDevice::TextFlags flags;
1664 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001665 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001666 newPaint->setFlags(flags.fFlags);
1667 newPaint->setHinting(flags.fHinting);
1668 fPaint = newPaint;
1669 } else {
1670 fPaint = &paint;
1671 }
1672 }
1673
reed@google.comf67e4cf2011-03-15 20:56:58 +00001674 const SkPaint& paint() const { return *fPaint; }
1675
1676private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001677 const SkPaint* fPaint;
1678 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001679};
1680
bungeman@google.com52c748b2011-08-22 21:30:43 +00001681void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1682 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001683 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001684 draw.fDevice->drawRect(draw, r, paint);
1685 } else {
1686 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001687 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001688 draw.fDevice->drawRect(draw, r, p);
1689 }
1690}
1691
1692void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1693 const char text[], size_t byteLength,
1694 SkScalar x, SkScalar y) {
1695 SkASSERT(byteLength == 0 || text != NULL);
1696
1697 // nothing to draw
1698 if (text == NULL || byteLength == 0 ||
1699 draw.fClip->isEmpty() ||
1700 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1701 return;
1702 }
1703
1704 SkScalar width = 0;
1705 SkPoint start;
1706
1707 start.set(0, 0); // to avoid warning
1708 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1709 SkPaint::kStrikeThruText_Flag)) {
1710 width = paint.measureText(text, byteLength);
1711
1712 SkScalar offsetX = 0;
1713 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1714 offsetX = SkScalarHalf(width);
1715 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1716 offsetX = width;
1717 }
1718 start.set(x - offsetX, y);
1719 }
1720
1721 if (0 == width) {
1722 return;
1723 }
1724
1725 uint32_t flags = paint.getFlags();
1726
1727 if (flags & (SkPaint::kUnderlineText_Flag |
1728 SkPaint::kStrikeThruText_Flag)) {
1729 SkScalar textSize = paint.getTextSize();
1730 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1731 SkRect r;
1732
1733 r.fLeft = start.fX;
1734 r.fRight = start.fX + width;
1735
1736 if (flags & SkPaint::kUnderlineText_Flag) {
1737 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1738 start.fY);
1739 r.fTop = offset;
1740 r.fBottom = offset + height;
1741 DrawRect(draw, paint, r, textSize);
1742 }
1743 if (flags & SkPaint::kStrikeThruText_Flag) {
1744 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1745 start.fY);
1746 r.fTop = offset;
1747 r.fBottom = offset + height;
1748 DrawRect(draw, paint, r, textSize);
1749 }
1750 }
1751}
1752
reed@android.com8a1c16f2008-12-17 15:59:43 +00001753void SkCanvas::drawText(const void* text, size_t byteLength,
1754 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001755 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001756
1757 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001758 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001759 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001760 DrawTextDecorations(iter, dfp.paint(),
1761 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001762 }
1763
reed@google.com4e2b3d32011-04-07 14:18:59 +00001764 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001765}
1766
1767void SkCanvas::drawPosText(const void* text, size_t byteLength,
1768 const SkPoint pos[], const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001769 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001770
reed@android.com8a1c16f2008-12-17 15:59:43 +00001771 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001772 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001773 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001774 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001775 }
reed@google.com4b226022011-01-11 18:32:13 +00001776
reed@google.com4e2b3d32011-04-07 14:18:59 +00001777 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001778}
1779
1780void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1781 const SkScalar xpos[], SkScalar constY,
1782 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001783 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001784
reed@android.com8a1c16f2008-12-17 15:59:43 +00001785 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001786 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001787 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001788 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001789 }
reed@google.com4b226022011-01-11 18:32:13 +00001790
reed@google.com4e2b3d32011-04-07 14:18:59 +00001791 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001792}
1793
1794void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1795 const SkPath& path, const SkMatrix* matrix,
1796 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001797 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001798
1799 while (iter.next()) {
1800 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001801 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001802 }
1803
reed@google.com4e2b3d32011-04-07 14:18:59 +00001804 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001805}
1806
djsollen@google.com56c69772011-11-08 19:00:26 +00001807#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001808void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1809 const SkPoint pos[], const SkPaint& paint,
1810 const SkPath& path, const SkMatrix* matrix) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001811 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001812
1813 while (iter.next()) {
1814 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001815 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001816 }
1817
reed@google.com4e2b3d32011-04-07 14:18:59 +00001818 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001819}
1820#endif
1821
reed@android.com8a1c16f2008-12-17 15:59:43 +00001822void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1823 const SkPoint verts[], const SkPoint texs[],
1824 const SkColor colors[], SkXfermode* xmode,
1825 const uint16_t indices[], int indexCount,
1826 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001827 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001828
reed@android.com8a1c16f2008-12-17 15:59:43 +00001829 while (iter.next()) {
1830 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001831 colors, xmode, indices, indexCount,
1832 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001833 }
reed@google.com4b226022011-01-11 18:32:13 +00001834
reed@google.com4e2b3d32011-04-07 14:18:59 +00001835 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001836}
1837
reed@android.comcb608442009-12-04 21:32:27 +00001838void SkCanvas::drawData(const void* data, size_t length) {
1839 // do nothing. Subclasses may do something with the data
1840}
1841
reed@android.com8a1c16f2008-12-17 15:59:43 +00001842//////////////////////////////////////////////////////////////////////////////
1843// These methods are NOT virtual, and therefore must call back into virtual
1844// methods, rather than actually drawing themselves.
1845//////////////////////////////////////////////////////////////////////////////
1846
1847void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001848 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001849 SkPaint paint;
1850
1851 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001852 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001853 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001854 }
1855 this->drawPaint(paint);
1856}
1857
reed@android.com845fdac2009-06-23 03:01:32 +00001858void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001859 SkPaint paint;
1860
1861 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001862 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001863 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001864 }
1865 this->drawPaint(paint);
1866}
1867
1868void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1869 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001870
reed@android.com8a1c16f2008-12-17 15:59:43 +00001871 pt.set(x, y);
1872 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1873}
1874
1875void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1876 SkPoint pt;
1877 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001878
reed@android.com8a1c16f2008-12-17 15:59:43 +00001879 pt.set(x, y);
1880 paint.setColor(color);
1881 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1882}
1883
1884void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1885 const SkPaint& paint) {
1886 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001887
reed@android.com8a1c16f2008-12-17 15:59:43 +00001888 pts[0].set(x0, y0);
1889 pts[1].set(x1, y1);
1890 this->drawPoints(kLines_PointMode, 2, pts, paint);
1891}
1892
1893void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1894 SkScalar right, SkScalar bottom,
1895 const SkPaint& paint) {
1896 SkRect r;
1897
1898 r.set(left, top, right, bottom);
1899 this->drawRect(r, paint);
1900}
1901
1902void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1903 const SkPaint& paint) {
1904 if (radius < 0) {
1905 radius = 0;
1906 }
1907
1908 SkRect r;
1909 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001910
reed@android.com8a1c16f2008-12-17 15:59:43 +00001911 if (paint.canComputeFastBounds()) {
1912 SkRect storage;
1913 if (this->quickReject(paint.computeFastBounds(r, &storage),
1914 paint2EdgeType(&paint))) {
1915 return;
1916 }
1917 }
reed@google.com4b226022011-01-11 18:32:13 +00001918
reed@android.com8a1c16f2008-12-17 15:59:43 +00001919 SkPath path;
1920 path.addOval(r);
1921 this->drawPath(path, paint);
1922}
1923
1924void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1925 const SkPaint& paint) {
1926 if (rx > 0 && ry > 0) {
1927 if (paint.canComputeFastBounds()) {
1928 SkRect storage;
1929 if (this->quickReject(paint.computeFastBounds(r, &storage),
1930 paint2EdgeType(&paint))) {
1931 return;
1932 }
1933 }
1934
1935 SkPath path;
1936 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1937 this->drawPath(path, paint);
1938 } else {
1939 this->drawRect(r, paint);
1940 }
1941}
1942
1943void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1944 if (paint.canComputeFastBounds()) {
1945 SkRect storage;
1946 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1947 paint2EdgeType(&paint))) {
1948 return;
1949 }
1950 }
1951
1952 SkPath path;
1953 path.addOval(oval);
1954 this->drawPath(path, paint);
1955}
1956
1957void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1958 SkScalar sweepAngle, bool useCenter,
1959 const SkPaint& paint) {
1960 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1961 this->drawOval(oval, paint);
1962 } else {
1963 SkPath path;
1964 if (useCenter) {
1965 path.moveTo(oval.centerX(), oval.centerY());
1966 }
1967 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1968 if (useCenter) {
1969 path.close();
1970 }
1971 this->drawPath(path, paint);
1972 }
1973}
1974
1975void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1976 const SkPath& path, SkScalar hOffset,
1977 SkScalar vOffset, const SkPaint& paint) {
1978 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001979
reed@android.com8a1c16f2008-12-17 15:59:43 +00001980 matrix.setTranslate(hOffset, vOffset);
1981 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1982}
1983
reed@android.comf76bacf2009-05-13 14:00:33 +00001984///////////////////////////////////////////////////////////////////////////////
1985
reed@android.com8a1c16f2008-12-17 15:59:43 +00001986void SkCanvas::drawPicture(SkPicture& picture) {
1987 int saveCount = save();
1988 picture.draw(this);
1989 restoreToCount(saveCount);
1990}
1991
reed@android.com8a1c16f2008-12-17 15:59:43 +00001992///////////////////////////////////////////////////////////////////////////////
1993///////////////////////////////////////////////////////////////////////////////
1994
1995SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00001996 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001997
1998 SkASSERT(canvas);
1999
2000 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2001 fDone = !fImpl->next();
2002}
2003
2004SkCanvas::LayerIter::~LayerIter() {
2005 fImpl->~SkDrawIter();
2006}
2007
2008void SkCanvas::LayerIter::next() {
2009 fDone = !fImpl->next();
2010}
2011
2012SkDevice* SkCanvas::LayerIter::device() const {
2013 return fImpl->getDevice();
2014}
2015
2016const SkMatrix& SkCanvas::LayerIter::matrix() const {
2017 return fImpl->getMatrix();
2018}
2019
2020const SkPaint& SkCanvas::LayerIter::paint() const {
2021 const SkPaint* paint = fImpl->getPaint();
2022 if (NULL == paint) {
2023 paint = &fDefaultPaint;
2024 }
2025 return *paint;
2026}
2027
2028const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2029int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2030int SkCanvas::LayerIter::y() const { return fImpl->getY(); }