SkMatrixClipStateMgr.h revision 105a4a584c4c2c84c24e102112326b15683673f5
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 "SkTArray.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
55    class MatrixClipState {
56    public:
57        class MatrixInfo {
58        public:
59            SkMatrix fMatrix;
60            // TODO: add an internal dictionary and an ID here
61        };
62
63        class ClipInfo : public SkNoncopyable {
64        public:
65            ClipInfo() {}
66
67            bool clipRect(const SkRect& rect,
68                          SkRegion::Op op,
69                          bool doAA,
70                          const SkMatrix& matrix) {
71                ClipOp& newClip = fClips.push_back();
72                newClip.fClipType = kRect_ClipType;
73                newClip.fGeom.fRRect.setRect(rect);   // storing the clipRect in the RRect
74                newClip.fOp = op;
75                newClip.fDoAA = doAA;
76                newClip.fMatrix = matrix;
77                newClip.fOffset = kInvalidJumpOffset;
78                return false;
79            }
80
81            bool clipRRect(const SkRRect& rrect,
82                           SkRegion::Op op,
83                           bool doAA,
84                           const SkMatrix& matrix) {
85                ClipOp& newClip = fClips.push_back();
86                newClip.fClipType = kRRect_ClipType;
87                newClip.fGeom.fRRect = rrect;
88                newClip.fOp = op;
89                newClip.fDoAA = doAA;
90                newClip.fMatrix = matrix;
91                newClip.fOffset = kInvalidJumpOffset;
92                return false;
93            }
94
95            bool clipPath(SkPictureRecord* picRecord,
96                          const SkPath& path,
97                          SkRegion::Op op,
98                          bool doAA,
99                          const SkMatrix& matrix);
100            bool clipRegion(SkPictureRecord* picRecord,
101                            const SkRegion& region,
102                            SkRegion::Op op,
103                            const SkMatrix& matrix);
104            void writeClip(SkMatrix* curMat,
105                           SkPictureRecord* picRecord,
106                           bool* overrideFirstOp);
107            void fillInSkips(SkWriter32* writer, int32_t restoreOffset);
108
109#ifdef SK_DEBUG
110            void checkOffsetNotEqual(int32_t offset) {
111                for (int i = 0; i < fClips.count(); ++i) {
112                    ClipOp& curClip = fClips[i];
113                    SkASSERT(offset != curClip.fOffset);
114                }
115            }
116#endif
117        private:
118            enum ClipType {
119                kRect_ClipType,
120                kRRect_ClipType,
121                kPath_ClipType,
122                kRegion_ClipType
123            };
124
125            static const int kInvalidJumpOffset = -1;
126
127            class ClipOp {
128            public:
129                ClipOp() {}
130                ~ClipOp() {
131                    if (kRegion_ClipType == fClipType) {
132                        SkDELETE(fGeom.fRegion);
133                    }
134                }
135
136                ClipType     fClipType;
137
138                union {
139                    SkRRect         fRRect;        // also stores clipRect
140                    int             fPathID;
141                    // TODO: add internal dictionary of regions
142                    // This parameter forces us to have a dtor and thus use
143                    // SkTArray rather then SkTDArray!
144                    const SkRegion* fRegion;
145                } fGeom;
146
147                bool         fDoAA;
148                SkRegion::Op fOp;
149
150                // The CTM in effect when this clip call was issued
151                // TODO: add an internal dictionary and replace with ID
152                SkMatrix     fMatrix;
153
154                // The offset of this clipOp's "jump-to-offset" location in the skp.
155                // -1 means the offset hasn't been written.
156                int32_t      fOffset;
157            };
158
159            SkTArray<ClipOp> fClips;
160
161            typedef SkNoncopyable INHERITED;
162        };
163
164        MatrixClipState(MatrixClipState* prev, int flags)
165#ifdef SK_DEBUG
166            : fPrev(prev)
167#endif
168        {
169            if (NULL == prev) {
170                fLayerID = 0;
171
172                fMatrixInfoStorage.fMatrix.reset();
173                fMatrixInfo = &fMatrixInfoStorage;
174                fClipInfo = &fClipInfoStorage;  // ctor handles init of fClipInfoStorage
175
176                // The identity/wide-open-clip state is current by default
177                fMCStateID = kIdentityWideOpenStateID;
178            }
179            else {
180                fLayerID = prev->fLayerID;
181
182                if (flags & SkCanvas::kMatrix_SaveFlag) {
183                    fMatrixInfoStorage = *prev->fMatrixInfo;
184                    fMatrixInfo = &fMatrixInfoStorage;
185                } else {
186                    fMatrixInfo = prev->fMatrixInfo;
187                }
188
189                if (flags & SkCanvas::kClip_SaveFlag) {
190                    // We don't copy the ClipOps of the previous clip states
191                    fClipInfo = &fClipInfoStorage;
192                } else {
193                    fClipInfo = prev->fClipInfo;
194                }
195
196                // Initially a new save/saveLayer represents the same MC state
197                // as its predecessor.
198                fMCStateID = prev->fMCStateID;
199            }
200
201            fIsSaveLayer = false;
202        }
203
204        MatrixInfo*  fMatrixInfo;
205        MatrixInfo   fMatrixInfoStorage;
206
207        ClipInfo*    fClipInfo;
208        ClipInfo     fClipInfoStorage;
209
210        // Tracks the current depth of saveLayers to support the isDrawingToLayer call
211        int          fLayerID;
212        // Does this MC state represent a saveLayer call?
213        bool         fIsSaveLayer;
214
215        // The next two fields are only valid when fIsSaveLayer is set.
216        int32_t      fSaveLayerBaseStateID;
217        bool         fSaveLayerBracketed;
218
219#ifdef SK_DEBUG
220        MatrixClipState* fPrev; // debugging aid
221#endif
222
223        int32_t     fMCStateID;
224    };
225
226    enum CallType {
227        kMatrix_CallType,
228        kClip_CallType,
229        kOther_CallType
230    };
231
232    SkMatrixClipStateMgr();
233
234    void init(SkPictureRecord* picRecord) {
235        // Note: we're not taking a ref here. It is expected that the SkMatrixClipStateMgr
236        // is owned by the SkPictureRecord object
237        fPicRecord = picRecord;
238    }
239
240    // TODO: need to override canvas' getSaveCount. Right now we pass the
241    // save* and restore calls on to the base SkCanvas in SkPictureRecord but
242    // this duplicates effort.
243    int getSaveCount() const { return fMatrixClipStack.count(); }
244
245    int save(SkCanvas::SaveFlags flags);
246
247    int saveLayer(const SkRect* bounds, const SkPaint* paint, SkCanvas::SaveFlags flags);
248
249    bool isDrawingToLayer() const {
250        return fCurMCState->fLayerID > 0;
251    }
252
253    void restore();
254
255    bool translate(SkScalar dx, SkScalar dy) {
256        this->call(kMatrix_CallType);
257        return fCurMCState->fMatrixInfo->fMatrix.preTranslate(dx, dy);
258    }
259
260    bool scale(SkScalar sx, SkScalar sy) {
261        this->call(kMatrix_CallType);
262        return fCurMCState->fMatrixInfo->fMatrix.preScale(sx, sy);
263    }
264
265    bool rotate(SkScalar degrees) {
266        this->call(kMatrix_CallType);
267        return fCurMCState->fMatrixInfo->fMatrix.preRotate(degrees);
268    }
269
270    bool skew(SkScalar sx, SkScalar sy) {
271        this->call(kMatrix_CallType);
272        return fCurMCState->fMatrixInfo->fMatrix.preSkew(sx, sy);
273    }
274
275    bool concat(const SkMatrix& matrix) {
276        this->call(kMatrix_CallType);
277        return fCurMCState->fMatrixInfo->fMatrix.preConcat(matrix);
278    }
279
280    void setMatrix(const SkMatrix& matrix) {
281        this->call(kMatrix_CallType);
282        fCurMCState->fMatrixInfo->fMatrix = matrix;
283    }
284
285    bool clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
286        this->call(SkMatrixClipStateMgr::kClip_CallType);
287        return fCurMCState->fClipInfo->clipRect(rect, op, doAA,
288                                                fCurMCState->fMatrixInfo->fMatrix);
289    }
290
291    bool clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
292        this->call(SkMatrixClipStateMgr::kClip_CallType);
293        return fCurMCState->fClipInfo->clipRRect(rrect, op, doAA,
294                                                 fCurMCState->fMatrixInfo->fMatrix);
295    }
296
297    bool clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
298        this->call(SkMatrixClipStateMgr::kClip_CallType);
299        return fCurMCState->fClipInfo->clipPath(fPicRecord, path, op, doAA,
300                                                fCurMCState->fMatrixInfo->fMatrix);
301    }
302
303    bool clipRegion(const SkRegion& region, SkRegion::Op op) {
304        this->call(SkMatrixClipStateMgr::kClip_CallType);
305        return fCurMCState->fClipInfo->clipRegion(fPicRecord, region, op,
306                                                  fCurMCState->fMatrixInfo->fMatrix);
307    }
308
309    bool call(CallType callType);
310
311    void fillInSkips(SkWriter32* writer, int32_t restoreOffset) {
312        // Since we write out the entire clip stack at each block start we
313        // need to update the skips for the entire stack each time too.
314        SkDeque::F2BIter iter(fMatrixClipStack);
315
316        for (const MatrixClipState* state = (const MatrixClipState*) iter.next();
317             state != NULL;
318             state = (const MatrixClipState*) iter.next()) {
319            state->fClipInfo->fillInSkips(writer, restoreOffset);
320        }
321    }
322
323    void finish();
324
325protected:
326    SkPictureRecord* fPicRecord;
327
328    uint32_t         fMatrixClipStackStorage[43]; // sized to fit 2 clip states
329    SkDeque          fMatrixClipStack;
330    MatrixClipState* fCurMCState;
331
332    // The MCStateID of the state currently in effect in the byte stream. 0 if none.
333    int32_t          fCurOpenStateID;
334
335    SkDEBUGCODE(void validate();)
336
337    static void WriteDeltaMat(SkPictureRecord* picRecord,
338                              const SkMatrix& current,
339                              const SkMatrix& desired);
340    static int32_t   NewMCStateID();
341};
342
343#endif