Index: src/effects/GrCircleBlurFragmentProcessor.cpp |
diff --git a/src/effects/GrCircleBlurFragmentProcessor.cpp b/src/effects/GrCircleBlurFragmentProcessor.cpp |
index e5c0299856bc784099109d90e0eef306e7270838..f2a6c36f36bd331cf49ce9ba7be61f2bf138ee13 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) { |