GrCircleBlurFragmentProcessor.cpp revision f57c0d67611186ba74179b53b421e64b63a579c7
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 =
178            resourceProvider->findProxyByUniqueKey(key, kTopLeft_GrSurfaceOrigin);
179    if (!blurProfile) {
180        static constexpr int kProfileTextureWidth = 512;
181        GrSurfaceDesc texDesc;
182        texDesc.fOrigin = kTopLeft_GrSurfaceOrigin;
183        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) {
199            return nullptr;
200        }
201
202        SkASSERT(blurProfile->origin() == kTopLeft_GrSurfaceOrigin);
203        resourceProvider->assignUniqueKeyToProxy(key, blurProfile.get());
204    }
205
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}
222#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 {
229public:
230    GrGLSLCircleBlurFragmentProcessor() {}
231    void emitCode(EmitArgs& args) override {
232        GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
233        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(
239                "float2 vec = float2((sk_FragCoord.x - %s.x) * %s.w, (sk_FragCoord.y - %s.y) * "
240                "%s.w);\nfloat dist = length(vec) + (0.5 - %s.z) * %s.w;\n%s = %s * texture(%s, "
241                "float2(dist, 0.5)).%s.w;\n",
242                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,
248                args.fInputColor ? args.fInputColor : "float4(1)",
249                fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[0]).c_str(),
250                fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[0]).c_str());
251    }
252
253private:
254    void onSetData(const GrGLSLProgramDataManager& data,
255                   const GrFragmentProcessor& _proc) override {
256        const GrCircleBlurFragmentProcessor& _outer = _proc.cast<GrCircleBlurFragmentProcessor>();
257        auto circleRect = _outer.circleRect();
258        (void)circleRect;
259        auto textureRadius = _outer.textureRadius();
260        (void)textureRadius;
261        auto solidRadius = _outer.solidRadius();
262        (void)solidRadius;
263        UniformHandle& blurProfileSampler = fBlurProfileSamplerVar;
264        (void)blurProfileSampler;
265        UniformHandle& circleData = fCircleDataVar;
266        (void)circleData;
267
268        data.set4f(circleData, circleRect.centerX(), circleRect.centerY(), solidRadius,
269                   1.f / textureRadius);
270    }
271    UniformHandle fCircleDataVar;
272    UniformHandle fBlurProfileSamplerVar;
273};
274GrGLSLFragmentProcessor* GrCircleBlurFragmentProcessor::onCreateGLSLInstance() const {
275    return new GrGLSLCircleBlurFragmentProcessor();
276}
277void GrCircleBlurFragmentProcessor::onGetGLSLProcessorKey(const GrShaderCaps& caps,
278                                                          GrProcessorKeyBuilder* b) const {}
279bool GrCircleBlurFragmentProcessor::onIsEqual(const GrFragmentProcessor& other) const {
280    const GrCircleBlurFragmentProcessor& that = other.cast<GrCircleBlurFragmentProcessor>();
281    (void)that;
282    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;
287}
288GrCircleBlurFragmentProcessor::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}
301GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrCircleBlurFragmentProcessor);
302#if GR_TEST_UTILS
303sk_sp<GrFragmentProcessor> GrCircleBlurFragmentProcessor::TestCreate(
304        GrProcessorTestData* testData) {
305    SkScalar wh = testData->fRandom->nextRangeScalar(100.f, 1000.f);
306    SkScalar sigma = testData->fRandom->nextRangeF(1.f, 10.f);
307    SkRect circle = SkRect::MakeWH(wh, wh);
308    return GrCircleBlurFragmentProcessor::Make(testData->resourceProvider(), circle, sigma);
309}
310#endif
311#endif
312