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 "GrCoordTransform.h"
12#include "GrFragmentProcessor.h"
13#include "glsl/GrGLSLFragmentProcessor.h"
14#include "glsl/GrGLSLProgramDataManager.h"
15
16class GrGLProgramBuilder;
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 half4 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    GrTextureDomain(const GrTextureDomain&) = default;
59
60    const SkRect& domain() const { return fDomain; }
61    Mode mode() const { return fMode; }
62
63    /* Computes a domain that bounds all the texels in texelRect. Note that with bilerp enabled
64       texels neighboring the domain may be read. */
65    static const SkRect MakeTexelDomain(const SkIRect& texelRect) {
66        return SkRect::Make(texelRect);
67    }
68
69    static const SkRect MakeTexelDomainForMode(const SkIRect& texelRect, Mode mode) {
70        // For Clamp mode, inset by half a texel.
71        SkScalar inset = (mode == kClamp_Mode && !texelRect.isEmpty()) ? SK_ScalarHalf : 0;
72        return SkRect::MakeLTRB(texelRect.fLeft + inset, texelRect.fTop + inset,
73                                texelRect.fRight - inset, texelRect.fBottom - inset);
74    }
75
76    bool operator==(const GrTextureDomain& that) const {
77        return fMode == that.fMode && (kIgnore_Mode == fMode || fDomain == that.fDomain);
78    }
79
80    /**
81     * A GrGLSLFragmentProcessor subclass that corresponds to a GrProcessor subclass that uses
82     * GrTextureDomain should include this helper. It generates the texture domain GLSL, produces
83     * the part of the effect key that reflects the texture domain code, and performs the uniform
84     * uploads necessary for texture domains.
85     */
86    class GLDomain {
87    public:
88        GLDomain() {
89            for (int i = 0; i < kPrevDomainCount; i++) {
90                fPrevDomain[i] = SK_FloatNaN;
91            }
92        }
93
94        /**
95         * Call this from GrGLSLFragmentProcessor::emitCode() to sample the texture W.R.T. the
96         * domain and mode.
97         *
98         * @param outcolor  name of half4 variable to hold the sampled color.
99         * @param inCoords  name of float2 variable containing the coords to be used with the domain.
100         *                  It is assumed that this is a variable and not an expression.
101         * @param inModulateColor   if non-nullptr the sampled color will be modulated with this
102         *                          expression before being written to outColor.
103         */
104        void sampleTexture(GrGLSLShaderBuilder* builder,
105                           GrGLSLUniformHandler* uniformHandler,
106                           const GrShaderCaps* shaderCaps,
107                           const GrTextureDomain& textureDomain,
108                           const char* outColor,
109                           const SkString& inCoords,
110                           GrGLSLFragmentProcessor::SamplerHandle sampler,
111                           const char* inModulateColor = 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&, const GrTextureDomain&, GrSurfaceProxy*);
119
120        enum {
121            kDomainKeyBits = 2, // See DomainKey().
122        };
123
124        /**
125         * GrGLSLFragmentProcessor::GenKey() must call this and include the returned value in it's
126         * computed key. The returned will be limited to the lower kDomainKeyBits bits.
127         */
128        static uint32_t DomainKey(const GrTextureDomain& domain) {
129            GR_STATIC_ASSERT(kModeCount <= (1 << kDomainKeyBits));
130            return domain.mode();
131        }
132
133    private:
134        static const int kPrevDomainCount = 4;
135        SkDEBUGCODE(Mode                        fMode;)
136        SkDEBUGCODE(bool                        fHasMode = false;)
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 GrFragmentProcessor {
152public:
153    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy>,
154                                                     const SkMatrix&,
155                                                     const SkRect& domain,
156                                                     GrTextureDomain::Mode,
157                                                     GrSamplerState::Filter filterMode);
158
159    const char* name() const override { return "TextureDomain"; }
160
161    std::unique_ptr<GrFragmentProcessor> clone() const override {
162        return std::unique_ptr<GrFragmentProcessor>(new GrTextureDomainEffect(*this));
163    }
164
165    SkString dumpInfo() const override {
166        SkString str;
167        str.appendf("Domain: [L: %.2f, T: %.2f, R: %.2f, B: %.2f]",
168                    fTextureDomain.domain().fLeft, fTextureDomain.domain().fTop,
169                    fTextureDomain.domain().fRight, fTextureDomain.domain().fBottom);
170        str.append(INHERITED::dumpInfo());
171        return str;
172    }
173
174private:
175    GrCoordTransform fCoordTransform;
176    GrTextureDomain fTextureDomain;
177    TextureSampler fTextureSampler;
178
179    GrTextureDomainEffect(sk_sp<GrTextureProxy>,
180                          const SkMatrix&,
181                          const SkRect& domain,
182                          GrTextureDomain::Mode,
183                          GrSamplerState::Filter);
184
185    explicit GrTextureDomainEffect(const GrTextureDomainEffect&);
186
187    static OptimizationFlags OptFlags(GrPixelConfig config, GrTextureDomain::Mode mode);
188
189    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
190
191    void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
192
193    bool onIsEqual(const GrFragmentProcessor&) const override;
194
195    GR_DECLARE_FRAGMENT_PROCESSOR_TEST
196
197    typedef GrFragmentProcessor INHERITED;
198};
199
200class GrDeviceSpaceTextureDecalFragmentProcessor : public GrFragmentProcessor {
201public:
202    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy>,
203                                                     const SkIRect& subset,
204                                                     const SkIPoint& deviceSpaceOffset);
205
206    const char* name() const override { return "GrDeviceSpaceTextureDecalFragmentProcessor"; }
207
208    SkString dumpInfo() const override {
209        SkString str;
210        str.appendf("Domain: [L: %.2f, T: %.2f, R: %.2f, B: %.2f] Offset: [%d %d]",
211                    fTextureDomain.domain().fLeft, fTextureDomain.domain().fTop,
212                    fTextureDomain.domain().fRight, fTextureDomain.domain().fBottom,
213                    fDeviceSpaceOffset.fX, fDeviceSpaceOffset.fY);
214        str.append(INHERITED::dumpInfo());
215        return str;
216    }
217
218    std::unique_ptr<GrFragmentProcessor> clone() const override;
219
220private:
221    TextureSampler fTextureSampler;
222    GrTextureDomain fTextureDomain;
223    SkIPoint fDeviceSpaceOffset;
224
225    GrDeviceSpaceTextureDecalFragmentProcessor(sk_sp<GrTextureProxy>,
226                                               const SkIRect&, const SkIPoint&);
227    GrDeviceSpaceTextureDecalFragmentProcessor(const GrDeviceSpaceTextureDecalFragmentProcessor&);
228
229    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
230
231    // Since we always use decal mode, there is no need for key data.
232    void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
233
234    bool onIsEqual(const GrFragmentProcessor& fp) const override;
235
236    GR_DECLARE_FRAGMENT_PROCESSOR_TEST
237
238    typedef GrFragmentProcessor INHERITED;
239};
240#endif
241