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 "gl/GrGLProcessor.h"
13 
14 class GrGLProgramBuilder;
15 class GrGLShaderBuilder;
16 class GrInvariantOutput;
17 struct SkRect;
18 
19 /**
20  * Limits a texture's lookup coordinates to a domain. Samples outside the domain are either clamped
21  * the edge of the domain or result in a vec4 of zeros (decal mode). The domain is clipped to
22  * normalized texture coords ([0,1]x[0,1] square). Bilinear filtering can cause texels outside the
23  * domain to affect the read value unless the caller considers this when calculating the domain.
24  */
25 class GrTextureDomain {
26 public:
27     enum Mode {
28         // Ignore the texture domain rectangle.
29         kIgnore_Mode,
30         // Clamp texture coords to the domain rectangle.
31         kClamp_Mode,
32         // Treat the area outside the domain rectangle as fully transparent.
33         kDecal_Mode,
34         // Wrap texture coordinates.  NOTE: filtering may not work as expected because Bilerp will
35         // read texels outside of the domain.  We could perform additional texture reads and filter
36         // in the shader, but are not currently doing this for performance reasons
37         kRepeat_Mode,
38 
39         kLastMode = kRepeat_Mode
40     };
41     static const int kModeCount = kLastMode + 1;
42 
IgnoredDomain()43     static const GrTextureDomain& IgnoredDomain() {
44         static const SkRect gDummyRect = {0, 0, 0, 0};
45         static const GrTextureDomain gDomain(gDummyRect, kIgnore_Mode);
46         return gDomain;
47     }
48 
49     /**
50      * @param index     Pass a value >= 0 if using multiple texture domains in the same effect.
51      *                  It is used to keep inserted variables from causing name collisions.
52      */
53     GrTextureDomain(const SkRect& domain, Mode, int index = -1);
54 
domain()55     const SkRect& domain() const { return fDomain; }
mode()56     Mode mode() const { return fMode; }
57 
58     /* Computes a domain that bounds all the texels in texelRect. Note that with bilerp enabled
59        texels neighboring the domain may be read. */
MakeTexelDomain(const GrTexture * texture,const SkIRect & texelRect)60     static const SkRect MakeTexelDomain(const GrTexture* texture, const SkIRect& texelRect) {
61         SkScalar wInv = SK_Scalar1 / texture->width();
62         SkScalar hInv = SK_Scalar1 / texture->height();
63         SkRect result = {
64             texelRect.fLeft * wInv,
65             texelRect.fTop * hInv,
66             texelRect.fRight * wInv,
67             texelRect.fBottom * hInv
68         };
69         return result;
70     }
71 
MakeTexelDomainForMode(const GrTexture * texture,const SkIRect & texelRect,Mode mode)72     static const SkRect MakeTexelDomainForMode(const GrTexture* texture, const SkIRect& texelRect, Mode mode) {
73         // For Clamp mode, inset by half a texel.
74         SkScalar wInv = SK_Scalar1 / texture->width();
75         SkScalar hInv = SK_Scalar1 / texture->height();
76         SkScalar inset = (mode == kClamp_Mode && !texelRect.isEmpty()) ? SK_ScalarHalf : 0;
77         return SkRect::MakeLTRB(
78             (texelRect.fLeft + inset) * wInv,
79             (texelRect.fTop + inset) * hInv,
80             (texelRect.fRight - inset) * wInv,
81             (texelRect.fBottom - inset) * hInv
82         );
83     }
84 
85     bool operator== (const GrTextureDomain& that) const {
86         return fMode == that.fMode && (kIgnore_Mode == fMode || fDomain == that.fDomain);
87     }
88 
89     /**
90      * A GrGLProcessor subclass that corresponds to a GrProcessor subclass that uses GrTextureDomain
91      * should include this helper. It generates the texture domain GLSL, produces the part of the
92      * effect key that reflects the texture domain code, and performs the uniform uploads necessary
93      * for texture domains.
94      */
95     class GLDomain {
96     public:
GLDomain()97         GLDomain() {
98             fPrevDomain[0] = SK_FloatNaN;
99             SkDEBUGCODE(fMode = (Mode) -1;)
100         }
101 
102         /**
103          * Call this from GrGLProcessor::emitCode() to sample the texture W.R.T. the domain and
104          * mode.
105          *
106          * @param outcolor  name of vec4 variable to hold the sampled color.
107          * @param inCoords  name of vec2 variable containing the coords to be used with the domain.
108          *                  It is assumed that this is a variable and not an expression.
109          * @param inModulateColor   if non-NULL the sampled color will be modulated with this
110          *                          expression before being written to outColor.
111          */
112         void sampleTexture(GrGLShaderBuilder* builder,
113                            const GrTextureDomain& textureDomain,
114                            const char* outColor,
115                            const SkString& inCoords,
116                            const GrGLProcessor::TextureSampler sampler,
117                            const char* inModulateColor = NULL);
118 
119         /**
120          * Call this from GrGLProcessor::setData() to upload uniforms necessary for the texture
121          * domain. The rectangle is automatically adjusted to account for the texture's origin.
122          */
123         void setData(const GrGLProgramDataManager& pdman, const GrTextureDomain& textureDomain,
124                      GrSurfaceOrigin textureOrigin);
125 
126         enum {
127             kDomainKeyBits = 2, // See DomainKey().
128         };
129 
130         /**
131          * GrGLProcessor::GenKey() must call this and include the returned value in it's computed
132          * key. The returned will be limited to the lower kDomainKeyBits bits.
133          */
DomainKey(const GrTextureDomain & domain)134         static uint32_t DomainKey(const GrTextureDomain& domain) {
135             GR_STATIC_ASSERT(kModeCount <= 4);
136             return domain.mode();
137         }
138 
139     private:
140         SkDEBUGCODE(Mode                      fMode;)
141         GrGLProgramDataManager::UniformHandle fDomainUni;
142         SkString                              fDomainName;
143         GrGLfloat                             fPrevDomain[4];
144     };
145 
146 protected:
147     Mode    fMode;
148     SkRect  fDomain;
149     int     fIndex;
150 
151     typedef GrSingleTextureEffect INHERITED;
152 };
153 
154 /**
155  * A basic texture effect that uses GrTextureDomain.
156  */
157 class GrTextureDomainEffect : public GrSingleTextureEffect {
158 
159 public:
160     static GrFragmentProcessor* Create(GrTexture*,
161                                        const SkMatrix&,
162                                        const SkRect& domain,
163                                        GrTextureDomain::Mode,
164                                        GrTextureParams::FilterMode filterMode,
165                                        GrCoordSet = kLocal_GrCoordSet);
166 
167     virtual ~GrTextureDomainEffect();
168 
name()169     const char* name() const override { return "TextureDomain"; }
170 
171     void getGLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override;
172 
173     GrGLFragmentProcessor* createGLInstance() const override;
174 
textureDomain()175     const GrTextureDomain& textureDomain() const { return fTextureDomain; }
176 
177 protected:
178     GrTextureDomain fTextureDomain;
179 
180 private:
181     GrTextureDomainEffect(GrTexture*,
182                           const SkMatrix&,
183                           const SkRect& domain,
184                           GrTextureDomain::Mode,
185                           GrTextureParams::FilterMode,
186                           GrCoordSet);
187 
188     bool onIsEqual(const GrFragmentProcessor&) const override;
189 
190     void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
191 
192     GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
193 
194     typedef GrSingleTextureEffect INHERITED;
195 };
196 
197 #endif
198