GrCircleBlurFragmentProcessor.cpp revision ceb4d48ef4839aab9d99d0200dcfe403ccd0cdf3
1/*
2 * Copyright 2017 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/*
9 * This file was autogenerated from GrCircleBlurFragmentProcessor.fp; do not modify.
10 */
11#include "GrCircleBlurFragmentProcessor.h"
12#if SK_SUPPORT_GPU
13
14    #include "GrResourceProvider.h"
15
16
17
18    static float make_unnormalized_half_kernel(float* halfKernel, int halfKernelSize, float sigma) {
19        const float invSigma = 1.f / sigma;
20        const float b = -0.5f * invSigma * invSigma;
21        float tot = 0.0f;
22
23        float t = 0.5f;
24        for (int i = 0; i < halfKernelSize; ++i) {
25            float value = expf(t * t * b);
26            tot += value;
27            halfKernel[i] = value;
28            t += 1.f;
29        }
30        return tot;
31    }
32
33
34
35    static void make_half_kernel_and_summed_table(float* halfKernel, float* summedHalfKernel,
36                                                  int halfKernelSize, float sigma) {
37
38        const float tot = 2.f * make_unnormalized_half_kernel(halfKernel, halfKernelSize, sigma);
39        float sum = 0.f;
40        for (int i = 0; i < halfKernelSize; ++i) {
41            halfKernel[i] /= tot;
42            sum += halfKernel[i];
43            summedHalfKernel[i] = sum;
44        }
45    }
46
47
48
49    void apply_kernel_in_y(float* results, int numSteps, float firstX, float circleR,
50                           int halfKernelSize, const float* summedHalfKernelTable) {
51        float x = firstX;
52        for (int i = 0; i < numSteps; ++i, x += 1.f) {
53            if (x < -circleR || x > circleR) {
54                results[i] = 0;
55                continue;
56            }
57            float y = sqrtf(circleR * circleR - x * x);
58
59
60            y -= 0.5f;
61            int yInt = SkScalarFloorToInt(y);
62            SkASSERT(yInt >= -1);
63            if (y < 0) {
64                results[i] = (y + 0.5f) * summedHalfKernelTable[0];
65            } else if (yInt >= halfKernelSize - 1) {
66                results[i] = 0.5f;
67            } else {
68                float yFrac = y - yInt;
69                results[i] = (1.f - yFrac) * summedHalfKernelTable[yInt] +
70                             yFrac * summedHalfKernelTable[yInt + 1];
71            }
72        }
73    }
74
75
76
77
78
79    static uint8_t eval_at(float evalX, float circleR, const float* halfKernel, int halfKernelSize,
80                           const float* yKernelEvaluations) {
81        float acc = 0;
82
83        float x = evalX - halfKernelSize;
84        for (int i = 0; i < halfKernelSize; ++i, x += 1.f) {
85            if (x < -circleR || x > circleR) {
86                continue;
87            }
88            float verticalEval = yKernelEvaluations[i];
89            acc += verticalEval * halfKernel[halfKernelSize - i - 1];
90        }
91        for (int i = 0; i < halfKernelSize; ++i, x += 1.f) {
92            if (x < -circleR || x > circleR) {
93                continue;
94            }
95            float verticalEval = yKernelEvaluations[i + halfKernelSize];
96            acc += verticalEval * halfKernel[i];
97        }
98
99
100        return SkUnitScalarClampToByte(2.f * acc);
101    }
102
103
104
105
106
107
108
109
110    static uint8_t* create_circle_profile(float sigma, float circleR, int profileTextureWidth) {
111        const int numSteps = profileTextureWidth;
112        uint8_t* weights = new uint8_t[numSteps];
113
114
115        int halfKernelSize = SkScalarCeilToInt(6.0f*sigma);
116
117        halfKernelSize = ((halfKernelSize + 1) & ~1) >> 1;
118
119
120        int numYSteps = numSteps + 2 * halfKernelSize;
121
122        SkAutoTArray<float> bulkAlloc(halfKernelSize + halfKernelSize + numYSteps);
123        float* halfKernel = bulkAlloc.get();
124        float* summedKernel = bulkAlloc.get() + halfKernelSize;
125        float* yEvals = bulkAlloc.get() + 2 * halfKernelSize;
126        make_half_kernel_and_summed_table(halfKernel, summedKernel, halfKernelSize, sigma);
127
128        float firstX = -halfKernelSize + 0.5f;
129        apply_kernel_in_y(yEvals, numYSteps, firstX, circleR, halfKernelSize, summedKernel);
130
131        for (int i = 0; i < numSteps - 1; ++i) {
132            float evalX = i + 0.5f;
133            weights[i] = eval_at(evalX, circleR, halfKernel, halfKernelSize, yEvals + i);
134        }
135
136        weights[numSteps - 1] = 0;
137        return weights;
138    }
139
140    static uint8_t* create_half_plane_profile(int profileWidth) {
141        SkASSERT(!(profileWidth & 0x1));
142
143        float sigma = profileWidth / 6.f;
144        int halfKernelSize = profileWidth / 2;
145
146        SkAutoTArray<float> halfKernel(halfKernelSize);
147        uint8_t* profile = new uint8_t[profileWidth];
148
149
150        const float tot = 2.f * make_unnormalized_half_kernel(halfKernel.get(), halfKernelSize,
151                                                              sigma);
152        float sum = 0.f;
153
154        for (int i = 0; i < halfKernelSize; ++i) {
155            halfKernel[halfKernelSize - i - 1] /= tot;
156            sum += halfKernel[halfKernelSize - i - 1];
157            profile[profileWidth - i - 1] = SkUnitScalarClampToByte(sum);
158        }
159
160
161        for (int i = 0; i < halfKernelSize; ++i) {
162            sum += halfKernel[i];
163            profile[halfKernelSize - i - 1] = SkUnitScalarClampToByte(sum);
164        }
165
166        profile[profileWidth - 1] = 0;
167        return profile;
168    }
169
170    static sk_sp<GrTextureProxy> create_profile_texture(GrResourceProvider* resourceProvider,
171                                                        const SkRect& circle,
172                                                        float sigma,
173                                                        float* solidRadius, float* textureRadius) {
174        float circleR = circle.width() / 2.0f;
175
176
177        SkScalar sigmaToCircleRRatio = sigma / circleR;
178
179
180
181
182        sigmaToCircleRRatio = SkTMin(sigmaToCircleRRatio, 8.f);
183        SkFixed sigmaToCircleRRatioFixed;
184        static const SkScalar kHalfPlaneThreshold = 0.1f;
185        bool useHalfPlaneApprox = false;
186        if (sigmaToCircleRRatio <= kHalfPlaneThreshold) {
187            useHalfPlaneApprox = true;
188            sigmaToCircleRRatioFixed = 0;
189            *solidRadius = circleR - 3 * sigma;
190            *textureRadius = 6 * sigma;
191        } else {
192
193            sigmaToCircleRRatioFixed = SkScalarToFixed(sigmaToCircleRRatio);
194
195
196            sigmaToCircleRRatioFixed &= ~0xff;
197            sigmaToCircleRRatio = SkFixedToScalar(sigmaToCircleRRatioFixed);
198            sigma = circleR * sigmaToCircleRRatio;
199            *solidRadius = 0;
200            *textureRadius = circleR + 3 * sigma;
201        }
202
203        static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
204        GrUniqueKey key;
205        GrUniqueKey::Builder builder(&key, kDomain, 1);
206        builder[0] = sigmaToCircleRRatioFixed;
207        builder.finish();
208
209        sk_sp<GrTextureProxy> blurProfile = resourceProvider->findProxyByUniqueKey(key);
210        if (!blurProfile) {
211            static constexpr int kProfileTextureWidth = 512;
212            GrSurfaceDesc texDesc;
213            texDesc.fWidth = kProfileTextureWidth;
214            texDesc.fHeight = 1;
215            texDesc.fConfig = kAlpha_8_GrPixelConfig;
216
217            std::unique_ptr<uint8_t[]> profile(nullptr);
218            if (useHalfPlaneApprox) {
219                profile.reset(create_half_plane_profile(kProfileTextureWidth));
220            } else {
221
222                SkScalar scale = kProfileTextureWidth / *textureRadius;
223                profile.reset(create_circle_profile(sigma * scale, circleR * scale,
224                                                    kProfileTextureWidth));
225            }
226
227            blurProfile = GrSurfaceProxy::MakeDeferred(resourceProvider,
228                                                       texDesc, SkBudgeted::kYes, profile.get(), 0);
229            if (!blurProfile) {
230                return nullptr;
231            }
232
233            resourceProvider->assignUniqueKeyToProxy(key, blurProfile.get());
234        }
235
236        return blurProfile;
237    }
238
239    sk_sp<GrFragmentProcessor> GrCircleBlurFragmentProcessor::Make(
240                                                               GrResourceProvider* resourceProvider,
241                                                               const SkRect& circle,
242                                                               float sigma) {
243        float solidRadius;
244        float textureRadius;
245        sk_sp<GrTextureProxy> profile(create_profile_texture(resourceProvider, circle, sigma,
246                                                             &solidRadius, &textureRadius));
247        if (!profile) {
248            return nullptr;
249        }
250        return sk_sp<GrFragmentProcessor>(new GrCircleBlurFragmentProcessor(circle,
251                                                                            textureRadius,
252                                                                            solidRadius,
253                                                                            std::move(profile),
254                                                                            resourceProvider));
255    }
256#include "glsl/GrGLSLColorSpaceXformHelper.h"
257#include "glsl/GrGLSLFragmentProcessor.h"
258#include "glsl/GrGLSLFragmentShaderBuilder.h"
259#include "glsl/GrGLSLProgramBuilder.h"
260#include "SkSLCPP.h"
261#include "SkSLUtil.h"
262class GrGLSLCircleBlurFragmentProcessor : public GrGLSLFragmentProcessor {
263public:
264    GrGLSLCircleBlurFragmentProcessor() {}
265    void emitCode(EmitArgs& args) override {
266        GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
267        const GrCircleBlurFragmentProcessor& _outer = args.fFp.cast<GrCircleBlurFragmentProcessor>();
268        (void) _outer;
269        fCircleDataVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kVec4f_GrSLType, kDefault_GrSLPrecision, "circleData");
270        fragBuilder->codeAppendf("vec2 vec = vec2((sk_FragCoord.x - %s.x) * %s.w, (sk_FragCoord.y - %s.y) * %s.w);\nfloat dist = length(vec) + (0.5 - %s.z) * %s.w;\n%s = %s * texture(%s, vec2(dist, 0.5)).%s.w;\n", args.fUniformHandler->getUniformCStr(fCircleDataVar), args.fUniformHandler->getUniformCStr(fCircleDataVar), args.fUniformHandler->getUniformCStr(fCircleDataVar), args.fUniformHandler->getUniformCStr(fCircleDataVar), args.fUniformHandler->getUniformCStr(fCircleDataVar), args.fUniformHandler->getUniformCStr(fCircleDataVar), args.fOutputColor, args.fInputColor ? args.fInputColor : "vec4(1)", fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[0]).c_str(), fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[0]).c_str());
271    }
272private:
273    void onSetData(const GrGLSLProgramDataManager& data, const GrFragmentProcessor& _proc) override {
274        const GrCircleBlurFragmentProcessor& _outer = _proc.cast<GrCircleBlurFragmentProcessor>();
275        auto circleRect = _outer.circleRect();
276        (void) circleRect;
277        auto textureRadius = _outer.textureRadius();
278        (void) textureRadius;
279        auto solidRadius = _outer.solidRadius();
280        (void) solidRadius;
281        UniformHandle& blurProfileSampler = fBlurProfileSamplerVar;
282        (void) blurProfileSampler;
283        UniformHandle& circleData = fCircleDataVar;
284        (void) circleData;
285
286    data.set4f(circleData, circleRect.centerX(), circleRect.centerY(), solidRadius,
287               1.f / textureRadius);
288    }
289    UniformHandle fCircleDataVar;
290    UniformHandle fBlurProfileSamplerVar;
291};
292GrGLSLFragmentProcessor* GrCircleBlurFragmentProcessor::onCreateGLSLInstance() const {
293    return new GrGLSLCircleBlurFragmentProcessor();
294}
295void GrCircleBlurFragmentProcessor::onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const {
296}
297bool GrCircleBlurFragmentProcessor::onIsEqual(const GrFragmentProcessor& other) const {
298    const GrCircleBlurFragmentProcessor& that = other.cast<GrCircleBlurFragmentProcessor>();
299    (void) that;
300    if (fCircleRect != that.fCircleRect) return false;
301    if (fTextureRadius != that.fTextureRadius) return false;
302    if (fSolidRadius != that.fSolidRadius) return false;
303    if (fBlurProfileSampler != that.fBlurProfileSampler) return false;
304    return true;
305}
306GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrCircleBlurFragmentProcessor);
307#if GR_TEST_UTILS
308sk_sp<GrFragmentProcessor> GrCircleBlurFragmentProcessor::TestCreate(GrProcessorTestData* testData) {
309
310    SkScalar wh = testData->fRandom->nextRangeScalar(100.f, 1000.f);
311    SkScalar sigma = testData->fRandom->nextRangeF(1.f,10.f);
312    SkRect circle = SkRect::MakeWH(wh, wh);
313    return GrCircleBlurFragmentProcessor::Make(testData->resourceProvider(), circle, sigma);
314}
315#endif
316#endif
317