1/*
2 * Copyright 2012 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
8#ifndef GrClipMaskCache_DEFINED
9#define GrClipMaskCache_DEFINED
10
11#include "GrContext.h"
12#include "SkClipStack.h"
13#include "SkTypes.h"
14
15class GrTexture;
16
17/**
18 * The stencil buffer stores the last clip path - providing a single entry
19 * "cache". This class provides similar functionality for AA clip paths
20 */
21class GrClipMaskCache : public SkNoncopyable {
22public:
23    GrClipMaskCache();
24
25    ~GrClipMaskCache() {
26
27        while (!fStack.empty()) {
28            GrClipStackFrame* temp = (GrClipStackFrame*) fStack.back();
29            temp->~GrClipStackFrame();
30            fStack.pop_back();
31        }
32    }
33
34    bool canReuse(int32_t clipGenID, const SkIRect& bounds) {
35
36        SkASSERT(clipGenID != SkClipStack::kWideOpenGenID);
37        SkASSERT(clipGenID != SkClipStack::kEmptyGenID);
38
39        GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
40
41        // We could reuse the mask if bounds is a subset of last bounds. We'd have to communicate
42        // an offset to the caller.
43        if (back->fLastMask.texture() &&
44            back->fLastBound == bounds &&
45            back->fLastClipGenID == clipGenID) {
46            return true;
47        }
48
49        return false;
50    }
51
52    void reset() {
53        if (fStack.empty()) {
54//            SkASSERT(false);
55            return;
56        }
57
58        GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
59
60        back->reset();
61    }
62
63    /**
64     * After a "push" the clip state is entirely open. Currently, the
65     * entire clip stack will be re-rendered into a new clip mask.
66     * TODO: can we take advantage of the nested nature of the clips to
67     * reduce the mask creation cost?
68     */
69    void push();
70
71    void pop() {
72        //SkASSERT(!fStack.empty());
73
74        if (!fStack.empty()) {
75            GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
76
77            back->~GrClipStackFrame();
78            fStack.pop_back();
79        }
80    }
81
82    int32_t getLastClipGenID() const {
83
84        if (fStack.empty()) {
85            return SkClipStack::kInvalidGenID;
86        }
87
88        return ((GrClipStackFrame*) fStack.back())->fLastClipGenID;
89    }
90
91    GrTexture* getLastMask() {
92
93        if (fStack.empty()) {
94            SkASSERT(false);
95            return NULL;
96        }
97
98        GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
99
100        return back->fLastMask.texture();
101    }
102
103    const GrTexture* getLastMask() const {
104
105        if (fStack.empty()) {
106            SkASSERT(false);
107            return NULL;
108        }
109
110        GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
111
112        return back->fLastMask.texture();
113    }
114
115    void acquireMask(int32_t clipGenID,
116                     const GrTextureDesc& desc,
117                     const SkIRect& bound) {
118
119        if (fStack.empty()) {
120            SkASSERT(false);
121            return;
122        }
123
124        GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
125
126        back->acquireMask(fContext, clipGenID, desc, bound);
127    }
128
129    int getLastMaskWidth() const {
130
131        if (fStack.empty()) {
132            SkASSERT(false);
133            return -1;
134        }
135
136        GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
137
138        if (NULL == back->fLastMask.texture()) {
139            return -1;
140        }
141
142        return back->fLastMask.texture()->width();
143    }
144
145    int getLastMaskHeight() const {
146
147        if (fStack.empty()) {
148            SkASSERT(false);
149            return -1;
150        }
151
152        GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
153
154        if (NULL == back->fLastMask.texture()) {
155            return -1;
156        }
157
158        return back->fLastMask.texture()->height();
159    }
160
161    void getLastBound(SkIRect* bound) const {
162
163        if (fStack.empty()) {
164            SkASSERT(false);
165            bound->setEmpty();
166            return;
167        }
168
169        GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
170
171        *bound = back->fLastBound;
172    }
173
174    void setContext(GrContext* context) {
175        fContext = context;
176    }
177
178    GrContext* getContext() {
179        return fContext;
180    }
181
182    void releaseResources() {
183
184        SkDeque::F2BIter iter(fStack);
185        for (GrClipStackFrame* frame = (GrClipStackFrame*) iter.next();
186                frame != NULL;
187                frame = (GrClipStackFrame*) iter.next()) {
188            frame->reset();
189        }
190    }
191
192private:
193    struct GrClipStackFrame {
194
195        GrClipStackFrame() {
196            this->reset();
197        }
198
199        void acquireMask(GrContext* context,
200                         int32_t clipGenID,
201                         const GrTextureDesc& desc,
202                         const SkIRect& bound) {
203
204            fLastClipGenID = clipGenID;
205
206            fLastMask.set(context, desc);
207
208            fLastBound = bound;
209        }
210
211        void reset () {
212            fLastClipGenID = SkClipStack::kInvalidGenID;
213
214            GrTextureDesc desc;
215
216            fLastMask.set(NULL, desc);
217            fLastBound.setEmpty();
218        }
219
220        int32_t                 fLastClipGenID;
221        // The mask's width & height values are used by GrClipMaskManager to correctly scale the
222        // texture coords for the geometry drawn with this mask.
223        GrAutoScratchTexture    fLastMask;
224        // fLastBound stores the bounding box of the clip mask in clip-stack space. This rect is
225        // used by GrClipMaskManager to position a rect and compute texture coords for the mask.
226        SkIRect                 fLastBound;
227    };
228
229    GrContext*   fContext;
230    SkDeque      fStack;
231
232    typedef SkNoncopyable INHERITED;
233};
234
235#endif // GrClipMaskCache_DEFINED
236