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
15class GrGLProgramBuilder;
16class GrGLSLShaderBuilder;
17class GrInvariantOutput;
18class GrGLSLTextureSampler;
19class GrGLSLUniformHandler;
20struct 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 */
28class GrTextureDomain {
29public:
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
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
58    const SkRect& domain() const { return fDomain; }
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. */
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
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:
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         */
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
155protected:
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 */
166class GrTextureDomainEffect : public GrSingleTextureEffect {
167
168public:
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
178    const char* name() const override { return "TextureDomain"; }
179
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
189    const GrTextureDomain& textureDomain() const { return fTextureDomain; }
190
191protected:
192    GrTextureDomain fTextureDomain;
193
194private:
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