blob: 353c92c6f0a5fc58c8c345adee8bdf7c57008b38 [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@google.com7c202932011-12-14 18:48:05 +0000407 fLayerCount = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000408
409 fMCRec = (MCRec*)fMCStack.push_back();
410 new (fMCRec) MCRec(NULL, 0);
411
412 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL));
413 fMCRec->fTopLayer = fMCRec->fLayer;
414 fMCRec->fNext = NULL;
415
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000416 fExternalMatrix.reset();
417 fExternalInverse.reset();
reed@android.comf2b98d62010-12-20 18:26:13 +0000418 fUseExternalMatrix = false;
419
reed@android.com8a1c16f2008-12-17 15:59:43 +0000420 return this->setDevice(device);
421}
422
reed@google.comcde92112011-07-06 20:00:52 +0000423SkCanvas::SkCanvas()
424: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000425 inc_canvas();
reed@google.comcde92112011-07-06 20:00:52 +0000426
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000427 this->init(NULL);
428}
429
reed@android.com8a1c16f2008-12-17 15:59:43 +0000430SkCanvas::SkCanvas(SkDevice* device)
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000431 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000432 inc_canvas();
433
434 this->init(device);
435}
436
437SkCanvas::SkCanvas(const SkBitmap& bitmap)
438 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
439 inc_canvas();
440
reed@google.comcde92112011-07-06 20:00:52 +0000441 this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000442}
443
444SkCanvas::~SkCanvas() {
445 // free up the contents of our deque
446 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000447 SkASSERT(0 == fLayerCount);
448
reed@android.com8a1c16f2008-12-17 15:59:43 +0000449 this->internalRestore(); // restore the last, since we're going away
450
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000451 SkSafeUnref(fBounder);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000452
reed@android.com8a1c16f2008-12-17 15:59:43 +0000453 dec_canvas();
454}
455
456SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
457 SkRefCnt_SafeAssign(fBounder, bounder);
458 return bounder;
459}
460
461SkDrawFilter* SkCanvas::getDrawFilter() const {
462 return fMCRec->fFilter;
463}
464
465SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
466 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
467 return filter;
468}
469
470///////////////////////////////////////////////////////////////////////////////
471
reed@google.com210ce002011-11-01 14:24:23 +0000472SkISize SkCanvas::getDeviceSize() const {
473 SkDevice* d = this->getDevice();
474 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
475}
476
reed@android.com8a1c16f2008-12-17 15:59:43 +0000477SkDevice* SkCanvas::getDevice() const {
478 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000479 SkDeque::F2BIter iter(fMCStack);
480 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000481 SkASSERT(rec && rec->fLayer);
482 return rec->fLayer->fDevice;
483}
484
reed@google.com9266fed2011-03-30 00:18:03 +0000485SkDevice* SkCanvas::getTopDevice() const {
486 return fMCRec->fTopLayer->fDevice;
487}
488
reed@android.com8a1c16f2008-12-17 15:59:43 +0000489SkDevice* SkCanvas::setDevice(SkDevice* device) {
490 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000491 SkDeque::F2BIter iter(fMCStack);
492 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000493 SkASSERT(rec && rec->fLayer);
494 SkDevice* rootDevice = rec->fLayer->fDevice;
495
496 if (rootDevice == device) {
497 return device;
498 }
reed@google.com4b226022011-01-11 18:32:13 +0000499
reed@android.com8a1c16f2008-12-17 15:59:43 +0000500 /* Notify the devices that they are going in/out of scope, so they can do
501 things like lock/unlock their pixels, etc.
502 */
503 if (device) {
504 device->lockPixels();
505 }
506 if (rootDevice) {
507 rootDevice->unlockPixels();
508 }
509
510 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
511 rootDevice = device;
512
513 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000514
reed@android.com8a1c16f2008-12-17 15:59:43 +0000515 /* Now we update our initial region to have the bounds of the new device,
516 and then intersect all of the clips in our stack with these bounds,
517 to ensure that we can't draw outside of the device's bounds (and trash
518 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000519
reed@android.com8a1c16f2008-12-17 15:59:43 +0000520 NOTE: this is only a partial-fix, since if the new device is larger than
521 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000522 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000523 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
524 reconstruct the correct clips, so this approximation will have to do.
525 The caller really needs to restore() back to the base if they want to
526 accurately take advantage of the new device bounds.
527 */
528
529 if (NULL == device) {
reed@google.com00177082011-10-12 14:34:30 +0000530 rec->fRasterClip->setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000531 while ((rec = (MCRec*)iter.next()) != NULL) {
reed@google.com00177082011-10-12 14:34:30 +0000532 (void)rec->fRasterClip->setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000533 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000534 fClipStack.reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000535 } else {
536 // compute our total bounds for all devices
537 SkIRect bounds;
reed@google.com4b226022011-01-11 18:32:13 +0000538
reed@android.com8a1c16f2008-12-17 15:59:43 +0000539 bounds.set(0, 0, device->width(), device->height());
540
541 // now jam our 1st clip to be bounds, and intersect the rest with that
reed@google.com00177082011-10-12 14:34:30 +0000542 rec->fRasterClip->setRect(bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000543 while ((rec = (MCRec*)iter.next()) != NULL) {
reed@google.com00177082011-10-12 14:34:30 +0000544 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000545 }
546 }
547 return device;
548}
549
reed@google.comaf951c92011-06-16 19:10:39 +0000550SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) {
551 SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (bitmap)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000552 device->unref();
553 return device;
554}
555
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000556bool SkCanvas::readPixels(SkBitmap* bitmap,
557 int x, int y,
558 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000559 SkDevice* device = this->getDevice();
560 if (!device) {
561 return false;
562 }
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000563 return device->readPixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000564}
565
bsalomon@google.comc6980972011-11-02 19:57:21 +0000566bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
bsalomon@google.comace7bd52011-11-02 19:39:51 +0000567 SkDevice* device = this->getDevice();
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000568
569 SkIRect bounds;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000570 bounds.set(0, 0, device->width(), device->height());
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000571 if (!bounds.intersect(srcRect)) {
572 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000573 }
574
575 SkBitmap tmp;
576 tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
577 bounds.height());
578 if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
579 bitmap->swap(tmp);
580 return true;
581 } else {
reed@google.com51df9e32010-12-23 19:29:18 +0000582 return false;
583 }
reed@google.com51df9e32010-12-23 19:29:18 +0000584}
585
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000586void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
587 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000588 SkDevice* device = this->getDevice();
589 if (device) {
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000590 device->writePixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000591 }
592}
593
reed@android.com8a1c16f2008-12-17 15:59:43 +0000594//////////////////////////////////////////////////////////////////////////////
595
reed@android.com8a1c16f2008-12-17 15:59:43 +0000596void SkCanvas::updateDeviceCMCache() {
597 if (fDeviceCMDirty) {
598 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000599 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000600 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000601
reed@android.com8a1c16f2008-12-17 15:59:43 +0000602 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000603 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000604 if (fUseExternalMatrix) {
605 layer->updateExternalMatrix(fExternalMatrix,
606 fExternalInverse);
607 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000608 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000609 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000610 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000611 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.comf2b98d62010-12-20 18:26:13 +0000612 if (fUseExternalMatrix) {
613 layer->updateExternalMatrix(fExternalMatrix,
614 fExternalInverse);
615 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000616 } while ((layer = layer->fNext) != NULL);
617 }
618 fDeviceCMDirty = false;
619 }
620}
621
reed@android.comf2b98d62010-12-20 18:26:13 +0000622void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000623 const SkRegion& clip,
624 const SkClipStack& clipStack) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000625 SkASSERT(device);
reed@android.com199f1082009-06-10 02:12:47 +0000626 if (fLastDeviceToGainFocus != device) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000627 device->gainFocus(this, matrix, clip, clipStack);
reed@android.com199f1082009-06-10 02:12:47 +0000628 fLastDeviceToGainFocus = device;
629 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000630}
631
632///////////////////////////////////////////////////////////////////////////////
633
634int SkCanvas::internalSave(SaveFlags flags) {
635 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000636
reed@android.com8a1c16f2008-12-17 15:59:43 +0000637 MCRec* newTop = (MCRec*)fMCStack.push_back();
638 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000639
reed@android.com8a1c16f2008-12-17 15:59:43 +0000640 newTop->fNext = fMCRec;
641 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000642
reed@google.com5c3d1472011-02-22 19:12:23 +0000643 fClipStack.save();
644 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
645
reed@android.com8a1c16f2008-12-17 15:59:43 +0000646 return saveCount;
647}
648
649int SkCanvas::save(SaveFlags flags) {
650 // call shared impl
651 return this->internalSave(flags);
652}
653
654#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
655#define C16MASK (1 << SkBitmap::kRGB_565_Config)
656#define C8MASK (1 << SkBitmap::kA8_Config)
657
658static SkBitmap::Config resolve_config(SkCanvas* canvas,
659 const SkIRect& bounds,
660 SkCanvas::SaveFlags flags,
661 bool* isOpaque) {
662 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
663
664#if 0
665 // loop through and union all the configs we may draw into
666 uint32_t configMask = 0;
667 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
668 {
669 SkDevice* device = canvas->getLayerDevice(i);
670 if (device->intersects(bounds))
671 configMask |= 1 << device->config();
672 }
673
674 // if the caller wants alpha or fullcolor, we can't return 565
675 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
676 SkCanvas::kHasAlphaLayer_SaveFlag))
677 configMask &= ~C16MASK;
678
679 switch (configMask) {
680 case C8MASK: // if we only have A8, return that
681 return SkBitmap::kA8_Config;
682
683 case C16MASK: // if we only have 565, return that
684 return SkBitmap::kRGB_565_Config;
685
686 default:
687 return SkBitmap::kARGB_8888_Config; // default answer
688 }
689#else
690 return SkBitmap::kARGB_8888_Config; // default answer
691#endif
692}
693
694static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
695 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
696}
697
698int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
699 SaveFlags flags) {
700 // do this before we create the layer. We don't call the public save() since
701 // that would invoke a possibly overridden virtual
702 int count = this->internalSave(flags);
703
704 fDeviceCMDirty = true;
705
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000706 SkIRect clipBounds;
707 if (!this->getClipDeviceBounds(&clipBounds)) {
reed@android.comf2b98d62010-12-20 18:26:13 +0000708 return count;
709 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000710
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000711 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000712 if (NULL != bounds) {
713 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000714
reed@android.com8a1c16f2008-12-17 15:59:43 +0000715 this->getTotalMatrix().mapRect(&r, *bounds);
716 r.roundOut(&ir);
717 // early exit if the layer's bounds are clipped out
718 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000719 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000720 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000721 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000722 return count;
723 }
724 } else { // no user bounds, so just use the clip
725 ir = clipBounds;
726 }
727
reed@google.com5c3d1472011-02-22 19:12:23 +0000728 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000729 // early exit if the clip is now empty
730 if (bounds_affects_clip(flags) &&
reed@google.com00177082011-10-12 14:34:30 +0000731 !fMCRec->fRasterClip->op(ir, SkRegion::kIntersect_Op)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000732 return count;
733 }
734
735 bool isOpaque;
736 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
737
bsalomon@google.come97f0852011-06-17 13:10:25 +0000738 SkDevice* device = this->createLayerDevice(config, ir.width(), ir.height(),
739 isOpaque);
bungeman@google.come25c6842011-08-17 14:53:54 +0000740 if (NULL == device) {
741 SkDebugf("Unable to create device for layer.");
742 return count;
743 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000744
reed@google.com6f8f2922011-03-04 22:27:10 +0000745 device->setOrigin(ir.fLeft, ir.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000746 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
747 device->unref();
748
749 layer->fNext = fMCRec->fTopLayer;
750 fMCRec->fLayer = layer;
751 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
752
reed@google.com7c202932011-12-14 18:48:05 +0000753 fLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000754 return count;
755}
756
757int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
758 SaveFlags flags) {
759 if (0xFF == alpha) {
760 return this->saveLayer(bounds, NULL, flags);
761 } else {
762 SkPaint tmpPaint;
763 tmpPaint.setAlpha(alpha);
764 return this->saveLayer(bounds, &tmpPaint, flags);
765 }
766}
767
768void SkCanvas::restore() {
769 // check for underflow
770 if (fMCStack.count() > 1) {
771 this->internalRestore();
772 }
773}
774
775void SkCanvas::internalRestore() {
776 SkASSERT(fMCStack.count() != 0);
777
778 fDeviceCMDirty = true;
779 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000780 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000781
reed@google.com5c3d1472011-02-22 19:12:23 +0000782 fClipStack.restore();
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000783 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000784 DeviceCM* layer = fMCRec->fLayer; // may be null
785 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
786 fMCRec->fLayer = NULL;
787
788 // now do the normal restore()
789 fMCRec->~MCRec(); // balanced in save()
790 fMCStack.pop_back();
791 fMCRec = (MCRec*)fMCStack.back();
792
793 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
794 since if we're being recorded, we don't want to record this (the
795 recorder will have already recorded the restore).
796 */
797 if (NULL != layer) {
798 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000799 const SkIPoint& origin = layer->fDevice->getOrigin();
800 this->drawDevice(layer->fDevice, origin.x(), origin.y(),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000801 layer->fPaint);
802 // reset this, since drawDevice will have set it to true
803 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000804
805 SkASSERT(fLayerCount > 0);
806 fLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000807 }
808 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000809 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000810
811 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000812}
813
814int SkCanvas::getSaveCount() const {
815 return fMCStack.count();
816}
817
818void SkCanvas::restoreToCount(int count) {
819 // sanity check
820 if (count < 1) {
821 count = 1;
822 }
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000823
824 int n = this->getSaveCount() - count;
825 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000826 this->restore();
827 }
828}
829
reed@google.com7c202932011-12-14 18:48:05 +0000830bool SkCanvas::isDrawingToLayer() const {
831 return fLayerCount > 0;
832}
833
reed@android.com8a1c16f2008-12-17 15:59:43 +0000834/////////////////////////////////////////////////////////////////////////////
835
836// can't draw it if its empty, or its too big for a fixed-point width or height
837static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000838 return bitmap.width() <= 0 || bitmap.height() <= 0
839#ifndef SK_ALLOW_OVER_32K_BITMAPS
840 || bitmap.width() > 32767 || bitmap.height() > 32767
841#endif
842 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000843}
844
reed@android.comf2b98d62010-12-20 18:26:13 +0000845void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000846 const SkMatrix& matrix, const SkPaint* paint) {
847 if (reject_bitmap(bitmap)) {
848 return;
849 }
850
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000851 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000852 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000853 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000854 }
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000855 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000856}
857
858void SkCanvas::drawDevice(SkDevice* device, int x, int y,
859 const SkPaint* paint) {
860 SkPaint tmp;
861 if (NULL == paint) {
862 tmp.setDither(true);
863 paint = &tmp;
864 }
reed@google.com4b226022011-01-11 18:32:13 +0000865
reed@google.com4e2b3d32011-04-07 14:18:59 +0000866 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000867 while (iter.next()) {
868 iter.fDevice->drawDevice(iter, device, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +0000869 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000870 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000871 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +0000872}
873
874/////////////////////////////////////////////////////////////////////////////
875
876bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
877 fDeviceCMDirty = true;
878 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000879 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000880 return fMCRec->fMatrix->preTranslate(dx, dy);
881}
882
883bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
884 fDeviceCMDirty = true;
885 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000886 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000887 return fMCRec->fMatrix->preScale(sx, sy);
888}
889
890bool SkCanvas::rotate(SkScalar degrees) {
891 fDeviceCMDirty = true;
892 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000893 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000894 return fMCRec->fMatrix->preRotate(degrees);
895}
896
897bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
898 fDeviceCMDirty = true;
899 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000900 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000901 return fMCRec->fMatrix->preSkew(sx, sy);
902}
903
904bool SkCanvas::concat(const SkMatrix& matrix) {
905 fDeviceCMDirty = true;
906 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000907 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000908 return fMCRec->fMatrix->preConcat(matrix);
909}
910
911void SkCanvas::setMatrix(const SkMatrix& matrix) {
912 fDeviceCMDirty = true;
913 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000914 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000915 *fMCRec->fMatrix = matrix;
916}
917
918// this is not virtual, so it must call a virtual method so that subclasses
919// will see its action
920void SkCanvas::resetMatrix() {
921 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +0000922
reed@android.com8a1c16f2008-12-17 15:59:43 +0000923 matrix.reset();
924 this->setMatrix(matrix);
925}
926
927//////////////////////////////////////////////////////////////////////////////
928
reed@google.comc42d35d2011-10-12 11:57:42 +0000929bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000930 AutoValidateClip avc(this);
931
reed@android.com8a1c16f2008-12-17 15:59:43 +0000932 fDeviceCMDirty = true;
933 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000934 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000935
936 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +0000937 // for these simpler matrices, we can stay a rect ever after applying
938 // the matrix. This means we don't have to a) make a path, and b) tell
939 // the region code to scan-convert the path, only to discover that it
940 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000941 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000942
943 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +0000944 fClipStack.clipDevRect(r, op, doAA);
945 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000946 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +0000947 // since we're rotate or some such thing, we convert the rect to a path
948 // and clip against that, since it can handle any matrix. However, to
949 // avoid recursion in the case where we are subclassed (e.g. Pictures)
950 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000951 SkPath path;
952
953 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +0000954 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000955 }
956}
957
reed@google.com00177082011-10-12 14:34:30 +0000958static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +0000959 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +0000960 // base is used to limit the size (and therefore memory allocation) of the
961 // region that results from scan converting devPath.
962 SkRegion base;
963
reed@google.com819c9212011-02-23 18:56:55 +0000964 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +0000965 // since we are intersect, we can do better (tighter) with currRgn's
966 // bounds, than just using the device. However, if currRgn is complex,
967 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +0000968 if (currClip->isRect()) {
969 return currClip->setPath(devPath, *currClip, doAA);
reed@google.com759876a2011-03-03 14:32:51 +0000970 } else {
reed@google.com00177082011-10-12 14:34:30 +0000971 base.setRect(currClip->getBounds());
972 SkRasterClip clip;
973 clip.setPath(devPath, base, doAA);
974 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +0000975 }
reed@google.com819c9212011-02-23 18:56:55 +0000976 } else {
reed@google.com819c9212011-02-23 18:56:55 +0000977 const SkBitmap& bm = canvas->getDevice()->accessBitmap(false);
978 base.setRect(0, 0, bm.width(), bm.height());
979
980 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +0000981 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +0000982 } else {
reed@google.com00177082011-10-12 14:34:30 +0000983 SkRasterClip clip;
984 clip.setPath(devPath, base, doAA);
985 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +0000986 }
987 }
988}
989
reed@google.comc42d35d2011-10-12 11:57:42 +0000990bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000991 AutoValidateClip avc(this);
992
reed@android.com8a1c16f2008-12-17 15:59:43 +0000993 fDeviceCMDirty = true;
994 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000995 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000996
997 SkPath devPath;
998 path.transform(*fMCRec->fMatrix, &devPath);
999
reed@google.comfe701122011-11-08 19:41:23 +00001000 // Check if the transfomation, or the original path itself
1001 // made us empty. Note this can also happen if we contained NaN
1002 // values. computing the bounds detects this, and will set our
1003 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1004 if (devPath.getBounds().isEmpty()) {
1005 // resetting the path will remove any NaN or other wanky values
1006 // that might upset our scan converter.
1007 devPath.reset();
1008 }
1009
reed@google.com5c3d1472011-02-22 19:12:23 +00001010 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +00001011 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +00001012
reed@google.com00177082011-10-12 14:34:30 +00001013 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001014}
1015
1016bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001017 AutoValidateClip avc(this);
1018
reed@android.com8a1c16f2008-12-17 15:59:43 +00001019 fDeviceCMDirty = true;
1020 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001021 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001022
reed@google.com5c3d1472011-02-22 19:12:23 +00001023 // todo: signal fClipStack that we have a region, and therefore (I guess)
1024 // we have to ignore it, and use the region directly?
1025 fClipStack.clipDevRect(rgn.getBounds());
1026
reed@google.com00177082011-10-12 14:34:30 +00001027 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001028}
1029
reed@google.com819c9212011-02-23 18:56:55 +00001030#ifdef SK_DEBUG
1031void SkCanvas::validateClip() const {
1032 // construct clipRgn from the clipstack
1033 const SkDevice* device = this->getDevice();
1034 SkIRect ir;
1035 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001036 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001037
1038 SkClipStack::B2FIter iter(fClipStack);
1039 const SkClipStack::B2FIter::Clip* clip;
1040 while ((clip = iter.next()) != NULL) {
1041 if (clip->fPath) {
reed@google.com00177082011-10-12 14:34:30 +00001042 clipPathHelper(this, &tmpClip, *clip->fPath, clip->fOp, clip->fDoAA);
reed@google.com819c9212011-02-23 18:56:55 +00001043 } else if (clip->fRect) {
1044 clip->fRect->round(&ir);
reed@google.com00177082011-10-12 14:34:30 +00001045 tmpClip.op(ir, clip->fOp);
reed@google.com819c9212011-02-23 18:56:55 +00001046 } else {
reed@google.com00177082011-10-12 14:34:30 +00001047 tmpClip.setEmpty();
reed@google.com819c9212011-02-23 18:56:55 +00001048 }
1049 }
1050
reed@google.com6f8f2922011-03-04 22:27:10 +00001051#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001052 // now compare against the current rgn
1053 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001054 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001055#endif
reed@google.com819c9212011-02-23 18:56:55 +00001056}
1057#endif
1058
reed@google.com5c3d1472011-02-22 19:12:23 +00001059///////////////////////////////////////////////////////////////////////////////
1060
reed@android.comba09de42010-02-05 20:46:05 +00001061void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001062 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001063 SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
1064 fLocalBoundsCompareTypeBW;
1065
1066 if (!this->getClipBounds(&r, et)) {
1067 rCompare.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001068 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001069 rCompare.set(SkScalarToCompareType(r.fLeft),
1070 SkScalarToCompareType(r.fTop),
1071 SkScalarToCompareType(r.fRight),
1072 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001073 }
1074}
1075
reed@android.comd252db02009-04-01 18:31:44 +00001076/* current impl ignores edgetype, and relies on
1077 getLocalClipBoundsCompareType(), which always returns a value assuming
1078 antialiasing (worst case)
1079 */
reed@android.comba09de42010-02-05 20:46:05 +00001080bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001081
reed@google.com16078632011-12-06 18:56:37 +00001082 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001083 return true;
1084
reed@google.com00177082011-10-12 14:34:30 +00001085 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001086 return true;
1087 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001088
tomhudson@google.com8d430182011-06-06 19:11:19 +00001089 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001090 SkRect dst;
1091 fMCRec->fMatrix->mapRect(&dst, rect);
1092 SkIRect idst;
1093 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001094 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001095 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001096 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
reed@android.comd252db02009-04-01 18:31:44 +00001097
reed@android.coma380ae42009-07-21 01:17:02 +00001098 // for speed, do the most likely reject compares first
1099 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1100 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1101 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1102 return true;
1103 }
1104 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1105 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1106 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1107 return true;
1108 }
1109 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001110 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001111}
1112
1113bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +00001114 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001115}
1116
1117bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
1118 /* current impl ignores edgetype, and relies on
1119 getLocalClipBoundsCompareType(), which always returns a value assuming
1120 antialiasing (worst case)
1121 */
1122
reed@google.com00177082011-10-12 14:34:30 +00001123 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001124 return true;
1125 }
reed@google.com4b226022011-01-11 18:32:13 +00001126
reed@android.comaefd2bc2009-03-30 21:02:14 +00001127 SkScalarCompareType userT = SkScalarToCompareType(top);
1128 SkScalarCompareType userB = SkScalarToCompareType(bottom);
reed@google.com4b226022011-01-11 18:32:13 +00001129
reed@android.com8a1c16f2008-12-17 15:59:43 +00001130 // check for invalid user Y coordinates (i.e. empty)
reed@android.comd252db02009-04-01 18:31:44 +00001131 // reed: why do we need to do this check, since it slows us down?
reed@android.com8a1c16f2008-12-17 15:59:43 +00001132 if (userT >= userB) {
1133 return true;
1134 }
reed@google.com4b226022011-01-11 18:32:13 +00001135
reed@android.com8a1c16f2008-12-17 15:59:43 +00001136 // check if we are above or below the local clip bounds
1137 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
1138 return userT >= clipR.fBottom || userB <= clipR.fTop;
1139}
1140
1141bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001142 SkIRect ibounds;
1143 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001144 return false;
1145 }
1146
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001147 SkMatrix inverse;
1148 // if we can't invert the CTM, we can't return local clip bounds
1149 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001150 if (bounds) {
1151 bounds->setEmpty();
1152 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001153 return false;
1154 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001155
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001156 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001157 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001158 // adjust it outwards if we are antialiasing
1159 int inset = (kAA_EdgeType == et);
1160 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1161 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001162 inverse.mapRect(bounds, r);
1163 }
1164 return true;
1165}
1166
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001167bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001168 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001169 if (clip.isEmpty()) {
1170 if (bounds) {
1171 bounds->setEmpty();
1172 }
1173 return false;
1174 }
1175
1176 if (NULL != bounds) {
1177 *bounds = clip.getBounds();
1178 }
1179 return true;
1180}
1181
reed@android.com8a1c16f2008-12-17 15:59:43 +00001182const SkMatrix& SkCanvas::getTotalMatrix() const {
1183 return *fMCRec->fMatrix;
1184}
1185
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001186SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001187 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1188 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001189 return kComplex_ClipType;
1190}
1191
reed@android.com8a1c16f2008-12-17 15:59:43 +00001192const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001193 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001194}
1195
reed@google.com7d7ca792011-02-23 22:39:18 +00001196const SkClipStack& SkCanvas::getTotalClipStack() const {
1197 return fClipStack;
1198}
1199
reed@android.comf2b98d62010-12-20 18:26:13 +00001200void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1201 if (NULL == matrix || matrix->isIdentity()) {
1202 if (fUseExternalMatrix) {
1203 fDeviceCMDirty = true;
1204 }
1205 fUseExternalMatrix = false;
1206 } else {
1207 fUseExternalMatrix = true;
1208 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.com4b226022011-01-11 18:32:13 +00001209
reed@android.comf2b98d62010-12-20 18:26:13 +00001210 fExternalMatrix = *matrix;
1211 matrix->invert(&fExternalInverse);
1212 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001213}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001214
bsalomon@google.come97f0852011-06-17 13:10:25 +00001215SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
1216 int width, int height,
1217 bool isOpaque) {
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001218 SkDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001219 if (device) {
1220 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1221 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001222 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001223 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001224 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001225}
1226
bsalomon@google.come97f0852011-06-17 13:10:25 +00001227SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
1228 int width, int height,
1229 bool isOpaque) {
1230 SkDevice* device = this->getDevice();
1231 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001232 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001233 } else {
1234 return NULL;
1235 }
1236}
1237
1238
reed@android.com8a1c16f2008-12-17 15:59:43 +00001239//////////////////////////////////////////////////////////////////////////////
1240// These are the virtual drawing methods
1241//////////////////////////////////////////////////////////////////////////////
1242
reed@google.com2a981812011-04-14 18:59:28 +00001243void SkCanvas::clear(SkColor color) {
1244 SkDrawIter iter(this);
1245
1246 while (iter.next()) {
1247 iter.fDevice->clear(color);
1248 }
1249}
1250
reed@android.com8a1c16f2008-12-17 15:59:43 +00001251void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001252 this->internalDrawPaint(paint);
1253}
1254
1255void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001256 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001257
1258 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001259 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001260 }
1261
reed@google.com4e2b3d32011-04-07 14:18:59 +00001262 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001263}
1264
1265void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1266 const SkPaint& paint) {
1267 if ((long)count <= 0) {
1268 return;
1269 }
1270
1271 SkASSERT(pts != NULL);
1272
reed@google.com4e2b3d32011-04-07 14:18:59 +00001273 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001274
reed@android.com8a1c16f2008-12-17 15:59:43 +00001275 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001276 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001277 }
reed@google.com4b226022011-01-11 18:32:13 +00001278
reed@google.com4e2b3d32011-04-07 14:18:59 +00001279 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001280}
1281
1282void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1283 if (paint.canComputeFastBounds()) {
1284 SkRect storage;
1285 if (this->quickReject(paint.computeFastBounds(r, &storage),
1286 paint2EdgeType(&paint))) {
1287 return;
1288 }
1289 }
reed@google.com4b226022011-01-11 18:32:13 +00001290
reed@google.com4e2b3d32011-04-07 14:18:59 +00001291 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001292
1293 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001294 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001295 }
1296
reed@google.com4e2b3d32011-04-07 14:18:59 +00001297 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001298}
1299
1300void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001301 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001302 SkRect storage;
1303 const SkRect& bounds = path.getBounds();
1304 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001305 paint2EdgeType(&paint))) {
1306 return;
1307 }
1308 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001309 if (path.isEmpty()) {
1310 if (path.isInverseFillType()) {
1311 this->internalDrawPaint(paint);
1312 }
1313 return;
1314 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001315
reed@google.com4e2b3d32011-04-07 14:18:59 +00001316 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001317
1318 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001319 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001320 }
1321
reed@google.com4e2b3d32011-04-07 14:18:59 +00001322 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001323}
1324
1325void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1326 const SkPaint* paint) {
1327 SkDEBUGCODE(bitmap.validate();)
1328
reed@google.com3d608122011-11-21 15:16:16 +00001329 if (NULL == paint || paint->canComputeFastBounds()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001330 SkRect fastBounds;
1331 fastBounds.set(x, y,
1332 x + SkIntToScalar(bitmap.width()),
1333 y + SkIntToScalar(bitmap.height()));
1334 if (this->quickReject(fastBounds, paint2EdgeType(paint))) {
1335 return;
1336 }
1337 }
reed@google.com4b226022011-01-11 18:32:13 +00001338
reed@android.com8a1c16f2008-12-17 15:59:43 +00001339 SkMatrix matrix;
1340 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001341 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001342}
1343
reed@google.com9987ec32011-09-07 11:57:52 +00001344// this one is non-virtual, so it can be called safely by other canvas apis
1345void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1346 const SkRect& dst, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001347 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1348 return;
1349 }
reed@google.com9987ec32011-09-07 11:57:52 +00001350
reed@android.com8a1c16f2008-12-17 15:59:43 +00001351 // do this now, to avoid the cost of calling extract for RLE bitmaps
reed@google.com3d608122011-11-21 15:16:16 +00001352 if (NULL == paint || paint->canComputeFastBounds()) {
1353 if (this->quickReject(dst, paint2EdgeType(paint))) {
1354 return;
1355 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001356 }
reed@google.com3d608122011-11-21 15:16:16 +00001357
reed@android.com8a1c16f2008-12-17 15:59:43 +00001358 const SkBitmap* bitmapPtr = &bitmap;
reed@google.com9987ec32011-09-07 11:57:52 +00001359
reed@android.com8a1c16f2008-12-17 15:59:43 +00001360 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001361 SkRect tmpSrc;
1362 if (src) {
1363 tmpSrc.set(*src);
1364 // if the extract process clipped off the top or left of the
1365 // original, we adjust for that here to get the position right.
1366 if (tmpSrc.fLeft > 0) {
1367 tmpSrc.fRight -= tmpSrc.fLeft;
1368 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001369 }
reed@android.com87899992009-10-16 14:48:38 +00001370 if (tmpSrc.fTop > 0) {
1371 tmpSrc.fBottom -= tmpSrc.fTop;
1372 tmpSrc.fTop = 0;
1373 }
1374 } else {
1375 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1376 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001377 }
reed@android.com87899992009-10-16 14:48:38 +00001378 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
reed@google.com9987ec32011-09-07 11:57:52 +00001379
reed@android.comf2b98d62010-12-20 18:26:13 +00001380 // ensure that src is "valid" before we pass it to our internal routines
1381 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1382 SkIRect tmpISrc;
1383 if (src) {
1384 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
reed@google.com2ade0862011-03-17 17:48:04 +00001385 if (!tmpISrc.intersect(*src)) {
1386 return;
1387 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001388 src = &tmpISrc;
1389 }
1390 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001391}
1392
reed@google.com9987ec32011-09-07 11:57:52 +00001393void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1394 const SkRect& dst, const SkPaint* paint) {
1395 SkDEBUGCODE(bitmap.validate();)
1396 this->internalDrawBitmapRect(bitmap, src, dst, paint);
1397}
1398
reed@android.com8a1c16f2008-12-17 15:59:43 +00001399void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1400 const SkPaint* paint) {
1401 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001402 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001403}
1404
reed@android.comf2b98d62010-12-20 18:26:13 +00001405void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1406 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001407 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001408
reed@google.com4e2b3d32011-04-07 14:18:59 +00001409 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001410
reed@android.com8a1c16f2008-12-17 15:59:43 +00001411 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001412 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001413 }
reed@android.com9b039062009-02-11 15:09:58 +00001414
reed@google.com4e2b3d32011-04-07 14:18:59 +00001415 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001416}
1417
reed@google.com9987ec32011-09-07 11:57:52 +00001418void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1419 const SkIRect& center, const SkRect& dst,
1420 const SkPaint* paint) {
reed@google.com3d608122011-11-21 15:16:16 +00001421 if (NULL == paint || paint->canComputeFastBounds()) {
1422 if (this->quickReject(dst, paint2EdgeType(paint))) {
1423 return;
1424 }
1425 }
1426
reed@google.com9987ec32011-09-07 11:57:52 +00001427 const int32_t w = bitmap.width();
1428 const int32_t h = bitmap.height();
1429
1430 SkIRect c = center;
1431 // pin center to the bounds of the bitmap
1432 c.fLeft = SkMax32(0, center.fLeft);
1433 c.fTop = SkMax32(0, center.fTop);
1434 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1435 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1436
1437 const int32_t srcX[4] = { 0, c.fLeft, c.fRight, w };
1438 const int32_t srcY[4] = { 0, c.fTop, c.fBottom, h };
1439 SkScalar dstX[4] = {
1440 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1441 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1442 };
1443 SkScalar dstY[4] = {
1444 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1445 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1446 };
1447
1448 if (dstX[1] > dstX[2]) {
1449 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1450 dstX[2] = dstX[1];
1451 }
1452
1453 if (dstY[1] > dstY[2]) {
1454 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1455 dstY[2] = dstY[1];
1456 }
1457
1458 SkIRect s;
1459 SkRect d;
1460 for (int y = 0; y < 3; y++) {
1461 s.fTop = srcY[y];
1462 s.fBottom = srcY[y+1];
1463 d.fTop = dstY[y];
1464 d.fBottom = dstY[y+1];
1465 for (int x = 0; x < 3; x++) {
1466 s.fLeft = srcX[x];
1467 s.fRight = srcX[x+1];
1468 d.fLeft = dstX[x];
1469 d.fRight = dstX[x+1];
1470 this->internalDrawBitmapRect(bitmap, &s, d, paint);
1471 }
1472 }
1473}
1474
1475void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1476 const SkRect& dst, const SkPaint* paint) {
1477 SkDEBUGCODE(bitmap.validate();)
1478
1479 // Need a device entry-point, so gpu can use a mesh
1480 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1481}
1482
reed@android.com8a1c16f2008-12-17 15:59:43 +00001483void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1484 const SkPaint* paint) {
1485 SkDEBUGCODE(bitmap.validate();)
reed@google.com4b226022011-01-11 18:32:13 +00001486
reed@android.com8a1c16f2008-12-17 15:59:43 +00001487 if (reject_bitmap(bitmap)) {
1488 return;
1489 }
reed@google.com4b226022011-01-11 18:32:13 +00001490
reed@android.com8a1c16f2008-12-17 15:59:43 +00001491 SkPaint tmp;
1492 if (NULL == paint) {
1493 paint = &tmp;
1494 }
reed@google.com4b226022011-01-11 18:32:13 +00001495
reed@google.com4e2b3d32011-04-07 14:18:59 +00001496 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001497
reed@android.com8a1c16f2008-12-17 15:59:43 +00001498 while (iter.next()) {
1499 iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +00001500 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001501 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001502 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001503}
1504
reed@google.comf67e4cf2011-03-15 20:56:58 +00001505class SkDeviceFilteredPaint {
1506public:
1507 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1508 SkDevice::TextFlags flags;
1509 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001510 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001511 newPaint->setFlags(flags.fFlags);
1512 newPaint->setHinting(flags.fHinting);
1513 fPaint = newPaint;
1514 } else {
1515 fPaint = &paint;
1516 }
1517 }
1518
reed@google.comf67e4cf2011-03-15 20:56:58 +00001519 const SkPaint& paint() const { return *fPaint; }
1520
1521private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001522 const SkPaint* fPaint;
1523 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001524};
1525
bungeman@google.com52c748b2011-08-22 21:30:43 +00001526void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1527 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001528 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001529 draw.fDevice->drawRect(draw, r, paint);
1530 } else {
1531 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001532 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001533 draw.fDevice->drawRect(draw, r, p);
1534 }
1535}
1536
1537void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1538 const char text[], size_t byteLength,
1539 SkScalar x, SkScalar y) {
1540 SkASSERT(byteLength == 0 || text != NULL);
1541
1542 // nothing to draw
1543 if (text == NULL || byteLength == 0 ||
1544 draw.fClip->isEmpty() ||
1545 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1546 return;
1547 }
1548
1549 SkScalar width = 0;
1550 SkPoint start;
1551
1552 start.set(0, 0); // to avoid warning
1553 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1554 SkPaint::kStrikeThruText_Flag)) {
1555 width = paint.measureText(text, byteLength);
1556
1557 SkScalar offsetX = 0;
1558 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1559 offsetX = SkScalarHalf(width);
1560 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1561 offsetX = width;
1562 }
1563 start.set(x - offsetX, y);
1564 }
1565
1566 if (0 == width) {
1567 return;
1568 }
1569
1570 uint32_t flags = paint.getFlags();
1571
1572 if (flags & (SkPaint::kUnderlineText_Flag |
1573 SkPaint::kStrikeThruText_Flag)) {
1574 SkScalar textSize = paint.getTextSize();
1575 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1576 SkRect r;
1577
1578 r.fLeft = start.fX;
1579 r.fRight = start.fX + width;
1580
1581 if (flags & SkPaint::kUnderlineText_Flag) {
1582 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1583 start.fY);
1584 r.fTop = offset;
1585 r.fBottom = offset + height;
1586 DrawRect(draw, paint, r, textSize);
1587 }
1588 if (flags & SkPaint::kStrikeThruText_Flag) {
1589 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1590 start.fY);
1591 r.fTop = offset;
1592 r.fBottom = offset + height;
1593 DrawRect(draw, paint, r, textSize);
1594 }
1595 }
1596}
1597
reed@android.com8a1c16f2008-12-17 15:59:43 +00001598void SkCanvas::drawText(const void* text, size_t byteLength,
1599 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001600 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001601
1602 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001603 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001604 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001605 DrawTextDecorations(iter, dfp.paint(),
1606 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001607 }
1608
reed@google.com4e2b3d32011-04-07 14:18:59 +00001609 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001610}
1611
1612void SkCanvas::drawPosText(const void* text, size_t byteLength,
1613 const SkPoint pos[], const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001614 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001615
reed@android.com8a1c16f2008-12-17 15:59:43 +00001616 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001617 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001618 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001619 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001620 }
reed@google.com4b226022011-01-11 18:32:13 +00001621
reed@google.com4e2b3d32011-04-07 14:18:59 +00001622 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001623}
1624
1625void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1626 const SkScalar xpos[], SkScalar constY,
1627 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001628 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001629
reed@android.com8a1c16f2008-12-17 15:59:43 +00001630 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001631 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001632 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001633 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001634 }
reed@google.com4b226022011-01-11 18:32:13 +00001635
reed@google.com4e2b3d32011-04-07 14:18:59 +00001636 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001637}
1638
1639void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1640 const SkPath& path, const SkMatrix* matrix,
1641 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001642 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001643
1644 while (iter.next()) {
1645 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001646 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001647 }
1648
reed@google.com4e2b3d32011-04-07 14:18:59 +00001649 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001650}
1651
djsollen@google.com56c69772011-11-08 19:00:26 +00001652#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001653void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1654 const SkPoint pos[], const SkPaint& paint,
1655 const SkPath& path, const SkMatrix* matrix) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001656 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001657
1658 while (iter.next()) {
1659 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001660 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001661 }
1662
reed@google.com4e2b3d32011-04-07 14:18:59 +00001663 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001664}
1665#endif
1666
reed@android.com8a1c16f2008-12-17 15:59:43 +00001667void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1668 const SkPoint verts[], const SkPoint texs[],
1669 const SkColor colors[], SkXfermode* xmode,
1670 const uint16_t indices[], int indexCount,
1671 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001672 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001673
reed@android.com8a1c16f2008-12-17 15:59:43 +00001674 while (iter.next()) {
1675 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001676 colors, xmode, indices, indexCount,
1677 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001678 }
reed@google.com4b226022011-01-11 18:32:13 +00001679
reed@google.com4e2b3d32011-04-07 14:18:59 +00001680 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001681}
1682
reed@android.comcb608442009-12-04 21:32:27 +00001683void SkCanvas::drawData(const void* data, size_t length) {
1684 // do nothing. Subclasses may do something with the data
1685}
1686
reed@android.com8a1c16f2008-12-17 15:59:43 +00001687//////////////////////////////////////////////////////////////////////////////
1688// These methods are NOT virtual, and therefore must call back into virtual
1689// methods, rather than actually drawing themselves.
1690//////////////////////////////////////////////////////////////////////////////
1691
1692void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001693 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001694 SkPaint paint;
1695
1696 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001697 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001698 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001699 }
1700 this->drawPaint(paint);
1701}
1702
reed@android.com845fdac2009-06-23 03:01:32 +00001703void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001704 SkPaint paint;
1705
1706 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001707 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001708 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001709 }
1710 this->drawPaint(paint);
1711}
1712
1713void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1714 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001715
reed@android.com8a1c16f2008-12-17 15:59:43 +00001716 pt.set(x, y);
1717 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1718}
1719
1720void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1721 SkPoint pt;
1722 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001723
reed@android.com8a1c16f2008-12-17 15:59:43 +00001724 pt.set(x, y);
1725 paint.setColor(color);
1726 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1727}
1728
1729void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1730 const SkPaint& paint) {
1731 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001732
reed@android.com8a1c16f2008-12-17 15:59:43 +00001733 pts[0].set(x0, y0);
1734 pts[1].set(x1, y1);
1735 this->drawPoints(kLines_PointMode, 2, pts, paint);
1736}
1737
1738void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1739 SkScalar right, SkScalar bottom,
1740 const SkPaint& paint) {
1741 SkRect r;
1742
1743 r.set(left, top, right, bottom);
1744 this->drawRect(r, paint);
1745}
1746
1747void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1748 const SkPaint& paint) {
1749 if (radius < 0) {
1750 radius = 0;
1751 }
1752
1753 SkRect r;
1754 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001755
reed@android.com8a1c16f2008-12-17 15:59:43 +00001756 if (paint.canComputeFastBounds()) {
1757 SkRect storage;
1758 if (this->quickReject(paint.computeFastBounds(r, &storage),
1759 paint2EdgeType(&paint))) {
1760 return;
1761 }
1762 }
reed@google.com4b226022011-01-11 18:32:13 +00001763
reed@android.com8a1c16f2008-12-17 15:59:43 +00001764 SkPath path;
1765 path.addOval(r);
1766 this->drawPath(path, paint);
1767}
1768
1769void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1770 const SkPaint& paint) {
1771 if (rx > 0 && ry > 0) {
1772 if (paint.canComputeFastBounds()) {
1773 SkRect storage;
1774 if (this->quickReject(paint.computeFastBounds(r, &storage),
1775 paint2EdgeType(&paint))) {
1776 return;
1777 }
1778 }
1779
1780 SkPath path;
1781 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1782 this->drawPath(path, paint);
1783 } else {
1784 this->drawRect(r, paint);
1785 }
1786}
1787
1788void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1789 if (paint.canComputeFastBounds()) {
1790 SkRect storage;
1791 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1792 paint2EdgeType(&paint))) {
1793 return;
1794 }
1795 }
1796
1797 SkPath path;
1798 path.addOval(oval);
1799 this->drawPath(path, paint);
1800}
1801
1802void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1803 SkScalar sweepAngle, bool useCenter,
1804 const SkPaint& paint) {
1805 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1806 this->drawOval(oval, paint);
1807 } else {
1808 SkPath path;
1809 if (useCenter) {
1810 path.moveTo(oval.centerX(), oval.centerY());
1811 }
1812 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1813 if (useCenter) {
1814 path.close();
1815 }
1816 this->drawPath(path, paint);
1817 }
1818}
1819
1820void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1821 const SkPath& path, SkScalar hOffset,
1822 SkScalar vOffset, const SkPaint& paint) {
1823 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001824
reed@android.com8a1c16f2008-12-17 15:59:43 +00001825 matrix.setTranslate(hOffset, vOffset);
1826 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1827}
1828
reed@android.comf76bacf2009-05-13 14:00:33 +00001829///////////////////////////////////////////////////////////////////////////////
1830
reed@android.com8a1c16f2008-12-17 15:59:43 +00001831void SkCanvas::drawPicture(SkPicture& picture) {
1832 int saveCount = save();
1833 picture.draw(this);
1834 restoreToCount(saveCount);
1835}
1836
reed@android.com8a1c16f2008-12-17 15:59:43 +00001837///////////////////////////////////////////////////////////////////////////////
1838///////////////////////////////////////////////////////////////////////////////
1839
1840SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00001841 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001842
1843 SkASSERT(canvas);
1844
1845 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
1846 fDone = !fImpl->next();
1847}
1848
1849SkCanvas::LayerIter::~LayerIter() {
1850 fImpl->~SkDrawIter();
1851}
1852
1853void SkCanvas::LayerIter::next() {
1854 fDone = !fImpl->next();
1855}
1856
1857SkDevice* SkCanvas::LayerIter::device() const {
1858 return fImpl->getDevice();
1859}
1860
1861const SkMatrix& SkCanvas::LayerIter::matrix() const {
1862 return fImpl->getMatrix();
1863}
1864
1865const SkPaint& SkCanvas::LayerIter::paint() const {
1866 const SkPaint* paint = fImpl->getPaint();
1867 if (NULL == paint) {
1868 paint = &fDefaultPaint;
1869 }
1870 return *paint;
1871}
1872
1873const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
1874int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
1875int SkCanvas::LayerIter::y() const { return fImpl->getY(); }