• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 GrTextureDomainEffect_DEFINED
9 #define GrTextureDomainEffect_DEFINED
10 
11 #include "GrCoordTransform.h"
12 #include "GrFragmentProcessor.h"
13 #include "glsl/GrGLSLFragmentProcessor.h"
14 #include "glsl/GrGLSLProgramDataManager.h"
15 
16 class GrGLProgramBuilder;
17 class GrGLSLShaderBuilder;
18 class GrInvariantOutput;
19 class GrGLSLUniformHandler;
20 struct SkRect;
21 
22 /**
23  * Limits a texture's lookup coordinates to a domain. Samples outside the domain are either clamped
24  * the edge of the domain or result in a half4 of zeros (decal mode). The domain is clipped to
25  * normalized texture coords ([0,1]x[0,1] square). Bilinear filtering can cause texels outside the
26  * domain to affect the read value unless the caller considers this when calculating the domain.
27  */
28 class GrTextureDomain {
29 public:
30     enum Mode {
31         // Ignore the texture domain rectangle.
32         kIgnore_Mode,
33         // Clamp texture coords to the domain rectangle.
34         kClamp_Mode,
35         // Treat the area outside the domain rectangle as fully transparent.
36         kDecal_Mode,
37         // Wrap texture coordinates.  NOTE: filtering may not work as expected because Bilerp will
38         // read texels outside of the domain.  We could perform additional texture reads and filter
39         // in the shader, but are not currently doing this for performance reasons
40         kRepeat_Mode,
41 
42         kLastMode = kRepeat_Mode
43     };
44     static const int kModeCount = kLastMode + 1;
45 
IgnoredDomain()46     static const GrTextureDomain& IgnoredDomain() {
47         static const GrTextureDomain gDomain((GrTextureProxy*)nullptr,
48                                              SkRect::MakeEmpty(), kIgnore_Mode, kIgnore_Mode);
49         return gDomain;
50     }
51 
52     /**
53      * @param index     Pass a value >= 0 if using multiple texture domains in the same effect.
54      *                  It is used to keep inserted variables from causing name collisions.
55      */
56     GrTextureDomain(GrTextureProxy*, const SkRect& domain, Mode modeX, Mode modeY, int index = -1);
57 
58     GrTextureDomain(const GrTextureDomain&) = default;
59 
domain()60     const SkRect& domain() const { return fDomain; }
modeX()61     Mode modeX() const { return fModeX; }
modeY()62     Mode modeY() const { return fModeY; }
63 
64     /*
65      * Computes a domain that bounds all the texels in texelRect, possibly insetting by half a pixel
66      * depending on the mode. The mode is used for both axes.
67      */
MakeTexelDomain(const SkIRect & texelRect,Mode mode)68     static const SkRect MakeTexelDomain(const SkIRect& texelRect, Mode mode) {
69         return MakeTexelDomain(texelRect, mode, mode);
70     }
71 
MakeTexelDomain(const SkIRect & texelRect,Mode modeX,Mode modeY)72     static const SkRect MakeTexelDomain(const SkIRect& texelRect, Mode modeX, Mode modeY) {
73         // For Clamp and decal modes, inset by half a texel
74         SkScalar insetX = ((modeX == kClamp_Mode || modeX == kDecal_Mode) && texelRect.width() > 0)
75                 ? SK_ScalarHalf : 0;
76         SkScalar insetY = ((modeY == kClamp_Mode || modeY == kDecal_Mode) && texelRect.height() > 0)
77                 ? SK_ScalarHalf : 0;
78         return SkRect::MakeLTRB(texelRect.fLeft + insetX, texelRect.fTop + insetY,
79                                 texelRect.fRight - insetX, texelRect.fBottom - insetY);
80     }
81 
82     // Convenience to determine if any axis of a texture uses an explicit decal mode or the hardware
83     // clamp to border decal mode.
IsDecalSampled(GrSamplerState::WrapMode wrapX,GrSamplerState::WrapMode wrapY,Mode modeX,Mode modeY)84     static bool IsDecalSampled(GrSamplerState::WrapMode wrapX, GrSamplerState::WrapMode wrapY,
85                                Mode modeX, Mode modeY) {
86         return wrapX == GrSamplerState::WrapMode::kClampToBorder ||
87                wrapY == GrSamplerState::WrapMode::kClampToBorder ||
88                modeX == kDecal_Mode ||
89                modeY == kDecal_Mode;
90     }
91 
IsDecalSampled(const GrSamplerState::WrapMode wraps[2],Mode modeX,Mode modeY)92     static bool IsDecalSampled(const GrSamplerState::WrapMode wraps[2], Mode modeX, Mode modeY) {
93         return IsDecalSampled(wraps[0], wraps[1], modeX, modeY);
94     }
95 
IsDecalSampled(const GrSamplerState & sampler,Mode modeX,Mode modeY)96     static bool IsDecalSampled(const GrSamplerState& sampler, Mode modeX, Mode modeY) {
97         return IsDecalSampled(sampler.wrapModeX(), sampler.wrapModeY(), modeX, modeY);
98     }
99 
100     bool operator==(const GrTextureDomain& that) const {
101         return fModeX == that.fModeX && fModeY == that.fModeY &&
102                (kIgnore_Mode == fModeX || (fDomain.fLeft == that.fDomain.fLeft &&
103                                            fDomain.fRight == that.fDomain.fRight)) &&
104                (kIgnore_Mode == fModeY || (fDomain.fTop == that.fDomain.fTop &&
105                                            fDomain.fBottom == that.fDomain.fBottom));
106     }
107 
108     /**
109      * A GrGLSLFragmentProcessor subclass that corresponds to a GrProcessor subclass that uses
110      * GrTextureDomain should include this helper. It generates the texture domain GLSL, produces
111      * the part of the effect key that reflects the texture domain code, and performs the uniform
112      * uploads necessary for texture domains.
113      */
114     class GLDomain {
115     public:
GLDomain()116         GLDomain() {
117             for (int i = 0; i < kPrevDomainCount; i++) {
118                 fPrevDomain[i] = SK_FloatNaN;
119             }
120         }
121 
122         /**
123          * Call this from GrGLSLFragmentProcessor::emitCode() to sample the texture W.R.T. the
124          * domain and mode.
125          *
126          * @param outcolor  name of half4 variable to hold the sampled color.
127          * @param inCoords  name of float2 variable containing the coords to be used with the domain.
128          *                  It is assumed that this is a variable and not an expression.
129          * @param inModulateColor   if non-nullptr the sampled color will be modulated with this
130          *                          expression before being written to outColor.
131          */
132         void sampleTexture(GrGLSLShaderBuilder* builder,
133                            GrGLSLUniformHandler* uniformHandler,
134                            const GrShaderCaps* shaderCaps,
135                            const GrTextureDomain& textureDomain,
136                            const char* outColor,
137                            const SkString& inCoords,
138                            GrGLSLFragmentProcessor::SamplerHandle sampler,
139                            const char* inModulateColor = nullptr);
140 
141         /**
142          * Call this from GrGLSLFragmentProcessor::setData() to upload uniforms necessary for the
143          * texture domain. The rectangle is automatically adjusted to account for the texture's
144          * origin.
145          */
146         void setData(const GrGLSLProgramDataManager&, const GrTextureDomain&, GrTextureProxy*,
147                      const GrSamplerState& sampler);
148 
149         enum {
150             kModeBits = 2, // See DomainKey().
151             kDomainKeyBits = 4
152         };
153 
154         /**
155          * GrGLSLFragmentProcessor::GenKey() must call this and include the returned value in it's
156          * computed key. The returned will be limited to the lower kDomainKeyBits bits.
157          */
DomainKey(const GrTextureDomain & domain)158         static uint32_t DomainKey(const GrTextureDomain& domain) {
159             GR_STATIC_ASSERT(kModeCount <= (1 << kModeBits));
160             return domain.modeX() | (domain.modeY() << kModeBits);
161         }
162 
163     private:
164         static const int kPrevDomainCount = 4;
165         SkDEBUGCODE(Mode                        fModeX;)
166         SkDEBUGCODE(Mode                        fModeY;)
167         SkDEBUGCODE(bool                        fHasMode = false;)
168         GrGLSLProgramDataManager::UniformHandle fDomainUni;
169         SkString                                fDomainName;
170 
171         // Only initialized if the domain has at least one decal axis
172         GrGLSLProgramDataManager::UniformHandle fDecalUni;
173         SkString                                fDecalName;
174 
175         float                                   fPrevDomain[kPrevDomainCount];
176     };
177 
178 protected:
179     Mode    fModeX;
180     Mode    fModeY;
181     SkRect  fDomain;
182     int     fIndex;
183 };
184 
185 /**
186  * A basic texture effect that uses GrTextureDomain.
187  */
188 class GrTextureDomainEffect : public GrFragmentProcessor {
189 public:
190     static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy>,
191                                                      const SkMatrix&,
192                                                      const SkRect& domain,
193                                                      GrTextureDomain::Mode mode,
194                                                      GrSamplerState::Filter filterMode);
195 
196     static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy>,
197                                                      const SkMatrix&,
198                                                      const SkRect& domain,
199                                                      GrTextureDomain::Mode modeX,
200                                                      GrTextureDomain::Mode modeY,
201                                                      const GrSamplerState& sampler);
202 
name()203     const char* name() const override { return "TextureDomain"; }
204 
clone()205     std::unique_ptr<GrFragmentProcessor> clone() const override {
206         return std::unique_ptr<GrFragmentProcessor>(new GrTextureDomainEffect(*this));
207     }
208 
209 #ifdef SK_DEBUG
dumpInfo()210     SkString dumpInfo() const override {
211         SkString str;
212         str.appendf("Domain: [L: %.2f, T: %.2f, R: %.2f, B: %.2f]",
213                     fTextureDomain.domain().fLeft, fTextureDomain.domain().fTop,
214                     fTextureDomain.domain().fRight, fTextureDomain.domain().fBottom);
215         str.append(INHERITED::dumpInfo());
216         return str;
217     }
218 #endif
219 
220 private:
221     GrCoordTransform fCoordTransform;
222     GrTextureDomain fTextureDomain;
223     TextureSampler fTextureSampler;
224 
225     GrTextureDomainEffect(sk_sp<GrTextureProxy>,
226                           const SkMatrix&,
227                           const SkRect& domain,
228                           GrTextureDomain::Mode modeX,
229                           GrTextureDomain::Mode modeY,
230                           const GrSamplerState&);
231 
232     explicit GrTextureDomainEffect(const GrTextureDomainEffect&);
233 
234     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
235 
236     void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
237 
238     bool onIsEqual(const GrFragmentProcessor&) const override;
239 
onTextureSampler(int)240     const TextureSampler& onTextureSampler(int) const override { return fTextureSampler; }
241 
242     GR_DECLARE_FRAGMENT_PROCESSOR_TEST
243 
244     typedef GrFragmentProcessor INHERITED;
245 };
246 
247 class GrDeviceSpaceTextureDecalFragmentProcessor : public GrFragmentProcessor {
248 public:
249     static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy>,
250                                                      const SkIRect& subset,
251                                                      const SkIPoint& deviceSpaceOffset);
252 
name()253     const char* name() const override { return "GrDeviceSpaceTextureDecalFragmentProcessor"; }
254 
255 #ifdef SK_DEBUG
dumpInfo()256     SkString dumpInfo() const override {
257         SkString str;
258         str.appendf("Domain: [L: %.2f, T: %.2f, R: %.2f, B: %.2f] Offset: [%d %d]",
259                     fTextureDomain.domain().fLeft, fTextureDomain.domain().fTop,
260                     fTextureDomain.domain().fRight, fTextureDomain.domain().fBottom,
261                     fDeviceSpaceOffset.fX, fDeviceSpaceOffset.fY);
262         str.append(INHERITED::dumpInfo());
263         return str;
264     }
265 #endif
266 
267     std::unique_ptr<GrFragmentProcessor> clone() const override;
268 
269 private:
270     TextureSampler fTextureSampler;
271     GrTextureDomain fTextureDomain;
272     SkIPoint fDeviceSpaceOffset;
273 
274     GrDeviceSpaceTextureDecalFragmentProcessor(sk_sp<GrTextureProxy>,
275                                                const SkIRect&, const SkIPoint&);
276     GrDeviceSpaceTextureDecalFragmentProcessor(const GrDeviceSpaceTextureDecalFragmentProcessor&);
277 
278     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
279 
280     // Since we always use decal mode, there is no need for key data.
onGetGLSLProcessorKey(const GrShaderCaps &,GrProcessorKeyBuilder *)281     void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
282 
283     bool onIsEqual(const GrFragmentProcessor& fp) const override;
284 
onTextureSampler(int)285     const TextureSampler& onTextureSampler(int) const override { return fTextureSampler; }
286 
287     GR_DECLARE_FRAGMENT_PROCESSOR_TEST
288 
289     typedef GrFragmentProcessor INHERITED;
290 };
291 #endif
292