Chromium Code Reviews| Index: src/effects/GrCircleBlurFragmentProcessor.cpp |
| diff --git a/src/effects/GrCircleBlurFragmentProcessor.cpp b/src/effects/GrCircleBlurFragmentProcessor.cpp |
| index e5c0299856bc784099109d90e0eef306e7270838..1cf980d3e8438518421074afb9598900623b0f7e 100644 |
| --- a/src/effects/GrCircleBlurFragmentProcessor.cpp |
| +++ b/src/effects/GrCircleBlurFragmentProcessor.cpp |
| @@ -20,7 +20,7 @@ |
| #include "SkFixed.h" |
| -class GrGLCircleBlurFragmentProcessor : public GrGLSLFragmentProcessor { |
| +class GrCircleBlurFragmentProcessor::GLSLProcessor : public GrGLSLFragmentProcessor { |
| public: |
| void emitCode(EmitArgs&) override; |
| @@ -33,14 +33,14 @@ private: |
| typedef GrGLSLFragmentProcessor INHERITED; |
| }; |
| -void GrGLCircleBlurFragmentProcessor::emitCode(EmitArgs& args) { |
| +void GrCircleBlurFragmentProcessor::GLSLProcessor::emitCode(EmitArgs& args) { |
| const char *dataName; |
| // The data is formatted as: |
| // x,y - the center of the circle |
| // z - the distance at which the intensity starts falling off (e.g., the start of the table) |
| - // w - the inverse of the profile texture size |
| + // w - the inverse of the distance over which the texture is stretched. |
| fDataUniform = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, |
| kVec4f_GrSLType, |
| kDefault_GrSLPrecision, |
| @@ -71,28 +71,30 @@ void GrGLCircleBlurFragmentProcessor::emitCode(EmitArgs& args) { |
| fragBuilder->codeAppendf("%s = src * intensity;\n", args.fOutputColor ); |
| } |
| -void GrGLCircleBlurFragmentProcessor::onSetData(const GrGLSLProgramDataManager& pdman, |
| - const GrProcessor& proc) { |
| +void GrCircleBlurFragmentProcessor::GLSLProcessor::onSetData(const GrGLSLProgramDataManager& pdman, |
| + const GrProcessor& proc) { |
| const GrCircleBlurFragmentProcessor& cbfp = proc.cast<GrCircleBlurFragmentProcessor>(); |
| - const SkRect& circle = cbfp.circle(); |
| + const SkRect& circle = cbfp.fCircle; |
| // The data is formatted as: |
| // x,y - the center of the circle |
| // z - the distance at which the intensity starts falling off (e.g., the start of the table) |
| - // w - the inverse of the profile texture size |
| - pdman.set4f(fDataUniform, circle.centerX(), circle.centerY(), cbfp.offset(), |
| - 1.0f / cbfp.profileSize()); |
| + // w - the inverse of the distance over which the profile texture is stretched. |
| + pdman.set4f(fDataUniform, circle.centerX(), circle.centerY(), cbfp.fSolidRadius, |
| + 1.f / cbfp.fTextureRadius); |
| } |
| /////////////////////////////////////////////////////////////////////////////// |
| GrCircleBlurFragmentProcessor::GrCircleBlurFragmentProcessor(const SkRect& circle, |
| float sigma, |
| - float offset, |
| + float solidRadius, |
| + float textureRadius, |
| GrTexture* blurProfile) |
| : fCircle(circle) |
| , fSigma(sigma) |
| - , fOffset(offset) |
| + , fSolidRadius(solidRadius) |
| + , fTextureRadius(textureRadius) |
| , fBlurProfileAccess(blurProfile, GrTextureParams::kBilerp_FilterMode) { |
| this->initClassID<GrCircleBlurFragmentProcessor>(); |
| this->addTextureAccess(&fBlurProfileAccess); |
| @@ -100,12 +102,12 @@ GrCircleBlurFragmentProcessor::GrCircleBlurFragmentProcessor(const SkRect& circl |
| } |
| GrGLSLFragmentProcessor* GrCircleBlurFragmentProcessor::onCreateGLSLInstance() const { |
| - return new GrGLCircleBlurFragmentProcessor; |
| + return new GLSLProcessor; |
| } |
| void GrCircleBlurFragmentProcessor::onGetGLSLProcessorKey(const GrGLSLCaps& caps, |
| GrProcessorKeyBuilder* b) const { |
| - GrGLCircleBlurFragmentProcessor::GenKey(*this, caps, b); |
| + return; |
|
jvanverth1
2016/06/14 18:51:15
Won't this return garbage (whatever was there befo
bsalomon
2016/06/14 19:16:51
This just leaves the key empty (since this process
|
| } |
| void GrCircleBlurFragmentProcessor::onComputeInvariantOutput(GrInvariantOutput* inout) const { |
| @@ -193,21 +195,6 @@ static uint8_t eval_at(float evalX, float circleR, const float* halfKernel, int |
| return SkUnitScalarClampToByte(2.f * acc); |
| } |
| -static inline void compute_profile_offset_and_size(float circleR, float sigma, |
| - float* offset, int* size) { |
| - if (3*sigma <= circleR) { |
| - // The circle is bigger than the Gaussian. In this case we know the interior of the |
| - // blurred circle is solid. |
| - *offset = circleR - 3 * sigma; // This location maps to 0.5f in the weights texture. |
| - // It should always be 255. |
| - *size = SkScalarCeilToInt(6*sigma); |
| - } else { |
| - // The Gaussian is bigger than the circle. |
| - *offset = 0.0f; |
| - *size = SkScalarCeilToInt(circleR + 3*sigma); |
| - } |
| -} |
| - |
| // This function creates a profile of a blurred circle. It does this by computing a kernel for |
| // half the Gaussian and a matching summed area table. The summed area table is used to compute |
| // an array of vertical applications of the half kernel to the circle along the x axis. The table |
| @@ -215,10 +202,9 @@ static inline void compute_profile_offset_and_size(float circleR, float sigma, |
| // of the profile being computed. Then for each of the n profile entries we walk out k steps in each |
| // horizontal direction multiplying the corresponding y evaluation by the half kernel entry and |
| // sum these values to compute the profile entry. |
| -static uint8_t* create_profile(float circleR, float sigma) { |
| - float offset; |
| - int numSteps; |
| - compute_profile_offset_and_size(circleR, sigma, &offset, &numSteps); |
| +static uint8_t* create_profile(float sigma, float circleR, float offset, int profileTextureWidth) { |
| + int numSteps = profileTextureWidth; |
| +// compute_profile_offset_and_size(circleR, sigma, &offset, &numSteps); |
|
jvanverth1
2016/06/14 18:51:15
Delete this line?
bsalomon
2016/06/14 19:16:51
oops, done.
|
| uint8_t* weights = new uint8_t[numSteps]; |
| @@ -248,35 +234,75 @@ static uint8_t* create_profile(float circleR, float sigma) { |
| return weights; |
| } |
| +static int next_pow2_16bits(int x) { |
| + SkASSERT(x > 0); |
| + SkASSERT(x <= SK_MaxS16); |
| + x--; |
| + x |= x >> 1; |
| + x |= x >> 2; |
| + x |= x >> 4; |
| + x |= x >> 8; |
| + return x + 1; |
| +} |
| + |
| GrTexture* GrCircleBlurFragmentProcessor::CreateCircleBlurProfileTexture( |
| GrTextureProvider* textureProvider, |
| const SkRect& circle, |
| float sigma, |
| - float* offset) { |
| + float* solidRadius, |
| + float* textureRadius) { |
| float circleR = circle.width() / 2.0f; |
| + // Profile textures are cached by the ratio of sigma to circle radius and by the size of the |
| + // profile texture (binned by powers of 2). |
| + SkScalar sigmaToCircleRRatio = sigma / circleR; |
| + // When sigma is really small this becomes a equivalent to convolving a Gaussian with a half- |
| + // plane. We could do that simpler computation. However, right now we're just using a lower |
| + // bound off the ratio. Similarly, in the extreme high ratio cases circle becomes a point WRT to |
| + // the Guassian and the profile texture is a just a Gaussian evaluation. |
| + sigmaToCircleRRatio = SkTPin(sigmaToCircleRRatio, 0.05f, 8.f); |
| + // Convert to fixed point for the key. |
| + SkFixed sigmaToCircleRRatioFixed = SkScalarToFixed(sigmaToCircleRRatio); |
| + // We shave off some bits to reduce the number of unique entries. We could probably shave off |
| + // more than we do. |
| + sigmaToCircleRRatioFixed &= ~0xff; |
| + // From the circle center to solidRadius is all 1s and represented by the leftmost pixel (with |
| + // value 255) in the profile texture. If it is zero then there is no solid center to the |
| + // blurred circle. |
| + if (3*sigma <= circleR) { |
| + // The circle is bigger than the Gaussian. In this case we know the interior of the |
| + // blurred circle is solid. |
| + *solidRadius = circleR - 3 * sigma; // This location maps to 0.5f in the weights texture. |
| + // It should always be 255. |
| + *textureRadius = SkScalarCeilToInt(6*sigma); |
| + } else { |
| + // The Gaussian is bigger than the circle. |
| + *solidRadius = 0.0f; |
| + *textureRadius = SkScalarCeilToInt(circleR + 3*sigma); |
| + } |
| + int profileTextureWidth = SkScalarCeilToInt(*textureRadius); |
| + profileTextureWidth = (profileTextureWidth >= 1024) ? 1024 : |
| + next_pow2_16bits(profileTextureWidth); |
| static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); |
| GrUniqueKey key; |
| GrUniqueKey::Builder builder(&key, kDomain, 2); |
| - // The profile curve varies with both the sigma of the Gaussian and the size of the |
| - // disk. Quantizing to 16.16 should be close enough though. |
| - builder[0] = SkScalarToFixed(sigma); |
| - builder[1] = SkScalarToFixed(circleR); |
| + builder[0] = sigmaToCircleRRatioFixed; |
| + builder[1] = profileTextureWidth; |
| builder.finish(); |
| GrTexture *blurProfile = textureProvider->findAndRefTextureByUniqueKey(key); |
| - int profileSize; |
| - compute_profile_offset_and_size(circleR, sigma, offset, &profileSize); |
| - |
| if (!blurProfile) { |
| - |
| GrSurfaceDesc texDesc; |
| - texDesc.fWidth = profileSize; |
| + texDesc.fWidth = profileTextureWidth; |
| texDesc.fHeight = 1; |
| texDesc.fConfig = kAlpha_8_GrPixelConfig; |
| - SkAutoTDeleteArray<uint8_t> profile(create_profile(circleR, sigma)); |
| + // Rescale params to the size of the texture we're creating. |
| + SkScalar scale = profileTextureWidth / *textureRadius; |
| + SkAutoTDeleteArray<uint8_t> profile(create_profile(sigma * scale, circleR * scale, |
| + *solidRadius * scale, |
| + profileTextureWidth)); |
| blurProfile = textureProvider->createTexture(texDesc, SkBudgeted::kYes, profile.get(), 0); |
| if (blurProfile) { |