blob: 04ab4f41ca9859af2cbcd116d671a696baf85504 [file] [log] [blame]
joshualittac977922014-07-22 09:52:11 -07001/*
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 "GrMatrixConvolutionEffect.h"
8#include "gl/GrGLShaderBuilder.h"
9#include "gl/GrGLEffect.h"
10#include "gl/GrGLSL.h"
11#include "gl/GrGLTexture.h"
12#include "GrTBackendEffectFactory.h"
13
14class GrGLMatrixConvolutionEffect : public GrGLEffect {
15public:
16 GrGLMatrixConvolutionEffect(const GrBackendEffectFactory& factory,
17 const GrDrawEffect& effect);
18 virtual void emitCode(GrGLShaderBuilder*,
19 const GrDrawEffect&,
20 const GrEffectKey&,
21 const char* outputColor,
22 const char* inputColor,
23 const TransformedCoordsArray&,
24 const TextureSamplerArray&) SK_OVERRIDE;
25
26 static inline void GenKey(const GrDrawEffect&, const GrGLCaps&, GrEffectKeyBuilder*);
27
28 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
29
30private:
31 typedef GrGLUniformManager::UniformHandle UniformHandle;
joshualitt5ae5fc52014-07-29 12:59:27 -070032 SkISize fKernelSize;
33 bool fConvolveAlpha;
joshualittac977922014-07-22 09:52:11 -070034
joshualitt5ae5fc52014-07-29 12:59:27 -070035 UniformHandle fBoundsUni;
36 UniformHandle fKernelUni;
37 UniformHandle fImageIncrementUni;
38 UniformHandle fKernelOffsetUni;
39 UniformHandle fGainUni;
40 UniformHandle fBiasUni;
41 GrTextureDomain::GLDomain fDomain;
joshualittac977922014-07-22 09:52:11 -070042
43 typedef GrGLEffect INHERITED;
44};
45
46GrGLMatrixConvolutionEffect::GrGLMatrixConvolutionEffect(const GrBackendEffectFactory& factory,
47 const GrDrawEffect& drawEffect)
48 : INHERITED(factory) {
49 const GrMatrixConvolutionEffect& m = drawEffect.castEffect<GrMatrixConvolutionEffect>();
50 fKernelSize = m.kernelSize();
joshualittac977922014-07-22 09:52:11 -070051 fConvolveAlpha = m.convolveAlpha();
52}
53
joshualittac977922014-07-22 09:52:11 -070054void GrGLMatrixConvolutionEffect::emitCode(GrGLShaderBuilder* builder,
joshualitt5ae5fc52014-07-29 12:59:27 -070055 const GrDrawEffect& drawEffect,
joshualittac977922014-07-22 09:52:11 -070056 const GrEffectKey& key,
57 const char* outputColor,
58 const char* inputColor,
59 const TransformedCoordsArray& coords,
60 const TextureSamplerArray& samplers) {
61 sk_ignore_unused_variable(inputColor);
joshualitt5ae5fc52014-07-29 12:59:27 -070062 const GrTextureDomain& domain = drawEffect.castEffect<GrMatrixConvolutionEffect>().domain();
joshualittac977922014-07-22 09:52:11 -070063 SkString coords2D = builder->ensureFSCoords2D(coords, 0);
64 fBoundsUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
65 kVec4f_GrSLType, "Bounds");
66 fImageIncrementUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
67 kVec2f_GrSLType, "ImageIncrement");
68 fKernelUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility,
joshualitt5ae5fc52014-07-29 12:59:27 -070069 kFloat_GrSLType,
70 "Kernel",
71 fKernelSize.width() * fKernelSize.height());
joshualittac977922014-07-22 09:52:11 -070072 fKernelOffsetUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
joshualitt5ae5fc52014-07-29 12:59:27 -070073 kVec2f_GrSLType, "KernelOffset");
joshualittac977922014-07-22 09:52:11 -070074 fGainUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
75 kFloat_GrSLType, "Gain");
76 fBiasUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
77 kFloat_GrSLType, "Bias");
78
joshualittac977922014-07-22 09:52:11 -070079 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 builder->fsCodeAppend("\t\tvec4 sum = vec4(0, 0, 0, 0);\n");
joshualitt5ae5fc52014-07-29 12:59:27 -070088 builder->fsCodeAppendf("\t\tvec2 coord = %s - %s * %s;\n", coords2D.c_str(), kernelOffset,
89 imgInc);
90 builder->fsCodeAppend("\t\tvec4 c;\n");
91
92 for (int y = 0; y < kHeight; y++) {
93 for (int x = 0; x < kWidth; x++) {
94 GrGLShaderBuilder::FSBlock block(builder);
95 builder->fsCodeAppendf("\t\tfloat k = %s[%d * %d + %d];\n", kernel, y, kWidth, x);
96 SkString coord;
97 coord.printf("coord + vec2(%d, %d) * %s", x, y, imgInc);
98 fDomain.sampleTexture(builder, domain, "c", coord, samplers[0]);
99 if (!fConvolveAlpha) {
100 builder->fsCodeAppend("\t\tc.rgb /= c.a;\n");
101 }
102 builder->fsCodeAppend("\t\tsum += c * k;\n");
103 }
joshualittac977922014-07-22 09:52:11 -0700104 }
joshualittac977922014-07-22 09:52:11 -0700105 if (fConvolveAlpha) {
106 builder->fsCodeAppendf("\t\t%s = sum * %s + %s;\n", outputColor, gain, bias);
107 builder->fsCodeAppendf("\t\t%s.rgb = clamp(%s.rgb, 0.0, %s.a);\n",
joshualitt5ae5fc52014-07-29 12:59:27 -0700108 outputColor, outputColor, outputColor);
joshualittac977922014-07-22 09:52:11 -0700109 } else {
joshualitt5ae5fc52014-07-29 12:59:27 -0700110 fDomain.sampleTexture(builder, domain, "c", coords2D, samplers[0]);
joshualittac977922014-07-22 09:52:11 -0700111 builder->fsCodeAppendf("\t\t%s.a = c.a;\n", outputColor);
112 builder->fsCodeAppendf("\t\t%s.rgb = sum.rgb * %s + %s;\n", outputColor, gain, bias);
113 builder->fsCodeAppendf("\t\t%s.rgb *= %s.a;\n", outputColor, outputColor);
114 }
115}
116
joshualittac977922014-07-22 09:52:11 -0700117void GrGLMatrixConvolutionEffect::GenKey(const GrDrawEffect& drawEffect,
118 const GrGLCaps&, GrEffectKeyBuilder* b) {
119 const GrMatrixConvolutionEffect& m = drawEffect.castEffect<GrMatrixConvolutionEffect>();
joshualitt5ae5fc52014-07-29 12:59:27 -0700120 SkASSERT(m.kernelSize().width() <= 0x7FFF && m.kernelSize().height() <= 0xFFFF);
121 uint32_t key = m.kernelSize().width() << 16 | m.kernelSize().height();
122 key |= m.convolveAlpha() ? 1 << 31 : 0;
joshualittac977922014-07-22 09:52:11 -0700123 b->add32(key);
joshualitt5ae5fc52014-07-29 12:59:27 -0700124 b->add32(GrTextureDomain::GLDomain::DomainKey(m.domain()));
joshualittac977922014-07-22 09:52:11 -0700125}
126
127void GrGLMatrixConvolutionEffect::setData(const GrGLUniformManager& uman,
128 const GrDrawEffect& drawEffect) {
129 const GrMatrixConvolutionEffect& conv = drawEffect.castEffect<GrMatrixConvolutionEffect>();
130 GrTexture& texture = *conv.texture(0);
131 // the code we generated was for a specific kernel size
132 SkASSERT(conv.kernelSize() == fKernelSize);
joshualittac977922014-07-22 09:52:11 -0700133 float imageIncrement[2];
134 float ySign = texture.origin() == kTopLeft_GrSurfaceOrigin ? 1.0f : -1.0f;
135 imageIncrement[0] = 1.0f / texture.width();
136 imageIncrement[1] = ySign / texture.height();
137 uman.set2fv(fImageIncrementUni, 1, imageIncrement);
138 uman.set2fv(fKernelOffsetUni, 1, conv.kernelOffset());
139 uman.set1fv(fKernelUni, fKernelSize.width() * fKernelSize.height(), conv.kernel());
140 uman.set1f(fGainUni, conv.gain());
141 uman.set1f(fBiasUni, conv.bias());
joshualitt5ae5fc52014-07-29 12:59:27 -0700142 fDomain.setData(uman, conv.domain(), texture.origin());
joshualittac977922014-07-22 09:52:11 -0700143}
144
145GrMatrixConvolutionEffect::GrMatrixConvolutionEffect(GrTexture* texture,
146 const SkIRect& bounds,
147 const SkISize& kernelSize,
148 const SkScalar* kernel,
149 SkScalar gain,
150 SkScalar bias,
151 const SkIPoint& kernelOffset,
joshualitt5ae5fc52014-07-29 12:59:27 -0700152 GrTextureDomain::Mode tileMode,
joshualittac977922014-07-22 09:52:11 -0700153 bool convolveAlpha)
154 : INHERITED(texture, MakeDivByTextureWHMatrix(texture)),
joshualittac977922014-07-22 09:52:11 -0700155 fKernelSize(kernelSize),
156 fGain(SkScalarToFloat(gain)),
157 fBias(SkScalarToFloat(bias) / 255.0f),
joshualitt5ae5fc52014-07-29 12:59:27 -0700158 fConvolveAlpha(convolveAlpha),
159 fDomain(GrTextureDomain::MakeTexelDomain(texture, bounds), tileMode) {
joshualittac977922014-07-22 09:52:11 -0700160 fKernel = new float[kernelSize.width() * kernelSize.height()];
161 for (int i = 0; i < kernelSize.width() * kernelSize.height(); i++) {
162 fKernel[i] = SkScalarToFloat(kernel[i]);
163 }
164 fKernelOffset[0] = static_cast<float>(kernelOffset.x());
165 fKernelOffset[1] = static_cast<float>(kernelOffset.y());
166 this->setWillNotUseInputColor();
167}
168
169GrMatrixConvolutionEffect::~GrMatrixConvolutionEffect() {
170 delete[] fKernel;
171}
172
173const GrBackendEffectFactory& GrMatrixConvolutionEffect::getFactory() const {
174 return GrTBackendEffectFactory<GrMatrixConvolutionEffect>::getInstance();
175}
176
177bool GrMatrixConvolutionEffect::onIsEqual(const GrEffect& sBase) const {
178 const GrMatrixConvolutionEffect& s = CastEffect<GrMatrixConvolutionEffect>(sBase);
179 return this->texture(0) == s.texture(0) &&
180 fKernelSize == s.kernelSize() &&
181 !memcmp(fKernel, s.kernel(),
182 fKernelSize.width() * fKernelSize.height() * sizeof(float)) &&
183 fGain == s.gain() &&
184 fBias == s.bias() &&
185 fKernelOffset == s.kernelOffset() &&
joshualitt5ae5fc52014-07-29 12:59:27 -0700186 fConvolveAlpha == s.convolveAlpha() &&
187 fDomain == s.domain();
joshualittac977922014-07-22 09:52:11 -0700188}
189
190GR_DEFINE_EFFECT_TEST(GrMatrixConvolutionEffect);
191
192GrEffect* GrMatrixConvolutionEffect::TestCreate(SkRandom* random,
193 GrContext* context,
194 const GrDrawTargetCaps&,
195 GrTexture* textures[]) {
196 int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
197 GrEffectUnitTest::kAlphaTextureIdx;
198 int width = random->nextRangeU(1, MAX_KERNEL_SIZE);
199 int height = random->nextRangeU(1, MAX_KERNEL_SIZE / width);
200 SkISize kernelSize = SkISize::Make(width, height);
201 SkAutoTDeleteArray<SkScalar> kernel(new SkScalar[width * height]);
202 for (int i = 0; i < width * height; i++) {
203 kernel.get()[i] = random->nextSScalar1();
204 }
205 SkScalar gain = random->nextSScalar1();
206 SkScalar bias = random->nextSScalar1();
207 SkIPoint kernelOffset = SkIPoint::Make(random->nextRangeU(0, kernelSize.width()),
208 random->nextRangeU(0, kernelSize.height()));
209 SkIRect bounds = SkIRect::MakeXYWH(random->nextRangeU(0, textures[texIdx]->width()),
210 random->nextRangeU(0, textures[texIdx]->height()),
211 random->nextRangeU(0, textures[texIdx]->width()),
212 random->nextRangeU(0, textures[texIdx]->height()));
joshualitt5ae5fc52014-07-29 12:59:27 -0700213 GrTextureDomain::Mode tileMode = static_cast<GrTextureDomain::Mode>(random->nextRangeU(0, 2));
joshualittac977922014-07-22 09:52:11 -0700214 bool convolveAlpha = random->nextBool();
215 return GrMatrixConvolutionEffect::Create(textures[texIdx],
216 bounds,
217 kernelSize,
218 kernel.get(),
219 gain,
220 bias,
221 kernelOffset,
222 tileMode,
223 convolveAlpha);
224}