blob: c4beca72f1a343a8b9bb4b68b6eb2142296d55b6 [file] [log] [blame]
robertphillips@google.com105a4a52014-02-11 15:10:40 +00001/*
2 * Copyright 2014 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7#ifndef SkMatrixClipStateMgr_DEFINED
8#define SkMatrixClipStateMgr_DEFINED
9
10#include "SkCanvas.h"
11#include "SkMatrix.h"
12#include "SkRegion.h"
13#include "SkRRect.h"
14#include "SkTypes.h"
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +000015#include "SkTDArray.h"
robertphillips@google.com105a4a52014-02-11 15:10:40 +000016
17class SkPictureRecord;
18class SkWriter32;
19
20// The SkMatrixClipStateMgr collapses the matrix/clip state of an SkPicture into
21// a series of save/restore blocks of consistent matrix clip state, e.g.:
22//
23// save
24// clip(s)
25// concat
26// ... draw ops ...
27// restore
28//
29// SaveLayers simply add another level, e.g.:
30//
31// save
32// clip(s)
33// concat
34// ... draw ops ...
35// saveLayer
36// save
37// clip(s)
38// concat
39// ... draw ops ...
40// restore
41// restore
42// restore
43//
44// As a side effect of this process all saves and saveLayers will become
45// kMatrixClip_SaveFlag style saves/saveLayers.
46
47// The SkMatrixClipStateMgr works by intercepting all the save*, restore, clip*,
48// and matrix calls sent to SkCanvas in order to track the current matrix/clip
skia.committer@gmail.com877c4492014-02-12 03:02:04 +000049// state. All the other canvas calls get funnelled into a generic "call" entry
robertphillips@google.com105a4a52014-02-11 15:10:40 +000050// point that signals that a state block is required.
51class SkMatrixClipStateMgr {
52public:
53 static const int32_t kIdentityWideOpenStateID = 0;
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +000054 static const int kIdentityMatID = 0;
robertphillips@google.com105a4a52014-02-11 15:10:40 +000055
56 class MatrixClipState {
57 public:
58 class MatrixInfo {
59 public:
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +000060 void reset() {
61 fMatrixID = kIdentityMatID;
62 fMatrix.reset();
63 }
64
65 bool preTranslate(SkScalar dx, SkScalar dy) {
66 fMatrixID = -1;
67 return fMatrix.preTranslate(dx, dy);
68 }
69
70 bool preScale(SkScalar sx, SkScalar sy) {
71 fMatrixID = -1;
72 return fMatrix.preScale(sx, sy);
73 }
74
75 bool preRotate(SkScalar degrees) {
76 fMatrixID = -1;
77 return fMatrix.preRotate(degrees);
78 }
79
80 bool preSkew(SkScalar sx, SkScalar sy) {
81 fMatrixID = -1;
82 return fMatrix.preSkew(sx, sy);
83 }
84
85 bool preConcat(const SkMatrix& matrix) {
86 fMatrixID = -1;
87 return fMatrix.preConcat(matrix);
88 }
89
90 void setMatrix(const SkMatrix& matrix) {
91 fMatrixID = -1;
92 fMatrix = matrix;
93 }
94
95 int getID(SkMatrixClipStateMgr* mgr) {
96 if (fMatrixID >= 0) {
97 return fMatrixID;
98 }
99
100 fMatrixID = mgr->addMatToDict(fMatrix);
101 return fMatrixID;
102 }
103
104 private:
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000105 SkMatrix fMatrix;
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000106 int fMatrixID;
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000107 };
108
109 class ClipInfo : public SkNoncopyable {
110 public:
111 ClipInfo() {}
112
skia.committer@gmail.com877c4492014-02-12 03:02:04 +0000113 bool clipRect(const SkRect& rect,
114 SkRegion::Op op,
115 bool doAA,
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000116 int matrixID) {
commit-bot@chromium.org9a67a7e2014-02-12 23:22:15 +0000117 ClipOp* newClip = fClips.append();
118 newClip->fClipType = kRect_ClipType;
119 newClip->fGeom.fRRect.setRect(rect); // storing the clipRect in the RRect
120 newClip->fOp = op;
121 newClip->fDoAA = doAA;
122 newClip->fMatrixID = matrixID;
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000123 return false;
124 }
125
skia.committer@gmail.com877c4492014-02-12 03:02:04 +0000126 bool clipRRect(const SkRRect& rrect,
127 SkRegion::Op op,
128 bool doAA,
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000129 int matrixID) {
commit-bot@chromium.org9a67a7e2014-02-12 23:22:15 +0000130 ClipOp* newClip = fClips.append();
131 newClip->fClipType = kRRect_ClipType;
132 newClip->fGeom.fRRect = rrect;
133 newClip->fOp = op;
134 newClip->fDoAA = doAA;
135 newClip->fMatrixID = matrixID;
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000136 return false;
137 }
138
skia.committer@gmail.com877c4492014-02-12 03:02:04 +0000139 bool clipPath(SkPictureRecord* picRecord,
140 const SkPath& path,
141 SkRegion::Op op,
142 bool doAA,
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000143 int matrixID);
skia.committer@gmail.com877c4492014-02-12 03:02:04 +0000144 bool clipRegion(SkPictureRecord* picRecord,
commit-bot@chromium.org9a67a7e2014-02-12 23:22:15 +0000145 int regionID,
skia.committer@gmail.com877c4492014-02-12 03:02:04 +0000146 SkRegion::Op op,
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000147 int matrixID);
commit-bot@chromium.org92da60c2014-02-19 15:11:23 +0000148 void writeClip(int* curMatID, SkMatrixClipStateMgr* mgr);
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000149
commit-bot@chromium.org92da60c2014-02-19 15:11:23 +0000150 SkDEBUGCODE(int numClips() const { return fClips.count(); })
151
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000152 private:
153 enum ClipType {
154 kRect_ClipType,
155 kRRect_ClipType,
156 kPath_ClipType,
157 kRegion_ClipType
158 };
159
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000160 class ClipOp {
161 public:
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000162 ClipType fClipType;
163
164 union {
commit-bot@chromium.org9a67a7e2014-02-12 23:22:15 +0000165 SkRRect fRRect; // also stores clipRect
166 int fPathID;
167 int fRegionID;
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000168 } fGeom;
169
170 bool fDoAA;
171 SkRegion::Op fOp;
172
173 // The CTM in effect when this clip call was issued
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000174 int fMatrixID;
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000175 };
176
commit-bot@chromium.org9a67a7e2014-02-12 23:22:15 +0000177 SkTDArray<ClipOp> fClips;
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000178
179 typedef SkNoncopyable INHERITED;
180 };
181
skia.committer@gmail.com877c4492014-02-12 03:02:04 +0000182 MatrixClipState(MatrixClipState* prev, int flags)
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000183#ifdef SK_DEBUG
184 : fPrev(prev)
185#endif
186 {
187 if (NULL == prev) {
188 fLayerID = 0;
189
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000190 fMatrixInfoStorage.reset();
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000191 fMatrixInfo = &fMatrixInfoStorage;
192 fClipInfo = &fClipInfoStorage; // ctor handles init of fClipInfoStorage
193
194 // The identity/wide-open-clip state is current by default
195 fMCStateID = kIdentityWideOpenStateID;
skia.committer@gmail.com877c4492014-02-12 03:02:04 +0000196 }
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000197 else {
198 fLayerID = prev->fLayerID;
199
200 if (flags & SkCanvas::kMatrix_SaveFlag) {
201 fMatrixInfoStorage = *prev->fMatrixInfo;
202 fMatrixInfo = &fMatrixInfoStorage;
203 } else {
204 fMatrixInfo = prev->fMatrixInfo;
205 }
206
207 if (flags & SkCanvas::kClip_SaveFlag) {
208 // We don't copy the ClipOps of the previous clip states
209 fClipInfo = &fClipInfoStorage;
210 } else {
211 fClipInfo = prev->fClipInfo;
212 }
213
214 // Initially a new save/saveLayer represents the same MC state
215 // as its predecessor.
216 fMCStateID = prev->fMCStateID;
217 }
218
219 fIsSaveLayer = false;
220 }
221
222 MatrixInfo* fMatrixInfo;
223 MatrixInfo fMatrixInfoStorage;
224
225 ClipInfo* fClipInfo;
226 ClipInfo fClipInfoStorage;
227
228 // Tracks the current depth of saveLayers to support the isDrawingToLayer call
229 int fLayerID;
230 // Does this MC state represent a saveLayer call?
231 bool fIsSaveLayer;
232
233 // The next two fields are only valid when fIsSaveLayer is set.
234 int32_t fSaveLayerBaseStateID;
commit-bot@chromium.org92da60c2014-02-19 15:11:23 +0000235 SkTDArray<int>* fSavedSkipOffsets;
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000236
237#ifdef SK_DEBUG
238 MatrixClipState* fPrev; // debugging aid
239#endif
240
241 int32_t fMCStateID;
242 };
243
244 enum CallType {
245 kMatrix_CallType,
246 kClip_CallType,
247 kOther_CallType
248 };
249
250 SkMatrixClipStateMgr();
commit-bot@chromium.org9a67a7e2014-02-12 23:22:15 +0000251 ~SkMatrixClipStateMgr();
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000252
253 void init(SkPictureRecord* picRecord) {
254 // Note: we're not taking a ref here. It is expected that the SkMatrixClipStateMgr
255 // is owned by the SkPictureRecord object
skia.committer@gmail.com877c4492014-02-12 03:02:04 +0000256 fPicRecord = picRecord;
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000257 }
258
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000259 SkPictureRecord* getPicRecord() { return fPicRecord; }
260
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000261 // TODO: need to override canvas' getSaveCount. Right now we pass the
262 // save* and restore calls on to the base SkCanvas in SkPictureRecord but
263 // this duplicates effort.
264 int getSaveCount() const { return fMatrixClipStack.count(); }
265
266 int save(SkCanvas::SaveFlags flags);
267
268 int saveLayer(const SkRect* bounds, const SkPaint* paint, SkCanvas::SaveFlags flags);
269
270 bool isDrawingToLayer() const {
271 return fCurMCState->fLayerID > 0;
272 }
273
274 void restore();
275
276 bool translate(SkScalar dx, SkScalar dy) {
277 this->call(kMatrix_CallType);
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000278 return fCurMCState->fMatrixInfo->preTranslate(dx, dy);
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000279 }
280
281 bool scale(SkScalar sx, SkScalar sy) {
282 this->call(kMatrix_CallType);
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000283 return fCurMCState->fMatrixInfo->preScale(sx, sy);
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000284 }
285
286 bool rotate(SkScalar degrees) {
287 this->call(kMatrix_CallType);
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000288 return fCurMCState->fMatrixInfo->preRotate(degrees);
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000289 }
290
291 bool skew(SkScalar sx, SkScalar sy) {
292 this->call(kMatrix_CallType);
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000293 return fCurMCState->fMatrixInfo->preSkew(sx, sy);
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000294 }
295
296 bool concat(const SkMatrix& matrix) {
297 this->call(kMatrix_CallType);
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000298 return fCurMCState->fMatrixInfo->preConcat(matrix);
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000299 }
300
301 void setMatrix(const SkMatrix& matrix) {
302 this->call(kMatrix_CallType);
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000303 fCurMCState->fMatrixInfo->setMatrix(matrix);
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000304 }
305
306 bool clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
307 this->call(SkMatrixClipStateMgr::kClip_CallType);
308 return fCurMCState->fClipInfo->clipRect(rect, op, doAA,
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000309 fCurMCState->fMatrixInfo->getID(this));
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000310 }
311
312 bool clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
313 this->call(SkMatrixClipStateMgr::kClip_CallType);
314 return fCurMCState->fClipInfo->clipRRect(rrect, op, doAA,
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000315 fCurMCState->fMatrixInfo->getID(this));
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000316 }
317
318 bool clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
319 this->call(SkMatrixClipStateMgr::kClip_CallType);
320 return fCurMCState->fClipInfo->clipPath(fPicRecord, path, op, doAA,
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000321 fCurMCState->fMatrixInfo->getID(this));
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000322 }
323
324 bool clipRegion(const SkRegion& region, SkRegion::Op op) {
325 this->call(SkMatrixClipStateMgr::kClip_CallType);
commit-bot@chromium.org9a67a7e2014-02-12 23:22:15 +0000326 int regionID = this->addRegionToDict(region);
327 return fCurMCState->fClipInfo->clipRegion(fPicRecord, regionID, op,
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000328 fCurMCState->fMatrixInfo->getID(this));
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000329 }
330
331 bool call(CallType callType);
332
commit-bot@chromium.org92da60c2014-02-19 15:11:23 +0000333 void fillInSkips(SkWriter32* writer, int32_t restoreOffset);
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000334
335 void finish();
336
337protected:
338 SkPictureRecord* fPicRecord;
339
340 uint32_t fMatrixClipStackStorage[43]; // sized to fit 2 clip states
341 SkDeque fMatrixClipStack;
342 MatrixClipState* fCurMCState;
343
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000344 // This dictionary doesn't actually de-duplicate the matrices (except for the
345 // identity matrix). It merely stores the matrices and allows them to be looked
skia.committer@gmail.com22ef2c32014-02-13 03:01:57 +0000346 // up by ID later. The de-duplication mainly falls upon the matrix/clip stack
347 // which stores the ID so a revisited clip/matrix (via popping the stack) will
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000348 // use the same ID.
349 SkTDArray<SkMatrix> fMatrixDict;
350
commit-bot@chromium.org9a67a7e2014-02-12 23:22:15 +0000351 SkTDArray<SkRegion*> fRegionDict;
352
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000353 // The MCStateID of the state currently in effect in the byte stream. 0 if none.
354 int32_t fCurOpenStateID;
skia.committer@gmail.comc05d2852014-02-20 03:01:56 +0000355 // The skip offsets for the current open state. These are the locations in the
commit-bot@chromium.org92da60c2014-02-19 15:11:23 +0000356 // skp that must be filled in when the current open state is closed. These are
357 // here rather then distributed across the MatrixClipState's because saveLayers
358 // can cause MC states to be nested.
359 SkTDArray<int32_t> *fSkipOffsets;
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000360
361 SkDEBUGCODE(void validate();)
362
commit-bot@chromium.org92da60c2014-02-19 15:11:23 +0000363 int MCStackPush(SkCanvas::SaveFlags flags);
364
365 void addClipOffset(int offset) {
366 SkASSERT(NULL != fSkipOffsets);
367 SkASSERT(kIdentityWideOpenStateID != fCurOpenStateID);
368
369 *fSkipOffsets->append() = offset;
370 }
371
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000372 void writeDeltaMat(int currentMatID, int desiredMatID);
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000373 static int32_t NewMCStateID();
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000374
commit-bot@chromium.org9a67a7e2014-02-12 23:22:15 +0000375 int addRegionToDict(const SkRegion& region);
376 const SkRegion* lookupRegion(int index) {
377 SkASSERT(index >= 0 && index < fRegionDict.count());
skia.committer@gmail.com22ef2c32014-02-13 03:01:57 +0000378 return fRegionDict[index];
commit-bot@chromium.org9a67a7e2014-02-12 23:22:15 +0000379 }
380
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000381 // TODO: add stats to check if the dictionary really does
382 // reduce the size of the SkPicture.
383 int addMatToDict(const SkMatrix& mat);
384 const SkMatrix& lookupMat(int index) {
385 SkASSERT(index >= 0 && index < fMatrixDict.count());
386 return fMatrixDict[index];
387 }
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000388};
389
robertphillips@google.com3cd6fdf2014-02-11 15:40:54 +0000390#endif