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