blob: 3ee29ce67e7a341095388dec9f975a1a6a6ca7ee [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"
17#include "SkScalarCompare.h"
18#include "SkTemplates.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000019#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000020#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000021#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000022
23//#define SK_TRACE_SAVERESTORE
24
25#ifdef SK_TRACE_SAVERESTORE
26 static int gLayerCounter;
27 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
28 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
29
30 static int gRecCounter;
31 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
32 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
33
34 static int gCanvasCounter;
35 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
36 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
37#else
38 #define inc_layer()
39 #define dec_layer()
40 #define inc_rec()
41 #define dec_rec()
42 #define inc_canvas()
43 #define dec_canvas()
44#endif
45
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000046typedef SkTLazy<SkPaint> SkLazyPaint;
47
reed@android.com8a1c16f2008-12-17 15:59:43 +000048///////////////////////////////////////////////////////////////////////////////
49// Helpers for computing fast bounds for quickReject tests
50
51static SkCanvas::EdgeType paint2EdgeType(const SkPaint* paint) {
52 return paint != NULL && paint->isAntiAlias() ?
53 SkCanvas::kAA_EdgeType : SkCanvas::kBW_EdgeType;
54}
55
56///////////////////////////////////////////////////////////////////////////////
57
58/* This is the record we keep for each SkDevice that the user installs.
59 The clip/matrix/proc are fields that reflect the top of the save/restore
60 stack. Whenever the canvas changes, it marks a dirty flag, and then before
61 these are used (assuming we're not on a layer) we rebuild these cache
62 values: they reflect the top of the save stack, but translated and clipped
63 by the device's XY offset and bitmap-bounds.
64*/
65struct DeviceCM {
66 DeviceCM* fNext;
67 SkDevice* fDevice;
68 SkRegion fClip;
69 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +000070 SkPaint* fPaint; // may be null (in the future)
reed@android.comf2b98d62010-12-20 18:26:13 +000071 // optional, related to canvas' external matrix
72 const SkMatrix* fMVMatrix;
73 const SkMatrix* fExtMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +000074
bungeman@google.com88edf1e2011-08-08 19:41:56 +000075 DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint)
reed@android.com8a1c16f2008-12-17 15:59:43 +000076 : fNext(NULL) {
77 if (NULL != device) {
78 device->ref();
79 device->lockPixels();
80 }
reed@google.com4b226022011-01-11 18:32:13 +000081 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +000082 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +000083 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000084
bungeman@google.com88edf1e2011-08-08 19:41:56 +000085 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000086 if (NULL != fDevice) {
87 fDevice->unlockPixels();
88 fDevice->unref();
89 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +000090 SkDELETE(fPaint);
91 }
reed@google.com4b226022011-01-11 18:32:13 +000092
reed@android.com8a1c16f2008-12-17 15:59:43 +000093 void updateMC(const SkMatrix& totalMatrix, const SkRegion& totalClip,
reed@google.com46799cd2011-02-22 20:56:26 +000094 const SkClipStack& clipStack, SkRegion* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +000095 int x = fDevice->getOrigin().x();
96 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +000097 int width = fDevice->width();
98 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +000099
reed@android.com8a1c16f2008-12-17 15:59:43 +0000100 if ((x | y) == 0) {
101 fMatrix = &totalMatrix;
102 fClip = totalClip;
103 } else {
104 fMatrixStorage = totalMatrix;
105 fMatrixStorage.postTranslate(SkIntToScalar(-x),
106 SkIntToScalar(-y));
107 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000108
reed@android.com8a1c16f2008-12-17 15:59:43 +0000109 totalClip.translate(-x, -y, &fClip);
110 }
111
112 fClip.op(0, 0, width, height, SkRegion::kIntersect_Op);
113
114 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000115
reed@android.com8a1c16f2008-12-17 15:59:43 +0000116 if (updateClip) {
117 updateClip->op(x, y, x + width, y + height,
118 SkRegion::kDifference_Op);
119 }
reed@google.com4b226022011-01-11 18:32:13 +0000120
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000121 fDevice->setMatrixClip(*fMatrix, fClip, clipStack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000122
123#ifdef SK_DEBUG
124 if (!fClip.isEmpty()) {
125 SkIRect deviceR;
126 deviceR.set(0, 0, width, height);
127 SkASSERT(deviceR.contains(fClip.getBounds()));
128 }
129#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000130 // default is to assume no external matrix
131 fMVMatrix = NULL;
132 fExtMatrix = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000133 }
reed@android.comf2b98d62010-12-20 18:26:13 +0000134
135 // can only be called after calling updateMC()
136 void updateExternalMatrix(const SkMatrix& extM, const SkMatrix& extI) {
137 fMVMatrixStorage.setConcat(extI, *fMatrix);
138 fMVMatrix = &fMVMatrixStorage;
139 fExtMatrix = &extM; // assumes extM has long life-time (owned by canvas)
140 }
141
reed@android.com8a1c16f2008-12-17 15:59:43 +0000142private:
reed@android.comf2b98d62010-12-20 18:26:13 +0000143 SkMatrix fMatrixStorage, fMVMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000144};
145
146/* This is the record we keep for each save/restore level in the stack.
147 Since a level optionally copies the matrix and/or stack, we have pointers
148 for these fields. If the value is copied for this level, the copy is
149 stored in the ...Storage field, and the pointer points to that. If the
150 value is not copied for this level, we ignore ...Storage, and just point
151 at the corresponding value in the previous level in the stack.
152*/
153class SkCanvas::MCRec {
154public:
155 MCRec* fNext;
156 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
157 SkRegion* fRegion; // points to either fRegionStorage or prev MCRec
158 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000159
reed@android.com8a1c16f2008-12-17 15:59:43 +0000160 DeviceCM* fLayer;
161 /* If there are any layers in the stack, this points to the top-most
162 one that is at or below this level in the stack (so we know what
163 bitmap/device to draw into from this level. This value is NOT
164 reference counted, since the real owner is either our fLayer field,
165 or a previous one in a lower level.)
166 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000167 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000168
169 MCRec(const MCRec* prev, int flags) {
170 if (NULL != prev) {
171 if (flags & SkCanvas::kMatrix_SaveFlag) {
172 fMatrixStorage = *prev->fMatrix;
173 fMatrix = &fMatrixStorage;
174 } else {
175 fMatrix = prev->fMatrix;
176 }
reed@google.com4b226022011-01-11 18:32:13 +0000177
reed@android.com8a1c16f2008-12-17 15:59:43 +0000178 if (flags & SkCanvas::kClip_SaveFlag) {
179 fRegionStorage = *prev->fRegion;
180 fRegion = &fRegionStorage;
181 } else {
182 fRegion = prev->fRegion;
183 }
184
185 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000186 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000187
188 fTopLayer = prev->fTopLayer;
189 } else { // no prev
190 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000191
reed@android.com8a1c16f2008-12-17 15:59:43 +0000192 fMatrix = &fMatrixStorage;
193 fRegion = &fRegionStorage;
194 fFilter = NULL;
195 fTopLayer = NULL;
196 }
197 fLayer = NULL;
198
199 // don't bother initializing fNext
200 inc_rec();
201 }
202 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000203 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204 SkDELETE(fLayer);
205 dec_rec();
206 }
reed@google.com4b226022011-01-11 18:32:13 +0000207
reed@android.com8a1c16f2008-12-17 15:59:43 +0000208private:
209 SkMatrix fMatrixStorage;
210 SkRegion fRegionStorage;
211};
212
213class SkDrawIter : public SkDraw {
214public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000215 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
216 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217 canvas->updateDeviceCMCache();
218
reed@google.com7d7ca792011-02-23 22:39:18 +0000219 fClipStack = &canvas->getTotalClipStack();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000220 fBounder = canvas->getBounder();
221 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000222 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000223 }
reed@google.com4b226022011-01-11 18:32:13 +0000224
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225 bool next() {
226 // skip over recs with empty clips
227 if (fSkipEmptyClips) {
228 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
229 fCurrLayer = fCurrLayer->fNext;
230 }
231 }
232
233 if (NULL != fCurrLayer) {
234 const DeviceCM* rec = fCurrLayer;
235
236 fMatrix = rec->fMatrix;
237 fClip = &rec->fClip;
238 fDevice = rec->fDevice;
239 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000241 fMVMatrix = rec->fMVMatrix;
242 fExtMatrix = rec->fExtMatrix;
243 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244
245 fCurrLayer = rec->fNext;
246 if (fBounder) {
247 fBounder->setClip(fClip);
248 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000249 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000250
bsalomon@google.comd302f142011-03-03 13:54:13 +0000251 fCanvas->prepareForDeviceDraw(fDevice, *fMatrix, *fClip, *fClipStack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252 return true;
253 }
254 return false;
255 }
reed@google.com4b226022011-01-11 18:32:13 +0000256
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257 SkDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000258 int getX() const { return fDevice->getOrigin().x(); }
259 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260 const SkMatrix& getMatrix() const { return *fMatrix; }
261 const SkRegion& getClip() const { return *fClip; }
262 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000263
reed@android.com8a1c16f2008-12-17 15:59:43 +0000264private:
265 SkCanvas* fCanvas;
266 const DeviceCM* fCurrLayer;
267 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000268 SkBool8 fSkipEmptyClips;
269
270 typedef SkDraw INHERITED;
271};
272
273/////////////////////////////////////////////////////////////////////////////
274
275class AutoDrawLooper {
276public:
reed@google.com4e2b3d32011-04-07 14:18:59 +0000277 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint) : fOrigPaint(paint) {
278 fCanvas = canvas;
279 fLooper = paint.getLooper();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000280 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000281 fPaint = NULL;
282 fSaveCount = canvas->getSaveCount();
283 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284
reed@google.com4e2b3d32011-04-07 14:18:59 +0000285 if (fLooper) {
286 fLooper->init(canvas);
287 }
288 }
289
reed@android.com8a1c16f2008-12-17 15:59:43 +0000290 ~AutoDrawLooper() {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000291 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000292 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000293
294 const SkPaint& paint() const {
295 SkASSERT(fPaint);
296 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000298
299 bool next(SkDrawFilter::Type drawType);
300
reed@android.com8a1c16f2008-12-17 15:59:43 +0000301private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000302 SkLazyPaint fLazyPaint;
303 SkCanvas* fCanvas;
304 const SkPaint& fOrigPaint;
305 SkDrawLooper* fLooper;
306 SkDrawFilter* fFilter;
307 const SkPaint* fPaint;
308 int fSaveCount;
309 bool fDone;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000310};
311
reed@google.com4e2b3d32011-04-07 14:18:59 +0000312bool AutoDrawLooper::next(SkDrawFilter::Type drawType) {
313 if (fDone) {
314 fPaint = NULL;
315 return false;
316 }
317 if (!fLooper && !fFilter) {
318 fDone = true;
319 fPaint = &fOrigPaint;
320 return true;
321 }
322
323 SkPaint* paint = fLazyPaint.set(fOrigPaint);
324 if (fLooper && !fLooper->next(fCanvas, paint)) {
325 fDone = true;
326 fPaint = NULL;
327 return false;
328 }
329 if (fFilter) {
330 fFilter->filter(paint, drawType);
mike@reedtribe.org53e3bed2011-04-08 00:37:03 +0000331 if (NULL == fLooper) {
332 // no looper means we only draw once
333 fDone = true;
334 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000335 }
336 fPaint = paint;
337 return true;
338}
339
reed@android.com8a1c16f2008-12-17 15:59:43 +0000340/* Stack helper for managing a SkBounder. In the destructor, if we were
341 given a bounder, we call its commit() method, signifying that we are
342 done accumulating bounds for that draw.
343*/
344class SkAutoBounderCommit {
345public:
346 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
347 ~SkAutoBounderCommit() {
348 if (NULL != fBounder) {
349 fBounder->commit();
350 }
351 }
352private:
353 SkBounder* fBounder;
354};
355
356#include "SkColorPriv.h"
357
358class AutoValidator {
359public:
360 AutoValidator(SkDevice* device) : fDevice(device) {}
361 ~AutoValidator() {
362#ifdef SK_DEBUG
363 const SkBitmap& bm = fDevice->accessBitmap(false);
364 if (bm.config() == SkBitmap::kARGB_4444_Config) {
365 for (int y = 0; y < bm.height(); y++) {
366 const SkPMColor16* p = bm.getAddr16(0, y);
367 for (int x = 0; x < bm.width(); x++) {
368 SkPMColor16 c = p[x];
369 SkPMColor16Assert(c);
370 }
371 }
372 }
373#endif
374 }
375private:
376 SkDevice* fDevice;
377};
378
379////////// macros to place around the internal draw calls //////////////////
380
reed@google.com4e2b3d32011-04-07 14:18:59 +0000381#define LOOPER_BEGIN(paint, type) \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000382/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000383 AutoDrawLooper looper(this, paint); \
384 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000385 SkAutoBounderCommit ac(fBounder); \
386 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000387
reed@google.com4e2b3d32011-04-07 14:18:59 +0000388#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000389
390////////////////////////////////////////////////////////////////////////////
391
392SkDevice* SkCanvas::init(SkDevice* device) {
393 fBounder = NULL;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000394 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000395 fLocalBoundsCompareTypeDirty = true;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000396 fLocalBoundsCompareTypeBW.setEmpty();
reed@android.comba09de42010-02-05 20:46:05 +0000397 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com199f1082009-06-10 02:12:47 +0000398 fLastDeviceToGainFocus = NULL;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000399 fDeviceCMDirty = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000400
401 fMCRec = (MCRec*)fMCStack.push_back();
402 new (fMCRec) MCRec(NULL, 0);
403
404 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL));
405 fMCRec->fTopLayer = fMCRec->fLayer;
406 fMCRec->fNext = NULL;
407
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000408 fExternalMatrix.reset();
409 fExternalInverse.reset();
reed@android.comf2b98d62010-12-20 18:26:13 +0000410 fUseExternalMatrix = false;
411
reed@android.com8a1c16f2008-12-17 15:59:43 +0000412 return this->setDevice(device);
413}
414
reed@google.comcde92112011-07-06 20:00:52 +0000415SkCanvas::SkCanvas()
416: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000417 inc_canvas();
reed@google.comcde92112011-07-06 20:00:52 +0000418
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000419 this->init(NULL);
420}
421
reed@android.com8a1c16f2008-12-17 15:59:43 +0000422SkCanvas::SkCanvas(SkDevice* device)
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000423 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000424 inc_canvas();
425
426 this->init(device);
427}
428
429SkCanvas::SkCanvas(const SkBitmap& bitmap)
430 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
431 inc_canvas();
432
reed@google.comcde92112011-07-06 20:00:52 +0000433 this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000434}
435
436SkCanvas::~SkCanvas() {
437 // free up the contents of our deque
438 this->restoreToCount(1); // restore everything but the last
439 this->internalRestore(); // restore the last, since we're going away
440
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000441 SkSafeUnref(fBounder);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000442
reed@android.com8a1c16f2008-12-17 15:59:43 +0000443 dec_canvas();
444}
445
446SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
447 SkRefCnt_SafeAssign(fBounder, bounder);
448 return bounder;
449}
450
451SkDrawFilter* SkCanvas::getDrawFilter() const {
452 return fMCRec->fFilter;
453}
454
455SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
456 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
457 return filter;
458}
459
460///////////////////////////////////////////////////////////////////////////////
461
462SkDevice* SkCanvas::getDevice() const {
463 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000464 SkDeque::F2BIter iter(fMCStack);
465 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000466 SkASSERT(rec && rec->fLayer);
467 return rec->fLayer->fDevice;
468}
469
reed@google.com9266fed2011-03-30 00:18:03 +0000470SkDevice* SkCanvas::getTopDevice() const {
471 return fMCRec->fTopLayer->fDevice;
472}
473
reed@android.com8a1c16f2008-12-17 15:59:43 +0000474SkDevice* SkCanvas::setDevice(SkDevice* device) {
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 SkDevice* rootDevice = rec->fLayer->fDevice;
480
481 if (rootDevice == device) {
482 return device;
483 }
reed@google.com4b226022011-01-11 18:32:13 +0000484
reed@android.com8a1c16f2008-12-17 15:59:43 +0000485 /* Notify the devices that they are going in/out of scope, so they can do
486 things like lock/unlock their pixels, etc.
487 */
488 if (device) {
489 device->lockPixels();
490 }
491 if (rootDevice) {
492 rootDevice->unlockPixels();
493 }
494
495 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
496 rootDevice = device;
497
498 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000499
reed@android.com8a1c16f2008-12-17 15:59:43 +0000500 /* Now we update our initial region to have the bounds of the new device,
501 and then intersect all of the clips in our stack with these bounds,
502 to ensure that we can't draw outside of the device's bounds (and trash
503 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000504
reed@android.com8a1c16f2008-12-17 15:59:43 +0000505 NOTE: this is only a partial-fix, since if the new device is larger than
506 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000507 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000508 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
509 reconstruct the correct clips, so this approximation will have to do.
510 The caller really needs to restore() back to the base if they want to
511 accurately take advantage of the new device bounds.
512 */
513
514 if (NULL == device) {
515 rec->fRegion->setEmpty();
516 while ((rec = (MCRec*)iter.next()) != NULL) {
517 (void)rec->fRegion->setEmpty();
518 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000519 fClipStack.reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000520 } else {
521 // compute our total bounds for all devices
522 SkIRect bounds;
reed@google.com4b226022011-01-11 18:32:13 +0000523
reed@android.com8a1c16f2008-12-17 15:59:43 +0000524 bounds.set(0, 0, device->width(), device->height());
525
526 // now jam our 1st clip to be bounds, and intersect the rest with that
527 rec->fRegion->setRect(bounds);
528 while ((rec = (MCRec*)iter.next()) != NULL) {
529 (void)rec->fRegion->op(bounds, SkRegion::kIntersect_Op);
530 }
531 }
532 return device;
533}
534
reed@google.comaf951c92011-06-16 19:10:39 +0000535SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) {
536 SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (bitmap)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000537 device->unref();
538 return device;
539}
540
reed@google.com51df9e32010-12-23 19:29:18 +0000541bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
542 SkDevice* device = this->getDevice();
543 if (!device) {
544 return false;
545 }
546 return device->readPixels(srcRect, bitmap);
547}
548
reed@google.com4b226022011-01-11 18:32:13 +0000549//////////////////////////////////////////////////////////////////////////////
550
reed@google.com51df9e32010-12-23 19:29:18 +0000551bool SkCanvas::readPixels(SkBitmap* bitmap) {
552 SkDevice* device = this->getDevice();
553 if (!device) {
554 return false;
555 }
556 SkIRect bounds;
557 bounds.set(0, 0, device->width(), device->height());
558 return this->readPixels(bounds, bitmap);
559}
560
561void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
562 SkDevice* device = this->getDevice();
563 if (device) {
564 device->writePixels(bitmap, x, y);
565 }
566}
567
reed@android.com8a1c16f2008-12-17 15:59:43 +0000568//////////////////////////////////////////////////////////////////////////////
569
reed@android.com8a1c16f2008-12-17 15:59:43 +0000570void SkCanvas::updateDeviceCMCache() {
571 if (fDeviceCMDirty) {
572 const SkMatrix& totalMatrix = this->getTotalMatrix();
573 const SkRegion& totalClip = this->getTotalClip();
574 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000575
reed@android.com8a1c16f2008-12-17 15:59:43 +0000576 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000577 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000578 if (fUseExternalMatrix) {
579 layer->updateExternalMatrix(fExternalMatrix,
580 fExternalInverse);
581 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000582 } else {
583 SkRegion clip;
584 clip = totalClip; // make a copy
585 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000586 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.comf2b98d62010-12-20 18:26:13 +0000587 if (fUseExternalMatrix) {
588 layer->updateExternalMatrix(fExternalMatrix,
589 fExternalInverse);
590 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000591 } while ((layer = layer->fNext) != NULL);
592 }
593 fDeviceCMDirty = false;
594 }
595}
596
reed@android.comf2b98d62010-12-20 18:26:13 +0000597void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000598 const SkRegion& clip,
599 const SkClipStack& clipStack) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000600 SkASSERT(device);
reed@android.com199f1082009-06-10 02:12:47 +0000601 if (fLastDeviceToGainFocus != device) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000602 device->gainFocus(this, matrix, clip, clipStack);
reed@android.com199f1082009-06-10 02:12:47 +0000603 fLastDeviceToGainFocus = device;
604 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605}
606
607///////////////////////////////////////////////////////////////////////////////
608
609int SkCanvas::internalSave(SaveFlags flags) {
610 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000611
reed@android.com8a1c16f2008-12-17 15:59:43 +0000612 MCRec* newTop = (MCRec*)fMCStack.push_back();
613 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000614
reed@android.com8a1c16f2008-12-17 15:59:43 +0000615 newTop->fNext = fMCRec;
616 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000617
reed@google.com5c3d1472011-02-22 19:12:23 +0000618 fClipStack.save();
619 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
620
reed@android.com8a1c16f2008-12-17 15:59:43 +0000621 return saveCount;
622}
623
624int SkCanvas::save(SaveFlags flags) {
625 // call shared impl
626 return this->internalSave(flags);
627}
628
629#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
630#define C16MASK (1 << SkBitmap::kRGB_565_Config)
631#define C8MASK (1 << SkBitmap::kA8_Config)
632
633static SkBitmap::Config resolve_config(SkCanvas* canvas,
634 const SkIRect& bounds,
635 SkCanvas::SaveFlags flags,
636 bool* isOpaque) {
637 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
638
639#if 0
640 // loop through and union all the configs we may draw into
641 uint32_t configMask = 0;
642 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
643 {
644 SkDevice* device = canvas->getLayerDevice(i);
645 if (device->intersects(bounds))
646 configMask |= 1 << device->config();
647 }
648
649 // if the caller wants alpha or fullcolor, we can't return 565
650 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
651 SkCanvas::kHasAlphaLayer_SaveFlag))
652 configMask &= ~C16MASK;
653
654 switch (configMask) {
655 case C8MASK: // if we only have A8, return that
656 return SkBitmap::kA8_Config;
657
658 case C16MASK: // if we only have 565, return that
659 return SkBitmap::kRGB_565_Config;
660
661 default:
662 return SkBitmap::kARGB_8888_Config; // default answer
663 }
664#else
665 return SkBitmap::kARGB_8888_Config; // default answer
666#endif
667}
668
669static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
670 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
671}
672
673int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
674 SaveFlags flags) {
675 // do this before we create the layer. We don't call the public save() since
676 // that would invoke a possibly overridden virtual
677 int count = this->internalSave(flags);
678
679 fDeviceCMDirty = true;
680
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000681 SkIRect clipBounds;
682 if (!this->getClipDeviceBounds(&clipBounds)) {
reed@android.comf2b98d62010-12-20 18:26:13 +0000683 return count;
684 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000685
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000686 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000687 if (NULL != bounds) {
688 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000689
reed@android.com8a1c16f2008-12-17 15:59:43 +0000690 this->getTotalMatrix().mapRect(&r, *bounds);
691 r.roundOut(&ir);
692 // early exit if the layer's bounds are clipped out
693 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000694 if (bounds_affects_clip(flags)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000695 fMCRec->fRegion->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000696 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000697 return count;
698 }
699 } else { // no user bounds, so just use the clip
700 ir = clipBounds;
701 }
702
reed@google.com5c3d1472011-02-22 19:12:23 +0000703 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000704 // early exit if the clip is now empty
705 if (bounds_affects_clip(flags) &&
706 !fMCRec->fRegion->op(ir, SkRegion::kIntersect_Op)) {
707 return count;
708 }
709
710 bool isOpaque;
711 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
712
bsalomon@google.come97f0852011-06-17 13:10:25 +0000713 SkDevice* device = this->createLayerDevice(config, ir.width(), ir.height(),
714 isOpaque);
bungeman@google.come25c6842011-08-17 14:53:54 +0000715 if (NULL == device) {
716 SkDebugf("Unable to create device for layer.");
717 return count;
718 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000719
reed@google.com6f8f2922011-03-04 22:27:10 +0000720 device->setOrigin(ir.fLeft, ir.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000721 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
722 device->unref();
723
724 layer->fNext = fMCRec->fTopLayer;
725 fMCRec->fLayer = layer;
726 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
727
728 return count;
729}
730
731int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
732 SaveFlags flags) {
733 if (0xFF == alpha) {
734 return this->saveLayer(bounds, NULL, flags);
735 } else {
736 SkPaint tmpPaint;
737 tmpPaint.setAlpha(alpha);
738 return this->saveLayer(bounds, &tmpPaint, flags);
739 }
740}
741
742void SkCanvas::restore() {
743 // check for underflow
744 if (fMCStack.count() > 1) {
745 this->internalRestore();
746 }
747}
748
749void SkCanvas::internalRestore() {
750 SkASSERT(fMCStack.count() != 0);
751
752 fDeviceCMDirty = true;
753 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000754 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000755
reed@google.com5c3d1472011-02-22 19:12:23 +0000756 fClipStack.restore();
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000757 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000758 DeviceCM* layer = fMCRec->fLayer; // may be null
759 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
760 fMCRec->fLayer = NULL;
761
762 // now do the normal restore()
763 fMCRec->~MCRec(); // balanced in save()
764 fMCStack.pop_back();
765 fMCRec = (MCRec*)fMCStack.back();
766
767 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
768 since if we're being recorded, we don't want to record this (the
769 recorder will have already recorded the restore).
770 */
771 if (NULL != layer) {
772 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000773 const SkIPoint& origin = layer->fDevice->getOrigin();
774 this->drawDevice(layer->fDevice, origin.x(), origin.y(),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000775 layer->fPaint);
776 // reset this, since drawDevice will have set it to true
777 fDeviceCMDirty = true;
778 }
779 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000780 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000781
782 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000783}
784
785int SkCanvas::getSaveCount() const {
786 return fMCStack.count();
787}
788
789void SkCanvas::restoreToCount(int count) {
790 // sanity check
791 if (count < 1) {
792 count = 1;
793 }
794 while (fMCStack.count() > count) {
795 this->restore();
796 }
797}
798
799/////////////////////////////////////////////////////////////////////////////
800
801// can't draw it if its empty, or its too big for a fixed-point width or height
802static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000803 return bitmap.width() <= 0 || bitmap.height() <= 0
804#ifndef SK_ALLOW_OVER_32K_BITMAPS
805 || bitmap.width() > 32767 || bitmap.height() > 32767
806#endif
807 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000808}
809
reed@android.comf2b98d62010-12-20 18:26:13 +0000810void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000811 const SkMatrix& matrix, const SkPaint* paint) {
812 if (reject_bitmap(bitmap)) {
813 return;
814 }
815
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000816 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000817 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000818 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000819 }
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000820 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000821}
822
823void SkCanvas::drawDevice(SkDevice* device, int x, int y,
824 const SkPaint* paint) {
825 SkPaint tmp;
826 if (NULL == paint) {
827 tmp.setDither(true);
828 paint = &tmp;
829 }
reed@google.com4b226022011-01-11 18:32:13 +0000830
reed@google.com4e2b3d32011-04-07 14:18:59 +0000831 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000832 while (iter.next()) {
833 iter.fDevice->drawDevice(iter, device, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +0000834 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000835 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000836 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +0000837}
838
839/////////////////////////////////////////////////////////////////////////////
840
841bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
842 fDeviceCMDirty = true;
843 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000844 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000845 return fMCRec->fMatrix->preTranslate(dx, dy);
846}
847
848bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
849 fDeviceCMDirty = true;
850 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000851 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000852 return fMCRec->fMatrix->preScale(sx, sy);
853}
854
855bool SkCanvas::rotate(SkScalar degrees) {
856 fDeviceCMDirty = true;
857 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000858 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000859 return fMCRec->fMatrix->preRotate(degrees);
860}
861
862bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
863 fDeviceCMDirty = true;
864 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000865 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000866 return fMCRec->fMatrix->preSkew(sx, sy);
867}
868
869bool SkCanvas::concat(const SkMatrix& matrix) {
870 fDeviceCMDirty = true;
871 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000872 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000873 return fMCRec->fMatrix->preConcat(matrix);
874}
875
876void SkCanvas::setMatrix(const SkMatrix& matrix) {
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 *fMCRec->fMatrix = matrix;
881}
882
883// this is not virtual, so it must call a virtual method so that subclasses
884// will see its action
885void SkCanvas::resetMatrix() {
886 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +0000887
reed@android.com8a1c16f2008-12-17 15:59:43 +0000888 matrix.reset();
889 this->setMatrix(matrix);
890}
891
892//////////////////////////////////////////////////////////////////////////////
893
894bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000895 AutoValidateClip avc(this);
896
reed@android.com8a1c16f2008-12-17 15:59:43 +0000897 fDeviceCMDirty = true;
898 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000899 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000900
901 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +0000902 // for these simpler matrices, we can stay a rect ever after applying
903 // the matrix. This means we don't have to a) make a path, and b) tell
904 // the region code to scan-convert the path, only to discover that it
905 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000906 SkRect r;
907 SkIRect ir;
908
909 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com5c3d1472011-02-22 19:12:23 +0000910 fClipStack.clipDevRect(r, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000911 r.round(&ir);
912 return fMCRec->fRegion->op(ir, op);
913 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +0000914 // since we're rotate or some such thing, we convert the rect to a path
915 // and clip against that, since it can handle any matrix. However, to
916 // avoid recursion in the case where we are subclassed (e.g. Pictures)
917 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000918 SkPath path;
919
920 path.addRect(rect);
reed@android.com98de2bd2009-03-02 19:41:36 +0000921 return this->SkCanvas::clipPath(path, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000922 }
923}
924
reed@google.com819c9212011-02-23 18:56:55 +0000925static bool clipPathHelper(const SkCanvas* canvas, SkRegion* currRgn,
926 const SkPath& devPath, SkRegion::Op op) {
reed@google.com759876a2011-03-03 14:32:51 +0000927 // base is used to limit the size (and therefore memory allocation) of the
928 // region that results from scan converting devPath.
929 SkRegion base;
930
reed@google.com819c9212011-02-23 18:56:55 +0000931 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +0000932 // since we are intersect, we can do better (tighter) with currRgn's
933 // bounds, than just using the device. However, if currRgn is complex,
934 // our region blitter may hork, so we do that case in two steps.
935 if (currRgn->isRect()) {
936 return currRgn->setPath(devPath, *currRgn);
937 } else {
938 base.setRect(currRgn->getBounds());
939 SkRegion rgn;
940 rgn.setPath(devPath, base);
941 return currRgn->op(rgn, op);
942 }
reed@google.com819c9212011-02-23 18:56:55 +0000943 } else {
reed@google.com819c9212011-02-23 18:56:55 +0000944 const SkBitmap& bm = canvas->getDevice()->accessBitmap(false);
945 base.setRect(0, 0, bm.width(), bm.height());
946
947 if (SkRegion::kReplace_Op == op) {
948 return currRgn->setPath(devPath, base);
949 } else {
950 SkRegion rgn;
951 rgn.setPath(devPath, base);
952 return currRgn->op(rgn, op);
953 }
954 }
955}
956
reed@android.com8a1c16f2008-12-17 15:59:43 +0000957bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000958 AutoValidateClip avc(this);
959
reed@android.com8a1c16f2008-12-17 15:59:43 +0000960 fDeviceCMDirty = true;
961 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000962 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000963
964 SkPath devPath;
965 path.transform(*fMCRec->fMatrix, &devPath);
966
reed@google.com5c3d1472011-02-22 19:12:23 +0000967 // if we called path.swap() we could avoid a deep copy of this path
968 fClipStack.clipDevPath(devPath, op);
969
reed@google.com819c9212011-02-23 18:56:55 +0000970 return clipPathHelper(this, fMCRec->fRegion, devPath, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000971}
972
973bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000974 AutoValidateClip avc(this);
975
reed@android.com8a1c16f2008-12-17 15:59:43 +0000976 fDeviceCMDirty = true;
977 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000978 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000979
reed@google.com5c3d1472011-02-22 19:12:23 +0000980 // todo: signal fClipStack that we have a region, and therefore (I guess)
981 // we have to ignore it, and use the region directly?
982 fClipStack.clipDevRect(rgn.getBounds());
983
reed@android.com8a1c16f2008-12-17 15:59:43 +0000984 return fMCRec->fRegion->op(rgn, op);
985}
986
reed@google.com819c9212011-02-23 18:56:55 +0000987#ifdef SK_DEBUG
988void SkCanvas::validateClip() const {
989 // construct clipRgn from the clipstack
990 const SkDevice* device = this->getDevice();
991 SkIRect ir;
992 ir.set(0, 0, device->width(), device->height());
993 SkRegion clipRgn(ir);
994
995 SkClipStack::B2FIter iter(fClipStack);
996 const SkClipStack::B2FIter::Clip* clip;
997 while ((clip = iter.next()) != NULL) {
998 if (clip->fPath) {
999 clipPathHelper(this, &clipRgn, *clip->fPath, clip->fOp);
1000 } else if (clip->fRect) {
1001 clip->fRect->round(&ir);
1002 clipRgn.op(ir, clip->fOp);
1003 } else {
reed@google.comdca7acb2011-02-23 21:47:37 +00001004 clipRgn.setEmpty();
reed@google.com819c9212011-02-23 18:56:55 +00001005 }
1006 }
1007
reed@google.com6f8f2922011-03-04 22:27:10 +00001008#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001009 // now compare against the current rgn
1010 const SkRegion& rgn = this->getTotalClip();
1011 SkASSERT(rgn == clipRgn);
reed@google.com02878b82011-02-24 13:04:42 +00001012#endif
reed@google.com819c9212011-02-23 18:56:55 +00001013}
1014#endif
1015
reed@google.com5c3d1472011-02-22 19:12:23 +00001016///////////////////////////////////////////////////////////////////////////////
1017
reed@android.comba09de42010-02-05 20:46:05 +00001018void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001019 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001020 SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
1021 fLocalBoundsCompareTypeBW;
1022
1023 if (!this->getClipBounds(&r, et)) {
1024 rCompare.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001025 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001026 rCompare.set(SkScalarToCompareType(r.fLeft),
1027 SkScalarToCompareType(r.fTop),
1028 SkScalarToCompareType(r.fRight),
1029 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001030 }
1031}
1032
reed@android.comd252db02009-04-01 18:31:44 +00001033/* current impl ignores edgetype, and relies on
1034 getLocalClipBoundsCompareType(), which always returns a value assuming
1035 antialiasing (worst case)
1036 */
reed@android.comba09de42010-02-05 20:46:05 +00001037bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001038
1039 if (!rect.hasValidCoordinates())
1040 return true;
1041
reed@android.com8a1c16f2008-12-17 15:59:43 +00001042 if (fMCRec->fRegion->isEmpty()) {
1043 return true;
1044 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001045
tomhudson@google.com8d430182011-06-06 19:11:19 +00001046 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001047 SkRect dst;
1048 fMCRec->fMatrix->mapRect(&dst, rect);
1049 SkIRect idst;
1050 dst.roundOut(&idst);
1051 return !SkIRect::Intersects(idst, fMCRec->fRegion->getBounds());
1052 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001053 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
reed@android.comd252db02009-04-01 18:31:44 +00001054
reed@android.coma380ae42009-07-21 01:17:02 +00001055 // for speed, do the most likely reject compares first
1056 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1057 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1058 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1059 return true;
1060 }
1061 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1062 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1063 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1064 return true;
1065 }
1066 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001067 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001068}
1069
1070bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +00001071 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001072}
1073
1074bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
1075 /* current impl ignores edgetype, and relies on
1076 getLocalClipBoundsCompareType(), which always returns a value assuming
1077 antialiasing (worst case)
1078 */
1079
1080 if (fMCRec->fRegion->isEmpty()) {
1081 return true;
1082 }
reed@google.com4b226022011-01-11 18:32:13 +00001083
reed@android.comaefd2bc2009-03-30 21:02:14 +00001084 SkScalarCompareType userT = SkScalarToCompareType(top);
1085 SkScalarCompareType userB = SkScalarToCompareType(bottom);
reed@google.com4b226022011-01-11 18:32:13 +00001086
reed@android.com8a1c16f2008-12-17 15:59:43 +00001087 // check for invalid user Y coordinates (i.e. empty)
reed@android.comd252db02009-04-01 18:31:44 +00001088 // reed: why do we need to do this check, since it slows us down?
reed@android.com8a1c16f2008-12-17 15:59:43 +00001089 if (userT >= userB) {
1090 return true;
1091 }
reed@google.com4b226022011-01-11 18:32:13 +00001092
reed@android.com8a1c16f2008-12-17 15:59:43 +00001093 // check if we are above or below the local clip bounds
1094 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
1095 return userT >= clipR.fBottom || userB <= clipR.fTop;
1096}
1097
1098bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001099 SkIRect ibounds;
1100 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001101 return false;
1102 }
1103
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001104 SkMatrix inverse;
1105 // if we can't invert the CTM, we can't return local clip bounds
1106 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001107 if (bounds) {
1108 bounds->setEmpty();
1109 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001110 return false;
1111 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001112
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001113 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001114 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001115 // adjust it outwards if we are antialiasing
1116 int inset = (kAA_EdgeType == et);
1117 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1118 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001119 inverse.mapRect(bounds, r);
1120 }
1121 return true;
1122}
1123
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001124bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
1125 const SkRegion& clip = *fMCRec->fRegion;
1126 if (clip.isEmpty()) {
1127 if (bounds) {
1128 bounds->setEmpty();
1129 }
1130 return false;
1131 }
1132
1133 if (NULL != bounds) {
1134 *bounds = clip.getBounds();
1135 }
1136 return true;
1137}
1138
reed@android.com8a1c16f2008-12-17 15:59:43 +00001139const SkMatrix& SkCanvas::getTotalMatrix() const {
1140 return *fMCRec->fMatrix;
1141}
1142
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001143SkCanvas::ClipType SkCanvas::getClipType() const {
1144 if (fMCRec->fRegion->isEmpty()) return kEmpty_ClipType;
1145 if (fMCRec->fRegion->isRect()) return kRect_ClipType;
1146 return kComplex_ClipType;
1147}
1148
reed@android.com8a1c16f2008-12-17 15:59:43 +00001149const SkRegion& SkCanvas::getTotalClip() const {
1150 return *fMCRec->fRegion;
1151}
1152
reed@google.com7d7ca792011-02-23 22:39:18 +00001153const SkClipStack& SkCanvas::getTotalClipStack() const {
1154 return fClipStack;
1155}
1156
reed@android.comf2b98d62010-12-20 18:26:13 +00001157void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1158 if (NULL == matrix || matrix->isIdentity()) {
1159 if (fUseExternalMatrix) {
1160 fDeviceCMDirty = true;
1161 }
1162 fUseExternalMatrix = false;
1163 } else {
1164 fUseExternalMatrix = true;
1165 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.com4b226022011-01-11 18:32:13 +00001166
reed@android.comf2b98d62010-12-20 18:26:13 +00001167 fExternalMatrix = *matrix;
1168 matrix->invert(&fExternalInverse);
1169 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001170}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001171
bsalomon@google.come97f0852011-06-17 13:10:25 +00001172SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
1173 int width, int height,
1174 bool isOpaque) {
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001175 SkDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001176 if (device) {
1177 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1178 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001179 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001180 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001181 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001182}
1183
bsalomon@google.come97f0852011-06-17 13:10:25 +00001184SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
1185 int width, int height,
1186 bool isOpaque) {
1187 SkDevice* device = this->getDevice();
1188 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001189 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001190 } else {
1191 return NULL;
1192 }
1193}
1194
1195
reed@android.com8a1c16f2008-12-17 15:59:43 +00001196//////////////////////////////////////////////////////////////////////////////
1197// These are the virtual drawing methods
1198//////////////////////////////////////////////////////////////////////////////
1199
reed@google.com2a981812011-04-14 18:59:28 +00001200void SkCanvas::clear(SkColor color) {
1201 SkDrawIter iter(this);
1202
1203 while (iter.next()) {
1204 iter.fDevice->clear(color);
1205 }
1206}
1207
reed@android.com8a1c16f2008-12-17 15:59:43 +00001208void SkCanvas::drawPaint(const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001209 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001210
1211 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001212 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001213 }
1214
reed@google.com4e2b3d32011-04-07 14:18:59 +00001215 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001216}
1217
1218void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1219 const SkPaint& paint) {
1220 if ((long)count <= 0) {
1221 return;
1222 }
1223
1224 SkASSERT(pts != NULL);
1225
reed@google.com4e2b3d32011-04-07 14:18:59 +00001226 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001227
reed@android.com8a1c16f2008-12-17 15:59:43 +00001228 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001229 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001230 }
reed@google.com4b226022011-01-11 18:32:13 +00001231
reed@google.com4e2b3d32011-04-07 14:18:59 +00001232 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001233}
1234
1235void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1236 if (paint.canComputeFastBounds()) {
1237 SkRect storage;
1238 if (this->quickReject(paint.computeFastBounds(r, &storage),
1239 paint2EdgeType(&paint))) {
1240 return;
1241 }
1242 }
reed@google.com4b226022011-01-11 18:32:13 +00001243
reed@google.com4e2b3d32011-04-07 14:18:59 +00001244 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001245
1246 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001247 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001248 }
1249
reed@google.com4e2b3d32011-04-07 14:18:59 +00001250 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001251}
1252
1253void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1254 if (paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001255 SkRect storage;
1256 const SkRect& bounds = path.getBounds();
1257 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001258 paint2EdgeType(&paint))) {
1259 return;
1260 }
1261 }
1262
reed@google.com4e2b3d32011-04-07 14:18:59 +00001263 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001264
1265 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001266 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001267 }
1268
reed@google.com4e2b3d32011-04-07 14:18:59 +00001269 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001270}
1271
1272void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1273 const SkPaint* paint) {
1274 SkDEBUGCODE(bitmap.validate();)
1275
1276 if (NULL == paint || (paint->getMaskFilter() == NULL)) {
1277 SkRect fastBounds;
1278 fastBounds.set(x, y,
1279 x + SkIntToScalar(bitmap.width()),
1280 y + SkIntToScalar(bitmap.height()));
1281 if (this->quickReject(fastBounds, paint2EdgeType(paint))) {
1282 return;
1283 }
1284 }
reed@google.com4b226022011-01-11 18:32:13 +00001285
reed@android.com8a1c16f2008-12-17 15:59:43 +00001286 SkMatrix matrix;
1287 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001288 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001289}
1290
reed@google.com9987ec32011-09-07 11:57:52 +00001291// this one is non-virtual, so it can be called safely by other canvas apis
1292void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1293 const SkRect& dst, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001294 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1295 return;
1296 }
reed@google.com9987ec32011-09-07 11:57:52 +00001297
reed@android.com8a1c16f2008-12-17 15:59:43 +00001298 // do this now, to avoid the cost of calling extract for RLE bitmaps
1299 if (this->quickReject(dst, paint2EdgeType(paint))) {
1300 return;
1301 }
reed@google.com9987ec32011-09-07 11:57:52 +00001302
reed@android.com8a1c16f2008-12-17 15:59:43 +00001303 const SkBitmap* bitmapPtr = &bitmap;
reed@google.com9987ec32011-09-07 11:57:52 +00001304
reed@android.com8a1c16f2008-12-17 15:59:43 +00001305 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001306 SkRect tmpSrc;
1307 if (src) {
1308 tmpSrc.set(*src);
1309 // if the extract process clipped off the top or left of the
1310 // original, we adjust for that here to get the position right.
1311 if (tmpSrc.fLeft > 0) {
1312 tmpSrc.fRight -= tmpSrc.fLeft;
1313 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001314 }
reed@android.com87899992009-10-16 14:48:38 +00001315 if (tmpSrc.fTop > 0) {
1316 tmpSrc.fBottom -= tmpSrc.fTop;
1317 tmpSrc.fTop = 0;
1318 }
1319 } else {
1320 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1321 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001322 }
reed@android.com87899992009-10-16 14:48:38 +00001323 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
reed@google.com9987ec32011-09-07 11:57:52 +00001324
reed@android.comf2b98d62010-12-20 18:26:13 +00001325 // ensure that src is "valid" before we pass it to our internal routines
1326 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1327 SkIRect tmpISrc;
1328 if (src) {
1329 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
reed@google.com2ade0862011-03-17 17:48:04 +00001330 if (!tmpISrc.intersect(*src)) {
1331 return;
1332 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001333 src = &tmpISrc;
1334 }
1335 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001336}
1337
reed@google.com9987ec32011-09-07 11:57:52 +00001338void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1339 const SkRect& dst, const SkPaint* paint) {
1340 SkDEBUGCODE(bitmap.validate();)
1341 this->internalDrawBitmapRect(bitmap, src, dst, paint);
1342}
1343
reed@android.com8a1c16f2008-12-17 15:59:43 +00001344void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1345 const SkPaint* paint) {
1346 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001347 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001348}
1349
reed@android.comf2b98d62010-12-20 18:26:13 +00001350void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1351 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001352 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001353
reed@google.com4e2b3d32011-04-07 14:18:59 +00001354 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001355
reed@android.com8a1c16f2008-12-17 15:59:43 +00001356 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001357 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001358 }
reed@android.com9b039062009-02-11 15:09:58 +00001359
reed@google.com4e2b3d32011-04-07 14:18:59 +00001360 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001361}
1362
reed@google.com9987ec32011-09-07 11:57:52 +00001363void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1364 const SkIRect& center, const SkRect& dst,
1365 const SkPaint* paint) {
1366 const int32_t w = bitmap.width();
1367 const int32_t h = bitmap.height();
1368
1369 SkIRect c = center;
1370 // pin center to the bounds of the bitmap
1371 c.fLeft = SkMax32(0, center.fLeft);
1372 c.fTop = SkMax32(0, center.fTop);
1373 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1374 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1375
1376 const int32_t srcX[4] = { 0, c.fLeft, c.fRight, w };
1377 const int32_t srcY[4] = { 0, c.fTop, c.fBottom, h };
1378 SkScalar dstX[4] = {
1379 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1380 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1381 };
1382 SkScalar dstY[4] = {
1383 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1384 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1385 };
1386
1387 if (dstX[1] > dstX[2]) {
1388 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1389 dstX[2] = dstX[1];
1390 }
1391
1392 if (dstY[1] > dstY[2]) {
1393 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1394 dstY[2] = dstY[1];
1395 }
1396
1397 SkIRect s;
1398 SkRect d;
1399 for (int y = 0; y < 3; y++) {
1400 s.fTop = srcY[y];
1401 s.fBottom = srcY[y+1];
1402 d.fTop = dstY[y];
1403 d.fBottom = dstY[y+1];
1404 for (int x = 0; x < 3; x++) {
1405 s.fLeft = srcX[x];
1406 s.fRight = srcX[x+1];
1407 d.fLeft = dstX[x];
1408 d.fRight = dstX[x+1];
1409 this->internalDrawBitmapRect(bitmap, &s, d, paint);
1410 }
1411 }
1412}
1413
1414void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1415 const SkRect& dst, const SkPaint* paint) {
1416 SkDEBUGCODE(bitmap.validate();)
1417
1418 // Need a device entry-point, so gpu can use a mesh
1419 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1420}
1421
reed@android.com8a1c16f2008-12-17 15:59:43 +00001422void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1423 const SkPaint* paint) {
1424 SkDEBUGCODE(bitmap.validate();)
reed@google.com4b226022011-01-11 18:32:13 +00001425
reed@android.com8a1c16f2008-12-17 15:59:43 +00001426 if (reject_bitmap(bitmap)) {
1427 return;
1428 }
reed@google.com4b226022011-01-11 18:32:13 +00001429
reed@android.com8a1c16f2008-12-17 15:59:43 +00001430 SkPaint tmp;
1431 if (NULL == paint) {
1432 paint = &tmp;
1433 }
reed@google.com4b226022011-01-11 18:32:13 +00001434
reed@google.com4e2b3d32011-04-07 14:18:59 +00001435 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001436
reed@android.com8a1c16f2008-12-17 15:59:43 +00001437 while (iter.next()) {
1438 iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +00001439 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001440 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001441 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001442}
1443
reed@google.comf67e4cf2011-03-15 20:56:58 +00001444class SkDeviceFilteredPaint {
1445public:
1446 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1447 SkDevice::TextFlags flags;
1448 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001449 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001450 newPaint->setFlags(flags.fFlags);
1451 newPaint->setHinting(flags.fHinting);
1452 fPaint = newPaint;
1453 } else {
1454 fPaint = &paint;
1455 }
1456 }
1457
reed@google.comf67e4cf2011-03-15 20:56:58 +00001458 const SkPaint& paint() const { return *fPaint; }
1459
1460private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001461 const SkPaint* fPaint;
1462 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001463};
1464
bungeman@google.com52c748b2011-08-22 21:30:43 +00001465void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1466 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001467 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001468 draw.fDevice->drawRect(draw, r, paint);
1469 } else {
1470 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001471 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001472 draw.fDevice->drawRect(draw, r, p);
1473 }
1474}
1475
1476void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1477 const char text[], size_t byteLength,
1478 SkScalar x, SkScalar y) {
1479 SkASSERT(byteLength == 0 || text != NULL);
1480
1481 // nothing to draw
1482 if (text == NULL || byteLength == 0 ||
1483 draw.fClip->isEmpty() ||
1484 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1485 return;
1486 }
1487
1488 SkScalar width = 0;
1489 SkPoint start;
1490
1491 start.set(0, 0); // to avoid warning
1492 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1493 SkPaint::kStrikeThruText_Flag)) {
1494 width = paint.measureText(text, byteLength);
1495
1496 SkScalar offsetX = 0;
1497 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1498 offsetX = SkScalarHalf(width);
1499 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1500 offsetX = width;
1501 }
1502 start.set(x - offsetX, y);
1503 }
1504
1505 if (0 == width) {
1506 return;
1507 }
1508
1509 uint32_t flags = paint.getFlags();
1510
1511 if (flags & (SkPaint::kUnderlineText_Flag |
1512 SkPaint::kStrikeThruText_Flag)) {
1513 SkScalar textSize = paint.getTextSize();
1514 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1515 SkRect r;
1516
1517 r.fLeft = start.fX;
1518 r.fRight = start.fX + width;
1519
1520 if (flags & SkPaint::kUnderlineText_Flag) {
1521 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1522 start.fY);
1523 r.fTop = offset;
1524 r.fBottom = offset + height;
1525 DrawRect(draw, paint, r, textSize);
1526 }
1527 if (flags & SkPaint::kStrikeThruText_Flag) {
1528 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1529 start.fY);
1530 r.fTop = offset;
1531 r.fBottom = offset + height;
1532 DrawRect(draw, paint, r, textSize);
1533 }
1534 }
1535}
1536
reed@android.com8a1c16f2008-12-17 15:59:43 +00001537void SkCanvas::drawText(const void* text, size_t byteLength,
1538 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001539 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001540
1541 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001542 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001543 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001544 DrawTextDecorations(iter, dfp.paint(),
1545 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001546 }
1547
reed@google.com4e2b3d32011-04-07 14:18:59 +00001548 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001549}
1550
1551void SkCanvas::drawPosText(const void* text, size_t byteLength,
1552 const SkPoint pos[], const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001553 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001554
reed@android.com8a1c16f2008-12-17 15:59:43 +00001555 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001556 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001557 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001558 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001559 }
reed@google.com4b226022011-01-11 18:32:13 +00001560
reed@google.com4e2b3d32011-04-07 14:18:59 +00001561 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001562}
1563
1564void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1565 const SkScalar xpos[], SkScalar constY,
1566 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001567 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001568
reed@android.com8a1c16f2008-12-17 15:59:43 +00001569 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001570 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001571 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001572 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001573 }
reed@google.com4b226022011-01-11 18:32:13 +00001574
reed@google.com4e2b3d32011-04-07 14:18:59 +00001575 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001576}
1577
1578void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1579 const SkPath& path, const SkMatrix* matrix,
1580 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001581 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001582
1583 while (iter.next()) {
1584 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001585 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001586 }
1587
reed@google.com4e2b3d32011-04-07 14:18:59 +00001588 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001589}
1590
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001591#ifdef ANDROID
1592void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1593 const SkPoint pos[], const SkPaint& paint,
1594 const SkPath& path, const SkMatrix* matrix) {
1595
reed@google.com4e2b3d32011-04-07 14:18:59 +00001596 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001597
1598 while (iter.next()) {
1599 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001600 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001601 }
1602
reed@google.com4e2b3d32011-04-07 14:18:59 +00001603 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001604}
1605#endif
1606
reed@android.com8a1c16f2008-12-17 15:59:43 +00001607void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1608 const SkPoint verts[], const SkPoint texs[],
1609 const SkColor colors[], SkXfermode* xmode,
1610 const uint16_t indices[], int indexCount,
1611 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001612 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001613
reed@android.com8a1c16f2008-12-17 15:59:43 +00001614 while (iter.next()) {
1615 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001616 colors, xmode, indices, indexCount,
1617 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001618 }
reed@google.com4b226022011-01-11 18:32:13 +00001619
reed@google.com4e2b3d32011-04-07 14:18:59 +00001620 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001621}
1622
reed@android.comcb608442009-12-04 21:32:27 +00001623void SkCanvas::drawData(const void* data, size_t length) {
1624 // do nothing. Subclasses may do something with the data
1625}
1626
reed@android.com8a1c16f2008-12-17 15:59:43 +00001627//////////////////////////////////////////////////////////////////////////////
1628// These methods are NOT virtual, and therefore must call back into virtual
1629// methods, rather than actually drawing themselves.
1630//////////////////////////////////////////////////////////////////////////////
1631
1632void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001633 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001634 SkPaint paint;
1635
1636 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001637 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001638 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001639 }
1640 this->drawPaint(paint);
1641}
1642
reed@android.com845fdac2009-06-23 03:01:32 +00001643void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001644 SkPaint paint;
1645
1646 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001647 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001648 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001649 }
1650 this->drawPaint(paint);
1651}
1652
1653void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1654 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001655
reed@android.com8a1c16f2008-12-17 15:59:43 +00001656 pt.set(x, y);
1657 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1658}
1659
1660void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1661 SkPoint pt;
1662 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001663
reed@android.com8a1c16f2008-12-17 15:59:43 +00001664 pt.set(x, y);
1665 paint.setColor(color);
1666 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1667}
1668
1669void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1670 const SkPaint& paint) {
1671 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001672
reed@android.com8a1c16f2008-12-17 15:59:43 +00001673 pts[0].set(x0, y0);
1674 pts[1].set(x1, y1);
1675 this->drawPoints(kLines_PointMode, 2, pts, paint);
1676}
1677
1678void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1679 SkScalar right, SkScalar bottom,
1680 const SkPaint& paint) {
1681 SkRect r;
1682
1683 r.set(left, top, right, bottom);
1684 this->drawRect(r, paint);
1685}
1686
1687void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1688 const SkPaint& paint) {
1689 if (radius < 0) {
1690 radius = 0;
1691 }
1692
1693 SkRect r;
1694 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001695
reed@android.com8a1c16f2008-12-17 15:59:43 +00001696 if (paint.canComputeFastBounds()) {
1697 SkRect storage;
1698 if (this->quickReject(paint.computeFastBounds(r, &storage),
1699 paint2EdgeType(&paint))) {
1700 return;
1701 }
1702 }
reed@google.com4b226022011-01-11 18:32:13 +00001703
reed@android.com8a1c16f2008-12-17 15:59:43 +00001704 SkPath path;
1705 path.addOval(r);
1706 this->drawPath(path, paint);
1707}
1708
1709void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1710 const SkPaint& paint) {
1711 if (rx > 0 && ry > 0) {
1712 if (paint.canComputeFastBounds()) {
1713 SkRect storage;
1714 if (this->quickReject(paint.computeFastBounds(r, &storage),
1715 paint2EdgeType(&paint))) {
1716 return;
1717 }
1718 }
1719
1720 SkPath path;
1721 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1722 this->drawPath(path, paint);
1723 } else {
1724 this->drawRect(r, paint);
1725 }
1726}
1727
1728void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1729 if (paint.canComputeFastBounds()) {
1730 SkRect storage;
1731 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1732 paint2EdgeType(&paint))) {
1733 return;
1734 }
1735 }
1736
1737 SkPath path;
1738 path.addOval(oval);
1739 this->drawPath(path, paint);
1740}
1741
1742void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1743 SkScalar sweepAngle, bool useCenter,
1744 const SkPaint& paint) {
1745 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1746 this->drawOval(oval, paint);
1747 } else {
1748 SkPath path;
1749 if (useCenter) {
1750 path.moveTo(oval.centerX(), oval.centerY());
1751 }
1752 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1753 if (useCenter) {
1754 path.close();
1755 }
1756 this->drawPath(path, paint);
1757 }
1758}
1759
1760void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1761 const SkPath& path, SkScalar hOffset,
1762 SkScalar vOffset, const SkPaint& paint) {
1763 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001764
reed@android.com8a1c16f2008-12-17 15:59:43 +00001765 matrix.setTranslate(hOffset, vOffset);
1766 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1767}
1768
reed@android.comf76bacf2009-05-13 14:00:33 +00001769///////////////////////////////////////////////////////////////////////////////
1770
reed@android.com8a1c16f2008-12-17 15:59:43 +00001771void SkCanvas::drawPicture(SkPicture& picture) {
1772 int saveCount = save();
1773 picture.draw(this);
1774 restoreToCount(saveCount);
1775}
1776
reed@android.com8a1c16f2008-12-17 15:59:43 +00001777///////////////////////////////////////////////////////////////////////////////
1778///////////////////////////////////////////////////////////////////////////////
1779
1780SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00001781 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001782
1783 SkASSERT(canvas);
1784
1785 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
1786 fDone = !fImpl->next();
1787}
1788
1789SkCanvas::LayerIter::~LayerIter() {
1790 fImpl->~SkDrawIter();
1791}
1792
1793void SkCanvas::LayerIter::next() {
1794 fDone = !fImpl->next();
1795}
1796
1797SkDevice* SkCanvas::LayerIter::device() const {
1798 return fImpl->getDevice();
1799}
1800
1801const SkMatrix& SkCanvas::LayerIter::matrix() const {
1802 return fImpl->getMatrix();
1803}
1804
1805const SkPaint& SkCanvas::LayerIter::paint() const {
1806 const SkPaint* paint = fImpl->getPaint();
1807 if (NULL == paint) {
1808 paint = &fDefaultPaint;
1809 }
1810 return *paint;
1811}
1812
1813const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
1814int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
1815int SkCanvas::LayerIter::y() const { return fImpl->getY(); }