• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "GrConvolutionEffect.h"
9 #include "glsl/GrGLSLFragmentProcessor.h"
10 #include "glsl/GrGLSLFragmentShaderBuilder.h"
11 #include "glsl/GrGLSLProgramDataManager.h"
12 #include "glsl/GrGLSLUniformHandler.h"
13 
14 // For brevity
15 typedef GrGLSLProgramDataManager::UniformHandle UniformHandle;
16 
17 class GrGLConvolutionEffect : public GrGLSLFragmentProcessor {
18 public:
19     void emitCode(EmitArgs&) override;
20 
21     static inline void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder*);
22 
23 protected:
24     void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor&) override;
25 
26 private:
27     UniformHandle       fKernelUni;
28     UniformHandle       fImageIncrementUni;
29     UniformHandle       fBoundsUni;
30 
31     typedef GrGLSLFragmentProcessor INHERITED;
32 };
33 
emitCode(EmitArgs & args)34 void GrGLConvolutionEffect::emitCode(EmitArgs& args) {
35     const GrConvolutionEffect& ce = args.fFp.cast<GrConvolutionEffect>();
36 
37     GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
38     fImageIncrementUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
39                                                     kVec2f_GrSLType, kDefault_GrSLPrecision,
40                                                     "ImageIncrement");
41     if (ce.useBounds()) {
42         fBoundsUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
43                                                 kVec2f_GrSLType, kDefault_GrSLPrecision,
44                                                 "Bounds");
45     }
46 
47     int width = Gr1DKernelEffect::WidthFromRadius(ce.radius());
48 
49     fKernelUni = uniformHandler->addUniformArray(kFragment_GrShaderFlag,
50                                                  kFloat_GrSLType, kDefault_GrSLPrecision,
51                                                  "Kernel", width);
52 
53     GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
54     SkString coords2D = fragBuilder->ensureFSCoords2D(args.fCoords, 0);
55 
56     fragBuilder->codeAppendf("%s = vec4(0, 0, 0, 0);", args.fOutputColor);
57 
58     const GrGLSLShaderVar& kernel = uniformHandler->getUniformVariable(fKernelUni);
59     const char* imgInc = uniformHandler->getUniformCStr(fImageIncrementUni);
60 
61     fragBuilder->codeAppendf("vec2 coord = %s - %d.0 * %s;", coords2D.c_str(), ce.radius(), imgInc);
62 
63     // Manually unroll loop because some drivers don't; yields 20-30% speedup.
64     for (int i = 0; i < width; i++) {
65         SkString index;
66         SkString kernelIndex;
67         index.appendS32(i);
68         kernel.appendArrayAccess(index.c_str(), &kernelIndex);
69 
70         if (ce.useBounds()) {
71             // We used to compute a bool indicating whether we're in bounds or not, cast it to a
72             // float, and then mul weight*texture_sample by the float. However, the Adreno 430 seems
73             // to have a bug that caused corruption.
74             const char* bounds = uniformHandler->getUniformCStr(fBoundsUni);
75             const char* component = ce.direction() == Gr1DKernelEffect::kY_Direction ? "y" : "x";
76             fragBuilder->codeAppendf("if (coord.%s >= %s.x && coord.%s <= %s.y) {",
77                                      component, bounds, component, bounds);
78         }
79         fragBuilder->codeAppendf("\t\t%s += ", args.fOutputColor);
80         fragBuilder->appendTextureLookup(args.fSamplers[0], "coord");
81         fragBuilder->codeAppendf(" * %s;\n", kernelIndex.c_str());
82         if (ce.useBounds()) {
83             fragBuilder->codeAppend("}");
84         }
85         fragBuilder->codeAppendf("\t\tcoord += %s;\n", imgInc);
86     }
87 
88     SkString modulate;
89     GrGLSLMulVarBy4f(&modulate, args.fOutputColor, args.fInputColor);
90     fragBuilder->codeAppend(modulate.c_str());
91 }
92 
onSetData(const GrGLSLProgramDataManager & pdman,const GrProcessor & processor)93 void GrGLConvolutionEffect::onSetData(const GrGLSLProgramDataManager& pdman,
94                                       const GrProcessor& processor) {
95     const GrConvolutionEffect& conv = processor.cast<GrConvolutionEffect>();
96     GrTexture& texture = *conv.texture(0);
97 
98     float imageIncrement[2] = { 0 };
99     float ySign = texture.origin() != kTopLeft_GrSurfaceOrigin ? 1.0f : -1.0f;
100     switch (conv.direction()) {
101         case Gr1DKernelEffect::kX_Direction:
102             imageIncrement[0] = 1.0f / texture.width();
103             break;
104         case Gr1DKernelEffect::kY_Direction:
105             imageIncrement[1] = ySign / texture.height();
106             break;
107         default:
108             SkFAIL("Unknown filter direction.");
109     }
110     pdman.set2fv(fImageIncrementUni, 1, imageIncrement);
111     if (conv.useBounds()) {
112         const float* bounds = conv.bounds();
113         if (Gr1DKernelEffect::kY_Direction == conv.direction() &&
114             texture.origin() != kTopLeft_GrSurfaceOrigin) {
115             pdman.set2f(fBoundsUni, 1.0f - bounds[1], 1.0f - bounds[0]);
116         } else {
117             pdman.set2f(fBoundsUni, bounds[0], bounds[1]);
118         }
119     }
120     int width = Gr1DKernelEffect::WidthFromRadius(conv.radius());
121 
122     pdman.set1fv(fKernelUni, width, conv.kernel());
123 }
124 
GenKey(const GrProcessor & processor,const GrGLSLCaps &,GrProcessorKeyBuilder * b)125 void GrGLConvolutionEffect::GenKey(const GrProcessor& processor, const GrGLSLCaps&,
126                                    GrProcessorKeyBuilder* b) {
127     const GrConvolutionEffect& conv = processor.cast<GrConvolutionEffect>();
128     uint32_t key = conv.radius();
129     key <<= 2;
130     if (conv.useBounds()) {
131         key |= 0x2;
132         key |= GrConvolutionEffect::kY_Direction == conv.direction() ? 0x1 : 0x0;
133     }
134     b->add32(key);
135 }
136 
137 ///////////////////////////////////////////////////////////////////////////////
138 
GrConvolutionEffect(GrTexture * texture,Direction direction,int radius,const float * kernel,bool useBounds,float bounds[2])139 GrConvolutionEffect::GrConvolutionEffect(GrTexture* texture,
140                                          Direction direction,
141                                          int radius,
142                                          const float* kernel,
143                                          bool useBounds,
144                                          float bounds[2])
145     : INHERITED(texture, direction, radius), fUseBounds(useBounds) {
146     this->initClassID<GrConvolutionEffect>();
147     SkASSERT(radius <= kMaxKernelRadius);
148     SkASSERT(kernel);
149     int width = this->width();
150     for (int i = 0; i < width; i++) {
151         fKernel[i] = kernel[i];
152     }
153     memcpy(fBounds, bounds, sizeof(fBounds));
154 }
155 
GrConvolutionEffect(GrTexture * texture,Direction direction,int radius,float gaussianSigma,bool useBounds,float bounds[2])156 GrConvolutionEffect::GrConvolutionEffect(GrTexture* texture,
157                                          Direction direction,
158                                          int radius,
159                                          float gaussianSigma,
160                                          bool useBounds,
161                                          float bounds[2])
162     : INHERITED(texture, direction, radius), fUseBounds(useBounds) {
163     this->initClassID<GrConvolutionEffect>();
164     SkASSERT(radius <= kMaxKernelRadius);
165     int width = this->width();
166 
167     float sum = 0.0f;
168     float denom = 1.0f / (2.0f * gaussianSigma * gaussianSigma);
169     for (int i = 0; i < width; ++i) {
170         float x = static_cast<float>(i - this->radius());
171         // Note that the constant term (1/(sqrt(2*pi*sigma^2)) of the Gaussian
172         // is dropped here, since we renormalize the kernel below.
173         fKernel[i] = sk_float_exp(- x * x * denom);
174         sum += fKernel[i];
175     }
176     // Normalize the kernel
177     float scale = 1.0f / sum;
178     for (int i = 0; i < width; ++i) {
179         fKernel[i] *= scale;
180     }
181     memcpy(fBounds, bounds, sizeof(fBounds));
182 }
183 
~GrConvolutionEffect()184 GrConvolutionEffect::~GrConvolutionEffect() {
185 }
186 
onGetGLSLProcessorKey(const GrGLSLCaps & caps,GrProcessorKeyBuilder * b) const187 void GrConvolutionEffect::onGetGLSLProcessorKey(const GrGLSLCaps& caps,
188                                                 GrProcessorKeyBuilder* b) const {
189     GrGLConvolutionEffect::GenKey(*this, caps, b);
190 }
191 
onCreateGLSLInstance() const192 GrGLSLFragmentProcessor* GrConvolutionEffect::onCreateGLSLInstance() const  {
193     return new GrGLConvolutionEffect;
194 }
195 
onIsEqual(const GrFragmentProcessor & sBase) const196 bool GrConvolutionEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
197     const GrConvolutionEffect& s = sBase.cast<GrConvolutionEffect>();
198     return (this->radius() == s.radius() &&
199             this->direction() == s.direction() &&
200             this->useBounds() == s.useBounds() &&
201             0 == memcmp(fBounds, s.fBounds, sizeof(fBounds)) &&
202             0 == memcmp(fKernel, s.fKernel, this->width() * sizeof(float)));
203 }
204 
205 ///////////////////////////////////////////////////////////////////////////////
206 
207 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrConvolutionEffect);
208 
TestCreate(GrProcessorTestData * d)209 const GrFragmentProcessor* GrConvolutionEffect::TestCreate(GrProcessorTestData* d) {
210     int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx :
211                                           GrProcessorUnitTest::kAlphaTextureIdx;
212     Direction dir = d->fRandom->nextBool() ? kX_Direction : kY_Direction;
213     int radius = d->fRandom->nextRangeU(1, kMaxKernelRadius);
214     float kernel[kMaxKernelWidth];
215     for (size_t i = 0; i < SK_ARRAY_COUNT(kernel); ++i) {
216         kernel[i] = d->fRandom->nextSScalar1();
217     }
218     float bounds[2];
219     for (size_t i = 0; i < SK_ARRAY_COUNT(bounds); ++i) {
220         bounds[i] = d->fRandom->nextF();
221     }
222 
223     bool useBounds = d->fRandom->nextBool();
224     return GrConvolutionEffect::Create(d->fTextures[texIdx],
225                                        dir,
226                                        radius,
227                                        kernel,
228                                        useBounds,
229                                        bounds);
230 }
231