blob: 68ba0af0d49a4cfc79068f9226a90d7679e34bec [file] [log] [blame]
robertphillips30c4cae2015-09-15 10:20:55 -07001/*
Ethan Nicholasceb4d482017-07-10 15:40:20 -04002 * Copyright 2017 Google Inc.
robertphillips30c4cae2015-09-15 10:20:55 -07003 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Ethan Nicholasceb4d482017-07-10 15:40:20 -04008/*
Brian Salomoncda20152017-07-17 16:44:32 -04009 * This file was autogenerated from GrCircleBlurFragmentProcessor.fp; do not modify.
Ethan Nicholasceb4d482017-07-10 15:40:20 -040010 */
robertphillips30c4cae2015-09-15 10:20:55 -070011#include "GrCircleBlurFragmentProcessor.h"
robertphillips30c4cae2015-09-15 10:20:55 -070012#if SK_SUPPORT_GPU
13
Ethan Nicholasb7e8c3b2017-07-19 13:54:20 -040014#include "GrResourceProvider.h"
Ethan Nicholas818ac5a2017-07-10 13:12:07 +000015
Ethan Nicholasb7e8c3b2017-07-19 13:54:20 -040016static float make_unnormalized_half_kernel(float* halfKernel, int halfKernelSize, float sigma) {
17 const float invSigma = 1.f / sigma;
18 const float b = -0.5f * invSigma * invSigma;
19 float tot = 0.0f;
20
21 float t = 0.5f;
22 for (int i = 0; i < halfKernelSize; ++i) {
23 float value = expf(t * t * b);
24 tot += value;
25 halfKernel[i] = value;
26 t += 1.f;
Ethan Nicholasceb4d482017-07-10 15:40:20 -040027 }
Ethan Nicholasb7e8c3b2017-07-19 13:54:20 -040028 return tot;
29}
Ethan Nicholasceb4d482017-07-10 15:40:20 -040030
Ethan Nicholasb7e8c3b2017-07-19 13:54:20 -040031static void make_half_kernel_and_summed_table(float* halfKernel, float* summedHalfKernel,
32 int halfKernelSize, float sigma) {
33 const float tot = 2.f * make_unnormalized_half_kernel(halfKernel, halfKernelSize, sigma);
34 float sum = 0.f;
35 for (int i = 0; i < halfKernelSize; ++i) {
36 halfKernel[i] /= tot;
37 sum += halfKernel[i];
38 summedHalfKernel[i] = sum;
Ethan Nicholasceb4d482017-07-10 15:40:20 -040039 }
Ethan Nicholasb7e8c3b2017-07-19 13:54:20 -040040}
Ethan Nicholasceb4d482017-07-10 15:40:20 -040041
Ethan Nicholasb7e8c3b2017-07-19 13:54:20 -040042void apply_kernel_in_y(float* results, int numSteps, float firstX, float circleR,
43 int halfKernelSize, const float* summedHalfKernelTable) {
44 float x = firstX;
45 for (int i = 0; i < numSteps; ++i, x += 1.f) {
46 if (x < -circleR || x > circleR) {
47 results[i] = 0;
48 continue;
Brian Salomoncda20152017-07-17 16:44:32 -040049 }
Ethan Nicholasb7e8c3b2017-07-19 13:54:20 -040050 float y = sqrtf(circleR * circleR - x * x);
Ethan Nicholasceb4d482017-07-10 15:40:20 -040051
Ethan Nicholasb7e8c3b2017-07-19 13:54:20 -040052 y -= 0.5f;
53 int yInt = SkScalarFloorToInt(y);
54 SkASSERT(yInt >= -1);
55 if (y < 0) {
56 results[i] = (y + 0.5f) * summedHalfKernelTable[0];
57 } else if (yInt >= halfKernelSize - 1) {
58 results[i] = 0.5f;
Brian Salomoncda20152017-07-17 16:44:32 -040059 } else {
Ethan Nicholasb7e8c3b2017-07-19 13:54:20 -040060 float yFrac = y - yInt;
61 results[i] = (1.f - yFrac) * summedHalfKernelTable[yInt] +
62 yFrac * summedHalfKernelTable[yInt + 1];
Brian Salomoncda20152017-07-17 16:44:32 -040063 }
Ethan Nicholasb7e8c3b2017-07-19 13:54:20 -040064 }
65}
Brian Salomoncda20152017-07-17 16:44:32 -040066
Ethan Nicholasb7e8c3b2017-07-19 13:54:20 -040067static uint8_t eval_at(float evalX, float circleR, const float* halfKernel, int halfKernelSize,
68 const float* yKernelEvaluations) {
69 float acc = 0;
Brian Salomoncda20152017-07-17 16:44:32 -040070
Ethan Nicholasb7e8c3b2017-07-19 13:54:20 -040071 float x = evalX - halfKernelSize;
72 for (int i = 0; i < halfKernelSize; ++i, x += 1.f) {
73 if (x < -circleR || x > circleR) {
74 continue;
Brian Salomoncda20152017-07-17 16:44:32 -040075 }
Ethan Nicholasb7e8c3b2017-07-19 13:54:20 -040076 float verticalEval = yKernelEvaluations[i];
77 acc += verticalEval * halfKernel[halfKernelSize - i - 1];
78 }
79 for (int i = 0; i < halfKernelSize; ++i, x += 1.f) {
80 if (x < -circleR || x > circleR) {
81 continue;
82 }
83 float verticalEval = yKernelEvaluations[i + halfKernelSize];
84 acc += verticalEval * halfKernel[i];
Brian Salomoncda20152017-07-17 16:44:32 -040085 }
86
Ethan Nicholasb7e8c3b2017-07-19 13:54:20 -040087 return SkUnitScalarClampToByte(2.f * acc);
88}
89
90static uint8_t* create_circle_profile(float sigma, float circleR, int profileTextureWidth) {
91 const int numSteps = profileTextureWidth;
92 uint8_t* weights = new uint8_t[numSteps];
93
94 int halfKernelSize = SkScalarCeilToInt(6.0f * sigma);
95
96 halfKernelSize = ((halfKernelSize + 1) & ~1) >> 1;
97
98 int numYSteps = numSteps + 2 * halfKernelSize;
99
100 SkAutoTArray<float> bulkAlloc(halfKernelSize + halfKernelSize + numYSteps);
101 float* halfKernel = bulkAlloc.get();
102 float* summedKernel = bulkAlloc.get() + halfKernelSize;
103 float* yEvals = bulkAlloc.get() + 2 * halfKernelSize;
104 make_half_kernel_and_summed_table(halfKernel, summedKernel, halfKernelSize, sigma);
105
106 float firstX = -halfKernelSize + 0.5f;
107 apply_kernel_in_y(yEvals, numYSteps, firstX, circleR, halfKernelSize, summedKernel);
108
109 for (int i = 0; i < numSteps - 1; ++i) {
110 float evalX = i + 0.5f;
111 weights[i] = eval_at(evalX, circleR, halfKernel, halfKernelSize, yEvals + i);
112 }
113
114 weights[numSteps - 1] = 0;
115 return weights;
116}
117
118static uint8_t* create_half_plane_profile(int profileWidth) {
119 SkASSERT(!(profileWidth & 0x1));
120
121 float sigma = profileWidth / 6.f;
122 int halfKernelSize = profileWidth / 2;
123
124 SkAutoTArray<float> halfKernel(halfKernelSize);
125 uint8_t* profile = new uint8_t[profileWidth];
126
127 const float tot = 2.f * make_unnormalized_half_kernel(halfKernel.get(), halfKernelSize, sigma);
128 float sum = 0.f;
129
130 for (int i = 0; i < halfKernelSize; ++i) {
131 halfKernel[halfKernelSize - i - 1] /= tot;
132 sum += halfKernel[halfKernelSize - i - 1];
133 profile[profileWidth - i - 1] = SkUnitScalarClampToByte(sum);
134 }
135
136 for (int i = 0; i < halfKernelSize; ++i) {
137 sum += halfKernel[i];
138 profile[halfKernelSize - i - 1] = SkUnitScalarClampToByte(sum);
139 }
140
141 profile[profileWidth - 1] = 0;
142 return profile;
143}
144
145static sk_sp<GrTextureProxy> create_profile_texture(GrResourceProvider* resourceProvider,
146 const SkRect& circle, float sigma,
147 float* solidRadius, float* textureRadius) {
148 float circleR = circle.width() / 2.0f;
149
150 SkScalar sigmaToCircleRRatio = sigma / circleR;
151
152 sigmaToCircleRRatio = SkTMin(sigmaToCircleRRatio, 8.f);
153 SkFixed sigmaToCircleRRatioFixed;
154 static const SkScalar kHalfPlaneThreshold = 0.1f;
155 bool useHalfPlaneApprox = false;
156 if (sigmaToCircleRRatio <= kHalfPlaneThreshold) {
157 useHalfPlaneApprox = true;
158 sigmaToCircleRRatioFixed = 0;
159 *solidRadius = circleR - 3 * sigma;
160 *textureRadius = 6 * sigma;
161 } else {
162 sigmaToCircleRRatioFixed = SkScalarToFixed(sigmaToCircleRRatio);
163
164 sigmaToCircleRRatioFixed &= ~0xff;
165 sigmaToCircleRRatio = SkFixedToScalar(sigmaToCircleRRatioFixed);
166 sigma = circleR * sigmaToCircleRRatio;
167 *solidRadius = 0;
168 *textureRadius = circleR + 3 * sigma;
169 }
170
171 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
172 GrUniqueKey key;
173 GrUniqueKey::Builder builder(&key, kDomain, 1);
174 builder[0] = sigmaToCircleRRatioFixed;
175 builder.finish();
176
Ethan Nicholas480c90a2017-07-25 16:45:15 -0400177 sk_sp<GrTextureProxy> blurProfile =
178 resourceProvider->findProxyByUniqueKey(key, kTopLeft_GrSurfaceOrigin);
Ethan Nicholasb7e8c3b2017-07-19 13:54:20 -0400179 if (!blurProfile) {
180 static constexpr int kProfileTextureWidth = 512;
181 GrSurfaceDesc texDesc;
Robert Phillipsc686ce32017-07-21 14:12:29 -0400182 texDesc.fOrigin = kTopLeft_GrSurfaceOrigin;
Ethan Nicholasb7e8c3b2017-07-19 13:54:20 -0400183 texDesc.fWidth = kProfileTextureWidth;
184 texDesc.fHeight = 1;
185 texDesc.fConfig = kAlpha_8_GrPixelConfig;
186
187 std::unique_ptr<uint8_t[]> profile(nullptr);
188 if (useHalfPlaneApprox) {
189 profile.reset(create_half_plane_profile(kProfileTextureWidth));
190 } else {
191 SkScalar scale = kProfileTextureWidth / *textureRadius;
192 profile.reset(
193 create_circle_profile(sigma * scale, circleR * scale, kProfileTextureWidth));
194 }
195
196 blurProfile = GrSurfaceProxy::MakeDeferred(resourceProvider, texDesc, SkBudgeted::kYes,
197 profile.get(), 0);
198 if (!blurProfile) {
Brian Salomoncda20152017-07-17 16:44:32 -0400199 return nullptr;
200 }
Ethan Nicholasb7e8c3b2017-07-19 13:54:20 -0400201
Robert Phillipsc686ce32017-07-21 14:12:29 -0400202 SkASSERT(blurProfile->origin() == kTopLeft_GrSurfaceOrigin);
Ethan Nicholasb7e8c3b2017-07-19 13:54:20 -0400203 resourceProvider->assignUniqueKeyToProxy(key, blurProfile.get());
Brian Salomoncda20152017-07-17 16:44:32 -0400204 }
Ethan Nicholasb7e8c3b2017-07-19 13:54:20 -0400205
206 return blurProfile;
207}
208
209sk_sp<GrFragmentProcessor> GrCircleBlurFragmentProcessor::Make(GrResourceProvider* resourceProvider,
210 const SkRect& circle,
211 float sigma) {
212 float solidRadius;
213 float textureRadius;
214 sk_sp<GrTextureProxy> profile(
215 create_profile_texture(resourceProvider, circle, sigma, &solidRadius, &textureRadius));
216 if (!profile) {
217 return nullptr;
218 }
219 return sk_sp<GrFragmentProcessor>(new GrCircleBlurFragmentProcessor(
220 circle, textureRadius, solidRadius, std::move(profile), resourceProvider));
221}
Ethan Nicholasceb4d482017-07-10 15:40:20 -0400222#include "glsl/GrGLSLColorSpaceXformHelper.h"
223#include "glsl/GrGLSLFragmentProcessor.h"
224#include "glsl/GrGLSLFragmentShaderBuilder.h"
225#include "glsl/GrGLSLProgramBuilder.h"
226#include "SkSLCPP.h"
227#include "SkSLUtil.h"
228class GrGLSLCircleBlurFragmentProcessor : public GrGLSLFragmentProcessor {
Brian Salomoncda20152017-07-17 16:44:32 -0400229public:
230 GrGLSLCircleBlurFragmentProcessor() {}
231 void emitCode(EmitArgs& args) override {
232 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
Ethan Nicholasb7e8c3b2017-07-19 13:54:20 -0400233 const GrCircleBlurFragmentProcessor& _outer =
234 args.fFp.cast<GrCircleBlurFragmentProcessor>();
235 (void)_outer;
236 fCircleDataVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kVec4f_GrSLType,
237 kDefault_GrSLPrecision, "circleData");
238 fragBuilder->codeAppendf(
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400239 "float2 vec = float2((sk_FragCoord.x - %s.x) * %s.w, (sk_FragCoord.y - %s.y) * "
Ethan Nicholasb7e8c3b2017-07-19 13:54:20 -0400240 "%s.w);\nfloat dist = length(vec) + (0.5 - %s.z) * %s.w;\n%s = %s * texture(%s, "
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400241 "float2(dist, 0.5)).%s.w;\n",
Ethan Nicholasb7e8c3b2017-07-19 13:54:20 -0400242 args.fUniformHandler->getUniformCStr(fCircleDataVar),
243 args.fUniformHandler->getUniformCStr(fCircleDataVar),
244 args.fUniformHandler->getUniformCStr(fCircleDataVar),
245 args.fUniformHandler->getUniformCStr(fCircleDataVar),
246 args.fUniformHandler->getUniformCStr(fCircleDataVar),
247 args.fUniformHandler->getUniformCStr(fCircleDataVar), args.fOutputColor,
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400248 args.fInputColor ? args.fInputColor : "float4(1)",
Ethan Nicholasb7e8c3b2017-07-19 13:54:20 -0400249 fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[0]).c_str(),
250 fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[0]).c_str());
Brian Salomoncda20152017-07-17 16:44:32 -0400251 }
Ethan Nicholasb7e8c3b2017-07-19 13:54:20 -0400252
Brian Salomoncda20152017-07-17 16:44:32 -0400253private:
Ethan Nicholasb7e8c3b2017-07-19 13:54:20 -0400254 void onSetData(const GrGLSLProgramDataManager& data,
255 const GrFragmentProcessor& _proc) override {
Brian Salomoncda20152017-07-17 16:44:32 -0400256 const GrCircleBlurFragmentProcessor& _outer = _proc.cast<GrCircleBlurFragmentProcessor>();
257 auto circleRect = _outer.circleRect();
Ethan Nicholasb7e8c3b2017-07-19 13:54:20 -0400258 (void)circleRect;
Brian Salomoncda20152017-07-17 16:44:32 -0400259 auto textureRadius = _outer.textureRadius();
Ethan Nicholasb7e8c3b2017-07-19 13:54:20 -0400260 (void)textureRadius;
Brian Salomoncda20152017-07-17 16:44:32 -0400261 auto solidRadius = _outer.solidRadius();
Ethan Nicholasb7e8c3b2017-07-19 13:54:20 -0400262 (void)solidRadius;
Brian Salomoncda20152017-07-17 16:44:32 -0400263 UniformHandle& blurProfileSampler = fBlurProfileSamplerVar;
Ethan Nicholasb7e8c3b2017-07-19 13:54:20 -0400264 (void)blurProfileSampler;
Brian Salomoncda20152017-07-17 16:44:32 -0400265 UniformHandle& circleData = fCircleDataVar;
Ethan Nicholasb7e8c3b2017-07-19 13:54:20 -0400266 (void)circleData;
Ethan Nicholasceb4d482017-07-10 15:40:20 -0400267
Ethan Nicholasb7e8c3b2017-07-19 13:54:20 -0400268 data.set4f(circleData, circleRect.centerX(), circleRect.centerY(), solidRadius,
269 1.f / textureRadius);
Brian Salomoncda20152017-07-17 16:44:32 -0400270 }
271 UniformHandle fCircleDataVar;
272 UniformHandle fBlurProfileSamplerVar;
Ethan Nicholasceb4d482017-07-10 15:40:20 -0400273};
Brian Salomoncda20152017-07-17 16:44:32 -0400274GrGLSLFragmentProcessor* GrCircleBlurFragmentProcessor::onCreateGLSLInstance() const {
275 return new GrGLSLCircleBlurFragmentProcessor();
Ethan Nicholas818ac5a2017-07-10 13:12:07 +0000276}
Ethan Nicholasb7e8c3b2017-07-19 13:54:20 -0400277void GrCircleBlurFragmentProcessor::onGetGLSLProcessorKey(const GrShaderCaps& caps,
278 GrProcessorKeyBuilder* b) const {}
Brian Salomoncda20152017-07-17 16:44:32 -0400279bool GrCircleBlurFragmentProcessor::onIsEqual(const GrFragmentProcessor& other) const {
280 const GrCircleBlurFragmentProcessor& that = other.cast<GrCircleBlurFragmentProcessor>();
Ethan Nicholasb7e8c3b2017-07-19 13:54:20 -0400281 (void)that;
Brian Salomoncda20152017-07-17 16:44:32 -0400282 if (fCircleRect != that.fCircleRect) return false;
283 if (fTextureRadius != that.fTextureRadius) return false;
284 if (fSolidRadius != that.fSolidRadius) return false;
285 if (fBlurProfileSampler != that.fBlurProfileSampler) return false;
286 return true;
Ethan Nicholasceb4d482017-07-10 15:40:20 -0400287}
Ethan Nicholasf57c0d62017-07-31 11:18:22 -0400288GrCircleBlurFragmentProcessor::GrCircleBlurFragmentProcessor(
289 const GrCircleBlurFragmentProcessor& src)
290 : INHERITED(src.optimizationFlags())
291 , fCircleRect(src.fCircleRect)
292 , fTextureRadius(src.fTextureRadius)
293 , fSolidRadius(src.fSolidRadius)
294 , fBlurProfileSampler(src.fBlurProfileSampler) {
295 this->initClassID<GrCircleBlurFragmentProcessor>();
296 this->addTextureSampler(&fBlurProfileSampler);
297}
298sk_sp<GrFragmentProcessor> GrCircleBlurFragmentProcessor::clone() const {
299 return sk_sp<GrFragmentProcessor>(new GrCircleBlurFragmentProcessor(*this));
300}
Ethan Nicholas818ac5a2017-07-10 13:12:07 +0000301GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrCircleBlurFragmentProcessor);
Ethan Nicholas818ac5a2017-07-10 13:12:07 +0000302#if GR_TEST_UTILS
Ethan Nicholasb7e8c3b2017-07-19 13:54:20 -0400303sk_sp<GrFragmentProcessor> GrCircleBlurFragmentProcessor::TestCreate(
304 GrProcessorTestData* testData) {
Brian Salomoncda20152017-07-17 16:44:32 -0400305 SkScalar wh = testData->fRandom->nextRangeScalar(100.f, 1000.f);
Ethan Nicholasb7e8c3b2017-07-19 13:54:20 -0400306 SkScalar sigma = testData->fRandom->nextRangeF(1.f, 10.f);
Brian Salomoncda20152017-07-17 16:44:32 -0400307 SkRect circle = SkRect::MakeWH(wh, wh);
308 return GrCircleBlurFragmentProcessor::Make(testData->resourceProvider(), circle, sigma);
robertphillips30c4cae2015-09-15 10:20:55 -0700309}
Hal Canary6f6961e2017-01-31 13:50:44 -0500310#endif
robertphillips30c4cae2015-09-15 10:20:55 -0700311#endif