blob: dde357049cc5c77889255327d7e1c0767a919a6d [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.com7d7ca792011-02-23 22:39:18 +0000221 fClipStack = &canvas->getTotalClipStack();
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;
reed@google.com7c202932011-12-14 18:48:05 +0000442 fLayerCount = 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
reed@google.com7c202932011-12-14 18:48:05 +0000482 SkASSERT(0 == fLayerCount);
483
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
574 if (NULL == device) {
reed@google.com00177082011-10-12 14:34:30 +0000575 rec->fRasterClip->setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000576 while ((rec = (MCRec*)iter.next()) != NULL) {
reed@google.com00177082011-10-12 14:34:30 +0000577 (void)rec->fRasterClip->setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000578 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000579 fClipStack.reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000580 } else {
581 // compute our total bounds for all devices
582 SkIRect bounds;
reed@google.com4b226022011-01-11 18:32:13 +0000583
reed@android.com8a1c16f2008-12-17 15:59:43 +0000584 bounds.set(0, 0, device->width(), device->height());
585
586 // now jam our 1st clip to be bounds, and intersect the rest with that
reed@google.com00177082011-10-12 14:34:30 +0000587 rec->fRasterClip->setRect(bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000588 while ((rec = (MCRec*)iter.next()) != NULL) {
reed@google.com00177082011-10-12 14:34:30 +0000589 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000590 }
591 }
592 return device;
593}
594
reed@google.comaf951c92011-06-16 19:10:39 +0000595SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) {
596 SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (bitmap)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000597 device->unref();
598 return device;
599}
600
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000601bool SkCanvas::readPixels(SkBitmap* bitmap,
602 int x, int y,
603 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000604 SkDevice* device = this->getDevice();
605 if (!device) {
606 return false;
607 }
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000608 return device->readPixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000609}
610
bsalomon@google.comc6980972011-11-02 19:57:21 +0000611bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
bsalomon@google.comace7bd52011-11-02 19:39:51 +0000612 SkDevice* device = this->getDevice();
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000613
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000614 SkIRect bounds;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000615 bounds.set(0, 0, device->width(), device->height());
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000616 if (!bounds.intersect(srcRect)) {
617 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000618 }
619
620 SkBitmap tmp;
621 tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
622 bounds.height());
623 if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
624 bitmap->swap(tmp);
625 return true;
626 } else {
reed@google.com51df9e32010-12-23 19:29:18 +0000627 return false;
628 }
reed@google.com51df9e32010-12-23 19:29:18 +0000629}
630
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000631void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
632 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000633 SkDevice* device = this->getDevice();
634 if (device) {
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000635 device->writePixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000636 }
637}
638
junov@google.com4370aed2012-01-18 16:21:08 +0000639SkCanvas* SkCanvas::canvasForDrawIter() {
640 return this;
641}
642
reed@android.com8a1c16f2008-12-17 15:59:43 +0000643//////////////////////////////////////////////////////////////////////////////
644
reed@android.com8a1c16f2008-12-17 15:59:43 +0000645void SkCanvas::updateDeviceCMCache() {
646 if (fDeviceCMDirty) {
647 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000648 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000649 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000650
reed@android.com8a1c16f2008-12-17 15:59:43 +0000651 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000652 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000653 if (fUseExternalMatrix) {
654 layer->updateExternalMatrix(fExternalMatrix,
655 fExternalInverse);
656 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000657 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000658 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000659 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000660 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.comf2b98d62010-12-20 18:26:13 +0000661 if (fUseExternalMatrix) {
662 layer->updateExternalMatrix(fExternalMatrix,
663 fExternalInverse);
664 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000665 } while ((layer = layer->fNext) != NULL);
666 }
667 fDeviceCMDirty = false;
668 }
669}
670
reed@android.comf2b98d62010-12-20 18:26:13 +0000671void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000672 const SkRegion& clip,
673 const SkClipStack& clipStack) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000674 SkASSERT(device);
reed@android.com199f1082009-06-10 02:12:47 +0000675 if (fLastDeviceToGainFocus != device) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000676 device->gainFocus(this, matrix, clip, clipStack);
reed@android.com199f1082009-06-10 02:12:47 +0000677 fLastDeviceToGainFocus = device;
678 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000679}
680
681///////////////////////////////////////////////////////////////////////////////
682
683int SkCanvas::internalSave(SaveFlags flags) {
684 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000685
reed@android.com8a1c16f2008-12-17 15:59:43 +0000686 MCRec* newTop = (MCRec*)fMCStack.push_back();
687 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000688
reed@android.com8a1c16f2008-12-17 15:59:43 +0000689 newTop->fNext = fMCRec;
690 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000691
reed@google.com5c3d1472011-02-22 19:12:23 +0000692 fClipStack.save();
693 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
694
reed@android.com8a1c16f2008-12-17 15:59:43 +0000695 return saveCount;
696}
697
698int SkCanvas::save(SaveFlags flags) {
699 // call shared impl
700 return this->internalSave(flags);
701}
702
703#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
704#define C16MASK (1 << SkBitmap::kRGB_565_Config)
705#define C8MASK (1 << SkBitmap::kA8_Config)
706
707static SkBitmap::Config resolve_config(SkCanvas* canvas,
708 const SkIRect& bounds,
709 SkCanvas::SaveFlags flags,
710 bool* isOpaque) {
711 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
712
713#if 0
714 // loop through and union all the configs we may draw into
715 uint32_t configMask = 0;
716 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
717 {
718 SkDevice* device = canvas->getLayerDevice(i);
719 if (device->intersects(bounds))
720 configMask |= 1 << device->config();
721 }
722
723 // if the caller wants alpha or fullcolor, we can't return 565
724 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
725 SkCanvas::kHasAlphaLayer_SaveFlag))
726 configMask &= ~C16MASK;
727
728 switch (configMask) {
729 case C8MASK: // if we only have A8, return that
730 return SkBitmap::kA8_Config;
731
732 case C16MASK: // if we only have 565, return that
733 return SkBitmap::kRGB_565_Config;
734
735 default:
736 return SkBitmap::kARGB_8888_Config; // default answer
737 }
738#else
739 return SkBitmap::kARGB_8888_Config; // default answer
740#endif
741}
742
743static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
744 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
745}
746
junov@chromium.orga907ac32012-02-24 21:54:07 +0000747bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
748 SkIRect* intersection) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000749 SkIRect clipBounds;
750 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000751 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000752 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000753 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000754 if (NULL != bounds) {
755 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000756
reed@android.com8a1c16f2008-12-17 15:59:43 +0000757 this->getTotalMatrix().mapRect(&r, *bounds);
758 r.roundOut(&ir);
759 // early exit if the layer's bounds are clipped out
760 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000761 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000762 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000763 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000764 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000765 }
766 } else { // no user bounds, so just use the clip
767 ir = clipBounds;
768 }
769
reed@google.com5c3d1472011-02-22 19:12:23 +0000770 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000771
reed@android.com8a1c16f2008-12-17 15:59:43 +0000772 // early exit if the clip is now empty
773 if (bounds_affects_clip(flags) &&
reed@google.com00177082011-10-12 14:34:30 +0000774 !fMCRec->fRasterClip->op(ir, SkRegion::kIntersect_Op)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000775 return false;
776 }
777
778 if (intersection) {
779 *intersection = ir;
780 }
781 return true;
782}
783
784int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
785 SaveFlags flags) {
reed@google.com8926b162012-03-23 15:36:36 +0000786 return this->internalSaveLayer(bounds, paint, flags, false);
787}
788
789int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
790 SaveFlags flags, bool justForImageFilter) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000791 // do this before we create the layer. We don't call the public save() since
792 // that would invoke a possibly overridden virtual
793 int count = this->internalSave(flags);
794
795 fDeviceCMDirty = true;
796
797 SkIRect ir;
798 if (!this->clipRectBounds(bounds, flags, &ir)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000799 return count;
800 }
801
reed@google.comb55deeb2012-01-06 14:43:09 +0000802 // Kill the imagefilter if our device doesn't allow it
803 SkLazyPaint lazyP;
804 if (paint && paint->getImageFilter()) {
805 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000806 if (justForImageFilter) {
807 // early exit if the layer was just for the imageFilter
808 return count;
809 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000810 SkPaint* p = lazyP.set(*paint);
811 p->setImageFilter(NULL);
812 paint = p;
813 }
814 }
815
reed@android.com8a1c16f2008-12-17 15:59:43 +0000816 bool isOpaque;
817 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
818
reed@google.com76dd2772012-01-05 21:15:07 +0000819 SkDevice* device;
820 if (paint && paint->getImageFilter()) {
821 device = this->createCompatibleDevice(config, ir.width(), ir.height(),
822 isOpaque);
823 } else {
824 device = this->createLayerDevice(config, ir.width(), ir.height(),
825 isOpaque);
826 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000827 if (NULL == device) {
828 SkDebugf("Unable to create device for layer.");
829 return count;
830 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000831
reed@google.com6f8f2922011-03-04 22:27:10 +0000832 device->setOrigin(ir.fLeft, ir.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000833 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
834 device->unref();
835
836 layer->fNext = fMCRec->fTopLayer;
837 fMCRec->fLayer = layer;
838 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
839
reed@google.com7c202932011-12-14 18:48:05 +0000840 fLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000841 return count;
842}
843
844int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
845 SaveFlags flags) {
846 if (0xFF == alpha) {
847 return this->saveLayer(bounds, NULL, flags);
848 } else {
849 SkPaint tmpPaint;
850 tmpPaint.setAlpha(alpha);
851 return this->saveLayer(bounds, &tmpPaint, flags);
852 }
853}
854
855void SkCanvas::restore() {
856 // check for underflow
857 if (fMCStack.count() > 1) {
858 this->internalRestore();
859 }
860}
861
862void SkCanvas::internalRestore() {
863 SkASSERT(fMCStack.count() != 0);
864
865 fDeviceCMDirty = true;
866 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000867 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000868
reed@google.com5c3d1472011-02-22 19:12:23 +0000869 fClipStack.restore();
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000870 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000871 DeviceCM* layer = fMCRec->fLayer; // may be null
872 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
873 fMCRec->fLayer = NULL;
874
875 // now do the normal restore()
876 fMCRec->~MCRec(); // balanced in save()
877 fMCStack.pop_back();
878 fMCRec = (MCRec*)fMCStack.back();
879
880 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
881 since if we're being recorded, we don't want to record this (the
882 recorder will have already recorded the restore).
883 */
884 if (NULL != layer) {
885 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000886 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000887 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
888 layer->fPaint);
889 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000890 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000891
892 SkASSERT(fLayerCount > 0);
893 fLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000894 }
895 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000896 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000897
898 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000899}
900
901int SkCanvas::getSaveCount() const {
902 return fMCStack.count();
903}
904
905void SkCanvas::restoreToCount(int count) {
906 // sanity check
907 if (count < 1) {
908 count = 1;
909 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000910
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000911 int n = this->getSaveCount() - count;
912 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000913 this->restore();
914 }
915}
916
reed@google.com7c202932011-12-14 18:48:05 +0000917bool SkCanvas::isDrawingToLayer() const {
918 return fLayerCount > 0;
919}
920
reed@android.com8a1c16f2008-12-17 15:59:43 +0000921/////////////////////////////////////////////////////////////////////////////
922
923// can't draw it if its empty, or its too big for a fixed-point width or height
924static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000925 return bitmap.width() <= 0 || bitmap.height() <= 0
926#ifndef SK_ALLOW_OVER_32K_BITMAPS
927 || bitmap.width() > 32767 || bitmap.height() > 32767
928#endif
929 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000930}
931
reed@android.comf2b98d62010-12-20 18:26:13 +0000932void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000933 const SkMatrix& matrix, const SkPaint* paint) {
934 if (reject_bitmap(bitmap)) {
935 return;
936 }
937
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000938 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000939 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000940 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000941 }
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000942 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000943}
944
reed@google.com76dd2772012-01-05 21:15:07 +0000945#include "SkImageFilter.h"
946
947class DeviceImageFilterProxy : public SkImageFilter::Proxy {
948public:
949 DeviceImageFilterProxy(SkDevice* device) : fDevice(device) {}
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000950
reed@google.com8926b162012-03-23 15:36:36 +0000951 virtual SkDevice* createDevice(int w, int h) SK_OVERRIDE {
952 return fDevice->createCompatibleDevice(SkBitmap::kARGB_8888_Config,
953 w, h, false);
954 }
955 virtual bool canHandleImageFilter(SkImageFilter* filter) SK_OVERRIDE {
956 return fDevice->canHandleImageFilter(filter);
957 }
958 virtual bool filterImage(SkImageFilter* filter, const SkBitmap& src,
reed@google.com76dd2772012-01-05 21:15:07 +0000959 const SkMatrix& ctm,
reed@google.com8926b162012-03-23 15:36:36 +0000960 SkBitmap* result, SkIPoint* offset) SK_OVERRIDE {
961 return fDevice->filterImage(filter, src, ctm, result, offset);
962 }
963
reed@google.com76dd2772012-01-05 21:15:07 +0000964private:
965 SkDevice* fDevice;
966};
967
reed@google.com8926b162012-03-23 15:36:36 +0000968void SkCanvas::internalDrawDevice(SkDevice* srcDev, int x, int y,
969 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000970 SkPaint tmp;
971 if (NULL == paint) {
972 tmp.setDither(true);
973 paint = &tmp;
974 }
reed@google.com4b226022011-01-11 18:32:13 +0000975
reed@google.com8926b162012-03-23 15:36:36 +0000976 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000977 while (iter.next()) {
reed@google.comb55deeb2012-01-06 14:43:09 +0000978 SkDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +0000979 paint = &looper.paint();
980 SkImageFilter* filter = paint->getImageFilter();
981 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +0000982 if (filter && !dstDev->canHandleImageFilter(filter)) {
reed@google.comb55deeb2012-01-06 14:43:09 +0000983 DeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +0000984 SkBitmap dst;
reed@google.comb55deeb2012-01-06 14:43:09 +0000985 const SkBitmap& src = srcDev->accessBitmap(false);
reed@google.com76dd2772012-01-05 21:15:07 +0000986 if (filter->filterImage(&proxy, src, *iter.fMatrix, &dst, &pos)) {
987 SkPaint tmp(*paint);
988 tmp.setImageFilter(NULL);
reed@google.comb55deeb2012-01-06 14:43:09 +0000989 dstDev->drawSprite(iter, dst, pos.x(), pos.y(), tmp);
reed@google.com76dd2772012-01-05 21:15:07 +0000990 }
991 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +0000992 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +0000993 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000994 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000995 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +0000996}
997
reed@google.com8926b162012-03-23 15:36:36 +0000998void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
999 const SkPaint* paint) {
1000 SkDEBUGCODE(bitmap.validate();)
1001
1002 if (reject_bitmap(bitmap)) {
1003 return;
1004 }
1005
1006 SkPaint tmp;
1007 if (NULL == paint) {
1008 paint = &tmp;
1009 }
1010
1011 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
1012
1013 while (iter.next()) {
1014 paint = &looper.paint();
1015 SkImageFilter* filter = paint->getImageFilter();
1016 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1017 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
1018 DeviceImageFilterProxy proxy(iter.fDevice);
1019 SkBitmap dst;
1020 if (filter->filterImage(&proxy, bitmap, *iter.fMatrix, &dst, &pos)) {
1021 SkPaint tmp(*paint);
1022 tmp.setImageFilter(NULL);
1023 iter.fDevice->drawSprite(iter, dst, pos.x(), pos.y(), tmp);
1024 }
1025 } else {
1026 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1027 }
1028 }
1029 LOOPER_END
1030}
1031
reed@android.com8a1c16f2008-12-17 15:59:43 +00001032/////////////////////////////////////////////////////////////////////////////
1033
1034bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
1035 fDeviceCMDirty = true;
1036 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001037 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001038 return fMCRec->fMatrix->preTranslate(dx, dy);
1039}
1040
1041bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
1042 fDeviceCMDirty = true;
1043 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001044 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001045 return fMCRec->fMatrix->preScale(sx, sy);
1046}
1047
1048bool SkCanvas::rotate(SkScalar degrees) {
1049 fDeviceCMDirty = true;
1050 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001051 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001052 return fMCRec->fMatrix->preRotate(degrees);
1053}
1054
1055bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
1056 fDeviceCMDirty = true;
1057 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001058 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001059 return fMCRec->fMatrix->preSkew(sx, sy);
1060}
1061
1062bool SkCanvas::concat(const SkMatrix& matrix) {
1063 fDeviceCMDirty = true;
1064 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001065 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001066 return fMCRec->fMatrix->preConcat(matrix);
1067}
1068
1069void SkCanvas::setMatrix(const SkMatrix& matrix) {
1070 fDeviceCMDirty = true;
1071 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001072 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001073 *fMCRec->fMatrix = matrix;
1074}
1075
1076// this is not virtual, so it must call a virtual method so that subclasses
1077// will see its action
1078void SkCanvas::resetMatrix() {
1079 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001080
reed@android.com8a1c16f2008-12-17 15:59:43 +00001081 matrix.reset();
1082 this->setMatrix(matrix);
1083}
1084
1085//////////////////////////////////////////////////////////////////////////////
1086
reed@google.comc42d35d2011-10-12 11:57:42 +00001087bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001088 AutoValidateClip avc(this);
1089
reed@android.com8a1c16f2008-12-17 15:59:43 +00001090 fDeviceCMDirty = true;
1091 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001092 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001093
1094 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +00001095 // for these simpler matrices, we can stay a rect ever after applying
1096 // the matrix. This means we don't have to a) make a path, and b) tell
1097 // the region code to scan-convert the path, only to discover that it
1098 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001099 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001100
1101 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +00001102 fClipStack.clipDevRect(r, op, doAA);
1103 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001104 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +00001105 // since we're rotate or some such thing, we convert the rect to a path
1106 // and clip against that, since it can handle any matrix. However, to
1107 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1108 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001109 SkPath path;
1110
1111 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +00001112 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001113 }
1114}
1115
reed@google.com00177082011-10-12 14:34:30 +00001116static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +00001117 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001118 // base is used to limit the size (and therefore memory allocation) of the
1119 // region that results from scan converting devPath.
1120 SkRegion base;
1121
reed@google.com819c9212011-02-23 18:56:55 +00001122 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001123 // since we are intersect, we can do better (tighter) with currRgn's
1124 // bounds, than just using the device. However, if currRgn is complex,
1125 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001126 if (currClip->isRect()) {
1127 return currClip->setPath(devPath, *currClip, doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001128 } else {
reed@google.com00177082011-10-12 14:34:30 +00001129 base.setRect(currClip->getBounds());
1130 SkRasterClip clip;
1131 clip.setPath(devPath, base, doAA);
1132 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001133 }
reed@google.com819c9212011-02-23 18:56:55 +00001134 } else {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001135 const SkDevice* device = canvas->getDevice();
1136 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001137
1138 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +00001139 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001140 } else {
reed@google.com00177082011-10-12 14:34:30 +00001141 SkRasterClip clip;
1142 clip.setPath(devPath, base, doAA);
1143 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001144 }
1145 }
1146}
1147
reed@google.comc42d35d2011-10-12 11:57:42 +00001148bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001149 AutoValidateClip avc(this);
1150
reed@android.com8a1c16f2008-12-17 15:59:43 +00001151 fDeviceCMDirty = true;
1152 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001153 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001154
1155 SkPath devPath;
1156 path.transform(*fMCRec->fMatrix, &devPath);
1157
reed@google.comfe701122011-11-08 19:41:23 +00001158 // Check if the transfomation, or the original path itself
1159 // made us empty. Note this can also happen if we contained NaN
1160 // values. computing the bounds detects this, and will set our
1161 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1162 if (devPath.getBounds().isEmpty()) {
1163 // resetting the path will remove any NaN or other wanky values
1164 // that might upset our scan converter.
1165 devPath.reset();
1166 }
1167
reed@google.com5c3d1472011-02-22 19:12:23 +00001168 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +00001169 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +00001170
reed@google.com00177082011-10-12 14:34:30 +00001171 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001172}
1173
1174bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001175 AutoValidateClip avc(this);
1176
reed@android.com8a1c16f2008-12-17 15:59:43 +00001177 fDeviceCMDirty = true;
1178 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001179 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001180
reed@google.com5c3d1472011-02-22 19:12:23 +00001181 // todo: signal fClipStack that we have a region, and therefore (I guess)
1182 // we have to ignore it, and use the region directly?
1183 fClipStack.clipDevRect(rgn.getBounds());
1184
reed@google.com00177082011-10-12 14:34:30 +00001185 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001186}
1187
reed@google.com819c9212011-02-23 18:56:55 +00001188#ifdef SK_DEBUG
1189void SkCanvas::validateClip() const {
1190 // construct clipRgn from the clipstack
1191 const SkDevice* device = this->getDevice();
1192 SkIRect ir;
1193 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001194 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001195
1196 SkClipStack::B2FIter iter(fClipStack);
1197 const SkClipStack::B2FIter::Clip* clip;
1198 while ((clip = iter.next()) != NULL) {
1199 if (clip->fPath) {
reed@google.com00177082011-10-12 14:34:30 +00001200 clipPathHelper(this, &tmpClip, *clip->fPath, clip->fOp, clip->fDoAA);
reed@google.com819c9212011-02-23 18:56:55 +00001201 } else if (clip->fRect) {
1202 clip->fRect->round(&ir);
reed@google.com00177082011-10-12 14:34:30 +00001203 tmpClip.op(ir, clip->fOp);
reed@google.com819c9212011-02-23 18:56:55 +00001204 } else {
reed@google.com00177082011-10-12 14:34:30 +00001205 tmpClip.setEmpty();
reed@google.com819c9212011-02-23 18:56:55 +00001206 }
1207 }
1208
reed@google.com6f8f2922011-03-04 22:27:10 +00001209#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001210 // now compare against the current rgn
1211 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001212 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001213#endif
reed@google.com819c9212011-02-23 18:56:55 +00001214}
1215#endif
1216
reed@google.com5c3d1472011-02-22 19:12:23 +00001217///////////////////////////////////////////////////////////////////////////////
1218
reed@android.comba09de42010-02-05 20:46:05 +00001219void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001220 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001221 SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
1222 fLocalBoundsCompareTypeBW;
1223
1224 if (!this->getClipBounds(&r, et)) {
1225 rCompare.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001226 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001227 rCompare.set(SkScalarToCompareType(r.fLeft),
1228 SkScalarToCompareType(r.fTop),
1229 SkScalarToCompareType(r.fRight),
1230 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001231 }
1232}
1233
reed@android.comd252db02009-04-01 18:31:44 +00001234/* current impl ignores edgetype, and relies on
1235 getLocalClipBoundsCompareType(), which always returns a value assuming
1236 antialiasing (worst case)
1237 */
reed@android.comba09de42010-02-05 20:46:05 +00001238bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001239
reed@google.com16078632011-12-06 18:56:37 +00001240 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001241 return true;
1242
reed@google.com00177082011-10-12 14:34:30 +00001243 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001244 return true;
1245 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001246
tomhudson@google.com8d430182011-06-06 19:11:19 +00001247 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001248 SkRect dst;
1249 fMCRec->fMatrix->mapRect(&dst, rect);
1250 SkIRect idst;
1251 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001252 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001253 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001254 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
reed@android.comd252db02009-04-01 18:31:44 +00001255
reed@android.coma380ae42009-07-21 01:17:02 +00001256 // for speed, do the most likely reject compares first
1257 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1258 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1259 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1260 return true;
1261 }
1262 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1263 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1264 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1265 return true;
1266 }
1267 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001268 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001269}
1270
1271bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +00001272 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001273}
1274
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001275static inline int pinIntForScalar(int x) {
1276#ifdef SK_SCALAR_IS_FIXED
1277 if (x < SK_MinS16) {
1278 x = SK_MinS16;
1279 } else if (x > SK_MaxS16) {
1280 x = SK_MaxS16;
1281 }
1282#endif
1283 return x;
1284}
1285
reed@android.com8a1c16f2008-12-17 15:59:43 +00001286bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001287 SkIRect ibounds;
1288 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001289 return false;
1290 }
1291
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001292 SkMatrix inverse;
1293 // if we can't invert the CTM, we can't return local clip bounds
1294 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001295 if (bounds) {
1296 bounds->setEmpty();
1297 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001298 return false;
1299 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001300
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001301 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001302 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001303 // adjust it outwards if we are antialiasing
1304 int inset = (kAA_EdgeType == et);
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001305
1306 // SkRect::iset() will correctly assert if we pass a value out of range
1307 // (when SkScalar==fixed), so we pin to legal values. This does not
1308 // really returnt the correct answer, but its the best we can do given
1309 // that we've promised to return SkRect (even though we support devices
1310 // that can be larger than 32K in width or height).
1311 r.iset(pinIntForScalar(ibounds.fLeft - inset),
1312 pinIntForScalar(ibounds.fTop - inset),
1313 pinIntForScalar(ibounds.fRight + inset),
1314 pinIntForScalar(ibounds.fBottom + inset));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001315 inverse.mapRect(bounds, r);
1316 }
1317 return true;
1318}
1319
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001320bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001321 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001322 if (clip.isEmpty()) {
1323 if (bounds) {
1324 bounds->setEmpty();
1325 }
1326 return false;
1327 }
1328
1329 if (NULL != bounds) {
1330 *bounds = clip.getBounds();
1331 }
1332 return true;
1333}
1334
reed@android.com8a1c16f2008-12-17 15:59:43 +00001335const SkMatrix& SkCanvas::getTotalMatrix() const {
1336 return *fMCRec->fMatrix;
1337}
1338
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001339SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001340 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1341 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001342 return kComplex_ClipType;
1343}
1344
reed@android.com8a1c16f2008-12-17 15:59:43 +00001345const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001346 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001347}
1348
reed@google.com7d7ca792011-02-23 22:39:18 +00001349const SkClipStack& SkCanvas::getTotalClipStack() const {
1350 return fClipStack;
1351}
1352
reed@android.comf2b98d62010-12-20 18:26:13 +00001353void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1354 if (NULL == matrix || matrix->isIdentity()) {
1355 if (fUseExternalMatrix) {
1356 fDeviceCMDirty = true;
1357 }
1358 fUseExternalMatrix = false;
1359 } else {
1360 fUseExternalMatrix = true;
1361 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.com4b226022011-01-11 18:32:13 +00001362
reed@android.comf2b98d62010-12-20 18:26:13 +00001363 fExternalMatrix = *matrix;
1364 matrix->invert(&fExternalInverse);
1365 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001366}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001367
bsalomon@google.come97f0852011-06-17 13:10:25 +00001368SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
1369 int width, int height,
1370 bool isOpaque) {
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001371 SkDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001372 if (device) {
1373 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1374 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001375 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001376 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001377 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001378}
1379
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001380SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
bsalomon@google.come97f0852011-06-17 13:10:25 +00001381 int width, int height,
1382 bool isOpaque) {
1383 SkDevice* device = this->getDevice();
1384 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001385 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001386 } else {
1387 return NULL;
1388 }
1389}
1390
1391
reed@android.com8a1c16f2008-12-17 15:59:43 +00001392//////////////////////////////////////////////////////////////////////////////
1393// These are the virtual drawing methods
1394//////////////////////////////////////////////////////////////////////////////
1395
reed@google.com2a981812011-04-14 18:59:28 +00001396void SkCanvas::clear(SkColor color) {
1397 SkDrawIter iter(this);
1398
1399 while (iter.next()) {
1400 iter.fDevice->clear(color);
1401 }
1402}
1403
reed@android.com8a1c16f2008-12-17 15:59:43 +00001404void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001405 this->internalDrawPaint(paint);
1406}
1407
1408void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001409 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001410
1411 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001412 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001413 }
1414
reed@google.com4e2b3d32011-04-07 14:18:59 +00001415 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001416}
1417
1418void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1419 const SkPaint& paint) {
1420 if ((long)count <= 0) {
1421 return;
1422 }
1423
1424 SkASSERT(pts != NULL);
1425
reed@google.com4e2b3d32011-04-07 14:18:59 +00001426 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001427
reed@android.com8a1c16f2008-12-17 15:59:43 +00001428 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001429 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001430 }
reed@google.com4b226022011-01-11 18:32:13 +00001431
reed@google.com4e2b3d32011-04-07 14:18:59 +00001432 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001433}
1434
1435void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1436 if (paint.canComputeFastBounds()) {
1437 SkRect storage;
1438 if (this->quickReject(paint.computeFastBounds(r, &storage),
1439 paint2EdgeType(&paint))) {
1440 return;
1441 }
1442 }
reed@google.com4b226022011-01-11 18:32:13 +00001443
reed@google.com4e2b3d32011-04-07 14:18:59 +00001444 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001445
1446 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001447 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001448 }
1449
reed@google.com4e2b3d32011-04-07 14:18:59 +00001450 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001451}
1452
1453void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001454 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001455 SkRect storage;
1456 const SkRect& bounds = path.getBounds();
1457 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001458 paint2EdgeType(&paint))) {
1459 return;
1460 }
1461 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001462 if (path.isEmpty()) {
1463 if (path.isInverseFillType()) {
1464 this->internalDrawPaint(paint);
1465 }
1466 return;
1467 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001468
reed@google.com4e2b3d32011-04-07 14:18:59 +00001469 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001470
1471 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001472 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001473 }
1474
reed@google.com4e2b3d32011-04-07 14:18:59 +00001475 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001476}
1477
1478void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1479 const SkPaint* paint) {
1480 SkDEBUGCODE(bitmap.validate();)
1481
reed@google.com3d608122011-11-21 15:16:16 +00001482 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001483 SkRect bounds = {
1484 x, y,
1485 x + SkIntToScalar(bitmap.width()),
1486 y + SkIntToScalar(bitmap.height())
1487 };
1488 if (paint) {
1489 (void)paint->computeFastBounds(bounds, &bounds);
1490 }
1491 if (this->quickReject(bounds, paint2EdgeType(paint))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001492 return;
1493 }
1494 }
reed@google.com4b226022011-01-11 18:32:13 +00001495
reed@android.com8a1c16f2008-12-17 15:59:43 +00001496 SkMatrix matrix;
1497 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001498 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001499}
1500
reed@google.com9987ec32011-09-07 11:57:52 +00001501// this one is non-virtual, so it can be called safely by other canvas apis
1502void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1503 const SkRect& dst, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001504 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1505 return;
1506 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001507
reed@android.com8a1c16f2008-12-17 15:59:43 +00001508 // do this now, to avoid the cost of calling extract for RLE bitmaps
reed@google.com3d608122011-11-21 15:16:16 +00001509 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001510 SkRect storage;
1511 const SkRect* bounds = &dst;
1512 if (paint) {
1513 bounds = &paint->computeFastBounds(dst, &storage);
1514 }
1515 if (this->quickReject(*bounds, paint2EdgeType(paint))) {
reed@google.com3d608122011-11-21 15:16:16 +00001516 return;
1517 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001518 }
reed@google.com3d608122011-11-21 15:16:16 +00001519
reed@android.com8a1c16f2008-12-17 15:59:43 +00001520 const SkBitmap* bitmapPtr = &bitmap;
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001521
reed@android.com8a1c16f2008-12-17 15:59:43 +00001522 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001523 SkRect tmpSrc;
1524 if (src) {
1525 tmpSrc.set(*src);
1526 // if the extract process clipped off the top or left of the
1527 // original, we adjust for that here to get the position right.
1528 if (tmpSrc.fLeft > 0) {
1529 tmpSrc.fRight -= tmpSrc.fLeft;
1530 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001531 }
reed@android.com87899992009-10-16 14:48:38 +00001532 if (tmpSrc.fTop > 0) {
1533 tmpSrc.fBottom -= tmpSrc.fTop;
1534 tmpSrc.fTop = 0;
1535 }
1536 } else {
1537 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1538 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001539 }
reed@android.com87899992009-10-16 14:48:38 +00001540 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001541
reed@android.comf2b98d62010-12-20 18:26:13 +00001542 // ensure that src is "valid" before we pass it to our internal routines
1543 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1544 SkIRect tmpISrc;
1545 if (src) {
1546 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
reed@google.com2ade0862011-03-17 17:48:04 +00001547 if (!tmpISrc.intersect(*src)) {
1548 return;
1549 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001550 src = &tmpISrc;
1551 }
1552 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001553}
1554
reed@google.com9987ec32011-09-07 11:57:52 +00001555void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1556 const SkRect& dst, const SkPaint* paint) {
1557 SkDEBUGCODE(bitmap.validate();)
1558 this->internalDrawBitmapRect(bitmap, src, dst, paint);
1559}
1560
reed@android.com8a1c16f2008-12-17 15:59:43 +00001561void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1562 const SkPaint* paint) {
1563 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001564 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001565}
1566
reed@android.comf2b98d62010-12-20 18:26:13 +00001567void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1568 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001569 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001570
reed@google.com4e2b3d32011-04-07 14:18:59 +00001571 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001572
reed@android.com8a1c16f2008-12-17 15:59:43 +00001573 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001574 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001575 }
reed@android.com9b039062009-02-11 15:09:58 +00001576
reed@google.com4e2b3d32011-04-07 14:18:59 +00001577 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001578}
1579
reed@google.com9987ec32011-09-07 11:57:52 +00001580void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1581 const SkIRect& center, const SkRect& dst,
1582 const SkPaint* paint) {
reed@google.com3d608122011-11-21 15:16:16 +00001583 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001584 SkRect storage;
1585 const SkRect* bounds = &dst;
1586 if (paint) {
1587 bounds = &paint->computeFastBounds(dst, &storage);
1588 }
1589 if (this->quickReject(*bounds, paint2EdgeType(paint))) {
reed@google.com3d608122011-11-21 15:16:16 +00001590 return;
1591 }
1592 }
1593
reed@google.com9987ec32011-09-07 11:57:52 +00001594 const int32_t w = bitmap.width();
1595 const int32_t h = bitmap.height();
1596
1597 SkIRect c = center;
1598 // pin center to the bounds of the bitmap
1599 c.fLeft = SkMax32(0, center.fLeft);
1600 c.fTop = SkMax32(0, center.fTop);
1601 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1602 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1603
1604 const int32_t srcX[4] = { 0, c.fLeft, c.fRight, w };
1605 const int32_t srcY[4] = { 0, c.fTop, c.fBottom, h };
1606 SkScalar dstX[4] = {
1607 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1608 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1609 };
1610 SkScalar dstY[4] = {
1611 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1612 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1613 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001614
reed@google.com9987ec32011-09-07 11:57:52 +00001615 if (dstX[1] > dstX[2]) {
1616 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1617 dstX[2] = dstX[1];
1618 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001619
reed@google.com9987ec32011-09-07 11:57:52 +00001620 if (dstY[1] > dstY[2]) {
1621 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1622 dstY[2] = dstY[1];
1623 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001624
reed@google.com9987ec32011-09-07 11:57:52 +00001625 SkIRect s;
1626 SkRect d;
1627 for (int y = 0; y < 3; y++) {
1628 s.fTop = srcY[y];
1629 s.fBottom = srcY[y+1];
1630 d.fTop = dstY[y];
1631 d.fBottom = dstY[y+1];
1632 for (int x = 0; x < 3; x++) {
1633 s.fLeft = srcX[x];
1634 s.fRight = srcX[x+1];
1635 d.fLeft = dstX[x];
1636 d.fRight = dstX[x+1];
1637 this->internalDrawBitmapRect(bitmap, &s, d, paint);
1638 }
1639 }
1640}
1641
1642void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1643 const SkRect& dst, const SkPaint* paint) {
1644 SkDEBUGCODE(bitmap.validate();)
1645
1646 // Need a device entry-point, so gpu can use a mesh
1647 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1648}
1649
reed@google.comf67e4cf2011-03-15 20:56:58 +00001650class SkDeviceFilteredPaint {
1651public:
1652 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1653 SkDevice::TextFlags flags;
1654 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001655 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001656 newPaint->setFlags(flags.fFlags);
1657 newPaint->setHinting(flags.fHinting);
1658 fPaint = newPaint;
1659 } else {
1660 fPaint = &paint;
1661 }
1662 }
1663
reed@google.comf67e4cf2011-03-15 20:56:58 +00001664 const SkPaint& paint() const { return *fPaint; }
1665
1666private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001667 const SkPaint* fPaint;
1668 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001669};
1670
bungeman@google.com52c748b2011-08-22 21:30:43 +00001671void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1672 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001673 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001674 draw.fDevice->drawRect(draw, r, paint);
1675 } else {
1676 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001677 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001678 draw.fDevice->drawRect(draw, r, p);
1679 }
1680}
1681
1682void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1683 const char text[], size_t byteLength,
1684 SkScalar x, SkScalar y) {
1685 SkASSERT(byteLength == 0 || text != NULL);
1686
1687 // nothing to draw
1688 if (text == NULL || byteLength == 0 ||
1689 draw.fClip->isEmpty() ||
1690 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1691 return;
1692 }
1693
1694 SkScalar width = 0;
1695 SkPoint start;
1696
1697 start.set(0, 0); // to avoid warning
1698 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1699 SkPaint::kStrikeThruText_Flag)) {
1700 width = paint.measureText(text, byteLength);
1701
1702 SkScalar offsetX = 0;
1703 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1704 offsetX = SkScalarHalf(width);
1705 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1706 offsetX = width;
1707 }
1708 start.set(x - offsetX, y);
1709 }
1710
1711 if (0 == width) {
1712 return;
1713 }
1714
1715 uint32_t flags = paint.getFlags();
1716
1717 if (flags & (SkPaint::kUnderlineText_Flag |
1718 SkPaint::kStrikeThruText_Flag)) {
1719 SkScalar textSize = paint.getTextSize();
1720 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1721 SkRect r;
1722
1723 r.fLeft = start.fX;
1724 r.fRight = start.fX + width;
1725
1726 if (flags & SkPaint::kUnderlineText_Flag) {
1727 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1728 start.fY);
1729 r.fTop = offset;
1730 r.fBottom = offset + height;
1731 DrawRect(draw, paint, r, textSize);
1732 }
1733 if (flags & SkPaint::kStrikeThruText_Flag) {
1734 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1735 start.fY);
1736 r.fTop = offset;
1737 r.fBottom = offset + height;
1738 DrawRect(draw, paint, r, textSize);
1739 }
1740 }
1741}
1742
reed@android.com8a1c16f2008-12-17 15:59:43 +00001743void SkCanvas::drawText(const void* text, size_t byteLength,
1744 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001745 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001746
1747 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001748 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001749 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001750 DrawTextDecorations(iter, dfp.paint(),
1751 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001752 }
1753
reed@google.com4e2b3d32011-04-07 14:18:59 +00001754 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001755}
1756
1757void SkCanvas::drawPosText(const void* text, size_t byteLength,
1758 const SkPoint pos[], const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001759 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001760
reed@android.com8a1c16f2008-12-17 15:59:43 +00001761 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001762 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001763 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001764 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001765 }
reed@google.com4b226022011-01-11 18:32:13 +00001766
reed@google.com4e2b3d32011-04-07 14:18:59 +00001767 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001768}
1769
1770void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1771 const SkScalar xpos[], SkScalar constY,
1772 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001773 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001774
reed@android.com8a1c16f2008-12-17 15:59:43 +00001775 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001776 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001777 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001778 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001779 }
reed@google.com4b226022011-01-11 18:32:13 +00001780
reed@google.com4e2b3d32011-04-07 14:18:59 +00001781 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001782}
1783
1784void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1785 const SkPath& path, const SkMatrix* matrix,
1786 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001787 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001788
1789 while (iter.next()) {
1790 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001791 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001792 }
1793
reed@google.com4e2b3d32011-04-07 14:18:59 +00001794 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001795}
1796
djsollen@google.com56c69772011-11-08 19:00:26 +00001797#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001798void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1799 const SkPoint pos[], const SkPaint& paint,
1800 const SkPath& path, const SkMatrix* matrix) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001801 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001802
1803 while (iter.next()) {
1804 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001805 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001806 }
1807
reed@google.com4e2b3d32011-04-07 14:18:59 +00001808 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001809}
1810#endif
1811
reed@android.com8a1c16f2008-12-17 15:59:43 +00001812void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1813 const SkPoint verts[], const SkPoint texs[],
1814 const SkColor colors[], SkXfermode* xmode,
1815 const uint16_t indices[], int indexCount,
1816 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001817 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001818
reed@android.com8a1c16f2008-12-17 15:59:43 +00001819 while (iter.next()) {
1820 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001821 colors, xmode, indices, indexCount,
1822 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001823 }
reed@google.com4b226022011-01-11 18:32:13 +00001824
reed@google.com4e2b3d32011-04-07 14:18:59 +00001825 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001826}
1827
reed@android.comcb608442009-12-04 21:32:27 +00001828void SkCanvas::drawData(const void* data, size_t length) {
1829 // do nothing. Subclasses may do something with the data
1830}
1831
reed@android.com8a1c16f2008-12-17 15:59:43 +00001832//////////////////////////////////////////////////////////////////////////////
1833// These methods are NOT virtual, and therefore must call back into virtual
1834// methods, rather than actually drawing themselves.
1835//////////////////////////////////////////////////////////////////////////////
1836
1837void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001838 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001839 SkPaint paint;
1840
1841 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001842 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001843 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001844 }
1845 this->drawPaint(paint);
1846}
1847
reed@android.com845fdac2009-06-23 03:01:32 +00001848void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001849 SkPaint paint;
1850
1851 paint.setColor(c);
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
1858void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1859 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001860
reed@android.com8a1c16f2008-12-17 15:59:43 +00001861 pt.set(x, y);
1862 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1863}
1864
1865void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1866 SkPoint pt;
1867 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001868
reed@android.com8a1c16f2008-12-17 15:59:43 +00001869 pt.set(x, y);
1870 paint.setColor(color);
1871 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1872}
1873
1874void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1875 const SkPaint& paint) {
1876 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001877
reed@android.com8a1c16f2008-12-17 15:59:43 +00001878 pts[0].set(x0, y0);
1879 pts[1].set(x1, y1);
1880 this->drawPoints(kLines_PointMode, 2, pts, paint);
1881}
1882
1883void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1884 SkScalar right, SkScalar bottom,
1885 const SkPaint& paint) {
1886 SkRect r;
1887
1888 r.set(left, top, right, bottom);
1889 this->drawRect(r, paint);
1890}
1891
1892void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1893 const SkPaint& paint) {
1894 if (radius < 0) {
1895 radius = 0;
1896 }
1897
1898 SkRect r;
1899 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001900
reed@android.com8a1c16f2008-12-17 15:59:43 +00001901 if (paint.canComputeFastBounds()) {
1902 SkRect storage;
1903 if (this->quickReject(paint.computeFastBounds(r, &storage),
1904 paint2EdgeType(&paint))) {
1905 return;
1906 }
1907 }
reed@google.com4b226022011-01-11 18:32:13 +00001908
reed@android.com8a1c16f2008-12-17 15:59:43 +00001909 SkPath path;
1910 path.addOval(r);
1911 this->drawPath(path, paint);
1912}
1913
1914void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1915 const SkPaint& paint) {
1916 if (rx > 0 && ry > 0) {
1917 if (paint.canComputeFastBounds()) {
1918 SkRect storage;
1919 if (this->quickReject(paint.computeFastBounds(r, &storage),
1920 paint2EdgeType(&paint))) {
1921 return;
1922 }
1923 }
1924
1925 SkPath path;
1926 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1927 this->drawPath(path, paint);
1928 } else {
1929 this->drawRect(r, paint);
1930 }
1931}
1932
1933void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1934 if (paint.canComputeFastBounds()) {
1935 SkRect storage;
1936 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1937 paint2EdgeType(&paint))) {
1938 return;
1939 }
1940 }
1941
1942 SkPath path;
1943 path.addOval(oval);
1944 this->drawPath(path, paint);
1945}
1946
1947void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1948 SkScalar sweepAngle, bool useCenter,
1949 const SkPaint& paint) {
1950 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1951 this->drawOval(oval, paint);
1952 } else {
1953 SkPath path;
1954 if (useCenter) {
1955 path.moveTo(oval.centerX(), oval.centerY());
1956 }
1957 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1958 if (useCenter) {
1959 path.close();
1960 }
1961 this->drawPath(path, paint);
1962 }
1963}
1964
1965void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1966 const SkPath& path, SkScalar hOffset,
1967 SkScalar vOffset, const SkPaint& paint) {
1968 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001969
reed@android.com8a1c16f2008-12-17 15:59:43 +00001970 matrix.setTranslate(hOffset, vOffset);
1971 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1972}
1973
reed@android.comf76bacf2009-05-13 14:00:33 +00001974///////////////////////////////////////////////////////////////////////////////
1975
reed@android.com8a1c16f2008-12-17 15:59:43 +00001976void SkCanvas::drawPicture(SkPicture& picture) {
1977 int saveCount = save();
1978 picture.draw(this);
1979 restoreToCount(saveCount);
1980}
1981
reed@android.com8a1c16f2008-12-17 15:59:43 +00001982///////////////////////////////////////////////////////////////////////////////
1983///////////////////////////////////////////////////////////////////////////////
1984
1985SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00001986 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001987
1988 SkASSERT(canvas);
1989
1990 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
1991 fDone = !fImpl->next();
1992}
1993
1994SkCanvas::LayerIter::~LayerIter() {
1995 fImpl->~SkDrawIter();
1996}
1997
1998void SkCanvas::LayerIter::next() {
1999 fDone = !fImpl->next();
2000}
2001
2002SkDevice* SkCanvas::LayerIter::device() const {
2003 return fImpl->getDevice();
2004}
2005
2006const SkMatrix& SkCanvas::LayerIter::matrix() const {
2007 return fImpl->getMatrix();
2008}
2009
2010const SkPaint& SkCanvas::LayerIter::paint() const {
2011 const SkPaint* paint = fImpl->getPaint();
2012 if (NULL == paint) {
2013 paint = &fDefaultPaint;
2014 }
2015 return *paint;
2016}
2017
2018const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2019int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2020int SkCanvas::LayerIter::y() const { return fImpl->getY(); }