• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2014 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 #include "gl/builders/GrGLProgramBuilder.h"
8 #include "GrMatrixConvolutionEffect.h"
9 #include "gl/GrGLProcessor.h"
10 #include "gl/GrGLSL.h"
11 #include "gl/GrGLTexture.h"
12 #include "GrTBackendProcessorFactory.h"
13 
14 class GrGLMatrixConvolutionEffect : public GrGLFragmentProcessor {
15 public:
16     GrGLMatrixConvolutionEffect(const GrBackendProcessorFactory& factory,
17                                 const GrProcessor&);
18     virtual void emitCode(GrGLProgramBuilder*,
19                           const GrFragmentProcessor&,
20                           const GrProcessorKey&,
21                           const char* outputColor,
22                           const char* inputColor,
23                           const TransformedCoordsArray&,
24                           const TextureSamplerArray&) SK_OVERRIDE;
25 
26     static inline void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder*);
27 
28     virtual void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
29 
30 private:
31     typedef GrGLProgramDataManager::UniformHandle UniformHandle;
32     SkISize                     fKernelSize;
33     bool                        fConvolveAlpha;
34 
35     UniformHandle               fBoundsUni;
36     UniformHandle               fKernelUni;
37     UniformHandle               fImageIncrementUni;
38     UniformHandle               fKernelOffsetUni;
39     UniformHandle               fGainUni;
40     UniformHandle               fBiasUni;
41     GrTextureDomain::GLDomain   fDomain;
42 
43     typedef GrGLFragmentProcessor INHERITED;
44 };
45 
GrGLMatrixConvolutionEffect(const GrBackendProcessorFactory & factory,const GrProcessor & processor)46 GrGLMatrixConvolutionEffect::GrGLMatrixConvolutionEffect(const GrBackendProcessorFactory& factory,
47                                                          const GrProcessor& processor)
48     : INHERITED(factory) {
49     const GrMatrixConvolutionEffect& m = processor.cast<GrMatrixConvolutionEffect>();
50     fKernelSize = m.kernelSize();
51     fConvolveAlpha = m.convolveAlpha();
52 }
53 
emitCode(GrGLProgramBuilder * builder,const GrFragmentProcessor & fp,const GrProcessorKey & key,const char * outputColor,const char * inputColor,const TransformedCoordsArray & coords,const TextureSamplerArray & samplers)54 void GrGLMatrixConvolutionEffect::emitCode(GrGLProgramBuilder* builder,
55                                            const GrFragmentProcessor& fp,
56                                            const GrProcessorKey& key,
57                                            const char* outputColor,
58                                            const char* inputColor,
59                                            const TransformedCoordsArray& coords,
60                                            const TextureSamplerArray& samplers) {
61     sk_ignore_unused_variable(inputColor);
62     const GrTextureDomain& domain = fp.cast<GrMatrixConvolutionEffect>().domain();
63 
64     fBoundsUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
65                                      kVec4f_GrSLType, "Bounds");
66     fImageIncrementUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
67                                              kVec2f_GrSLType, "ImageIncrement");
68     fKernelUni = builder->addUniformArray(GrGLProgramBuilder::kFragment_Visibility,
69                                           kFloat_GrSLType,
70                                           "Kernel",
71                                           fKernelSize.width() * fKernelSize.height());
72     fKernelOffsetUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
73                                            kVec2f_GrSLType, "KernelOffset");
74     fGainUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
75                                    kFloat_GrSLType, "Gain");
76     fBiasUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
77                                    kFloat_GrSLType, "Bias");
78 
79     const char* kernelOffset = builder->getUniformCStr(fKernelOffsetUni);
80     const char* imgInc = builder->getUniformCStr(fImageIncrementUni);
81     const char* kernel = builder->getUniformCStr(fKernelUni);
82     const char* gain = builder->getUniformCStr(fGainUni);
83     const char* bias = builder->getUniformCStr(fBiasUni);
84     int kWidth = fKernelSize.width();
85     int kHeight = fKernelSize.height();
86 
87     GrGLFragmentShaderBuilder* fsBuilder = builder->getFragmentShaderBuilder();
88     SkString coords2D = fsBuilder->ensureFSCoords2D(coords, 0);
89     fsBuilder->codeAppend("vec4 sum = vec4(0, 0, 0, 0);");
90     fsBuilder->codeAppendf("vec2 coord = %s - %s * %s;", coords2D.c_str(), kernelOffset,
91                            imgInc);
92     fsBuilder->codeAppend("vec4 c;");
93 
94     for (int y = 0; y < kHeight; y++) {
95         for (int x = 0; x < kWidth; x++) {
96             GrGLShaderBuilder::ShaderBlock block(fsBuilder);
97             fsBuilder->codeAppendf("float k = %s[%d * %d + %d];", kernel, y, kWidth, x);
98             SkString coord;
99             coord.printf("coord + vec2(%d, %d) * %s", x, y, imgInc);
100             fDomain.sampleTexture(fsBuilder, domain, "c", coord, samplers[0]);
101             if (!fConvolveAlpha) {
102                 fsBuilder->codeAppend("c.rgb /= c.a;");
103             }
104             fsBuilder->codeAppend("sum += c * k;");
105         }
106     }
107     if (fConvolveAlpha) {
108         fsBuilder->codeAppendf("%s = sum * %s + %s;", outputColor, gain, bias);
109         fsBuilder->codeAppendf("%s.rgb = clamp(%s.rgb, 0.0, %s.a);",
110                                outputColor, outputColor, outputColor);
111     } else {
112         fDomain.sampleTexture(fsBuilder, domain, "c", coords2D, samplers[0]);
113         fsBuilder->codeAppendf("%s.a = c.a;", outputColor);
114         fsBuilder->codeAppendf("%s.rgb = sum.rgb * %s + %s;", outputColor, gain, bias);
115         fsBuilder->codeAppendf("%s.rgb *= %s.a;", outputColor, outputColor);
116     }
117 
118     SkString modulate;
119     GrGLSLMulVarBy4f(&modulate, 2, outputColor, inputColor);
120     fsBuilder->codeAppend(modulate.c_str());
121 }
122 
GenKey(const GrProcessor & processor,const GrGLCaps &,GrProcessorKeyBuilder * b)123 void GrGLMatrixConvolutionEffect::GenKey(const GrProcessor& processor,
124                                          const GrGLCaps&, GrProcessorKeyBuilder* b) {
125     const GrMatrixConvolutionEffect& m = processor.cast<GrMatrixConvolutionEffect>();
126     SkASSERT(m.kernelSize().width() <= 0x7FFF && m.kernelSize().height() <= 0xFFFF);
127     uint32_t key = m.kernelSize().width() << 16 | m.kernelSize().height();
128     key |= m.convolveAlpha() ? 1 << 31 : 0;
129     b->add32(key);
130     b->add32(GrTextureDomain::GLDomain::DomainKey(m.domain()));
131 }
132 
setData(const GrGLProgramDataManager & pdman,const GrProcessor & processor)133 void GrGLMatrixConvolutionEffect::setData(const GrGLProgramDataManager& pdman,
134                                           const GrProcessor& processor) {
135     const GrMatrixConvolutionEffect& conv = processor.cast<GrMatrixConvolutionEffect>();
136     GrTexture& texture = *conv.texture(0);
137     // the code we generated was for a specific kernel size
138     SkASSERT(conv.kernelSize() == fKernelSize);
139     float imageIncrement[2];
140     float ySign = texture.origin() == kTopLeft_GrSurfaceOrigin ? 1.0f : -1.0f;
141     imageIncrement[0] = 1.0f / texture.width();
142     imageIncrement[1] = ySign / texture.height();
143     pdman.set2fv(fImageIncrementUni, 1, imageIncrement);
144     pdman.set2fv(fKernelOffsetUni, 1, conv.kernelOffset());
145     pdman.set1fv(fKernelUni, fKernelSize.width() * fKernelSize.height(), conv.kernel());
146     pdman.set1f(fGainUni, conv.gain());
147     pdman.set1f(fBiasUni, conv.bias());
148     fDomain.setData(pdman, conv.domain(), texture.origin());
149 }
150 
GrMatrixConvolutionEffect(GrTexture * texture,const SkIRect & bounds,const SkISize & kernelSize,const SkScalar * kernel,SkScalar gain,SkScalar bias,const SkIPoint & kernelOffset,GrTextureDomain::Mode tileMode,bool convolveAlpha)151 GrMatrixConvolutionEffect::GrMatrixConvolutionEffect(GrTexture* texture,
152                                                      const SkIRect& bounds,
153                                                      const SkISize& kernelSize,
154                                                      const SkScalar* kernel,
155                                                      SkScalar gain,
156                                                      SkScalar bias,
157                                                      const SkIPoint& kernelOffset,
158                                                      GrTextureDomain::Mode tileMode,
159                                                      bool convolveAlpha)
160   : INHERITED(texture, GrCoordTransform::MakeDivByTextureWHMatrix(texture)),
161     fKernelSize(kernelSize),
162     fGain(SkScalarToFloat(gain)),
163     fBias(SkScalarToFloat(bias) / 255.0f),
164     fConvolveAlpha(convolveAlpha),
165     fDomain(GrTextureDomain::MakeTexelDomain(texture, bounds), tileMode) {
166     for (int i = 0; i < kernelSize.width() * kernelSize.height(); i++) {
167         fKernel[i] = SkScalarToFloat(kernel[i]);
168     }
169     fKernelOffset[0] = static_cast<float>(kernelOffset.x());
170     fKernelOffset[1] = static_cast<float>(kernelOffset.y());
171 }
172 
~GrMatrixConvolutionEffect()173 GrMatrixConvolutionEffect::~GrMatrixConvolutionEffect() {
174 }
175 
getFactory() const176 const GrBackendFragmentProcessorFactory& GrMatrixConvolutionEffect::getFactory() const {
177     return GrTBackendFragmentProcessorFactory<GrMatrixConvolutionEffect>::getInstance();
178 }
179 
onIsEqual(const GrProcessor & sBase) const180 bool GrMatrixConvolutionEffect::onIsEqual(const GrProcessor& sBase) const {
181     const GrMatrixConvolutionEffect& s = sBase.cast<GrMatrixConvolutionEffect>();
182     return this->texture(0) == s.texture(0) &&
183            fKernelSize == s.kernelSize() &&
184            !memcmp(fKernel, s.kernel(),
185                    fKernelSize.width() * fKernelSize.height() * sizeof(float)) &&
186            fGain == s.gain() &&
187            fBias == s.bias() &&
188            fKernelOffset == s.kernelOffset() &&
189            fConvolveAlpha == s.convolveAlpha() &&
190            fDomain == s.domain();
191 }
192 
193 // Static function to create a 2D convolution
194 GrFragmentProcessor*
CreateGaussian(GrTexture * texture,const SkIRect & bounds,const SkISize & kernelSize,SkScalar gain,SkScalar bias,const SkIPoint & kernelOffset,GrTextureDomain::Mode tileMode,bool convolveAlpha,SkScalar sigmaX,SkScalar sigmaY)195 GrMatrixConvolutionEffect::CreateGaussian(GrTexture* texture,
196                                           const SkIRect& bounds,
197                                           const SkISize& kernelSize,
198                                           SkScalar gain,
199                                           SkScalar bias,
200                                           const SkIPoint& kernelOffset,
201                                           GrTextureDomain::Mode tileMode,
202                                           bool convolveAlpha,
203                                           SkScalar sigmaX,
204                                           SkScalar sigmaY) {
205     float kernel[MAX_KERNEL_SIZE];
206     int width = kernelSize.width();
207     int height = kernelSize.height();
208     SkASSERT(width * height <= MAX_KERNEL_SIZE);
209     float sum = 0.0f;
210     float sigmaXDenom = 1.0f / (2.0f * SkScalarToFloat(SkScalarSquare(sigmaX)));
211     float sigmaYDenom = 1.0f / (2.0f * SkScalarToFloat(SkScalarSquare(sigmaY)));
212     int xRadius = width / 2;
213     int yRadius = height / 2;
214     for (int x = 0; x < width; x++) {
215       float xTerm = static_cast<float>(x - xRadius);
216       xTerm = xTerm * xTerm * sigmaXDenom;
217       for (int y = 0; y < height; y++) {
218         float yTerm = static_cast<float>(y - yRadius);
219         float xyTerm = sk_float_exp(-(xTerm + yTerm * yTerm * sigmaYDenom));
220         // Note that the constant term (1/(sqrt(2*pi*sigma^2)) of the Gaussian
221        // is dropped here, since we renormalize the kernel below.
222         kernel[y * width + x] = xyTerm;
223         sum += xyTerm;
224       }
225     }
226     // Normalize the kernel
227     float scale = 1.0f / sum;
228     for (int i = 0; i < width * height; ++i) {
229         kernel[i] *= scale;
230     }
231     return SkNEW_ARGS(GrMatrixConvolutionEffect, (texture,
232                                                   bounds,
233                                                   kernelSize,
234                                                   kernel,
235                                                   gain,
236                                                   bias,
237                                                   kernelOffset,
238                                                   tileMode,
239                                                   convolveAlpha));
240 }
241 
242 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrMatrixConvolutionEffect);
243 
TestCreate(SkRandom * random,GrContext * context,const GrDrawTargetCaps &,GrTexture * textures[])244 GrFragmentProcessor* GrMatrixConvolutionEffect::TestCreate(SkRandom* random,
245                                                            GrContext* context,
246                                                            const GrDrawTargetCaps&,
247                                                            GrTexture* textures[]) {
248     int texIdx = random->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx :
249                                       GrProcessorUnitTest::kAlphaTextureIdx;
250     int width = random->nextRangeU(1, MAX_KERNEL_SIZE);
251     int height = random->nextRangeU(1, MAX_KERNEL_SIZE / width);
252     SkISize kernelSize = SkISize::Make(width, height);
253     SkAutoTDeleteArray<SkScalar> kernel(new SkScalar[width * height]);
254     for (int i = 0; i < width * height; i++) {
255         kernel.get()[i] = random->nextSScalar1();
256     }
257     SkScalar gain = random->nextSScalar1();
258     SkScalar bias = random->nextSScalar1();
259     SkIPoint kernelOffset = SkIPoint::Make(random->nextRangeU(0, kernelSize.width()),
260                                            random->nextRangeU(0, kernelSize.height()));
261     SkIRect bounds = SkIRect::MakeXYWH(random->nextRangeU(0, textures[texIdx]->width()),
262                                        random->nextRangeU(0, textures[texIdx]->height()),
263                                        random->nextRangeU(0, textures[texIdx]->width()),
264                                        random->nextRangeU(0, textures[texIdx]->height()));
265     GrTextureDomain::Mode tileMode = static_cast<GrTextureDomain::Mode>(random->nextRangeU(0, 2));
266     bool convolveAlpha = random->nextBool();
267     return GrMatrixConvolutionEffect::Create(textures[texIdx],
268                                              bounds,
269                                              kernelSize,
270                                              kernel.get(),
271                                              gain,
272                                              bias,
273                                              kernelOffset,
274                                              tileMode,
275                                              convolveAlpha);
276 }
277