blob: 6e061c200583e84a6b264b25edc99f66d7865d8d [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
710int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
711 SaveFlags flags) {
712 // do this before we create the layer. We don't call the public save() since
713 // that would invoke a possibly overridden virtual
714 int count = this->internalSave(flags);
715
716 fDeviceCMDirty = true;
717
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000718 SkIRect clipBounds;
719 if (!this->getClipDeviceBounds(&clipBounds)) {
reed@android.comf2b98d62010-12-20 18:26:13 +0000720 return count;
721 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000722
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000723 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000724 if (NULL != bounds) {
725 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000726
reed@android.com8a1c16f2008-12-17 15:59:43 +0000727 this->getTotalMatrix().mapRect(&r, *bounds);
728 r.roundOut(&ir);
729 // early exit if the layer's bounds are clipped out
730 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000731 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000732 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000733 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000734 return count;
735 }
736 } else { // no user bounds, so just use the clip
737 ir = clipBounds;
738 }
739
reed@google.com5c3d1472011-02-22 19:12:23 +0000740 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000741 // early exit if the clip is now empty
742 if (bounds_affects_clip(flags) &&
reed@google.com00177082011-10-12 14:34:30 +0000743 !fMCRec->fRasterClip->op(ir, SkRegion::kIntersect_Op)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000744 return count;
745 }
746
reed@google.comb55deeb2012-01-06 14:43:09 +0000747 // Kill the imagefilter if our device doesn't allow it
748 SkLazyPaint lazyP;
749 if (paint && paint->getImageFilter()) {
750 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
751 SkPaint* p = lazyP.set(*paint);
752 p->setImageFilter(NULL);
753 paint = p;
754 }
755 }
756
reed@android.com8a1c16f2008-12-17 15:59:43 +0000757 bool isOpaque;
758 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
759
reed@google.com76dd2772012-01-05 21:15:07 +0000760 SkDevice* device;
761 if (paint && paint->getImageFilter()) {
762 device = this->createCompatibleDevice(config, ir.width(), ir.height(),
763 isOpaque);
764 } else {
765 device = this->createLayerDevice(config, ir.width(), ir.height(),
766 isOpaque);
767 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000768 if (NULL == device) {
769 SkDebugf("Unable to create device for layer.");
770 return count;
771 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000772
reed@google.com6f8f2922011-03-04 22:27:10 +0000773 device->setOrigin(ir.fLeft, ir.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000774 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
775 device->unref();
776
777 layer->fNext = fMCRec->fTopLayer;
778 fMCRec->fLayer = layer;
779 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
780
reed@google.com7c202932011-12-14 18:48:05 +0000781 fLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000782 return count;
783}
784
785int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
786 SaveFlags flags) {
787 if (0xFF == alpha) {
788 return this->saveLayer(bounds, NULL, flags);
789 } else {
790 SkPaint tmpPaint;
791 tmpPaint.setAlpha(alpha);
792 return this->saveLayer(bounds, &tmpPaint, flags);
793 }
794}
795
796void SkCanvas::restore() {
797 // check for underflow
798 if (fMCStack.count() > 1) {
799 this->internalRestore();
800 }
801}
802
803void SkCanvas::internalRestore() {
804 SkASSERT(fMCStack.count() != 0);
805
806 fDeviceCMDirty = true;
807 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000808 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000809
reed@google.com5c3d1472011-02-22 19:12:23 +0000810 fClipStack.restore();
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000811 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000812 DeviceCM* layer = fMCRec->fLayer; // may be null
813 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
814 fMCRec->fLayer = NULL;
815
816 // now do the normal restore()
817 fMCRec->~MCRec(); // balanced in save()
818 fMCStack.pop_back();
819 fMCRec = (MCRec*)fMCStack.back();
820
821 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
822 since if we're being recorded, we don't want to record this (the
823 recorder will have already recorded the restore).
824 */
825 if (NULL != layer) {
826 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000827 const SkIPoint& origin = layer->fDevice->getOrigin();
828 this->drawDevice(layer->fDevice, origin.x(), origin.y(),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000829 layer->fPaint);
830 // reset this, since drawDevice will have set it to true
831 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000832
833 SkASSERT(fLayerCount > 0);
834 fLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000835 }
836 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000837 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000838
839 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000840}
841
842int SkCanvas::getSaveCount() const {
843 return fMCStack.count();
844}
845
846void SkCanvas::restoreToCount(int count) {
847 // sanity check
848 if (count < 1) {
849 count = 1;
850 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000851
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000852 int n = this->getSaveCount() - count;
853 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000854 this->restore();
855 }
856}
857
reed@google.com7c202932011-12-14 18:48:05 +0000858bool SkCanvas::isDrawingToLayer() const {
859 return fLayerCount > 0;
860}
861
reed@android.com8a1c16f2008-12-17 15:59:43 +0000862/////////////////////////////////////////////////////////////////////////////
863
864// can't draw it if its empty, or its too big for a fixed-point width or height
865static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000866 return bitmap.width() <= 0 || bitmap.height() <= 0
867#ifndef SK_ALLOW_OVER_32K_BITMAPS
868 || bitmap.width() > 32767 || bitmap.height() > 32767
869#endif
870 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000871}
872
reed@android.comf2b98d62010-12-20 18:26:13 +0000873void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000874 const SkMatrix& matrix, const SkPaint* paint) {
875 if (reject_bitmap(bitmap)) {
876 return;
877 }
878
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000879 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000880 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000881 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000882 }
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000883 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000884}
885
reed@google.com76dd2772012-01-05 21:15:07 +0000886#include "SkImageFilter.h"
887
888class DeviceImageFilterProxy : public SkImageFilter::Proxy {
889public:
890 DeviceImageFilterProxy(SkDevice* device) : fDevice(device) {}
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000891
reed@google.com76dd2772012-01-05 21:15:07 +0000892 virtual SkDevice* createDevice(int w, int h) SK_OVERRIDE;
893 virtual bool filterImage(SkImageFilter*, const SkBitmap& src,
894 const SkMatrix& ctm,
895 SkBitmap* result, SkIPoint* offset) SK_OVERRIDE;
896
897private:
898 SkDevice* fDevice;
899};
900
901SkDevice* DeviceImageFilterProxy::createDevice(int w, int h) {
902 return fDevice->createCompatibleDevice(SkBitmap::kARGB_8888_Config,
903 w, h, false);
904}
905
906bool DeviceImageFilterProxy::filterImage(SkImageFilter* filter,
907 const SkBitmap& src,
908 const SkMatrix& ctm,
909 SkBitmap* result,
910 SkIPoint* offset) {
911 return fDevice->filterImage(filter, src, ctm, result, offset);
912}
913
reed@google.comb55deeb2012-01-06 14:43:09 +0000914void SkCanvas::drawDevice(SkDevice* srcDev, int x, int y,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000915 const SkPaint* paint) {
916 SkPaint tmp;
917 if (NULL == paint) {
918 tmp.setDither(true);
919 paint = &tmp;
920 }
reed@google.com4b226022011-01-11 18:32:13 +0000921
reed@google.com4e2b3d32011-04-07 14:18:59 +0000922 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000923 while (iter.next()) {
reed@google.comb55deeb2012-01-06 14:43:09 +0000924 SkDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +0000925 paint = &looper.paint();
926 SkImageFilter* filter = paint->getImageFilter();
927 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
928 if (filter) {
reed@google.comb55deeb2012-01-06 14:43:09 +0000929 DeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +0000930 SkBitmap dst;
reed@google.comb55deeb2012-01-06 14:43:09 +0000931 const SkBitmap& src = srcDev->accessBitmap(false);
reed@google.com76dd2772012-01-05 21:15:07 +0000932 if (filter->filterImage(&proxy, src, *iter.fMatrix, &dst, &pos)) {
933 SkPaint tmp(*paint);
934 tmp.setImageFilter(NULL);
reed@google.comb55deeb2012-01-06 14:43:09 +0000935 dstDev->drawSprite(iter, dst, pos.x(), pos.y(), tmp);
reed@google.com76dd2772012-01-05 21:15:07 +0000936 }
937 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +0000938 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +0000939 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000940 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000941 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +0000942}
943
944/////////////////////////////////////////////////////////////////////////////
945
946bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
947 fDeviceCMDirty = true;
948 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000949 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000950 return fMCRec->fMatrix->preTranslate(dx, dy);
951}
952
953bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
954 fDeviceCMDirty = true;
955 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000956 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000957 return fMCRec->fMatrix->preScale(sx, sy);
958}
959
960bool SkCanvas::rotate(SkScalar degrees) {
961 fDeviceCMDirty = true;
962 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000963 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000964 return fMCRec->fMatrix->preRotate(degrees);
965}
966
967bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
968 fDeviceCMDirty = true;
969 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000970 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000971 return fMCRec->fMatrix->preSkew(sx, sy);
972}
973
974bool SkCanvas::concat(const SkMatrix& matrix) {
975 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 return fMCRec->fMatrix->preConcat(matrix);
979}
980
981void SkCanvas::setMatrix(const SkMatrix& matrix) {
982 fDeviceCMDirty = true;
983 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000984 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000985 *fMCRec->fMatrix = matrix;
986}
987
988// this is not virtual, so it must call a virtual method so that subclasses
989// will see its action
990void SkCanvas::resetMatrix() {
991 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +0000992
reed@android.com8a1c16f2008-12-17 15:59:43 +0000993 matrix.reset();
994 this->setMatrix(matrix);
995}
996
997//////////////////////////////////////////////////////////////////////////////
998
reed@google.comc42d35d2011-10-12 11:57:42 +0000999bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001000 AutoValidateClip avc(this);
1001
reed@android.com8a1c16f2008-12-17 15:59:43 +00001002 fDeviceCMDirty = true;
1003 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001004 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001005
1006 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +00001007 // for these simpler matrices, we can stay a rect ever after applying
1008 // the matrix. This means we don't have to a) make a path, and b) tell
1009 // the region code to scan-convert the path, only to discover that it
1010 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001011 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001012
1013 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +00001014 fClipStack.clipDevRect(r, op, doAA);
1015 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001016 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +00001017 // since we're rotate or some such thing, we convert the rect to a path
1018 // and clip against that, since it can handle any matrix. However, to
1019 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1020 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001021 SkPath path;
1022
1023 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +00001024 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001025 }
1026}
1027
reed@google.com00177082011-10-12 14:34:30 +00001028static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +00001029 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001030 // base is used to limit the size (and therefore memory allocation) of the
1031 // region that results from scan converting devPath.
1032 SkRegion base;
1033
reed@google.com819c9212011-02-23 18:56:55 +00001034 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001035 // since we are intersect, we can do better (tighter) with currRgn's
1036 // bounds, than just using the device. However, if currRgn is complex,
1037 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001038 if (currClip->isRect()) {
1039 return currClip->setPath(devPath, *currClip, doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001040 } else {
reed@google.com00177082011-10-12 14:34:30 +00001041 base.setRect(currClip->getBounds());
1042 SkRasterClip clip;
1043 clip.setPath(devPath, base, doAA);
1044 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001045 }
reed@google.com819c9212011-02-23 18:56:55 +00001046 } else {
reed@google.com819c9212011-02-23 18:56:55 +00001047 const SkBitmap& bm = canvas->getDevice()->accessBitmap(false);
1048 base.setRect(0, 0, bm.width(), bm.height());
1049
1050 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +00001051 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001052 } else {
reed@google.com00177082011-10-12 14:34:30 +00001053 SkRasterClip clip;
1054 clip.setPath(devPath, base, doAA);
1055 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001056 }
1057 }
1058}
1059
reed@google.comc42d35d2011-10-12 11:57:42 +00001060bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001061 AutoValidateClip avc(this);
1062
reed@android.com8a1c16f2008-12-17 15:59:43 +00001063 fDeviceCMDirty = true;
1064 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001065 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001066
1067 SkPath devPath;
1068 path.transform(*fMCRec->fMatrix, &devPath);
1069
reed@google.comfe701122011-11-08 19:41:23 +00001070 // Check if the transfomation, or the original path itself
1071 // made us empty. Note this can also happen if we contained NaN
1072 // values. computing the bounds detects this, and will set our
1073 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1074 if (devPath.getBounds().isEmpty()) {
1075 // resetting the path will remove any NaN or other wanky values
1076 // that might upset our scan converter.
1077 devPath.reset();
1078 }
1079
reed@google.com5c3d1472011-02-22 19:12:23 +00001080 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +00001081 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +00001082
reed@google.com00177082011-10-12 14:34:30 +00001083 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001084}
1085
1086bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001087 AutoValidateClip avc(this);
1088
reed@android.com8a1c16f2008-12-17 15:59:43 +00001089 fDeviceCMDirty = true;
1090 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001091 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001092
reed@google.com5c3d1472011-02-22 19:12:23 +00001093 // todo: signal fClipStack that we have a region, and therefore (I guess)
1094 // we have to ignore it, and use the region directly?
1095 fClipStack.clipDevRect(rgn.getBounds());
1096
reed@google.com00177082011-10-12 14:34:30 +00001097 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001098}
1099
reed@google.com819c9212011-02-23 18:56:55 +00001100#ifdef SK_DEBUG
1101void SkCanvas::validateClip() const {
1102 // construct clipRgn from the clipstack
1103 const SkDevice* device = this->getDevice();
1104 SkIRect ir;
1105 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001106 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001107
1108 SkClipStack::B2FIter iter(fClipStack);
1109 const SkClipStack::B2FIter::Clip* clip;
1110 while ((clip = iter.next()) != NULL) {
1111 if (clip->fPath) {
reed@google.com00177082011-10-12 14:34:30 +00001112 clipPathHelper(this, &tmpClip, *clip->fPath, clip->fOp, clip->fDoAA);
reed@google.com819c9212011-02-23 18:56:55 +00001113 } else if (clip->fRect) {
1114 clip->fRect->round(&ir);
reed@google.com00177082011-10-12 14:34:30 +00001115 tmpClip.op(ir, clip->fOp);
reed@google.com819c9212011-02-23 18:56:55 +00001116 } else {
reed@google.com00177082011-10-12 14:34:30 +00001117 tmpClip.setEmpty();
reed@google.com819c9212011-02-23 18:56:55 +00001118 }
1119 }
1120
reed@google.com6f8f2922011-03-04 22:27:10 +00001121#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001122 // now compare against the current rgn
1123 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001124 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001125#endif
reed@google.com819c9212011-02-23 18:56:55 +00001126}
1127#endif
1128
reed@google.com5c3d1472011-02-22 19:12:23 +00001129///////////////////////////////////////////////////////////////////////////////
1130
reed@android.comba09de42010-02-05 20:46:05 +00001131void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001132 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001133 SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
1134 fLocalBoundsCompareTypeBW;
1135
1136 if (!this->getClipBounds(&r, et)) {
1137 rCompare.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001138 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001139 rCompare.set(SkScalarToCompareType(r.fLeft),
1140 SkScalarToCompareType(r.fTop),
1141 SkScalarToCompareType(r.fRight),
1142 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001143 }
1144}
1145
reed@android.comd252db02009-04-01 18:31:44 +00001146/* current impl ignores edgetype, and relies on
1147 getLocalClipBoundsCompareType(), which always returns a value assuming
1148 antialiasing (worst case)
1149 */
reed@android.comba09de42010-02-05 20:46:05 +00001150bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001151
reed@google.com16078632011-12-06 18:56:37 +00001152 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001153 return true;
1154
reed@google.com00177082011-10-12 14:34:30 +00001155 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001156 return true;
1157 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001158
tomhudson@google.com8d430182011-06-06 19:11:19 +00001159 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001160 SkRect dst;
1161 fMCRec->fMatrix->mapRect(&dst, rect);
1162 SkIRect idst;
1163 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001164 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001165 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001166 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
reed@android.comd252db02009-04-01 18:31:44 +00001167
reed@android.coma380ae42009-07-21 01:17:02 +00001168 // for speed, do the most likely reject compares first
1169 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1170 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1171 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1172 return true;
1173 }
1174 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1175 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1176 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1177 return true;
1178 }
1179 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001180 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001181}
1182
1183bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +00001184 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001185}
1186
1187bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
1188 /* current impl ignores edgetype, and relies on
1189 getLocalClipBoundsCompareType(), which always returns a value assuming
1190 antialiasing (worst case)
1191 */
1192
reed@google.com00177082011-10-12 14:34:30 +00001193 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001194 return true;
1195 }
reed@google.com4b226022011-01-11 18:32:13 +00001196
reed@android.comaefd2bc2009-03-30 21:02:14 +00001197 SkScalarCompareType userT = SkScalarToCompareType(top);
1198 SkScalarCompareType userB = SkScalarToCompareType(bottom);
reed@google.com4b226022011-01-11 18:32:13 +00001199
reed@android.com8a1c16f2008-12-17 15:59:43 +00001200 // check for invalid user Y coordinates (i.e. empty)
reed@android.comd252db02009-04-01 18:31:44 +00001201 // reed: why do we need to do this check, since it slows us down?
reed@android.com8a1c16f2008-12-17 15:59:43 +00001202 if (userT >= userB) {
1203 return true;
1204 }
reed@google.com4b226022011-01-11 18:32:13 +00001205
reed@android.com8a1c16f2008-12-17 15:59:43 +00001206 // check if we are above or below the local clip bounds
1207 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
1208 return userT >= clipR.fBottom || userB <= clipR.fTop;
1209}
1210
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001211static inline int pinIntForScalar(int x) {
1212#ifdef SK_SCALAR_IS_FIXED
1213 if (x < SK_MinS16) {
1214 x = SK_MinS16;
1215 } else if (x > SK_MaxS16) {
1216 x = SK_MaxS16;
1217 }
1218#endif
1219 return x;
1220}
1221
reed@android.com8a1c16f2008-12-17 15:59:43 +00001222bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001223 SkIRect ibounds;
1224 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001225 return false;
1226 }
1227
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001228 SkMatrix inverse;
1229 // if we can't invert the CTM, we can't return local clip bounds
1230 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001231 if (bounds) {
1232 bounds->setEmpty();
1233 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001234 return false;
1235 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001236
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001237 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001238 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001239 // adjust it outwards if we are antialiasing
1240 int inset = (kAA_EdgeType == et);
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001241
1242 // SkRect::iset() will correctly assert if we pass a value out of range
1243 // (when SkScalar==fixed), so we pin to legal values. This does not
1244 // really returnt the correct answer, but its the best we can do given
1245 // that we've promised to return SkRect (even though we support devices
1246 // that can be larger than 32K in width or height).
1247 r.iset(pinIntForScalar(ibounds.fLeft - inset),
1248 pinIntForScalar(ibounds.fTop - inset),
1249 pinIntForScalar(ibounds.fRight + inset),
1250 pinIntForScalar(ibounds.fBottom + inset));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001251 inverse.mapRect(bounds, r);
1252 }
1253 return true;
1254}
1255
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001256bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001257 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001258 if (clip.isEmpty()) {
1259 if (bounds) {
1260 bounds->setEmpty();
1261 }
1262 return false;
1263 }
1264
1265 if (NULL != bounds) {
1266 *bounds = clip.getBounds();
1267 }
1268 return true;
1269}
1270
reed@android.com8a1c16f2008-12-17 15:59:43 +00001271const SkMatrix& SkCanvas::getTotalMatrix() const {
1272 return *fMCRec->fMatrix;
1273}
1274
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001275SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001276 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1277 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001278 return kComplex_ClipType;
1279}
1280
reed@android.com8a1c16f2008-12-17 15:59:43 +00001281const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001282 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001283}
1284
reed@google.com7d7ca792011-02-23 22:39:18 +00001285const SkClipStack& SkCanvas::getTotalClipStack() const {
1286 return fClipStack;
1287}
1288
reed@android.comf2b98d62010-12-20 18:26:13 +00001289void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1290 if (NULL == matrix || matrix->isIdentity()) {
1291 if (fUseExternalMatrix) {
1292 fDeviceCMDirty = true;
1293 }
1294 fUseExternalMatrix = false;
1295 } else {
1296 fUseExternalMatrix = true;
1297 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.com4b226022011-01-11 18:32:13 +00001298
reed@android.comf2b98d62010-12-20 18:26:13 +00001299 fExternalMatrix = *matrix;
1300 matrix->invert(&fExternalInverse);
1301 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001302}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001303
bsalomon@google.come97f0852011-06-17 13:10:25 +00001304SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
1305 int width, int height,
1306 bool isOpaque) {
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001307 SkDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001308 if (device) {
1309 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1310 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001311 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001312 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001313 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001314}
1315
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001316SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
bsalomon@google.come97f0852011-06-17 13:10:25 +00001317 int width, int height,
1318 bool isOpaque) {
1319 SkDevice* device = this->getDevice();
1320 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001321 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001322 } else {
1323 return NULL;
1324 }
1325}
1326
1327
reed@android.com8a1c16f2008-12-17 15:59:43 +00001328//////////////////////////////////////////////////////////////////////////////
1329// These are the virtual drawing methods
1330//////////////////////////////////////////////////////////////////////////////
1331
reed@google.com2a981812011-04-14 18:59:28 +00001332void SkCanvas::clear(SkColor color) {
1333 SkDrawIter iter(this);
1334
1335 while (iter.next()) {
1336 iter.fDevice->clear(color);
1337 }
1338}
1339
reed@android.com8a1c16f2008-12-17 15:59:43 +00001340void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001341 this->internalDrawPaint(paint);
1342}
1343
1344void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001345 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001346
1347 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001348 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001349 }
1350
reed@google.com4e2b3d32011-04-07 14:18:59 +00001351 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001352}
1353
1354void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1355 const SkPaint& paint) {
1356 if ((long)count <= 0) {
1357 return;
1358 }
1359
1360 SkASSERT(pts != NULL);
1361
reed@google.com4e2b3d32011-04-07 14:18:59 +00001362 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001363
reed@android.com8a1c16f2008-12-17 15:59:43 +00001364 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001365 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001366 }
reed@google.com4b226022011-01-11 18:32:13 +00001367
reed@google.com4e2b3d32011-04-07 14:18:59 +00001368 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001369}
1370
1371void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1372 if (paint.canComputeFastBounds()) {
1373 SkRect storage;
1374 if (this->quickReject(paint.computeFastBounds(r, &storage),
1375 paint2EdgeType(&paint))) {
1376 return;
1377 }
1378 }
reed@google.com4b226022011-01-11 18:32:13 +00001379
reed@google.com4e2b3d32011-04-07 14:18:59 +00001380 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001381
1382 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001383 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001384 }
1385
reed@google.com4e2b3d32011-04-07 14:18:59 +00001386 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001387}
1388
1389void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001390 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001391 SkRect storage;
1392 const SkRect& bounds = path.getBounds();
1393 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001394 paint2EdgeType(&paint))) {
1395 return;
1396 }
1397 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001398 if (path.isEmpty()) {
1399 if (path.isInverseFillType()) {
1400 this->internalDrawPaint(paint);
1401 }
1402 return;
1403 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001404
reed@google.com4e2b3d32011-04-07 14:18:59 +00001405 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001406
1407 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001408 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001409 }
1410
reed@google.com4e2b3d32011-04-07 14:18:59 +00001411 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001412}
1413
1414void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1415 const SkPaint* paint) {
1416 SkDEBUGCODE(bitmap.validate();)
1417
reed@google.com3d608122011-11-21 15:16:16 +00001418 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001419 SkRect bounds = {
1420 x, y,
1421 x + SkIntToScalar(bitmap.width()),
1422 y + SkIntToScalar(bitmap.height())
1423 };
1424 if (paint) {
1425 (void)paint->computeFastBounds(bounds, &bounds);
1426 }
1427 if (this->quickReject(bounds, paint2EdgeType(paint))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001428 return;
1429 }
1430 }
reed@google.com4b226022011-01-11 18:32:13 +00001431
reed@android.com8a1c16f2008-12-17 15:59:43 +00001432 SkMatrix matrix;
1433 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001434 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001435}
1436
reed@google.com9987ec32011-09-07 11:57:52 +00001437// this one is non-virtual, so it can be called safely by other canvas apis
1438void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1439 const SkRect& dst, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001440 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1441 return;
1442 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001443
reed@android.com8a1c16f2008-12-17 15:59:43 +00001444 // do this now, to avoid the cost of calling extract for RLE bitmaps
reed@google.com3d608122011-11-21 15:16:16 +00001445 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001446 SkRect storage;
1447 const SkRect* bounds = &dst;
1448 if (paint) {
1449 bounds = &paint->computeFastBounds(dst, &storage);
1450 }
1451 if (this->quickReject(*bounds, paint2EdgeType(paint))) {
reed@google.com3d608122011-11-21 15:16:16 +00001452 return;
1453 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001454 }
reed@google.com3d608122011-11-21 15:16:16 +00001455
reed@android.com8a1c16f2008-12-17 15:59:43 +00001456 const SkBitmap* bitmapPtr = &bitmap;
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001457
reed@android.com8a1c16f2008-12-17 15:59:43 +00001458 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001459 SkRect tmpSrc;
1460 if (src) {
1461 tmpSrc.set(*src);
1462 // if the extract process clipped off the top or left of the
1463 // original, we adjust for that here to get the position right.
1464 if (tmpSrc.fLeft > 0) {
1465 tmpSrc.fRight -= tmpSrc.fLeft;
1466 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001467 }
reed@android.com87899992009-10-16 14:48:38 +00001468 if (tmpSrc.fTop > 0) {
1469 tmpSrc.fBottom -= tmpSrc.fTop;
1470 tmpSrc.fTop = 0;
1471 }
1472 } else {
1473 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1474 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001475 }
reed@android.com87899992009-10-16 14:48:38 +00001476 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001477
reed@android.comf2b98d62010-12-20 18:26:13 +00001478 // ensure that src is "valid" before we pass it to our internal routines
1479 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1480 SkIRect tmpISrc;
1481 if (src) {
1482 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
reed@google.com2ade0862011-03-17 17:48:04 +00001483 if (!tmpISrc.intersect(*src)) {
1484 return;
1485 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001486 src = &tmpISrc;
1487 }
1488 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001489}
1490
reed@google.com9987ec32011-09-07 11:57:52 +00001491void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1492 const SkRect& dst, const SkPaint* paint) {
1493 SkDEBUGCODE(bitmap.validate();)
1494 this->internalDrawBitmapRect(bitmap, src, dst, paint);
1495}
1496
reed@android.com8a1c16f2008-12-17 15:59:43 +00001497void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1498 const SkPaint* paint) {
1499 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001500 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001501}
1502
reed@android.comf2b98d62010-12-20 18:26:13 +00001503void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1504 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001505 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001506
reed@google.com4e2b3d32011-04-07 14:18:59 +00001507 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001508
reed@android.com8a1c16f2008-12-17 15:59:43 +00001509 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001510 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001511 }
reed@android.com9b039062009-02-11 15:09:58 +00001512
reed@google.com4e2b3d32011-04-07 14:18:59 +00001513 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001514}
1515
reed@google.com9987ec32011-09-07 11:57:52 +00001516void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1517 const SkIRect& center, const SkRect& dst,
1518 const SkPaint* paint) {
reed@google.com3d608122011-11-21 15:16:16 +00001519 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001520 SkRect storage;
1521 const SkRect* bounds = &dst;
1522 if (paint) {
1523 bounds = &paint->computeFastBounds(dst, &storage);
1524 }
1525 if (this->quickReject(*bounds, paint2EdgeType(paint))) {
reed@google.com3d608122011-11-21 15:16:16 +00001526 return;
1527 }
1528 }
1529
reed@google.com9987ec32011-09-07 11:57:52 +00001530 const int32_t w = bitmap.width();
1531 const int32_t h = bitmap.height();
1532
1533 SkIRect c = center;
1534 // pin center to the bounds of the bitmap
1535 c.fLeft = SkMax32(0, center.fLeft);
1536 c.fTop = SkMax32(0, center.fTop);
1537 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1538 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1539
1540 const int32_t srcX[4] = { 0, c.fLeft, c.fRight, w };
1541 const int32_t srcY[4] = { 0, c.fTop, c.fBottom, h };
1542 SkScalar dstX[4] = {
1543 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1544 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1545 };
1546 SkScalar dstY[4] = {
1547 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1548 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1549 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001550
reed@google.com9987ec32011-09-07 11:57:52 +00001551 if (dstX[1] > dstX[2]) {
1552 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1553 dstX[2] = dstX[1];
1554 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001555
reed@google.com9987ec32011-09-07 11:57:52 +00001556 if (dstY[1] > dstY[2]) {
1557 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1558 dstY[2] = dstY[1];
1559 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001560
reed@google.com9987ec32011-09-07 11:57:52 +00001561 SkIRect s;
1562 SkRect d;
1563 for (int y = 0; y < 3; y++) {
1564 s.fTop = srcY[y];
1565 s.fBottom = srcY[y+1];
1566 d.fTop = dstY[y];
1567 d.fBottom = dstY[y+1];
1568 for (int x = 0; x < 3; x++) {
1569 s.fLeft = srcX[x];
1570 s.fRight = srcX[x+1];
1571 d.fLeft = dstX[x];
1572 d.fRight = dstX[x+1];
1573 this->internalDrawBitmapRect(bitmap, &s, d, paint);
1574 }
1575 }
1576}
1577
1578void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1579 const SkRect& dst, const SkPaint* paint) {
1580 SkDEBUGCODE(bitmap.validate();)
1581
1582 // Need a device entry-point, so gpu can use a mesh
1583 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1584}
1585
reed@android.com8a1c16f2008-12-17 15:59:43 +00001586void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1587 const SkPaint* paint) {
1588 SkDEBUGCODE(bitmap.validate();)
reed@google.com4b226022011-01-11 18:32:13 +00001589
reed@android.com8a1c16f2008-12-17 15:59:43 +00001590 if (reject_bitmap(bitmap)) {
1591 return;
1592 }
reed@google.com4b226022011-01-11 18:32:13 +00001593
reed@android.com8a1c16f2008-12-17 15:59:43 +00001594 SkPaint tmp;
1595 if (NULL == paint) {
1596 paint = &tmp;
1597 }
reed@google.com4b226022011-01-11 18:32:13 +00001598
reed@google.com4e2b3d32011-04-07 14:18:59 +00001599 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001600
reed@android.com8a1c16f2008-12-17 15:59:43 +00001601 while (iter.next()) {
1602 iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +00001603 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001604 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001605 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001606}
1607
reed@google.comf67e4cf2011-03-15 20:56:58 +00001608class SkDeviceFilteredPaint {
1609public:
1610 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1611 SkDevice::TextFlags flags;
1612 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001613 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001614 newPaint->setFlags(flags.fFlags);
1615 newPaint->setHinting(flags.fHinting);
1616 fPaint = newPaint;
1617 } else {
1618 fPaint = &paint;
1619 }
1620 }
1621
reed@google.comf67e4cf2011-03-15 20:56:58 +00001622 const SkPaint& paint() const { return *fPaint; }
1623
1624private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001625 const SkPaint* fPaint;
1626 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001627};
1628
bungeman@google.com52c748b2011-08-22 21:30:43 +00001629void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1630 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001631 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001632 draw.fDevice->drawRect(draw, r, paint);
1633 } else {
1634 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001635 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001636 draw.fDevice->drawRect(draw, r, p);
1637 }
1638}
1639
1640void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1641 const char text[], size_t byteLength,
1642 SkScalar x, SkScalar y) {
1643 SkASSERT(byteLength == 0 || text != NULL);
1644
1645 // nothing to draw
1646 if (text == NULL || byteLength == 0 ||
1647 draw.fClip->isEmpty() ||
1648 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1649 return;
1650 }
1651
1652 SkScalar width = 0;
1653 SkPoint start;
1654
1655 start.set(0, 0); // to avoid warning
1656 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1657 SkPaint::kStrikeThruText_Flag)) {
1658 width = paint.measureText(text, byteLength);
1659
1660 SkScalar offsetX = 0;
1661 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1662 offsetX = SkScalarHalf(width);
1663 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1664 offsetX = width;
1665 }
1666 start.set(x - offsetX, y);
1667 }
1668
1669 if (0 == width) {
1670 return;
1671 }
1672
1673 uint32_t flags = paint.getFlags();
1674
1675 if (flags & (SkPaint::kUnderlineText_Flag |
1676 SkPaint::kStrikeThruText_Flag)) {
1677 SkScalar textSize = paint.getTextSize();
1678 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1679 SkRect r;
1680
1681 r.fLeft = start.fX;
1682 r.fRight = start.fX + width;
1683
1684 if (flags & SkPaint::kUnderlineText_Flag) {
1685 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1686 start.fY);
1687 r.fTop = offset;
1688 r.fBottom = offset + height;
1689 DrawRect(draw, paint, r, textSize);
1690 }
1691 if (flags & SkPaint::kStrikeThruText_Flag) {
1692 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1693 start.fY);
1694 r.fTop = offset;
1695 r.fBottom = offset + height;
1696 DrawRect(draw, paint, r, textSize);
1697 }
1698 }
1699}
1700
reed@android.com8a1c16f2008-12-17 15:59:43 +00001701void SkCanvas::drawText(const void* text, size_t byteLength,
1702 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001703 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001704
1705 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001706 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001707 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001708 DrawTextDecorations(iter, dfp.paint(),
1709 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001710 }
1711
reed@google.com4e2b3d32011-04-07 14:18:59 +00001712 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001713}
1714
1715void SkCanvas::drawPosText(const void* text, size_t byteLength,
1716 const SkPoint pos[], const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001717 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001718
reed@android.com8a1c16f2008-12-17 15:59:43 +00001719 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001720 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001721 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001722 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001723 }
reed@google.com4b226022011-01-11 18:32:13 +00001724
reed@google.com4e2b3d32011-04-07 14:18:59 +00001725 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001726}
1727
1728void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1729 const SkScalar xpos[], SkScalar constY,
1730 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001731 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001732
reed@android.com8a1c16f2008-12-17 15:59:43 +00001733 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001734 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001735 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001736 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001737 }
reed@google.com4b226022011-01-11 18:32:13 +00001738
reed@google.com4e2b3d32011-04-07 14:18:59 +00001739 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001740}
1741
1742void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1743 const SkPath& path, const SkMatrix* matrix,
1744 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001745 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001746
1747 while (iter.next()) {
1748 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001749 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001750 }
1751
reed@google.com4e2b3d32011-04-07 14:18:59 +00001752 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001753}
1754
djsollen@google.com56c69772011-11-08 19:00:26 +00001755#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001756void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1757 const SkPoint pos[], const SkPaint& paint,
1758 const SkPath& path, const SkMatrix* matrix) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001759 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001760
1761 while (iter.next()) {
1762 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001763 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001764 }
1765
reed@google.com4e2b3d32011-04-07 14:18:59 +00001766 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001767}
1768#endif
1769
reed@android.com8a1c16f2008-12-17 15:59:43 +00001770void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1771 const SkPoint verts[], const SkPoint texs[],
1772 const SkColor colors[], SkXfermode* xmode,
1773 const uint16_t indices[], int indexCount,
1774 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001775 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001776
reed@android.com8a1c16f2008-12-17 15:59:43 +00001777 while (iter.next()) {
1778 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001779 colors, xmode, indices, indexCount,
1780 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001781 }
reed@google.com4b226022011-01-11 18:32:13 +00001782
reed@google.com4e2b3d32011-04-07 14:18:59 +00001783 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001784}
1785
reed@android.comcb608442009-12-04 21:32:27 +00001786void SkCanvas::drawData(const void* data, size_t length) {
1787 // do nothing. Subclasses may do something with the data
1788}
1789
reed@android.com8a1c16f2008-12-17 15:59:43 +00001790//////////////////////////////////////////////////////////////////////////////
1791// These methods are NOT virtual, and therefore must call back into virtual
1792// methods, rather than actually drawing themselves.
1793//////////////////////////////////////////////////////////////////////////////
1794
1795void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001796 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001797 SkPaint paint;
1798
1799 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001800 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001801 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001802 }
1803 this->drawPaint(paint);
1804}
1805
reed@android.com845fdac2009-06-23 03:01:32 +00001806void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001807 SkPaint paint;
1808
1809 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001810 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001811 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001812 }
1813 this->drawPaint(paint);
1814}
1815
1816void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1817 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001818
reed@android.com8a1c16f2008-12-17 15:59:43 +00001819 pt.set(x, y);
1820 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1821}
1822
1823void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1824 SkPoint pt;
1825 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001826
reed@android.com8a1c16f2008-12-17 15:59:43 +00001827 pt.set(x, y);
1828 paint.setColor(color);
1829 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1830}
1831
1832void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1833 const SkPaint& paint) {
1834 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001835
reed@android.com8a1c16f2008-12-17 15:59:43 +00001836 pts[0].set(x0, y0);
1837 pts[1].set(x1, y1);
1838 this->drawPoints(kLines_PointMode, 2, pts, paint);
1839}
1840
1841void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1842 SkScalar right, SkScalar bottom,
1843 const SkPaint& paint) {
1844 SkRect r;
1845
1846 r.set(left, top, right, bottom);
1847 this->drawRect(r, paint);
1848}
1849
1850void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1851 const SkPaint& paint) {
1852 if (radius < 0) {
1853 radius = 0;
1854 }
1855
1856 SkRect r;
1857 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001858
reed@android.com8a1c16f2008-12-17 15:59:43 +00001859 if (paint.canComputeFastBounds()) {
1860 SkRect storage;
1861 if (this->quickReject(paint.computeFastBounds(r, &storage),
1862 paint2EdgeType(&paint))) {
1863 return;
1864 }
1865 }
reed@google.com4b226022011-01-11 18:32:13 +00001866
reed@android.com8a1c16f2008-12-17 15:59:43 +00001867 SkPath path;
1868 path.addOval(r);
1869 this->drawPath(path, paint);
1870}
1871
1872void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1873 const SkPaint& paint) {
1874 if (rx > 0 && ry > 0) {
1875 if (paint.canComputeFastBounds()) {
1876 SkRect storage;
1877 if (this->quickReject(paint.computeFastBounds(r, &storage),
1878 paint2EdgeType(&paint))) {
1879 return;
1880 }
1881 }
1882
1883 SkPath path;
1884 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1885 this->drawPath(path, paint);
1886 } else {
1887 this->drawRect(r, paint);
1888 }
1889}
1890
1891void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1892 if (paint.canComputeFastBounds()) {
1893 SkRect storage;
1894 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1895 paint2EdgeType(&paint))) {
1896 return;
1897 }
1898 }
1899
1900 SkPath path;
1901 path.addOval(oval);
1902 this->drawPath(path, paint);
1903}
1904
1905void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1906 SkScalar sweepAngle, bool useCenter,
1907 const SkPaint& paint) {
1908 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1909 this->drawOval(oval, paint);
1910 } else {
1911 SkPath path;
1912 if (useCenter) {
1913 path.moveTo(oval.centerX(), oval.centerY());
1914 }
1915 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1916 if (useCenter) {
1917 path.close();
1918 }
1919 this->drawPath(path, paint);
1920 }
1921}
1922
1923void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1924 const SkPath& path, SkScalar hOffset,
1925 SkScalar vOffset, const SkPaint& paint) {
1926 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001927
reed@android.com8a1c16f2008-12-17 15:59:43 +00001928 matrix.setTranslate(hOffset, vOffset);
1929 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1930}
1931
reed@android.comf76bacf2009-05-13 14:00:33 +00001932///////////////////////////////////////////////////////////////////////////////
1933
reed@android.com8a1c16f2008-12-17 15:59:43 +00001934void SkCanvas::drawPicture(SkPicture& picture) {
1935 int saveCount = save();
1936 picture.draw(this);
1937 restoreToCount(saveCount);
1938}
1939
reed@android.com8a1c16f2008-12-17 15:59:43 +00001940///////////////////////////////////////////////////////////////////////////////
1941///////////////////////////////////////////////////////////////////////////////
1942
1943SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00001944 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001945
1946 SkASSERT(canvas);
1947
1948 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
1949 fDone = !fImpl->next();
1950}
1951
1952SkCanvas::LayerIter::~LayerIter() {
1953 fImpl->~SkDrawIter();
1954}
1955
1956void SkCanvas::LayerIter::next() {
1957 fDone = !fImpl->next();
1958}
1959
1960SkDevice* SkCanvas::LayerIter::device() const {
1961 return fImpl->getDevice();
1962}
1963
1964const SkMatrix& SkCanvas::LayerIter::matrix() const {
1965 return fImpl->getMatrix();
1966}
1967
1968const SkPaint& SkCanvas::LayerIter::paint() const {
1969 const SkPaint* paint = fImpl->getPaint();
1970 if (NULL == paint) {
1971 paint = &fDefaultPaint;
1972 }
1973 return *paint;
1974}
1975
1976const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
1977int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
1978int SkCanvas::LayerIter::y() const { return fImpl->getY(); }