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 GrGLSLColorSpaceXformHelper;
17class GrGLSLShaderBuilder;
18class GrInvariantOutput;
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 GrTextureDomain gDomain((GrTextureProxy*)nullptr,
48                                             SkRect::MakeEmpty(), 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, 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 SkIRect& texelRect) {
64        return SkRect::Make(texelRect);
65    }
66
67    static const SkRect MakeTexelDomainForMode(const SkIRect& texelRect, Mode mode) {
68        // For Clamp mode, inset by half a texel.
69        SkScalar inset = (mode == kClamp_Mode && !texelRect.isEmpty()) ? SK_ScalarHalf : 0;
70        return SkRect::MakeLTRB(texelRect.fLeft + inset, texelRect.fTop + inset,
71                                texelRect.fRight - inset, texelRect.fBottom - inset);
72    }
73
74    bool operator==(const GrTextureDomain& that) const {
75        return fMode == that.fMode && (kIgnore_Mode == fMode || fDomain == that.fDomain);
76    }
77
78    /**
79     * A GrGLSLFragmentProcessor subclass that corresponds to a GrProcessor subclass that uses
80     * GrTextureDomain should include this helper. It generates the texture domain GLSL, produces
81     * the part of the effect key that reflects the texture domain code, and performs the uniform
82     * uploads necessary for texture domains.
83     */
84    class GLDomain {
85    public:
86        GLDomain() {
87            for (int i = 0; i < kPrevDomainCount; i++) {
88                fPrevDomain[i] = SK_FloatNaN;
89            }
90            SkDEBUGCODE(fMode = (Mode) -1;)
91        }
92
93        /**
94         * Call this from GrGLSLFragmentProcessor::emitCode() to sample the texture W.R.T. the
95         * domain and mode.
96         *
97         * @param outcolor  name of vec4 variable to hold the sampled color.
98         * @param inCoords  name of vec2 variable containing the coords to be used with the domain.
99         *                  It is assumed that this is a variable and not an expression.
100         * @param inModulateColor   if non-nullptr the sampled color will be modulated with this
101         *                          expression before being written to outColor.
102         */
103        void sampleTexture(GrGLSLShaderBuilder* builder,
104                           GrGLSLUniformHandler* uniformHandler,
105                           const GrShaderCaps* shaderCaps,
106                           const GrTextureDomain& textureDomain,
107                           const char* outColor,
108                           const SkString& inCoords,
109                           GrGLSLFragmentProcessor::SamplerHandle sampler,
110                           const char* inModulateColor = nullptr,
111                           GrGLSLColorSpaceXformHelper* colorXformHelper = nullptr);
112
113        /**
114         * Call this from GrGLSLFragmentProcessor::setData() to upload uniforms necessary for the
115         * texture domain. The rectangle is automatically adjusted to account for the texture's
116         * origin.
117         */
118        void setData(const GrGLSLProgramDataManager& pdman, const GrTextureDomain& textureDomain,
119                     GrTexture* texure);
120
121        enum {
122            kDomainKeyBits = 2, // See DomainKey().
123        };
124
125        /**
126         * GrGLSLFragmentProcessor::GenKey() must call this and include the returned value in it's
127         * computed key. The returned will be limited to the lower kDomainKeyBits bits.
128         */
129        static uint32_t DomainKey(const GrTextureDomain& domain) {
130            GR_STATIC_ASSERT(kModeCount <= (1 << kDomainKeyBits));
131            return domain.mode();
132        }
133
134    private:
135        static const int kPrevDomainCount = 4;
136        SkDEBUGCODE(Mode                        fMode;)
137        GrGLSLProgramDataManager::UniformHandle fDomainUni;
138        SkString                                fDomainName;
139        float                                   fPrevDomain[kPrevDomainCount];
140    };
141
142protected:
143    Mode    fMode;
144    SkRect  fDomain;
145    int     fIndex;
146};
147
148/**
149 * A basic texture effect that uses GrTextureDomain.
150 */
151class GrTextureDomainEffect : public GrSingleTextureEffect {
152
153public:
154    static sk_sp<GrFragmentProcessor> Make(sk_sp<GrTextureProxy>,
155                                           sk_sp<GrColorSpaceXform>,
156                                           const SkMatrix&,
157                                           const SkRect& domain,
158                                           GrTextureDomain::Mode,
159                                           GrSamplerParams::FilterMode filterMode);
160
161    const char* name() const override { return "TextureDomain"; }
162
163    SkString dumpInfo() const override {
164        SkString str;
165        str.appendf("Domain: [L: %.2f, T: %.2f, R: %.2f, B: %.2f]",
166                    fTextureDomain.domain().fLeft, fTextureDomain.domain().fTop,
167                    fTextureDomain.domain().fRight, fTextureDomain.domain().fBottom);
168        str.append(INHERITED::dumpInfo());
169        return str;
170    }
171
172private:
173    GrTextureDomain fTextureDomain;
174
175    GrTextureDomainEffect(sk_sp<GrTextureProxy>,
176                          sk_sp<GrColorSpaceXform>,
177                          const SkMatrix&,
178                          const SkRect& domain,
179                          GrTextureDomain::Mode,
180                          GrSamplerParams::FilterMode);
181
182    static OptimizationFlags OptFlags(GrPixelConfig config, GrTextureDomain::Mode mode);
183
184    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
185
186    void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
187
188    bool onIsEqual(const GrFragmentProcessor&) const override;
189
190    GR_DECLARE_FRAGMENT_PROCESSOR_TEST
191
192    typedef GrSingleTextureEffect INHERITED;
193};
194
195class GrDeviceSpaceTextureDecalFragmentProcessor : public GrFragmentProcessor {
196public:
197    static sk_sp<GrFragmentProcessor> Make(sk_sp<GrTextureProxy>,
198                                           const SkIRect& subset,
199                                           const SkIPoint& deviceSpaceOffset);
200
201    const char* name() const override { return "GrDeviceSpaceTextureDecalFragmentProcessor"; }
202
203    SkString dumpInfo() const override {
204        SkString str;
205        str.appendf("Domain: [L: %.2f, T: %.2f, R: %.2f, B: %.2f] Offset: [%d %d]",
206                    fTextureDomain.domain().fLeft, fTextureDomain.domain().fTop,
207                    fTextureDomain.domain().fRight, fTextureDomain.domain().fBottom,
208                    fDeviceSpaceOffset.fX, fDeviceSpaceOffset.fY);
209        str.append(INHERITED::dumpInfo());
210        return str;
211    }
212
213private:
214    TextureSampler fTextureSampler;
215    GrTextureDomain fTextureDomain;
216    SkIPoint fDeviceSpaceOffset;
217
218    GrDeviceSpaceTextureDecalFragmentProcessor(sk_sp<GrTextureProxy>,
219                                               const SkIRect&, const SkIPoint&);
220
221    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
222
223    // Since we always use decal mode, there is no need for key data.
224    void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
225
226    bool onIsEqual(const GrFragmentProcessor& fp) const override;
227
228    GR_DECLARE_FRAGMENT_PROCESSOR_TEST
229
230    typedef GrFragmentProcessor INHERITED;
231};
232#endif
233