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 "GrSingleTextureEffect.h"
12 #include "glsl/GrGLSLFragmentProcessor.h"
13 #include "glsl/GrGLSLProgramDataManager.h"
14 
15 class GrGLProgramBuilder;
16 class GrGLSLShaderBuilder;
17 class GrInvariantOutput;
18 class GrGLSLTextureSampler;
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 vec4 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 SkRect gDummyRect = {0, 0, 0, 0};
48         static const GrTextureDomain gDomain(gDummyRect, 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(const SkRect& domain, Mode, int index = -1);
57 
domain()58     const SkRect& domain() const { return fDomain; }
mode()59     Mode mode() const { return fMode; }
60 
61     /* Computes a domain that bounds all the texels in texelRect. Note that with bilerp enabled
62        texels neighboring the domain may be read. */
MakeTexelDomain(const GrTexture * texture,const SkIRect & texelRect)63     static const SkRect MakeTexelDomain(const GrTexture* texture, const SkIRect& texelRect) {
64         SkScalar wInv = SK_Scalar1 / texture->width();
65         SkScalar hInv = SK_Scalar1 / texture->height();
66         SkRect result = {
67             texelRect.fLeft * wInv,
68             texelRect.fTop * hInv,
69             texelRect.fRight * wInv,
70             texelRect.fBottom * hInv
71         };
72         return result;
73     }
74 
MakeTexelDomainForMode(const GrTexture * texture,const SkIRect & texelRect,Mode mode)75     static const SkRect MakeTexelDomainForMode(const GrTexture* texture, const SkIRect& texelRect, Mode mode) {
76         // For Clamp mode, inset by half a texel.
77         SkScalar wInv = SK_Scalar1 / texture->width();
78         SkScalar hInv = SK_Scalar1 / texture->height();
79         SkScalar inset = (mode == kClamp_Mode && !texelRect.isEmpty()) ? SK_ScalarHalf : 0;
80         return SkRect::MakeLTRB(
81             (texelRect.fLeft + inset) * wInv,
82             (texelRect.fTop + inset) * hInv,
83             (texelRect.fRight - inset) * wInv,
84             (texelRect.fBottom - inset) * hInv
85         );
86     }
87 
88     bool operator== (const GrTextureDomain& that) const {
89         return fMode == that.fMode && (kIgnore_Mode == fMode || fDomain == that.fDomain);
90     }
91 
92     /**
93      * A GrGLSLFragmentProcessor subclass that corresponds to a GrProcessor subclass that uses
94      * GrTextureDomain should include this helper. It generates the texture domain GLSL, produces
95      * the part of the effect key that reflects the texture domain code, and performs the uniform
96      * uploads necessary for texture domains.
97      */
98     class GLDomain {
99     public:
GLDomain()100         GLDomain() {
101             for (int i = 0; i < kPrevDomainCount; i++) {
102                 fPrevDomain[i] = SK_FloatNaN;
103             }
104             SkDEBUGCODE(fMode = (Mode) -1;)
105         }
106 
107         /**
108          * Call this from GrGLSLFragmentProcessor::emitCode() to sample the texture W.R.T. the
109          * domain and mode.
110          *
111          * @param outcolor  name of vec4 variable to hold the sampled color.
112          * @param inCoords  name of vec2 variable containing the coords to be used with the domain.
113          *                  It is assumed that this is a variable and not an expression.
114          * @param inModulateColor   if non-nullptr the sampled color will be modulated with this
115          *                          expression before being written to outColor.
116          */
117         void sampleTexture(GrGLSLShaderBuilder* builder,
118                            GrGLSLUniformHandler* uniformHandler,
119                            const GrGLSLCaps* glslCaps,
120                            const GrTextureDomain& textureDomain,
121                            const char* outColor,
122                            const SkString& inCoords,
123                            const GrGLSLTextureSampler& sampler,
124                            const char* inModulateColor = nullptr);
125 
126         /**
127          * Call this from GrGLSLFragmentProcessor::setData() to upload uniforms necessary for the
128          * texture domain. The rectangle is automatically adjusted to account for the texture's
129          * origin.
130          */
131         void setData(const GrGLSLProgramDataManager& pdman, const GrTextureDomain& textureDomain,
132                      GrSurfaceOrigin textureOrigin);
133 
134         enum {
135             kDomainKeyBits = 2, // See DomainKey().
136         };
137 
138         /**
139          * GrGLSLFragmentProcessor::GenKey() must call this and include the returned value in it's
140          * computed key. The returned will be limited to the lower kDomainKeyBits bits.
141          */
DomainKey(const GrTextureDomain & domain)142         static uint32_t DomainKey(const GrTextureDomain& domain) {
143             GR_STATIC_ASSERT(kModeCount <= 4);
144             return domain.mode();
145         }
146 
147     private:
148         static const int kPrevDomainCount = 4;
149         SkDEBUGCODE(Mode                        fMode;)
150         GrGLSLProgramDataManager::UniformHandle fDomainUni;
151         SkString                                fDomainName;
152         float                                   fPrevDomain[kPrevDomainCount];
153     };
154 
155 protected:
156     Mode    fMode;
157     SkRect  fDomain;
158     int     fIndex;
159 
160     typedef GrSingleTextureEffect INHERITED;
161 };
162 
163 /**
164  * A basic texture effect that uses GrTextureDomain.
165  */
166 class GrTextureDomainEffect : public GrSingleTextureEffect {
167 
168 public:
169     static const GrFragmentProcessor* Create(GrTexture*,
170                                              const SkMatrix&,
171                                              const SkRect& domain,
172                                              GrTextureDomain::Mode,
173                                              GrTextureParams::FilterMode filterMode,
174                                              GrCoordSet = kLocal_GrCoordSet);
175 
176     virtual ~GrTextureDomainEffect();
177 
name()178     const char* name() const override { return "TextureDomain"; }
179 
dumpInfo()180     SkString dumpInfo() const override {
181         SkString str;
182         str.appendf("Domain: [L: %.2f, T: %.2f, R: %.2f, B: %.2f] ",
183                     fTextureDomain.domain().fLeft, fTextureDomain.domain().fTop,
184                     fTextureDomain.domain().fRight, fTextureDomain.domain().fBottom);
185         str.append(INHERITED::dumpInfo());
186         return str;
187     }
188 
textureDomain()189     const GrTextureDomain& textureDomain() const { return fTextureDomain; }
190 
191 protected:
192     GrTextureDomain fTextureDomain;
193 
194 private:
195     GrTextureDomainEffect(GrTexture*,
196                           const SkMatrix&,
197                           const SkRect& domain,
198                           GrTextureDomain::Mode,
199                           GrTextureParams::FilterMode,
200                           GrCoordSet);
201 
202     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
203 
204     void onGetGLSLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
205 
206     bool onIsEqual(const GrFragmentProcessor&) const override;
207 
208     void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
209 
210     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
211 
212     typedef GrSingleTextureEffect INHERITED;
213 };
214 
215 #endif
216