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 : SkNoncopyable {
57    public:
58        class MatrixInfo {
59        public:
60            void reset() {
61                fMatrixID = kIdentityMatID;
62                fMatrix.reset();
63            }
64
65            void preTranslate(SkScalar dx, SkScalar dy) {
66                fMatrixID = -1;
67                fMatrix.preTranslate(dx, dy);
68            }
69
70            void preScale(SkScalar sx, SkScalar sy) {
71                fMatrixID = -1;
72                fMatrix.preScale(sx, sy);
73            }
74
75            void preRotate(SkScalar degrees) {
76                fMatrixID = -1;
77                fMatrix.preRotate(degrees);
78            }
79
80            void preSkew(SkScalar sx, SkScalar sy) {
81                fMatrixID = -1;
82                fMatrix.preSkew(sx, sy);
83            }
84
85            void preConcat(const SkMatrix& matrix) {
86                fMatrixID = -1;
87                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            typedef SkNoncopyable INHERITED;
109        };
110
111        class ClipInfo : SkNoncopyable {
112        public:
113            ClipInfo() {}
114
115            bool clipRect(const SkRect& rect,
116                          SkRegion::Op op,
117                          bool doAA,
118                          int matrixID) {
119                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;
125                return false;
126            }
127
128            bool clipRRect(const SkRRect& rrect,
129                           SkRegion::Op op,
130                           bool doAA,
131                           int matrixID) {
132                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;
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, SkMatrixClipStateMgr* mgr);
151
152            SkDEBUGCODE(int numClips() const { return fClips.count(); })
153
154        private:
155            enum ClipType {
156                kRect_ClipType,
157                kRRect_ClipType,
158                kPath_ClipType,
159                kRegion_ClipType
160            };
161
162            class ClipOp {
163            public:
164                ClipType     fClipType;
165
166                union {
167                    SkRRect fRRect;        // also stores clip rect
168                    int     fPathID;
169                    int     fRegionID;
170                } fGeom;
171
172                bool         fDoAA;
173                SkRegion::Op fOp;
174
175                // The CTM in effect when this clip call was issued
176                int          fMatrixID;
177            };
178
179            SkTDArray<ClipOp> fClips;
180
181            typedef SkNoncopyable INHERITED;
182        };
183
184        MatrixClipState(MatrixClipState* prev, int flags)
185            : fPrev(prev)
186        {
187            fHasOpen = false;
188
189            if (NULL == prev) {
190                fLayerID = 0;
191
192                fMatrixInfoStorage.reset();
193                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;
198#ifdef SK_DEBUG
199                fExpectedDepth = 1;
200#endif
201            }
202            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;
222#ifdef SK_DEBUG
223                fExpectedDepth = prev->fExpectedDepth;
224#endif
225            }
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
241        // The next field is only valid when fIsSaveLayer is set.
242        SkTDArray<int>* fSavedSkipOffsets;
243
244        // Does the MC state have an open block in the skp?
245        bool         fHasOpen;
246
247        MatrixClipState* fPrev;
248
249#ifdef SK_DEBUG
250        int              fExpectedDepth;    // debugging aid
251#endif
252
253        int32_t     fMCStateID;
254    };
255
256    enum CallType {
257        kMatrix_CallType,
258        kClip_CallType,
259        kOther_CallType
260    };
261
262    SkMatrixClipStateMgr();
263    ~SkMatrixClipStateMgr();
264
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
268        fPicRecord = picRecord;
269    }
270
271    SkPictureRecord* getPicRecord() { return fPicRecord; }
272
273    // 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
288    void translate(SkScalar dx, SkScalar dy) {
289        this->call(kMatrix_CallType);
290        fCurMCState->fMatrixInfo->preTranslate(dx, dy);
291    }
292
293    void scale(SkScalar sx, SkScalar sy) {
294        this->call(kMatrix_CallType);
295        fCurMCState->fMatrixInfo->preScale(sx, sy);
296    }
297
298    void rotate(SkScalar degrees) {
299        this->call(kMatrix_CallType);
300        fCurMCState->fMatrixInfo->preRotate(degrees);
301    }
302
303    void skew(SkScalar sx, SkScalar sy) {
304        this->call(kMatrix_CallType);
305        fCurMCState->fMatrixInfo->preSkew(sx, sy);
306    }
307
308    void concat(const SkMatrix& matrix) {
309        this->call(kMatrix_CallType);
310        fCurMCState->fMatrixInfo->preConcat(matrix);
311    }
312
313    void setMatrix(const SkMatrix& matrix) {
314        this->call(kMatrix_CallType);
315        fCurMCState->fMatrixInfo->setMatrix(matrix);
316    }
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,
321                                                fCurMCState->fMatrixInfo->getID(this));
322    }
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,
327                                                 fCurMCState->fMatrixInfo->getID(this));
328    }
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,
333                                                fCurMCState->fMatrixInfo->getID(this));
334    }
335
336    bool clipRegion(const SkRegion& region, SkRegion::Op op) {
337        this->call(SkMatrixClipStateMgr::kClip_CallType);
338        int regionID = this->addRegionToDict(region);
339        return fCurMCState->fClipInfo->clipRegion(fPicRecord, regionID, op,
340                                                  fCurMCState->fMatrixInfo->getID(this));
341    }
342
343    bool call(CallType callType);
344
345    void fillInSkips(SkWriter32* writer, int32_t restoreOffset);
346
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
356    // 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
358    // 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
360    // use the same ID.
361    SkTDArray<SkMatrix> fMatrixDict;
362
363    SkTDArray<SkRegion*> fRegionDict;
364
365    // The MCStateID of the state currently in effect in the byte stream. 0 if none.
366    int32_t          fCurOpenStateID;
367    // The skip offsets for the current open state. These are the locations in the
368    // 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;  // TODO: should we store u32 or size_t instead?
372
373    SkDEBUGCODE(void validate();)
374
375    int MCStackPush(SkCanvas::SaveFlags flags);
376
377    void addClipOffset(size_t offset) {
378        SkASSERT(NULL != fSkipOffsets);
379        SkASSERT(kIdentityWideOpenStateID != fCurOpenStateID);
380        SkASSERT(fCurMCState->fHasOpen);
381        SkASSERT(!fCurMCState->fIsSaveLayer);
382
383        *fSkipOffsets->append() = SkToS32(offset);
384    }
385
386    void writeDeltaMat(int currentMatID, int desiredMatID);
387    static int32_t   NewMCStateID();
388
389    int addRegionToDict(const SkRegion& region);
390    const SkRegion* lookupRegion(int index) {
391        SkASSERT(index >= 0 && index < fRegionDict.count());
392        return fRegionDict[index];
393    }
394
395    // 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    }
402
403    bool isNestingMCState(int stateID);
404
405#ifdef SK_DEBUG
406    int fActualDepth;
407#endif
408
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;
412};
413
414#endif
415