1fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot/*
2fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Copyright 2018 Google Inc.
3fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot *
4fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Use of this source code is governed by a BSD-style license that can be
5fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * found in the LICENSE file.
6fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot */
7fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
8fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot/**************************************************************************************************
9fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot *** This file was autogenerated from GrCircleBlurFragmentProcessor.fp; do not modify.
10fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot **************************************************************************************************/
11fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "GrCircleBlurFragmentProcessor.h"
12fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#if SK_SUPPORT_GPU
13fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
14fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "GrProxyProvider.h"
15fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
16fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// Computes an unnormalized half kernel (right side). Returns the summation of all the half
17fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// kernel values.
18fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic float make_unnormalized_half_kernel(float* halfKernel, int halfKernelSize, float sigma) {
19fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const float invSigma = 1.f / sigma;
20fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const float b = -0.5f * invSigma * invSigma;
21fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    float tot = 0.0f;
22fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Compute half kernel values at half pixel steps out from the center.
23fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    float t = 0.5f;
24fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (int i = 0; i < halfKernelSize; ++i) {
25fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        float value = expf(t * t * b);
26fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        tot += value;
27fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        halfKernel[i] = value;
28fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        t += 1.f;
29fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
30fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return tot;
31fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
32fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
33fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// Create a Gaussian half-kernel (right side) and a summed area table given a sigma and number
34fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// of discrete steps. The half kernel is normalized to sum to 0.5.
35fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic void make_half_kernel_and_summed_table(float* halfKernel, float* summedHalfKernel,
36fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                              int halfKernelSize, float sigma) {
37fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // The half kernel should sum to 0.5 not 1.0.
38fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const float tot = 2.f * make_unnormalized_half_kernel(halfKernel, halfKernelSize, sigma);
39fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    float sum = 0.f;
40fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (int i = 0; i < halfKernelSize; ++i) {
41fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        halfKernel[i] /= tot;
42fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        sum += halfKernel[i];
43fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        summedHalfKernel[i] = sum;
44fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
45fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
46fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
47fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// Applies the 1D half kernel vertically at points along the x axis to a circle centered at the
48fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// origin with radius circleR.
49fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid apply_kernel_in_y(float* results, int numSteps, float firstX, float circleR,
50fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                       int halfKernelSize, const float* summedHalfKernelTable) {
51fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    float x = firstX;
52fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (int i = 0; i < numSteps; ++i, x += 1.f) {
53fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (x < -circleR || x > circleR) {
54fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            results[i] = 0;
55fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            continue;
56fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
57fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        float y = sqrtf(circleR * circleR - x * x);
58fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // In the column at x we exit the circle at +y and -y
59fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // The summed table entry j is actually reflects an offset of j + 0.5.
60fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        y -= 0.5f;
61fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        int yInt = SkScalarFloorToInt(y);
62fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(yInt >= -1);
63fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (y < 0) {
64fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            results[i] = (y + 0.5f) * summedHalfKernelTable[0];
65fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        } else if (yInt >= halfKernelSize - 1) {
66fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            results[i] = 0.5f;
67fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        } else {
68fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            float yFrac = y - yInt;
69fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            results[i] = (1.f - yFrac) * summedHalfKernelTable[yInt] +
70fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                         yFrac * summedHalfKernelTable[yInt + 1];
71fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
72fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
73fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
74fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
75fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// Apply a Gaussian at point (evalX, 0) to a circle centered at the origin with radius circleR.
76fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// This relies on having a half kernel computed for the Gaussian and a table of applications of
77fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// the half kernel in y to columns at (evalX - halfKernel, evalX - halfKernel + 1, ..., evalX +
78fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// halfKernel) passed in as yKernelEvaluations.
79fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic uint8_t eval_at(float evalX, float circleR, const float* halfKernel, int halfKernelSize,
80fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                       const float* yKernelEvaluations) {
81fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    float acc = 0;
82fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
83fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    float x = evalX - halfKernelSize;
84fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (int i = 0; i < halfKernelSize; ++i, x += 1.f) {
85fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (x < -circleR || x > circleR) {
86fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            continue;
87fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
88fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        float verticalEval = yKernelEvaluations[i];
89fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        acc += verticalEval * halfKernel[halfKernelSize - i - 1];
90fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
91fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (int i = 0; i < halfKernelSize; ++i, x += 1.f) {
92fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (x < -circleR || x > circleR) {
93fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            continue;
94fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
95fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        float verticalEval = yKernelEvaluations[i + halfKernelSize];
96fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        acc += verticalEval * halfKernel[i];
97fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
98fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Since we applied a half kernel in y we multiply acc by 2 (the circle is symmetric about
99fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // the x axis).
100fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return SkUnitScalarClampToByte(2.f * acc);
101fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
102fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
103fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// This function creates a profile of a blurred circle. It does this by computing a kernel for
104fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// half the Gaussian and a matching summed area table. The summed area table is used to compute
105fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// an array of vertical applications of the half kernel to the circle along the x axis. The
106fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// table of y evaluations has 2 * k + n entries where k is the size of the half kernel and n is
107fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// the size of the profile being computed. Then for each of the n profile entries we walk out k
108fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// steps in each horizontal direction multiplying the corresponding y evaluation by the half
109fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// kernel entry and sum these values to compute the profile entry.
110fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic uint8_t* create_circle_profile(float sigma, float circleR, int profileTextureWidth) {
111fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const int numSteps = profileTextureWidth;
112fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint8_t* weights = new uint8_t[numSteps];
113fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
114fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // The full kernel is 6 sigmas wide.
115fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int halfKernelSize = SkScalarCeilToInt(6.0f * sigma);
116fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // round up to next multiple of 2 and then divide by 2
117fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    halfKernelSize = ((halfKernelSize + 1) & ~1) >> 1;
118fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
119fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Number of x steps at which to apply kernel in y to cover all the profile samples in x.
120fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int numYSteps = numSteps + 2 * halfKernelSize;
121fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
122fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkAutoTArray<float> bulkAlloc(halfKernelSize + halfKernelSize + numYSteps);
123fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    float* halfKernel = bulkAlloc.get();
124fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    float* summedKernel = bulkAlloc.get() + halfKernelSize;
125fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    float* yEvals = bulkAlloc.get() + 2 * halfKernelSize;
126fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    make_half_kernel_and_summed_table(halfKernel, summedKernel, halfKernelSize, sigma);
127fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
128fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    float firstX = -halfKernelSize + 0.5f;
129fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    apply_kernel_in_y(yEvals, numYSteps, firstX, circleR, halfKernelSize, summedKernel);
130fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
131fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (int i = 0; i < numSteps - 1; ++i) {
132fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        float evalX = i + 0.5f;
133fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        weights[i] = eval_at(evalX, circleR, halfKernel, halfKernelSize, yEvals + i);
134fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
135fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Ensure the tail of the Gaussian goes to zero.
136fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    weights[numSteps - 1] = 0;
137fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return weights;
138fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
139fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
140fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic uint8_t* create_half_plane_profile(int profileWidth) {
141fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(!(profileWidth & 0x1));
142fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // The full kernel is 6 sigmas wide.
143fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    float sigma = profileWidth / 6.f;
144fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int halfKernelSize = profileWidth / 2;
145fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
146fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkAutoTArray<float> halfKernel(halfKernelSize);
147fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint8_t* profile = new uint8_t[profileWidth];
148fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
149fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // The half kernel should sum to 0.5.
150fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const float tot = 2.f * make_unnormalized_half_kernel(halfKernel.get(), halfKernelSize, sigma);
151fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    float sum = 0.f;
152fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Populate the profile from the right edge to the middle.
153fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (int i = 0; i < halfKernelSize; ++i) {
154fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        halfKernel[halfKernelSize - i - 1] /= tot;
155fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        sum += halfKernel[halfKernelSize - i - 1];
156fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        profile[profileWidth - i - 1] = SkUnitScalarClampToByte(sum);
157fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
158fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Populate the profile from the middle to the left edge (by flipping the half kernel and
159fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // continuing the summation).
160fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (int i = 0; i < halfKernelSize; ++i) {
161fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        sum += halfKernel[i];
162fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        profile[halfKernelSize - i - 1] = SkUnitScalarClampToByte(sum);
163fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
164fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Ensure tail goes to 0.
165fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    profile[profileWidth - 1] = 0;
166fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return profile;
167fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
168fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
169fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic sk_sp<GrTextureProxy> create_profile_texture(GrProxyProvider* proxyProvider,
170fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                    const SkRect& circle, float sigma,
171fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                    float* solidRadius, float* textureRadius) {
172fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    float circleR = circle.width() / 2.0f;
173fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (circleR < SK_ScalarNearlyZero) {
174fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return nullptr;
175fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
176fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Profile textures are cached by the ratio of sigma to circle radius and by the size of the
177fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // profile texture (binned by powers of 2).
178fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkScalar sigmaToCircleRRatio = sigma / circleR;
179fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // When sigma is really small this becomes a equivalent to convolving a Gaussian with a
180fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // half-plane. Similarly, in the extreme high ratio cases circle becomes a point WRT to the
181fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Guassian and the profile texture is a just a Gaussian evaluation. However, we haven't yet
182fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // implemented this latter optimization.
183fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    sigmaToCircleRRatio = SkTMin(sigmaToCircleRRatio, 8.f);
184fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkFixed sigmaToCircleRRatioFixed;
185fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    static const SkScalar kHalfPlaneThreshold = 0.1f;
186fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    bool useHalfPlaneApprox = false;
187fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (sigmaToCircleRRatio <= kHalfPlaneThreshold) {
188fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        useHalfPlaneApprox = true;
189fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        sigmaToCircleRRatioFixed = 0;
190fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        *solidRadius = circleR - 3 * sigma;
191fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        *textureRadius = 6 * sigma;
192fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } else {
193fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // Convert to fixed point for the key.
194fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        sigmaToCircleRRatioFixed = SkScalarToFixed(sigmaToCircleRRatio);
195fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // We shave off some bits to reduce the number of unique entries. We could probably
196fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // shave off more than we do.
197fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        sigmaToCircleRRatioFixed &= ~0xff;
198fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        sigmaToCircleRRatio = SkFixedToScalar(sigmaToCircleRRatioFixed);
199fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        sigma = circleR * sigmaToCircleRRatio;
200fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        *solidRadius = 0;
201fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        *textureRadius = circleR + 3 * sigma;
202fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
203fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
204fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
205fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    GrUniqueKey key;
206fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    GrUniqueKey::Builder builder(&key, kDomain, 1);
207fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    builder[0] = sigmaToCircleRRatioFixed;
208fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    builder.finish();
209fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
210fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    sk_sp<GrTextureProxy> blurProfile =
211fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            proxyProvider->findOrCreateProxyByUniqueKey(key, kTopLeft_GrSurfaceOrigin);
212fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!blurProfile) {
213fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        static constexpr int kProfileTextureWidth = 512;
214fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        GrSurfaceDesc texDesc;
215fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        texDesc.fOrigin = kTopLeft_GrSurfaceOrigin;
216fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        texDesc.fWidth = kProfileTextureWidth;
217fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        texDesc.fHeight = 1;
218fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        texDesc.fConfig = kAlpha_8_GrPixelConfig;
219fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
220fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        std::unique_ptr<uint8_t[]> profile(nullptr);
221fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (useHalfPlaneApprox) {
222fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            profile.reset(create_half_plane_profile(kProfileTextureWidth));
223fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        } else {
224fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // Rescale params to the size of the texture we're creating.
225fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkScalar scale = kProfileTextureWidth / *textureRadius;
226fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            profile.reset(
227fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    create_circle_profile(sigma * scale, circleR * scale, kProfileTextureWidth));
228fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
229fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
230fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        blurProfile =
231fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                proxyProvider->createTextureProxy(texDesc, SkBudgeted::kYes, profile.get(), 0);
232fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (!blurProfile) {
233fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return nullptr;
234fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
235fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
236fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(blurProfile->origin() == kTopLeft_GrSurfaceOrigin);
237fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        proxyProvider->assignUniqueKeyToProxy(key, blurProfile.get());
238fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
239fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
240fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return blurProfile;
241fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
242fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
243fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstd::unique_ptr<GrFragmentProcessor> GrCircleBlurFragmentProcessor::Make(
244fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        GrProxyProvider* proxyProvider, const SkRect& circle, float sigma) {
245fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    float solidRadius;
246fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    float textureRadius;
247fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    sk_sp<GrTextureProxy> profile(
248fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            create_profile_texture(proxyProvider, circle, sigma, &solidRadius, &textureRadius));
249fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!profile) {
250fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return nullptr;
251fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
252fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return std::unique_ptr<GrFragmentProcessor>(new GrCircleBlurFragmentProcessor(
253fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            circle, textureRadius, solidRadius, std::move(profile)));
254fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
255fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "glsl/GrGLSLFragmentProcessor.h"
256fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "glsl/GrGLSLFragmentShaderBuilder.h"
257fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "glsl/GrGLSLProgramBuilder.h"
258fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "GrTexture.h"
259fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkSLCPP.h"
260fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkSLUtil.h"
261fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotclass GrGLSLCircleBlurFragmentProcessor : public GrGLSLFragmentProcessor {
262fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotpublic:
263fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    GrGLSLCircleBlurFragmentProcessor() {}
264fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    void emitCode(EmitArgs& args) override {
265fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
266fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const GrCircleBlurFragmentProcessor& _outer =
267fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                args.fFp.cast<GrCircleBlurFragmentProcessor>();
268fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        (void)_outer;
269fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        auto circleRect = _outer.circleRect();
270fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        (void)circleRect;
271fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        auto textureRadius = _outer.textureRadius();
272fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        (void)textureRadius;
273fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        auto solidRadius = _outer.solidRadius();
274fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        (void)solidRadius;
275fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fCircleDataVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType,
276fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                          kDefault_GrSLPrecision, "circleData");
277fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fragBuilder->codeAppendf(
278fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                "half2 vec = half2(half((sk_FragCoord.x - float(%s.x)) * float(%s.w)), "
279fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                "half((sk_FragCoord.y - float(%s.y)) * float(%s.w)));\nhalf dist = "
280fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                "float(length(vec)) + (0.5 - float(%s.z)) * float(%s.w);\n%s = %s * texture(%s, "
281fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                "float2(half2(dist, 0.5))).%s.w;\n",
282fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                args.fUniformHandler->getUniformCStr(fCircleDataVar),
283fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                args.fUniformHandler->getUniformCStr(fCircleDataVar),
284fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                args.fUniformHandler->getUniformCStr(fCircleDataVar),
285fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                args.fUniformHandler->getUniformCStr(fCircleDataVar),
286fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                args.fUniformHandler->getUniformCStr(fCircleDataVar),
287fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                args.fUniformHandler->getUniformCStr(fCircleDataVar), args.fOutputColor,
288fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                args.fInputColor ? args.fInputColor : "half4(1)",
289fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[0]).c_str(),
290fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[0]).c_str());
291fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
292fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
293fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotprivate:
294fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    void onSetData(const GrGLSLProgramDataManager& data,
295fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                   const GrFragmentProcessor& _proc) override {
296fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const GrCircleBlurFragmentProcessor& _outer = _proc.cast<GrCircleBlurFragmentProcessor>();
297fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        auto circleRect = _outer.circleRect();
298fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        (void)circleRect;
299fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        auto textureRadius = _outer.textureRadius();
300fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        (void)textureRadius;
301fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        auto solidRadius = _outer.solidRadius();
302fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        (void)solidRadius;
303fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        GrSurfaceProxy& blurProfileSamplerProxy = *_outer.textureSampler(0).proxy();
304fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        GrTexture& blurProfileSampler = *blurProfileSamplerProxy.priv().peekTexture();
305fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        (void)blurProfileSampler;
306fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        UniformHandle& circleData = fCircleDataVar;
307fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        (void)circleData;
308fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
309fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        data.set4f(circleData, circleRect.centerX(), circleRect.centerY(), solidRadius,
310fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                   1.f / textureRadius);
311fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
312fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    UniformHandle fCircleDataVar;
313fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot};
314fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotGrGLSLFragmentProcessor* GrCircleBlurFragmentProcessor::onCreateGLSLInstance() const {
315fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return new GrGLSLCircleBlurFragmentProcessor();
316fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
317fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid GrCircleBlurFragmentProcessor::onGetGLSLProcessorKey(const GrShaderCaps& caps,
318fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                          GrProcessorKeyBuilder* b) const {}
319fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotbool GrCircleBlurFragmentProcessor::onIsEqual(const GrFragmentProcessor& other) const {
320fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const GrCircleBlurFragmentProcessor& that = other.cast<GrCircleBlurFragmentProcessor>();
321fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    (void)that;
322fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (fCircleRect != that.fCircleRect) return false;
323fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (fTextureRadius != that.fTextureRadius) return false;
324fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (fSolidRadius != that.fSolidRadius) return false;
325fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (fBlurProfileSampler != that.fBlurProfileSampler) return false;
326fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return true;
327fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
328fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotGrCircleBlurFragmentProcessor::GrCircleBlurFragmentProcessor(
329fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const GrCircleBlurFragmentProcessor& src)
330fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        : INHERITED(kGrCircleBlurFragmentProcessor_ClassID, src.optimizationFlags())
331fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fCircleRect(src.fCircleRect)
332fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fTextureRadius(src.fTextureRadius)
333fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fSolidRadius(src.fSolidRadius)
334fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fBlurProfileSampler(src.fBlurProfileSampler) {
335fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    this->addTextureSampler(&fBlurProfileSampler);
336fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
337fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstd::unique_ptr<GrFragmentProcessor> GrCircleBlurFragmentProcessor::clone() const {
338fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return std::unique_ptr<GrFragmentProcessor>(new GrCircleBlurFragmentProcessor(*this));
339fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
340fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotGR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrCircleBlurFragmentProcessor);
341fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#if GR_TEST_UTILS
342fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstd::unique_ptr<GrFragmentProcessor> GrCircleBlurFragmentProcessor::TestCreate(
343fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        GrProcessorTestData* testData) {
344fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkScalar wh = testData->fRandom->nextRangeScalar(100.f, 1000.f);
345fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkScalar sigma = testData->fRandom->nextRangeF(1.f, 10.f);
346fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkRect circle = SkRect::MakeWH(wh, wh);
347fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return GrCircleBlurFragmentProcessor::Make(testData->proxyProvider(), circle, sigma);
348fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
349fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#endif
350fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#endif
351