diff options
Diffstat (limited to 'src/effects')
-rw-r--r-- | src/effects/GrCircleBlurFragmentProcessor.cpp | 113 | ||||
-rw-r--r-- | src/effects/GrCircleBlurFragmentProcessor.h | 47 |
2 files changed, 97 insertions, 63 deletions
diff --git a/src/effects/GrCircleBlurFragmentProcessor.cpp b/src/effects/GrCircleBlurFragmentProcessor.cpp index e5c0299856..f2a6c36f36 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,13 @@ 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); + // The code for this processor is always the same so there is nothing to add to the key. + return; } void GrCircleBlurFragmentProcessor::onComputeInvariantOutput(GrInvariantOutput* inout) const { @@ -193,21 +196,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,11 +203,8 @@ 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) { + const int numSteps = profileTextureWidth; uint8_t* weights = new uint8_t[numSteps]; // The full kernel is 6 sigmas wide. @@ -248,35 +233,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 = SkScalarCeilToScalar(6 * sigma); + } else { + // The Gaussian is bigger than the circle. + *solidRadius = 0.0f; + *textureRadius = SkScalarCeilToScalar(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) { diff --git a/src/effects/GrCircleBlurFragmentProcessor.h b/src/effects/GrCircleBlurFragmentProcessor.h index 6dc599b7bb..89c067c43b 100644 --- a/src/effects/GrCircleBlurFragmentProcessor.h +++ b/src/effects/GrCircleBlurFragmentProcessor.h @@ -28,35 +28,41 @@ public: SkString dumpInfo() const override { SkString str; - str.appendf("Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], Sigma %.2f, Offset: %.2f", + str.appendf("Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], Sigma %.2f, solidR: %.2f, " + "textureR: %.2f", fCircle.fLeft, fCircle.fTop, fCircle.fRight, fCircle.fBottom, - fSigma, fOffset); + fSigma, fSolidRadius, fTextureRadius); return str; } static sk_sp<GrFragmentProcessor> Make(GrTextureProvider*textureProvider, const SkRect& circle, float sigma) { - float offset; - - SkAutoTUnref<GrTexture> blurProfile(CreateCircleBlurProfileTexture(textureProvider, - circle, - sigma, - &offset)); - if (!blurProfile) { + float solidRadius; + float textureRadius; + + SkAutoTUnref<GrTexture> profile(CreateCircleBlurProfileTexture(textureProvider, + circle, + sigma, + &solidRadius, + &textureRadius)); + if (!profile) { return nullptr; } return sk_sp<GrFragmentProcessor>( - new GrCircleBlurFragmentProcessor(circle, sigma, offset, blurProfile)); + new GrCircleBlurFragmentProcessor(circle, sigma, solidRadius, textureRadius, profile)); } - const SkRect& circle() const { return fCircle; } - float sigma() const { return fSigma; } - float offset() const { return fOffset; } - int profileSize() const { return fBlurProfileAccess.getTexture()->width(); } - private: + // This nested GLSL processor implementation is defined in the cpp file. + class GLSLProcessor; + + /** + * Creates a profile texture for the circle and sigma. The texture will have a height of 1. + * The x texture coord should map from 0 to 1 across the radius range of solidRadius to + * solidRadius + textureRadius. + */ GrCircleBlurFragmentProcessor(const SkRect& circle, float sigma, - float offset, GrTexture* blurProfile); + float solidRadius, float textureRadius, GrTexture* blurProfile); GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; @@ -65,18 +71,21 @@ private: bool onIsEqual(const GrFragmentProcessor& other) const override { const GrCircleBlurFragmentProcessor& cbfp = other.cast<GrCircleBlurFragmentProcessor>(); // fOffset is computed from the circle width and the sigma - return this->circle() == cbfp.circle() && fSigma == cbfp.fSigma; + return this->fCircle == cbfp.fCircle && fSigma == cbfp.fSigma; } void onComputeInvariantOutput(GrInvariantOutput* inout) const override; static GrTexture* CreateCircleBlurProfileTexture(GrTextureProvider*, const SkRect& circle, - float sigma, float* offset); + float sigma, + float* solidRadius, + float* textureRadius); SkRect fCircle; float fSigma; - float fOffset; + float fSolidRadius; + float fTextureRadius; GrTextureAccess fBlurProfileAccess; GR_DECLARE_FRAGMENT_PROCESSOR_TEST; |