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 "src/gpu/GrCoordTransform.h"
12 #include "src/gpu/GrFragmentProcessor.h"
13 #include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
14 #include "src/gpu/glsl/GrGLSLProgramDataManager.h"
15 
16 class GrGLProgramBuilder;
17 class GrGLSLShaderBuilder;
18 class GrInvariantOutput;
19 class GrGLSLUniformHandler;
20 struct 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  */
28 class GrTextureDomain {
29 public:
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 
IgnoredDomain()46     static const GrTextureDomain& IgnoredDomain() {
47         static const GrTextureDomain gDomain((GrTextureProxy*)nullptr,
48                                              SkRect::MakeEmpty(), kIgnore_Mode, 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 modeX, Mode modeY, int index = -1);
57 
58     GrTextureDomain(const GrTextureDomain&) = default;
59 
domain()60     const SkRect& domain() const { return fDomain; }
modeX()61     Mode modeX() const { return fModeX; }
modeY()62     Mode modeY() const { return fModeY; }
63 
64     /*
65      * Computes a domain that bounds all the texels in texelRect, possibly insetting by half a pixel
66      * depending on the mode. The mode is used for both axes.
67      */
MakeTexelDomain(const SkIRect & texelRect,Mode mode)68     static const SkRect MakeTexelDomain(const SkIRect& texelRect, Mode mode) {
69         return MakeTexelDomain(texelRect, mode, mode);
70     }
71 
MakeTexelDomain(const SkIRect & texelRect,Mode modeX,Mode modeY)72     static const SkRect MakeTexelDomain(const SkIRect& texelRect, Mode modeX, Mode modeY) {
73         // For Clamp and decal modes, inset by half a texel
74         SkScalar insetX = ((modeX == kClamp_Mode || modeX == kDecal_Mode) && texelRect.width() > 0)
75                 ? SK_ScalarHalf : 0;
76         SkScalar insetY = ((modeY == kClamp_Mode || modeY == kDecal_Mode) && texelRect.height() > 0)
77                 ? SK_ScalarHalf : 0;
78         return SkRect::MakeLTRB(texelRect.fLeft + insetX, texelRect.fTop + insetY,
79                                 texelRect.fRight - insetX, texelRect.fBottom - insetY);
80     }
81 
82     // Convenience to determine if any axis of a texture uses an explicit decal mode or the hardware
83     // clamp to border decal mode.
IsDecalSampled(GrSamplerState::WrapMode wrapX,GrSamplerState::WrapMode wrapY,Mode modeX,Mode modeY)84     static bool IsDecalSampled(GrSamplerState::WrapMode wrapX, GrSamplerState::WrapMode wrapY,
85                                Mode modeX, Mode modeY) {
86         return wrapX == GrSamplerState::WrapMode::kClampToBorder ||
87                wrapY == GrSamplerState::WrapMode::kClampToBorder ||
88                modeX == kDecal_Mode ||
89                modeY == kDecal_Mode;
90     }
91 
IsDecalSampled(const GrSamplerState::WrapMode wraps[2],Mode modeX,Mode modeY)92     static bool IsDecalSampled(const GrSamplerState::WrapMode wraps[2], Mode modeX, Mode modeY) {
93         return IsDecalSampled(wraps[0], wraps[1], modeX, modeY);
94     }
95 
IsDecalSampled(const GrSamplerState & sampler,Mode modeX,Mode modeY)96     static bool IsDecalSampled(const GrSamplerState& sampler, Mode modeX, Mode modeY) {
97         return IsDecalSampled(sampler.wrapModeX(), sampler.wrapModeY(), modeX, modeY);
98     }
99 
100     bool operator==(const GrTextureDomain& that) const {
101         return fModeX == that.fModeX && fModeY == that.fModeY &&
102                (kIgnore_Mode == fModeX || (fDomain.fLeft == that.fDomain.fLeft &&
103                                            fDomain.fRight == that.fDomain.fRight)) &&
104                (kIgnore_Mode == fModeY || (fDomain.fTop == that.fDomain.fTop &&
105                                            fDomain.fBottom == that.fDomain.fBottom));
106     }
107 
108     /**
109      * A GrGLSLFragmentProcessor subclass that corresponds to a GrProcessor subclass that uses
110      * GrTextureDomain should include this helper. It generates the texture domain GLSL, produces
111      * the part of the effect key that reflects the texture domain code, and performs the uniform
112      * uploads necessary for texture domains.
113      */
114     class GLDomain {
115     public:
GLDomain()116         GLDomain() {
117             for (int i = 0; i < kPrevDomainCount; i++) {
118                 fPrevDomain[i] = SK_FloatNaN;
119             }
120         }
121 
122         /**
123          * Call this from GrGLSLFragmentProcessor::emitCode() to sample the texture W.R.T. the
124          * domain and mode.
125          *
126          * @param outcolor  name of half4 variable to hold the sampled color.
127          * @param inCoords  name of float2 variable containing the coords to be used with the domain.
128          *                  It is assumed that this is a variable and not an expression.
129          * @param inModulateColor   if non-nullptr the sampled color will be modulated with this
130          *                          expression before being written to outColor.
131          */
132         void sampleTexture(GrGLSLShaderBuilder* builder,
133                            GrGLSLUniformHandler* uniformHandler,
134                            const GrShaderCaps* shaderCaps,
135                            const GrTextureDomain& textureDomain,
136                            const char* outColor,
137                            const SkString& inCoords,
138                            GrGLSLFragmentProcessor::SamplerHandle sampler,
139                            const char* inModulateColor = nullptr);
140 
141         /**
142          * Call this from GrGLSLFragmentProcessor::setData() to upload uniforms necessary for the
143          * texture domain. The rectangle is automatically adjusted to account for the texture's
144          * origin.
145          */
146         void setData(const GrGLSLProgramDataManager&, const GrTextureDomain&, GrTextureProxy*,
147                      const GrSamplerState& sampler);
148 
149         enum {
150             kModeBits = 2, // See DomainKey().
151             kDomainKeyBits = 4
152         };
153 
154         /**
155          * GrGLSLFragmentProcessor::GenKey() must call this and include the returned value in it's
156          * computed key. The returned will be limited to the lower kDomainKeyBits bits.
157          */
DomainKey(const GrTextureDomain & domain)158         static uint32_t DomainKey(const GrTextureDomain& domain) {
159             GR_STATIC_ASSERT(kModeCount <= (1 << kModeBits));
160             return domain.modeX() | (domain.modeY() << kModeBits);
161         }
162 
163     private:
164         static const int kPrevDomainCount = 4;
165         SkDEBUGCODE(Mode                        fModeX;)
166         SkDEBUGCODE(Mode                        fModeY;)
167         SkDEBUGCODE(bool                        fHasMode = false;)
168         GrGLSLProgramDataManager::UniformHandle fDomainUni;
169         SkString                                fDomainName;
170 
171         // Only initialized if the domain has at least one decal axis
172         GrGLSLProgramDataManager::UniformHandle fDecalUni;
173         SkString                                fDecalName;
174 
175         float                                   fPrevDomain[kPrevDomainCount];
176     };
177 
178 protected:
179     Mode    fModeX;
180     Mode    fModeY;
181     SkRect  fDomain;
182     int     fIndex;
183 };
184 
185 /**
186  * A basic texture effect that uses GrTextureDomain.
187  */
188 class GrTextureDomainEffect : public GrFragmentProcessor {
189 public:
190     static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy>,
191                                                      GrColorType srcColorType,
192                                                      const SkMatrix&,
193                                                      const SkRect& domain,
194                                                      GrTextureDomain::Mode mode,
195                                                      GrSamplerState::Filter filterMode);
196 
197     static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy>,
198                                                      GrColorType srcColorType,
199                                                      const SkMatrix&,
200                                                      const SkRect& domain,
201                                                      GrTextureDomain::Mode modeX,
202                                                      GrTextureDomain::Mode modeY,
203                                                      const GrSamplerState& sampler);
204 
name()205     const char* name() const override { return "TextureDomain"; }
206 
clone()207     std::unique_ptr<GrFragmentProcessor> clone() const override {
208         return std::unique_ptr<GrFragmentProcessor>(new GrTextureDomainEffect(*this));
209     }
210 
211 #ifdef SK_DEBUG
dumpInfo()212     SkString dumpInfo() const override {
213         SkString str;
214         str.appendf("Domain: [L: %.2f, T: %.2f, R: %.2f, B: %.2f]",
215                     fTextureDomain.domain().fLeft, fTextureDomain.domain().fTop,
216                     fTextureDomain.domain().fRight, fTextureDomain.domain().fBottom);
217         str.append(INHERITED::dumpInfo());
218         return str;
219     }
220 #endif
221 
222 private:
223     GrCoordTransform fCoordTransform;
224     GrTextureDomain fTextureDomain;
225     TextureSampler fTextureSampler;
226 
227     GrTextureDomainEffect(sk_sp<GrTextureProxy>,
228                           GrColorType srcColorType,
229                           const SkMatrix&,
230                           const SkRect& domain,
231                           GrTextureDomain::Mode modeX,
232                           GrTextureDomain::Mode modeY,
233                           const GrSamplerState&);
234 
235     explicit GrTextureDomainEffect(const GrTextureDomainEffect&);
236 
237     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
238 
239     void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
240 
241     bool onIsEqual(const GrFragmentProcessor&) const override;
242 
onTextureSampler(int)243     const TextureSampler& onTextureSampler(int) const override { return fTextureSampler; }
244 
245     GR_DECLARE_FRAGMENT_PROCESSOR_TEST
246 
247     typedef GrFragmentProcessor INHERITED;
248 };
249 
250 class GrDeviceSpaceTextureDecalFragmentProcessor : public GrFragmentProcessor {
251 public:
252     static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy>,
253                                                      const SkIRect& subset,
254                                                      const SkIPoint& deviceSpaceOffset);
255 
name()256     const char* name() const override { return "GrDeviceSpaceTextureDecalFragmentProcessor"; }
257 
258 #ifdef SK_DEBUG
dumpInfo()259     SkString dumpInfo() const override {
260         SkString str;
261         str.appendf("Domain: [L: %.2f, T: %.2f, R: %.2f, B: %.2f] Offset: [%d %d]",
262                     fTextureDomain.domain().fLeft, fTextureDomain.domain().fTop,
263                     fTextureDomain.domain().fRight, fTextureDomain.domain().fBottom,
264                     fDeviceSpaceOffset.fX, fDeviceSpaceOffset.fY);
265         str.append(INHERITED::dumpInfo());
266         return str;
267     }
268 #endif
269 
270     std::unique_ptr<GrFragmentProcessor> clone() const override;
271 
272 private:
273     TextureSampler fTextureSampler;
274     GrTextureDomain fTextureDomain;
275     SkIPoint fDeviceSpaceOffset;
276 
277     GrDeviceSpaceTextureDecalFragmentProcessor(sk_sp<GrTextureProxy>,
278                                                const SkIRect&, const SkIPoint&);
279     GrDeviceSpaceTextureDecalFragmentProcessor(const GrDeviceSpaceTextureDecalFragmentProcessor&);
280 
281     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
282 
283     // Since we always use decal mode, there is no need for key data.
onGetGLSLProcessorKey(const GrShaderCaps &,GrProcessorKeyBuilder *)284     void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
285 
286     bool onIsEqual(const GrFragmentProcessor& fp) const override;
287 
onTextureSampler(int)288     const TextureSampler& onTextureSampler(int) const override { return fTextureSampler; }
289 
290     GR_DECLARE_FRAGMENT_PROCESSOR_TEST
291 
292     typedef GrFragmentProcessor INHERITED;
293 };
294 #endif
295