SkMatrixClipStateMgr.h revision 22ef2c37e65e601ba78851e3dbb4ca7a0e4c7d61
1/*
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"
15#include "SkTDArray.h"
16
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
49// state. All the other canvas calls get funnelled into a generic "call" entry
50// point that signals that a state block is required.
51class SkMatrixClipStateMgr {
52public:
53    static const int32_t kIdentityWideOpenStateID = 0;
54    static const int kIdentityMatID = 0;
55
56    class MatrixClipState {
57    public:
58        class MatrixInfo {
59        public:
60            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:
105            SkMatrix fMatrix;
106            int      fMatrixID;
107        };
108
109        class ClipInfo : public SkNoncopyable {
110        public:
111            ClipInfo() {}
112
113            bool clipRect(const SkRect& rect,
114                          SkRegion::Op op,
115                          bool doAA,
116                          int matrixID) {
117                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;
123                newClip->fOffset = kInvalidJumpOffset;
124                return false;
125            }
126
127            bool clipRRect(const SkRRect& rrect,
128                           SkRegion::Op op,
129                           bool doAA,
130                           int matrixID) {
131                ClipOp* newClip = fClips.append();
132                newClip->fClipType = kRRect_ClipType;
133                newClip->fGeom.fRRect = rrect;
134                newClip->fOp = op;
135                newClip->fDoAA = doAA;
136                newClip->fMatrixID = matrixID;
137                newClip->fOffset = kInvalidJumpOffset;
138                return false;
139            }
140
141            bool clipPath(SkPictureRecord* picRecord,
142                          const SkPath& path,
143                          SkRegion::Op op,
144                          bool doAA,
145                          int matrixID);
146            bool clipRegion(SkPictureRecord* picRecord,
147                            int regionID,
148                            SkRegion::Op op,
149                            int matrixID);
150            void writeClip(int* curMatID,
151                           SkMatrixClipStateMgr* mgr,
152                           bool* overrideFirstOp);
153            void fillInSkips(SkWriter32* writer, int32_t restoreOffset);
154
155#ifdef SK_DEBUG
156            void checkOffsetNotEqual(int32_t offset) {
157                for (int i = 0; i < fClips.count(); ++i) {
158                    ClipOp& curClip = fClips[i];
159                    SkASSERT(offset != curClip.fOffset);
160                }
161            }
162#endif
163        private:
164            enum ClipType {
165                kRect_ClipType,
166                kRRect_ClipType,
167                kPath_ClipType,
168                kRegion_ClipType
169            };
170
171            static const int kInvalidJumpOffset = -1;
172
173            class ClipOp {
174            public:
175                ClipType     fClipType;
176
177                union {
178                    SkRRect fRRect;        // also stores clipRect
179                    int     fPathID;
180                    int     fRegionID;
181                } fGeom;
182
183                bool         fDoAA;
184                SkRegion::Op fOp;
185
186                // The CTM in effect when this clip call was issued
187                int          fMatrixID;
188
189                // The offset of this clipOp's "jump-to-offset" location in the skp.
190                // -1 means the offset hasn't been written.
191                int32_t      fOffset;
192            };
193
194            SkTDArray<ClipOp> fClips;
195
196            typedef SkNoncopyable INHERITED;
197        };
198
199        MatrixClipState(MatrixClipState* prev, int flags)
200#ifdef SK_DEBUG
201            : fPrev(prev)
202#endif
203        {
204            if (NULL == prev) {
205                fLayerID = 0;
206
207                fMatrixInfoStorage.reset();
208                fMatrixInfo = &fMatrixInfoStorage;
209                fClipInfo = &fClipInfoStorage;  // ctor handles init of fClipInfoStorage
210
211                // The identity/wide-open-clip state is current by default
212                fMCStateID = kIdentityWideOpenStateID;
213            }
214            else {
215                fLayerID = prev->fLayerID;
216
217                if (flags & SkCanvas::kMatrix_SaveFlag) {
218                    fMatrixInfoStorage = *prev->fMatrixInfo;
219                    fMatrixInfo = &fMatrixInfoStorage;
220                } else {
221                    fMatrixInfo = prev->fMatrixInfo;
222                }
223
224                if (flags & SkCanvas::kClip_SaveFlag) {
225                    // We don't copy the ClipOps of the previous clip states
226                    fClipInfo = &fClipInfoStorage;
227                } else {
228                    fClipInfo = prev->fClipInfo;
229                }
230
231                // Initially a new save/saveLayer represents the same MC state
232                // as its predecessor.
233                fMCStateID = prev->fMCStateID;
234            }
235
236            fIsSaveLayer = false;
237        }
238
239        MatrixInfo*  fMatrixInfo;
240        MatrixInfo   fMatrixInfoStorage;
241
242        ClipInfo*    fClipInfo;
243        ClipInfo     fClipInfoStorage;
244
245        // Tracks the current depth of saveLayers to support the isDrawingToLayer call
246        int          fLayerID;
247        // Does this MC state represent a saveLayer call?
248        bool         fIsSaveLayer;
249
250        // The next two fields are only valid when fIsSaveLayer is set.
251        int32_t      fSaveLayerBaseStateID;
252        bool         fSaveLayerBracketed;
253
254#ifdef SK_DEBUG
255        MatrixClipState* fPrev; // debugging aid
256#endif
257
258        int32_t     fMCStateID;
259    };
260
261    enum CallType {
262        kMatrix_CallType,
263        kClip_CallType,
264        kOther_CallType
265    };
266
267    SkMatrixClipStateMgr();
268    ~SkMatrixClipStateMgr();
269
270    void init(SkPictureRecord* picRecord) {
271        // Note: we're not taking a ref here. It is expected that the SkMatrixClipStateMgr
272        // is owned by the SkPictureRecord object
273        fPicRecord = picRecord;
274    }
275
276    SkPictureRecord* getPicRecord() { return fPicRecord; }
277
278    // TODO: need to override canvas' getSaveCount. Right now we pass the
279    // save* and restore calls on to the base SkCanvas in SkPictureRecord but
280    // this duplicates effort.
281    int getSaveCount() const { return fMatrixClipStack.count(); }
282
283    int save(SkCanvas::SaveFlags flags);
284
285    int saveLayer(const SkRect* bounds, const SkPaint* paint, SkCanvas::SaveFlags flags);
286
287    bool isDrawingToLayer() const {
288        return fCurMCState->fLayerID > 0;
289    }
290
291    void restore();
292
293    bool translate(SkScalar dx, SkScalar dy) {
294        this->call(kMatrix_CallType);
295        return fCurMCState->fMatrixInfo->preTranslate(dx, dy);
296    }
297
298    bool scale(SkScalar sx, SkScalar sy) {
299        this->call(kMatrix_CallType);
300        return fCurMCState->fMatrixInfo->preScale(sx, sy);
301    }
302
303    bool rotate(SkScalar degrees) {
304        this->call(kMatrix_CallType);
305        return fCurMCState->fMatrixInfo->preRotate(degrees);
306    }
307
308    bool skew(SkScalar sx, SkScalar sy) {
309        this->call(kMatrix_CallType);
310        return fCurMCState->fMatrixInfo->preSkew(sx, sy);
311    }
312
313    bool concat(const SkMatrix& matrix) {
314        this->call(kMatrix_CallType);
315        return fCurMCState->fMatrixInfo->preConcat(matrix);
316    }
317
318    void setMatrix(const SkMatrix& matrix) {
319        this->call(kMatrix_CallType);
320        fCurMCState->fMatrixInfo->setMatrix(matrix);
321    }
322
323    bool clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
324        this->call(SkMatrixClipStateMgr::kClip_CallType);
325        return fCurMCState->fClipInfo->clipRect(rect, op, doAA,
326                                                fCurMCState->fMatrixInfo->getID(this));
327    }
328
329    bool clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
330        this->call(SkMatrixClipStateMgr::kClip_CallType);
331        return fCurMCState->fClipInfo->clipRRect(rrect, op, doAA,
332                                                 fCurMCState->fMatrixInfo->getID(this));
333    }
334
335    bool clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
336        this->call(SkMatrixClipStateMgr::kClip_CallType);
337        return fCurMCState->fClipInfo->clipPath(fPicRecord, path, op, doAA,
338                                                fCurMCState->fMatrixInfo->getID(this));
339    }
340
341    bool clipRegion(const SkRegion& region, SkRegion::Op op) {
342        this->call(SkMatrixClipStateMgr::kClip_CallType);
343        int regionID = this->addRegionToDict(region);
344        return fCurMCState->fClipInfo->clipRegion(fPicRecord, regionID, op,
345                                                  fCurMCState->fMatrixInfo->getID(this));
346    }
347
348    bool call(CallType callType);
349
350    void fillInSkips(SkWriter32* writer, int32_t restoreOffset) {
351        // Since we write out the entire clip stack at each block start we
352        // need to update the skips for the entire stack each time too.
353        SkDeque::F2BIter iter(fMatrixClipStack);
354
355        for (const MatrixClipState* state = (const MatrixClipState*) iter.next();
356             state != NULL;
357             state = (const MatrixClipState*) iter.next()) {
358            state->fClipInfo->fillInSkips(writer, restoreOffset);
359        }
360    }
361
362    void finish();
363
364protected:
365    SkPictureRecord* fPicRecord;
366
367    uint32_t         fMatrixClipStackStorage[43]; // sized to fit 2 clip states
368    SkDeque          fMatrixClipStack;
369    MatrixClipState* fCurMCState;
370
371    // This dictionary doesn't actually de-duplicate the matrices (except for the
372    // identity matrix). It merely stores the matrices and allows them to be looked
373    // up by ID later. The de-duplication mainly falls upon the matrix/clip stack
374    // which stores the ID so a revisited clip/matrix (via popping the stack) will
375    // use the same ID.
376    SkTDArray<SkMatrix> fMatrixDict;
377
378    SkTDArray<SkRegion*> fRegionDict;
379
380    // The MCStateID of the state currently in effect in the byte stream. 0 if none.
381    int32_t          fCurOpenStateID;
382
383    SkDEBUGCODE(void validate();)
384
385    void writeDeltaMat(int currentMatID, int desiredMatID);
386    static int32_t   NewMCStateID();
387
388    int addRegionToDict(const SkRegion& region);
389    const SkRegion* lookupRegion(int index) {
390        SkASSERT(index >= 0 && index < fRegionDict.count());
391        return fRegionDict[index];
392    }
393
394    // TODO: add stats to check if the dictionary really does
395    // reduce the size of the SkPicture.
396    int addMatToDict(const SkMatrix& mat);
397    const SkMatrix& lookupMat(int index) {
398        SkASSERT(index >= 0 && index < fMatrixDict.count());
399        return fMatrixDict[index];
400    }
401};
402
403#endif
404