Home | History | Annotate | Download | only in effects
      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 static 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 
     31 static 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 
     42 void 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 
     67 static 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 
     90 static 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 
    118 static 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 
    145 static 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 
    206 sk_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"
    225 class GrGLSLCircleBlurFragmentProcessor : public GrGLSLFragmentProcessor {
    226 public:
    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 
    250 private:
    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 };
    271 GrGLSLFragmentProcessor* GrCircleBlurFragmentProcessor::onCreateGLSLInstance() const {
    272     return new GrGLSLCircleBlurFragmentProcessor();
    273 }
    274 void GrCircleBlurFragmentProcessor::onGetGLSLProcessorKey(const GrShaderCaps& caps,
    275                                                           GrProcessorKeyBuilder* b) const {}
    276 bool 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 }
    285 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrCircleBlurFragmentProcessor);
    286 #if GR_TEST_UTILS
    287 sk_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