blob: 14ab405022f276c9e56044d37a1418cedeb75c73 [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
235 if (NULL != fCurrLayer) {
236 const DeviceCM* rec = fCurrLayer;
237
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()) {
1520 if (this->quickReject(dst, paint2EdgeType(paint))) {
1521 return;
1522 }
1523 }
1524
reed@google.com9987ec32011-09-07 11:57:52 +00001525 const int32_t w = bitmap.width();
1526 const int32_t h = bitmap.height();
1527
1528 SkIRect c = center;
1529 // pin center to the bounds of the bitmap
1530 c.fLeft = SkMax32(0, center.fLeft);
1531 c.fTop = SkMax32(0, center.fTop);
1532 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1533 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1534
1535 const int32_t srcX[4] = { 0, c.fLeft, c.fRight, w };
1536 const int32_t srcY[4] = { 0, c.fTop, c.fBottom, h };
1537 SkScalar dstX[4] = {
1538 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1539 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1540 };
1541 SkScalar dstY[4] = {
1542 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1543 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1544 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001545
reed@google.com9987ec32011-09-07 11:57:52 +00001546 if (dstX[1] > dstX[2]) {
1547 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1548 dstX[2] = dstX[1];
1549 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001550
reed@google.com9987ec32011-09-07 11:57:52 +00001551 if (dstY[1] > dstY[2]) {
1552 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1553 dstY[2] = dstY[1];
1554 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001555
reed@google.com9987ec32011-09-07 11:57:52 +00001556 SkIRect s;
1557 SkRect d;
1558 for (int y = 0; y < 3; y++) {
1559 s.fTop = srcY[y];
1560 s.fBottom = srcY[y+1];
1561 d.fTop = dstY[y];
1562 d.fBottom = dstY[y+1];
1563 for (int x = 0; x < 3; x++) {
1564 s.fLeft = srcX[x];
1565 s.fRight = srcX[x+1];
1566 d.fLeft = dstX[x];
1567 d.fRight = dstX[x+1];
1568 this->internalDrawBitmapRect(bitmap, &s, d, paint);
1569 }
1570 }
1571}
1572
1573void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1574 const SkRect& dst, const SkPaint* paint) {
1575 SkDEBUGCODE(bitmap.validate();)
1576
1577 // Need a device entry-point, so gpu can use a mesh
1578 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1579}
1580
reed@android.com8a1c16f2008-12-17 15:59:43 +00001581void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1582 const SkPaint* paint) {
1583 SkDEBUGCODE(bitmap.validate();)
reed@google.com4b226022011-01-11 18:32:13 +00001584
reed@android.com8a1c16f2008-12-17 15:59:43 +00001585 if (reject_bitmap(bitmap)) {
1586 return;
1587 }
reed@google.com4b226022011-01-11 18:32:13 +00001588
reed@android.com8a1c16f2008-12-17 15:59:43 +00001589 SkPaint tmp;
1590 if (NULL == paint) {
1591 paint = &tmp;
1592 }
reed@google.com4b226022011-01-11 18:32:13 +00001593
reed@google.com4e2b3d32011-04-07 14:18:59 +00001594 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001595
reed@android.com8a1c16f2008-12-17 15:59:43 +00001596 while (iter.next()) {
1597 iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +00001598 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001599 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001600 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001601}
1602
reed@google.comf67e4cf2011-03-15 20:56:58 +00001603class SkDeviceFilteredPaint {
1604public:
1605 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1606 SkDevice::TextFlags flags;
1607 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001608 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001609 newPaint->setFlags(flags.fFlags);
1610 newPaint->setHinting(flags.fHinting);
1611 fPaint = newPaint;
1612 } else {
1613 fPaint = &paint;
1614 }
1615 }
1616
reed@google.comf67e4cf2011-03-15 20:56:58 +00001617 const SkPaint& paint() const { return *fPaint; }
1618
1619private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001620 const SkPaint* fPaint;
1621 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001622};
1623
bungeman@google.com52c748b2011-08-22 21:30:43 +00001624void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1625 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001626 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001627 draw.fDevice->drawRect(draw, r, paint);
1628 } else {
1629 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001630 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001631 draw.fDevice->drawRect(draw, r, p);
1632 }
1633}
1634
1635void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1636 const char text[], size_t byteLength,
1637 SkScalar x, SkScalar y) {
1638 SkASSERT(byteLength == 0 || text != NULL);
1639
1640 // nothing to draw
1641 if (text == NULL || byteLength == 0 ||
1642 draw.fClip->isEmpty() ||
1643 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1644 return;
1645 }
1646
1647 SkScalar width = 0;
1648 SkPoint start;
1649
1650 start.set(0, 0); // to avoid warning
1651 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1652 SkPaint::kStrikeThruText_Flag)) {
1653 width = paint.measureText(text, byteLength);
1654
1655 SkScalar offsetX = 0;
1656 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1657 offsetX = SkScalarHalf(width);
1658 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1659 offsetX = width;
1660 }
1661 start.set(x - offsetX, y);
1662 }
1663
1664 if (0 == width) {
1665 return;
1666 }
1667
1668 uint32_t flags = paint.getFlags();
1669
1670 if (flags & (SkPaint::kUnderlineText_Flag |
1671 SkPaint::kStrikeThruText_Flag)) {
1672 SkScalar textSize = paint.getTextSize();
1673 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1674 SkRect r;
1675
1676 r.fLeft = start.fX;
1677 r.fRight = start.fX + width;
1678
1679 if (flags & SkPaint::kUnderlineText_Flag) {
1680 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1681 start.fY);
1682 r.fTop = offset;
1683 r.fBottom = offset + height;
1684 DrawRect(draw, paint, r, textSize);
1685 }
1686 if (flags & SkPaint::kStrikeThruText_Flag) {
1687 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1688 start.fY);
1689 r.fTop = offset;
1690 r.fBottom = offset + height;
1691 DrawRect(draw, paint, r, textSize);
1692 }
1693 }
1694}
1695
reed@android.com8a1c16f2008-12-17 15:59:43 +00001696void SkCanvas::drawText(const void* text, size_t byteLength,
1697 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001698 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001699
1700 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001701 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001702 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001703 DrawTextDecorations(iter, dfp.paint(),
1704 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001705 }
1706
reed@google.com4e2b3d32011-04-07 14:18:59 +00001707 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001708}
1709
1710void SkCanvas::drawPosText(const void* text, size_t byteLength,
1711 const SkPoint pos[], const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001712 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001713
reed@android.com8a1c16f2008-12-17 15:59:43 +00001714 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001715 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001716 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001717 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001718 }
reed@google.com4b226022011-01-11 18:32:13 +00001719
reed@google.com4e2b3d32011-04-07 14:18:59 +00001720 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001721}
1722
1723void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1724 const SkScalar xpos[], SkScalar constY,
1725 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001726 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001727
reed@android.com8a1c16f2008-12-17 15:59:43 +00001728 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001729 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001730 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001731 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001732 }
reed@google.com4b226022011-01-11 18:32:13 +00001733
reed@google.com4e2b3d32011-04-07 14:18:59 +00001734 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001735}
1736
1737void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1738 const SkPath& path, const SkMatrix* matrix,
1739 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001740 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001741
1742 while (iter.next()) {
1743 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001744 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001745 }
1746
reed@google.com4e2b3d32011-04-07 14:18:59 +00001747 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001748}
1749
djsollen@google.com56c69772011-11-08 19:00:26 +00001750#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001751void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1752 const SkPoint pos[], const SkPaint& paint,
1753 const SkPath& path, const SkMatrix* matrix) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001754 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001755
1756 while (iter.next()) {
1757 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001758 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001759 }
1760
reed@google.com4e2b3d32011-04-07 14:18:59 +00001761 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001762}
1763#endif
1764
reed@android.com8a1c16f2008-12-17 15:59:43 +00001765void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1766 const SkPoint verts[], const SkPoint texs[],
1767 const SkColor colors[], SkXfermode* xmode,
1768 const uint16_t indices[], int indexCount,
1769 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001770 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001771
reed@android.com8a1c16f2008-12-17 15:59:43 +00001772 while (iter.next()) {
1773 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001774 colors, xmode, indices, indexCount,
1775 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001776 }
reed@google.com4b226022011-01-11 18:32:13 +00001777
reed@google.com4e2b3d32011-04-07 14:18:59 +00001778 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001779}
1780
reed@android.comcb608442009-12-04 21:32:27 +00001781void SkCanvas::drawData(const void* data, size_t length) {
1782 // do nothing. Subclasses may do something with the data
1783}
1784
reed@android.com8a1c16f2008-12-17 15:59:43 +00001785//////////////////////////////////////////////////////////////////////////////
1786// These methods are NOT virtual, and therefore must call back into virtual
1787// methods, rather than actually drawing themselves.
1788//////////////////////////////////////////////////////////////////////////////
1789
1790void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001791 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001792 SkPaint paint;
1793
1794 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001795 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001796 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001797 }
1798 this->drawPaint(paint);
1799}
1800
reed@android.com845fdac2009-06-23 03:01:32 +00001801void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001802 SkPaint paint;
1803
1804 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001805 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001806 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001807 }
1808 this->drawPaint(paint);
1809}
1810
1811void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1812 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001813
reed@android.com8a1c16f2008-12-17 15:59:43 +00001814 pt.set(x, y);
1815 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1816}
1817
1818void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1819 SkPoint pt;
1820 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001821
reed@android.com8a1c16f2008-12-17 15:59:43 +00001822 pt.set(x, y);
1823 paint.setColor(color);
1824 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1825}
1826
1827void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1828 const SkPaint& paint) {
1829 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001830
reed@android.com8a1c16f2008-12-17 15:59:43 +00001831 pts[0].set(x0, y0);
1832 pts[1].set(x1, y1);
1833 this->drawPoints(kLines_PointMode, 2, pts, paint);
1834}
1835
1836void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1837 SkScalar right, SkScalar bottom,
1838 const SkPaint& paint) {
1839 SkRect r;
1840
1841 r.set(left, top, right, bottom);
1842 this->drawRect(r, paint);
1843}
1844
1845void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1846 const SkPaint& paint) {
1847 if (radius < 0) {
1848 radius = 0;
1849 }
1850
1851 SkRect r;
1852 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001853
reed@android.com8a1c16f2008-12-17 15:59:43 +00001854 if (paint.canComputeFastBounds()) {
1855 SkRect storage;
1856 if (this->quickReject(paint.computeFastBounds(r, &storage),
1857 paint2EdgeType(&paint))) {
1858 return;
1859 }
1860 }
reed@google.com4b226022011-01-11 18:32:13 +00001861
reed@android.com8a1c16f2008-12-17 15:59:43 +00001862 SkPath path;
1863 path.addOval(r);
1864 this->drawPath(path, paint);
1865}
1866
1867void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1868 const SkPaint& paint) {
1869 if (rx > 0 && ry > 0) {
1870 if (paint.canComputeFastBounds()) {
1871 SkRect storage;
1872 if (this->quickReject(paint.computeFastBounds(r, &storage),
1873 paint2EdgeType(&paint))) {
1874 return;
1875 }
1876 }
1877
1878 SkPath path;
1879 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1880 this->drawPath(path, paint);
1881 } else {
1882 this->drawRect(r, paint);
1883 }
1884}
1885
1886void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1887 if (paint.canComputeFastBounds()) {
1888 SkRect storage;
1889 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1890 paint2EdgeType(&paint))) {
1891 return;
1892 }
1893 }
1894
1895 SkPath path;
1896 path.addOval(oval);
1897 this->drawPath(path, paint);
1898}
1899
1900void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1901 SkScalar sweepAngle, bool useCenter,
1902 const SkPaint& paint) {
1903 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1904 this->drawOval(oval, paint);
1905 } else {
1906 SkPath path;
1907 if (useCenter) {
1908 path.moveTo(oval.centerX(), oval.centerY());
1909 }
1910 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1911 if (useCenter) {
1912 path.close();
1913 }
1914 this->drawPath(path, paint);
1915 }
1916}
1917
1918void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1919 const SkPath& path, SkScalar hOffset,
1920 SkScalar vOffset, const SkPaint& paint) {
1921 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001922
reed@android.com8a1c16f2008-12-17 15:59:43 +00001923 matrix.setTranslate(hOffset, vOffset);
1924 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1925}
1926
reed@android.comf76bacf2009-05-13 14:00:33 +00001927///////////////////////////////////////////////////////////////////////////////
1928
reed@android.com8a1c16f2008-12-17 15:59:43 +00001929void SkCanvas::drawPicture(SkPicture& picture) {
1930 int saveCount = save();
1931 picture.draw(this);
1932 restoreToCount(saveCount);
1933}
1934
reed@android.com8a1c16f2008-12-17 15:59:43 +00001935///////////////////////////////////////////////////////////////////////////////
1936///////////////////////////////////////////////////////////////////////////////
1937
1938SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00001939 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001940
1941 SkASSERT(canvas);
1942
1943 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
1944 fDone = !fImpl->next();
1945}
1946
1947SkCanvas::LayerIter::~LayerIter() {
1948 fImpl->~SkDrawIter();
1949}
1950
1951void SkCanvas::LayerIter::next() {
1952 fDone = !fImpl->next();
1953}
1954
1955SkDevice* SkCanvas::LayerIter::device() const {
1956 return fImpl->getDevice();
1957}
1958
1959const SkMatrix& SkCanvas::LayerIter::matrix() const {
1960 return fImpl->getMatrix();
1961}
1962
1963const SkPaint& SkCanvas::LayerIter::paint() const {
1964 const SkPaint* paint = fImpl->getPaint();
1965 if (NULL == paint) {
1966 paint = &fDefaultPaint;
1967 }
1968 return *paint;
1969}
1970
1971const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
1972int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
1973int SkCanvas::LayerIter::y() const { return fImpl->getY(); }