blob: c108d3416386452ba75b5001abf187092ebbf55c [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 */
Mike Kleinc0bd9f92019-04-23 12:05:21 -05007#include "src/gpu/effects/GrMatrixConvolutionEffect.h"
Robert Phillips296b1cc2017-03-15 10:42:12 -04008
Adlai Holler1ed43912020-05-04 13:02:28 -04009#include "src/gpu/GrBitmapTextureMaker.h"
10#include "src/gpu/GrContextPriv.h"
11#include "src/gpu/GrProxyProvider.h"
12#include "src/gpu/GrRecordingContextPriv.h"
Greg Daniel456f9b52020-03-05 19:14:18 +000013#include "src/gpu/GrTexture.h"
Greg Danielf91aeb22019-06-18 09:58:02 -040014#include "src/gpu/GrTextureProxy.h"
Brian Salomon694ec492020-04-14 13:39:31 -040015#include "src/gpu/effects/GrTextureEffect.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050016#include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
17#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
18#include "src/gpu/glsl/GrGLSLProgramDataManager.h"
19#include "src/gpu/glsl/GrGLSLUniformHandler.h"
joshualittac977922014-07-22 09:52:11 -070020
egdaniel64c47282015-11-13 06:54:19 -080021class GrGLMatrixConvolutionEffect : public GrGLSLFragmentProcessor {
joshualittac977922014-07-22 09:52:11 -070022public:
robertphillips9cdb9922016-02-03 12:25:40 -080023 void emitCode(EmitArgs&) override;
joshualittac977922014-07-22 09:52:11 -070024
Brian Salomon94efbf52016-11-29 13:43:05 -050025 static inline void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*);
joshualittac977922014-07-22 09:52:11 -070026
wangyixb1daa862015-08-18 11:29:31 -070027protected:
Brian Salomonab015ef2017-04-04 10:15:51 -040028 void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
joshualittac977922014-07-22 09:52:11 -070029
30private:
egdaniel018fb622015-10-28 07:26:40 -070031 typedef GrGLSLProgramDataManager::UniformHandle UniformHandle;
joshualittac977922014-07-22 09:52:11 -070032
joshualitt5ae5fc52014-07-29 12:59:27 -070033 UniformHandle fKernelUni;
joshualitt5ae5fc52014-07-29 12:59:27 -070034 UniformHandle fKernelOffsetUni;
35 UniformHandle fGainUni;
36 UniformHandle fBiasUni;
Adlai Holler1ed43912020-05-04 13:02:28 -040037 UniformHandle fKernelBiasUni;
joshualittac977922014-07-22 09:52:11 -070038
egdaniel64c47282015-11-13 06:54:19 -080039 typedef GrGLSLFragmentProcessor INHERITED;
joshualittac977922014-07-22 09:52:11 -070040};
41
Adlai Holler1ed43912020-05-04 13:02:28 -040042GrMatrixConvolutionEffect::KernelWrapper GrMatrixConvolutionEffect::KernelWrapper::Make(
43 GrRecordingContext* context, SkISize size, const SkScalar* values) {
44 if (nullptr == context || nullptr == values || size.isEmpty()) {
45 return {};
46 }
47 const int length = size.area();
48 // Small kernel -> just fill the array.
49 KernelWrapper result(size);
50 if (length <= kMaxUniformSize) {
51 for (int i = 0; i < length; i++) {
52 result.fArray[i] = SkScalarToFloat(values[i]);
53 }
54 return result;
55 }
56
57 ScalableSampler& scalableSampler = result.fScalableSampler;
58 // Determine min and max values to figure out inner gain & bias.
59 SkScalar min = values[0];
60 SkScalar max = values[0];
61 for (int i = 1; i < length; i++) {
62 if (values[i] < min) {
63 min = values[i];
64 }
65 if (values[i] > max) {
66 max = values[i];
67 }
68 }
69 // Treat near-0 gain (i.e. box blur) as 1, and let the kernelBias
70 // move everything up to the final value.
71 const SkScalar computedGain = max - min;
72 scalableSampler.fGain = SkScalarNearlyZero(computedGain) ? 1.0f : SkScalarToFloat(computedGain);
73 // Inner bias is pre-inner-gain so we divide that out.
74 scalableSampler.fBias = SkScalarToFloat(min) / scalableSampler.fGain;
75
76 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
77 GrUniqueKey key;
78 GrUniqueKey::Builder builder(&key, kDomain, length, "Matrix Convolution Kernel");
79 // Texture cache key is the exact content of the kernel.
80 static_assert(sizeof(float) == 4);
81 for (int i = 0; i < length; i++) {
82 builder[i] = *(const uint32_t*)&values[i];
83 }
84 builder.finish();
85
86 // Find or create a texture.
87 GrProxyProvider* proxyProvider = context->priv().proxyProvider();
88 GrSurfaceProxyView view;
89 if (sk_sp<GrTextureProxy> kernelProxy = proxyProvider->findOrCreateProxyByUniqueKey(key)) {
90 GrSwizzle swizzle =
91 context->priv().caps()->getReadSwizzle(kernelProxy->backendFormat(),
92 GrColorType::kAlpha_8);
93 view = {std::move(kernelProxy), kTopLeft_GrSurfaceOrigin, swizzle};
94 } else {
95 SkBitmap bm;
96 if (!bm.tryAllocPixels(SkImageInfo::MakeA8(GrNextPow2(length), 1))) {
97 return {};
98 }
99 for (int i = 0; i < length; i++) {
100 *(bm.getAddr8(i, 0)) =
101 SkScalarRoundToInt((values[i] - min) / scalableSampler.fGain * 255);
102 }
103 bm.setImmutable();
104 GrBitmapTextureMaker maker(context, bm, GrImageTexGenPolicy::kNew_Uncached_Budgeted);
105 view = maker.view(GrMipMapped::kNo);
106 if (!view) {
107 return {};
108 }
109 proxyProvider->assignUniqueKeyToProxy(key, view.asTextureProxy());
110 }
111 scalableSampler.fSampler = { std::move(view) };
112 return result;
113}
114
115bool GrMatrixConvolutionEffect::KernelWrapper::operator==(const KernelWrapper& k) const {
116 return fSize == k.fSize &&
117 (this->isSampled() ? fScalableSampler == k.fScalableSampler : fArray == k.fArray);
118}
119
120bool GrMatrixConvolutionEffect::KernelWrapper::ScalableSampler::operator==(
121 const ScalableSampler& k) const {
122 return fSampler == k.fSampler && fGain == k.fGain && fBias == k.fBias;
123}
124
wangyix7c157a92015-07-22 15:08:53 -0700125void GrGLMatrixConvolutionEffect::emitCode(EmitArgs& args) {
robertphillipsbf536af2016-02-04 06:11:53 -0800126 const GrMatrixConvolutionEffect& mce = args.fFp.cast<GrMatrixConvolutionEffect>();
robertphillipsbf536af2016-02-04 06:11:53 -0800127
Adlai Holler1ed43912020-05-04 13:02:28 -0400128 int kernelWidth = mce.kernelSize().width();
129 int kernelHeight = mce.kernelSize().height();
robertphillipsbf536af2016-02-04 06:11:53 -0800130
Adlai Holler1ed43912020-05-04 13:02:28 -0400131 int arrayCount = (kernelWidth * kernelHeight + 3) / 4;
132 SkASSERT(4 * arrayCount >= kernelWidth * kernelHeight);
jvanverth78d6eb02016-03-02 13:21:16 -0800133
egdaniel7ea439b2015-12-03 09:20:44 -0800134 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
Adlai Holler1ed43912020-05-04 13:02:28 -0400135 if (mce.kernelIsSampled()) {
136 fKernelBiasUni = uniformHandler->addUniform(&mce, kFragment_GrShaderFlag,
137 kFloat_GrSLType, "KernelBias");
138 } else {
139 fKernelUni = uniformHandler->addUniformArray(&mce, kFragment_GrShaderFlag,
140 kFloat4_GrSLType, "Kernel", arrayCount);
141 }
Ethan Nicholas16464c32020-04-06 13:53:05 -0400142 fKernelOffsetUni = uniformHandler->addUniform(&mce, kFragment_GrShaderFlag, kHalf2_GrSLType,
egdaniel7ea439b2015-12-03 09:20:44 -0800143 "KernelOffset");
Adlai Holler1ed43912020-05-04 13:02:28 -0400144 fGainUni = uniformHandler->addUniform(&mce, kFragment_GrShaderFlag, kFloat_GrSLType, "Gain");
145 fBiasUni = uniformHandler->addUniform(&mce, kFragment_GrShaderFlag, kFloat_GrSLType, "Bias");
joshualittac977922014-07-22 09:52:11 -0700146
egdaniel7ea439b2015-12-03 09:20:44 -0800147 const char* kernelOffset = uniformHandler->getUniformCStr(fKernelOffsetUni);
egdaniel7ea439b2015-12-03 09:20:44 -0800148 const char* gain = uniformHandler->getUniformCStr(fGainUni);
149 const char* bias = uniformHandler->getUniformCStr(fBiasUni);
joshualittac977922014-07-22 09:52:11 -0700150
cdalton85285412016-02-18 12:37:07 -0800151 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
Ethan Nicholas58430122020-04-14 09:54:02 -0400152 SkString coords2D = fragBuilder->ensureCoords2D(args.fTransformedCoords[0].fVaryingPoint,
153 mce.sampleMatrix());
Adlai Holler1ed43912020-05-04 13:02:28 -0400154 fragBuilder->codeAppend("float4 sum = float4(0, 0, 0, 0);");
Brian Salomon694ec492020-04-14 13:39:31 -0400155 fragBuilder->codeAppendf("float2 coord = %s - %s;", coords2D.c_str(), kernelOffset);
Adlai Holler1ed43912020-05-04 13:02:28 -0400156 fragBuilder->codeAppend("float4 c;");
joshualitt5ae5fc52014-07-29 12:59:27 -0700157
Adlai Holler1ed43912020-05-04 13:02:28 -0400158 for (int y = 0; y < kernelHeight; y++) {
159 for (int x = 0; x < kernelWidth; x++) {
egdaniel4ca2e602015-11-18 08:01:26 -0800160 GrGLSLShaderBuilder::ShaderBlock block(fragBuilder);
Adlai Holler1ed43912020-05-04 13:02:28 -0400161 int offset = y*kernelWidth + x;
jvanverth78d6eb02016-03-02 13:21:16 -0800162
Adlai Holler1ed43912020-05-04 13:02:28 -0400163 if (mce.kernelIsSampled()) {
164 const char* kernelBias = uniformHandler->getUniformCStr(fKernelBiasUni);
165 float xCoord = offset / (float)GrNextPow2(mce.kernelSize().area());
166
167 fragBuilder->codeAppend("float k = ");
168 fragBuilder->appendTextureLookup(args.fTexSamplers[0],
169 SkSL::String::printf("half2(%f, 0.5)", xCoord).c_str());
170 fragBuilder->codeAppendf(".w + %s;", kernelBias);
171 } else {
172 static constexpr const char* kVecSuffix[4] = { ".x", ".y", ".z", ".w" };
173 const char* kernel = uniformHandler->getUniformCStr(fKernelUni);
174 fragBuilder->codeAppendf("float k = %s[%d]%s;", kernel, offset / 4,
175 kVecSuffix[offset & 0x3]);
176 }
177
Brian Salomon694ec492020-04-14 13:39:31 -0400178 SkSL::String coord;
179 coord.appendf("coord + half2(%d, %d)", x, y);
180 auto sample = this->invokeChild(0, args, coord);
Adlai Holler1ed43912020-05-04 13:02:28 -0400181 fragBuilder->codeAppendf("float4 c = %s;", sample.c_str());
robertphillipsbf536af2016-02-04 06:11:53 -0800182 if (!mce.convolveAlpha()) {
egdaniel4ca2e602015-11-18 08:01:26 -0800183 fragBuilder->codeAppend("c.rgb /= c.a;");
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400184 fragBuilder->codeAppend("c.rgb = saturate(c.rgb);");
joshualitt5ae5fc52014-07-29 12:59:27 -0700185 }
egdaniel4ca2e602015-11-18 08:01:26 -0800186 fragBuilder->codeAppend("sum += c * k;");
joshualitt5ae5fc52014-07-29 12:59:27 -0700187 }
joshualittac977922014-07-22 09:52:11 -0700188 }
robertphillipsbf536af2016-02-04 06:11:53 -0800189 if (mce.convolveAlpha()) {
Adlai Holler1ed43912020-05-04 13:02:28 -0400190 fragBuilder->codeAppendf("%s = half4(sum * %s + %s);", args.fOutputColor, gain, bias);
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -0400191 fragBuilder->codeAppendf("%s.a = saturate(%s.a);", args.fOutputColor, args.fOutputColor);
egdaniel4ca2e602015-11-18 08:01:26 -0800192 fragBuilder->codeAppendf("%s.rgb = clamp(%s.rgb, 0.0, %s.a);",
193 args.fOutputColor, args.fOutputColor, args.fOutputColor);
joshualittac977922014-07-22 09:52:11 -0700194 } else {
Brian Salomon694ec492020-04-14 13:39:31 -0400195 auto sample = this->invokeChild(0, args, coords2D.c_str());
196 fragBuilder->codeAppendf("c = %s;", sample.c_str());
Adlai Holler1ed43912020-05-04 13:02:28 -0400197 fragBuilder->codeAppendf("%s.a = half(c.a);", args.fOutputColor);
198 fragBuilder->codeAppendf("%s.rgb = half3(saturate(sum.rgb * %s + %s));",
199 args.fOutputColor, gain, bias);
egdaniel4ca2e602015-11-18 08:01:26 -0800200 fragBuilder->codeAppendf("%s.rgb *= %s.a;", args.fOutputColor, args.fOutputColor);
joshualittac977922014-07-22 09:52:11 -0700201 }
Ethan Nicholas2983f402017-05-08 09:36:08 -0400202 fragBuilder->codeAppendf("%s *= %s;\n", args.fOutputColor, args.fInputColor);
joshualittac977922014-07-22 09:52:11 -0700203}
204
joshualittb0a8a372014-09-23 09:50:21 -0700205void GrGLMatrixConvolutionEffect::GenKey(const GrProcessor& processor,
Brian Salomon94efbf52016-11-29 13:43:05 -0500206 const GrShaderCaps&, GrProcessorKeyBuilder* b) {
joshualittb0a8a372014-09-23 09:50:21 -0700207 const GrMatrixConvolutionEffect& m = processor.cast<GrMatrixConvolutionEffect>();
joshualitt5ae5fc52014-07-29 12:59:27 -0700208 SkASSERT(m.kernelSize().width() <= 0x7FFF && m.kernelSize().height() <= 0xFFFF);
209 uint32_t key = m.kernelSize().width() << 16 | m.kernelSize().height();
caryclark952538e2016-02-26 05:01:42 -0800210 key |= m.convolveAlpha() ? 1U << 31 : 0;
joshualittac977922014-07-22 09:52:11 -0700211 b->add32(key);
212}
213
egdaniel018fb622015-10-28 07:26:40 -0700214void GrGLMatrixConvolutionEffect::onSetData(const GrGLSLProgramDataManager& pdman,
Brian Salomonab015ef2017-04-04 10:15:51 -0400215 const GrFragmentProcessor& processor) {
joshualittb0a8a372014-09-23 09:50:21 -0700216 const GrMatrixConvolutionEffect& conv = processor.cast<GrMatrixConvolutionEffect>();
Brian Salomon694ec492020-04-14 13:39:31 -0400217 pdman.set2fv(fKernelOffsetUni, 1, conv.kernelOffset().ptr());
Adlai Holler1ed43912020-05-04 13:02:28 -0400218 float totalGain = conv.gain();
219 if (conv.kernelIsSampled()) {
220 totalGain *= conv.kernelSampleGain();
221 pdman.set1f(fKernelBiasUni, conv.kernelSampleBias());
222 } else {
223 int kernelCount = conv.kernelSize().area();
224 int arrayCount = (kernelCount + 3) / 4;
225 SkASSERT(4 * arrayCount >= kernelCount);
226 pdman.set4fv(fKernelUni, arrayCount, conv.kernel());
227 }
kkinnunen7510b222014-07-30 00:04:16 -0700228 pdman.set1f(fBiasUni, conv.bias());
Adlai Holler1ed43912020-05-04 13:02:28 -0400229 pdman.set1f(fGainUni, totalGain);
joshualittac977922014-07-22 09:52:11 -0700230}
231
Brian Salomon694ec492020-04-14 13:39:31 -0400232GrMatrixConvolutionEffect::GrMatrixConvolutionEffect(std::unique_ptr<GrFragmentProcessor> child,
Adlai Holler1ed43912020-05-04 13:02:28 -0400233 KernelWrapper kernel,
Robert Phillips40fd7c92017-01-30 08:06:27 -0500234 SkScalar gain,
235 SkScalar bias,
236 const SkIPoint& kernelOffset,
Robert Phillips40fd7c92017-01-30 08:06:27 -0500237 bool convolveAlpha)
Brian Salomon6cd51b52017-07-26 19:07:15 -0400238 // To advertise either the modulation or opaqueness optimizations we'd have to examine the
239 // parameters.
Ethan Nicholasabff9562017-10-09 10:54:08 -0400240 : INHERITED(kGrMatrixConvolutionEffect_ClassID, kNone_OptimizationFlags)
Adlai Holler1ed43912020-05-04 13:02:28 -0400241 , fKernel(std::move(kernel))
Brian Salomon6cd51b52017-07-26 19:07:15 -0400242 , fGain(SkScalarToFloat(gain))
243 , fBias(SkScalarToFloat(bias) / 255.0f)
244 , fConvolveAlpha(convolveAlpha) {
Brian Salomon694ec492020-04-14 13:39:31 -0400245 child->setSampledWithExplicitCoords();
246 this->registerChildProcessor(std::move(child));
Adlai Holler1ed43912020-05-04 13:02:28 -0400247 if (fKernel.isSampled()) {
248 this->setTextureSamplerCnt(1);
Robert Phillips40fd7c92017-01-30 08:06:27 -0500249 }
Brian Salomon694ec492020-04-14 13:39:31 -0400250 fKernelOffset = {static_cast<float>(kernelOffset.x()),
251 static_cast<float>(kernelOffset.y())};
252 this->addCoordTransform(&fCoordTransform);
Robert Phillips40fd7c92017-01-30 08:06:27 -0500253}
254
Brian Salomon3f6f9652017-07-28 07:34:05 -0400255GrMatrixConvolutionEffect::GrMatrixConvolutionEffect(const GrMatrixConvolutionEffect& that)
Ethan Nicholasabff9562017-10-09 10:54:08 -0400256 : INHERITED(kGrMatrixConvolutionEffect_ClassID, kNone_OptimizationFlags)
Adlai Holler1ed43912020-05-04 13:02:28 -0400257 , fKernel(that.fKernel)
Brian Salomon3f6f9652017-07-28 07:34:05 -0400258 , fGain(that.fGain)
259 , fBias(that.fBias)
Brian Salomon694ec492020-04-14 13:39:31 -0400260 , fKernelOffset(that.fKernelOffset)
Brian Salomon3f6f9652017-07-28 07:34:05 -0400261 , fConvolveAlpha(that.fConvolveAlpha) {
Brian Salomon694ec492020-04-14 13:39:31 -0400262 auto child = that.childProcessor(0).clone();
263 child->setSampledWithExplicitCoords();
264 this->registerChildProcessor(std::move(child));
Adlai Holler1ed43912020-05-04 13:02:28 -0400265 if (fKernel.isSampled()) {
266 this->setTextureSamplerCnt(1);
267 }
Brian Salomon3f6f9652017-07-28 07:34:05 -0400268 this->addCoordTransform(&fCoordTransform);
Brian Salomon3f6f9652017-07-28 07:34:05 -0400269}
270
Brian Salomonaff329b2017-08-11 09:40:37 -0400271std::unique_ptr<GrFragmentProcessor> GrMatrixConvolutionEffect::clone() const {
272 return std::unique_ptr<GrFragmentProcessor>(new GrMatrixConvolutionEffect(*this));
Brian Salomon3f6f9652017-07-28 07:34:05 -0400273}
274
Brian Salomon94efbf52016-11-29 13:43:05 -0500275void GrMatrixConvolutionEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
egdaniel57d3b032015-11-13 11:57:27 -0800276 GrProcessorKeyBuilder* b) const {
joshualitteb2a6762014-12-04 11:35:33 -0800277 GrGLMatrixConvolutionEffect::GenKey(*this, caps, b);
278}
279
egdaniel57d3b032015-11-13 11:57:27 -0800280GrGLSLFragmentProcessor* GrMatrixConvolutionEffect::onCreateGLSLInstance() const {
robertphillipsbf536af2016-02-04 06:11:53 -0800281 return new GrGLMatrixConvolutionEffect;
joshualittac977922014-07-22 09:52:11 -0700282}
283
bsalomon0e08fc12014-10-15 08:19:04 -0700284bool GrMatrixConvolutionEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
joshualitt49586be2014-09-16 08:21:41 -0700285 const GrMatrixConvolutionEffect& s = sBase.cast<GrMatrixConvolutionEffect>();
Adlai Holler1ed43912020-05-04 13:02:28 -0400286 return fKernel == s.fKernel &&
joshualittac977922014-07-22 09:52:11 -0700287 fGain == s.gain() &&
288 fBias == s.bias() &&
Brian Salomon694ec492020-04-14 13:39:31 -0400289 fKernelOffset == s.kernelOffset() &&
290 fConvolveAlpha == s.convolveAlpha();
joshualittac977922014-07-22 09:52:11 -0700291}
292
Adlai Holler1ed43912020-05-04 13:02:28 -0400293const GrFragmentProcessor::TextureSampler& GrMatrixConvolutionEffect::onTextureSampler(
294 int index) const {
295 return IthTextureSampler(index, fKernel.scalableSampler().fSampler);
296}
297
Robert Phillips4e962c62018-06-20 07:46:19 -0400298static void fill_in_1D_gaussian_kernel_with_stride(float* kernel, int size, int stride,
299 float twoSigmaSqrd) {
300 SkASSERT(!SkScalarNearlyZero(twoSigmaSqrd, SK_ScalarNearlyZero));
301
302 const float sigmaDenom = 1.0f / twoSigmaSqrd;
303 const int radius = size / 2;
304
305 float sum = 0.0f;
306 for (int i = 0; i < size; ++i) {
307 float term = static_cast<float>(i - radius);
308 // Note that the constant term (1/(sqrt(2*pi*sigma^2)) of the Gaussian
309 // is dropped here, since we renormalize the kernel below.
310 kernel[i * stride] = sk_float_exp(-term * term * sigmaDenom);
311 sum += kernel[i * stride];
312 }
313 // Normalize the kernel
314 float scale = 1.0f / sum;
315 for (int i = 0; i < size; ++i) {
316 kernel[i * stride] *= scale;
317 }
318}
319
Robert Phillips40fd7c92017-01-30 08:06:27 -0500320static void fill_in_2D_gaussian_kernel(float* kernel, int width, int height,
321 SkScalar sigmaX, SkScalar sigmaY) {
Greg Daniel4eda8d92018-04-03 14:03:15 -0400322 const float twoSigmaSqrdX = 2.0f * SkScalarToFloat(SkScalarSquare(sigmaX));
323 const float twoSigmaSqrdY = 2.0f * SkScalarToFloat(SkScalarSquare(sigmaY));
324
Robert Phillips4e962c62018-06-20 07:46:19 -0400325 // TODO: in all of these degenerate cases we're uploading (and using) a whole lot of zeros.
Greg Daniel4eda8d92018-04-03 14:03:15 -0400326 if (SkScalarNearlyZero(twoSigmaSqrdX, SK_ScalarNearlyZero) ||
327 SkScalarNearlyZero(twoSigmaSqrdY, SK_ScalarNearlyZero)) {
Robert Phillips4e962c62018-06-20 07:46:19 -0400328 // In this case the 2D Gaussian degenerates to a 1D Gaussian (in X or Y) or a point
329 SkASSERT(3 == width || 3 == height);
Brian Salomon694ec492020-04-14 13:39:31 -0400330 std::fill_n(kernel, width*height, 0);
Robert Phillips4e962c62018-06-20 07:46:19 -0400331
332 if (SkScalarNearlyZero(twoSigmaSqrdX, SK_ScalarNearlyZero) &&
333 SkScalarNearlyZero(twoSigmaSqrdY, SK_ScalarNearlyZero)) {
334 // A point
335 SkASSERT(3 == width && 3 == height);
336 kernel[4] = 1.0f;
337 } else if (SkScalarNearlyZero(twoSigmaSqrdX, SK_ScalarNearlyZero)) {
338 // A 1D Gaussian in Y
339 SkASSERT(3 == width);
340 // Down the middle column of the kernel with a stride of width
341 fill_in_1D_gaussian_kernel_with_stride(&kernel[1], height, width, twoSigmaSqrdY);
342 } else {
343 // A 1D Gaussian in X
344 SkASSERT(SkScalarNearlyZero(twoSigmaSqrdY, SK_ScalarNearlyZero));
345 SkASSERT(3 == height);
346 // Down the middle row of the kernel with a stride of 1
347 fill_in_1D_gaussian_kernel_with_stride(&kernel[width], width, 1, twoSigmaSqrdX);
Greg Daniel4eda8d92018-04-03 14:03:15 -0400348 }
349 return;
350 }
351
352 const float sigmaXDenom = 1.0f / twoSigmaSqrdX;
353 const float sigmaYDenom = 1.0f / twoSigmaSqrdY;
Robert Phillips40fd7c92017-01-30 08:06:27 -0500354 const int xRadius = width / 2;
355 const int yRadius = height / 2;
356
joshualitt5acfea72014-08-11 13:55:34 -0700357 float sum = 0.0f;
joshualitt5acfea72014-08-11 13:55:34 -0700358 for (int x = 0; x < width; x++) {
Robert Phillips40fd7c92017-01-30 08:06:27 -0500359 float xTerm = static_cast<float>(x - xRadius);
360 xTerm = xTerm * xTerm * sigmaXDenom;
361 for (int y = 0; y < height; y++) {
362 float yTerm = static_cast<float>(y - yRadius);
363 float xyTerm = sk_float_exp(-(xTerm + yTerm * yTerm * sigmaYDenom));
364 // Note that the constant term (1/(sqrt(2*pi*sigma^2)) of the Gaussian
365 // is dropped here, since we renormalize the kernel below.
366 kernel[y * width + x] = xyTerm;
367 sum += xyTerm;
368 }
joshualitt5acfea72014-08-11 13:55:34 -0700369 }
370 // Normalize the kernel
371 float scale = 1.0f / sum;
372 for (int i = 0; i < width * height; ++i) {
373 kernel[i] *= scale;
374 }
Robert Phillips40fd7c92017-01-30 08:06:27 -0500375}
376
Adlai Holler1ed43912020-05-04 13:02:28 -0400377std::unique_ptr<GrFragmentProcessor> GrMatrixConvolutionEffect::Make(GrRecordingContext* context,
378 GrSurfaceProxyView srcView,
Brian Salomon694ec492020-04-14 13:39:31 -0400379 const SkIRect& srcBounds,
380 const SkISize& kernelSize,
381 const SkScalar* kernel,
382 SkScalar gain,
383 SkScalar bias,
384 const SkIPoint& kernelOffset,
385 GrSamplerState::WrapMode wm,
386 bool convolveAlpha,
387 const GrCaps& caps) {
Adlai Holler1ed43912020-05-04 13:02:28 -0400388 auto kw = KernelWrapper::Make(context, kernelSize, kernel);
389 if (!kw.isValid()) {
390 return nullptr;
391 }
Brian Salomon694ec492020-04-14 13:39:31 -0400392 GrSamplerState sampler(wm, GrSamplerState::Filter::kNearest);
393 auto child = GrTextureEffect::MakeSubset(std::move(srcView), kPremul_SkAlphaType, SkMatrix::I(),
394 sampler, SkRect::Make(srcBounds), caps);
395 return std::unique_ptr<GrFragmentProcessor>(new GrMatrixConvolutionEffect(
Adlai Holler1ed43912020-05-04 13:02:28 -0400396 std::move(child), std::move(kw), gain, bias, kernelOffset, convolveAlpha));
Brian Salomon694ec492020-04-14 13:39:31 -0400397}
398
Brian Salomonaff329b2017-08-11 09:40:37 -0400399std::unique_ptr<GrFragmentProcessor> GrMatrixConvolutionEffect::MakeGaussian(
Adlai Holler1ed43912020-05-04 13:02:28 -0400400 GrRecordingContext* context,
Greg Daniel5c082492020-01-29 15:06:49 -0500401 GrSurfaceProxyView srcView,
Robert Phillips5140f9a2018-05-11 16:11:45 -0400402 const SkIRect& srcBounds,
Brian Salomonaff329b2017-08-11 09:40:37 -0400403 const SkISize& kernelSize,
404 SkScalar gain,
405 SkScalar bias,
406 const SkIPoint& kernelOffset,
Brian Salomon694ec492020-04-14 13:39:31 -0400407 GrSamplerState::WrapMode wm,
Brian Salomonaff329b2017-08-11 09:40:37 -0400408 bool convolveAlpha,
409 SkScalar sigmaX,
Brian Salomon694ec492020-04-14 13:39:31 -0400410 SkScalar sigmaY,
411 const GrCaps& caps) {
Adlai Holler1ed43912020-05-04 13:02:28 -0400412 SkAutoSTMalloc<32, float> kernel(kernelSize.area());
413 fill_in_2D_gaussian_kernel(kernel.get(), kernelSize.width(), kernelSize.height(),
414 sigmaX, sigmaY);
415 return Make(context, std::move(srcView), srcBounds, kernelSize, kernel.get(),
416 gain, bias, kernelOffset, wm, convolveAlpha, caps);
Robert Phillips40fd7c92017-01-30 08:06:27 -0500417}
418
joshualittb0a8a372014-09-23 09:50:21 -0700419GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrMatrixConvolutionEffect);
joshualittac977922014-07-22 09:52:11 -0700420
Hal Canary6f6961e2017-01-31 13:50:44 -0500421#if GR_TEST_UTILS
Brian Salomonaff329b2017-08-11 09:40:37 -0400422std::unique_ptr<GrFragmentProcessor> GrMatrixConvolutionEffect::TestCreate(GrProcessorTestData* d) {
Greg Daniel026a60c2020-02-12 10:53:51 -0500423 auto [view, ct, at] = d->randomView();
Robert Phillipsdbc8eeb2017-02-21 10:04:31 -0500424
Adlai Holler1ed43912020-05-04 13:02:28 -0400425 static constexpr size_t kMaxTestKernelSize = 2 * kMaxUniformSize;
426 int width = d->fRandom->nextRangeU(1, kMaxTestKernelSize);
427 int height = d->fRandom->nextRangeU(1, kMaxTestKernelSize / width);
joshualittac977922014-07-22 09:52:11 -0700428 SkISize kernelSize = SkISize::Make(width, height);
Ben Wagner7ecc5962016-11-02 17:07:33 -0400429 std::unique_ptr<SkScalar[]> kernel(new SkScalar[width * height]);
joshualittac977922014-07-22 09:52:11 -0700430 for (int i = 0; i < width * height; i++) {
joshualitt0067ff52015-07-08 14:26:19 -0700431 kernel.get()[i] = d->fRandom->nextSScalar1();
joshualittac977922014-07-22 09:52:11 -0700432 }
joshualitt0067ff52015-07-08 14:26:19 -0700433 SkScalar gain = d->fRandom->nextSScalar1();
434 SkScalar bias = d->fRandom->nextSScalar1();
Greg Daniel026a60c2020-02-12 10:53:51 -0500435
436 uint32_t kernalOffsetX = d->fRandom->nextRangeU(0, kernelSize.width());
437 uint32_t kernalOffsetY = d->fRandom->nextRangeU(0, kernelSize.height());
438 SkIPoint kernelOffset = SkIPoint::Make(kernalOffsetX, kernalOffsetY);
439
440 uint32_t boundsX = d->fRandom->nextRangeU(0, view.width());
441 uint32_t boundsY = d->fRandom->nextRangeU(0, view.height());
442 uint32_t boundsW = d->fRandom->nextRangeU(0, view.width());
443 uint32_t boundsH = d->fRandom->nextRangeU(0, view.height());
444 SkIRect bounds = SkIRect::MakeXYWH(boundsX, boundsY, boundsW, boundsH);
445
Brian Salomon694ec492020-04-14 13:39:31 -0400446 auto wm = static_cast<GrSamplerState::WrapMode>(
447 d->fRandom->nextULessThan(GrSamplerState::kWrapModeCount));
joshualitt0067ff52015-07-08 14:26:19 -0700448 bool convolveAlpha = d->fRandom->nextBool();
Adlai Holler1ed43912020-05-04 13:02:28 -0400449 return GrMatrixConvolutionEffect::Make(d->context()->priv().asRecordingContext(),
450 std::move(view),
bungeman06ca8ec2016-06-09 08:01:03 -0700451 bounds,
452 kernelSize,
453 kernel.get(),
454 gain,
455 bias,
456 kernelOffset,
Brian Salomon694ec492020-04-14 13:39:31 -0400457 wm,
458 convolveAlpha,
459 *d->caps());
joshualittac977922014-07-22 09:52:11 -0700460}
Hal Canary6f6961e2017-01-31 13:50:44 -0500461#endif