blob: 5f97d68c3c0d723b091ebe68cef1f4c807373d0e [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
reed@android.com8a1c16f2008-12-17 15:59:43 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00004 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00005 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00007 */
8
epoger@google.comec3ed6a2011-07-28 14:26:00 +00009
reed@android.com8a1c16f2008-12-17 15:59:43 +000010#include "SkCanvas.h"
11#include "SkBounder.h"
12#include "SkDevice.h"
13#include "SkDraw.h"
14#include "SkDrawFilter.h"
15#include "SkDrawLooper.h"
16#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000017#include "SkRasterClip.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000018#include "SkScalarCompare.h"
19#include "SkTemplates.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000020#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000021#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000022#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000023
24//#define SK_TRACE_SAVERESTORE
25
26#ifdef SK_TRACE_SAVERESTORE
27 static int gLayerCounter;
28 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
29 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
30
31 static int gRecCounter;
32 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
33 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
34
35 static int gCanvasCounter;
36 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
37 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
38#else
39 #define inc_layer()
40 #define dec_layer()
41 #define inc_rec()
42 #define dec_rec()
43 #define inc_canvas()
44 #define dec_canvas()
45#endif
46
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000047typedef SkTLazy<SkPaint> SkLazyPaint;
48
reed@android.com8a1c16f2008-12-17 15:59:43 +000049///////////////////////////////////////////////////////////////////////////////
50// Helpers for computing fast bounds for quickReject tests
51
52static SkCanvas::EdgeType paint2EdgeType(const SkPaint* paint) {
53 return paint != NULL && paint->isAntiAlias() ?
54 SkCanvas::kAA_EdgeType : SkCanvas::kBW_EdgeType;
55}
56
57///////////////////////////////////////////////////////////////////////////////
58
59/* This is the record we keep for each SkDevice that the user installs.
60 The clip/matrix/proc are fields that reflect the top of the save/restore
61 stack. Whenever the canvas changes, it marks a dirty flag, and then before
62 these are used (assuming we're not on a layer) we rebuild these cache
63 values: they reflect the top of the save stack, but translated and clipped
64 by the device's XY offset and bitmap-bounds.
65*/
66struct DeviceCM {
67 DeviceCM* fNext;
68 SkDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +000069 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +000070 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +000071 SkPaint* fPaint; // may be null (in the future)
reed@android.comf2b98d62010-12-20 18:26:13 +000072 // optional, related to canvas' external matrix
73 const SkMatrix* fMVMatrix;
74 const SkMatrix* fExtMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +000075
bungeman@google.com88edf1e2011-08-08 19:41:56 +000076 DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint)
reed@android.com8a1c16f2008-12-17 15:59:43 +000077 : fNext(NULL) {
78 if (NULL != device) {
79 device->ref();
80 device->lockPixels();
81 }
reed@google.com4b226022011-01-11 18:32:13 +000082 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +000083 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +000084 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000085
bungeman@google.com88edf1e2011-08-08 19:41:56 +000086 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000087 if (NULL != fDevice) {
88 fDevice->unlockPixels();
89 fDevice->unref();
90 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +000091 SkDELETE(fPaint);
92 }
reed@google.com4b226022011-01-11 18:32:13 +000093
reed@google.com045e62d2011-10-24 12:19:46 +000094 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
95 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +000096 int x = fDevice->getOrigin().x();
97 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +000098 int width = fDevice->width();
99 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000100
reed@android.com8a1c16f2008-12-17 15:59:43 +0000101 if ((x | y) == 0) {
102 fMatrix = &totalMatrix;
103 fClip = totalClip;
104 } else {
105 fMatrixStorage = totalMatrix;
106 fMatrixStorage.postTranslate(SkIntToScalar(-x),
107 SkIntToScalar(-y));
108 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000109
reed@android.com8a1c16f2008-12-17 15:59:43 +0000110 totalClip.translate(-x, -y, &fClip);
111 }
112
reed@google.com045e62d2011-10-24 12:19:46 +0000113 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000114
115 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000116
reed@android.com8a1c16f2008-12-17 15:59:43 +0000117 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000118 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000119 SkRegion::kDifference_Op);
120 }
reed@google.com4b226022011-01-11 18:32:13 +0000121
reed@google.com045e62d2011-10-24 12:19:46 +0000122 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000123
124#ifdef SK_DEBUG
125 if (!fClip.isEmpty()) {
126 SkIRect deviceR;
127 deviceR.set(0, 0, width, height);
128 SkASSERT(deviceR.contains(fClip.getBounds()));
129 }
130#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000131 // default is to assume no external matrix
132 fMVMatrix = NULL;
133 fExtMatrix = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000134 }
reed@android.comf2b98d62010-12-20 18:26:13 +0000135
136 // can only be called after calling updateMC()
137 void updateExternalMatrix(const SkMatrix& extM, const SkMatrix& extI) {
138 fMVMatrixStorage.setConcat(extI, *fMatrix);
139 fMVMatrix = &fMVMatrixStorage;
140 fExtMatrix = &extM; // assumes extM has long life-time (owned by canvas)
141 }
142
reed@android.com8a1c16f2008-12-17 15:59:43 +0000143private:
reed@android.comf2b98d62010-12-20 18:26:13 +0000144 SkMatrix fMatrixStorage, fMVMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000145};
146
147/* This is the record we keep for each save/restore level in the stack.
148 Since a level optionally copies the matrix and/or stack, we have pointers
149 for these fields. If the value is copied for this level, the copy is
150 stored in the ...Storage field, and the pointer points to that. If the
151 value is not copied for this level, we ignore ...Storage, and just point
152 at the corresponding value in the previous level in the stack.
153*/
154class SkCanvas::MCRec {
155public:
156 MCRec* fNext;
reed@google.com00177082011-10-12 14:34:30 +0000157 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
158 SkRasterClip* fRasterClip; // points to either fRegionStorage or prev MCRec
159 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000160
reed@android.com8a1c16f2008-12-17 15:59:43 +0000161 DeviceCM* fLayer;
162 /* If there are any layers in the stack, this points to the top-most
163 one that is at or below this level in the stack (so we know what
164 bitmap/device to draw into from this level. This value is NOT
165 reference counted, since the real owner is either our fLayer field,
166 or a previous one in a lower level.)
167 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000168 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000169
170 MCRec(const MCRec* prev, int flags) {
171 if (NULL != prev) {
172 if (flags & SkCanvas::kMatrix_SaveFlag) {
173 fMatrixStorage = *prev->fMatrix;
174 fMatrix = &fMatrixStorage;
175 } else {
176 fMatrix = prev->fMatrix;
177 }
reed@google.com4b226022011-01-11 18:32:13 +0000178
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179 if (flags & SkCanvas::kClip_SaveFlag) {
reed@google.com00177082011-10-12 14:34:30 +0000180 fRasterClipStorage = *prev->fRasterClip;
181 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000182 } else {
reed@google.com00177082011-10-12 14:34:30 +0000183 fRasterClip = prev->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000184 }
185
186 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000187 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000188
189 fTopLayer = prev->fTopLayer;
190 } else { // no prev
191 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000192
reed@android.com8a1c16f2008-12-17 15:59:43 +0000193 fMatrix = &fMatrixStorage;
reed@google.com00177082011-10-12 14:34:30 +0000194 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000195 fFilter = NULL;
196 fTopLayer = NULL;
197 }
198 fLayer = NULL;
199
200 // don't bother initializing fNext
201 inc_rec();
202 }
203 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000204 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000205 SkDELETE(fLayer);
206 dec_rec();
207 }
reed@google.com4b226022011-01-11 18:32:13 +0000208
reed@android.com8a1c16f2008-12-17 15:59:43 +0000209private:
reed@google.com00177082011-10-12 14:34:30 +0000210 SkMatrix fMatrixStorage;
211 SkRasterClip fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000212};
213
214class SkDrawIter : public SkDraw {
215public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000216 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000217 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000218 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000219 canvas->updateDeviceCMCache();
220
reed@google.com7d7ca792011-02-23 22:39:18 +0000221 fClipStack = &canvas->getTotalClipStack();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000222 fBounder = canvas->getBounder();
223 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000224 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225 }
reed@google.com4b226022011-01-11 18:32:13 +0000226
reed@android.com8a1c16f2008-12-17 15:59:43 +0000227 bool next() {
228 // skip over recs with empty clips
229 if (fSkipEmptyClips) {
230 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
231 fCurrLayer = fCurrLayer->fNext;
232 }
233 }
234
reed@google.comf68c5e22012-02-24 16:38:58 +0000235 const DeviceCM* rec = fCurrLayer;
236 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000237
238 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000239 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
240 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000241 fDevice = rec->fDevice;
242 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000244 fMVMatrix = rec->fMVMatrix;
245 fExtMatrix = rec->fExtMatrix;
246 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247
248 fCurrLayer = rec->fNext;
249 if (fBounder) {
250 fBounder->setClip(fClip);
251 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000253
bsalomon@google.comd302f142011-03-03 13:54:13 +0000254 fCanvas->prepareForDeviceDraw(fDevice, *fMatrix, *fClip, *fClipStack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255 return true;
256 }
257 return false;
258 }
reed@google.com4b226022011-01-11 18:32:13 +0000259
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260 SkDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000261 int getX() const { return fDevice->getOrigin().x(); }
262 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000263 const SkMatrix& getMatrix() const { return *fMatrix; }
264 const SkRegion& getClip() const { return *fClip; }
265 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000266
reed@android.com8a1c16f2008-12-17 15:59:43 +0000267private:
268 SkCanvas* fCanvas;
269 const DeviceCM* fCurrLayer;
270 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000271 SkBool8 fSkipEmptyClips;
272
273 typedef SkDraw INHERITED;
274};
275
276/////////////////////////////////////////////////////////////////////////////
277
278class AutoDrawLooper {
279public:
reed@google.com4e2b3d32011-04-07 14:18:59 +0000280 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint) : fOrigPaint(paint) {
281 fCanvas = canvas;
282 fLooper = paint.getLooper();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000283 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000284 fPaint = NULL;
285 fSaveCount = canvas->getSaveCount();
286 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287
reed@google.com4e2b3d32011-04-07 14:18:59 +0000288 if (fLooper) {
289 fLooper->init(canvas);
290 }
291 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000292
reed@android.com8a1c16f2008-12-17 15:59:43 +0000293 ~AutoDrawLooper() {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000294 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000295 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000296
reed@google.com4e2b3d32011-04-07 14:18:59 +0000297 const SkPaint& paint() const {
298 SkASSERT(fPaint);
299 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000300 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000301
reed@google.com4e2b3d32011-04-07 14:18:59 +0000302 bool next(SkDrawFilter::Type drawType);
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000303
reed@android.com8a1c16f2008-12-17 15:59:43 +0000304private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000305 SkLazyPaint fLazyPaint;
306 SkCanvas* fCanvas;
307 const SkPaint& fOrigPaint;
308 SkDrawLooper* fLooper;
309 SkDrawFilter* fFilter;
310 const SkPaint* fPaint;
311 int fSaveCount;
312 bool fDone;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000313};
314
reed@google.com4e2b3d32011-04-07 14:18:59 +0000315bool AutoDrawLooper::next(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000316 fPaint = NULL;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000317 if (fDone) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000318 return false;
319 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000320
reed@google.com632e1a22011-10-06 12:37:00 +0000321 if (fLooper || fFilter) {
322 SkPaint* paint = fLazyPaint.set(fOrigPaint);
323 if (fLooper && !fLooper->next(fCanvas, paint)) {
324 fDone = true;
325 return false;
326 }
327 if (fFilter) {
328 fFilter->filter(paint, drawType);
329 if (NULL == fLooper) {
330 // no looper means we only draw once
331 fDone = true;
332 }
333 }
334 fPaint = paint;
335 } else {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000336 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000337 fPaint = &fOrigPaint;
338 }
339
340 // call this after any possible paint modifiers
341 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000342 fPaint = NULL;
343 return false;
344 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000345 return true;
346}
347
reed@android.com8a1c16f2008-12-17 15:59:43 +0000348/* Stack helper for managing a SkBounder. In the destructor, if we were
349 given a bounder, we call its commit() method, signifying that we are
350 done accumulating bounds for that draw.
351*/
352class SkAutoBounderCommit {
353public:
354 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
355 ~SkAutoBounderCommit() {
356 if (NULL != fBounder) {
357 fBounder->commit();
358 }
359 }
360private:
361 SkBounder* fBounder;
362};
363
364#include "SkColorPriv.h"
365
366class AutoValidator {
367public:
368 AutoValidator(SkDevice* device) : fDevice(device) {}
369 ~AutoValidator() {
370#ifdef SK_DEBUG
371 const SkBitmap& bm = fDevice->accessBitmap(false);
372 if (bm.config() == SkBitmap::kARGB_4444_Config) {
373 for (int y = 0; y < bm.height(); y++) {
374 const SkPMColor16* p = bm.getAddr16(0, y);
375 for (int x = 0; x < bm.width(); x++) {
376 SkPMColor16 c = p[x];
377 SkPMColor16Assert(c);
378 }
379 }
380 }
381#endif
382 }
383private:
384 SkDevice* fDevice;
385};
386
387////////// macros to place around the internal draw calls //////////////////
388
reed@google.com4e2b3d32011-04-07 14:18:59 +0000389#define LOOPER_BEGIN(paint, type) \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000390/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000391 AutoDrawLooper looper(this, paint); \
392 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000393 SkAutoBounderCommit ac(fBounder); \
394 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000395
reed@google.com4e2b3d32011-04-07 14:18:59 +0000396#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000397
398////////////////////////////////////////////////////////////////////////////
399
400SkDevice* SkCanvas::init(SkDevice* device) {
401 fBounder = NULL;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000402 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000403 fLocalBoundsCompareTypeDirty = true;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000404 fLocalBoundsCompareTypeBW.setEmpty();
reed@android.comba09de42010-02-05 20:46:05 +0000405 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com199f1082009-06-10 02:12:47 +0000406 fLastDeviceToGainFocus = NULL;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000407 fDeviceCMDirty = false;
reed@google.com7c202932011-12-14 18:48:05 +0000408 fLayerCount = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000409
410 fMCRec = (MCRec*)fMCStack.push_back();
411 new (fMCRec) MCRec(NULL, 0);
412
413 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL));
414 fMCRec->fTopLayer = fMCRec->fLayer;
415 fMCRec->fNext = NULL;
416
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000417 fExternalMatrix.reset();
418 fExternalInverse.reset();
reed@android.comf2b98d62010-12-20 18:26:13 +0000419 fUseExternalMatrix = false;
420
reed@android.com8a1c16f2008-12-17 15:59:43 +0000421 return this->setDevice(device);
422}
423
reed@google.comcde92112011-07-06 20:00:52 +0000424SkCanvas::SkCanvas()
425: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000426 inc_canvas();
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000427
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000428 this->init(NULL);
429}
430
reed@android.com8a1c16f2008-12-17 15:59:43 +0000431SkCanvas::SkCanvas(SkDevice* device)
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000432 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000433 inc_canvas();
434
435 this->init(device);
436}
437
438SkCanvas::SkCanvas(const SkBitmap& bitmap)
439 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
440 inc_canvas();
441
reed@google.comcde92112011-07-06 20:00:52 +0000442 this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000443}
444
445SkCanvas::~SkCanvas() {
446 // free up the contents of our deque
447 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000448 SkASSERT(0 == fLayerCount);
449
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450 this->internalRestore(); // restore the last, since we're going away
451
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000452 SkSafeUnref(fBounder);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000453
reed@android.com8a1c16f2008-12-17 15:59:43 +0000454 dec_canvas();
455}
456
457SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
458 SkRefCnt_SafeAssign(fBounder, bounder);
459 return bounder;
460}
461
462SkDrawFilter* SkCanvas::getDrawFilter() const {
463 return fMCRec->fFilter;
464}
465
466SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
467 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
468 return filter;
469}
470
471///////////////////////////////////////////////////////////////////////////////
472
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000473void SkCanvas::flush() {
474 SkDevice* device = this->getDevice();
475 if (device) {
476 device->flush();
477 }
478}
479
reed@google.com210ce002011-11-01 14:24:23 +0000480SkISize SkCanvas::getDeviceSize() const {
481 SkDevice* d = this->getDevice();
482 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
483}
484
reed@android.com8a1c16f2008-12-17 15:59:43 +0000485SkDevice* SkCanvas::getDevice() const {
486 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000487 SkDeque::F2BIter iter(fMCStack);
488 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000489 SkASSERT(rec && rec->fLayer);
490 return rec->fLayer->fDevice;
491}
492
reed@google.com9266fed2011-03-30 00:18:03 +0000493SkDevice* SkCanvas::getTopDevice() const {
494 return fMCRec->fTopLayer->fDevice;
495}
496
reed@android.com8a1c16f2008-12-17 15:59:43 +0000497SkDevice* SkCanvas::setDevice(SkDevice* device) {
498 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000499 SkDeque::F2BIter iter(fMCStack);
500 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000501 SkASSERT(rec && rec->fLayer);
502 SkDevice* rootDevice = rec->fLayer->fDevice;
503
504 if (rootDevice == device) {
505 return device;
506 }
reed@google.com4b226022011-01-11 18:32:13 +0000507
reed@android.com8a1c16f2008-12-17 15:59:43 +0000508 /* Notify the devices that they are going in/out of scope, so they can do
509 things like lock/unlock their pixels, etc.
510 */
511 if (device) {
512 device->lockPixels();
513 }
514 if (rootDevice) {
515 rootDevice->unlockPixels();
516 }
517
518 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
519 rootDevice = device;
520
521 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000522
reed@android.com8a1c16f2008-12-17 15:59:43 +0000523 /* Now we update our initial region to have the bounds of the new device,
524 and then intersect all of the clips in our stack with these bounds,
525 to ensure that we can't draw outside of the device's bounds (and trash
526 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000527
reed@android.com8a1c16f2008-12-17 15:59:43 +0000528 NOTE: this is only a partial-fix, since if the new device is larger than
529 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000530 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000531 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
532 reconstruct the correct clips, so this approximation will have to do.
533 The caller really needs to restore() back to the base if they want to
534 accurately take advantage of the new device bounds.
535 */
536
537 if (NULL == device) {
reed@google.com00177082011-10-12 14:34:30 +0000538 rec->fRasterClip->setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000539 while ((rec = (MCRec*)iter.next()) != NULL) {
reed@google.com00177082011-10-12 14:34:30 +0000540 (void)rec->fRasterClip->setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000541 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000542 fClipStack.reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000543 } else {
544 // compute our total bounds for all devices
545 SkIRect bounds;
reed@google.com4b226022011-01-11 18:32:13 +0000546
reed@android.com8a1c16f2008-12-17 15:59:43 +0000547 bounds.set(0, 0, device->width(), device->height());
548
549 // now jam our 1st clip to be bounds, and intersect the rest with that
reed@google.com00177082011-10-12 14:34:30 +0000550 rec->fRasterClip->setRect(bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000551 while ((rec = (MCRec*)iter.next()) != NULL) {
reed@google.com00177082011-10-12 14:34:30 +0000552 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000553 }
554 }
555 return device;
556}
557
reed@google.comaf951c92011-06-16 19:10:39 +0000558SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) {
559 SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (bitmap)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000560 device->unref();
561 return device;
562}
563
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000564bool SkCanvas::readPixels(SkBitmap* bitmap,
565 int x, int y,
566 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000567 SkDevice* device = this->getDevice();
568 if (!device) {
569 return false;
570 }
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000571 return device->readPixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000572}
573
bsalomon@google.comc6980972011-11-02 19:57:21 +0000574bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
bsalomon@google.comace7bd52011-11-02 19:39:51 +0000575 SkDevice* device = this->getDevice();
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000576
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000577 SkIRect bounds;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000578 bounds.set(0, 0, device->width(), device->height());
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000579 if (!bounds.intersect(srcRect)) {
580 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000581 }
582
583 SkBitmap tmp;
584 tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
585 bounds.height());
586 if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
587 bitmap->swap(tmp);
588 return true;
589 } else {
reed@google.com51df9e32010-12-23 19:29:18 +0000590 return false;
591 }
reed@google.com51df9e32010-12-23 19:29:18 +0000592}
593
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000594void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
595 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000596 SkDevice* device = this->getDevice();
597 if (device) {
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000598 device->writePixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000599 }
600}
601
junov@google.com4370aed2012-01-18 16:21:08 +0000602SkCanvas* SkCanvas::canvasForDrawIter() {
603 return this;
604}
605
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606//////////////////////////////////////////////////////////////////////////////
607
reed@android.com8a1c16f2008-12-17 15:59:43 +0000608void SkCanvas::updateDeviceCMCache() {
609 if (fDeviceCMDirty) {
610 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000611 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000612 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000613
reed@android.com8a1c16f2008-12-17 15:59:43 +0000614 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000615 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000616 if (fUseExternalMatrix) {
617 layer->updateExternalMatrix(fExternalMatrix,
618 fExternalInverse);
619 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000620 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000621 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000622 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000623 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.comf2b98d62010-12-20 18:26:13 +0000624 if (fUseExternalMatrix) {
625 layer->updateExternalMatrix(fExternalMatrix,
626 fExternalInverse);
627 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000628 } while ((layer = layer->fNext) != NULL);
629 }
630 fDeviceCMDirty = false;
631 }
632}
633
reed@android.comf2b98d62010-12-20 18:26:13 +0000634void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000635 const SkRegion& clip,
636 const SkClipStack& clipStack) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000637 SkASSERT(device);
reed@android.com199f1082009-06-10 02:12:47 +0000638 if (fLastDeviceToGainFocus != device) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000639 device->gainFocus(this, matrix, clip, clipStack);
reed@android.com199f1082009-06-10 02:12:47 +0000640 fLastDeviceToGainFocus = device;
641 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000642}
643
644///////////////////////////////////////////////////////////////////////////////
645
646int SkCanvas::internalSave(SaveFlags flags) {
647 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000648
reed@android.com8a1c16f2008-12-17 15:59:43 +0000649 MCRec* newTop = (MCRec*)fMCStack.push_back();
650 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000651
reed@android.com8a1c16f2008-12-17 15:59:43 +0000652 newTop->fNext = fMCRec;
653 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000654
reed@google.com5c3d1472011-02-22 19:12:23 +0000655 fClipStack.save();
656 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
657
reed@android.com8a1c16f2008-12-17 15:59:43 +0000658 return saveCount;
659}
660
661int SkCanvas::save(SaveFlags flags) {
662 // call shared impl
663 return this->internalSave(flags);
664}
665
666#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
667#define C16MASK (1 << SkBitmap::kRGB_565_Config)
668#define C8MASK (1 << SkBitmap::kA8_Config)
669
670static SkBitmap::Config resolve_config(SkCanvas* canvas,
671 const SkIRect& bounds,
672 SkCanvas::SaveFlags flags,
673 bool* isOpaque) {
674 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
675
676#if 0
677 // loop through and union all the configs we may draw into
678 uint32_t configMask = 0;
679 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
680 {
681 SkDevice* device = canvas->getLayerDevice(i);
682 if (device->intersects(bounds))
683 configMask |= 1 << device->config();
684 }
685
686 // if the caller wants alpha or fullcolor, we can't return 565
687 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
688 SkCanvas::kHasAlphaLayer_SaveFlag))
689 configMask &= ~C16MASK;
690
691 switch (configMask) {
692 case C8MASK: // if we only have A8, return that
693 return SkBitmap::kA8_Config;
694
695 case C16MASK: // if we only have 565, return that
696 return SkBitmap::kRGB_565_Config;
697
698 default:
699 return SkBitmap::kARGB_8888_Config; // default answer
700 }
701#else
702 return SkBitmap::kARGB_8888_Config; // default answer
703#endif
704}
705
706static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
707 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
708}
709
junov@chromium.orga907ac32012-02-24 21:54:07 +0000710bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
711 SkIRect* intersection) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000712 SkIRect clipBounds;
713 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000714 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000715 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000716 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000717 if (NULL != bounds) {
718 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000719
reed@android.com8a1c16f2008-12-17 15:59:43 +0000720 this->getTotalMatrix().mapRect(&r, *bounds);
721 r.roundOut(&ir);
722 // early exit if the layer's bounds are clipped out
723 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000724 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000725 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000726 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000727 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000728 }
729 } else { // no user bounds, so just use the clip
730 ir = clipBounds;
731 }
732
reed@google.com5c3d1472011-02-22 19:12:23 +0000733 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000734
reed@android.com8a1c16f2008-12-17 15:59:43 +0000735 // early exit if the clip is now empty
736 if (bounds_affects_clip(flags) &&
reed@google.com00177082011-10-12 14:34:30 +0000737 !fMCRec->fRasterClip->op(ir, SkRegion::kIntersect_Op)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000738 return false;
739 }
740
741 if (intersection) {
742 *intersection = ir;
743 }
744 return true;
745}
746
747int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
748 SaveFlags flags) {
749 // do this before we create the layer. We don't call the public save() since
750 // that would invoke a possibly overridden virtual
751 int count = this->internalSave(flags);
752
753 fDeviceCMDirty = true;
754
755 SkIRect ir;
756 if (!this->clipRectBounds(bounds, flags, &ir)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000757 return count;
758 }
759
reed@google.comb55deeb2012-01-06 14:43:09 +0000760 // Kill the imagefilter if our device doesn't allow it
761 SkLazyPaint lazyP;
762 if (paint && paint->getImageFilter()) {
763 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
764 SkPaint* p = lazyP.set(*paint);
765 p->setImageFilter(NULL);
766 paint = p;
767 }
768 }
769
reed@android.com8a1c16f2008-12-17 15:59:43 +0000770 bool isOpaque;
771 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
772
reed@google.com76dd2772012-01-05 21:15:07 +0000773 SkDevice* device;
774 if (paint && paint->getImageFilter()) {
775 device = this->createCompatibleDevice(config, ir.width(), ir.height(),
776 isOpaque);
777 } else {
778 device = this->createLayerDevice(config, ir.width(), ir.height(),
779 isOpaque);
780 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000781 if (NULL == device) {
782 SkDebugf("Unable to create device for layer.");
783 return count;
784 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000785
reed@google.com6f8f2922011-03-04 22:27:10 +0000786 device->setOrigin(ir.fLeft, ir.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000787 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
788 device->unref();
789
790 layer->fNext = fMCRec->fTopLayer;
791 fMCRec->fLayer = layer;
792 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
793
reed@google.com7c202932011-12-14 18:48:05 +0000794 fLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000795 return count;
796}
797
798int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
799 SaveFlags flags) {
800 if (0xFF == alpha) {
801 return this->saveLayer(bounds, NULL, flags);
802 } else {
803 SkPaint tmpPaint;
804 tmpPaint.setAlpha(alpha);
805 return this->saveLayer(bounds, &tmpPaint, flags);
806 }
807}
808
809void SkCanvas::restore() {
810 // check for underflow
811 if (fMCStack.count() > 1) {
812 this->internalRestore();
813 }
814}
815
816void SkCanvas::internalRestore() {
817 SkASSERT(fMCStack.count() != 0);
818
819 fDeviceCMDirty = true;
820 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000821 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000822
reed@google.com5c3d1472011-02-22 19:12:23 +0000823 fClipStack.restore();
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000824 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000825 DeviceCM* layer = fMCRec->fLayer; // may be null
826 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
827 fMCRec->fLayer = NULL;
828
829 // now do the normal restore()
830 fMCRec->~MCRec(); // balanced in save()
831 fMCStack.pop_back();
832 fMCRec = (MCRec*)fMCStack.back();
833
834 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
835 since if we're being recorded, we don't want to record this (the
836 recorder will have already recorded the restore).
837 */
838 if (NULL != layer) {
839 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000840 const SkIPoint& origin = layer->fDevice->getOrigin();
841 this->drawDevice(layer->fDevice, origin.x(), origin.y(),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000842 layer->fPaint);
843 // reset this, since drawDevice will have set it to true
844 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000845
846 SkASSERT(fLayerCount > 0);
847 fLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000848 }
849 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000850 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000851
852 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000853}
854
855int SkCanvas::getSaveCount() const {
856 return fMCStack.count();
857}
858
859void SkCanvas::restoreToCount(int count) {
860 // sanity check
861 if (count < 1) {
862 count = 1;
863 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000864
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000865 int n = this->getSaveCount() - count;
866 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000867 this->restore();
868 }
869}
870
reed@google.com7c202932011-12-14 18:48:05 +0000871bool SkCanvas::isDrawingToLayer() const {
872 return fLayerCount > 0;
873}
874
reed@android.com8a1c16f2008-12-17 15:59:43 +0000875/////////////////////////////////////////////////////////////////////////////
876
877// can't draw it if its empty, or its too big for a fixed-point width or height
878static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000879 return bitmap.width() <= 0 || bitmap.height() <= 0
880#ifndef SK_ALLOW_OVER_32K_BITMAPS
881 || bitmap.width() > 32767 || bitmap.height() > 32767
882#endif
883 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000884}
885
reed@android.comf2b98d62010-12-20 18:26:13 +0000886void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000887 const SkMatrix& matrix, const SkPaint* paint) {
888 if (reject_bitmap(bitmap)) {
889 return;
890 }
891
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000892 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000893 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000894 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000895 }
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000896 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000897}
898
reed@google.com76dd2772012-01-05 21:15:07 +0000899#include "SkImageFilter.h"
900
901class DeviceImageFilterProxy : public SkImageFilter::Proxy {
902public:
903 DeviceImageFilterProxy(SkDevice* device) : fDevice(device) {}
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000904
reed@google.com76dd2772012-01-05 21:15:07 +0000905 virtual SkDevice* createDevice(int w, int h) SK_OVERRIDE;
906 virtual bool filterImage(SkImageFilter*, const SkBitmap& src,
907 const SkMatrix& ctm,
908 SkBitmap* result, SkIPoint* offset) SK_OVERRIDE;
909
910private:
911 SkDevice* fDevice;
912};
913
914SkDevice* DeviceImageFilterProxy::createDevice(int w, int h) {
915 return fDevice->createCompatibleDevice(SkBitmap::kARGB_8888_Config,
916 w, h, false);
917}
918
919bool DeviceImageFilterProxy::filterImage(SkImageFilter* filter,
920 const SkBitmap& src,
921 const SkMatrix& ctm,
922 SkBitmap* result,
923 SkIPoint* offset) {
924 return fDevice->filterImage(filter, src, ctm, result, offset);
925}
926
reed@google.comb55deeb2012-01-06 14:43:09 +0000927void SkCanvas::drawDevice(SkDevice* srcDev, int x, int y,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000928 const SkPaint* paint) {
929 SkPaint tmp;
930 if (NULL == paint) {
931 tmp.setDither(true);
932 paint = &tmp;
933 }
reed@google.com4b226022011-01-11 18:32:13 +0000934
reed@google.com4e2b3d32011-04-07 14:18:59 +0000935 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000936 while (iter.next()) {
reed@google.comb55deeb2012-01-06 14:43:09 +0000937 SkDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +0000938 paint = &looper.paint();
939 SkImageFilter* filter = paint->getImageFilter();
940 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
941 if (filter) {
reed@google.comb55deeb2012-01-06 14:43:09 +0000942 DeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +0000943 SkBitmap dst;
reed@google.comb55deeb2012-01-06 14:43:09 +0000944 const SkBitmap& src = srcDev->accessBitmap(false);
reed@google.com76dd2772012-01-05 21:15:07 +0000945 if (filter->filterImage(&proxy, src, *iter.fMatrix, &dst, &pos)) {
946 SkPaint tmp(*paint);
947 tmp.setImageFilter(NULL);
reed@google.comb55deeb2012-01-06 14:43:09 +0000948 dstDev->drawSprite(iter, dst, pos.x(), pos.y(), tmp);
reed@google.com76dd2772012-01-05 21:15:07 +0000949 }
950 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +0000951 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +0000952 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000953 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000954 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +0000955}
956
957/////////////////////////////////////////////////////////////////////////////
958
959bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
960 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 return fMCRec->fMatrix->preTranslate(dx, dy);
964}
965
966bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
967 fDeviceCMDirty = true;
968 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000969 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000970 return fMCRec->fMatrix->preScale(sx, sy);
971}
972
973bool SkCanvas::rotate(SkScalar degrees) {
974 fDeviceCMDirty = true;
975 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000976 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000977 return fMCRec->fMatrix->preRotate(degrees);
978}
979
980bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
981 fDeviceCMDirty = true;
982 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000983 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000984 return fMCRec->fMatrix->preSkew(sx, sy);
985}
986
987bool SkCanvas::concat(const SkMatrix& matrix) {
988 fDeviceCMDirty = true;
989 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000990 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000991 return fMCRec->fMatrix->preConcat(matrix);
992}
993
994void SkCanvas::setMatrix(const SkMatrix& matrix) {
995 fDeviceCMDirty = true;
996 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000997 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000998 *fMCRec->fMatrix = matrix;
999}
1000
1001// this is not virtual, so it must call a virtual method so that subclasses
1002// will see its action
1003void SkCanvas::resetMatrix() {
1004 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001005
reed@android.com8a1c16f2008-12-17 15:59:43 +00001006 matrix.reset();
1007 this->setMatrix(matrix);
1008}
1009
1010//////////////////////////////////////////////////////////////////////////////
1011
reed@google.comc42d35d2011-10-12 11:57:42 +00001012bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001013 AutoValidateClip avc(this);
1014
reed@android.com8a1c16f2008-12-17 15:59:43 +00001015 fDeviceCMDirty = true;
1016 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001017 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001018
1019 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +00001020 // for these simpler matrices, we can stay a rect ever after applying
1021 // the matrix. This means we don't have to a) make a path, and b) tell
1022 // the region code to scan-convert the path, only to discover that it
1023 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001024 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001025
1026 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +00001027 fClipStack.clipDevRect(r, op, doAA);
1028 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001029 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +00001030 // since we're rotate or some such thing, we convert the rect to a path
1031 // and clip against that, since it can handle any matrix. However, to
1032 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1033 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001034 SkPath path;
1035
1036 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +00001037 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001038 }
1039}
1040
reed@google.com00177082011-10-12 14:34:30 +00001041static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +00001042 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001043 // base is used to limit the size (and therefore memory allocation) of the
1044 // region that results from scan converting devPath.
1045 SkRegion base;
1046
reed@google.com819c9212011-02-23 18:56:55 +00001047 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001048 // since we are intersect, we can do better (tighter) with currRgn's
1049 // bounds, than just using the device. However, if currRgn is complex,
1050 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001051 if (currClip->isRect()) {
1052 return currClip->setPath(devPath, *currClip, doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001053 } else {
reed@google.com00177082011-10-12 14:34:30 +00001054 base.setRect(currClip->getBounds());
1055 SkRasterClip clip;
1056 clip.setPath(devPath, base, doAA);
1057 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001058 }
reed@google.com819c9212011-02-23 18:56:55 +00001059 } else {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001060 const SkDevice* device = canvas->getDevice();
1061 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001062
1063 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +00001064 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001065 } else {
reed@google.com00177082011-10-12 14:34:30 +00001066 SkRasterClip clip;
1067 clip.setPath(devPath, base, doAA);
1068 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001069 }
1070 }
1071}
1072
reed@google.comc42d35d2011-10-12 11:57:42 +00001073bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001074 AutoValidateClip avc(this);
1075
reed@android.com8a1c16f2008-12-17 15:59:43 +00001076 fDeviceCMDirty = true;
1077 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001078 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001079
1080 SkPath devPath;
1081 path.transform(*fMCRec->fMatrix, &devPath);
1082
reed@google.comfe701122011-11-08 19:41:23 +00001083 // Check if the transfomation, or the original path itself
1084 // made us empty. Note this can also happen if we contained NaN
1085 // values. computing the bounds detects this, and will set our
1086 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1087 if (devPath.getBounds().isEmpty()) {
1088 // resetting the path will remove any NaN or other wanky values
1089 // that might upset our scan converter.
1090 devPath.reset();
1091 }
1092
reed@google.com5c3d1472011-02-22 19:12:23 +00001093 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +00001094 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +00001095
reed@google.com00177082011-10-12 14:34:30 +00001096 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001097}
1098
1099bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001100 AutoValidateClip avc(this);
1101
reed@android.com8a1c16f2008-12-17 15:59:43 +00001102 fDeviceCMDirty = true;
1103 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001104 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001105
reed@google.com5c3d1472011-02-22 19:12:23 +00001106 // todo: signal fClipStack that we have a region, and therefore (I guess)
1107 // we have to ignore it, and use the region directly?
1108 fClipStack.clipDevRect(rgn.getBounds());
1109
reed@google.com00177082011-10-12 14:34:30 +00001110 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001111}
1112
reed@google.com819c9212011-02-23 18:56:55 +00001113#ifdef SK_DEBUG
1114void SkCanvas::validateClip() const {
1115 // construct clipRgn from the clipstack
1116 const SkDevice* device = this->getDevice();
1117 SkIRect ir;
1118 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001119 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001120
1121 SkClipStack::B2FIter iter(fClipStack);
1122 const SkClipStack::B2FIter::Clip* clip;
1123 while ((clip = iter.next()) != NULL) {
1124 if (clip->fPath) {
reed@google.com00177082011-10-12 14:34:30 +00001125 clipPathHelper(this, &tmpClip, *clip->fPath, clip->fOp, clip->fDoAA);
reed@google.com819c9212011-02-23 18:56:55 +00001126 } else if (clip->fRect) {
1127 clip->fRect->round(&ir);
reed@google.com00177082011-10-12 14:34:30 +00001128 tmpClip.op(ir, clip->fOp);
reed@google.com819c9212011-02-23 18:56:55 +00001129 } else {
reed@google.com00177082011-10-12 14:34:30 +00001130 tmpClip.setEmpty();
reed@google.com819c9212011-02-23 18:56:55 +00001131 }
1132 }
1133
reed@google.com6f8f2922011-03-04 22:27:10 +00001134#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001135 // now compare against the current rgn
1136 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001137 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001138#endif
reed@google.com819c9212011-02-23 18:56:55 +00001139}
1140#endif
1141
reed@google.com5c3d1472011-02-22 19:12:23 +00001142///////////////////////////////////////////////////////////////////////////////
1143
reed@android.comba09de42010-02-05 20:46:05 +00001144void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001145 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001146 SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
1147 fLocalBoundsCompareTypeBW;
1148
1149 if (!this->getClipBounds(&r, et)) {
1150 rCompare.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001151 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001152 rCompare.set(SkScalarToCompareType(r.fLeft),
1153 SkScalarToCompareType(r.fTop),
1154 SkScalarToCompareType(r.fRight),
1155 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001156 }
1157}
1158
reed@android.comd252db02009-04-01 18:31:44 +00001159/* current impl ignores edgetype, and relies on
1160 getLocalClipBoundsCompareType(), which always returns a value assuming
1161 antialiasing (worst case)
1162 */
reed@android.comba09de42010-02-05 20:46:05 +00001163bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001164
reed@google.com16078632011-12-06 18:56:37 +00001165 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001166 return true;
1167
reed@google.com00177082011-10-12 14:34:30 +00001168 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001169 return true;
1170 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001171
tomhudson@google.com8d430182011-06-06 19:11:19 +00001172 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001173 SkRect dst;
1174 fMCRec->fMatrix->mapRect(&dst, rect);
1175 SkIRect idst;
1176 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001177 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001178 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001179 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
reed@android.comd252db02009-04-01 18:31:44 +00001180
reed@android.coma380ae42009-07-21 01:17:02 +00001181 // for speed, do the most likely reject compares first
1182 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1183 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1184 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1185 return true;
1186 }
1187 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1188 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1189 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1190 return true;
1191 }
1192 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001193 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001194}
1195
1196bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +00001197 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001198}
1199
djsollen@google.com3cc0d8a2012-02-24 21:21:51 +00001200bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
1201 /* current impl ignores edgetype, and relies on
1202 getLocalClipBoundsCompareType(), which always returns a value assuming
1203 antialiasing (worst case)
1204 */
1205
1206 if (fMCRec->fRasterClip->isEmpty()) {
1207 return true;
1208 }
1209
1210 SkScalarCompareType userT = SkScalarToCompareType(top);
1211 SkScalarCompareType userB = SkScalarToCompareType(bottom);
1212
1213 // check for invalid user Y coordinates (i.e. empty)
1214 // reed: why do we need to do this check, since it slows us down?
1215 if (userT >= userB) {
1216 return true;
1217 }
1218
1219 // check if we are above or below the local clip bounds
1220 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
1221 return userT >= clipR.fBottom || userB <= clipR.fTop;
1222}
1223
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001224static inline int pinIntForScalar(int x) {
1225#ifdef SK_SCALAR_IS_FIXED
1226 if (x < SK_MinS16) {
1227 x = SK_MinS16;
1228 } else if (x > SK_MaxS16) {
1229 x = SK_MaxS16;
1230 }
1231#endif
1232 return x;
1233}
1234
reed@android.com8a1c16f2008-12-17 15:59:43 +00001235bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001236 SkIRect ibounds;
1237 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001238 return false;
1239 }
1240
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001241 SkMatrix inverse;
1242 // if we can't invert the CTM, we can't return local clip bounds
1243 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001244 if (bounds) {
1245 bounds->setEmpty();
1246 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001247 return false;
1248 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001249
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001250 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001251 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001252 // adjust it outwards if we are antialiasing
1253 int inset = (kAA_EdgeType == et);
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001254
1255 // SkRect::iset() will correctly assert if we pass a value out of range
1256 // (when SkScalar==fixed), so we pin to legal values. This does not
1257 // really returnt the correct answer, but its the best we can do given
1258 // that we've promised to return SkRect (even though we support devices
1259 // that can be larger than 32K in width or height).
1260 r.iset(pinIntForScalar(ibounds.fLeft - inset),
1261 pinIntForScalar(ibounds.fTop - inset),
1262 pinIntForScalar(ibounds.fRight + inset),
1263 pinIntForScalar(ibounds.fBottom + inset));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001264 inverse.mapRect(bounds, r);
1265 }
1266 return true;
1267}
1268
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001269bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001270 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001271 if (clip.isEmpty()) {
1272 if (bounds) {
1273 bounds->setEmpty();
1274 }
1275 return false;
1276 }
1277
1278 if (NULL != bounds) {
1279 *bounds = clip.getBounds();
1280 }
1281 return true;
1282}
1283
reed@android.com8a1c16f2008-12-17 15:59:43 +00001284const SkMatrix& SkCanvas::getTotalMatrix() const {
1285 return *fMCRec->fMatrix;
1286}
1287
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001288SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001289 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1290 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001291 return kComplex_ClipType;
1292}
1293
reed@android.com8a1c16f2008-12-17 15:59:43 +00001294const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001295 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001296}
1297
reed@google.com7d7ca792011-02-23 22:39:18 +00001298const SkClipStack& SkCanvas::getTotalClipStack() const {
1299 return fClipStack;
1300}
1301
reed@android.comf2b98d62010-12-20 18:26:13 +00001302void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1303 if (NULL == matrix || matrix->isIdentity()) {
1304 if (fUseExternalMatrix) {
1305 fDeviceCMDirty = true;
1306 }
1307 fUseExternalMatrix = false;
1308 } else {
1309 fUseExternalMatrix = true;
1310 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.com4b226022011-01-11 18:32:13 +00001311
reed@android.comf2b98d62010-12-20 18:26:13 +00001312 fExternalMatrix = *matrix;
1313 matrix->invert(&fExternalInverse);
1314 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001315}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001316
bsalomon@google.come97f0852011-06-17 13:10:25 +00001317SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
1318 int width, int height,
1319 bool isOpaque) {
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001320 SkDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001321 if (device) {
1322 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1323 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001324 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001325 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001326 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001327}
1328
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001329SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
bsalomon@google.come97f0852011-06-17 13:10:25 +00001330 int width, int height,
1331 bool isOpaque) {
1332 SkDevice* device = this->getDevice();
1333 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001334 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001335 } else {
1336 return NULL;
1337 }
1338}
1339
1340
reed@android.com8a1c16f2008-12-17 15:59:43 +00001341//////////////////////////////////////////////////////////////////////////////
1342// These are the virtual drawing methods
1343//////////////////////////////////////////////////////////////////////////////
1344
reed@google.com2a981812011-04-14 18:59:28 +00001345void SkCanvas::clear(SkColor color) {
1346 SkDrawIter iter(this);
1347
1348 while (iter.next()) {
1349 iter.fDevice->clear(color);
1350 }
1351}
1352
reed@android.com8a1c16f2008-12-17 15:59:43 +00001353void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001354 this->internalDrawPaint(paint);
1355}
1356
1357void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001358 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001359
1360 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001361 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001362 }
1363
reed@google.com4e2b3d32011-04-07 14:18:59 +00001364 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001365}
1366
1367void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1368 const SkPaint& paint) {
1369 if ((long)count <= 0) {
1370 return;
1371 }
1372
1373 SkASSERT(pts != NULL);
1374
reed@google.com4e2b3d32011-04-07 14:18:59 +00001375 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001376
reed@android.com8a1c16f2008-12-17 15:59:43 +00001377 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001378 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001379 }
reed@google.com4b226022011-01-11 18:32:13 +00001380
reed@google.com4e2b3d32011-04-07 14:18:59 +00001381 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001382}
1383
1384void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1385 if (paint.canComputeFastBounds()) {
1386 SkRect storage;
1387 if (this->quickReject(paint.computeFastBounds(r, &storage),
1388 paint2EdgeType(&paint))) {
1389 return;
1390 }
1391 }
reed@google.com4b226022011-01-11 18:32:13 +00001392
reed@google.com4e2b3d32011-04-07 14:18:59 +00001393 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001394
1395 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001396 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001397 }
1398
reed@google.com4e2b3d32011-04-07 14:18:59 +00001399 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001400}
1401
1402void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001403 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001404 SkRect storage;
1405 const SkRect& bounds = path.getBounds();
1406 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001407 paint2EdgeType(&paint))) {
1408 return;
1409 }
1410 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001411 if (path.isEmpty()) {
1412 if (path.isInverseFillType()) {
1413 this->internalDrawPaint(paint);
1414 }
1415 return;
1416 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001417
reed@google.com4e2b3d32011-04-07 14:18:59 +00001418 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001419
1420 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001421 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001422 }
1423
reed@google.com4e2b3d32011-04-07 14:18:59 +00001424 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001425}
1426
1427void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1428 const SkPaint* paint) {
1429 SkDEBUGCODE(bitmap.validate();)
1430
reed@google.com3d608122011-11-21 15:16:16 +00001431 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001432 SkRect bounds = {
1433 x, y,
1434 x + SkIntToScalar(bitmap.width()),
1435 y + SkIntToScalar(bitmap.height())
1436 };
1437 if (paint) {
1438 (void)paint->computeFastBounds(bounds, &bounds);
1439 }
1440 if (this->quickReject(bounds, paint2EdgeType(paint))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001441 return;
1442 }
1443 }
reed@google.com4b226022011-01-11 18:32:13 +00001444
reed@android.com8a1c16f2008-12-17 15:59:43 +00001445 SkMatrix matrix;
1446 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001447 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001448}
1449
reed@google.com9987ec32011-09-07 11:57:52 +00001450// this one is non-virtual, so it can be called safely by other canvas apis
1451void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1452 const SkRect& dst, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001453 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1454 return;
1455 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001456
reed@android.com8a1c16f2008-12-17 15:59:43 +00001457 // do this now, to avoid the cost of calling extract for RLE bitmaps
reed@google.com3d608122011-11-21 15:16:16 +00001458 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001459 SkRect storage;
1460 const SkRect* bounds = &dst;
1461 if (paint) {
1462 bounds = &paint->computeFastBounds(dst, &storage);
1463 }
1464 if (this->quickReject(*bounds, paint2EdgeType(paint))) {
reed@google.com3d608122011-11-21 15:16:16 +00001465 return;
1466 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001467 }
reed@google.com3d608122011-11-21 15:16:16 +00001468
reed@android.com8a1c16f2008-12-17 15:59:43 +00001469 const SkBitmap* bitmapPtr = &bitmap;
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001470
reed@android.com8a1c16f2008-12-17 15:59:43 +00001471 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001472 SkRect tmpSrc;
1473 if (src) {
1474 tmpSrc.set(*src);
1475 // if the extract process clipped off the top or left of the
1476 // original, we adjust for that here to get the position right.
1477 if (tmpSrc.fLeft > 0) {
1478 tmpSrc.fRight -= tmpSrc.fLeft;
1479 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001480 }
reed@android.com87899992009-10-16 14:48:38 +00001481 if (tmpSrc.fTop > 0) {
1482 tmpSrc.fBottom -= tmpSrc.fTop;
1483 tmpSrc.fTop = 0;
1484 }
1485 } else {
1486 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1487 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001488 }
reed@android.com87899992009-10-16 14:48:38 +00001489 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001490
reed@android.comf2b98d62010-12-20 18:26:13 +00001491 // ensure that src is "valid" before we pass it to our internal routines
1492 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1493 SkIRect tmpISrc;
1494 if (src) {
1495 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
reed@google.com2ade0862011-03-17 17:48:04 +00001496 if (!tmpISrc.intersect(*src)) {
1497 return;
1498 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001499 src = &tmpISrc;
1500 }
1501 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001502}
1503
reed@google.com9987ec32011-09-07 11:57:52 +00001504void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1505 const SkRect& dst, const SkPaint* paint) {
1506 SkDEBUGCODE(bitmap.validate();)
1507 this->internalDrawBitmapRect(bitmap, src, dst, paint);
1508}
1509
reed@android.com8a1c16f2008-12-17 15:59:43 +00001510void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1511 const SkPaint* paint) {
1512 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001513 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001514}
1515
reed@android.comf2b98d62010-12-20 18:26:13 +00001516void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1517 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001518 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001519
reed@google.com4e2b3d32011-04-07 14:18:59 +00001520 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001521
reed@android.com8a1c16f2008-12-17 15:59:43 +00001522 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001523 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001524 }
reed@android.com9b039062009-02-11 15:09:58 +00001525
reed@google.com4e2b3d32011-04-07 14:18:59 +00001526 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001527}
1528
reed@google.com9987ec32011-09-07 11:57:52 +00001529void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1530 const SkIRect& center, const SkRect& dst,
1531 const SkPaint* paint) {
reed@google.com3d608122011-11-21 15:16:16 +00001532 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001533 SkRect storage;
1534 const SkRect* bounds = &dst;
1535 if (paint) {
1536 bounds = &paint->computeFastBounds(dst, &storage);
1537 }
1538 if (this->quickReject(*bounds, paint2EdgeType(paint))) {
reed@google.com3d608122011-11-21 15:16:16 +00001539 return;
1540 }
1541 }
1542
reed@google.com9987ec32011-09-07 11:57:52 +00001543 const int32_t w = bitmap.width();
1544 const int32_t h = bitmap.height();
1545
1546 SkIRect c = center;
1547 // pin center to the bounds of the bitmap
1548 c.fLeft = SkMax32(0, center.fLeft);
1549 c.fTop = SkMax32(0, center.fTop);
1550 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1551 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1552
1553 const int32_t srcX[4] = { 0, c.fLeft, c.fRight, w };
1554 const int32_t srcY[4] = { 0, c.fTop, c.fBottom, h };
1555 SkScalar dstX[4] = {
1556 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1557 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1558 };
1559 SkScalar dstY[4] = {
1560 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1561 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1562 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001563
reed@google.com9987ec32011-09-07 11:57:52 +00001564 if (dstX[1] > dstX[2]) {
1565 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1566 dstX[2] = dstX[1];
1567 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001568
reed@google.com9987ec32011-09-07 11:57:52 +00001569 if (dstY[1] > dstY[2]) {
1570 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1571 dstY[2] = dstY[1];
1572 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001573
reed@google.com9987ec32011-09-07 11:57:52 +00001574 SkIRect s;
1575 SkRect d;
1576 for (int y = 0; y < 3; y++) {
1577 s.fTop = srcY[y];
1578 s.fBottom = srcY[y+1];
1579 d.fTop = dstY[y];
1580 d.fBottom = dstY[y+1];
1581 for (int x = 0; x < 3; x++) {
1582 s.fLeft = srcX[x];
1583 s.fRight = srcX[x+1];
1584 d.fLeft = dstX[x];
1585 d.fRight = dstX[x+1];
1586 this->internalDrawBitmapRect(bitmap, &s, d, paint);
1587 }
1588 }
1589}
1590
1591void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1592 const SkRect& dst, const SkPaint* paint) {
1593 SkDEBUGCODE(bitmap.validate();)
1594
1595 // Need a device entry-point, so gpu can use a mesh
1596 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1597}
1598
reed@android.com8a1c16f2008-12-17 15:59:43 +00001599void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1600 const SkPaint* paint) {
1601 SkDEBUGCODE(bitmap.validate();)
reed@google.com4b226022011-01-11 18:32:13 +00001602
reed@android.com8a1c16f2008-12-17 15:59:43 +00001603 if (reject_bitmap(bitmap)) {
1604 return;
1605 }
reed@google.com4b226022011-01-11 18:32:13 +00001606
reed@android.com8a1c16f2008-12-17 15:59:43 +00001607 SkPaint tmp;
1608 if (NULL == paint) {
1609 paint = &tmp;
1610 }
reed@google.com4b226022011-01-11 18:32:13 +00001611
reed@google.com4e2b3d32011-04-07 14:18:59 +00001612 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_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->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +00001616 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001617 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001618 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001619}
1620
reed@google.comf67e4cf2011-03-15 20:56:58 +00001621class SkDeviceFilteredPaint {
1622public:
1623 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1624 SkDevice::TextFlags flags;
1625 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001626 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001627 newPaint->setFlags(flags.fFlags);
1628 newPaint->setHinting(flags.fHinting);
1629 fPaint = newPaint;
1630 } else {
1631 fPaint = &paint;
1632 }
1633 }
1634
reed@google.comf67e4cf2011-03-15 20:56:58 +00001635 const SkPaint& paint() const { return *fPaint; }
1636
1637private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001638 const SkPaint* fPaint;
1639 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001640};
1641
bungeman@google.com52c748b2011-08-22 21:30:43 +00001642void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1643 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001644 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001645 draw.fDevice->drawRect(draw, r, paint);
1646 } else {
1647 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001648 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001649 draw.fDevice->drawRect(draw, r, p);
1650 }
1651}
1652
1653void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1654 const char text[], size_t byteLength,
1655 SkScalar x, SkScalar y) {
1656 SkASSERT(byteLength == 0 || text != NULL);
1657
1658 // nothing to draw
1659 if (text == NULL || byteLength == 0 ||
1660 draw.fClip->isEmpty() ||
1661 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1662 return;
1663 }
1664
1665 SkScalar width = 0;
1666 SkPoint start;
1667
1668 start.set(0, 0); // to avoid warning
1669 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1670 SkPaint::kStrikeThruText_Flag)) {
1671 width = paint.measureText(text, byteLength);
1672
1673 SkScalar offsetX = 0;
1674 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1675 offsetX = SkScalarHalf(width);
1676 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1677 offsetX = width;
1678 }
1679 start.set(x - offsetX, y);
1680 }
1681
1682 if (0 == width) {
1683 return;
1684 }
1685
1686 uint32_t flags = paint.getFlags();
1687
1688 if (flags & (SkPaint::kUnderlineText_Flag |
1689 SkPaint::kStrikeThruText_Flag)) {
1690 SkScalar textSize = paint.getTextSize();
1691 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1692 SkRect r;
1693
1694 r.fLeft = start.fX;
1695 r.fRight = start.fX + width;
1696
1697 if (flags & SkPaint::kUnderlineText_Flag) {
1698 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1699 start.fY);
1700 r.fTop = offset;
1701 r.fBottom = offset + height;
1702 DrawRect(draw, paint, r, textSize);
1703 }
1704 if (flags & SkPaint::kStrikeThruText_Flag) {
1705 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1706 start.fY);
1707 r.fTop = offset;
1708 r.fBottom = offset + height;
1709 DrawRect(draw, paint, r, textSize);
1710 }
1711 }
1712}
1713
reed@android.com8a1c16f2008-12-17 15:59:43 +00001714void SkCanvas::drawText(const void* text, size_t byteLength,
1715 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001716 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001717
1718 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001719 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001720 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001721 DrawTextDecorations(iter, dfp.paint(),
1722 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001723 }
1724
reed@google.com4e2b3d32011-04-07 14:18:59 +00001725 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001726}
1727
1728void SkCanvas::drawPosText(const void* text, size_t byteLength,
1729 const SkPoint pos[], const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001730 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001731
reed@android.com8a1c16f2008-12-17 15:59:43 +00001732 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001733 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001734 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001735 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001736 }
reed@google.com4b226022011-01-11 18:32:13 +00001737
reed@google.com4e2b3d32011-04-07 14:18:59 +00001738 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001739}
1740
1741void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1742 const SkScalar xpos[], SkScalar constY,
1743 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001744 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001745
reed@android.com8a1c16f2008-12-17 15:59:43 +00001746 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001747 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001748 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001749 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001750 }
reed@google.com4b226022011-01-11 18:32:13 +00001751
reed@google.com4e2b3d32011-04-07 14:18:59 +00001752 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001753}
1754
1755void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1756 const SkPath& path, const SkMatrix* matrix,
1757 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001758 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001759
1760 while (iter.next()) {
1761 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001762 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001763 }
1764
reed@google.com4e2b3d32011-04-07 14:18:59 +00001765 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001766}
1767
djsollen@google.com56c69772011-11-08 19:00:26 +00001768#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001769void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1770 const SkPoint pos[], const SkPaint& paint,
1771 const SkPath& path, const SkMatrix* matrix) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001772 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001773
1774 while (iter.next()) {
1775 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001776 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001777 }
1778
reed@google.com4e2b3d32011-04-07 14:18:59 +00001779 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001780}
1781#endif
1782
reed@android.com8a1c16f2008-12-17 15:59:43 +00001783void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1784 const SkPoint verts[], const SkPoint texs[],
1785 const SkColor colors[], SkXfermode* xmode,
1786 const uint16_t indices[], int indexCount,
1787 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001788 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001789
reed@android.com8a1c16f2008-12-17 15:59:43 +00001790 while (iter.next()) {
1791 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001792 colors, xmode, indices, indexCount,
1793 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001794 }
reed@google.com4b226022011-01-11 18:32:13 +00001795
reed@google.com4e2b3d32011-04-07 14:18:59 +00001796 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001797}
1798
reed@android.comcb608442009-12-04 21:32:27 +00001799void SkCanvas::drawData(const void* data, size_t length) {
1800 // do nothing. Subclasses may do something with the data
1801}
1802
reed@android.com8a1c16f2008-12-17 15:59:43 +00001803//////////////////////////////////////////////////////////////////////////////
1804// These methods are NOT virtual, and therefore must call back into virtual
1805// methods, rather than actually drawing themselves.
1806//////////////////////////////////////////////////////////////////////////////
1807
1808void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001809 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001810 SkPaint paint;
1811
1812 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001813 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001814 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001815 }
1816 this->drawPaint(paint);
1817}
1818
reed@android.com845fdac2009-06-23 03:01:32 +00001819void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001820 SkPaint paint;
1821
1822 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001823 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001824 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001825 }
1826 this->drawPaint(paint);
1827}
1828
1829void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1830 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001831
reed@android.com8a1c16f2008-12-17 15:59:43 +00001832 pt.set(x, y);
1833 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1834}
1835
1836void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1837 SkPoint pt;
1838 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001839
reed@android.com8a1c16f2008-12-17 15:59:43 +00001840 pt.set(x, y);
1841 paint.setColor(color);
1842 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1843}
1844
1845void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1846 const SkPaint& paint) {
1847 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001848
reed@android.com8a1c16f2008-12-17 15:59:43 +00001849 pts[0].set(x0, y0);
1850 pts[1].set(x1, y1);
1851 this->drawPoints(kLines_PointMode, 2, pts, paint);
1852}
1853
1854void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1855 SkScalar right, SkScalar bottom,
1856 const SkPaint& paint) {
1857 SkRect r;
1858
1859 r.set(left, top, right, bottom);
1860 this->drawRect(r, paint);
1861}
1862
1863void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1864 const SkPaint& paint) {
1865 if (radius < 0) {
1866 radius = 0;
1867 }
1868
1869 SkRect r;
1870 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001871
reed@android.com8a1c16f2008-12-17 15:59:43 +00001872 if (paint.canComputeFastBounds()) {
1873 SkRect storage;
1874 if (this->quickReject(paint.computeFastBounds(r, &storage),
1875 paint2EdgeType(&paint))) {
1876 return;
1877 }
1878 }
reed@google.com4b226022011-01-11 18:32:13 +00001879
reed@android.com8a1c16f2008-12-17 15:59:43 +00001880 SkPath path;
1881 path.addOval(r);
1882 this->drawPath(path, paint);
1883}
1884
1885void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1886 const SkPaint& paint) {
1887 if (rx > 0 && ry > 0) {
1888 if (paint.canComputeFastBounds()) {
1889 SkRect storage;
1890 if (this->quickReject(paint.computeFastBounds(r, &storage),
1891 paint2EdgeType(&paint))) {
1892 return;
1893 }
1894 }
1895
1896 SkPath path;
1897 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1898 this->drawPath(path, paint);
1899 } else {
1900 this->drawRect(r, paint);
1901 }
1902}
1903
1904void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1905 if (paint.canComputeFastBounds()) {
1906 SkRect storage;
1907 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1908 paint2EdgeType(&paint))) {
1909 return;
1910 }
1911 }
1912
1913 SkPath path;
1914 path.addOval(oval);
1915 this->drawPath(path, paint);
1916}
1917
1918void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1919 SkScalar sweepAngle, bool useCenter,
1920 const SkPaint& paint) {
1921 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1922 this->drawOval(oval, paint);
1923 } else {
1924 SkPath path;
1925 if (useCenter) {
1926 path.moveTo(oval.centerX(), oval.centerY());
1927 }
1928 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1929 if (useCenter) {
1930 path.close();
1931 }
1932 this->drawPath(path, paint);
1933 }
1934}
1935
1936void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1937 const SkPath& path, SkScalar hOffset,
1938 SkScalar vOffset, const SkPaint& paint) {
1939 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001940
reed@android.com8a1c16f2008-12-17 15:59:43 +00001941 matrix.setTranslate(hOffset, vOffset);
1942 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1943}
1944
reed@android.comf76bacf2009-05-13 14:00:33 +00001945///////////////////////////////////////////////////////////////////////////////
1946
reed@android.com8a1c16f2008-12-17 15:59:43 +00001947void SkCanvas::drawPicture(SkPicture& picture) {
1948 int saveCount = save();
1949 picture.draw(this);
1950 restoreToCount(saveCount);
1951}
1952
reed@android.com8a1c16f2008-12-17 15:59:43 +00001953///////////////////////////////////////////////////////////////////////////////
1954///////////////////////////////////////////////////////////////////////////////
1955
1956SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00001957 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001958
1959 SkASSERT(canvas);
1960
1961 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
1962 fDone = !fImpl->next();
1963}
1964
1965SkCanvas::LayerIter::~LayerIter() {
1966 fImpl->~SkDrawIter();
1967}
1968
1969void SkCanvas::LayerIter::next() {
1970 fDone = !fImpl->next();
1971}
1972
1973SkDevice* SkCanvas::LayerIter::device() const {
1974 return fImpl->getDevice();
1975}
1976
1977const SkMatrix& SkCanvas::LayerIter::matrix() const {
1978 return fImpl->getMatrix();
1979}
1980
1981const SkPaint& SkCanvas::LayerIter::paint() const {
1982 const SkPaint* paint = fImpl->getPaint();
1983 if (NULL == paint) {
1984 paint = &fDefaultPaint;
1985 }
1986 return *paint;
1987}
1988
1989const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
1990int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
1991int SkCanvas::LayerIter::y() const { return fImpl->getY(); }