blob: b7c06c33510e11247ab11234f25bda65b5a3256a [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
reed@google.com82ce2b82012-06-26 17:43:26 +000024SK_DEFINE_INST_COUNT(SkBounder)
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000025SK_DEFINE_INST_COUNT(SkCanvas)
reed@google.com82ce2b82012-06-26 17:43:26 +000026SK_DEFINE_INST_COUNT(SkDrawFilter)
27
reed@google.comda17f752012-08-16 18:27:05 +000028// experimental for faster tiled drawing...
29//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000030
reed@android.com8a1c16f2008-12-17 15:59:43 +000031//#define SK_TRACE_SAVERESTORE
32
33#ifdef SK_TRACE_SAVERESTORE
34 static int gLayerCounter;
35 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
36 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
37
38 static int gRecCounter;
39 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
40 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
41
42 static int gCanvasCounter;
43 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
44 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
45#else
46 #define inc_layer()
47 #define dec_layer()
48 #define inc_rec()
49 #define dec_rec()
50 #define inc_canvas()
51 #define dec_canvas()
52#endif
53
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000054typedef SkTLazy<SkPaint> SkLazyPaint;
55
reed@android.com8a1c16f2008-12-17 15:59:43 +000056///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000057
58/* This is the record we keep for each SkDevice that the user installs.
59 The clip/matrix/proc are fields that reflect the top of the save/restore
60 stack. Whenever the canvas changes, it marks a dirty flag, and then before
61 these are used (assuming we're not on a layer) we rebuild these cache
62 values: they reflect the top of the save stack, but translated and clipped
63 by the device's XY offset and bitmap-bounds.
64*/
65struct DeviceCM {
66 DeviceCM* fNext;
67 SkDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +000068 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +000069 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +000070 SkPaint* fPaint; // may be null (in the future)
reed@android.comf2b98d62010-12-20 18:26:13 +000071 // optional, related to canvas' external matrix
72 const SkMatrix* fMVMatrix;
73 const SkMatrix* fExtMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +000074
robertphillips@google.com40a1ae42012-07-13 15:36:15 +000075 DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas)
reed@android.com8a1c16f2008-12-17 15:59:43 +000076 : fNext(NULL) {
77 if (NULL != device) {
78 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +000079 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +000080 }
reed@google.com4b226022011-01-11 18:32:13 +000081 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +000082 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +000083 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000084
bungeman@google.com88edf1e2011-08-08 19:41:56 +000085 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000086 if (NULL != fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +000087 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +000088 fDevice->unref();
89 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +000090 SkDELETE(fPaint);
91 }
reed@google.com4b226022011-01-11 18:32:13 +000092
reed@google.com045e62d2011-10-24 12:19:46 +000093 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
94 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +000095 int x = fDevice->getOrigin().x();
96 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +000097 int width = fDevice->width();
98 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +000099
reed@android.com8a1c16f2008-12-17 15:59:43 +0000100 if ((x | y) == 0) {
101 fMatrix = &totalMatrix;
102 fClip = totalClip;
103 } else {
104 fMatrixStorage = totalMatrix;
105 fMatrixStorage.postTranslate(SkIntToScalar(-x),
106 SkIntToScalar(-y));
107 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000108
reed@android.com8a1c16f2008-12-17 15:59:43 +0000109 totalClip.translate(-x, -y, &fClip);
110 }
111
reed@google.com045e62d2011-10-24 12:19:46 +0000112 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000113
114 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000115
reed@android.com8a1c16f2008-12-17 15:59:43 +0000116 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000117 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000118 SkRegion::kDifference_Op);
119 }
reed@google.com4b226022011-01-11 18:32:13 +0000120
reed@google.com045e62d2011-10-24 12:19:46 +0000121 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000122
123#ifdef SK_DEBUG
124 if (!fClip.isEmpty()) {
125 SkIRect deviceR;
126 deviceR.set(0, 0, width, height);
127 SkASSERT(deviceR.contains(fClip.getBounds()));
128 }
129#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000130 // default is to assume no external matrix
131 fMVMatrix = NULL;
132 fExtMatrix = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000133 }
reed@android.comf2b98d62010-12-20 18:26:13 +0000134
135 // can only be called after calling updateMC()
136 void updateExternalMatrix(const SkMatrix& extM, const SkMatrix& extI) {
137 fMVMatrixStorage.setConcat(extI, *fMatrix);
138 fMVMatrix = &fMVMatrixStorage;
139 fExtMatrix = &extM; // assumes extM has long life-time (owned by canvas)
140 }
141
reed@android.com8a1c16f2008-12-17 15:59:43 +0000142private:
reed@android.comf2b98d62010-12-20 18:26:13 +0000143 SkMatrix fMatrixStorage, fMVMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000144};
145
146/* This is the record we keep for each save/restore level in the stack.
147 Since a level optionally copies the matrix and/or stack, we have pointers
148 for these fields. If the value is copied for this level, the copy is
149 stored in the ...Storage field, and the pointer points to that. If the
150 value is not copied for this level, we ignore ...Storage, and just point
151 at the corresponding value in the previous level in the stack.
152*/
153class SkCanvas::MCRec {
154public:
155 MCRec* fNext;
reed@google.com00177082011-10-12 14:34:30 +0000156 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
157 SkRasterClip* fRasterClip; // points to either fRegionStorage or prev MCRec
158 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000159
reed@android.com8a1c16f2008-12-17 15:59:43 +0000160 DeviceCM* fLayer;
161 /* If there are any layers in the stack, this points to the top-most
162 one that is at or below this level in the stack (so we know what
163 bitmap/device to draw into from this level. This value is NOT
164 reference counted, since the real owner is either our fLayer field,
165 or a previous one in a lower level.)
166 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000167 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000168
169 MCRec(const MCRec* prev, int flags) {
170 if (NULL != prev) {
171 if (flags & SkCanvas::kMatrix_SaveFlag) {
172 fMatrixStorage = *prev->fMatrix;
173 fMatrix = &fMatrixStorage;
174 } else {
175 fMatrix = prev->fMatrix;
176 }
reed@google.com4b226022011-01-11 18:32:13 +0000177
reed@android.com8a1c16f2008-12-17 15:59:43 +0000178 if (flags & SkCanvas::kClip_SaveFlag) {
reed@google.com00177082011-10-12 14:34:30 +0000179 fRasterClipStorage = *prev->fRasterClip;
180 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000181 } else {
reed@google.com00177082011-10-12 14:34:30 +0000182 fRasterClip = prev->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000183 }
184
185 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000186 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000187
188 fTopLayer = prev->fTopLayer;
189 } else { // no prev
190 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000191
reed@android.com8a1c16f2008-12-17 15:59:43 +0000192 fMatrix = &fMatrixStorage;
reed@google.com00177082011-10-12 14:34:30 +0000193 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194 fFilter = NULL;
195 fTopLayer = NULL;
196 }
197 fLayer = NULL;
198
199 // don't bother initializing fNext
200 inc_rec();
201 }
202 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000203 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204 SkDELETE(fLayer);
205 dec_rec();
206 }
reed@google.com4b226022011-01-11 18:32:13 +0000207
reed@android.com8a1c16f2008-12-17 15:59:43 +0000208private:
reed@google.com00177082011-10-12 14:34:30 +0000209 SkMatrix fMatrixStorage;
210 SkRasterClip fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211};
212
213class SkDrawIter : public SkDraw {
214public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000215 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000216 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000217 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000218 canvas->updateDeviceCMCache();
219
reed@google.com90c07ea2012-04-13 13:50:27 +0000220 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221 fBounder = canvas->getBounder();
222 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000223 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000224 }
reed@google.com4b226022011-01-11 18:32:13 +0000225
reed@android.com8a1c16f2008-12-17 15:59:43 +0000226 bool next() {
227 // skip over recs with empty clips
228 if (fSkipEmptyClips) {
229 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
230 fCurrLayer = fCurrLayer->fNext;
231 }
232 }
233
reed@google.comf68c5e22012-02-24 16:38:58 +0000234 const DeviceCM* rec = fCurrLayer;
235 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000236
237 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000238 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
239 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240 fDevice = rec->fDevice;
241 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000243 fMVMatrix = rec->fMVMatrix;
244 fExtMatrix = rec->fExtMatrix;
245 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000246
247 fCurrLayer = rec->fNext;
248 if (fBounder) {
249 fBounder->setClip(fClip);
250 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000251 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000252
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000253 fCanvas->prepareForDeviceDraw(fDevice, *fMatrix, *fClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000254 return true;
255 }
256 return false;
257 }
reed@google.com4b226022011-01-11 18:32:13 +0000258
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259 SkDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000260 int getX() const { return fDevice->getOrigin().x(); }
261 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262 const SkMatrix& getMatrix() const { return *fMatrix; }
263 const SkRegion& getClip() const { return *fClip; }
264 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000265
reed@android.com8a1c16f2008-12-17 15:59:43 +0000266private:
267 SkCanvas* fCanvas;
268 const DeviceCM* fCurrLayer;
269 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000270 SkBool8 fSkipEmptyClips;
271
272 typedef SkDraw INHERITED;
273};
274
275/////////////////////////////////////////////////////////////////////////////
276
277class AutoDrawLooper {
278public:
reed@google.com8926b162012-03-23 15:36:36 +0000279 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint,
280 bool skipLayerForImageFilter = false) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000281 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();
reed@google.com8926b162012-03-23 15:36:36 +0000286 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000287 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288
reed@google.com8926b162012-03-23 15:36:36 +0000289 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
290 SkPaint tmp;
291 tmp.setImageFilter(fOrigPaint.getImageFilter());
292 // it would be nice if we had a guess at the bounds, instead of null
293 (void)canvas->internalSaveLayer(NULL, &tmp,
294 SkCanvas::kARGB_ClipLayer_SaveFlag, true);
295 // we'll clear the imageFilter for the actual draws in next(), so
296 // it will only be applied during the restore().
297 fDoClearImageFilter = true;
298 }
299
reed@google.com4e2b3d32011-04-07 14:18:59 +0000300 if (fLooper) {
301 fLooper->init(canvas);
reed@google.com129ec222012-05-15 13:24:09 +0000302 fIsSimple = false;
303 } else {
304 // can we be marked as simple?
305 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000306 }
307 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000308
reed@android.com8a1c16f2008-12-17 15:59:43 +0000309 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000310 if (fDoClearImageFilter) {
311 fCanvas->internalRestore();
312 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000313 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000314 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000315
reed@google.com4e2b3d32011-04-07 14:18:59 +0000316 const SkPaint& paint() const {
317 SkASSERT(fPaint);
318 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000320
reed@google.com129ec222012-05-15 13:24:09 +0000321 bool next(SkDrawFilter::Type drawType) {
322 if (fDone) {
323 return false;
324 } else if (fIsSimple) {
325 fDone = true;
326 fPaint = &fOrigPaint;
327 return !fPaint->nothingToDraw();
328 } else {
329 return this->doNext(drawType);
330 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000331 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000332
reed@android.com8a1c16f2008-12-17 15:59:43 +0000333private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000334 SkLazyPaint fLazyPaint;
335 SkCanvas* fCanvas;
336 const SkPaint& fOrigPaint;
337 SkDrawLooper* fLooper;
338 SkDrawFilter* fFilter;
339 const SkPaint* fPaint;
340 int fSaveCount;
reed@google.com8926b162012-03-23 15:36:36 +0000341 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000342 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000343 bool fIsSimple;
344
345 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000346};
347
reed@google.com129ec222012-05-15 13:24:09 +0000348bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000349 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000350 SkASSERT(!fIsSimple);
351 SkASSERT(fLooper || fFilter || fDoClearImageFilter);
352
353 SkPaint* paint = fLazyPaint.set(fOrigPaint);
354
355 if (fDoClearImageFilter) {
356 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000357 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000358
reed@google.com129ec222012-05-15 13:24:09 +0000359 if (fLooper && !fLooper->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000360 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000361 return false;
362 }
363 if (fFilter) {
364 fFilter->filter(paint, drawType);
365 if (NULL == fLooper) {
366 // no looper means we only draw once
367 fDone = true;
368 }
369 }
370 fPaint = paint;
371
372 // if we only came in here for the imagefilter, mark us as done
373 if (!fLooper && !fFilter) {
374 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000375 }
376
377 // call this after any possible paint modifiers
378 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000379 fPaint = NULL;
380 return false;
381 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000382 return true;
383}
384
reed@android.com8a1c16f2008-12-17 15:59:43 +0000385/* Stack helper for managing a SkBounder. In the destructor, if we were
386 given a bounder, we call its commit() method, signifying that we are
387 done accumulating bounds for that draw.
388*/
389class SkAutoBounderCommit {
390public:
391 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
392 ~SkAutoBounderCommit() {
393 if (NULL != fBounder) {
394 fBounder->commit();
395 }
396 }
397private:
398 SkBounder* fBounder;
399};
400
401#include "SkColorPriv.h"
402
403class AutoValidator {
404public:
405 AutoValidator(SkDevice* device) : fDevice(device) {}
406 ~AutoValidator() {
407#ifdef SK_DEBUG
408 const SkBitmap& bm = fDevice->accessBitmap(false);
409 if (bm.config() == SkBitmap::kARGB_4444_Config) {
410 for (int y = 0; y < bm.height(); y++) {
411 const SkPMColor16* p = bm.getAddr16(0, y);
412 for (int x = 0; x < bm.width(); x++) {
413 SkPMColor16 c = p[x];
414 SkPMColor16Assert(c);
415 }
416 }
417 }
418#endif
419 }
420private:
421 SkDevice* fDevice;
422};
423
424////////// macros to place around the internal draw calls //////////////////
425
reed@google.com8926b162012-03-23 15:36:36 +0000426#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
427/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
428 AutoDrawLooper looper(this, paint, true); \
429 while (looper.next(type)) { \
430 SkAutoBounderCommit ac(fBounder); \
431 SkDrawIter iter(this);
432
reed@google.com4e2b3d32011-04-07 14:18:59 +0000433#define LOOPER_BEGIN(paint, type) \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000434/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000435 AutoDrawLooper looper(this, paint); \
436 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437 SkAutoBounderCommit ac(fBounder); \
438 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000439
reed@google.com4e2b3d32011-04-07 14:18:59 +0000440#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000441
442////////////////////////////////////////////////////////////////////////////
443
444SkDevice* SkCanvas::init(SkDevice* device) {
445 fBounder = NULL;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000446 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000447 fLocalBoundsCompareTypeDirty = true;
reed@android.com199f1082009-06-10 02:12:47 +0000448 fLastDeviceToGainFocus = NULL;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000449 fDeviceCMDirty = false;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000450 fSaveLayerCount = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000451
452 fMCRec = (MCRec*)fMCStack.push_back();
453 new (fMCRec) MCRec(NULL, 0);
454
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000455 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000456 fMCRec->fTopLayer = fMCRec->fLayer;
457 fMCRec->fNext = NULL;
458
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000459 fExternalMatrix.reset();
460 fExternalInverse.reset();
reed@android.comf2b98d62010-12-20 18:26:13 +0000461 fUseExternalMatrix = false;
462
reed@android.com8a1c16f2008-12-17 15:59:43 +0000463 return this->setDevice(device);
464}
465
reed@google.comcde92112011-07-06 20:00:52 +0000466SkCanvas::SkCanvas()
467: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000468 inc_canvas();
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000469
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000470 this->init(NULL);
471}
472
reed@android.com8a1c16f2008-12-17 15:59:43 +0000473SkCanvas::SkCanvas(SkDevice* device)
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000474 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000475 inc_canvas();
476
477 this->init(device);
478}
479
480SkCanvas::SkCanvas(const SkBitmap& bitmap)
481 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
482 inc_canvas();
483
reed@google.comcde92112011-07-06 20:00:52 +0000484 this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000485}
486
487SkCanvas::~SkCanvas() {
488 // free up the contents of our deque
489 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000490 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000491
reed@android.com8a1c16f2008-12-17 15:59:43 +0000492 this->internalRestore(); // restore the last, since we're going away
493
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000494 SkSafeUnref(fBounder);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000495
reed@android.com8a1c16f2008-12-17 15:59:43 +0000496 dec_canvas();
497}
498
499SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
500 SkRefCnt_SafeAssign(fBounder, bounder);
501 return bounder;
502}
503
504SkDrawFilter* SkCanvas::getDrawFilter() const {
505 return fMCRec->fFilter;
506}
507
508SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
509 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
510 return filter;
511}
512
513///////////////////////////////////////////////////////////////////////////////
514
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000515void SkCanvas::flush() {
516 SkDevice* device = this->getDevice();
517 if (device) {
518 device->flush();
519 }
520}
521
reed@google.com210ce002011-11-01 14:24:23 +0000522SkISize SkCanvas::getDeviceSize() const {
523 SkDevice* d = this->getDevice();
524 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
525}
526
reed@android.com8a1c16f2008-12-17 15:59:43 +0000527SkDevice* SkCanvas::getDevice() const {
528 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000529 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000530 SkASSERT(rec && rec->fLayer);
531 return rec->fLayer->fDevice;
532}
533
reed@google.com0b53d592012-03-19 18:26:34 +0000534SkDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
535 if (updateMatrixClip) {
536 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
537 }
reed@google.com9266fed2011-03-30 00:18:03 +0000538 return fMCRec->fTopLayer->fDevice;
539}
540
reed@android.com8a1c16f2008-12-17 15:59:43 +0000541SkDevice* SkCanvas::setDevice(SkDevice* device) {
542 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000543 SkDeque::F2BIter iter(fMCStack);
544 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000545 SkASSERT(rec && rec->fLayer);
546 SkDevice* rootDevice = rec->fLayer->fDevice;
547
548 if (rootDevice == device) {
549 return device;
550 }
reed@google.com4b226022011-01-11 18:32:13 +0000551
reed@android.com8a1c16f2008-12-17 15:59:43 +0000552 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000553 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000554 }
555 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000556 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000557 }
558
559 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
560 rootDevice = device;
561
562 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000563
reed@android.com8a1c16f2008-12-17 15:59:43 +0000564 /* Now we update our initial region to have the bounds of the new device,
565 and then intersect all of the clips in our stack with these bounds,
566 to ensure that we can't draw outside of the device's bounds (and trash
567 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000568
reed@android.com8a1c16f2008-12-17 15:59:43 +0000569 NOTE: this is only a partial-fix, since if the new device is larger than
570 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000571 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000572 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
573 reconstruct the correct clips, so this approximation will have to do.
574 The caller really needs to restore() back to the base if they want to
575 accurately take advantage of the new device bounds.
576 */
577
reed@google.com42aea282012-03-28 16:19:15 +0000578 SkIRect bounds;
579 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000580 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000581 } else {
582 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000583 }
reed@google.com42aea282012-03-28 16:19:15 +0000584 // now jam our 1st clip to be bounds, and intersect the rest with that
585 rec->fRasterClip->setRect(bounds);
586 while ((rec = (MCRec*)iter.next()) != NULL) {
587 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
588 }
589
reed@android.com8a1c16f2008-12-17 15:59:43 +0000590 return device;
591}
592
reed@google.comaf951c92011-06-16 19:10:39 +0000593SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) {
594 SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (bitmap)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000595 device->unref();
596 return device;
597}
598
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000599bool SkCanvas::readPixels(SkBitmap* bitmap,
600 int x, int y,
601 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000602 SkDevice* device = this->getDevice();
603 if (!device) {
604 return false;
605 }
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000606 return device->readPixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000607}
608
bsalomon@google.comc6980972011-11-02 19:57:21 +0000609bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
bsalomon@google.comace7bd52011-11-02 19:39:51 +0000610 SkDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000611 if (!device) {
612 return false;
613 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000614
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000615 SkIRect bounds;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000616 bounds.set(0, 0, device->width(), device->height());
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000617 if (!bounds.intersect(srcRect)) {
618 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000619 }
620
621 SkBitmap tmp;
622 tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
623 bounds.height());
624 if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
625 bitmap->swap(tmp);
626 return true;
627 } else {
reed@google.com51df9e32010-12-23 19:29:18 +0000628 return false;
629 }
reed@google.com51df9e32010-12-23 19:29:18 +0000630}
631
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000632void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
633 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000634 SkDevice* device = this->getDevice();
635 if (device) {
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000636 device->writePixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000637 }
638}
639
junov@google.com4370aed2012-01-18 16:21:08 +0000640SkCanvas* SkCanvas::canvasForDrawIter() {
641 return this;
642}
643
reed@android.com8a1c16f2008-12-17 15:59:43 +0000644//////////////////////////////////////////////////////////////////////////////
645
reed@android.com8a1c16f2008-12-17 15:59:43 +0000646void SkCanvas::updateDeviceCMCache() {
647 if (fDeviceCMDirty) {
648 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000649 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000650 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000651
reed@android.com8a1c16f2008-12-17 15:59:43 +0000652 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000653 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000654 if (fUseExternalMatrix) {
655 layer->updateExternalMatrix(fExternalMatrix,
656 fExternalInverse);
657 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000658 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000659 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000660 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000661 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.comf2b98d62010-12-20 18:26:13 +0000662 if (fUseExternalMatrix) {
663 layer->updateExternalMatrix(fExternalMatrix,
664 fExternalInverse);
665 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000666 } while ((layer = layer->fNext) != NULL);
667 }
668 fDeviceCMDirty = false;
669 }
670}
671
reed@android.comf2b98d62010-12-20 18:26:13 +0000672void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000673 const SkRegion& clip) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000674 SkASSERT(device);
reed@android.com199f1082009-06-10 02:12:47 +0000675 if (fLastDeviceToGainFocus != device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000676 device->gainFocus(matrix, clip);
reed@android.com199f1082009-06-10 02:12:47 +0000677 fLastDeviceToGainFocus = device;
678 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000679}
680
681///////////////////////////////////////////////////////////////////////////////
682
683int SkCanvas::internalSave(SaveFlags flags) {
684 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000685
reed@android.com8a1c16f2008-12-17 15:59:43 +0000686 MCRec* newTop = (MCRec*)fMCStack.push_back();
687 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000688
reed@android.com8a1c16f2008-12-17 15:59:43 +0000689 newTop->fNext = fMCRec;
690 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000691
reed@google.com5c3d1472011-02-22 19:12:23 +0000692 fClipStack.save();
693 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
694
reed@android.com8a1c16f2008-12-17 15:59:43 +0000695 return saveCount;
696}
697
698int SkCanvas::save(SaveFlags flags) {
699 // call shared impl
700 return this->internalSave(flags);
701}
702
703#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
704#define C16MASK (1 << SkBitmap::kRGB_565_Config)
705#define C8MASK (1 << SkBitmap::kA8_Config)
706
707static SkBitmap::Config resolve_config(SkCanvas* canvas,
708 const SkIRect& bounds,
709 SkCanvas::SaveFlags flags,
710 bool* isOpaque) {
711 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
712
713#if 0
714 // loop through and union all the configs we may draw into
715 uint32_t configMask = 0;
716 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
717 {
718 SkDevice* device = canvas->getLayerDevice(i);
719 if (device->intersects(bounds))
720 configMask |= 1 << device->config();
721 }
722
723 // if the caller wants alpha or fullcolor, we can't return 565
724 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
725 SkCanvas::kHasAlphaLayer_SaveFlag))
726 configMask &= ~C16MASK;
727
728 switch (configMask) {
729 case C8MASK: // if we only have A8, return that
730 return SkBitmap::kA8_Config;
731
732 case C16MASK: // if we only have 565, return that
733 return SkBitmap::kRGB_565_Config;
734
735 default:
736 return SkBitmap::kARGB_8888_Config; // default answer
737 }
738#else
739 return SkBitmap::kARGB_8888_Config; // default answer
740#endif
741}
742
743static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
744 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
745}
746
junov@chromium.orga907ac32012-02-24 21:54:07 +0000747bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
748 SkIRect* intersection) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000749 SkIRect clipBounds;
750 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000751 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000752 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000753 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000754 if (NULL != bounds) {
755 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000756
reed@android.com8a1c16f2008-12-17 15:59:43 +0000757 this->getTotalMatrix().mapRect(&r, *bounds);
758 r.roundOut(&ir);
759 // early exit if the layer's bounds are clipped out
760 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000761 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000762 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000763 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000764 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000765 }
766 } else { // no user bounds, so just use the clip
767 ir = clipBounds;
768 }
769
reed@google.com5c3d1472011-02-22 19:12:23 +0000770 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000771
reed@android.com8a1c16f2008-12-17 15:59:43 +0000772 // early exit if the clip is now empty
773 if (bounds_affects_clip(flags) &&
reed@google.com00177082011-10-12 14:34:30 +0000774 !fMCRec->fRasterClip->op(ir, SkRegion::kIntersect_Op)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000775 return false;
776 }
777
778 if (intersection) {
779 *intersection = ir;
780 }
781 return true;
782}
783
784int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
785 SaveFlags flags) {
reed@google.com8926b162012-03-23 15:36:36 +0000786 return this->internalSaveLayer(bounds, paint, flags, false);
787}
788
789int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
790 SaveFlags flags, bool justForImageFilter) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000791 // do this before we create the layer. We don't call the public save() since
792 // that would invoke a possibly overridden virtual
793 int count = this->internalSave(flags);
794
795 fDeviceCMDirty = true;
796
797 SkIRect ir;
798 if (!this->clipRectBounds(bounds, flags, &ir)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000799 return count;
800 }
801
reed@google.comb55deeb2012-01-06 14:43:09 +0000802 // Kill the imagefilter if our device doesn't allow it
803 SkLazyPaint lazyP;
804 if (paint && paint->getImageFilter()) {
805 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000806 if (justForImageFilter) {
807 // early exit if the layer was just for the imageFilter
808 return count;
809 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000810 SkPaint* p = lazyP.set(*paint);
811 p->setImageFilter(NULL);
812 paint = p;
813 }
814 }
815
reed@android.com8a1c16f2008-12-17 15:59:43 +0000816 bool isOpaque;
817 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
818
reed@google.com76dd2772012-01-05 21:15:07 +0000819 SkDevice* device;
820 if (paint && paint->getImageFilter()) {
821 device = this->createCompatibleDevice(config, ir.width(), ir.height(),
822 isOpaque);
823 } else {
824 device = this->createLayerDevice(config, ir.width(), ir.height(),
825 isOpaque);
826 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000827 if (NULL == device) {
828 SkDebugf("Unable to create device for layer.");
829 return count;
830 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000831
reed@google.com6f8f2922011-03-04 22:27:10 +0000832 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000833 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000834 device->unref();
835
836 layer->fNext = fMCRec->fTopLayer;
837 fMCRec->fLayer = layer;
838 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
839
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000840 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000841 return count;
842}
843
844int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
845 SaveFlags flags) {
846 if (0xFF == alpha) {
847 return this->saveLayer(bounds, NULL, flags);
848 } else {
849 SkPaint tmpPaint;
850 tmpPaint.setAlpha(alpha);
851 return this->saveLayer(bounds, &tmpPaint, flags);
852 }
853}
854
855void SkCanvas::restore() {
856 // check for underflow
857 if (fMCStack.count() > 1) {
858 this->internalRestore();
859 }
860}
861
862void SkCanvas::internalRestore() {
863 SkASSERT(fMCStack.count() != 0);
864
865 fDeviceCMDirty = true;
866 fLocalBoundsCompareTypeDirty = true;
867
reed@google.com5c3d1472011-02-22 19:12:23 +0000868 fClipStack.restore();
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000869 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000870 DeviceCM* layer = fMCRec->fLayer; // may be null
871 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
872 fMCRec->fLayer = NULL;
873
874 // now do the normal restore()
875 fMCRec->~MCRec(); // balanced in save()
876 fMCStack.pop_back();
877 fMCRec = (MCRec*)fMCStack.back();
878
879 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
880 since if we're being recorded, we don't want to record this (the
881 recorder will have already recorded the restore).
882 */
883 if (NULL != layer) {
884 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000885 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000886 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
887 layer->fPaint);
888 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000889 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000890
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000891 SkASSERT(fSaveLayerCount > 0);
892 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000893 }
894 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000895 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000896
897 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000898}
899
900int SkCanvas::getSaveCount() const {
901 return fMCStack.count();
902}
903
904void SkCanvas::restoreToCount(int count) {
905 // sanity check
906 if (count < 1) {
907 count = 1;
908 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000909
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000910 int n = this->getSaveCount() - count;
911 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000912 this->restore();
913 }
914}
915
reed@google.com7c202932011-12-14 18:48:05 +0000916bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000917 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000918}
919
reed@android.com8a1c16f2008-12-17 15:59:43 +0000920/////////////////////////////////////////////////////////////////////////////
921
922// can't draw it if its empty, or its too big for a fixed-point width or height
923static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000924 return bitmap.width() <= 0 || bitmap.height() <= 0
925#ifndef SK_ALLOW_OVER_32K_BITMAPS
926 || bitmap.width() > 32767 || bitmap.height() > 32767
927#endif
928 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000929}
930
reed@android.comf2b98d62010-12-20 18:26:13 +0000931void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000932 const SkMatrix& matrix, const SkPaint* paint) {
933 if (reject_bitmap(bitmap)) {
934 return;
935 }
936
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000937 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000938 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000939 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000940 }
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000941 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000942}
943
reed@google.com76dd2772012-01-05 21:15:07 +0000944#include "SkImageFilter.h"
945
946class DeviceImageFilterProxy : public SkImageFilter::Proxy {
947public:
948 DeviceImageFilterProxy(SkDevice* device) : fDevice(device) {}
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000949
reed@google.com8926b162012-03-23 15:36:36 +0000950 virtual SkDevice* createDevice(int w, int h) SK_OVERRIDE {
951 return fDevice->createCompatibleDevice(SkBitmap::kARGB_8888_Config,
952 w, h, false);
953 }
954 virtual bool canHandleImageFilter(SkImageFilter* filter) SK_OVERRIDE {
955 return fDevice->canHandleImageFilter(filter);
956 }
957 virtual bool filterImage(SkImageFilter* filter, const SkBitmap& src,
reed@google.com76dd2772012-01-05 21:15:07 +0000958 const SkMatrix& ctm,
reed@google.com8926b162012-03-23 15:36:36 +0000959 SkBitmap* result, SkIPoint* offset) SK_OVERRIDE {
960 return fDevice->filterImage(filter, src, ctm, result, offset);
961 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000962
reed@google.com76dd2772012-01-05 21:15:07 +0000963private:
964 SkDevice* fDevice;
965};
966
reed@google.com8926b162012-03-23 15:36:36 +0000967void SkCanvas::internalDrawDevice(SkDevice* srcDev, int x, int y,
968 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000969 SkPaint tmp;
970 if (NULL == paint) {
971 tmp.setDither(true);
972 paint = &tmp;
973 }
reed@google.com4b226022011-01-11 18:32:13 +0000974
reed@google.com8926b162012-03-23 15:36:36 +0000975 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000976 while (iter.next()) {
reed@google.comb55deeb2012-01-06 14:43:09 +0000977 SkDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +0000978 paint = &looper.paint();
979 SkImageFilter* filter = paint->getImageFilter();
980 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +0000981 if (filter && !dstDev->canHandleImageFilter(filter)) {
reed@google.comb55deeb2012-01-06 14:43:09 +0000982 DeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +0000983 SkBitmap dst;
reed@google.comb55deeb2012-01-06 14:43:09 +0000984 const SkBitmap& src = srcDev->accessBitmap(false);
reed@google.com76dd2772012-01-05 21:15:07 +0000985 if (filter->filterImage(&proxy, src, *iter.fMatrix, &dst, &pos)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +0000986 SkPaint tmpUnfiltered(*paint);
987 tmpUnfiltered.setImageFilter(NULL);
988 dstDev->drawSprite(iter, dst, pos.x(), pos.y(), tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +0000989 }
990 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +0000991 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +0000992 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000993 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000994 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +0000995}
996
reed@google.com8926b162012-03-23 15:36:36 +0000997void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
998 const SkPaint* paint) {
999 SkDEBUGCODE(bitmap.validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001000
reed@google.com8926b162012-03-23 15:36:36 +00001001 if (reject_bitmap(bitmap)) {
1002 return;
1003 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001004
reed@google.com8926b162012-03-23 15:36:36 +00001005 SkPaint tmp;
1006 if (NULL == paint) {
1007 paint = &tmp;
1008 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001009
reed@google.com8926b162012-03-23 15:36:36 +00001010 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001011
reed@google.com8926b162012-03-23 15:36:36 +00001012 while (iter.next()) {
1013 paint = &looper.paint();
1014 SkImageFilter* filter = paint->getImageFilter();
1015 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1016 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
1017 DeviceImageFilterProxy proxy(iter.fDevice);
1018 SkBitmap dst;
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001019 if (filter->filterImage(&proxy, bitmap, *iter.fMatrix,
1020 &dst, &pos)) {
1021 SkPaint tmpUnfiltered(*paint);
1022 tmpUnfiltered.setImageFilter(NULL);
1023 iter.fDevice->drawSprite(iter, dst, pos.x(), pos.y(),
1024 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001025 }
1026 } else {
1027 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1028 }
1029 }
1030 LOOPER_END
1031}
1032
reed@android.com8a1c16f2008-12-17 15:59:43 +00001033/////////////////////////////////////////////////////////////////////////////
1034
1035bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
1036 fDeviceCMDirty = true;
1037 fLocalBoundsCompareTypeDirty = true;
1038 return fMCRec->fMatrix->preTranslate(dx, dy);
1039}
1040
1041bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
1042 fDeviceCMDirty = true;
1043 fLocalBoundsCompareTypeDirty = true;
1044 return fMCRec->fMatrix->preScale(sx, sy);
1045}
1046
1047bool SkCanvas::rotate(SkScalar degrees) {
1048 fDeviceCMDirty = true;
1049 fLocalBoundsCompareTypeDirty = true;
1050 return fMCRec->fMatrix->preRotate(degrees);
1051}
1052
1053bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
1054 fDeviceCMDirty = true;
1055 fLocalBoundsCompareTypeDirty = true;
1056 return fMCRec->fMatrix->preSkew(sx, sy);
1057}
1058
1059bool SkCanvas::concat(const SkMatrix& matrix) {
1060 fDeviceCMDirty = true;
1061 fLocalBoundsCompareTypeDirty = true;
1062 return fMCRec->fMatrix->preConcat(matrix);
1063}
1064
1065void SkCanvas::setMatrix(const SkMatrix& matrix) {
1066 fDeviceCMDirty = true;
1067 fLocalBoundsCompareTypeDirty = true;
1068 *fMCRec->fMatrix = matrix;
1069}
1070
1071// this is not virtual, so it must call a virtual method so that subclasses
1072// will see its action
1073void SkCanvas::resetMatrix() {
1074 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001075
reed@android.com8a1c16f2008-12-17 15:59:43 +00001076 matrix.reset();
1077 this->setMatrix(matrix);
1078}
1079
1080//////////////////////////////////////////////////////////////////////////////
1081
reed@google.comc42d35d2011-10-12 11:57:42 +00001082bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001083#ifdef SK_ENABLE_CLIP_QUICKREJECT
1084 if (SkRegion::kIntersect_Op == op) {
1085 if (fMCRec->fRasterClip->isEmpty()) {
1086 return false;
1087 }
1088
reed@google.com3b3e8952012-08-16 20:53:31 +00001089 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001090 fDeviceCMDirty = true;
1091 fLocalBoundsCompareTypeDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001092
1093 fClipStack.clipEmpty();
1094 return fMCRec->fRasterClip->setEmpty();
1095 }
1096 }
1097#endif
1098
reed@google.com5c3d1472011-02-22 19:12:23 +00001099 AutoValidateClip avc(this);
1100
reed@android.com8a1c16f2008-12-17 15:59:43 +00001101 fDeviceCMDirty = true;
1102 fLocalBoundsCompareTypeDirty = true;
1103
1104 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +00001105 // for these simpler matrices, we can stay a rect ever after applying
1106 // the matrix. This means we don't have to a) make a path, and b) tell
1107 // the region code to scan-convert the path, only to discover that it
1108 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001109 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001110
1111 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +00001112 fClipStack.clipDevRect(r, op, doAA);
1113 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001114 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +00001115 // since we're rotate or some such thing, we convert the rect to a path
1116 // and clip against that, since it can handle any matrix. However, to
1117 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1118 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001119 SkPath path;
1120
1121 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +00001122 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001123 }
1124}
1125
reed@google.com00177082011-10-12 14:34:30 +00001126static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +00001127 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001128 // base is used to limit the size (and therefore memory allocation) of the
1129 // region that results from scan converting devPath.
1130 SkRegion base;
1131
reed@google.com819c9212011-02-23 18:56:55 +00001132 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001133 // since we are intersect, we can do better (tighter) with currRgn's
1134 // bounds, than just using the device. However, if currRgn is complex,
1135 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001136 if (currClip->isRect()) {
1137 return currClip->setPath(devPath, *currClip, doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001138 } else {
reed@google.com00177082011-10-12 14:34:30 +00001139 base.setRect(currClip->getBounds());
1140 SkRasterClip clip;
1141 clip.setPath(devPath, base, doAA);
1142 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001143 }
reed@google.com819c9212011-02-23 18:56:55 +00001144 } else {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001145 const SkDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001146 if (!device) {
1147 return currClip->setEmpty();
1148 }
1149
junov@chromium.orga907ac32012-02-24 21:54:07 +00001150 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001151
1152 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +00001153 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001154 } else {
reed@google.com00177082011-10-12 14:34:30 +00001155 SkRasterClip clip;
1156 clip.setPath(devPath, base, doAA);
1157 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001158 }
1159 }
1160}
1161
reed@google.comc42d35d2011-10-12 11:57:42 +00001162bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001163#ifdef SK_ENABLE_CLIP_QUICKREJECT
1164 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1165 if (fMCRec->fRasterClip->isEmpty()) {
1166 return false;
1167 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001168
reed@google.com3b3e8952012-08-16 20:53:31 +00001169 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001170 fDeviceCMDirty = true;
1171 fLocalBoundsCompareTypeDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001172
reed@google.comda17f752012-08-16 18:27:05 +00001173 fClipStack.clipEmpty();
1174 return fMCRec->fRasterClip->setEmpty();
1175 }
1176 }
1177#endif
1178
reed@google.com5c3d1472011-02-22 19:12:23 +00001179 AutoValidateClip avc(this);
1180
reed@android.com8a1c16f2008-12-17 15:59:43 +00001181 fDeviceCMDirty = true;
1182 fLocalBoundsCompareTypeDirty = true;
1183
1184 SkPath devPath;
1185 path.transform(*fMCRec->fMatrix, &devPath);
1186
reed@google.comfe701122011-11-08 19:41:23 +00001187 // Check if the transfomation, or the original path itself
1188 // made us empty. Note this can also happen if we contained NaN
1189 // values. computing the bounds detects this, and will set our
1190 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1191 if (devPath.getBounds().isEmpty()) {
1192 // resetting the path will remove any NaN or other wanky values
1193 // that might upset our scan converter.
1194 devPath.reset();
1195 }
1196
reed@google.com5c3d1472011-02-22 19:12:23 +00001197 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +00001198 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +00001199
reed@google.com00177082011-10-12 14:34:30 +00001200 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001201}
1202
1203bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001204 AutoValidateClip avc(this);
1205
reed@android.com8a1c16f2008-12-17 15:59:43 +00001206 fDeviceCMDirty = true;
1207 fLocalBoundsCompareTypeDirty = true;
1208
reed@google.com5c3d1472011-02-22 19:12:23 +00001209 // todo: signal fClipStack that we have a region, and therefore (I guess)
1210 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001211 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001212
reed@google.com00177082011-10-12 14:34:30 +00001213 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001214}
1215
reed@google.com819c9212011-02-23 18:56:55 +00001216#ifdef SK_DEBUG
1217void SkCanvas::validateClip() const {
1218 // construct clipRgn from the clipstack
1219 const SkDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001220 if (!device) {
1221 SkASSERT(this->getTotalClip().isEmpty());
1222 return;
1223 }
1224
reed@google.com819c9212011-02-23 18:56:55 +00001225 SkIRect ir;
1226 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001227 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001228
robertphillips@google.com80214e22012-07-20 15:33:18 +00001229 SkClipStack::B2TIter iter(fClipStack);
1230 const SkClipStack::B2TIter::Clip* clip;
reed@google.com819c9212011-02-23 18:56:55 +00001231 while ((clip = iter.next()) != NULL) {
1232 if (clip->fPath) {
reed@google.com00177082011-10-12 14:34:30 +00001233 clipPathHelper(this, &tmpClip, *clip->fPath, clip->fOp, clip->fDoAA);
reed@google.com819c9212011-02-23 18:56:55 +00001234 } else if (clip->fRect) {
1235 clip->fRect->round(&ir);
reed@google.com00177082011-10-12 14:34:30 +00001236 tmpClip.op(ir, clip->fOp);
reed@google.com819c9212011-02-23 18:56:55 +00001237 } else {
reed@google.com00177082011-10-12 14:34:30 +00001238 tmpClip.setEmpty();
reed@google.com819c9212011-02-23 18:56:55 +00001239 }
1240 }
1241
reed@google.com6f8f2922011-03-04 22:27:10 +00001242#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001243 // now compare against the current rgn
1244 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001245 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001246#endif
reed@google.com819c9212011-02-23 18:56:55 +00001247}
1248#endif
1249
reed@google.com90c07ea2012-04-13 13:50:27 +00001250void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001251 SkClipStack::B2TIter iter(fClipStack);
1252 const SkClipStack::B2TIter::Clip* clip;
reed@google.com90c07ea2012-04-13 13:50:27 +00001253
robertphillips@google.com7460b372012-04-25 16:54:51 +00001254 SkRect empty = { 0, 0, 0, 0 };
reed@google.com90c07ea2012-04-13 13:50:27 +00001255 while ((clip = iter.next()) != NULL) {
1256 if (clip->fPath) {
1257 visitor->clipPath(*clip->fPath, clip->fOp, clip->fDoAA);
1258 } else if (clip->fRect) {
1259 visitor->clipRect(*clip->fRect, clip->fOp, clip->fDoAA);
1260 } else {
1261 visitor->clipRect(empty, SkRegion::kIntersect_Op, false);
1262 }
1263 }
1264}
1265
reed@google.com5c3d1472011-02-22 19:12:23 +00001266///////////////////////////////////////////////////////////////////////////////
1267
reed@google.com3b3e8952012-08-16 20:53:31 +00001268void SkCanvas::computeLocalClipBoundsCompareType() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001269 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001270
reed@google.com3b3e8952012-08-16 20:53:31 +00001271 if (!this->getClipBounds(&r)) {
1272 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001273 } else {
reed@google.com3b3e8952012-08-16 20:53:31 +00001274 fLocalBoundsCompareType.set(SkScalarToCompareType(r.fLeft),
1275 SkScalarToCompareType(r.fTop),
1276 SkScalarToCompareType(r.fRight),
1277 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001278 }
1279}
1280
reed@google.com3b3e8952012-08-16 20:53:31 +00001281bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001282
reed@google.com16078632011-12-06 18:56:37 +00001283 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001284 return true;
1285
reed@google.com00177082011-10-12 14:34:30 +00001286 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001287 return true;
1288 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001289
tomhudson@google.com8d430182011-06-06 19:11:19 +00001290 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001291 SkRect dst;
1292 fMCRec->fMatrix->mapRect(&dst, rect);
1293 SkIRect idst;
1294 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001295 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001296 } else {
reed@google.com3b3e8952012-08-16 20:53:31 +00001297 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
reed@android.comd252db02009-04-01 18:31:44 +00001298
reed@android.coma380ae42009-07-21 01:17:02 +00001299 // for speed, do the most likely reject compares first
1300 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1301 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1302 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1303 return true;
1304 }
1305 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1306 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1307 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1308 return true;
1309 }
1310 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001311 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001312}
1313
reed@google.com3b3e8952012-08-16 20:53:31 +00001314bool SkCanvas::quickReject(const SkPath& path) const {
1315 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001316}
1317
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001318static inline int pinIntForScalar(int x) {
1319#ifdef SK_SCALAR_IS_FIXED
1320 if (x < SK_MinS16) {
1321 x = SK_MinS16;
1322 } else if (x > SK_MaxS16) {
1323 x = SK_MaxS16;
1324 }
1325#endif
1326 return x;
1327}
1328
reed@google.com3b3e8952012-08-16 20:53:31 +00001329bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001330 SkIRect ibounds;
1331 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001332 return false;
1333 }
1334
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001335 SkMatrix inverse;
1336 // if we can't invert the CTM, we can't return local clip bounds
1337 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001338 if (bounds) {
1339 bounds->setEmpty();
1340 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001341 return false;
1342 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001343
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001344 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001345 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001346 // adjust it outwards in case we are antialiasing
1347 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001348
1349 // SkRect::iset() will correctly assert if we pass a value out of range
1350 // (when SkScalar==fixed), so we pin to legal values. This does not
1351 // really returnt the correct answer, but its the best we can do given
1352 // that we've promised to return SkRect (even though we support devices
1353 // that can be larger than 32K in width or height).
1354 r.iset(pinIntForScalar(ibounds.fLeft - inset),
1355 pinIntForScalar(ibounds.fTop - inset),
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001356 pinIntForScalar(ibounds.fRight + inset),
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001357 pinIntForScalar(ibounds.fBottom + inset));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001358 inverse.mapRect(bounds, r);
1359 }
1360 return true;
1361}
1362
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001363bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001364 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001365 if (clip.isEmpty()) {
1366 if (bounds) {
1367 bounds->setEmpty();
1368 }
1369 return false;
1370 }
1371
1372 if (NULL != bounds) {
1373 *bounds = clip.getBounds();
1374 }
1375 return true;
1376}
1377
reed@android.com8a1c16f2008-12-17 15:59:43 +00001378const SkMatrix& SkCanvas::getTotalMatrix() const {
1379 return *fMCRec->fMatrix;
1380}
1381
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001382SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001383 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1384 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001385 return kComplex_ClipType;
1386}
1387
reed@android.com8a1c16f2008-12-17 15:59:43 +00001388const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001389 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001390}
1391
reed@android.comf2b98d62010-12-20 18:26:13 +00001392void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1393 if (NULL == matrix || matrix->isIdentity()) {
1394 if (fUseExternalMatrix) {
1395 fDeviceCMDirty = true;
1396 }
1397 fUseExternalMatrix = false;
1398 } else {
mike@reedtribe.org90bf4272012-04-14 19:06:16 +00001399 if (matrix->invert(&fExternalInverse)) {
1400 fExternalMatrix = *matrix;
1401 fUseExternalMatrix = true;
1402 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.comfc9a3be2012-04-12 13:52:14 +00001403 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001404 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001405}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001406
bsalomon@google.come97f0852011-06-17 13:10:25 +00001407SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
1408 int width, int height,
1409 bool isOpaque) {
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001410 SkDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001411 if (device) {
1412 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1413 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001414 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001415 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001416 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001417}
1418
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001419SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
bsalomon@google.come97f0852011-06-17 13:10:25 +00001420 int width, int height,
1421 bool isOpaque) {
1422 SkDevice* device = this->getDevice();
1423 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001424 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001425 } else {
1426 return NULL;
1427 }
1428}
1429
1430
reed@android.com8a1c16f2008-12-17 15:59:43 +00001431//////////////////////////////////////////////////////////////////////////////
1432// These are the virtual drawing methods
1433//////////////////////////////////////////////////////////////////////////////
1434
reed@google.com2a981812011-04-14 18:59:28 +00001435void SkCanvas::clear(SkColor color) {
1436 SkDrawIter iter(this);
1437
1438 while (iter.next()) {
1439 iter.fDevice->clear(color);
1440 }
1441}
1442
reed@android.com8a1c16f2008-12-17 15:59:43 +00001443void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001444 this->internalDrawPaint(paint);
1445}
1446
1447void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001448 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001449
1450 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001451 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001452 }
1453
reed@google.com4e2b3d32011-04-07 14:18:59 +00001454 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001455}
1456
1457void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1458 const SkPaint& paint) {
1459 if ((long)count <= 0) {
1460 return;
1461 }
1462
reed@google.coma584aed2012-05-16 14:06:02 +00001463 if (paint.canComputeFastBounds()) {
1464 SkRect r;
1465 // special-case 2 points (common for drawing a single line)
1466 if (2 == count) {
1467 r.set(pts[0], pts[1]);
1468 } else {
1469 r.set(pts, count);
1470 }
1471 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001472 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
reed@google.coma584aed2012-05-16 14:06:02 +00001473 return;
1474 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001475 }
reed@google.coma584aed2012-05-16 14:06:02 +00001476
reed@android.com8a1c16f2008-12-17 15:59:43 +00001477 SkASSERT(pts != NULL);
1478
reed@google.com4e2b3d32011-04-07 14:18:59 +00001479 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001480
reed@android.com8a1c16f2008-12-17 15:59:43 +00001481 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001482 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001483 }
reed@google.com4b226022011-01-11 18:32:13 +00001484
reed@google.com4e2b3d32011-04-07 14:18:59 +00001485 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001486}
1487
1488void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1489 if (paint.canComputeFastBounds()) {
1490 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001491 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001492 return;
1493 }
1494 }
reed@google.com4b226022011-01-11 18:32:13 +00001495
reed@google.com4e2b3d32011-04-07 14:18:59 +00001496 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001497
1498 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001499 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001500 }
1501
reed@google.com4e2b3d32011-04-07 14:18:59 +00001502 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001503}
1504
1505void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00001506 if (!path.isFinite()) {
1507 return;
1508 }
1509
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001510 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001511 SkRect storage;
1512 const SkRect& bounds = path.getBounds();
reed@google.com3b3e8952012-08-16 20:53:31 +00001513 if (this->quickReject(paint.computeFastBounds(bounds, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001514 return;
1515 }
1516 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001517 if (path.isEmpty()) {
1518 if (path.isInverseFillType()) {
1519 this->internalDrawPaint(paint);
1520 }
1521 return;
1522 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001523
reed@google.com4e2b3d32011-04-07 14:18:59 +00001524 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001525
1526 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001527 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001528 }
1529
reed@google.com4e2b3d32011-04-07 14:18:59 +00001530 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001531}
1532
1533void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1534 const SkPaint* paint) {
1535 SkDEBUGCODE(bitmap.validate();)
1536
reed@google.com3d608122011-11-21 15:16:16 +00001537 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001538 SkRect bounds = {
1539 x, y,
1540 x + SkIntToScalar(bitmap.width()),
1541 y + SkIntToScalar(bitmap.height())
1542 };
1543 if (paint) {
1544 (void)paint->computeFastBounds(bounds, &bounds);
1545 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001546 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001547 return;
1548 }
1549 }
reed@google.com4b226022011-01-11 18:32:13 +00001550
reed@android.com8a1c16f2008-12-17 15:59:43 +00001551 SkMatrix matrix;
1552 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001553 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001554}
1555
reed@google.com9987ec32011-09-07 11:57:52 +00001556// this one is non-virtual, so it can be called safely by other canvas apis
1557void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1558 const SkRect& dst, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001559 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1560 return;
1561 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001562
reed@android.com8a1c16f2008-12-17 15:59:43 +00001563 // do this now, to avoid the cost of calling extract for RLE bitmaps
reed@google.com3d608122011-11-21 15:16:16 +00001564 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001565 SkRect storage;
1566 const SkRect* bounds = &dst;
1567 if (paint) {
1568 bounds = &paint->computeFastBounds(dst, &storage);
1569 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001570 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001571 return;
1572 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001573 }
reed@google.com3d608122011-11-21 15:16:16 +00001574
reed@android.com8a1c16f2008-12-17 15:59:43 +00001575 const SkBitmap* bitmapPtr = &bitmap;
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001576
reed@android.com8a1c16f2008-12-17 15:59:43 +00001577 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001578 SkRect tmpSrc;
1579 if (src) {
1580 tmpSrc.set(*src);
1581 // if the extract process clipped off the top or left of the
1582 // original, we adjust for that here to get the position right.
1583 if (tmpSrc.fLeft > 0) {
1584 tmpSrc.fRight -= tmpSrc.fLeft;
1585 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001586 }
reed@android.com87899992009-10-16 14:48:38 +00001587 if (tmpSrc.fTop > 0) {
1588 tmpSrc.fBottom -= tmpSrc.fTop;
1589 tmpSrc.fTop = 0;
1590 }
1591 } else {
1592 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1593 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001594 }
reed@android.com87899992009-10-16 14:48:38 +00001595 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001596
reed@android.comf2b98d62010-12-20 18:26:13 +00001597 // ensure that src is "valid" before we pass it to our internal routines
1598 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1599 SkIRect tmpISrc;
1600 if (src) {
1601 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
reed@google.com2ade0862011-03-17 17:48:04 +00001602 if (!tmpISrc.intersect(*src)) {
1603 return;
1604 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001605 src = &tmpISrc;
1606 }
1607 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001608}
1609
reed@google.com9987ec32011-09-07 11:57:52 +00001610void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1611 const SkRect& dst, const SkPaint* paint) {
1612 SkDEBUGCODE(bitmap.validate();)
1613 this->internalDrawBitmapRect(bitmap, src, dst, paint);
1614}
1615
reed@android.com8a1c16f2008-12-17 15:59:43 +00001616void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1617 const SkPaint* paint) {
1618 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001619 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001620}
1621
reed@android.comf2b98d62010-12-20 18:26:13 +00001622void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1623 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001624 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001625
reed@google.com4e2b3d32011-04-07 14:18:59 +00001626 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001627
reed@android.com8a1c16f2008-12-17 15:59:43 +00001628 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001629 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001630 }
reed@android.com9b039062009-02-11 15:09:58 +00001631
reed@google.com4e2b3d32011-04-07 14:18:59 +00001632 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001633}
1634
reed@google.com9987ec32011-09-07 11:57:52 +00001635void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1636 const SkIRect& center, const SkRect& dst,
1637 const SkPaint* paint) {
reed@google.com3d608122011-11-21 15:16:16 +00001638 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001639 SkRect storage;
1640 const SkRect* bounds = &dst;
1641 if (paint) {
1642 bounds = &paint->computeFastBounds(dst, &storage);
1643 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001644 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001645 return;
1646 }
1647 }
1648
reed@google.com9987ec32011-09-07 11:57:52 +00001649 const int32_t w = bitmap.width();
1650 const int32_t h = bitmap.height();
1651
1652 SkIRect c = center;
1653 // pin center to the bounds of the bitmap
1654 c.fLeft = SkMax32(0, center.fLeft);
1655 c.fTop = SkMax32(0, center.fTop);
1656 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1657 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1658
1659 const int32_t srcX[4] = { 0, c.fLeft, c.fRight, w };
1660 const int32_t srcY[4] = { 0, c.fTop, c.fBottom, h };
1661 SkScalar dstX[4] = {
1662 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1663 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1664 };
1665 SkScalar dstY[4] = {
1666 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1667 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1668 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001669
reed@google.com9987ec32011-09-07 11:57:52 +00001670 if (dstX[1] > dstX[2]) {
1671 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1672 dstX[2] = dstX[1];
1673 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001674
reed@google.com9987ec32011-09-07 11:57:52 +00001675 if (dstY[1] > dstY[2]) {
1676 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1677 dstY[2] = dstY[1];
1678 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001679
reed@google.com9987ec32011-09-07 11:57:52 +00001680 SkIRect s;
1681 SkRect d;
1682 for (int y = 0; y < 3; y++) {
1683 s.fTop = srcY[y];
1684 s.fBottom = srcY[y+1];
1685 d.fTop = dstY[y];
1686 d.fBottom = dstY[y+1];
1687 for (int x = 0; x < 3; x++) {
1688 s.fLeft = srcX[x];
1689 s.fRight = srcX[x+1];
1690 d.fLeft = dstX[x];
1691 d.fRight = dstX[x+1];
1692 this->internalDrawBitmapRect(bitmap, &s, d, paint);
1693 }
1694 }
1695}
1696
1697void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1698 const SkRect& dst, const SkPaint* paint) {
1699 SkDEBUGCODE(bitmap.validate();)
1700
1701 // Need a device entry-point, so gpu can use a mesh
1702 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1703}
1704
reed@google.comf67e4cf2011-03-15 20:56:58 +00001705class SkDeviceFilteredPaint {
1706public:
1707 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1708 SkDevice::TextFlags flags;
1709 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001710 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001711 newPaint->setFlags(flags.fFlags);
1712 newPaint->setHinting(flags.fHinting);
1713 fPaint = newPaint;
1714 } else {
1715 fPaint = &paint;
1716 }
1717 }
1718
reed@google.comf67e4cf2011-03-15 20:56:58 +00001719 const SkPaint& paint() const { return *fPaint; }
1720
1721private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001722 const SkPaint* fPaint;
1723 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001724};
1725
bungeman@google.com52c748b2011-08-22 21:30:43 +00001726void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1727 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001728 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001729 draw.fDevice->drawRect(draw, r, paint);
1730 } else {
1731 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001732 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001733 draw.fDevice->drawRect(draw, r, p);
1734 }
1735}
1736
1737void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1738 const char text[], size_t byteLength,
1739 SkScalar x, SkScalar y) {
1740 SkASSERT(byteLength == 0 || text != NULL);
1741
1742 // nothing to draw
1743 if (text == NULL || byteLength == 0 ||
1744 draw.fClip->isEmpty() ||
1745 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1746 return;
1747 }
1748
1749 SkScalar width = 0;
1750 SkPoint start;
1751
1752 start.set(0, 0); // to avoid warning
1753 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1754 SkPaint::kStrikeThruText_Flag)) {
1755 width = paint.measureText(text, byteLength);
1756
1757 SkScalar offsetX = 0;
1758 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1759 offsetX = SkScalarHalf(width);
1760 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1761 offsetX = width;
1762 }
1763 start.set(x - offsetX, y);
1764 }
1765
1766 if (0 == width) {
1767 return;
1768 }
1769
1770 uint32_t flags = paint.getFlags();
1771
1772 if (flags & (SkPaint::kUnderlineText_Flag |
1773 SkPaint::kStrikeThruText_Flag)) {
1774 SkScalar textSize = paint.getTextSize();
1775 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1776 SkRect r;
1777
1778 r.fLeft = start.fX;
1779 r.fRight = start.fX + width;
1780
1781 if (flags & SkPaint::kUnderlineText_Flag) {
1782 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1783 start.fY);
1784 r.fTop = offset;
1785 r.fBottom = offset + height;
1786 DrawRect(draw, paint, r, textSize);
1787 }
1788 if (flags & SkPaint::kStrikeThruText_Flag) {
1789 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1790 start.fY);
1791 r.fTop = offset;
1792 r.fBottom = offset + height;
1793 DrawRect(draw, paint, r, textSize);
1794 }
1795 }
1796}
1797
reed@android.com8a1c16f2008-12-17 15:59:43 +00001798void SkCanvas::drawText(const void* text, size_t byteLength,
1799 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001800 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001801
1802 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001803 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001804 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001805 DrawTextDecorations(iter, dfp.paint(),
1806 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001807 }
1808
reed@google.com4e2b3d32011-04-07 14:18:59 +00001809 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001810}
1811
1812void SkCanvas::drawPosText(const void* text, size_t byteLength,
1813 const SkPoint pos[], const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001814 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001815
reed@android.com8a1c16f2008-12-17 15:59:43 +00001816 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001817 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001818 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001819 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001820 }
reed@google.com4b226022011-01-11 18:32:13 +00001821
reed@google.com4e2b3d32011-04-07 14:18:59 +00001822 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001823}
1824
1825void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1826 const SkScalar xpos[], SkScalar constY,
1827 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001828 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001829
reed@android.com8a1c16f2008-12-17 15:59:43 +00001830 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001831 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001832 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001833 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001834 }
reed@google.com4b226022011-01-11 18:32:13 +00001835
reed@google.com4e2b3d32011-04-07 14:18:59 +00001836 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001837}
1838
1839void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1840 const SkPath& path, const SkMatrix* matrix,
1841 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001842 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001843
1844 while (iter.next()) {
1845 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001846 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001847 }
1848
reed@google.com4e2b3d32011-04-07 14:18:59 +00001849 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001850}
1851
djsollen@google.com56c69772011-11-08 19:00:26 +00001852#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001853void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1854 const SkPoint pos[], const SkPaint& paint,
1855 const SkPath& path, const SkMatrix* matrix) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001856 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001857
1858 while (iter.next()) {
1859 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001860 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001861 }
1862
reed@google.com4e2b3d32011-04-07 14:18:59 +00001863 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001864}
1865#endif
1866
reed@android.com8a1c16f2008-12-17 15:59:43 +00001867void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1868 const SkPoint verts[], const SkPoint texs[],
1869 const SkColor colors[], SkXfermode* xmode,
1870 const uint16_t indices[], int indexCount,
1871 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001872 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001873
reed@android.com8a1c16f2008-12-17 15:59:43 +00001874 while (iter.next()) {
1875 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001876 colors, xmode, indices, indexCount,
1877 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001878 }
reed@google.com4b226022011-01-11 18:32:13 +00001879
reed@google.com4e2b3d32011-04-07 14:18:59 +00001880 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001881}
1882
reed@android.comcb608442009-12-04 21:32:27 +00001883void SkCanvas::drawData(const void* data, size_t length) {
1884 // do nothing. Subclasses may do something with the data
1885}
1886
reed@android.com8a1c16f2008-12-17 15:59:43 +00001887//////////////////////////////////////////////////////////////////////////////
1888// These methods are NOT virtual, and therefore must call back into virtual
1889// methods, rather than actually drawing themselves.
1890//////////////////////////////////////////////////////////////////////////////
1891
1892void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001893 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001894 SkPaint paint;
1895
1896 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001897 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001898 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001899 }
1900 this->drawPaint(paint);
1901}
1902
reed@android.com845fdac2009-06-23 03:01:32 +00001903void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001904 SkPaint paint;
1905
1906 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001907 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001908 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001909 }
1910 this->drawPaint(paint);
1911}
1912
1913void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1914 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001915
reed@android.com8a1c16f2008-12-17 15:59:43 +00001916 pt.set(x, y);
1917 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1918}
1919
1920void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1921 SkPoint pt;
1922 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001923
reed@android.com8a1c16f2008-12-17 15:59:43 +00001924 pt.set(x, y);
1925 paint.setColor(color);
1926 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1927}
1928
1929void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1930 const SkPaint& paint) {
1931 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001932
reed@android.com8a1c16f2008-12-17 15:59:43 +00001933 pts[0].set(x0, y0);
1934 pts[1].set(x1, y1);
1935 this->drawPoints(kLines_PointMode, 2, pts, paint);
1936}
1937
1938void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1939 SkScalar right, SkScalar bottom,
1940 const SkPaint& paint) {
1941 SkRect r;
1942
1943 r.set(left, top, right, bottom);
1944 this->drawRect(r, paint);
1945}
1946
1947void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1948 const SkPaint& paint) {
1949 if (radius < 0) {
1950 radius = 0;
1951 }
1952
1953 SkRect r;
1954 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001955
reed@android.com8a1c16f2008-12-17 15:59:43 +00001956 if (paint.canComputeFastBounds()) {
1957 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001958 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001959 return;
1960 }
1961 }
reed@google.com4b226022011-01-11 18:32:13 +00001962
reed@android.com8a1c16f2008-12-17 15:59:43 +00001963 SkPath path;
1964 path.addOval(r);
1965 this->drawPath(path, paint);
1966}
1967
1968void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1969 const SkPaint& paint) {
1970 if (rx > 0 && ry > 0) {
1971 if (paint.canComputeFastBounds()) {
1972 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001973 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001974 return;
1975 }
1976 }
1977
1978 SkPath path;
1979 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1980 this->drawPath(path, paint);
1981 } else {
1982 this->drawRect(r, paint);
1983 }
1984}
1985
1986void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1987 if (paint.canComputeFastBounds()) {
1988 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001989 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001990 return;
1991 }
1992 }
1993
1994 SkPath path;
1995 path.addOval(oval);
1996 this->drawPath(path, paint);
1997}
1998
1999void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2000 SkScalar sweepAngle, bool useCenter,
2001 const SkPaint& paint) {
2002 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2003 this->drawOval(oval, paint);
2004 } else {
2005 SkPath path;
2006 if (useCenter) {
2007 path.moveTo(oval.centerX(), oval.centerY());
2008 }
2009 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2010 if (useCenter) {
2011 path.close();
2012 }
2013 this->drawPath(path, paint);
2014 }
2015}
2016
2017void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2018 const SkPath& path, SkScalar hOffset,
2019 SkScalar vOffset, const SkPaint& paint) {
2020 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002021
reed@android.com8a1c16f2008-12-17 15:59:43 +00002022 matrix.setTranslate(hOffset, vOffset);
2023 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2024}
2025
reed@android.comf76bacf2009-05-13 14:00:33 +00002026///////////////////////////////////////////////////////////////////////////////
2027
reed@android.com8a1c16f2008-12-17 15:59:43 +00002028void SkCanvas::drawPicture(SkPicture& picture) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002029 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002030}
2031
reed@android.com8a1c16f2008-12-17 15:59:43 +00002032///////////////////////////////////////////////////////////////////////////////
2033///////////////////////////////////////////////////////////////////////////////
2034
2035SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002036 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002037
2038 SkASSERT(canvas);
2039
2040 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2041 fDone = !fImpl->next();
2042}
2043
2044SkCanvas::LayerIter::~LayerIter() {
2045 fImpl->~SkDrawIter();
2046}
2047
2048void SkCanvas::LayerIter::next() {
2049 fDone = !fImpl->next();
2050}
2051
2052SkDevice* SkCanvas::LayerIter::device() const {
2053 return fImpl->getDevice();
2054}
2055
2056const SkMatrix& SkCanvas::LayerIter::matrix() const {
2057 return fImpl->getMatrix();
2058}
2059
2060const SkPaint& SkCanvas::LayerIter::paint() const {
2061 const SkPaint* paint = fImpl->getPaint();
2062 if (NULL == paint) {
2063 paint = &fDefaultPaint;
2064 }
2065 return *paint;
2066}
2067
2068const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2069int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2070int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002071
2072///////////////////////////////////////////////////////////////////////////////
2073
2074SkCanvas::ClipVisitor::~ClipVisitor() { }