blob: 13ca462bd629420f07ff07657413d072f736074f [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) {
217 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000218 canvas->updateDeviceCMCache();
219
reed@google.com7d7ca792011-02-23 22:39:18 +0000220 fClipStack = &canvas->getTotalClipStack();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221 fBounder = canvas->getBounder();
222 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000223 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000224 }
reed@google.com4b226022011-01-11 18:32:13 +0000225
reed@android.com8a1c16f2008-12-17 15:59:43 +0000226 bool next() {
227 // skip over recs with empty clips
228 if (fSkipEmptyClips) {
229 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
230 fCurrLayer = fCurrLayer->fNext;
231 }
232 }
233
234 if (NULL != fCurrLayer) {
235 const DeviceCM* rec = fCurrLayer;
236
237 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000238 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
239 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240 fDevice = rec->fDevice;
241 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000243 fMVMatrix = rec->fMVMatrix;
244 fExtMatrix = rec->fExtMatrix;
245 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000246
247 fCurrLayer = rec->fNext;
248 if (fBounder) {
249 fBounder->setClip(fClip);
250 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000251 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000252
bsalomon@google.comd302f142011-03-03 13:54:13 +0000253 fCanvas->prepareForDeviceDraw(fDevice, *fMatrix, *fClip, *fClipStack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000254 return true;
255 }
256 return false;
257 }
reed@google.com4b226022011-01-11 18:32:13 +0000258
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259 SkDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000260 int getX() const { return fDevice->getOrigin().x(); }
261 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262 const SkMatrix& getMatrix() const { return *fMatrix; }
263 const SkRegion& getClip() const { return *fClip; }
264 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000265
reed@android.com8a1c16f2008-12-17 15:59:43 +0000266private:
267 SkCanvas* fCanvas;
268 const DeviceCM* fCurrLayer;
269 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000270 SkBool8 fSkipEmptyClips;
271
272 typedef SkDraw INHERITED;
273};
274
275/////////////////////////////////////////////////////////////////////////////
276
277class AutoDrawLooper {
278public:
reed@google.com4e2b3d32011-04-07 14:18:59 +0000279 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint) : fOrigPaint(paint) {
280 fCanvas = canvas;
281 fLooper = paint.getLooper();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000282 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000283 fPaint = NULL;
284 fSaveCount = canvas->getSaveCount();
285 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000286
reed@google.com4e2b3d32011-04-07 14:18:59 +0000287 if (fLooper) {
288 fLooper->init(canvas);
289 }
290 }
291
reed@android.com8a1c16f2008-12-17 15:59:43 +0000292 ~AutoDrawLooper() {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000293 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000294 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000295
296 const SkPaint& paint() const {
297 SkASSERT(fPaint);
298 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000300
301 bool next(SkDrawFilter::Type drawType);
302
reed@android.com8a1c16f2008-12-17 15:59:43 +0000303private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000304 SkLazyPaint fLazyPaint;
305 SkCanvas* fCanvas;
306 const SkPaint& fOrigPaint;
307 SkDrawLooper* fLooper;
308 SkDrawFilter* fFilter;
309 const SkPaint* fPaint;
310 int fSaveCount;
311 bool fDone;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000312};
313
reed@google.com4e2b3d32011-04-07 14:18:59 +0000314bool AutoDrawLooper::next(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000315 fPaint = NULL;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000316 if (fDone) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000317 return false;
318 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000319
reed@google.com632e1a22011-10-06 12:37:00 +0000320 if (fLooper || fFilter) {
321 SkPaint* paint = fLazyPaint.set(fOrigPaint);
322 if (fLooper && !fLooper->next(fCanvas, paint)) {
323 fDone = true;
324 return false;
325 }
326 if (fFilter) {
327 fFilter->filter(paint, drawType);
328 if (NULL == fLooper) {
329 // no looper means we only draw once
330 fDone = true;
331 }
332 }
333 fPaint = paint;
334 } else {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000335 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000336 fPaint = &fOrigPaint;
337 }
338
339 // call this after any possible paint modifiers
340 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000341 fPaint = NULL;
342 return false;
343 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000344 return true;
345}
346
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347/* Stack helper for managing a SkBounder. In the destructor, if we were
348 given a bounder, we call its commit() method, signifying that we are
349 done accumulating bounds for that draw.
350*/
351class SkAutoBounderCommit {
352public:
353 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
354 ~SkAutoBounderCommit() {
355 if (NULL != fBounder) {
356 fBounder->commit();
357 }
358 }
359private:
360 SkBounder* fBounder;
361};
362
363#include "SkColorPriv.h"
364
365class AutoValidator {
366public:
367 AutoValidator(SkDevice* device) : fDevice(device) {}
368 ~AutoValidator() {
369#ifdef SK_DEBUG
370 const SkBitmap& bm = fDevice->accessBitmap(false);
371 if (bm.config() == SkBitmap::kARGB_4444_Config) {
372 for (int y = 0; y < bm.height(); y++) {
373 const SkPMColor16* p = bm.getAddr16(0, y);
374 for (int x = 0; x < bm.width(); x++) {
375 SkPMColor16 c = p[x];
376 SkPMColor16Assert(c);
377 }
378 }
379 }
380#endif
381 }
382private:
383 SkDevice* fDevice;
384};
385
386////////// macros to place around the internal draw calls //////////////////
387
reed@google.com4e2b3d32011-04-07 14:18:59 +0000388#define LOOPER_BEGIN(paint, type) \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000389/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000390 AutoDrawLooper looper(this, paint); \
391 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000392 SkAutoBounderCommit ac(fBounder); \
393 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000394
reed@google.com4e2b3d32011-04-07 14:18:59 +0000395#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000396
397////////////////////////////////////////////////////////////////////////////
398
399SkDevice* SkCanvas::init(SkDevice* device) {
400 fBounder = NULL;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000401 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000402 fLocalBoundsCompareTypeDirty = true;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000403 fLocalBoundsCompareTypeBW.setEmpty();
reed@android.comba09de42010-02-05 20:46:05 +0000404 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com199f1082009-06-10 02:12:47 +0000405 fLastDeviceToGainFocus = NULL;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000406 fDeviceCMDirty = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000407
408 fMCRec = (MCRec*)fMCStack.push_back();
409 new (fMCRec) MCRec(NULL, 0);
410
411 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL));
412 fMCRec->fTopLayer = fMCRec->fLayer;
413 fMCRec->fNext = NULL;
414
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000415 fExternalMatrix.reset();
416 fExternalInverse.reset();
reed@android.comf2b98d62010-12-20 18:26:13 +0000417 fUseExternalMatrix = false;
418
reed@android.com8a1c16f2008-12-17 15:59:43 +0000419 return this->setDevice(device);
420}
421
reed@google.comcde92112011-07-06 20:00:52 +0000422SkCanvas::SkCanvas()
423: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000424 inc_canvas();
reed@google.comcde92112011-07-06 20:00:52 +0000425
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000426 this->init(NULL);
427}
428
reed@android.com8a1c16f2008-12-17 15:59:43 +0000429SkCanvas::SkCanvas(SkDevice* device)
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000430 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000431 inc_canvas();
432
433 this->init(device);
434}
435
436SkCanvas::SkCanvas(const SkBitmap& bitmap)
437 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
438 inc_canvas();
439
reed@google.comcde92112011-07-06 20:00:52 +0000440 this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000441}
442
443SkCanvas::~SkCanvas() {
444 // free up the contents of our deque
445 this->restoreToCount(1); // restore everything but the last
446 this->internalRestore(); // restore the last, since we're going away
447
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000448 SkSafeUnref(fBounder);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000449
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450 dec_canvas();
451}
452
453SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
454 SkRefCnt_SafeAssign(fBounder, bounder);
455 return bounder;
456}
457
458SkDrawFilter* SkCanvas::getDrawFilter() const {
459 return fMCRec->fFilter;
460}
461
462SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
463 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
464 return filter;
465}
466
467///////////////////////////////////////////////////////////////////////////////
468
reed@google.com210ce002011-11-01 14:24:23 +0000469SkISize SkCanvas::getDeviceSize() const {
470 SkDevice* d = this->getDevice();
471 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
472}
473
reed@android.com8a1c16f2008-12-17 15:59:43 +0000474SkDevice* SkCanvas::getDevice() const {
475 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000476 SkDeque::F2BIter iter(fMCStack);
477 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000478 SkASSERT(rec && rec->fLayer);
479 return rec->fLayer->fDevice;
480}
481
reed@google.com9266fed2011-03-30 00:18:03 +0000482SkDevice* SkCanvas::getTopDevice() const {
483 return fMCRec->fTopLayer->fDevice;
484}
485
reed@android.com8a1c16f2008-12-17 15:59:43 +0000486SkDevice* SkCanvas::setDevice(SkDevice* device) {
487 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000488 SkDeque::F2BIter iter(fMCStack);
489 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000490 SkASSERT(rec && rec->fLayer);
491 SkDevice* rootDevice = rec->fLayer->fDevice;
492
493 if (rootDevice == device) {
494 return device;
495 }
reed@google.com4b226022011-01-11 18:32:13 +0000496
reed@android.com8a1c16f2008-12-17 15:59:43 +0000497 /* Notify the devices that they are going in/out of scope, so they can do
498 things like lock/unlock their pixels, etc.
499 */
500 if (device) {
501 device->lockPixels();
502 }
503 if (rootDevice) {
504 rootDevice->unlockPixels();
505 }
506
507 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
508 rootDevice = device;
509
510 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000511
reed@android.com8a1c16f2008-12-17 15:59:43 +0000512 /* Now we update our initial region to have the bounds of the new device,
513 and then intersect all of the clips in our stack with these bounds,
514 to ensure that we can't draw outside of the device's bounds (and trash
515 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000516
reed@android.com8a1c16f2008-12-17 15:59:43 +0000517 NOTE: this is only a partial-fix, since if the new device is larger than
518 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000519 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000520 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
521 reconstruct the correct clips, so this approximation will have to do.
522 The caller really needs to restore() back to the base if they want to
523 accurately take advantage of the new device bounds.
524 */
525
526 if (NULL == device) {
reed@google.com00177082011-10-12 14:34:30 +0000527 rec->fRasterClip->setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000528 while ((rec = (MCRec*)iter.next()) != NULL) {
reed@google.com00177082011-10-12 14:34:30 +0000529 (void)rec->fRasterClip->setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000530 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000531 fClipStack.reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000532 } else {
533 // compute our total bounds for all devices
534 SkIRect bounds;
reed@google.com4b226022011-01-11 18:32:13 +0000535
reed@android.com8a1c16f2008-12-17 15:59:43 +0000536 bounds.set(0, 0, device->width(), device->height());
537
538 // now jam our 1st clip to be bounds, and intersect the rest with that
reed@google.com00177082011-10-12 14:34:30 +0000539 rec->fRasterClip->setRect(bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000540 while ((rec = (MCRec*)iter.next()) != NULL) {
reed@google.com00177082011-10-12 14:34:30 +0000541 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000542 }
543 }
544 return device;
545}
546
reed@google.comaf951c92011-06-16 19:10:39 +0000547SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) {
548 SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (bitmap)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000549 device->unref();
550 return device;
551}
552
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000553bool SkCanvas::readPixels(SkBitmap* bitmap,
554 int x, int y,
555 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000556 SkDevice* device = this->getDevice();
557 if (!device) {
558 return false;
559 }
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000560 return device->readPixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000561}
562
bsalomon@google.comc6980972011-11-02 19:57:21 +0000563bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
bsalomon@google.comace7bd52011-11-02 19:39:51 +0000564 SkDevice* device = this->getDevice();
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000565
566 SkIRect bounds;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000567 bounds.set(0, 0, device->width(), device->height());
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000568 if (!bounds.intersect(srcRect)) {
569 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000570 }
571
572 SkBitmap tmp;
573 tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
574 bounds.height());
575 if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
576 bitmap->swap(tmp);
577 return true;
578 } else {
reed@google.com51df9e32010-12-23 19:29:18 +0000579 return false;
580 }
reed@google.com51df9e32010-12-23 19:29:18 +0000581}
582
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000583void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
584 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000585 SkDevice* device = this->getDevice();
586 if (device) {
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000587 device->writePixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000588 }
589}
590
reed@android.com8a1c16f2008-12-17 15:59:43 +0000591//////////////////////////////////////////////////////////////////////////////
592
reed@android.com8a1c16f2008-12-17 15:59:43 +0000593void SkCanvas::updateDeviceCMCache() {
594 if (fDeviceCMDirty) {
595 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000596 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000597 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000598
reed@android.com8a1c16f2008-12-17 15:59:43 +0000599 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000600 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000601 if (fUseExternalMatrix) {
602 layer->updateExternalMatrix(fExternalMatrix,
603 fExternalInverse);
604 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000606 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000607 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000608 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.comf2b98d62010-12-20 18:26:13 +0000609 if (fUseExternalMatrix) {
610 layer->updateExternalMatrix(fExternalMatrix,
611 fExternalInverse);
612 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000613 } while ((layer = layer->fNext) != NULL);
614 }
615 fDeviceCMDirty = false;
616 }
617}
618
reed@android.comf2b98d62010-12-20 18:26:13 +0000619void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000620 const SkRegion& clip,
621 const SkClipStack& clipStack) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000622 SkASSERT(device);
reed@android.com199f1082009-06-10 02:12:47 +0000623 if (fLastDeviceToGainFocus != device) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000624 device->gainFocus(this, matrix, clip, clipStack);
reed@android.com199f1082009-06-10 02:12:47 +0000625 fLastDeviceToGainFocus = device;
626 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000627}
628
629///////////////////////////////////////////////////////////////////////////////
630
631int SkCanvas::internalSave(SaveFlags flags) {
632 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000633
reed@android.com8a1c16f2008-12-17 15:59:43 +0000634 MCRec* newTop = (MCRec*)fMCStack.push_back();
635 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000636
reed@android.com8a1c16f2008-12-17 15:59:43 +0000637 newTop->fNext = fMCRec;
638 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000639
reed@google.com5c3d1472011-02-22 19:12:23 +0000640 fClipStack.save();
641 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
642
reed@android.com8a1c16f2008-12-17 15:59:43 +0000643 return saveCount;
644}
645
646int SkCanvas::save(SaveFlags flags) {
647 // call shared impl
648 return this->internalSave(flags);
649}
650
651#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
652#define C16MASK (1 << SkBitmap::kRGB_565_Config)
653#define C8MASK (1 << SkBitmap::kA8_Config)
654
655static SkBitmap::Config resolve_config(SkCanvas* canvas,
656 const SkIRect& bounds,
657 SkCanvas::SaveFlags flags,
658 bool* isOpaque) {
659 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
660
661#if 0
662 // loop through and union all the configs we may draw into
663 uint32_t configMask = 0;
664 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
665 {
666 SkDevice* device = canvas->getLayerDevice(i);
667 if (device->intersects(bounds))
668 configMask |= 1 << device->config();
669 }
670
671 // if the caller wants alpha or fullcolor, we can't return 565
672 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
673 SkCanvas::kHasAlphaLayer_SaveFlag))
674 configMask &= ~C16MASK;
675
676 switch (configMask) {
677 case C8MASK: // if we only have A8, return that
678 return SkBitmap::kA8_Config;
679
680 case C16MASK: // if we only have 565, return that
681 return SkBitmap::kRGB_565_Config;
682
683 default:
684 return SkBitmap::kARGB_8888_Config; // default answer
685 }
686#else
687 return SkBitmap::kARGB_8888_Config; // default answer
688#endif
689}
690
691static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
692 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
693}
694
695int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
696 SaveFlags flags) {
697 // do this before we create the layer. We don't call the public save() since
698 // that would invoke a possibly overridden virtual
699 int count = this->internalSave(flags);
700
701 fDeviceCMDirty = true;
702
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000703 SkIRect clipBounds;
704 if (!this->getClipDeviceBounds(&clipBounds)) {
reed@android.comf2b98d62010-12-20 18:26:13 +0000705 return count;
706 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000707
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000708 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000709 if (NULL != bounds) {
710 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000711
reed@android.com8a1c16f2008-12-17 15:59:43 +0000712 this->getTotalMatrix().mapRect(&r, *bounds);
713 r.roundOut(&ir);
714 // early exit if the layer's bounds are clipped out
715 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000716 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000717 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000718 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000719 return count;
720 }
721 } else { // no user bounds, so just use the clip
722 ir = clipBounds;
723 }
724
reed@google.com5c3d1472011-02-22 19:12:23 +0000725 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000726 // early exit if the clip is now empty
727 if (bounds_affects_clip(flags) &&
reed@google.com00177082011-10-12 14:34:30 +0000728 !fMCRec->fRasterClip->op(ir, SkRegion::kIntersect_Op)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000729 return count;
730 }
731
732 bool isOpaque;
733 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
734
bsalomon@google.come97f0852011-06-17 13:10:25 +0000735 SkDevice* device = this->createLayerDevice(config, ir.width(), ir.height(),
736 isOpaque);
bungeman@google.come25c6842011-08-17 14:53:54 +0000737 if (NULL == device) {
738 SkDebugf("Unable to create device for layer.");
739 return count;
740 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000741
reed@google.com6f8f2922011-03-04 22:27:10 +0000742 device->setOrigin(ir.fLeft, ir.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000743 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
744 device->unref();
745
746 layer->fNext = fMCRec->fTopLayer;
747 fMCRec->fLayer = layer;
748 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
749
750 return count;
751}
752
753int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
754 SaveFlags flags) {
755 if (0xFF == alpha) {
756 return this->saveLayer(bounds, NULL, flags);
757 } else {
758 SkPaint tmpPaint;
759 tmpPaint.setAlpha(alpha);
760 return this->saveLayer(bounds, &tmpPaint, flags);
761 }
762}
763
764void SkCanvas::restore() {
765 // check for underflow
766 if (fMCStack.count() > 1) {
767 this->internalRestore();
768 }
769}
770
771void SkCanvas::internalRestore() {
772 SkASSERT(fMCStack.count() != 0);
773
774 fDeviceCMDirty = true;
775 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000776 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000777
reed@google.com5c3d1472011-02-22 19:12:23 +0000778 fClipStack.restore();
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000779 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000780 DeviceCM* layer = fMCRec->fLayer; // may be null
781 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
782 fMCRec->fLayer = NULL;
783
784 // now do the normal restore()
785 fMCRec->~MCRec(); // balanced in save()
786 fMCStack.pop_back();
787 fMCRec = (MCRec*)fMCStack.back();
788
789 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
790 since if we're being recorded, we don't want to record this (the
791 recorder will have already recorded the restore).
792 */
793 if (NULL != layer) {
794 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000795 const SkIPoint& origin = layer->fDevice->getOrigin();
796 this->drawDevice(layer->fDevice, origin.x(), origin.y(),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000797 layer->fPaint);
798 // reset this, since drawDevice will have set it to true
799 fDeviceCMDirty = true;
800 }
801 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000802 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000803
804 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000805}
806
807int SkCanvas::getSaveCount() const {
808 return fMCStack.count();
809}
810
811void SkCanvas::restoreToCount(int count) {
812 // sanity check
813 if (count < 1) {
814 count = 1;
815 }
816 while (fMCStack.count() > count) {
817 this->restore();
818 }
819}
820
821/////////////////////////////////////////////////////////////////////////////
822
823// can't draw it if its empty, or its too big for a fixed-point width or height
824static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000825 return bitmap.width() <= 0 || bitmap.height() <= 0
826#ifndef SK_ALLOW_OVER_32K_BITMAPS
827 || bitmap.width() > 32767 || bitmap.height() > 32767
828#endif
829 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000830}
831
reed@android.comf2b98d62010-12-20 18:26:13 +0000832void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000833 const SkMatrix& matrix, const SkPaint* paint) {
834 if (reject_bitmap(bitmap)) {
835 return;
836 }
837
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000838 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000839 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000840 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000841 }
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000842 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000843}
844
845void SkCanvas::drawDevice(SkDevice* device, int x, int y,
846 const SkPaint* paint) {
847 SkPaint tmp;
848 if (NULL == paint) {
849 tmp.setDither(true);
850 paint = &tmp;
851 }
reed@google.com4b226022011-01-11 18:32:13 +0000852
reed@google.com4e2b3d32011-04-07 14:18:59 +0000853 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000854 while (iter.next()) {
855 iter.fDevice->drawDevice(iter, device, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +0000856 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000857 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000858 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +0000859}
860
861/////////////////////////////////////////////////////////////////////////////
862
863bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
864 fDeviceCMDirty = true;
865 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000866 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000867 return fMCRec->fMatrix->preTranslate(dx, dy);
868}
869
870bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
871 fDeviceCMDirty = true;
872 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000873 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000874 return fMCRec->fMatrix->preScale(sx, sy);
875}
876
877bool SkCanvas::rotate(SkScalar degrees) {
878 fDeviceCMDirty = true;
879 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000880 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000881 return fMCRec->fMatrix->preRotate(degrees);
882}
883
884bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
885 fDeviceCMDirty = true;
886 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000887 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000888 return fMCRec->fMatrix->preSkew(sx, sy);
889}
890
891bool SkCanvas::concat(const SkMatrix& matrix) {
892 fDeviceCMDirty = true;
893 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000894 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000895 return fMCRec->fMatrix->preConcat(matrix);
896}
897
898void SkCanvas::setMatrix(const SkMatrix& matrix) {
899 fDeviceCMDirty = true;
900 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000901 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000902 *fMCRec->fMatrix = matrix;
903}
904
905// this is not virtual, so it must call a virtual method so that subclasses
906// will see its action
907void SkCanvas::resetMatrix() {
908 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +0000909
reed@android.com8a1c16f2008-12-17 15:59:43 +0000910 matrix.reset();
911 this->setMatrix(matrix);
912}
913
914//////////////////////////////////////////////////////////////////////////////
915
reed@google.comc42d35d2011-10-12 11:57:42 +0000916bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000917 AutoValidateClip avc(this);
918
reed@android.com8a1c16f2008-12-17 15:59:43 +0000919 fDeviceCMDirty = true;
920 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000921 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000922
923 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +0000924 // for these simpler matrices, we can stay a rect ever after applying
925 // the matrix. This means we don't have to a) make a path, and b) tell
926 // the region code to scan-convert the path, only to discover that it
927 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000928 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000929
930 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +0000931 fClipStack.clipDevRect(r, op, doAA);
932 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000933 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +0000934 // since we're rotate or some such thing, we convert the rect to a path
935 // and clip against that, since it can handle any matrix. However, to
936 // avoid recursion in the case where we are subclassed (e.g. Pictures)
937 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000938 SkPath path;
939
940 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +0000941 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000942 }
943}
944
reed@google.com00177082011-10-12 14:34:30 +0000945static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +0000946 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +0000947 // base is used to limit the size (and therefore memory allocation) of the
948 // region that results from scan converting devPath.
949 SkRegion base;
950
reed@google.com819c9212011-02-23 18:56:55 +0000951 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +0000952 // since we are intersect, we can do better (tighter) with currRgn's
953 // bounds, than just using the device. However, if currRgn is complex,
954 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +0000955 if (currClip->isRect()) {
956 return currClip->setPath(devPath, *currClip, doAA);
reed@google.com759876a2011-03-03 14:32:51 +0000957 } else {
reed@google.com00177082011-10-12 14:34:30 +0000958 base.setRect(currClip->getBounds());
959 SkRasterClip clip;
960 clip.setPath(devPath, base, doAA);
961 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +0000962 }
reed@google.com819c9212011-02-23 18:56:55 +0000963 } else {
reed@google.com819c9212011-02-23 18:56:55 +0000964 const SkBitmap& bm = canvas->getDevice()->accessBitmap(false);
965 base.setRect(0, 0, bm.width(), bm.height());
966
967 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +0000968 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +0000969 } else {
reed@google.com00177082011-10-12 14:34:30 +0000970 SkRasterClip clip;
971 clip.setPath(devPath, base, doAA);
972 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +0000973 }
974 }
975}
976
reed@google.comc42d35d2011-10-12 11:57:42 +0000977bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000978 AutoValidateClip avc(this);
979
reed@android.com8a1c16f2008-12-17 15:59:43 +0000980 fDeviceCMDirty = true;
981 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000982 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000983
984 SkPath devPath;
985 path.transform(*fMCRec->fMatrix, &devPath);
986
reed@google.comfe701122011-11-08 19:41:23 +0000987 // Check if the transfomation, or the original path itself
988 // made us empty. Note this can also happen if we contained NaN
989 // values. computing the bounds detects this, and will set our
990 // bounds to empty if that is the case. (see SkRect::set(pts, count))
991 if (devPath.getBounds().isEmpty()) {
992 // resetting the path will remove any NaN or other wanky values
993 // that might upset our scan converter.
994 devPath.reset();
995 }
996
reed@google.com5c3d1472011-02-22 19:12:23 +0000997 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +0000998 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +0000999
reed@google.com00177082011-10-12 14:34:30 +00001000 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001001}
1002
1003bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001004 AutoValidateClip avc(this);
1005
reed@android.com8a1c16f2008-12-17 15:59:43 +00001006 fDeviceCMDirty = true;
1007 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001008 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001009
reed@google.com5c3d1472011-02-22 19:12:23 +00001010 // todo: signal fClipStack that we have a region, and therefore (I guess)
1011 // we have to ignore it, and use the region directly?
1012 fClipStack.clipDevRect(rgn.getBounds());
1013
reed@google.com00177082011-10-12 14:34:30 +00001014 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001015}
1016
reed@google.com819c9212011-02-23 18:56:55 +00001017#ifdef SK_DEBUG
1018void SkCanvas::validateClip() const {
1019 // construct clipRgn from the clipstack
1020 const SkDevice* device = this->getDevice();
1021 SkIRect ir;
1022 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001023 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001024
1025 SkClipStack::B2FIter iter(fClipStack);
1026 const SkClipStack::B2FIter::Clip* clip;
1027 while ((clip = iter.next()) != NULL) {
1028 if (clip->fPath) {
reed@google.com00177082011-10-12 14:34:30 +00001029 clipPathHelper(this, &tmpClip, *clip->fPath, clip->fOp, clip->fDoAA);
reed@google.com819c9212011-02-23 18:56:55 +00001030 } else if (clip->fRect) {
1031 clip->fRect->round(&ir);
reed@google.com00177082011-10-12 14:34:30 +00001032 tmpClip.op(ir, clip->fOp);
reed@google.com819c9212011-02-23 18:56:55 +00001033 } else {
reed@google.com00177082011-10-12 14:34:30 +00001034 tmpClip.setEmpty();
reed@google.com819c9212011-02-23 18:56:55 +00001035 }
1036 }
1037
reed@google.com6f8f2922011-03-04 22:27:10 +00001038#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001039 // now compare against the current rgn
1040 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001041 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001042#endif
reed@google.com819c9212011-02-23 18:56:55 +00001043}
1044#endif
1045
reed@google.com5c3d1472011-02-22 19:12:23 +00001046///////////////////////////////////////////////////////////////////////////////
1047
reed@android.comba09de42010-02-05 20:46:05 +00001048void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001049 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001050 SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
1051 fLocalBoundsCompareTypeBW;
1052
1053 if (!this->getClipBounds(&r, et)) {
1054 rCompare.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001055 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001056 rCompare.set(SkScalarToCompareType(r.fLeft),
1057 SkScalarToCompareType(r.fTop),
1058 SkScalarToCompareType(r.fRight),
1059 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001060 }
1061}
1062
reed@android.comd252db02009-04-01 18:31:44 +00001063/* current impl ignores edgetype, and relies on
1064 getLocalClipBoundsCompareType(), which always returns a value assuming
1065 antialiasing (worst case)
1066 */
reed@android.comba09de42010-02-05 20:46:05 +00001067bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001068
1069 if (!rect.hasValidCoordinates())
1070 return true;
1071
reed@google.com00177082011-10-12 14:34:30 +00001072 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001073 return true;
1074 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001075
tomhudson@google.com8d430182011-06-06 19:11:19 +00001076 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001077 SkRect dst;
1078 fMCRec->fMatrix->mapRect(&dst, rect);
1079 SkIRect idst;
1080 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001081 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001082 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001083 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
reed@android.comd252db02009-04-01 18:31:44 +00001084
reed@android.coma380ae42009-07-21 01:17:02 +00001085 // for speed, do the most likely reject compares first
1086 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1087 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1088 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1089 return true;
1090 }
1091 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1092 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1093 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1094 return true;
1095 }
1096 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001097 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001098}
1099
1100bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +00001101 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001102}
1103
1104bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
1105 /* current impl ignores edgetype, and relies on
1106 getLocalClipBoundsCompareType(), which always returns a value assuming
1107 antialiasing (worst case)
1108 */
1109
reed@google.com00177082011-10-12 14:34:30 +00001110 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001111 return true;
1112 }
reed@google.com4b226022011-01-11 18:32:13 +00001113
reed@android.comaefd2bc2009-03-30 21:02:14 +00001114 SkScalarCompareType userT = SkScalarToCompareType(top);
1115 SkScalarCompareType userB = SkScalarToCompareType(bottom);
reed@google.com4b226022011-01-11 18:32:13 +00001116
reed@android.com8a1c16f2008-12-17 15:59:43 +00001117 // check for invalid user Y coordinates (i.e. empty)
reed@android.comd252db02009-04-01 18:31:44 +00001118 // reed: why do we need to do this check, since it slows us down?
reed@android.com8a1c16f2008-12-17 15:59:43 +00001119 if (userT >= userB) {
1120 return true;
1121 }
reed@google.com4b226022011-01-11 18:32:13 +00001122
reed@android.com8a1c16f2008-12-17 15:59:43 +00001123 // check if we are above or below the local clip bounds
1124 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
1125 return userT >= clipR.fBottom || userB <= clipR.fTop;
1126}
1127
1128bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001129 SkIRect ibounds;
1130 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001131 return false;
1132 }
1133
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001134 SkMatrix inverse;
1135 // if we can't invert the CTM, we can't return local clip bounds
1136 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001137 if (bounds) {
1138 bounds->setEmpty();
1139 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001140 return false;
1141 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001142
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001143 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001144 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001145 // adjust it outwards if we are antialiasing
1146 int inset = (kAA_EdgeType == et);
1147 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1148 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001149 inverse.mapRect(bounds, r);
1150 }
1151 return true;
1152}
1153
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001154bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001155 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001156 if (clip.isEmpty()) {
1157 if (bounds) {
1158 bounds->setEmpty();
1159 }
1160 return false;
1161 }
1162
1163 if (NULL != bounds) {
1164 *bounds = clip.getBounds();
1165 }
1166 return true;
1167}
1168
reed@android.com8a1c16f2008-12-17 15:59:43 +00001169const SkMatrix& SkCanvas::getTotalMatrix() const {
1170 return *fMCRec->fMatrix;
1171}
1172
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001173SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001174 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1175 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001176 return kComplex_ClipType;
1177}
1178
reed@android.com8a1c16f2008-12-17 15:59:43 +00001179const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001180 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001181}
1182
reed@google.com7d7ca792011-02-23 22:39:18 +00001183const SkClipStack& SkCanvas::getTotalClipStack() const {
1184 return fClipStack;
1185}
1186
reed@android.comf2b98d62010-12-20 18:26:13 +00001187void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1188 if (NULL == matrix || matrix->isIdentity()) {
1189 if (fUseExternalMatrix) {
1190 fDeviceCMDirty = true;
1191 }
1192 fUseExternalMatrix = false;
1193 } else {
1194 fUseExternalMatrix = true;
1195 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.com4b226022011-01-11 18:32:13 +00001196
reed@android.comf2b98d62010-12-20 18:26:13 +00001197 fExternalMatrix = *matrix;
1198 matrix->invert(&fExternalInverse);
1199 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001200}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001201
bsalomon@google.come97f0852011-06-17 13:10:25 +00001202SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
1203 int width, int height,
1204 bool isOpaque) {
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001205 SkDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001206 if (device) {
1207 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1208 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001209 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001210 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001211 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001212}
1213
bsalomon@google.come97f0852011-06-17 13:10:25 +00001214SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
1215 int width, int height,
1216 bool isOpaque) {
1217 SkDevice* device = this->getDevice();
1218 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001219 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001220 } else {
1221 return NULL;
1222 }
1223}
1224
1225
reed@android.com8a1c16f2008-12-17 15:59:43 +00001226//////////////////////////////////////////////////////////////////////////////
1227// These are the virtual drawing methods
1228//////////////////////////////////////////////////////////////////////////////
1229
reed@google.com2a981812011-04-14 18:59:28 +00001230void SkCanvas::clear(SkColor color) {
1231 SkDrawIter iter(this);
1232
1233 while (iter.next()) {
1234 iter.fDevice->clear(color);
1235 }
1236}
1237
reed@android.com8a1c16f2008-12-17 15:59:43 +00001238void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001239 this->internalDrawPaint(paint);
1240}
1241
1242void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001243 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001244
1245 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001246 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001247 }
1248
reed@google.com4e2b3d32011-04-07 14:18:59 +00001249 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001250}
1251
1252void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1253 const SkPaint& paint) {
1254 if ((long)count <= 0) {
1255 return;
1256 }
1257
1258 SkASSERT(pts != NULL);
1259
reed@google.com4e2b3d32011-04-07 14:18:59 +00001260 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001261
reed@android.com8a1c16f2008-12-17 15:59:43 +00001262 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001263 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001264 }
reed@google.com4b226022011-01-11 18:32:13 +00001265
reed@google.com4e2b3d32011-04-07 14:18:59 +00001266 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001267}
1268
1269void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1270 if (paint.canComputeFastBounds()) {
1271 SkRect storage;
1272 if (this->quickReject(paint.computeFastBounds(r, &storage),
1273 paint2EdgeType(&paint))) {
1274 return;
1275 }
1276 }
reed@google.com4b226022011-01-11 18:32:13 +00001277
reed@google.com4e2b3d32011-04-07 14:18:59 +00001278 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001279
1280 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001281 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001282 }
1283
reed@google.com4e2b3d32011-04-07 14:18:59 +00001284 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001285}
1286
1287void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001288 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001289 SkRect storage;
1290 const SkRect& bounds = path.getBounds();
1291 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001292 paint2EdgeType(&paint))) {
1293 return;
1294 }
1295 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001296 if (path.isEmpty()) {
1297 if (path.isInverseFillType()) {
1298 this->internalDrawPaint(paint);
1299 }
1300 return;
1301 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001302
reed@google.com4e2b3d32011-04-07 14:18:59 +00001303 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001304
1305 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001306 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001307 }
1308
reed@google.com4e2b3d32011-04-07 14:18:59 +00001309 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001310}
1311
1312void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1313 const SkPaint* paint) {
1314 SkDEBUGCODE(bitmap.validate();)
1315
reed@google.com3d608122011-11-21 15:16:16 +00001316 if (NULL == paint || paint->canComputeFastBounds()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001317 SkRect fastBounds;
1318 fastBounds.set(x, y,
1319 x + SkIntToScalar(bitmap.width()),
1320 y + SkIntToScalar(bitmap.height()));
1321 if (this->quickReject(fastBounds, paint2EdgeType(paint))) {
1322 return;
1323 }
1324 }
reed@google.com4b226022011-01-11 18:32:13 +00001325
reed@android.com8a1c16f2008-12-17 15:59:43 +00001326 SkMatrix matrix;
1327 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001328 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001329}
1330
reed@google.com9987ec32011-09-07 11:57:52 +00001331// this one is non-virtual, so it can be called safely by other canvas apis
1332void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1333 const SkRect& dst, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001334 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1335 return;
1336 }
reed@google.com9987ec32011-09-07 11:57:52 +00001337
reed@android.com8a1c16f2008-12-17 15:59:43 +00001338 // do this now, to avoid the cost of calling extract for RLE bitmaps
reed@google.com3d608122011-11-21 15:16:16 +00001339 if (NULL == paint || paint->canComputeFastBounds()) {
1340 if (this->quickReject(dst, paint2EdgeType(paint))) {
1341 return;
1342 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001343 }
reed@google.com3d608122011-11-21 15:16:16 +00001344
reed@android.com8a1c16f2008-12-17 15:59:43 +00001345 const SkBitmap* bitmapPtr = &bitmap;
reed@google.com9987ec32011-09-07 11:57:52 +00001346
reed@android.com8a1c16f2008-12-17 15:59:43 +00001347 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001348 SkRect tmpSrc;
1349 if (src) {
1350 tmpSrc.set(*src);
1351 // if the extract process clipped off the top or left of the
1352 // original, we adjust for that here to get the position right.
1353 if (tmpSrc.fLeft > 0) {
1354 tmpSrc.fRight -= tmpSrc.fLeft;
1355 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001356 }
reed@android.com87899992009-10-16 14:48:38 +00001357 if (tmpSrc.fTop > 0) {
1358 tmpSrc.fBottom -= tmpSrc.fTop;
1359 tmpSrc.fTop = 0;
1360 }
1361 } else {
1362 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1363 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001364 }
reed@android.com87899992009-10-16 14:48:38 +00001365 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
reed@google.com9987ec32011-09-07 11:57:52 +00001366
reed@android.comf2b98d62010-12-20 18:26:13 +00001367 // ensure that src is "valid" before we pass it to our internal routines
1368 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1369 SkIRect tmpISrc;
1370 if (src) {
1371 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
reed@google.com2ade0862011-03-17 17:48:04 +00001372 if (!tmpISrc.intersect(*src)) {
1373 return;
1374 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001375 src = &tmpISrc;
1376 }
1377 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001378}
1379
reed@google.com9987ec32011-09-07 11:57:52 +00001380void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1381 const SkRect& dst, const SkPaint* paint) {
1382 SkDEBUGCODE(bitmap.validate();)
1383 this->internalDrawBitmapRect(bitmap, src, dst, paint);
1384}
1385
reed@android.com8a1c16f2008-12-17 15:59:43 +00001386void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1387 const SkPaint* paint) {
1388 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001389 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001390}
1391
reed@android.comf2b98d62010-12-20 18:26:13 +00001392void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1393 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001394 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001395
reed@google.com4e2b3d32011-04-07 14:18:59 +00001396 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001397
reed@android.com8a1c16f2008-12-17 15:59:43 +00001398 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001399 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001400 }
reed@android.com9b039062009-02-11 15:09:58 +00001401
reed@google.com4e2b3d32011-04-07 14:18:59 +00001402 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001403}
1404
reed@google.com9987ec32011-09-07 11:57:52 +00001405void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1406 const SkIRect& center, const SkRect& dst,
1407 const SkPaint* paint) {
reed@google.com3d608122011-11-21 15:16:16 +00001408 if (NULL == paint || paint->canComputeFastBounds()) {
1409 if (this->quickReject(dst, paint2EdgeType(paint))) {
1410 return;
1411 }
1412 }
1413
reed@google.com9987ec32011-09-07 11:57:52 +00001414 const int32_t w = bitmap.width();
1415 const int32_t h = bitmap.height();
1416
1417 SkIRect c = center;
1418 // pin center to the bounds of the bitmap
1419 c.fLeft = SkMax32(0, center.fLeft);
1420 c.fTop = SkMax32(0, center.fTop);
1421 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1422 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1423
1424 const int32_t srcX[4] = { 0, c.fLeft, c.fRight, w };
1425 const int32_t srcY[4] = { 0, c.fTop, c.fBottom, h };
1426 SkScalar dstX[4] = {
1427 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1428 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1429 };
1430 SkScalar dstY[4] = {
1431 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1432 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1433 };
1434
1435 if (dstX[1] > dstX[2]) {
1436 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1437 dstX[2] = dstX[1];
1438 }
1439
1440 if (dstY[1] > dstY[2]) {
1441 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1442 dstY[2] = dstY[1];
1443 }
1444
1445 SkIRect s;
1446 SkRect d;
1447 for (int y = 0; y < 3; y++) {
1448 s.fTop = srcY[y];
1449 s.fBottom = srcY[y+1];
1450 d.fTop = dstY[y];
1451 d.fBottom = dstY[y+1];
1452 for (int x = 0; x < 3; x++) {
1453 s.fLeft = srcX[x];
1454 s.fRight = srcX[x+1];
1455 d.fLeft = dstX[x];
1456 d.fRight = dstX[x+1];
1457 this->internalDrawBitmapRect(bitmap, &s, d, paint);
1458 }
1459 }
1460}
1461
1462void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1463 const SkRect& dst, const SkPaint* paint) {
1464 SkDEBUGCODE(bitmap.validate();)
1465
1466 // Need a device entry-point, so gpu can use a mesh
1467 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1468}
1469
reed@android.com8a1c16f2008-12-17 15:59:43 +00001470void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1471 const SkPaint* paint) {
1472 SkDEBUGCODE(bitmap.validate();)
reed@google.com4b226022011-01-11 18:32:13 +00001473
reed@android.com8a1c16f2008-12-17 15:59:43 +00001474 if (reject_bitmap(bitmap)) {
1475 return;
1476 }
reed@google.com4b226022011-01-11 18:32:13 +00001477
reed@android.com8a1c16f2008-12-17 15:59:43 +00001478 SkPaint tmp;
1479 if (NULL == paint) {
1480 paint = &tmp;
1481 }
reed@google.com4b226022011-01-11 18:32:13 +00001482
reed@google.com4e2b3d32011-04-07 14:18:59 +00001483 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001484
reed@android.com8a1c16f2008-12-17 15:59:43 +00001485 while (iter.next()) {
1486 iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +00001487 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001488 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001489 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001490}
1491
reed@google.comf67e4cf2011-03-15 20:56:58 +00001492class SkDeviceFilteredPaint {
1493public:
1494 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1495 SkDevice::TextFlags flags;
1496 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001497 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001498 newPaint->setFlags(flags.fFlags);
1499 newPaint->setHinting(flags.fHinting);
1500 fPaint = newPaint;
1501 } else {
1502 fPaint = &paint;
1503 }
1504 }
1505
reed@google.comf67e4cf2011-03-15 20:56:58 +00001506 const SkPaint& paint() const { return *fPaint; }
1507
1508private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001509 const SkPaint* fPaint;
1510 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001511};
1512
bungeman@google.com52c748b2011-08-22 21:30:43 +00001513void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1514 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001515 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001516 draw.fDevice->drawRect(draw, r, paint);
1517 } else {
1518 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001519 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001520 draw.fDevice->drawRect(draw, r, p);
1521 }
1522}
1523
1524void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1525 const char text[], size_t byteLength,
1526 SkScalar x, SkScalar y) {
1527 SkASSERT(byteLength == 0 || text != NULL);
1528
1529 // nothing to draw
1530 if (text == NULL || byteLength == 0 ||
1531 draw.fClip->isEmpty() ||
1532 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1533 return;
1534 }
1535
1536 SkScalar width = 0;
1537 SkPoint start;
1538
1539 start.set(0, 0); // to avoid warning
1540 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1541 SkPaint::kStrikeThruText_Flag)) {
1542 width = paint.measureText(text, byteLength);
1543
1544 SkScalar offsetX = 0;
1545 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1546 offsetX = SkScalarHalf(width);
1547 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1548 offsetX = width;
1549 }
1550 start.set(x - offsetX, y);
1551 }
1552
1553 if (0 == width) {
1554 return;
1555 }
1556
1557 uint32_t flags = paint.getFlags();
1558
1559 if (flags & (SkPaint::kUnderlineText_Flag |
1560 SkPaint::kStrikeThruText_Flag)) {
1561 SkScalar textSize = paint.getTextSize();
1562 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1563 SkRect r;
1564
1565 r.fLeft = start.fX;
1566 r.fRight = start.fX + width;
1567
1568 if (flags & SkPaint::kUnderlineText_Flag) {
1569 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1570 start.fY);
1571 r.fTop = offset;
1572 r.fBottom = offset + height;
1573 DrawRect(draw, paint, r, textSize);
1574 }
1575 if (flags & SkPaint::kStrikeThruText_Flag) {
1576 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1577 start.fY);
1578 r.fTop = offset;
1579 r.fBottom = offset + height;
1580 DrawRect(draw, paint, r, textSize);
1581 }
1582 }
1583}
1584
reed@android.com8a1c16f2008-12-17 15:59:43 +00001585void SkCanvas::drawText(const void* text, size_t byteLength,
1586 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001587 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001588
1589 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001590 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001591 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001592 DrawTextDecorations(iter, dfp.paint(),
1593 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001594 }
1595
reed@google.com4e2b3d32011-04-07 14:18:59 +00001596 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001597}
1598
1599void SkCanvas::drawPosText(const void* text, size_t byteLength,
1600 const SkPoint pos[], const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001601 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001602
reed@android.com8a1c16f2008-12-17 15:59:43 +00001603 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001604 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001605 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001606 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001607 }
reed@google.com4b226022011-01-11 18:32:13 +00001608
reed@google.com4e2b3d32011-04-07 14:18:59 +00001609 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001610}
1611
1612void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1613 const SkScalar xpos[], SkScalar constY,
1614 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001615 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001616
reed@android.com8a1c16f2008-12-17 15:59:43 +00001617 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001618 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001619 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001620 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001621 }
reed@google.com4b226022011-01-11 18:32:13 +00001622
reed@google.com4e2b3d32011-04-07 14:18:59 +00001623 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001624}
1625
1626void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1627 const SkPath& path, const SkMatrix* matrix,
1628 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001629 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001630
1631 while (iter.next()) {
1632 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001633 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001634 }
1635
reed@google.com4e2b3d32011-04-07 14:18:59 +00001636 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001637}
1638
djsollen@google.com56c69772011-11-08 19:00:26 +00001639#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001640void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1641 const SkPoint pos[], const SkPaint& paint,
1642 const SkPath& path, const SkMatrix* matrix) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001643 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001644
1645 while (iter.next()) {
1646 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001647 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001648 }
1649
reed@google.com4e2b3d32011-04-07 14:18:59 +00001650 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001651}
1652#endif
1653
reed@android.com8a1c16f2008-12-17 15:59:43 +00001654void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1655 const SkPoint verts[], const SkPoint texs[],
1656 const SkColor colors[], SkXfermode* xmode,
1657 const uint16_t indices[], int indexCount,
1658 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001659 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001660
reed@android.com8a1c16f2008-12-17 15:59:43 +00001661 while (iter.next()) {
1662 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001663 colors, xmode, indices, indexCount,
1664 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001665 }
reed@google.com4b226022011-01-11 18:32:13 +00001666
reed@google.com4e2b3d32011-04-07 14:18:59 +00001667 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001668}
1669
reed@android.comcb608442009-12-04 21:32:27 +00001670void SkCanvas::drawData(const void* data, size_t length) {
1671 // do nothing. Subclasses may do something with the data
1672}
1673
reed@android.com8a1c16f2008-12-17 15:59:43 +00001674//////////////////////////////////////////////////////////////////////////////
1675// These methods are NOT virtual, and therefore must call back into virtual
1676// methods, rather than actually drawing themselves.
1677//////////////////////////////////////////////////////////////////////////////
1678
1679void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001680 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001681 SkPaint paint;
1682
1683 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001684 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001685 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001686 }
1687 this->drawPaint(paint);
1688}
1689
reed@android.com845fdac2009-06-23 03:01:32 +00001690void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001691 SkPaint paint;
1692
1693 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001694 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001695 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001696 }
1697 this->drawPaint(paint);
1698}
1699
1700void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1701 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001702
reed@android.com8a1c16f2008-12-17 15:59:43 +00001703 pt.set(x, y);
1704 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1705}
1706
1707void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1708 SkPoint pt;
1709 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001710
reed@android.com8a1c16f2008-12-17 15:59:43 +00001711 pt.set(x, y);
1712 paint.setColor(color);
1713 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1714}
1715
1716void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1717 const SkPaint& paint) {
1718 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001719
reed@android.com8a1c16f2008-12-17 15:59:43 +00001720 pts[0].set(x0, y0);
1721 pts[1].set(x1, y1);
1722 this->drawPoints(kLines_PointMode, 2, pts, paint);
1723}
1724
1725void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1726 SkScalar right, SkScalar bottom,
1727 const SkPaint& paint) {
1728 SkRect r;
1729
1730 r.set(left, top, right, bottom);
1731 this->drawRect(r, paint);
1732}
1733
1734void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1735 const SkPaint& paint) {
1736 if (radius < 0) {
1737 radius = 0;
1738 }
1739
1740 SkRect r;
1741 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001742
reed@android.com8a1c16f2008-12-17 15:59:43 +00001743 if (paint.canComputeFastBounds()) {
1744 SkRect storage;
1745 if (this->quickReject(paint.computeFastBounds(r, &storage),
1746 paint2EdgeType(&paint))) {
1747 return;
1748 }
1749 }
reed@google.com4b226022011-01-11 18:32:13 +00001750
reed@android.com8a1c16f2008-12-17 15:59:43 +00001751 SkPath path;
1752 path.addOval(r);
1753 this->drawPath(path, paint);
1754}
1755
1756void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1757 const SkPaint& paint) {
1758 if (rx > 0 && ry > 0) {
1759 if (paint.canComputeFastBounds()) {
1760 SkRect storage;
1761 if (this->quickReject(paint.computeFastBounds(r, &storage),
1762 paint2EdgeType(&paint))) {
1763 return;
1764 }
1765 }
1766
1767 SkPath path;
1768 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1769 this->drawPath(path, paint);
1770 } else {
1771 this->drawRect(r, paint);
1772 }
1773}
1774
1775void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1776 if (paint.canComputeFastBounds()) {
1777 SkRect storage;
1778 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1779 paint2EdgeType(&paint))) {
1780 return;
1781 }
1782 }
1783
1784 SkPath path;
1785 path.addOval(oval);
1786 this->drawPath(path, paint);
1787}
1788
1789void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1790 SkScalar sweepAngle, bool useCenter,
1791 const SkPaint& paint) {
1792 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1793 this->drawOval(oval, paint);
1794 } else {
1795 SkPath path;
1796 if (useCenter) {
1797 path.moveTo(oval.centerX(), oval.centerY());
1798 }
1799 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1800 if (useCenter) {
1801 path.close();
1802 }
1803 this->drawPath(path, paint);
1804 }
1805}
1806
1807void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1808 const SkPath& path, SkScalar hOffset,
1809 SkScalar vOffset, const SkPaint& paint) {
1810 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001811
reed@android.com8a1c16f2008-12-17 15:59:43 +00001812 matrix.setTranslate(hOffset, vOffset);
1813 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1814}
1815
reed@android.comf76bacf2009-05-13 14:00:33 +00001816///////////////////////////////////////////////////////////////////////////////
1817
reed@android.com8a1c16f2008-12-17 15:59:43 +00001818void SkCanvas::drawPicture(SkPicture& picture) {
1819 int saveCount = save();
1820 picture.draw(this);
1821 restoreToCount(saveCount);
1822}
1823
reed@android.com8a1c16f2008-12-17 15:59:43 +00001824///////////////////////////////////////////////////////////////////////////////
1825///////////////////////////////////////////////////////////////////////////////
1826
1827SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00001828 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001829
1830 SkASSERT(canvas);
1831
1832 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
1833 fDone = !fImpl->next();
1834}
1835
1836SkCanvas::LayerIter::~LayerIter() {
1837 fImpl->~SkDrawIter();
1838}
1839
1840void SkCanvas::LayerIter::next() {
1841 fDone = !fImpl->next();
1842}
1843
1844SkDevice* SkCanvas::LayerIter::device() const {
1845 return fImpl->getDevice();
1846}
1847
1848const SkMatrix& SkCanvas::LayerIter::matrix() const {
1849 return fImpl->getMatrix();
1850}
1851
1852const SkPaint& SkCanvas::LayerIter::paint() const {
1853 const SkPaint* paint = fImpl->getPaint();
1854 if (NULL == paint) {
1855 paint = &fDefaultPaint;
1856 }
1857 return *paint;
1858}
1859
1860const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
1861int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
1862int SkCanvas::LayerIter::y() const { return fImpl->getY(); }