GrCircleBlurFragmentProcessor.cpp revision 96be9df1300b2281641b038ef11064157f6e0b41
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
16static 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;
27    }
28    return tot;
29}
30
31static 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;
39    }
40}
41
42void 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;
49        }
50        float y = sqrtf(circleR * circleR - x * x);
51
52        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;
59        } else {
60            float yFrac = y - yInt;
61            results[i] = (1.f - yFrac) * summedHalfKernelTable[yInt] +
62                         yFrac * summedHalfKernelTable[yInt + 1];
63        }
64    }
65}
66
67static uint8_t eval_at(float evalX, float circleR, const float* halfKernel, int halfKernelSize,
68                       const float* yKernelEvaluations) {
69    float acc = 0;
70
71    float x = evalX - halfKernelSize;
72    for (int i = 0; i < halfKernelSize; ++i, x += 1.f) {
73        if (x < -circleR || x > circleR) {
74            continue;
75        }
76        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];
85    }
86
87    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
177    sk_sp<GrTextureProxy> blurProfile = resourceProvider->findProxyByUniqueKey(key);
178    if (!blurProfile) {
179        static constexpr int kProfileTextureWidth = 512;
180        GrSurfaceDesc texDesc;
181        texDesc.fWidth = kProfileTextureWidth;
182        texDesc.fHeight = 1;
183        texDesc.fConfig = kAlpha_8_GrPixelConfig;
184
185        std::unique_ptr<uint8_t[]> profile(nullptr);
186        if (useHalfPlaneApprox) {
187            profile.reset(create_half_plane_profile(kProfileTextureWidth));
188        } else {
189            SkScalar scale = kProfileTextureWidth / *textureRadius;
190            profile.reset(
191                    create_circle_profile(sigma * scale, circleR * scale, kProfileTextureWidth));
192        }
193
194        blurProfile = GrSurfaceProxy::MakeDeferred(resourceProvider, texDesc, SkBudgeted::kYes,
195                                                   profile.get(), 0);
196        if (!blurProfile) {
197            return nullptr;
198        }
199
200        resourceProvider->assignUniqueKeyToProxy(key, blurProfile.get());
201    }
202
203    return blurProfile;
204}
205
206sk_sp<GrFragmentProcessor> GrCircleBlurFragmentProcessor::Make(GrResourceProvider* resourceProvider,
207                                                               const SkRect& circle,
208                                                               float sigma) {
209    float solidRadius;
210    float textureRadius;
211    sk_sp<GrTextureProxy> profile(
212            create_profile_texture(resourceProvider, circle, sigma, &solidRadius, &textureRadius));
213    if (!profile) {
214        return nullptr;
215    }
216    return sk_sp<GrFragmentProcessor>(new GrCircleBlurFragmentProcessor(
217            circle, textureRadius, solidRadius, std::move(profile), resourceProvider));
218}
219#include "glsl/GrGLSLColorSpaceXformHelper.h"
220#include "glsl/GrGLSLFragmentProcessor.h"
221#include "glsl/GrGLSLFragmentShaderBuilder.h"
222#include "glsl/GrGLSLProgramBuilder.h"
223#include "SkSLCPP.h"
224#include "SkSLUtil.h"
225class GrGLSLCircleBlurFragmentProcessor : public GrGLSLFragmentProcessor {
226public:
227    GrGLSLCircleBlurFragmentProcessor() {}
228    void emitCode(EmitArgs& args) override {
229        GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
230        const GrCircleBlurFragmentProcessor& _outer =
231                args.fFp.cast<GrCircleBlurFragmentProcessor>();
232        (void)_outer;
233        fCircleDataVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kVec4f_GrSLType,
234                                                          kDefault_GrSLPrecision, "circleData");
235        fragBuilder->codeAppendf(
236                "vec2 vec = vec2((sk_FragCoord.x - %s.x) * %s.w, (sk_FragCoord.y - %s.y) * "
237                "%s.w);\nfloat dist = length(vec) + (0.5 - %s.z) * %s.w;\n%s = %s * texture(%s, "
238                "vec2(dist, 0.5)).%s.w;\n",
239                args.fUniformHandler->getUniformCStr(fCircleDataVar),
240                args.fUniformHandler->getUniformCStr(fCircleDataVar),
241                args.fUniformHandler->getUniformCStr(fCircleDataVar),
242                args.fUniformHandler->getUniformCStr(fCircleDataVar),
243                args.fUniformHandler->getUniformCStr(fCircleDataVar),
244                args.fUniformHandler->getUniformCStr(fCircleDataVar), args.fOutputColor,
245                args.fInputColor ? args.fInputColor : "vec4(1)",
246                fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[0]).c_str(),
247                fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[0]).c_str());
248    }
249
250private:
251    void onSetData(const GrGLSLProgramDataManager& data,
252                   const GrFragmentProcessor& _proc) override {
253        const GrCircleBlurFragmentProcessor& _outer = _proc.cast<GrCircleBlurFragmentProcessor>();
254        auto circleRect = _outer.circleRect();
255        (void)circleRect;
256        auto textureRadius = _outer.textureRadius();
257        (void)textureRadius;
258        auto solidRadius = _outer.solidRadius();
259        (void)solidRadius;
260        UniformHandle& blurProfileSampler = fBlurProfileSamplerVar;
261        (void)blurProfileSampler;
262        UniformHandle& circleData = fCircleDataVar;
263        (void)circleData;
264
265        data.set4f(circleData, circleRect.centerX(), circleRect.centerY(), solidRadius,
266                   1.f / textureRadius);
267    }
268    UniformHandle fCircleDataVar;
269    UniformHandle fBlurProfileSamplerVar;
270};
271GrGLSLFragmentProcessor* GrCircleBlurFragmentProcessor::onCreateGLSLInstance() const {
272    return new GrGLSLCircleBlurFragmentProcessor();
273}
274void GrCircleBlurFragmentProcessor::onGetGLSLProcessorKey(const GrShaderCaps& caps,
275                                                          GrProcessorKeyBuilder* b) const {}
276bool GrCircleBlurFragmentProcessor::onIsEqual(const GrFragmentProcessor& other) const {
277    const GrCircleBlurFragmentProcessor& that = other.cast<GrCircleBlurFragmentProcessor>();
278    (void)that;
279    if (fCircleRect != that.fCircleRect) return false;
280    if (fTextureRadius != that.fTextureRadius) return false;
281    if (fSolidRadius != that.fSolidRadius) return false;
282    if (fBlurProfileSampler != that.fBlurProfileSampler) return false;
283    return true;
284}
285GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrCircleBlurFragmentProcessor);
286#if GR_TEST_UTILS
287sk_sp<GrFragmentProcessor> GrCircleBlurFragmentProcessor::TestCreate(
288        GrProcessorTestData* testData) {
289    SkScalar wh = testData->fRandom->nextRangeScalar(100.f, 1000.f);
290    SkScalar sigma = testData->fRandom->nextRangeF(1.f, 10.f);
291    SkRect circle = SkRect::MakeWH(wh, wh);
292    return GrCircleBlurFragmentProcessor::Make(testData->resourceProvider(), circle, sigma);
293}
294#endif
295#endif
296