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