blob: 1bb3a456e9fd246b18ff06f398b5c2edca31f470 [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"
reed@google.com97af1a62012-08-28 12:19:02 +000019#include "SkSurface_Base.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000020#include "SkTemplates.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000021#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000022#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000023#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000024
reed@google.com82ce2b82012-06-26 17:43:26 +000025SK_DEFINE_INST_COUNT(SkBounder)
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000026SK_DEFINE_INST_COUNT(SkCanvas)
reed@google.com82ce2b82012-06-26 17:43:26 +000027SK_DEFINE_INST_COUNT(SkDrawFilter)
28
reed@google.comda17f752012-08-16 18:27:05 +000029// experimental for faster tiled drawing...
30//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000031
reed@android.com8a1c16f2008-12-17 15:59:43 +000032//#define SK_TRACE_SAVERESTORE
33
34#ifdef SK_TRACE_SAVERESTORE
35 static int gLayerCounter;
36 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
37 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
38
39 static int gRecCounter;
40 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
41 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
42
43 static int gCanvasCounter;
44 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
45 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
46#else
47 #define inc_layer()
48 #define dec_layer()
49 #define inc_rec()
50 #define dec_rec()
51 #define inc_canvas()
52 #define dec_canvas()
53#endif
54
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000055typedef SkTLazy<SkPaint> SkLazyPaint;
56
reed@google.com97af1a62012-08-28 12:19:02 +000057void SkCanvas::predrawNotify() {
58 if (fSurfaceBase) {
59 fSurfaceBase->aboutToDraw(this);
60 }
61}
62
reed@android.com8a1c16f2008-12-17 15:59:43 +000063///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000064
65/* This is the record we keep for each SkDevice that the user installs.
66 The clip/matrix/proc are fields that reflect the top of the save/restore
67 stack. Whenever the canvas changes, it marks a dirty flag, and then before
68 these are used (assuming we're not on a layer) we rebuild these cache
69 values: they reflect the top of the save stack, but translated and clipped
70 by the device's XY offset and bitmap-bounds.
71*/
72struct DeviceCM {
73 DeviceCM* fNext;
74 SkDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +000075 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +000076 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +000077 SkPaint* fPaint; // may be null (in the future)
reed@android.comf2b98d62010-12-20 18:26:13 +000078 // optional, related to canvas' external matrix
79 const SkMatrix* fMVMatrix;
80 const SkMatrix* fExtMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +000081
robertphillips@google.com40a1ae42012-07-13 15:36:15 +000082 DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas)
reed@android.com8a1c16f2008-12-17 15:59:43 +000083 : fNext(NULL) {
84 if (NULL != device) {
85 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +000086 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +000087 }
reed@google.com4b226022011-01-11 18:32:13 +000088 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +000089 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +000090 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000091
bungeman@google.com88edf1e2011-08-08 19:41:56 +000092 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000093 if (NULL != fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +000094 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +000095 fDevice->unref();
96 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +000097 SkDELETE(fPaint);
98 }
reed@google.com4b226022011-01-11 18:32:13 +000099
reed@google.com045e62d2011-10-24 12:19:46 +0000100 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
101 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000102 int x = fDevice->getOrigin().x();
103 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000104 int width = fDevice->width();
105 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000106
reed@android.com8a1c16f2008-12-17 15:59:43 +0000107 if ((x | y) == 0) {
108 fMatrix = &totalMatrix;
109 fClip = totalClip;
110 } else {
111 fMatrixStorage = totalMatrix;
112 fMatrixStorage.postTranslate(SkIntToScalar(-x),
113 SkIntToScalar(-y));
114 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000115
reed@android.com8a1c16f2008-12-17 15:59:43 +0000116 totalClip.translate(-x, -y, &fClip);
117 }
118
reed@google.com045e62d2011-10-24 12:19:46 +0000119 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000120
121 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000122
reed@android.com8a1c16f2008-12-17 15:59:43 +0000123 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000124 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125 SkRegion::kDifference_Op);
126 }
reed@google.com4b226022011-01-11 18:32:13 +0000127
reed@google.com045e62d2011-10-24 12:19:46 +0000128 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000129
130#ifdef SK_DEBUG
131 if (!fClip.isEmpty()) {
132 SkIRect deviceR;
133 deviceR.set(0, 0, width, height);
134 SkASSERT(deviceR.contains(fClip.getBounds()));
135 }
136#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000137 // default is to assume no external matrix
138 fMVMatrix = NULL;
139 fExtMatrix = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000140 }
reed@android.comf2b98d62010-12-20 18:26:13 +0000141
142 // can only be called after calling updateMC()
143 void updateExternalMatrix(const SkMatrix& extM, const SkMatrix& extI) {
144 fMVMatrixStorage.setConcat(extI, *fMatrix);
145 fMVMatrix = &fMVMatrixStorage;
146 fExtMatrix = &extM; // assumes extM has long life-time (owned by canvas)
147 }
148
reed@android.com8a1c16f2008-12-17 15:59:43 +0000149private:
reed@android.comf2b98d62010-12-20 18:26:13 +0000150 SkMatrix fMatrixStorage, fMVMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000151};
152
153/* This is the record we keep for each save/restore level in the stack.
154 Since a level optionally copies the matrix and/or stack, we have pointers
155 for these fields. If the value is copied for this level, the copy is
156 stored in the ...Storage field, and the pointer points to that. If the
157 value is not copied for this level, we ignore ...Storage, and just point
158 at the corresponding value in the previous level in the stack.
159*/
160class SkCanvas::MCRec {
161public:
162 MCRec* fNext;
reed@google.com00177082011-10-12 14:34:30 +0000163 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
164 SkRasterClip* fRasterClip; // points to either fRegionStorage or prev MCRec
165 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000166
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167 DeviceCM* fLayer;
168 /* If there are any layers in the stack, this points to the top-most
169 one that is at or below this level in the stack (so we know what
170 bitmap/device to draw into from this level. This value is NOT
171 reference counted, since the real owner is either our fLayer field,
172 or a previous one in a lower level.)
173 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000174 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000175
176 MCRec(const MCRec* prev, int flags) {
177 if (NULL != prev) {
178 if (flags & SkCanvas::kMatrix_SaveFlag) {
179 fMatrixStorage = *prev->fMatrix;
180 fMatrix = &fMatrixStorage;
181 } else {
182 fMatrix = prev->fMatrix;
183 }
reed@google.com4b226022011-01-11 18:32:13 +0000184
reed@android.com8a1c16f2008-12-17 15:59:43 +0000185 if (flags & SkCanvas::kClip_SaveFlag) {
reed@google.com00177082011-10-12 14:34:30 +0000186 fRasterClipStorage = *prev->fRasterClip;
187 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000188 } else {
reed@google.com00177082011-10-12 14:34:30 +0000189 fRasterClip = prev->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000190 }
191
192 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000193 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194
195 fTopLayer = prev->fTopLayer;
196 } else { // no prev
197 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000198
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199 fMatrix = &fMatrixStorage;
reed@google.com00177082011-10-12 14:34:30 +0000200 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000201 fFilter = NULL;
202 fTopLayer = NULL;
203 }
204 fLayer = NULL;
205
206 // don't bother initializing fNext
207 inc_rec();
208 }
209 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000210 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211 SkDELETE(fLayer);
212 dec_rec();
213 }
reed@google.com4b226022011-01-11 18:32:13 +0000214
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215private:
reed@google.com00177082011-10-12 14:34:30 +0000216 SkMatrix fMatrixStorage;
217 SkRasterClip fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000218};
219
220class SkDrawIter : public SkDraw {
221public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000222 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000223 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000224 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225 canvas->updateDeviceCMCache();
226
reed@google.com90c07ea2012-04-13 13:50:27 +0000227 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000228 fBounder = canvas->getBounder();
229 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000230 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231 }
reed@google.com4b226022011-01-11 18:32:13 +0000232
reed@android.com8a1c16f2008-12-17 15:59:43 +0000233 bool next() {
234 // skip over recs with empty clips
235 if (fSkipEmptyClips) {
236 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
237 fCurrLayer = fCurrLayer->fNext;
238 }
239 }
240
reed@google.comf68c5e22012-02-24 16:38:58 +0000241 const DeviceCM* rec = fCurrLayer;
242 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243
244 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000245 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
246 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247 fDevice = rec->fDevice;
248 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000249 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000250 fMVMatrix = rec->fMVMatrix;
251 fExtMatrix = rec->fExtMatrix;
252 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253
254 fCurrLayer = rec->fNext;
255 if (fBounder) {
256 fBounder->setClip(fClip);
257 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000258 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000259
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000260 fCanvas->prepareForDeviceDraw(fDevice, *fMatrix, *fClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000261 return true;
262 }
263 return false;
264 }
reed@google.com4b226022011-01-11 18:32:13 +0000265
reed@android.com8a1c16f2008-12-17 15:59:43 +0000266 SkDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000267 int getX() const { return fDevice->getOrigin().x(); }
268 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269 const SkMatrix& getMatrix() const { return *fMatrix; }
270 const SkRegion& getClip() const { return *fClip; }
271 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000272
reed@android.com8a1c16f2008-12-17 15:59:43 +0000273private:
274 SkCanvas* fCanvas;
275 const DeviceCM* fCurrLayer;
276 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000277 SkBool8 fSkipEmptyClips;
278
279 typedef SkDraw INHERITED;
280};
281
282/////////////////////////////////////////////////////////////////////////////
283
284class AutoDrawLooper {
285public:
reed@google.com8926b162012-03-23 15:36:36 +0000286 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint,
287 bool skipLayerForImageFilter = false) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000288 fCanvas = canvas;
289 fLooper = paint.getLooper();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000290 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000291 fPaint = NULL;
292 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000293 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000294 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000295
reed@google.com8926b162012-03-23 15:36:36 +0000296 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
297 SkPaint tmp;
298 tmp.setImageFilter(fOrigPaint.getImageFilter());
299 // it would be nice if we had a guess at the bounds, instead of null
300 (void)canvas->internalSaveLayer(NULL, &tmp,
301 SkCanvas::kARGB_ClipLayer_SaveFlag, true);
302 // we'll clear the imageFilter for the actual draws in next(), so
303 // it will only be applied during the restore().
304 fDoClearImageFilter = true;
305 }
306
reed@google.com4e2b3d32011-04-07 14:18:59 +0000307 if (fLooper) {
308 fLooper->init(canvas);
reed@google.com129ec222012-05-15 13:24:09 +0000309 fIsSimple = false;
310 } else {
311 // can we be marked as simple?
312 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000313 }
314 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000315
reed@android.com8a1c16f2008-12-17 15:59:43 +0000316 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000317 if (fDoClearImageFilter) {
318 fCanvas->internalRestore();
319 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000320 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000321 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000322
reed@google.com4e2b3d32011-04-07 14:18:59 +0000323 const SkPaint& paint() const {
324 SkASSERT(fPaint);
325 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000326 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000327
reed@google.com129ec222012-05-15 13:24:09 +0000328 bool next(SkDrawFilter::Type drawType) {
329 if (fDone) {
330 return false;
331 } else if (fIsSimple) {
332 fDone = true;
333 fPaint = &fOrigPaint;
334 return !fPaint->nothingToDraw();
335 } else {
336 return this->doNext(drawType);
337 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000338 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000339
reed@android.com8a1c16f2008-12-17 15:59:43 +0000340private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000341 SkLazyPaint fLazyPaint;
342 SkCanvas* fCanvas;
343 const SkPaint& fOrigPaint;
344 SkDrawLooper* fLooper;
345 SkDrawFilter* fFilter;
346 const SkPaint* fPaint;
347 int fSaveCount;
reed@google.com8926b162012-03-23 15:36:36 +0000348 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000349 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000350 bool fIsSimple;
351
352 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000353};
354
reed@google.com129ec222012-05-15 13:24:09 +0000355bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000356 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000357 SkASSERT(!fIsSimple);
358 SkASSERT(fLooper || fFilter || fDoClearImageFilter);
359
360 SkPaint* paint = fLazyPaint.set(fOrigPaint);
361
362 if (fDoClearImageFilter) {
363 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000364 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000365
reed@google.com129ec222012-05-15 13:24:09 +0000366 if (fLooper && !fLooper->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000367 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000368 return false;
369 }
370 if (fFilter) {
371 fFilter->filter(paint, drawType);
372 if (NULL == fLooper) {
373 // no looper means we only draw once
374 fDone = true;
375 }
376 }
377 fPaint = paint;
378
379 // if we only came in here for the imagefilter, mark us as done
380 if (!fLooper && !fFilter) {
381 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000382 }
383
384 // call this after any possible paint modifiers
385 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000386 fPaint = NULL;
387 return false;
388 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000389 return true;
390}
391
reed@android.com8a1c16f2008-12-17 15:59:43 +0000392/* Stack helper for managing a SkBounder. In the destructor, if we were
393 given a bounder, we call its commit() method, signifying that we are
394 done accumulating bounds for that draw.
395*/
396class SkAutoBounderCommit {
397public:
398 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
399 ~SkAutoBounderCommit() {
400 if (NULL != fBounder) {
401 fBounder->commit();
402 }
403 }
404private:
405 SkBounder* fBounder;
406};
407
408#include "SkColorPriv.h"
409
410class AutoValidator {
411public:
412 AutoValidator(SkDevice* device) : fDevice(device) {}
413 ~AutoValidator() {
414#ifdef SK_DEBUG
415 const SkBitmap& bm = fDevice->accessBitmap(false);
416 if (bm.config() == SkBitmap::kARGB_4444_Config) {
417 for (int y = 0; y < bm.height(); y++) {
418 const SkPMColor16* p = bm.getAddr16(0, y);
419 for (int x = 0; x < bm.width(); x++) {
420 SkPMColor16 c = p[x];
421 SkPMColor16Assert(c);
422 }
423 }
424 }
425#endif
426 }
427private:
428 SkDevice* fDevice;
429};
430
431////////// macros to place around the internal draw calls //////////////////
432
reed@google.com8926b162012-03-23 15:36:36 +0000433#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
434/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
reed@google.com97af1a62012-08-28 12:19:02 +0000435 this->predrawNotify(); \
reed@google.com8926b162012-03-23 15:36:36 +0000436 AutoDrawLooper looper(this, paint, true); \
437 while (looper.next(type)) { \
438 SkAutoBounderCommit ac(fBounder); \
439 SkDrawIter iter(this);
440
reed@google.com4e2b3d32011-04-07 14:18:59 +0000441#define LOOPER_BEGIN(paint, type) \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000442/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
reed@google.com97af1a62012-08-28 12:19:02 +0000443 this->predrawNotify(); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000444 AutoDrawLooper looper(this, paint); \
445 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000446 SkAutoBounderCommit ac(fBounder); \
447 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000448
reed@google.com4e2b3d32011-04-07 14:18:59 +0000449#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450
451////////////////////////////////////////////////////////////////////////////
452
453SkDevice* SkCanvas::init(SkDevice* device) {
454 fBounder = NULL;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000455 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000456 fLocalBoundsCompareTypeDirty = true;
reed@android.com199f1082009-06-10 02:12:47 +0000457 fLastDeviceToGainFocus = NULL;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000458 fDeviceCMDirty = false;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000459 fSaveLayerCount = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000460
461 fMCRec = (MCRec*)fMCStack.push_back();
462 new (fMCRec) MCRec(NULL, 0);
463
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000464 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000465 fMCRec->fTopLayer = fMCRec->fLayer;
466 fMCRec->fNext = NULL;
467
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000468 fExternalMatrix.reset();
469 fExternalInverse.reset();
reed@android.comf2b98d62010-12-20 18:26:13 +0000470 fUseExternalMatrix = false;
skia.committer@gmail.coma27096b2012-08-30 14:38:00 +0000471
reed@google.com97af1a62012-08-28 12:19:02 +0000472 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000473
reed@android.com8a1c16f2008-12-17 15:59:43 +0000474 return this->setDevice(device);
475}
476
reed@google.comcde92112011-07-06 20:00:52 +0000477SkCanvas::SkCanvas()
478: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000479 inc_canvas();
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000480
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000481 this->init(NULL);
482}
483
reed@android.com8a1c16f2008-12-17 15:59:43 +0000484SkCanvas::SkCanvas(SkDevice* device)
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000485 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000486 inc_canvas();
487
488 this->init(device);
489}
490
491SkCanvas::SkCanvas(const SkBitmap& bitmap)
492 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
493 inc_canvas();
494
reed@google.comcde92112011-07-06 20:00:52 +0000495 this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000496}
497
498SkCanvas::~SkCanvas() {
499 // free up the contents of our deque
500 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000501 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000502
reed@android.com8a1c16f2008-12-17 15:59:43 +0000503 this->internalRestore(); // restore the last, since we're going away
504
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000505 SkSafeUnref(fBounder);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000506
reed@android.com8a1c16f2008-12-17 15:59:43 +0000507 dec_canvas();
508}
509
510SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
511 SkRefCnt_SafeAssign(fBounder, bounder);
512 return bounder;
513}
514
515SkDrawFilter* SkCanvas::getDrawFilter() const {
516 return fMCRec->fFilter;
517}
518
519SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
520 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
521 return filter;
522}
523
524///////////////////////////////////////////////////////////////////////////////
525
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000526void SkCanvas::flush() {
527 SkDevice* device = this->getDevice();
528 if (device) {
529 device->flush();
530 }
531}
532
reed@google.com210ce002011-11-01 14:24:23 +0000533SkISize SkCanvas::getDeviceSize() const {
534 SkDevice* d = this->getDevice();
535 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
536}
537
reed@android.com8a1c16f2008-12-17 15:59:43 +0000538SkDevice* SkCanvas::getDevice() const {
539 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000540 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000541 SkASSERT(rec && rec->fLayer);
542 return rec->fLayer->fDevice;
543}
544
reed@google.com0b53d592012-03-19 18:26:34 +0000545SkDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
546 if (updateMatrixClip) {
547 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
548 }
reed@google.com9266fed2011-03-30 00:18:03 +0000549 return fMCRec->fTopLayer->fDevice;
550}
551
reed@android.com8a1c16f2008-12-17 15:59:43 +0000552SkDevice* SkCanvas::setDevice(SkDevice* device) {
553 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000554 SkDeque::F2BIter iter(fMCStack);
555 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000556 SkASSERT(rec && rec->fLayer);
557 SkDevice* rootDevice = rec->fLayer->fDevice;
558
559 if (rootDevice == device) {
560 return device;
561 }
reed@google.com4b226022011-01-11 18:32:13 +0000562
reed@android.com8a1c16f2008-12-17 15:59:43 +0000563 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000564 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000565 }
566 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000567 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000568 }
569
570 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
571 rootDevice = device;
572
573 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000574
reed@android.com8a1c16f2008-12-17 15:59:43 +0000575 /* Now we update our initial region to have the bounds of the new device,
576 and then intersect all of the clips in our stack with these bounds,
577 to ensure that we can't draw outside of the device's bounds (and trash
578 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000579
reed@android.com8a1c16f2008-12-17 15:59:43 +0000580 NOTE: this is only a partial-fix, since if the new device is larger than
581 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000582 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000583 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
584 reconstruct the correct clips, so this approximation will have to do.
585 The caller really needs to restore() back to the base if they want to
586 accurately take advantage of the new device bounds.
587 */
588
reed@google.com42aea282012-03-28 16:19:15 +0000589 SkIRect bounds;
590 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000591 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000592 } else {
593 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000594 }
reed@google.com42aea282012-03-28 16:19:15 +0000595 // now jam our 1st clip to be bounds, and intersect the rest with that
596 rec->fRasterClip->setRect(bounds);
597 while ((rec = (MCRec*)iter.next()) != NULL) {
598 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
599 }
600
reed@android.com8a1c16f2008-12-17 15:59:43 +0000601 return device;
602}
603
reed@google.comaf951c92011-06-16 19:10:39 +0000604SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) {
605 SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (bitmap)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606 device->unref();
607 return device;
608}
609
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000610bool SkCanvas::readPixels(SkBitmap* bitmap,
611 int x, int y,
612 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000613 SkDevice* device = this->getDevice();
614 if (!device) {
615 return false;
616 }
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000617 return device->readPixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000618}
619
bsalomon@google.comc6980972011-11-02 19:57:21 +0000620bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
bsalomon@google.comace7bd52011-11-02 19:39:51 +0000621 SkDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000622 if (!device) {
623 return false;
624 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000625
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000626 SkIRect bounds;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000627 bounds.set(0, 0, device->width(), device->height());
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000628 if (!bounds.intersect(srcRect)) {
629 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000630 }
631
632 SkBitmap tmp;
633 tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
634 bounds.height());
635 if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
636 bitmap->swap(tmp);
637 return true;
638 } else {
reed@google.com51df9e32010-12-23 19:29:18 +0000639 return false;
640 }
reed@google.com51df9e32010-12-23 19:29:18 +0000641}
642
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000643void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
644 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000645 SkDevice* device = this->getDevice();
646 if (device) {
bsalomon@google.com405d0f42012-08-29 21:26:13 +0000647 if (SkIRect::Intersects(SkIRect::MakeSize(this->getDeviceSize()),
648 SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height()))) {
649 device->accessBitmap(true);
650 device->writePixels(bitmap, x, y, config8888);
651 }
reed@google.com51df9e32010-12-23 19:29:18 +0000652 }
653}
654
junov@google.com4370aed2012-01-18 16:21:08 +0000655SkCanvas* SkCanvas::canvasForDrawIter() {
656 return this;
657}
658
reed@android.com8a1c16f2008-12-17 15:59:43 +0000659//////////////////////////////////////////////////////////////////////////////
660
reed@android.com8a1c16f2008-12-17 15:59:43 +0000661void SkCanvas::updateDeviceCMCache() {
662 if (fDeviceCMDirty) {
663 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000664 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000665 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000666
reed@android.com8a1c16f2008-12-17 15:59:43 +0000667 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000668 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000669 if (fUseExternalMatrix) {
670 layer->updateExternalMatrix(fExternalMatrix,
671 fExternalInverse);
672 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000673 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000674 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000675 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000676 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.comf2b98d62010-12-20 18:26:13 +0000677 if (fUseExternalMatrix) {
678 layer->updateExternalMatrix(fExternalMatrix,
679 fExternalInverse);
680 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000681 } while ((layer = layer->fNext) != NULL);
682 }
683 fDeviceCMDirty = false;
684 }
685}
686
reed@android.comf2b98d62010-12-20 18:26:13 +0000687void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000688 const SkRegion& clip) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000689 SkASSERT(device);
reed@android.com199f1082009-06-10 02:12:47 +0000690 if (fLastDeviceToGainFocus != device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000691 device->gainFocus(matrix, clip);
reed@android.com199f1082009-06-10 02:12:47 +0000692 fLastDeviceToGainFocus = device;
693 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000694}
695
696///////////////////////////////////////////////////////////////////////////////
697
698int SkCanvas::internalSave(SaveFlags flags) {
699 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000700
reed@android.com8a1c16f2008-12-17 15:59:43 +0000701 MCRec* newTop = (MCRec*)fMCStack.push_back();
702 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000703
reed@android.com8a1c16f2008-12-17 15:59:43 +0000704 newTop->fNext = fMCRec;
705 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000706
reed@google.com5c3d1472011-02-22 19:12:23 +0000707 fClipStack.save();
708 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
709
reed@android.com8a1c16f2008-12-17 15:59:43 +0000710 return saveCount;
711}
712
713int SkCanvas::save(SaveFlags flags) {
714 // call shared impl
715 return this->internalSave(flags);
716}
717
718#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
719#define C16MASK (1 << SkBitmap::kRGB_565_Config)
720#define C8MASK (1 << SkBitmap::kA8_Config)
721
722static SkBitmap::Config resolve_config(SkCanvas* canvas,
723 const SkIRect& bounds,
724 SkCanvas::SaveFlags flags,
725 bool* isOpaque) {
726 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
727
728#if 0
729 // loop through and union all the configs we may draw into
730 uint32_t configMask = 0;
731 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
732 {
733 SkDevice* device = canvas->getLayerDevice(i);
734 if (device->intersects(bounds))
735 configMask |= 1 << device->config();
736 }
737
738 // if the caller wants alpha or fullcolor, we can't return 565
739 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
740 SkCanvas::kHasAlphaLayer_SaveFlag))
741 configMask &= ~C16MASK;
742
743 switch (configMask) {
744 case C8MASK: // if we only have A8, return that
745 return SkBitmap::kA8_Config;
746
747 case C16MASK: // if we only have 565, return that
748 return SkBitmap::kRGB_565_Config;
749
750 default:
751 return SkBitmap::kARGB_8888_Config; // default answer
752 }
753#else
754 return SkBitmap::kARGB_8888_Config; // default answer
755#endif
756}
757
758static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
759 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
760}
761
junov@chromium.orga907ac32012-02-24 21:54:07 +0000762bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
763 SkIRect* intersection) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000764 SkIRect clipBounds;
765 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000766 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000767 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000768 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000769 if (NULL != bounds) {
770 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000771
reed@android.com8a1c16f2008-12-17 15:59:43 +0000772 this->getTotalMatrix().mapRect(&r, *bounds);
773 r.roundOut(&ir);
774 // early exit if the layer's bounds are clipped out
775 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000776 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000777 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000778 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000779 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000780 }
781 } else { // no user bounds, so just use the clip
782 ir = clipBounds;
783 }
784
reed@google.com5c3d1472011-02-22 19:12:23 +0000785 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000786
reed@android.com8a1c16f2008-12-17 15:59:43 +0000787 // early exit if the clip is now empty
788 if (bounds_affects_clip(flags) &&
reed@google.com00177082011-10-12 14:34:30 +0000789 !fMCRec->fRasterClip->op(ir, SkRegion::kIntersect_Op)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000790 return false;
791 }
792
793 if (intersection) {
794 *intersection = ir;
795 }
796 return true;
797}
798
799int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
800 SaveFlags flags) {
reed@google.com8926b162012-03-23 15:36:36 +0000801 return this->internalSaveLayer(bounds, paint, flags, false);
802}
803
804int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
805 SaveFlags flags, bool justForImageFilter) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000806 // do this before we create the layer. We don't call the public save() since
807 // that would invoke a possibly overridden virtual
808 int count = this->internalSave(flags);
809
810 fDeviceCMDirty = true;
811
812 SkIRect ir;
813 if (!this->clipRectBounds(bounds, flags, &ir)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000814 return count;
815 }
816
reed@google.comb55deeb2012-01-06 14:43:09 +0000817 // Kill the imagefilter if our device doesn't allow it
818 SkLazyPaint lazyP;
819 if (paint && paint->getImageFilter()) {
820 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000821 if (justForImageFilter) {
822 // early exit if the layer was just for the imageFilter
823 return count;
824 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000825 SkPaint* p = lazyP.set(*paint);
826 p->setImageFilter(NULL);
827 paint = p;
828 }
829 }
830
reed@android.com8a1c16f2008-12-17 15:59:43 +0000831 bool isOpaque;
832 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
833
reed@google.com76dd2772012-01-05 21:15:07 +0000834 SkDevice* device;
835 if (paint && paint->getImageFilter()) {
836 device = this->createCompatibleDevice(config, ir.width(), ir.height(),
837 isOpaque);
838 } else {
839 device = this->createLayerDevice(config, ir.width(), ir.height(),
840 isOpaque);
841 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000842 if (NULL == device) {
843 SkDebugf("Unable to create device for layer.");
844 return count;
845 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000846
reed@google.com6f8f2922011-03-04 22:27:10 +0000847 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000848 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000849 device->unref();
850
851 layer->fNext = fMCRec->fTopLayer;
852 fMCRec->fLayer = layer;
853 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
854
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000855 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000856 return count;
857}
858
859int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
860 SaveFlags flags) {
861 if (0xFF == alpha) {
862 return this->saveLayer(bounds, NULL, flags);
863 } else {
864 SkPaint tmpPaint;
865 tmpPaint.setAlpha(alpha);
866 return this->saveLayer(bounds, &tmpPaint, flags);
867 }
868}
869
870void SkCanvas::restore() {
871 // check for underflow
872 if (fMCStack.count() > 1) {
873 this->internalRestore();
874 }
875}
876
877void SkCanvas::internalRestore() {
878 SkASSERT(fMCStack.count() != 0);
879
880 fDeviceCMDirty = true;
881 fLocalBoundsCompareTypeDirty = true;
bsalomon@google.comc2a24832012-08-29 19:40:59 +0000882 // Dirty this pointer to handle the case of a new device created at the same address as the
883 // device we are restoring from. E.g.:
884 // saveLayer (creates a device)
885 // drawSomething
886 // restore (deletes the device)
887 // saveLayer (oops new device at the same address)
888 fLastDeviceToGainFocus = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000889
reed@google.com5c3d1472011-02-22 19:12:23 +0000890 fClipStack.restore();
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000891 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000892 DeviceCM* layer = fMCRec->fLayer; // may be null
893 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
894 fMCRec->fLayer = NULL;
895
896 // now do the normal restore()
897 fMCRec->~MCRec(); // balanced in save()
898 fMCStack.pop_back();
899 fMCRec = (MCRec*)fMCStack.back();
900
901 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
902 since if we're being recorded, we don't want to record this (the
903 recorder will have already recorded the restore).
904 */
905 if (NULL != layer) {
906 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000907 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000908 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
909 layer->fPaint);
910 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000911 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000912
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000913 SkASSERT(fSaveLayerCount > 0);
914 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000915 }
916 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000917 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000918
919 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000920}
921
922int SkCanvas::getSaveCount() const {
923 return fMCStack.count();
924}
925
926void SkCanvas::restoreToCount(int count) {
927 // sanity check
928 if (count < 1) {
929 count = 1;
930 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000931
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000932 int n = this->getSaveCount() - count;
933 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000934 this->restore();
935 }
936}
937
reed@google.com7c202932011-12-14 18:48:05 +0000938bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000939 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000940}
941
reed@android.com8a1c16f2008-12-17 15:59:43 +0000942/////////////////////////////////////////////////////////////////////////////
943
944// can't draw it if its empty, or its too big for a fixed-point width or height
945static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000946 return bitmap.width() <= 0 || bitmap.height() <= 0
947#ifndef SK_ALLOW_OVER_32K_BITMAPS
948 || bitmap.width() > 32767 || bitmap.height() > 32767
949#endif
950 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000951}
952
reed@android.comf2b98d62010-12-20 18:26:13 +0000953void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000954 const SkMatrix& matrix, const SkPaint* paint) {
955 if (reject_bitmap(bitmap)) {
956 return;
957 }
958
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000959 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000960 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000961 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000962 }
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000963 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000964}
965
reed@google.com76dd2772012-01-05 21:15:07 +0000966#include "SkImageFilter.h"
967
968class DeviceImageFilterProxy : public SkImageFilter::Proxy {
969public:
970 DeviceImageFilterProxy(SkDevice* device) : fDevice(device) {}
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000971
reed@google.com8926b162012-03-23 15:36:36 +0000972 virtual SkDevice* createDevice(int w, int h) SK_OVERRIDE {
973 return fDevice->createCompatibleDevice(SkBitmap::kARGB_8888_Config,
974 w, h, false);
975 }
976 virtual bool canHandleImageFilter(SkImageFilter* filter) SK_OVERRIDE {
977 return fDevice->canHandleImageFilter(filter);
978 }
979 virtual bool filterImage(SkImageFilter* filter, const SkBitmap& src,
reed@google.com76dd2772012-01-05 21:15:07 +0000980 const SkMatrix& ctm,
reed@google.com8926b162012-03-23 15:36:36 +0000981 SkBitmap* result, SkIPoint* offset) SK_OVERRIDE {
982 return fDevice->filterImage(filter, src, ctm, result, offset);
983 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000984
reed@google.com76dd2772012-01-05 21:15:07 +0000985private:
986 SkDevice* fDevice;
987};
988
reed@google.com8926b162012-03-23 15:36:36 +0000989void SkCanvas::internalDrawDevice(SkDevice* srcDev, int x, int y,
990 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000991 SkPaint tmp;
992 if (NULL == paint) {
993 tmp.setDither(true);
994 paint = &tmp;
995 }
reed@google.com4b226022011-01-11 18:32:13 +0000996
reed@google.com8926b162012-03-23 15:36:36 +0000997 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000998 while (iter.next()) {
reed@google.comb55deeb2012-01-06 14:43:09 +0000999 SkDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001000 paint = &looper.paint();
1001 SkImageFilter* filter = paint->getImageFilter();
1002 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001003 if (filter && !dstDev->canHandleImageFilter(filter)) {
reed@google.comb55deeb2012-01-06 14:43:09 +00001004 DeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001005 SkBitmap dst;
reed@google.comb55deeb2012-01-06 14:43:09 +00001006 const SkBitmap& src = srcDev->accessBitmap(false);
reed@google.com76dd2772012-01-05 21:15:07 +00001007 if (filter->filterImage(&proxy, src, *iter.fMatrix, &dst, &pos)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001008 SkPaint tmpUnfiltered(*paint);
1009 tmpUnfiltered.setImageFilter(NULL);
1010 dstDev->drawSprite(iter, dst, pos.x(), pos.y(), tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001011 }
1012 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001013 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001014 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001015 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001016 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001017}
1018
reed@google.com8926b162012-03-23 15:36:36 +00001019void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1020 const SkPaint* paint) {
1021 SkDEBUGCODE(bitmap.validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001022
reed@google.com8926b162012-03-23 15:36:36 +00001023 if (reject_bitmap(bitmap)) {
1024 return;
1025 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001026
reed@google.com8926b162012-03-23 15:36:36 +00001027 SkPaint tmp;
1028 if (NULL == paint) {
1029 paint = &tmp;
1030 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001031
reed@google.com8926b162012-03-23 15:36:36 +00001032 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001033
reed@google.com8926b162012-03-23 15:36:36 +00001034 while (iter.next()) {
1035 paint = &looper.paint();
1036 SkImageFilter* filter = paint->getImageFilter();
1037 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1038 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
1039 DeviceImageFilterProxy proxy(iter.fDevice);
1040 SkBitmap dst;
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001041 if (filter->filterImage(&proxy, bitmap, *iter.fMatrix,
1042 &dst, &pos)) {
1043 SkPaint tmpUnfiltered(*paint);
1044 tmpUnfiltered.setImageFilter(NULL);
1045 iter.fDevice->drawSprite(iter, dst, pos.x(), pos.y(),
1046 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001047 }
1048 } else {
1049 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1050 }
1051 }
1052 LOOPER_END
1053}
1054
reed@android.com8a1c16f2008-12-17 15:59:43 +00001055/////////////////////////////////////////////////////////////////////////////
1056
1057bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
1058 fDeviceCMDirty = true;
1059 fLocalBoundsCompareTypeDirty = true;
1060 return fMCRec->fMatrix->preTranslate(dx, dy);
1061}
1062
1063bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
1064 fDeviceCMDirty = true;
1065 fLocalBoundsCompareTypeDirty = true;
1066 return fMCRec->fMatrix->preScale(sx, sy);
1067}
1068
1069bool SkCanvas::rotate(SkScalar degrees) {
1070 fDeviceCMDirty = true;
1071 fLocalBoundsCompareTypeDirty = true;
1072 return fMCRec->fMatrix->preRotate(degrees);
1073}
1074
1075bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
1076 fDeviceCMDirty = true;
1077 fLocalBoundsCompareTypeDirty = true;
1078 return fMCRec->fMatrix->preSkew(sx, sy);
1079}
1080
1081bool SkCanvas::concat(const SkMatrix& matrix) {
1082 fDeviceCMDirty = true;
1083 fLocalBoundsCompareTypeDirty = true;
1084 return fMCRec->fMatrix->preConcat(matrix);
1085}
1086
1087void SkCanvas::setMatrix(const SkMatrix& matrix) {
1088 fDeviceCMDirty = true;
1089 fLocalBoundsCompareTypeDirty = true;
1090 *fMCRec->fMatrix = matrix;
1091}
1092
1093// this is not virtual, so it must call a virtual method so that subclasses
1094// will see its action
1095void SkCanvas::resetMatrix() {
1096 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001097
reed@android.com8a1c16f2008-12-17 15:59:43 +00001098 matrix.reset();
1099 this->setMatrix(matrix);
1100}
1101
1102//////////////////////////////////////////////////////////////////////////////
1103
reed@google.comc42d35d2011-10-12 11:57:42 +00001104bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001105#ifdef SK_ENABLE_CLIP_QUICKREJECT
1106 if (SkRegion::kIntersect_Op == op) {
1107 if (fMCRec->fRasterClip->isEmpty()) {
1108 return false;
1109 }
1110
reed@google.com3b3e8952012-08-16 20:53:31 +00001111 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001112 fDeviceCMDirty = true;
1113 fLocalBoundsCompareTypeDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001114
1115 fClipStack.clipEmpty();
1116 return fMCRec->fRasterClip->setEmpty();
1117 }
1118 }
1119#endif
1120
reed@google.com5c3d1472011-02-22 19:12:23 +00001121 AutoValidateClip avc(this);
1122
reed@android.com8a1c16f2008-12-17 15:59:43 +00001123 fDeviceCMDirty = true;
1124 fLocalBoundsCompareTypeDirty = true;
1125
1126 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +00001127 // for these simpler matrices, we can stay a rect ever after applying
1128 // the matrix. This means we don't have to a) make a path, and b) tell
1129 // the region code to scan-convert the path, only to discover that it
1130 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001131 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001132
1133 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +00001134 fClipStack.clipDevRect(r, op, doAA);
1135 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001136 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +00001137 // since we're rotate or some such thing, we convert the rect to a path
1138 // and clip against that, since it can handle any matrix. However, to
1139 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1140 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001141 SkPath path;
1142
1143 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +00001144 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001145 }
1146}
1147
reed@google.com00177082011-10-12 14:34:30 +00001148static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +00001149 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001150 // base is used to limit the size (and therefore memory allocation) of the
1151 // region that results from scan converting devPath.
1152 SkRegion base;
1153
reed@google.com819c9212011-02-23 18:56:55 +00001154 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001155 // since we are intersect, we can do better (tighter) with currRgn's
1156 // bounds, than just using the device. However, if currRgn is complex,
1157 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001158 if (currClip->isRect()) {
1159 return currClip->setPath(devPath, *currClip, doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001160 } else {
reed@google.com00177082011-10-12 14:34:30 +00001161 base.setRect(currClip->getBounds());
1162 SkRasterClip clip;
1163 clip.setPath(devPath, base, doAA);
1164 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001165 }
reed@google.com819c9212011-02-23 18:56:55 +00001166 } else {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001167 const SkDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001168 if (!device) {
1169 return currClip->setEmpty();
1170 }
1171
junov@chromium.orga907ac32012-02-24 21:54:07 +00001172 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001173
1174 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +00001175 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001176 } else {
reed@google.com00177082011-10-12 14:34:30 +00001177 SkRasterClip clip;
1178 clip.setPath(devPath, base, doAA);
1179 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001180 }
1181 }
1182}
1183
reed@google.comc42d35d2011-10-12 11:57:42 +00001184bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001185#ifdef SK_ENABLE_CLIP_QUICKREJECT
1186 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1187 if (fMCRec->fRasterClip->isEmpty()) {
1188 return false;
1189 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001190
reed@google.com3b3e8952012-08-16 20:53:31 +00001191 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001192 fDeviceCMDirty = true;
1193 fLocalBoundsCompareTypeDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001194
reed@google.comda17f752012-08-16 18:27:05 +00001195 fClipStack.clipEmpty();
1196 return fMCRec->fRasterClip->setEmpty();
1197 }
1198 }
1199#endif
1200
reed@google.com5c3d1472011-02-22 19:12:23 +00001201 AutoValidateClip avc(this);
1202
reed@android.com8a1c16f2008-12-17 15:59:43 +00001203 fDeviceCMDirty = true;
1204 fLocalBoundsCompareTypeDirty = true;
1205
1206 SkPath devPath;
1207 path.transform(*fMCRec->fMatrix, &devPath);
1208
reed@google.comfe701122011-11-08 19:41:23 +00001209 // Check if the transfomation, or the original path itself
1210 // made us empty. Note this can also happen if we contained NaN
1211 // values. computing the bounds detects this, and will set our
1212 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1213 if (devPath.getBounds().isEmpty()) {
1214 // resetting the path will remove any NaN or other wanky values
1215 // that might upset our scan converter.
1216 devPath.reset();
1217 }
1218
reed@google.com5c3d1472011-02-22 19:12:23 +00001219 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +00001220 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +00001221
reed@google.com00177082011-10-12 14:34:30 +00001222 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001223}
1224
1225bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001226 AutoValidateClip avc(this);
1227
reed@android.com8a1c16f2008-12-17 15:59:43 +00001228 fDeviceCMDirty = true;
1229 fLocalBoundsCompareTypeDirty = true;
1230
reed@google.com5c3d1472011-02-22 19:12:23 +00001231 // todo: signal fClipStack that we have a region, and therefore (I guess)
1232 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001233 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001234
reed@google.com00177082011-10-12 14:34:30 +00001235 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001236}
1237
reed@google.com819c9212011-02-23 18:56:55 +00001238#ifdef SK_DEBUG
1239void SkCanvas::validateClip() const {
1240 // construct clipRgn from the clipstack
1241 const SkDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001242 if (!device) {
1243 SkASSERT(this->getTotalClip().isEmpty());
1244 return;
1245 }
1246
reed@google.com819c9212011-02-23 18:56:55 +00001247 SkIRect ir;
1248 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001249 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001250
robertphillips@google.com80214e22012-07-20 15:33:18 +00001251 SkClipStack::B2TIter iter(fClipStack);
1252 const SkClipStack::B2TIter::Clip* clip;
reed@google.com819c9212011-02-23 18:56:55 +00001253 while ((clip = iter.next()) != NULL) {
1254 if (clip->fPath) {
reed@google.com00177082011-10-12 14:34:30 +00001255 clipPathHelper(this, &tmpClip, *clip->fPath, clip->fOp, clip->fDoAA);
reed@google.com819c9212011-02-23 18:56:55 +00001256 } else if (clip->fRect) {
1257 clip->fRect->round(&ir);
reed@google.com00177082011-10-12 14:34:30 +00001258 tmpClip.op(ir, clip->fOp);
reed@google.com819c9212011-02-23 18:56:55 +00001259 } else {
reed@google.com00177082011-10-12 14:34:30 +00001260 tmpClip.setEmpty();
reed@google.com819c9212011-02-23 18:56:55 +00001261 }
1262 }
1263
reed@google.com6f8f2922011-03-04 22:27:10 +00001264#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001265 // now compare against the current rgn
1266 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001267 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001268#endif
reed@google.com819c9212011-02-23 18:56:55 +00001269}
1270#endif
1271
reed@google.com90c07ea2012-04-13 13:50:27 +00001272void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001273 SkClipStack::B2TIter iter(fClipStack);
1274 const SkClipStack::B2TIter::Clip* clip;
reed@google.com90c07ea2012-04-13 13:50:27 +00001275
robertphillips@google.com7460b372012-04-25 16:54:51 +00001276 SkRect empty = { 0, 0, 0, 0 };
reed@google.com90c07ea2012-04-13 13:50:27 +00001277 while ((clip = iter.next()) != NULL) {
1278 if (clip->fPath) {
1279 visitor->clipPath(*clip->fPath, clip->fOp, clip->fDoAA);
1280 } else if (clip->fRect) {
1281 visitor->clipRect(*clip->fRect, clip->fOp, clip->fDoAA);
1282 } else {
1283 visitor->clipRect(empty, SkRegion::kIntersect_Op, false);
1284 }
1285 }
1286}
1287
reed@google.com5c3d1472011-02-22 19:12:23 +00001288///////////////////////////////////////////////////////////////////////////////
1289
reed@google.com3b3e8952012-08-16 20:53:31 +00001290void SkCanvas::computeLocalClipBoundsCompareType() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001291 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001292
reed@google.com3b3e8952012-08-16 20:53:31 +00001293 if (!this->getClipBounds(&r)) {
1294 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001295 } else {
reed@google.com3b3e8952012-08-16 20:53:31 +00001296 fLocalBoundsCompareType.set(SkScalarToCompareType(r.fLeft),
1297 SkScalarToCompareType(r.fTop),
1298 SkScalarToCompareType(r.fRight),
1299 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001300 }
1301}
1302
reed@google.com3b3e8952012-08-16 20:53:31 +00001303bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001304
reed@google.com16078632011-12-06 18:56:37 +00001305 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001306 return true;
1307
reed@google.com00177082011-10-12 14:34:30 +00001308 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001309 return true;
1310 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001311
tomhudson@google.com8d430182011-06-06 19:11:19 +00001312 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001313 SkRect dst;
1314 fMCRec->fMatrix->mapRect(&dst, rect);
1315 SkIRect idst;
1316 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001317 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001318 } else {
reed@google.com3b3e8952012-08-16 20:53:31 +00001319 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
reed@android.comd252db02009-04-01 18:31:44 +00001320
reed@android.coma380ae42009-07-21 01:17:02 +00001321 // for speed, do the most likely reject compares first
1322 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1323 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1324 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1325 return true;
1326 }
1327 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1328 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1329 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1330 return true;
1331 }
1332 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001333 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001334}
1335
reed@google.com3b3e8952012-08-16 20:53:31 +00001336bool SkCanvas::quickReject(const SkPath& path) const {
1337 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001338}
1339
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001340static inline int pinIntForScalar(int x) {
1341#ifdef SK_SCALAR_IS_FIXED
1342 if (x < SK_MinS16) {
1343 x = SK_MinS16;
1344 } else if (x > SK_MaxS16) {
1345 x = SK_MaxS16;
1346 }
1347#endif
1348 return x;
1349}
1350
reed@google.com3b3e8952012-08-16 20:53:31 +00001351bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001352 SkIRect ibounds;
1353 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001354 return false;
1355 }
1356
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001357 SkMatrix inverse;
1358 // if we can't invert the CTM, we can't return local clip bounds
1359 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001360 if (bounds) {
1361 bounds->setEmpty();
1362 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001363 return false;
1364 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001365
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001366 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001367 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001368 // adjust it outwards in case we are antialiasing
1369 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001370
1371 // SkRect::iset() will correctly assert if we pass a value out of range
1372 // (when SkScalar==fixed), so we pin to legal values. This does not
1373 // really returnt the correct answer, but its the best we can do given
1374 // that we've promised to return SkRect (even though we support devices
1375 // that can be larger than 32K in width or height).
1376 r.iset(pinIntForScalar(ibounds.fLeft - inset),
1377 pinIntForScalar(ibounds.fTop - inset),
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001378 pinIntForScalar(ibounds.fRight + inset),
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001379 pinIntForScalar(ibounds.fBottom + inset));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001380 inverse.mapRect(bounds, r);
1381 }
1382 return true;
1383}
1384
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001385bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001386 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001387 if (clip.isEmpty()) {
1388 if (bounds) {
1389 bounds->setEmpty();
1390 }
1391 return false;
1392 }
1393
1394 if (NULL != bounds) {
1395 *bounds = clip.getBounds();
1396 }
1397 return true;
1398}
1399
reed@android.com8a1c16f2008-12-17 15:59:43 +00001400const SkMatrix& SkCanvas::getTotalMatrix() const {
1401 return *fMCRec->fMatrix;
1402}
1403
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001404SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001405 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1406 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001407 return kComplex_ClipType;
1408}
1409
reed@android.com8a1c16f2008-12-17 15:59:43 +00001410const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001411 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001412}
1413
reed@android.comf2b98d62010-12-20 18:26:13 +00001414void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1415 if (NULL == matrix || matrix->isIdentity()) {
1416 if (fUseExternalMatrix) {
1417 fDeviceCMDirty = true;
1418 }
1419 fUseExternalMatrix = false;
1420 } else {
mike@reedtribe.org90bf4272012-04-14 19:06:16 +00001421 if (matrix->invert(&fExternalInverse)) {
1422 fExternalMatrix = *matrix;
1423 fUseExternalMatrix = true;
1424 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.comfc9a3be2012-04-12 13:52:14 +00001425 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001426 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001427}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001428
bsalomon@google.come97f0852011-06-17 13:10:25 +00001429SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
1430 int width, int height,
1431 bool isOpaque) {
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001432 SkDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001433 if (device) {
1434 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1435 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001436 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001437 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001438 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001439}
1440
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001441SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
bsalomon@google.come97f0852011-06-17 13:10:25 +00001442 int width, int height,
1443 bool isOpaque) {
1444 SkDevice* device = this->getDevice();
1445 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001446 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001447 } else {
1448 return NULL;
1449 }
1450}
1451
1452
reed@android.com8a1c16f2008-12-17 15:59:43 +00001453//////////////////////////////////////////////////////////////////////////////
1454// These are the virtual drawing methods
1455//////////////////////////////////////////////////////////////////////////////
1456
reed@google.com2a981812011-04-14 18:59:28 +00001457void SkCanvas::clear(SkColor color) {
1458 SkDrawIter iter(this);
1459
1460 while (iter.next()) {
1461 iter.fDevice->clear(color);
1462 }
1463}
1464
reed@android.com8a1c16f2008-12-17 15:59:43 +00001465void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001466 this->internalDrawPaint(paint);
1467}
1468
1469void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001470 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001471
1472 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001473 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001474 }
1475
reed@google.com4e2b3d32011-04-07 14:18:59 +00001476 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001477}
1478
1479void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1480 const SkPaint& paint) {
1481 if ((long)count <= 0) {
1482 return;
1483 }
1484
reed@google.coma584aed2012-05-16 14:06:02 +00001485 if (paint.canComputeFastBounds()) {
1486 SkRect r;
1487 // special-case 2 points (common for drawing a single line)
1488 if (2 == count) {
1489 r.set(pts[0], pts[1]);
1490 } else {
1491 r.set(pts, count);
1492 }
1493 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001494 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
reed@google.coma584aed2012-05-16 14:06:02 +00001495 return;
1496 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001497 }
reed@google.coma584aed2012-05-16 14:06:02 +00001498
reed@android.com8a1c16f2008-12-17 15:59:43 +00001499 SkASSERT(pts != NULL);
1500
reed@google.com4e2b3d32011-04-07 14:18:59 +00001501 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001502
reed@android.com8a1c16f2008-12-17 15:59:43 +00001503 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001504 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001505 }
reed@google.com4b226022011-01-11 18:32:13 +00001506
reed@google.com4e2b3d32011-04-07 14:18:59 +00001507 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001508}
1509
1510void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1511 if (paint.canComputeFastBounds()) {
1512 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001513 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001514 return;
1515 }
1516 }
reed@google.com4b226022011-01-11 18:32:13 +00001517
reed@google.com4e2b3d32011-04-07 14:18:59 +00001518 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001519
1520 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001521 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001522 }
1523
reed@google.com4e2b3d32011-04-07 14:18:59 +00001524 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001525}
1526
1527void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00001528 if (!path.isFinite()) {
1529 return;
1530 }
1531
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001532 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001533 SkRect storage;
1534 const SkRect& bounds = path.getBounds();
reed@google.com3b3e8952012-08-16 20:53:31 +00001535 if (this->quickReject(paint.computeFastBounds(bounds, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001536 return;
1537 }
1538 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001539 if (path.isEmpty()) {
1540 if (path.isInverseFillType()) {
1541 this->internalDrawPaint(paint);
1542 }
1543 return;
1544 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001545
reed@google.com4e2b3d32011-04-07 14:18:59 +00001546 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001547
1548 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001549 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001550 }
1551
reed@google.com4e2b3d32011-04-07 14:18:59 +00001552 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001553}
1554
1555void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1556 const SkPaint* paint) {
1557 SkDEBUGCODE(bitmap.validate();)
1558
reed@google.com3d608122011-11-21 15:16:16 +00001559 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001560 SkRect bounds = {
1561 x, y,
1562 x + SkIntToScalar(bitmap.width()),
1563 y + SkIntToScalar(bitmap.height())
1564 };
1565 if (paint) {
1566 (void)paint->computeFastBounds(bounds, &bounds);
1567 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001568 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001569 return;
1570 }
1571 }
reed@google.com4b226022011-01-11 18:32:13 +00001572
reed@android.com8a1c16f2008-12-17 15:59:43 +00001573 SkMatrix matrix;
1574 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001575 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001576}
1577
reed@google.com9987ec32011-09-07 11:57:52 +00001578// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001579void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
reed@google.com9987ec32011-09-07 11:57:52 +00001580 const SkRect& dst, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001581 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1582 return;
1583 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001584
reed@google.com3d608122011-11-21 15:16:16 +00001585 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001586 SkRect storage;
1587 const SkRect* bounds = &dst;
1588 if (paint) {
1589 bounds = &paint->computeFastBounds(dst, &storage);
1590 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001591 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001592 return;
1593 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001594 }
reed@google.com3d608122011-11-21 15:16:16 +00001595
reed@google.com33535f32012-09-25 15:37:50 +00001596 SkLazyPaint lazy;
1597 if (NULL == paint) {
1598 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001599 }
reed@google.com33535f32012-09-25 15:37:50 +00001600
1601 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
1602
1603 while (iter.next()) {
1604 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint());
reed@android.comf2b98d62010-12-20 18:26:13 +00001605 }
reed@google.com33535f32012-09-25 15:37:50 +00001606
1607 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001608}
1609
reed@google.com71121732012-09-18 15:14:33 +00001610void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
1611 const SkRect& dst, const SkPaint* paint) {
reed@google.com9987ec32011-09-07 11:57:52 +00001612 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
reed@google.com71121732012-09-18 15:14:33 +00001659 const SkScalar srcX[4] = {
1660 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), w
1661 };
1662 const SkScalar srcY[4] = {
1663 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), h
1664 };
reed@google.com9987ec32011-09-07 11:57:52 +00001665 SkScalar dstX[4] = {
1666 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1667 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1668 };
1669 SkScalar dstY[4] = {
1670 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1671 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1672 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001673
reed@google.com9987ec32011-09-07 11:57:52 +00001674 if (dstX[1] > dstX[2]) {
1675 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1676 dstX[2] = dstX[1];
1677 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001678
reed@google.com9987ec32011-09-07 11:57:52 +00001679 if (dstY[1] > dstY[2]) {
1680 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1681 dstY[2] = dstY[1];
1682 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001683
reed@google.com9987ec32011-09-07 11:57:52 +00001684 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00001685 SkRect s, d;
1686
reed@google.com9987ec32011-09-07 11:57:52 +00001687 s.fTop = srcY[y];
1688 s.fBottom = srcY[y+1];
1689 d.fTop = dstY[y];
1690 d.fBottom = dstY[y+1];
1691 for (int x = 0; x < 3; x++) {
1692 s.fLeft = srcX[x];
1693 s.fRight = srcX[x+1];
1694 d.fLeft = dstX[x];
1695 d.fRight = dstX[x+1];
1696 this->internalDrawBitmapRect(bitmap, &s, d, paint);
1697 }
1698 }
1699}
1700
1701void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1702 const SkRect& dst, const SkPaint* paint) {
1703 SkDEBUGCODE(bitmap.validate();)
1704
1705 // Need a device entry-point, so gpu can use a mesh
1706 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1707}
1708
reed@google.comf67e4cf2011-03-15 20:56:58 +00001709class SkDeviceFilteredPaint {
1710public:
1711 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1712 SkDevice::TextFlags flags;
1713 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001714 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001715 newPaint->setFlags(flags.fFlags);
1716 newPaint->setHinting(flags.fHinting);
1717 fPaint = newPaint;
1718 } else {
1719 fPaint = &paint;
1720 }
1721 }
1722
reed@google.comf67e4cf2011-03-15 20:56:58 +00001723 const SkPaint& paint() const { return *fPaint; }
1724
1725private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001726 const SkPaint* fPaint;
1727 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001728};
1729
bungeman@google.com52c748b2011-08-22 21:30:43 +00001730void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1731 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001732 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001733 draw.fDevice->drawRect(draw, r, paint);
1734 } else {
1735 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001736 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001737 draw.fDevice->drawRect(draw, r, p);
1738 }
1739}
1740
1741void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1742 const char text[], size_t byteLength,
1743 SkScalar x, SkScalar y) {
1744 SkASSERT(byteLength == 0 || text != NULL);
1745
1746 // nothing to draw
1747 if (text == NULL || byteLength == 0 ||
1748 draw.fClip->isEmpty() ||
1749 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1750 return;
1751 }
1752
1753 SkScalar width = 0;
1754 SkPoint start;
1755
1756 start.set(0, 0); // to avoid warning
1757 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1758 SkPaint::kStrikeThruText_Flag)) {
1759 width = paint.measureText(text, byteLength);
1760
1761 SkScalar offsetX = 0;
1762 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1763 offsetX = SkScalarHalf(width);
1764 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1765 offsetX = width;
1766 }
1767 start.set(x - offsetX, y);
1768 }
1769
1770 if (0 == width) {
1771 return;
1772 }
1773
1774 uint32_t flags = paint.getFlags();
1775
1776 if (flags & (SkPaint::kUnderlineText_Flag |
1777 SkPaint::kStrikeThruText_Flag)) {
1778 SkScalar textSize = paint.getTextSize();
1779 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1780 SkRect r;
1781
1782 r.fLeft = start.fX;
1783 r.fRight = start.fX + width;
1784
1785 if (flags & SkPaint::kUnderlineText_Flag) {
1786 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1787 start.fY);
1788 r.fTop = offset;
1789 r.fBottom = offset + height;
1790 DrawRect(draw, paint, r, textSize);
1791 }
1792 if (flags & SkPaint::kStrikeThruText_Flag) {
1793 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1794 start.fY);
1795 r.fTop = offset;
1796 r.fBottom = offset + height;
1797 DrawRect(draw, paint, r, textSize);
1798 }
1799 }
1800}
1801
reed@android.com8a1c16f2008-12-17 15:59:43 +00001802void SkCanvas::drawText(const void* text, size_t byteLength,
1803 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001804 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001805
1806 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001807 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001808 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001809 DrawTextDecorations(iter, dfp.paint(),
1810 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001811 }
1812
reed@google.com4e2b3d32011-04-07 14:18:59 +00001813 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001814}
1815
1816void SkCanvas::drawPosText(const void* text, size_t byteLength,
1817 const SkPoint pos[], const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001818 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001819
reed@android.com8a1c16f2008-12-17 15:59:43 +00001820 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001821 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001822 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001823 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001824 }
reed@google.com4b226022011-01-11 18:32:13 +00001825
reed@google.com4e2b3d32011-04-07 14:18:59 +00001826 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001827}
1828
1829void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1830 const SkScalar xpos[], SkScalar constY,
1831 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001832 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001833
reed@android.com8a1c16f2008-12-17 15:59:43 +00001834 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001835 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001836 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001837 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001838 }
reed@google.com4b226022011-01-11 18:32:13 +00001839
reed@google.com4e2b3d32011-04-07 14:18:59 +00001840 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001841}
1842
1843void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1844 const SkPath& path, const SkMatrix* matrix,
1845 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001846 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001847
1848 while (iter.next()) {
1849 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001850 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001851 }
1852
reed@google.com4e2b3d32011-04-07 14:18:59 +00001853 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001854}
1855
djsollen@google.com56c69772011-11-08 19:00:26 +00001856#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001857void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1858 const SkPoint pos[], const SkPaint& paint,
1859 const SkPath& path, const SkMatrix* matrix) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001860 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001861
1862 while (iter.next()) {
1863 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001864 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001865 }
1866
reed@google.com4e2b3d32011-04-07 14:18:59 +00001867 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001868}
1869#endif
1870
reed@android.com8a1c16f2008-12-17 15:59:43 +00001871void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1872 const SkPoint verts[], const SkPoint texs[],
1873 const SkColor colors[], SkXfermode* xmode,
1874 const uint16_t indices[], int indexCount,
1875 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001876 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001877
reed@android.com8a1c16f2008-12-17 15:59:43 +00001878 while (iter.next()) {
1879 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001880 colors, xmode, indices, indexCount,
1881 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001882 }
reed@google.com4b226022011-01-11 18:32:13 +00001883
reed@google.com4e2b3d32011-04-07 14:18:59 +00001884 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001885}
1886
reed@android.comcb608442009-12-04 21:32:27 +00001887void SkCanvas::drawData(const void* data, size_t length) {
1888 // do nothing. Subclasses may do something with the data
1889}
1890
reed@android.com8a1c16f2008-12-17 15:59:43 +00001891//////////////////////////////////////////////////////////////////////////////
1892// These methods are NOT virtual, and therefore must call back into virtual
1893// methods, rather than actually drawing themselves.
1894//////////////////////////////////////////////////////////////////////////////
1895
1896void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001897 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001898 SkPaint paint;
1899
1900 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001901 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001902 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001903 }
1904 this->drawPaint(paint);
1905}
1906
reed@android.com845fdac2009-06-23 03:01:32 +00001907void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001908 SkPaint paint;
1909
1910 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001911 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001912 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001913 }
1914 this->drawPaint(paint);
1915}
1916
1917void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1918 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001919
reed@android.com8a1c16f2008-12-17 15:59:43 +00001920 pt.set(x, y);
1921 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1922}
1923
1924void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1925 SkPoint pt;
1926 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001927
reed@android.com8a1c16f2008-12-17 15:59:43 +00001928 pt.set(x, y);
1929 paint.setColor(color);
1930 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1931}
1932
1933void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1934 const SkPaint& paint) {
1935 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001936
reed@android.com8a1c16f2008-12-17 15:59:43 +00001937 pts[0].set(x0, y0);
1938 pts[1].set(x1, y1);
1939 this->drawPoints(kLines_PointMode, 2, pts, paint);
1940}
1941
1942void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1943 SkScalar right, SkScalar bottom,
1944 const SkPaint& paint) {
1945 SkRect r;
1946
1947 r.set(left, top, right, bottom);
1948 this->drawRect(r, paint);
1949}
1950
1951void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1952 const SkPaint& paint) {
1953 if (radius < 0) {
1954 radius = 0;
1955 }
1956
1957 SkRect r;
1958 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001959
reed@android.com8a1c16f2008-12-17 15:59:43 +00001960 if (paint.canComputeFastBounds()) {
1961 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001962 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001963 return;
1964 }
1965 }
reed@google.com4b226022011-01-11 18:32:13 +00001966
reed@android.com8a1c16f2008-12-17 15:59:43 +00001967 SkPath path;
1968 path.addOval(r);
1969 this->drawPath(path, paint);
1970}
1971
1972void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1973 const SkPaint& paint) {
1974 if (rx > 0 && ry > 0) {
1975 if (paint.canComputeFastBounds()) {
1976 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001977 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001978 return;
1979 }
1980 }
1981
1982 SkPath path;
1983 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1984 this->drawPath(path, paint);
1985 } else {
1986 this->drawRect(r, paint);
1987 }
1988}
1989
1990void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1991 if (paint.canComputeFastBounds()) {
1992 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001993 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001994 return;
1995 }
1996 }
1997
1998 SkPath path;
1999 path.addOval(oval);
2000 this->drawPath(path, paint);
2001}
2002
2003void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2004 SkScalar sweepAngle, bool useCenter,
2005 const SkPaint& paint) {
2006 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2007 this->drawOval(oval, paint);
2008 } else {
2009 SkPath path;
2010 if (useCenter) {
2011 path.moveTo(oval.centerX(), oval.centerY());
2012 }
2013 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2014 if (useCenter) {
2015 path.close();
2016 }
2017 this->drawPath(path, paint);
2018 }
2019}
2020
2021void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2022 const SkPath& path, SkScalar hOffset,
2023 SkScalar vOffset, const SkPaint& paint) {
2024 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002025
reed@android.com8a1c16f2008-12-17 15:59:43 +00002026 matrix.setTranslate(hOffset, vOffset);
2027 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2028}
2029
reed@android.comf76bacf2009-05-13 14:00:33 +00002030///////////////////////////////////////////////////////////////////////////////
2031
reed@android.com8a1c16f2008-12-17 15:59:43 +00002032void SkCanvas::drawPicture(SkPicture& picture) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002033 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002034}
2035
reed@android.com8a1c16f2008-12-17 15:59:43 +00002036///////////////////////////////////////////////////////////////////////////////
2037///////////////////////////////////////////////////////////////////////////////
2038
2039SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002040 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002041
2042 SkASSERT(canvas);
2043
2044 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2045 fDone = !fImpl->next();
2046}
2047
2048SkCanvas::LayerIter::~LayerIter() {
2049 fImpl->~SkDrawIter();
2050}
2051
2052void SkCanvas::LayerIter::next() {
2053 fDone = !fImpl->next();
2054}
2055
2056SkDevice* SkCanvas::LayerIter::device() const {
2057 return fImpl->getDevice();
2058}
2059
2060const SkMatrix& SkCanvas::LayerIter::matrix() const {
2061 return fImpl->getMatrix();
2062}
2063
2064const SkPaint& SkCanvas::LayerIter::paint() const {
2065 const SkPaint* paint = fImpl->getPaint();
2066 if (NULL == paint) {
2067 paint = &fDefaultPaint;
2068 }
2069 return *paint;
2070}
2071
2072const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2073int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2074int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002075
2076///////////////////////////////////////////////////////////////////////////////
2077
2078SkCanvas::ClipVisitor::~ClipVisitor() { }
reed@google.com97af1a62012-08-28 12:19:02 +00002079
2080