1 /*
2  * Copyright 2017 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 GrTextureEffect_DEFINED
9 #define GrTextureEffect_DEFINED
10 
11 #include "include/core/SkImageInfo.h"
12 #include "include/core/SkMatrix.h"
13 #include "src/gpu/GrFragmentProcessor.h"
14 #include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
15 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
16 
17 class GrTextureEffect : public GrFragmentProcessor {
18 public:
19     static constexpr float kDefaultBorder[4] = {0};
20 
21     /** Make from a filter. The sampler will be configured with clamp mode. */
22     static std::unique_ptr<GrFragmentProcessor> Make(
23             GrSurfaceProxyView,
24             SkAlphaType,
25             const SkMatrix& = SkMatrix::I(),
26             GrSamplerState::Filter = GrSamplerState::Filter::kNearest,
27             GrSamplerState::MipmapMode mipmapMode = GrSamplerState::MipmapMode::kNone);
28 
29     /**
30      * Make from a full GrSamplerState. Caps are required to determine support for kClampToBorder.
31      * This will be emulated in the shader if there is no hardware support.
32      */
33     static std::unique_ptr<GrFragmentProcessor> Make(GrSurfaceProxyView, SkAlphaType,
34                                                      const SkMatrix&, GrSamplerState,
35                                                      const GrCaps& caps,
36                                                      const float border[4] = kDefaultBorder);
37 
38     /**
39      * Makes a texture effect that samples a subset of a texture. The wrap modes of the
40      * GrSampleState are applied to the subset in the shader rather than using HW samplers.
41      * The 'subset' parameter specifies the texels in the base level. The shader code will
42      * avoid allowing linear filtering to read outside the texel window. However, if MIP
43      * filtering is used and a shader invocation reads from a level other than the base
44      * then it may read texel values that were computed from in part from base level texels
45      * outside the window. More specifically, we treat the MIP map case exactly like the
46      * linear case in terms of how the final texture coords are computed. If
47      * alwaysUseShaderTileMode is true then MakeSubset won't attempt to use HW wrap modes if the
48      * subset contains the entire texture.
49      */
50     static std::unique_ptr<GrFragmentProcessor> MakeSubset(GrSurfaceProxyView,
51                                                            SkAlphaType,
52                                                            const SkMatrix&,
53                                                            GrSamplerState,
54                                                            const SkRect& subset,
55                                                            const GrCaps& caps,
56                                                            const float border[4] = kDefaultBorder,
57                                                            bool alwaysUseShaderTileMode = false);
58 
59     /**
60      * The same as above but also takes a 'domain' that specifies any known limit on the post-
61      * matrix texture coords that will be used to sample the texture. Specifying this requires
62      * knowledge of how this effect will be nested into a paint, the local coords used with the
63      * draw, etc. It is only used to attempt to optimize away the shader subset calculations.
64      */
65     static std::unique_ptr<GrFragmentProcessor> MakeSubset(GrSurfaceProxyView,
66                                                            SkAlphaType,
67                                                            const SkMatrix&,
68                                                            GrSamplerState,
69                                                            const SkRect& subset,
70                                                            const SkRect& domain,
71                                                            const GrCaps& caps,
72                                                            const float border[4] = kDefaultBorder);
73 
74     /**
75      * Like MakeSubset() but always uses kLinear filtering. MakeSubset() uses the subset rect
76      * dimensions to determine the period of the wrap mode (for repeat and mirror). Once it computes
77      * the wrapped texture coordinate inside subset rect it further clamps it to a 0.5 inset rect of
78      * subset. When subset is an integer rectangle this clamping avoids the hw linear filtering from
79      * reading texels just outside the subset rect. This factory allows a custom inset clamping
80      * distance rather than 0.5, allowing those neighboring texels to influence the linear filtering
81      * sample result. If there is a known restriction on the post-matrix texture coords it can be
82      * specified using domain.
83      */
84     static std::unique_ptr<GrFragmentProcessor> MakeCustomLinearFilterInset(
85             GrSurfaceProxyView,
86             SkAlphaType,
87             const SkMatrix&,
88             GrSamplerState::WrapMode wx,
89             GrSamplerState::WrapMode wy,
90             const SkRect& subset,
91             const SkRect* domain,
92             SkVector inset,
93             const GrCaps& caps,
94             const float border[4] = kDefaultBorder);
95 
96     std::unique_ptr<GrFragmentProcessor> clone() const override;
97 
name()98     const char* name() const override { return "TextureEffect"; }
99 
samplerState()100     GrSamplerState samplerState() const { return fSamplerState; }
101 
texture()102     GrTexture* texture() const { return fView.asTextureProxy()->peekTexture(); }
103 
view()104     const GrSurfaceProxyView& view() const { return fView; }
105 
106     // Gets a matrix that is concat'ed by wrapping GrMatrixEffect that handles y-flip and coord
107     // normalization if required. This matrix is not always known when we make the GrTextureEffect
108     // because of fully-lazy proxies. Hence, this method  exists to allow this concat to happen
109     // after proxy instantiation with coordination from GrMatrixEffect.
110     SkMatrix coordAdjustmentMatrix() const;
111 
112     class Impl : public GrGLSLFragmentProcessor {
113     public:
114         void emitCode(EmitArgs&) override;
115         void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
116 
setSamplerHandle(GrGLSLShaderBuilder::SamplerHandle handle)117         void setSamplerHandle(GrGLSLShaderBuilder::SamplerHandle handle) {
118             fSamplerHandle = handle;
119         }
120 
121     private:
122         UniformHandle fSubsetUni;
123         UniformHandle fClampUni;
124         UniformHandle fIDimsUni;
125         UniformHandle fBorderUni;
126         GrGLSLShaderBuilder::SamplerHandle fSamplerHandle;
127     };
128 
129 private:
130     struct Sampling;
131 
132     /**
133      * Possible implementation of wrap mode in shader code. Some modes are specialized by
134      * filter.
135      */
136     enum class ShaderMode : uint16_t {
137         kNone,                   // Using HW mode
138         kClamp,                  // Shader based clamp, no filter specialization
139         kRepeat_Nearest_None,    // Simple repeat for nearest sampling, no mipmapping
140         kRepeat_Linear_None,     // Filter the subset boundary for kRepeat mode, no mip mapping
141         kRepeat_Linear_Mipmap,   // Logic for linear filtering and LOD selection with kRepeat mode.
142         kRepeat_Nearest_Mipmap,  // Logic for nearest filtering and LOD selection with kRepeat mode.
143         kMirrorRepeat,           // Mirror repeat (doesn't depend on filter))
144         kClampToBorder_Nearest,  // Logic for hard transition to border color when not filtering.
145         kClampToBorder_Filter,   // Logic for fading to border color when filtering.
146     };
147     static ShaderMode GetShaderMode(GrSamplerState::WrapMode,
148                                     GrSamplerState::Filter,
149                                     GrSamplerState::MipmapMode);
150     static bool ShaderModeIsClampToBorder(ShaderMode);
151     // To keep things a little simpler, when we have filtering logic in the shader we
152     // operate on unnormalized texture coordinates. We will add a uniform that stores
153     // {1/w, 1/h} in a float2 and normalizes after the mode is handled if the texture
154     // is not rectangle.
155     static bool ShaderModeRequiresUnormCoord(ShaderMode);
156 
157     GrSurfaceProxyView fView;
158     GrSamplerState fSamplerState;
159     float fBorder[4];
160     SkRect fSubset;
161     SkRect fClamp;
162     ShaderMode fShaderModes[2];
163 
164     inline GrTextureEffect(GrSurfaceProxyView, SkAlphaType, const Sampling&);
165 
166     explicit GrTextureEffect(const GrTextureEffect& src);
167 
168     std::unique_ptr<GrGLSLFragmentProcessor> onMakeProgramImpl() const override;
169 
170     void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
171 
172     bool onIsEqual(const GrFragmentProcessor&) const override;
173 
174     bool matrixEffectShouldNormalize() const;
175 
hasClampToBorderShaderMode()176     bool hasClampToBorderShaderMode() const {
177         return ShaderModeIsClampToBorder(fShaderModes[0]) ||
178                ShaderModeIsClampToBorder(fShaderModes[1]);
179     }
180 
181     GR_DECLARE_FRAGMENT_PROCESSOR_TEST
182 
183     using INHERITED = GrFragmentProcessor;
184 };
185 #endif
186