blob: 270eacd0328293e4d2fbcf207468235dd8a995af [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
commit-bot@chromium.orge3beb6b2014-04-07 19:34:38 +000056 class MatrixClipState : SkNoncopyable {
robertphillips@google.com105a4a52014-02-11 15:10:40 +000057 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
commit-bot@chromium.org92362382014-03-18 12:51:48 +000065 void preTranslate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +000066 fMatrixID = -1;
commit-bot@chromium.org92362382014-03-18 12:51:48 +000067 fMatrix.preTranslate(dx, dy);
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +000068 }
69
commit-bot@chromium.org92362382014-03-18 12:51:48 +000070 void preScale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +000071 fMatrixID = -1;
commit-bot@chromium.org92362382014-03-18 12:51:48 +000072 fMatrix.preScale(sx, sy);
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +000073 }
74
commit-bot@chromium.org92362382014-03-18 12:51:48 +000075 void preRotate(SkScalar degrees) {
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +000076 fMatrixID = -1;
commit-bot@chromium.org92362382014-03-18 12:51:48 +000077 fMatrix.preRotate(degrees);
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +000078 }
79
commit-bot@chromium.org92362382014-03-18 12:51:48 +000080 void preSkew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +000081 fMatrixID = -1;
commit-bot@chromium.org92362382014-03-18 12:51:48 +000082 fMatrix.preSkew(sx, sy);
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +000083 }
84
commit-bot@chromium.org92362382014-03-18 12:51:48 +000085 void preConcat(const SkMatrix& matrix) {
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +000086 fMatrixID = -1;
commit-bot@chromium.org92362382014-03-18 12:51:48 +000087 fMatrix.preConcat(matrix);
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +000088 }
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;
commit-bot@chromium.org6c5f1fd2014-02-24 17:24:15 +0000107
108 typedef SkNoncopyable INHERITED;
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000109 };
110
commit-bot@chromium.orge3beb6b2014-04-07 19:34:38 +0000111 class ClipInfo : SkNoncopyable {
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000112 public:
113 ClipInfo() {}
114
skia.committer@gmail.com877c4492014-02-12 03:02:04 +0000115 bool clipRect(const SkRect& rect,
116 SkRegion::Op op,
117 bool doAA,
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000118 int matrixID) {
commit-bot@chromium.org9a67a7e2014-02-12 23:22:15 +0000119 ClipOp* newClip = fClips.append();
120 newClip->fClipType = kRect_ClipType;
121 newClip->fGeom.fRRect.setRect(rect); // storing the clipRect in the RRect
122 newClip->fOp = op;
123 newClip->fDoAA = doAA;
124 newClip->fMatrixID = matrixID;
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000125 return false;
126 }
127
skia.committer@gmail.com877c4492014-02-12 03:02:04 +0000128 bool clipRRect(const SkRRect& rrect,
129 SkRegion::Op op,
130 bool doAA,
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000131 int matrixID) {
commit-bot@chromium.org9a67a7e2014-02-12 23:22:15 +0000132 ClipOp* newClip = fClips.append();
133 newClip->fClipType = kRRect_ClipType;
134 newClip->fGeom.fRRect = rrect;
135 newClip->fOp = op;
136 newClip->fDoAA = doAA;
137 newClip->fMatrixID = matrixID;
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000138 return false;
139 }
140
skia.committer@gmail.com877c4492014-02-12 03:02:04 +0000141 bool clipPath(SkPictureRecord* picRecord,
142 const SkPath& path,
143 SkRegion::Op op,
144 bool doAA,
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000145 int matrixID);
skia.committer@gmail.com877c4492014-02-12 03:02:04 +0000146 bool clipRegion(SkPictureRecord* picRecord,
commit-bot@chromium.org9a67a7e2014-02-12 23:22:15 +0000147 int regionID,
skia.committer@gmail.com877c4492014-02-12 03:02:04 +0000148 SkRegion::Op op,
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000149 int matrixID);
commit-bot@chromium.org92da60c2014-02-19 15:11:23 +0000150 void writeClip(int* curMatID, SkMatrixClipStateMgr* mgr);
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000151
commit-bot@chromium.org92da60c2014-02-19 15:11:23 +0000152 SkDEBUGCODE(int numClips() const { return fClips.count(); })
153
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000154 private:
155 enum ClipType {
156 kRect_ClipType,
157 kRRect_ClipType,
158 kPath_ClipType,
159 kRegion_ClipType
160 };
161
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000162 class ClipOp {
163 public:
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000164 ClipType fClipType;
165
166 union {
commit-bot@chromium.org6c5f1fd2014-02-24 17:24:15 +0000167 SkRRect fRRect; // also stores clip rect
commit-bot@chromium.org9a67a7e2014-02-12 23:22:15 +0000168 int fPathID;
169 int fRegionID;
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000170 } fGeom;
171
172 bool fDoAA;
173 SkRegion::Op fOp;
174
175 // The CTM in effect when this clip call was issued
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000176 int fMatrixID;
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000177 };
178
commit-bot@chromium.org9a67a7e2014-02-12 23:22:15 +0000179 SkTDArray<ClipOp> fClips;
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000180
181 typedef SkNoncopyable INHERITED;
182 };
183
skia.committer@gmail.com877c4492014-02-12 03:02:04 +0000184 MatrixClipState(MatrixClipState* prev, int flags)
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000185 : fPrev(prev)
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000186 {
robertphillips@google.comfc4ded92014-02-21 17:46:37 +0000187 fHasOpen = false;
188
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000189 if (NULL == prev) {
190 fLayerID = 0;
191
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000192 fMatrixInfoStorage.reset();
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000193 fMatrixInfo = &fMatrixInfoStorage;
194 fClipInfo = &fClipInfoStorage; // ctor handles init of fClipInfoStorage
195
196 // The identity/wide-open-clip state is current by default
197 fMCStateID = kIdentityWideOpenStateID;
robertphillips@google.comfc4ded92014-02-21 17:46:37 +0000198#ifdef SK_DEBUG
199 fExpectedDepth = 1;
200#endif
skia.committer@gmail.com877c4492014-02-12 03:02:04 +0000201 }
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000202 else {
203 fLayerID = prev->fLayerID;
204
205 if (flags & SkCanvas::kMatrix_SaveFlag) {
206 fMatrixInfoStorage = *prev->fMatrixInfo;
207 fMatrixInfo = &fMatrixInfoStorage;
208 } else {
209 fMatrixInfo = prev->fMatrixInfo;
210 }
211
212 if (flags & SkCanvas::kClip_SaveFlag) {
213 // We don't copy the ClipOps of the previous clip states
214 fClipInfo = &fClipInfoStorage;
215 } else {
216 fClipInfo = prev->fClipInfo;
217 }
218
219 // Initially a new save/saveLayer represents the same MC state
220 // as its predecessor.
221 fMCStateID = prev->fMCStateID;
robertphillips@google.comfc4ded92014-02-21 17:46:37 +0000222#ifdef SK_DEBUG
223 fExpectedDepth = prev->fExpectedDepth;
224#endif
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000225 }
226
227 fIsSaveLayer = false;
228 }
229
230 MatrixInfo* fMatrixInfo;
231 MatrixInfo fMatrixInfoStorage;
232
233 ClipInfo* fClipInfo;
234 ClipInfo fClipInfoStorage;
235
236 // Tracks the current depth of saveLayers to support the isDrawingToLayer call
237 int fLayerID;
238 // Does this MC state represent a saveLayer call?
239 bool fIsSaveLayer;
240
commit-bot@chromium.org6c5f1fd2014-02-24 17:24:15 +0000241 // The next field is only valid when fIsSaveLayer is set.
commit-bot@chromium.org92da60c2014-02-19 15:11:23 +0000242 SkTDArray<int>* fSavedSkipOffsets;
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000243
robertphillips@google.comfc4ded92014-02-21 17:46:37 +0000244 // Does the MC state have an open block in the skp?
245 bool fHasOpen;
246
skia.committer@gmail.com7911ade2014-02-22 03:02:18 +0000247 MatrixClipState* fPrev;
robertphillips@google.comfc4ded92014-02-21 17:46:37 +0000248
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000249#ifdef SK_DEBUG
robertphillips@google.comfc4ded92014-02-21 17:46:37 +0000250 int fExpectedDepth; // debugging aid
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000251#endif
252
253 int32_t fMCStateID;
254 };
255
256 enum CallType {
257 kMatrix_CallType,
258 kClip_CallType,
259 kOther_CallType
260 };
261
262 SkMatrixClipStateMgr();
commit-bot@chromium.org9a67a7e2014-02-12 23:22:15 +0000263 ~SkMatrixClipStateMgr();
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000264
265 void init(SkPictureRecord* picRecord) {
266 // Note: we're not taking a ref here. It is expected that the SkMatrixClipStateMgr
267 // is owned by the SkPictureRecord object
skia.committer@gmail.com877c4492014-02-12 03:02:04 +0000268 fPicRecord = picRecord;
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000269 }
270
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000271 SkPictureRecord* getPicRecord() { return fPicRecord; }
272
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000273 // TODO: need to override canvas' getSaveCount. Right now we pass the
274 // save* and restore calls on to the base SkCanvas in SkPictureRecord but
275 // this duplicates effort.
276 int getSaveCount() const { return fMatrixClipStack.count(); }
277
278 int save(SkCanvas::SaveFlags flags);
279
280 int saveLayer(const SkRect* bounds, const SkPaint* paint, SkCanvas::SaveFlags flags);
281
282 bool isDrawingToLayer() const {
283 return fCurMCState->fLayerID > 0;
284 }
285
286 void restore();
287
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000288 void translate(SkScalar dx, SkScalar dy) {
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000289 this->call(kMatrix_CallType);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000290 fCurMCState->fMatrixInfo->preTranslate(dx, dy);
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000291 }
292
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000293 void scale(SkScalar sx, SkScalar sy) {
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000294 this->call(kMatrix_CallType);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000295 fCurMCState->fMatrixInfo->preScale(sx, sy);
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000296 }
297
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000298 void rotate(SkScalar degrees) {
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000299 this->call(kMatrix_CallType);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000300 fCurMCState->fMatrixInfo->preRotate(degrees);
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000301 }
302
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000303 void skew(SkScalar sx, SkScalar sy) {
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000304 this->call(kMatrix_CallType);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000305 fCurMCState->fMatrixInfo->preSkew(sx, sy);
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000306 }
307
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000308 void concat(const SkMatrix& matrix) {
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000309 this->call(kMatrix_CallType);
commit-bot@chromium.org92362382014-03-18 12:51:48 +0000310 fCurMCState->fMatrixInfo->preConcat(matrix);
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000311 }
312
313 void setMatrix(const SkMatrix& matrix) {
314 this->call(kMatrix_CallType);
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000315 fCurMCState->fMatrixInfo->setMatrix(matrix);
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000316 }
317
318 bool clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
319 this->call(SkMatrixClipStateMgr::kClip_CallType);
320 return fCurMCState->fClipInfo->clipRect(rect, 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 clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
325 this->call(SkMatrixClipStateMgr::kClip_CallType);
326 return fCurMCState->fClipInfo->clipRRect(rrect, op, doAA,
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000327 fCurMCState->fMatrixInfo->getID(this));
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000328 }
329
330 bool clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
331 this->call(SkMatrixClipStateMgr::kClip_CallType);
332 return fCurMCState->fClipInfo->clipPath(fPicRecord, path, op, doAA,
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000333 fCurMCState->fMatrixInfo->getID(this));
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000334 }
335
336 bool clipRegion(const SkRegion& region, SkRegion::Op op) {
337 this->call(SkMatrixClipStateMgr::kClip_CallType);
commit-bot@chromium.org9a67a7e2014-02-12 23:22:15 +0000338 int regionID = this->addRegionToDict(region);
339 return fCurMCState->fClipInfo->clipRegion(fPicRecord, regionID, op,
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000340 fCurMCState->fMatrixInfo->getID(this));
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000341 }
342
343 bool call(CallType callType);
344
commit-bot@chromium.org92da60c2014-02-19 15:11:23 +0000345 void fillInSkips(SkWriter32* writer, int32_t restoreOffset);
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000346
347 void finish();
348
349protected:
350 SkPictureRecord* fPicRecord;
351
352 uint32_t fMatrixClipStackStorage[43]; // sized to fit 2 clip states
353 SkDeque fMatrixClipStack;
354 MatrixClipState* fCurMCState;
355
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000356 // This dictionary doesn't actually de-duplicate the matrices (except for the
357 // identity matrix). It merely stores the matrices and allows them to be looked
skia.committer@gmail.com22ef2c32014-02-13 03:01:57 +0000358 // up by ID later. The de-duplication mainly falls upon the matrix/clip stack
359 // which stores the ID so a revisited clip/matrix (via popping the stack) will
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000360 // use the same ID.
361 SkTDArray<SkMatrix> fMatrixDict;
362
commit-bot@chromium.org9a67a7e2014-02-12 23:22:15 +0000363 SkTDArray<SkRegion*> fRegionDict;
364
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000365 // The MCStateID of the state currently in effect in the byte stream. 0 if none.
366 int32_t fCurOpenStateID;
skia.committer@gmail.comc05d2852014-02-20 03:01:56 +0000367 // The skip offsets for the current open state. These are the locations in the
commit-bot@chromium.org92da60c2014-02-19 15:11:23 +0000368 // skp that must be filled in when the current open state is closed. These are
369 // here rather then distributed across the MatrixClipState's because saveLayers
370 // can cause MC states to be nested.
371 SkTDArray<int32_t> *fSkipOffsets;
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000372
373 SkDEBUGCODE(void validate();)
374
commit-bot@chromium.org92da60c2014-02-19 15:11:23 +0000375 int MCStackPush(SkCanvas::SaveFlags flags);
376
377 void addClipOffset(int offset) {
378 SkASSERT(NULL != fSkipOffsets);
379 SkASSERT(kIdentityWideOpenStateID != fCurOpenStateID);
robertphillips@google.comfc4ded92014-02-21 17:46:37 +0000380 SkASSERT(fCurMCState->fHasOpen);
381 SkASSERT(!fCurMCState->fIsSaveLayer);
commit-bot@chromium.org92da60c2014-02-19 15:11:23 +0000382
383 *fSkipOffsets->append() = offset;
384 }
385
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000386 void writeDeltaMat(int currentMatID, int desiredMatID);
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000387 static int32_t NewMCStateID();
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000388
commit-bot@chromium.org9a67a7e2014-02-12 23:22:15 +0000389 int addRegionToDict(const SkRegion& region);
390 const SkRegion* lookupRegion(int index) {
391 SkASSERT(index >= 0 && index < fRegionDict.count());
skia.committer@gmail.com22ef2c32014-02-13 03:01:57 +0000392 return fRegionDict[index];
commit-bot@chromium.org9a67a7e2014-02-12 23:22:15 +0000393 }
394
commit-bot@chromium.orgc5dada82014-02-12 15:48:04 +0000395 // TODO: add stats to check if the dictionary really does
396 // reduce the size of the SkPicture.
397 int addMatToDict(const SkMatrix& mat);
398 const SkMatrix& lookupMat(int index) {
399 SkASSERT(index >= 0 && index < fMatrixDict.count());
400 return fMatrixDict[index];
401 }
robertphillips@google.comfc4ded92014-02-21 17:46:37 +0000402
commit-bot@chromium.org6c5f1fd2014-02-24 17:24:15 +0000403 bool isNestingMCState(int stateID);
robertphillips@google.comfc4ded92014-02-21 17:46:37 +0000404
405#ifdef SK_DEBUG
406 int fActualDepth;
407#endif
commit-bot@chromium.org6c5f1fd2014-02-24 17:24:15 +0000408
409 // save layers are nested within a specific MC state. This stack tracks
410 // the nesting MC state's ID as save layers are pushed and popped.
411 SkTDArray<int> fStateIDStack;
robertphillips@google.com105a4a52014-02-11 15:10:40 +0000412};
413
robertphillips@google.com3cd6fdf2014-02-11 15:40:54 +0000414#endif