blob: 1f75aa9ad62db5851b42b483daa5fefc06d54e90 [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
reed@google.com46799cd2011-02-22 20:56:26 +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:
215 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
216 fCanvas = canvas;
217 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;
222 fSkipEmptyClips = skipEmptyClips;
223 }
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
681 SkIRect ir;
682 const SkIRect& clipBounds = this->getTotalClip().getBounds();
reed@android.comf2b98d62010-12-20 18:26:13 +0000683 if (clipBounds.isEmpty()) {
684 return count;
685 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000686
687 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)) {
694 if (bounds_affects_clip(flags))
695 fMCRec->fRegion->setEmpty();
696 return count;
697 }
698 } else { // no user bounds, so just use the clip
699 ir = clipBounds;
700 }
701
reed@google.com5c3d1472011-02-22 19:12:23 +0000702 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000703 // early exit if the clip is now empty
704 if (bounds_affects_clip(flags) &&
705 !fMCRec->fRegion->op(ir, SkRegion::kIntersect_Op)) {
706 return count;
707 }
708
709 bool isOpaque;
710 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
711
bsalomon@google.come97f0852011-06-17 13:10:25 +0000712 SkDevice* device = this->createLayerDevice(config, ir.width(), ir.height(),
713 isOpaque);
bungeman@google.come25c6842011-08-17 14:53:54 +0000714 if (NULL == device) {
715 SkDebugf("Unable to create device for layer.");
716 return count;
717 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000718
reed@google.com6f8f2922011-03-04 22:27:10 +0000719 device->setOrigin(ir.fLeft, ir.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000720 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
721 device->unref();
722
723 layer->fNext = fMCRec->fTopLayer;
724 fMCRec->fLayer = layer;
725 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
726
727 return count;
728}
729
730int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
731 SaveFlags flags) {
732 if (0xFF == alpha) {
733 return this->saveLayer(bounds, NULL, flags);
734 } else {
735 SkPaint tmpPaint;
736 tmpPaint.setAlpha(alpha);
737 return this->saveLayer(bounds, &tmpPaint, flags);
738 }
739}
740
741void SkCanvas::restore() {
742 // check for underflow
743 if (fMCStack.count() > 1) {
744 this->internalRestore();
745 }
746}
747
748void SkCanvas::internalRestore() {
749 SkASSERT(fMCStack.count() != 0);
750
751 fDeviceCMDirty = true;
752 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000753 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000754
reed@google.com5c3d1472011-02-22 19:12:23 +0000755 fClipStack.restore();
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000756 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000757 DeviceCM* layer = fMCRec->fLayer; // may be null
758 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
759 fMCRec->fLayer = NULL;
760
761 // now do the normal restore()
762 fMCRec->~MCRec(); // balanced in save()
763 fMCStack.pop_back();
764 fMCRec = (MCRec*)fMCStack.back();
765
766 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
767 since if we're being recorded, we don't want to record this (the
768 recorder will have already recorded the restore).
769 */
770 if (NULL != layer) {
771 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000772 const SkIPoint& origin = layer->fDevice->getOrigin();
773 this->drawDevice(layer->fDevice, origin.x(), origin.y(),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000774 layer->fPaint);
775 // reset this, since drawDevice will have set it to true
776 fDeviceCMDirty = true;
777 }
778 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000779 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000780
781 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000782}
783
784int SkCanvas::getSaveCount() const {
785 return fMCStack.count();
786}
787
788void SkCanvas::restoreToCount(int count) {
789 // sanity check
790 if (count < 1) {
791 count = 1;
792 }
793 while (fMCStack.count() > count) {
794 this->restore();
795 }
796}
797
798/////////////////////////////////////////////////////////////////////////////
799
800// can't draw it if its empty, or its too big for a fixed-point width or height
801static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000802 return bitmap.width() <= 0 || bitmap.height() <= 0
803#ifndef SK_ALLOW_OVER_32K_BITMAPS
804 || bitmap.width() > 32767 || bitmap.height() > 32767
805#endif
806 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000807}
808
reed@android.comf2b98d62010-12-20 18:26:13 +0000809void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000810 const SkMatrix& matrix, const SkPaint* paint) {
811 if (reject_bitmap(bitmap)) {
812 return;
813 }
814
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000815 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000816 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000817 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000818 }
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000819 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000820}
821
822void SkCanvas::drawDevice(SkDevice* device, int x, int y,
823 const SkPaint* paint) {
824 SkPaint tmp;
825 if (NULL == paint) {
826 tmp.setDither(true);
827 paint = &tmp;
828 }
reed@google.com4b226022011-01-11 18:32:13 +0000829
reed@google.com4e2b3d32011-04-07 14:18:59 +0000830 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000831 while (iter.next()) {
832 iter.fDevice->drawDevice(iter, device, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +0000833 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000834 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000835 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +0000836}
837
838/////////////////////////////////////////////////////////////////////////////
839
840bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
841 fDeviceCMDirty = true;
842 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000843 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000844 return fMCRec->fMatrix->preTranslate(dx, dy);
845}
846
847bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
848 fDeviceCMDirty = true;
849 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000850 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000851 return fMCRec->fMatrix->preScale(sx, sy);
852}
853
854bool SkCanvas::rotate(SkScalar degrees) {
855 fDeviceCMDirty = true;
856 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000857 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000858 return fMCRec->fMatrix->preRotate(degrees);
859}
860
861bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
862 fDeviceCMDirty = true;
863 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000864 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000865 return fMCRec->fMatrix->preSkew(sx, sy);
866}
867
868bool SkCanvas::concat(const SkMatrix& matrix) {
869 fDeviceCMDirty = true;
870 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000871 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000872 return fMCRec->fMatrix->preConcat(matrix);
873}
874
875void SkCanvas::setMatrix(const SkMatrix& matrix) {
876 fDeviceCMDirty = true;
877 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000878 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000879 *fMCRec->fMatrix = matrix;
880}
881
882// this is not virtual, so it must call a virtual method so that subclasses
883// will see its action
884void SkCanvas::resetMatrix() {
885 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +0000886
reed@android.com8a1c16f2008-12-17 15:59:43 +0000887 matrix.reset();
888 this->setMatrix(matrix);
889}
890
891//////////////////////////////////////////////////////////////////////////////
892
893bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000894 AutoValidateClip avc(this);
895
reed@android.com8a1c16f2008-12-17 15:59:43 +0000896 fDeviceCMDirty = true;
897 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000898 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000899
900 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +0000901 // for these simpler matrices, we can stay a rect ever after applying
902 // the matrix. This means we don't have to a) make a path, and b) tell
903 // the region code to scan-convert the path, only to discover that it
904 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000905 SkRect r;
906 SkIRect ir;
907
908 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com5c3d1472011-02-22 19:12:23 +0000909 fClipStack.clipDevRect(r, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000910 r.round(&ir);
911 return fMCRec->fRegion->op(ir, op);
912 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +0000913 // since we're rotate or some such thing, we convert the rect to a path
914 // and clip against that, since it can handle any matrix. However, to
915 // avoid recursion in the case where we are subclassed (e.g. Pictures)
916 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000917 SkPath path;
918
919 path.addRect(rect);
reed@android.com98de2bd2009-03-02 19:41:36 +0000920 return this->SkCanvas::clipPath(path, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000921 }
922}
923
reed@google.com819c9212011-02-23 18:56:55 +0000924static bool clipPathHelper(const SkCanvas* canvas, SkRegion* currRgn,
925 const SkPath& devPath, SkRegion::Op op) {
reed@google.com759876a2011-03-03 14:32:51 +0000926 // base is used to limit the size (and therefore memory allocation) of the
927 // region that results from scan converting devPath.
928 SkRegion base;
929
reed@google.com819c9212011-02-23 18:56:55 +0000930 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +0000931 // since we are intersect, we can do better (tighter) with currRgn's
932 // bounds, than just using the device. However, if currRgn is complex,
933 // our region blitter may hork, so we do that case in two steps.
934 if (currRgn->isRect()) {
935 return currRgn->setPath(devPath, *currRgn);
936 } else {
937 base.setRect(currRgn->getBounds());
938 SkRegion rgn;
939 rgn.setPath(devPath, base);
940 return currRgn->op(rgn, op);
941 }
reed@google.com819c9212011-02-23 18:56:55 +0000942 } else {
reed@google.com819c9212011-02-23 18:56:55 +0000943 const SkBitmap& bm = canvas->getDevice()->accessBitmap(false);
944 base.setRect(0, 0, bm.width(), bm.height());
945
946 if (SkRegion::kReplace_Op == op) {
947 return currRgn->setPath(devPath, base);
948 } else {
949 SkRegion rgn;
950 rgn.setPath(devPath, base);
951 return currRgn->op(rgn, op);
952 }
953 }
954}
955
reed@android.com8a1c16f2008-12-17 15:59:43 +0000956bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000957 AutoValidateClip avc(this);
958
reed@android.com8a1c16f2008-12-17 15:59:43 +0000959 fDeviceCMDirty = true;
960 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000961 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000962
963 SkPath devPath;
964 path.transform(*fMCRec->fMatrix, &devPath);
965
reed@google.com5c3d1472011-02-22 19:12:23 +0000966 // if we called path.swap() we could avoid a deep copy of this path
967 fClipStack.clipDevPath(devPath, op);
968
reed@google.com819c9212011-02-23 18:56:55 +0000969 return clipPathHelper(this, fMCRec->fRegion, devPath, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000970}
971
972bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000973 AutoValidateClip avc(this);
974
reed@android.com8a1c16f2008-12-17 15:59:43 +0000975 fDeviceCMDirty = true;
976 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000977 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000978
reed@google.com5c3d1472011-02-22 19:12:23 +0000979 // todo: signal fClipStack that we have a region, and therefore (I guess)
980 // we have to ignore it, and use the region directly?
981 fClipStack.clipDevRect(rgn.getBounds());
982
reed@android.com8a1c16f2008-12-17 15:59:43 +0000983 return fMCRec->fRegion->op(rgn, op);
984}
985
reed@google.com819c9212011-02-23 18:56:55 +0000986#ifdef SK_DEBUG
987void SkCanvas::validateClip() const {
988 // construct clipRgn from the clipstack
989 const SkDevice* device = this->getDevice();
990 SkIRect ir;
991 ir.set(0, 0, device->width(), device->height());
992 SkRegion clipRgn(ir);
993
994 SkClipStack::B2FIter iter(fClipStack);
995 const SkClipStack::B2FIter::Clip* clip;
996 while ((clip = iter.next()) != NULL) {
997 if (clip->fPath) {
998 clipPathHelper(this, &clipRgn, *clip->fPath, clip->fOp);
999 } else if (clip->fRect) {
1000 clip->fRect->round(&ir);
1001 clipRgn.op(ir, clip->fOp);
1002 } else {
reed@google.comdca7acb2011-02-23 21:47:37 +00001003 clipRgn.setEmpty();
reed@google.com819c9212011-02-23 18:56:55 +00001004 }
1005 }
1006
reed@google.com6f8f2922011-03-04 22:27:10 +00001007#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001008 // now compare against the current rgn
1009 const SkRegion& rgn = this->getTotalClip();
1010 SkASSERT(rgn == clipRgn);
reed@google.com02878b82011-02-24 13:04:42 +00001011#endif
reed@google.com819c9212011-02-23 18:56:55 +00001012}
1013#endif
1014
reed@google.com5c3d1472011-02-22 19:12:23 +00001015///////////////////////////////////////////////////////////////////////////////
1016
reed@android.comba09de42010-02-05 20:46:05 +00001017void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001018 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001019 SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
1020 fLocalBoundsCompareTypeBW;
1021
1022 if (!this->getClipBounds(&r, et)) {
1023 rCompare.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001024 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001025 rCompare.set(SkScalarToCompareType(r.fLeft),
1026 SkScalarToCompareType(r.fTop),
1027 SkScalarToCompareType(r.fRight),
1028 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001029 }
1030}
1031
reed@android.comd252db02009-04-01 18:31:44 +00001032/* current impl ignores edgetype, and relies on
1033 getLocalClipBoundsCompareType(), which always returns a value assuming
1034 antialiasing (worst case)
1035 */
reed@android.comba09de42010-02-05 20:46:05 +00001036bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001037
1038 if (!rect.hasValidCoordinates())
1039 return true;
1040
reed@android.com8a1c16f2008-12-17 15:59:43 +00001041 if (fMCRec->fRegion->isEmpty()) {
1042 return true;
1043 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001044
tomhudson@google.com8d430182011-06-06 19:11:19 +00001045 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001046 SkRect dst;
1047 fMCRec->fMatrix->mapRect(&dst, rect);
1048 SkIRect idst;
1049 dst.roundOut(&idst);
1050 return !SkIRect::Intersects(idst, fMCRec->fRegion->getBounds());
1051 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001052 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
reed@android.comd252db02009-04-01 18:31:44 +00001053
reed@android.coma380ae42009-07-21 01:17:02 +00001054 // for speed, do the most likely reject compares first
1055 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1056 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1057 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1058 return true;
1059 }
1060 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1061 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1062 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1063 return true;
1064 }
1065 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001066 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001067}
1068
1069bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +00001070 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001071}
1072
1073bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
1074 /* current impl ignores edgetype, and relies on
1075 getLocalClipBoundsCompareType(), which always returns a value assuming
1076 antialiasing (worst case)
1077 */
1078
1079 if (fMCRec->fRegion->isEmpty()) {
1080 return true;
1081 }
reed@google.com4b226022011-01-11 18:32:13 +00001082
reed@android.comaefd2bc2009-03-30 21:02:14 +00001083 SkScalarCompareType userT = SkScalarToCompareType(top);
1084 SkScalarCompareType userB = SkScalarToCompareType(bottom);
reed@google.com4b226022011-01-11 18:32:13 +00001085
reed@android.com8a1c16f2008-12-17 15:59:43 +00001086 // check for invalid user Y coordinates (i.e. empty)
reed@android.comd252db02009-04-01 18:31:44 +00001087 // reed: why do we need to do this check, since it slows us down?
reed@android.com8a1c16f2008-12-17 15:59:43 +00001088 if (userT >= userB) {
1089 return true;
1090 }
reed@google.com4b226022011-01-11 18:32:13 +00001091
reed@android.com8a1c16f2008-12-17 15:59:43 +00001092 // check if we are above or below the local clip bounds
1093 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
1094 return userT >= clipR.fBottom || userB <= clipR.fTop;
1095}
1096
1097bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
1098 const SkRegion& clip = *fMCRec->fRegion;
1099 if (clip.isEmpty()) {
1100 if (bounds) {
1101 bounds->setEmpty();
1102 }
1103 return false;
1104 }
1105
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001106 SkMatrix inverse;
1107 // if we can't invert the CTM, we can't return local clip bounds
1108 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001109 if (bounds) {
1110 bounds->setEmpty();
1111 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001112 return false;
1113 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001114
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001115 if (NULL != bounds) {
1116 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001117 // get the clip's bounds
1118 const SkIRect& ibounds = clip.getBounds();
1119 // adjust it outwards if we are antialiasing
1120 int inset = (kAA_EdgeType == et);
1121 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1122 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@google.com4b226022011-01-11 18:32:13 +00001123
reed@android.com8a1c16f2008-12-17 15:59:43 +00001124 // invert into local coordinates
1125 inverse.mapRect(bounds, r);
1126 }
1127 return true;
1128}
1129
1130const SkMatrix& SkCanvas::getTotalMatrix() const {
1131 return *fMCRec->fMatrix;
1132}
1133
1134const SkRegion& SkCanvas::getTotalClip() const {
1135 return *fMCRec->fRegion;
1136}
1137
reed@google.com7d7ca792011-02-23 22:39:18 +00001138const SkClipStack& SkCanvas::getTotalClipStack() const {
1139 return fClipStack;
1140}
1141
reed@android.comf2b98d62010-12-20 18:26:13 +00001142void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1143 if (NULL == matrix || matrix->isIdentity()) {
1144 if (fUseExternalMatrix) {
1145 fDeviceCMDirty = true;
1146 }
1147 fUseExternalMatrix = false;
1148 } else {
1149 fUseExternalMatrix = true;
1150 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.com4b226022011-01-11 18:32:13 +00001151
reed@android.comf2b98d62010-12-20 18:26:13 +00001152 fExternalMatrix = *matrix;
1153 matrix->invert(&fExternalInverse);
1154 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001155}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001156
bsalomon@google.come97f0852011-06-17 13:10:25 +00001157SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
1158 int width, int height,
1159 bool isOpaque) {
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001160 SkDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001161 if (device) {
1162 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1163 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001164 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001165 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001166 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001167}
1168
bsalomon@google.come97f0852011-06-17 13:10:25 +00001169SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
1170 int width, int height,
1171 bool isOpaque) {
1172 SkDevice* device = this->getDevice();
1173 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001174 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001175 } else {
1176 return NULL;
1177 }
1178}
1179
1180
reed@android.com8a1c16f2008-12-17 15:59:43 +00001181//////////////////////////////////////////////////////////////////////////////
1182// These are the virtual drawing methods
1183//////////////////////////////////////////////////////////////////////////////
1184
reed@google.com2a981812011-04-14 18:59:28 +00001185void SkCanvas::clear(SkColor color) {
1186 SkDrawIter iter(this);
1187
1188 while (iter.next()) {
1189 iter.fDevice->clear(color);
1190 }
1191}
1192
reed@android.com8a1c16f2008-12-17 15:59:43 +00001193void SkCanvas::drawPaint(const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001194 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001195
1196 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001197 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001198 }
1199
reed@google.com4e2b3d32011-04-07 14:18:59 +00001200 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001201}
1202
1203void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1204 const SkPaint& paint) {
1205 if ((long)count <= 0) {
1206 return;
1207 }
1208
1209 SkASSERT(pts != NULL);
1210
reed@google.com4e2b3d32011-04-07 14:18:59 +00001211 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001212
reed@android.com8a1c16f2008-12-17 15:59:43 +00001213 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001214 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001215 }
reed@google.com4b226022011-01-11 18:32:13 +00001216
reed@google.com4e2b3d32011-04-07 14:18:59 +00001217 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001218}
1219
1220void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1221 if (paint.canComputeFastBounds()) {
1222 SkRect storage;
1223 if (this->quickReject(paint.computeFastBounds(r, &storage),
1224 paint2EdgeType(&paint))) {
1225 return;
1226 }
1227 }
reed@google.com4b226022011-01-11 18:32:13 +00001228
reed@google.com4e2b3d32011-04-07 14:18:59 +00001229 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001230
1231 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001232 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001233 }
1234
reed@google.com4e2b3d32011-04-07 14:18:59 +00001235 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001236}
1237
1238void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1239 if (paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001240 SkRect storage;
1241 const SkRect& bounds = path.getBounds();
1242 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001243 paint2EdgeType(&paint))) {
1244 return;
1245 }
1246 }
1247
reed@google.com4e2b3d32011-04-07 14:18:59 +00001248 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001249
1250 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001251 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001252 }
1253
reed@google.com4e2b3d32011-04-07 14:18:59 +00001254 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001255}
1256
1257void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1258 const SkPaint* paint) {
1259 SkDEBUGCODE(bitmap.validate();)
1260
1261 if (NULL == paint || (paint->getMaskFilter() == NULL)) {
1262 SkRect fastBounds;
1263 fastBounds.set(x, y,
1264 x + SkIntToScalar(bitmap.width()),
1265 y + SkIntToScalar(bitmap.height()));
1266 if (this->quickReject(fastBounds, paint2EdgeType(paint))) {
1267 return;
1268 }
1269 }
reed@google.com4b226022011-01-11 18:32:13 +00001270
reed@android.com8a1c16f2008-12-17 15:59:43 +00001271 SkMatrix matrix;
1272 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001273 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001274}
1275
reed@google.com9987ec32011-09-07 11:57:52 +00001276// this one is non-virtual, so it can be called safely by other canvas apis
1277void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1278 const SkRect& dst, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001279 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1280 return;
1281 }
reed@google.com9987ec32011-09-07 11:57:52 +00001282
reed@android.com8a1c16f2008-12-17 15:59:43 +00001283 // do this now, to avoid the cost of calling extract for RLE bitmaps
1284 if (this->quickReject(dst, paint2EdgeType(paint))) {
1285 return;
1286 }
reed@google.com9987ec32011-09-07 11:57:52 +00001287
reed@android.com8a1c16f2008-12-17 15:59:43 +00001288 const SkBitmap* bitmapPtr = &bitmap;
reed@google.com9987ec32011-09-07 11:57:52 +00001289
reed@android.com8a1c16f2008-12-17 15:59:43 +00001290 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001291 SkRect tmpSrc;
1292 if (src) {
1293 tmpSrc.set(*src);
1294 // if the extract process clipped off the top or left of the
1295 // original, we adjust for that here to get the position right.
1296 if (tmpSrc.fLeft > 0) {
1297 tmpSrc.fRight -= tmpSrc.fLeft;
1298 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001299 }
reed@android.com87899992009-10-16 14:48:38 +00001300 if (tmpSrc.fTop > 0) {
1301 tmpSrc.fBottom -= tmpSrc.fTop;
1302 tmpSrc.fTop = 0;
1303 }
1304 } else {
1305 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1306 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001307 }
reed@android.com87899992009-10-16 14:48:38 +00001308 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
reed@google.com9987ec32011-09-07 11:57:52 +00001309
reed@android.comf2b98d62010-12-20 18:26:13 +00001310 // ensure that src is "valid" before we pass it to our internal routines
1311 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1312 SkIRect tmpISrc;
1313 if (src) {
1314 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
reed@google.com2ade0862011-03-17 17:48:04 +00001315 if (!tmpISrc.intersect(*src)) {
1316 return;
1317 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001318 src = &tmpISrc;
1319 }
1320 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001321}
1322
reed@google.com9987ec32011-09-07 11:57:52 +00001323void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1324 const SkRect& dst, const SkPaint* paint) {
1325 SkDEBUGCODE(bitmap.validate();)
1326 this->internalDrawBitmapRect(bitmap, src, dst, paint);
1327}
1328
reed@android.com8a1c16f2008-12-17 15:59:43 +00001329void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1330 const SkPaint* paint) {
1331 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001332 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001333}
1334
reed@android.comf2b98d62010-12-20 18:26:13 +00001335void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1336 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001337 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001338
reed@google.com4e2b3d32011-04-07 14:18:59 +00001339 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001340
reed@android.com8a1c16f2008-12-17 15:59:43 +00001341 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001342 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001343 }
reed@android.com9b039062009-02-11 15:09:58 +00001344
reed@google.com4e2b3d32011-04-07 14:18:59 +00001345 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001346}
1347
reed@google.com9987ec32011-09-07 11:57:52 +00001348void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1349 const SkIRect& center, const SkRect& dst,
1350 const SkPaint* paint) {
1351 const int32_t w = bitmap.width();
1352 const int32_t h = bitmap.height();
1353
1354 SkIRect c = center;
1355 // pin center to the bounds of the bitmap
1356 c.fLeft = SkMax32(0, center.fLeft);
1357 c.fTop = SkMax32(0, center.fTop);
1358 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1359 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1360
1361 const int32_t srcX[4] = { 0, c.fLeft, c.fRight, w };
1362 const int32_t srcY[4] = { 0, c.fTop, c.fBottom, h };
1363 SkScalar dstX[4] = {
1364 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1365 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1366 };
1367 SkScalar dstY[4] = {
1368 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1369 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1370 };
1371
1372 if (dstX[1] > dstX[2]) {
1373 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1374 dstX[2] = dstX[1];
1375 }
1376
1377 if (dstY[1] > dstY[2]) {
1378 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1379 dstY[2] = dstY[1];
1380 }
1381
1382 SkIRect s;
1383 SkRect d;
1384 for (int y = 0; y < 3; y++) {
1385 s.fTop = srcY[y];
1386 s.fBottom = srcY[y+1];
1387 d.fTop = dstY[y];
1388 d.fBottom = dstY[y+1];
1389 for (int x = 0; x < 3; x++) {
1390 s.fLeft = srcX[x];
1391 s.fRight = srcX[x+1];
1392 d.fLeft = dstX[x];
1393 d.fRight = dstX[x+1];
1394 this->internalDrawBitmapRect(bitmap, &s, d, paint);
1395 }
1396 }
1397}
1398
1399void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1400 const SkRect& dst, const SkPaint* paint) {
1401 SkDEBUGCODE(bitmap.validate();)
1402
1403 // Need a device entry-point, so gpu can use a mesh
1404 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1405}
1406
reed@android.com8a1c16f2008-12-17 15:59:43 +00001407void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1408 const SkPaint* paint) {
1409 SkDEBUGCODE(bitmap.validate();)
reed@google.com4b226022011-01-11 18:32:13 +00001410
reed@android.com8a1c16f2008-12-17 15:59:43 +00001411 if (reject_bitmap(bitmap)) {
1412 return;
1413 }
reed@google.com4b226022011-01-11 18:32:13 +00001414
reed@android.com8a1c16f2008-12-17 15:59:43 +00001415 SkPaint tmp;
1416 if (NULL == paint) {
1417 paint = &tmp;
1418 }
reed@google.com4b226022011-01-11 18:32:13 +00001419
reed@google.com4e2b3d32011-04-07 14:18:59 +00001420 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001421
reed@android.com8a1c16f2008-12-17 15:59:43 +00001422 while (iter.next()) {
1423 iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +00001424 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001425 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001426 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001427}
1428
reed@google.comf67e4cf2011-03-15 20:56:58 +00001429class SkDeviceFilteredPaint {
1430public:
1431 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1432 SkDevice::TextFlags flags;
1433 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001434 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001435 newPaint->setFlags(flags.fFlags);
1436 newPaint->setHinting(flags.fHinting);
1437 fPaint = newPaint;
1438 } else {
1439 fPaint = &paint;
1440 }
1441 }
1442
reed@google.comf67e4cf2011-03-15 20:56:58 +00001443 const SkPaint& paint() const { return *fPaint; }
1444
1445private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001446 const SkPaint* fPaint;
1447 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001448};
1449
bungeman@google.com52c748b2011-08-22 21:30:43 +00001450void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1451 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001452 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001453 draw.fDevice->drawRect(draw, r, paint);
1454 } else {
1455 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001456 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001457 draw.fDevice->drawRect(draw, r, p);
1458 }
1459}
1460
1461void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1462 const char text[], size_t byteLength,
1463 SkScalar x, SkScalar y) {
1464 SkASSERT(byteLength == 0 || text != NULL);
1465
1466 // nothing to draw
1467 if (text == NULL || byteLength == 0 ||
1468 draw.fClip->isEmpty() ||
1469 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1470 return;
1471 }
1472
1473 SkScalar width = 0;
1474 SkPoint start;
1475
1476 start.set(0, 0); // to avoid warning
1477 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1478 SkPaint::kStrikeThruText_Flag)) {
1479 width = paint.measureText(text, byteLength);
1480
1481 SkScalar offsetX = 0;
1482 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1483 offsetX = SkScalarHalf(width);
1484 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1485 offsetX = width;
1486 }
1487 start.set(x - offsetX, y);
1488 }
1489
1490 if (0 == width) {
1491 return;
1492 }
1493
1494 uint32_t flags = paint.getFlags();
1495
1496 if (flags & (SkPaint::kUnderlineText_Flag |
1497 SkPaint::kStrikeThruText_Flag)) {
1498 SkScalar textSize = paint.getTextSize();
1499 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1500 SkRect r;
1501
1502 r.fLeft = start.fX;
1503 r.fRight = start.fX + width;
1504
1505 if (flags & SkPaint::kUnderlineText_Flag) {
1506 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1507 start.fY);
1508 r.fTop = offset;
1509 r.fBottom = offset + height;
1510 DrawRect(draw, paint, r, textSize);
1511 }
1512 if (flags & SkPaint::kStrikeThruText_Flag) {
1513 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1514 start.fY);
1515 r.fTop = offset;
1516 r.fBottom = offset + height;
1517 DrawRect(draw, paint, r, textSize);
1518 }
1519 }
1520}
1521
reed@android.com8a1c16f2008-12-17 15:59:43 +00001522void SkCanvas::drawText(const void* text, size_t byteLength,
1523 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001524 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001525
1526 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001527 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001528 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001529 DrawTextDecorations(iter, dfp.paint(),
1530 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001531 }
1532
reed@google.com4e2b3d32011-04-07 14:18:59 +00001533 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001534}
1535
1536void SkCanvas::drawPosText(const void* text, size_t byteLength,
1537 const SkPoint pos[], const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001538 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001539
reed@android.com8a1c16f2008-12-17 15:59:43 +00001540 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001541 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001542 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001543 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001544 }
reed@google.com4b226022011-01-11 18:32:13 +00001545
reed@google.com4e2b3d32011-04-07 14:18:59 +00001546 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001547}
1548
1549void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1550 const SkScalar xpos[], SkScalar constY,
1551 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001552 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001553
reed@android.com8a1c16f2008-12-17 15:59:43 +00001554 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001555 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001556 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001557 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001558 }
reed@google.com4b226022011-01-11 18:32:13 +00001559
reed@google.com4e2b3d32011-04-07 14:18:59 +00001560 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001561}
1562
1563void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1564 const SkPath& path, const SkMatrix* matrix,
1565 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001566 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001567
1568 while (iter.next()) {
1569 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001570 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001571 }
1572
reed@google.com4e2b3d32011-04-07 14:18:59 +00001573 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001574}
1575
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001576#ifdef ANDROID
1577void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1578 const SkPoint pos[], const SkPaint& paint,
1579 const SkPath& path, const SkMatrix* matrix) {
1580
reed@google.com4e2b3d32011-04-07 14:18:59 +00001581 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001582
1583 while (iter.next()) {
1584 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001585 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001586 }
1587
reed@google.com4e2b3d32011-04-07 14:18:59 +00001588 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001589}
1590#endif
1591
reed@android.com8a1c16f2008-12-17 15:59:43 +00001592void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1593 const SkPoint verts[], const SkPoint texs[],
1594 const SkColor colors[], SkXfermode* xmode,
1595 const uint16_t indices[], int indexCount,
1596 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001597 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001598
reed@android.com8a1c16f2008-12-17 15:59:43 +00001599 while (iter.next()) {
1600 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001601 colors, xmode, indices, indexCount,
1602 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001603 }
reed@google.com4b226022011-01-11 18:32:13 +00001604
reed@google.com4e2b3d32011-04-07 14:18:59 +00001605 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001606}
1607
reed@android.comcb608442009-12-04 21:32:27 +00001608void SkCanvas::drawData(const void* data, size_t length) {
1609 // do nothing. Subclasses may do something with the data
1610}
1611
reed@android.com8a1c16f2008-12-17 15:59:43 +00001612//////////////////////////////////////////////////////////////////////////////
1613// These methods are NOT virtual, and therefore must call back into virtual
1614// methods, rather than actually drawing themselves.
1615//////////////////////////////////////////////////////////////////////////////
1616
1617void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001618 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001619 SkPaint paint;
1620
1621 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001622 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001623 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001624 }
1625 this->drawPaint(paint);
1626}
1627
reed@android.com845fdac2009-06-23 03:01:32 +00001628void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001629 SkPaint paint;
1630
1631 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001632 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001633 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001634 }
1635 this->drawPaint(paint);
1636}
1637
1638void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1639 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001640
reed@android.com8a1c16f2008-12-17 15:59:43 +00001641 pt.set(x, y);
1642 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1643}
1644
1645void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1646 SkPoint pt;
1647 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001648
reed@android.com8a1c16f2008-12-17 15:59:43 +00001649 pt.set(x, y);
1650 paint.setColor(color);
1651 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1652}
1653
1654void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1655 const SkPaint& paint) {
1656 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001657
reed@android.com8a1c16f2008-12-17 15:59:43 +00001658 pts[0].set(x0, y0);
1659 pts[1].set(x1, y1);
1660 this->drawPoints(kLines_PointMode, 2, pts, paint);
1661}
1662
1663void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1664 SkScalar right, SkScalar bottom,
1665 const SkPaint& paint) {
1666 SkRect r;
1667
1668 r.set(left, top, right, bottom);
1669 this->drawRect(r, paint);
1670}
1671
1672void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1673 const SkPaint& paint) {
1674 if (radius < 0) {
1675 radius = 0;
1676 }
1677
1678 SkRect r;
1679 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001680
reed@android.com8a1c16f2008-12-17 15:59:43 +00001681 if (paint.canComputeFastBounds()) {
1682 SkRect storage;
1683 if (this->quickReject(paint.computeFastBounds(r, &storage),
1684 paint2EdgeType(&paint))) {
1685 return;
1686 }
1687 }
reed@google.com4b226022011-01-11 18:32:13 +00001688
reed@android.com8a1c16f2008-12-17 15:59:43 +00001689 SkPath path;
1690 path.addOval(r);
1691 this->drawPath(path, paint);
1692}
1693
1694void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1695 const SkPaint& paint) {
1696 if (rx > 0 && ry > 0) {
1697 if (paint.canComputeFastBounds()) {
1698 SkRect storage;
1699 if (this->quickReject(paint.computeFastBounds(r, &storage),
1700 paint2EdgeType(&paint))) {
1701 return;
1702 }
1703 }
1704
1705 SkPath path;
1706 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1707 this->drawPath(path, paint);
1708 } else {
1709 this->drawRect(r, paint);
1710 }
1711}
1712
1713void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1714 if (paint.canComputeFastBounds()) {
1715 SkRect storage;
1716 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1717 paint2EdgeType(&paint))) {
1718 return;
1719 }
1720 }
1721
1722 SkPath path;
1723 path.addOval(oval);
1724 this->drawPath(path, paint);
1725}
1726
1727void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1728 SkScalar sweepAngle, bool useCenter,
1729 const SkPaint& paint) {
1730 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1731 this->drawOval(oval, paint);
1732 } else {
1733 SkPath path;
1734 if (useCenter) {
1735 path.moveTo(oval.centerX(), oval.centerY());
1736 }
1737 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1738 if (useCenter) {
1739 path.close();
1740 }
1741 this->drawPath(path, paint);
1742 }
1743}
1744
1745void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1746 const SkPath& path, SkScalar hOffset,
1747 SkScalar vOffset, const SkPaint& paint) {
1748 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001749
reed@android.com8a1c16f2008-12-17 15:59:43 +00001750 matrix.setTranslate(hOffset, vOffset);
1751 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1752}
1753
reed@android.comf76bacf2009-05-13 14:00:33 +00001754///////////////////////////////////////////////////////////////////////////////
1755
reed@android.com8a1c16f2008-12-17 15:59:43 +00001756void SkCanvas::drawPicture(SkPicture& picture) {
1757 int saveCount = save();
1758 picture.draw(this);
1759 restoreToCount(saveCount);
1760}
1761
reed@android.com8a1c16f2008-12-17 15:59:43 +00001762///////////////////////////////////////////////////////////////////////////////
1763///////////////////////////////////////////////////////////////////////////////
1764
1765SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00001766 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001767
1768 SkASSERT(canvas);
1769
1770 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
1771 fDone = !fImpl->next();
1772}
1773
1774SkCanvas::LayerIter::~LayerIter() {
1775 fImpl->~SkDrawIter();
1776}
1777
1778void SkCanvas::LayerIter::next() {
1779 fDone = !fImpl->next();
1780}
1781
1782SkDevice* SkCanvas::LayerIter::device() const {
1783 return fImpl->getDevice();
1784}
1785
1786const SkMatrix& SkCanvas::LayerIter::matrix() const {
1787 return fImpl->getMatrix();
1788}
1789
1790const SkPaint& SkCanvas::LayerIter::paint() const {
1791 const SkPaint* paint = fImpl->getPaint();
1792 if (NULL == paint) {
1793 paint = &fDefaultPaint;
1794 }
1795 return *paint;
1796}
1797
1798const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
1799int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
1800int SkCanvas::LayerIter::y() const { return fImpl->getY(); }