Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(374)

Unified Diff: src/effects/GrCircleBlurFragmentProcessor.cpp

Issue 1311583005: Add special case circle blur for Ganesh (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: fix no-GPU build Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/effects/GrCircleBlurFragmentProcessor.h ('k') | src/effects/SkBlurMask.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/effects/GrCircleBlurFragmentProcessor.cpp
diff --git a/src/effects/GrCircleBlurFragmentProcessor.cpp b/src/effects/GrCircleBlurFragmentProcessor.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c5aafdce0eb94e263b349841d6b7454cec38b7e2
--- /dev/null
+++ b/src/effects/GrCircleBlurFragmentProcessor.cpp
@@ -0,0 +1,259 @@
+
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrCircleBlurFragmentProcessor.h"
+
+#if SK_SUPPORT_GPU
+
+#include "GrContext.h"
+#include "GrTextureProvider.h"
+
+#include "gl/GrGLFragmentProcessor.h"
+#include "gl/builders/GrGLProgramBuilder.h"
+
+class GrGLCircleBlurFragmentProcessor : public GrGLFragmentProcessor {
+public:
+ GrGLCircleBlurFragmentProcessor(const GrProcessor&) {}
+ void emitCode(EmitArgs&) override;
+
+protected:
+ void onSetData(const GrGLProgramDataManager&, const GrProcessor&) override;
+
+private:
+ GrGLProgramDataManager::UniformHandle fDataUniform;
+
+ typedef GrGLFragmentProcessor INHERITED;
+};
+
+void GrGLCircleBlurFragmentProcessor::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 size of the profile texture
+ fDataUniform = args.fBuilder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
+ kVec4f_GrSLType,
+ kDefault_GrSLPrecision,
+ "data",
+ &dataName);
+
+ GrGLFragmentBuilder* fsBuilder = args.fBuilder->getFragmentShaderBuilder();
+ const char *fragmentPos = fsBuilder->fragmentPosition();
+
+ if (args.fInputColor) {
+ fsBuilder->codeAppendf("vec4 src=%s;", args.fInputColor);
+ } else {
+ fsBuilder->codeAppendf("vec4 src=vec4(1);");
+ }
+
+ fsBuilder->codeAppendf("vec2 vec = %s.xy - %s.xy;", fragmentPos, dataName);
+ fsBuilder->codeAppendf("float dist = (length(vec) - %s.z + 0.5) / %s.w;", dataName, dataName);
+
+ fsBuilder->codeAppendf("float intensity = ");
+ fsBuilder->appendTextureLookup(args.fSamplers[0], "vec2(dist, 0.5)");
+ fsBuilder->codeAppend(".a;");
+
+ fsBuilder->codeAppendf("%s = src * intensity;\n", args.fOutputColor );
+}
+
+void GrGLCircleBlurFragmentProcessor::onSetData(const GrGLProgramDataManager& pdman,
+ const GrProcessor& proc) {
+ const GrCircleBlurFragmentProcessor& cbfp = proc.cast<GrCircleBlurFragmentProcessor>();
+ const SkRect& circle = cbfp.circle();
+
+ // 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 size of the profile texture
+ pdman.set4f(fDataUniform, circle.centerX(), circle.centerY(), cbfp.offset(),
+ SkIntToScalar(cbfp.profileSize()));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrCircleBlurFragmentProcessor::GrCircleBlurFragmentProcessor(const SkRect& circle,
+ float sigma,
+ float offset,
+ GrTexture* blurProfile)
+ : fCircle(circle)
+ , fSigma(sigma)
+ , fOffset(offset)
+ , fBlurProfileAccess(blurProfile, GrTextureParams::kBilerp_FilterMode) {
+ this->initClassID<GrCircleBlurFragmentProcessor>();
+ this->addTextureAccess(&fBlurProfileAccess);
+ this->setWillReadFragmentPosition();
+}
+
+GrGLFragmentProcessor* GrCircleBlurFragmentProcessor::onCreateGLInstance() const {
+ return new GrGLCircleBlurFragmentProcessor(*this);
+}
+
+void GrCircleBlurFragmentProcessor::onGetGLProcessorKey(const GrGLSLCaps& caps,
+ GrProcessorKeyBuilder* b) const {
+ GrGLCircleBlurFragmentProcessor::GenKey(*this, caps, b);
+}
+
+void GrCircleBlurFragmentProcessor::onComputeInvariantOutput(GrInvariantOutput* inout) const {
+ inout->mulByUnknownSingleComponent();
+}
+
+// Evaluate an AA circle function centered at the origin with 'radius' at (x,y)
+static inline float disk(float x, float y, float radius) {
+ float distSq = x*x + y*y;
+ if (distSq <= (radius-0.5f)*(radius-0.5f)) {
+ return 1.0f;
+ } else if (distSq >= (radius+0.5f)*(radius+0.5f)) {
+ return 0.0f;
+ } else {
+ float ramp = radius + 0.5f - sqrt(distSq);
+ SkASSERT(ramp >= 0.0f && ramp <= 1.0f);
+ return ramp;
+ }
+}
+
+// Create the top half of an even-sized Gaussian kernel
+static void make_half_kernel(float* kernel, int kernelWH, float sigma) {
+ SkASSERT(!(kernelWH & 1));
+
+ const float kernelOff = (kernelWH-1)/2.0f;
+
+ float b = 1.0f / (2.0f * sigma * sigma);
+ // omit the scale term since we're just going to renormalize
+
+ float tot = 0.0f;
+ for (int y = 0; y < kernelWH/2; ++y) {
+ for (int x = 0; x < kernelWH/2; ++x) {
+ // TODO: use a cheap approximation of the 2D Guassian?
+ float x2 = (x-kernelOff) * (x-kernelOff);
+ float y2 = (y-kernelOff) * (y-kernelOff);
+ // The kernel is symmetric so only compute it once for both sides
+ kernel[y*kernelWH+(kernelWH-x-1)] = kernel[y*kernelWH+x] = exp(-(x2 + y2) * b);
+ tot += 2.0f * kernel[y*kernelWH+x];
+ }
+ }
+ // Still normalize the half kernel to 1.0 (rather than 0.5) so we don't
+ // have to scale by 2.0 after convolution.
+ for (int y = 0; y < kernelWH/2; ++y) {
+ for (int x = 0; x < kernelWH; ++x) {
+ kernel[y*kernelWH+x] /= tot;
+ }
+ }
+}
+
+// Apply the half-kernel at 't' away from the center of the circle
+static uint8_t eval_at(float t, float halfWidth, float* halfKernel, int kernelWH) {
+ SkASSERT(!(kernelWH & 1));
+
+ const float kernelOff = (kernelWH-1)/2.0f;
+
+ float acc = 0;
+
+ for (int y = 0; y < kernelWH/2; ++y) {
+ if (kernelOff-y > halfWidth+0.5f) {
+ // All disk() samples in this row will be 0.0f
+ continue;
+ }
+
+ for (int x = 0; x < kernelWH; ++x) {
+ float image = disk(t - kernelOff + x, -kernelOff + y, halfWidth);
+ float kernel = halfKernel[y*kernelWH+x];
+ acc += kernel * image;
+ }
+ }
+
+ return SkUnitScalarClampToByte(acc);
+}
+
+static inline void compute_profile_offset_and_size(float halfWH, float sigma,
+ float* offset, int* size) {
+
+ if (3*sigma <= halfWH) {
+ // The circle is bigger than the Gaussian. In this case we know the interior of the
+ // blurred circle is solid.
+ *offset = halfWH - 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(halfWH + 3*sigma);
+ }
+}
+
+static uint8_t* create_profile(float halfWH, float sigma) {
+
+ int kernelWH = SkScalarCeilToInt(6.0f*sigma);
+ kernelWH = (kernelWH + 1) & ~1; // make it the next even number up
+
+ SkAutoTArray<float> halfKernel(kernelWH*kernelWH/2);
+
+ make_half_kernel(halfKernel.get(), kernelWH, sigma);
+
+ float offset;
+ int numSteps;
+
+ compute_profile_offset_and_size(halfWH, sigma, &offset, &numSteps);
+
+ uint8_t* weights = new uint8_t[numSteps];
+ for (int i = 0; i < numSteps; ++i) {
+ weights[i] = eval_at(offset+i, halfWH, halfKernel.get(), kernelWH);
+ }
+
+ return weights;
+}
+
+GrTexture* GrCircleBlurFragmentProcessor::CreateCircleBlurProfileTexture(
+ GrTextureProvider* textureProvider,
+ const SkRect& circle,
+ float sigma,
+ float* offset) {
+ float halfWH = circle.width() / 2.0f;
+
+ int size;
+ compute_profile_offset_and_size(halfWH, sigma, offset, &size);
+
+ GrSurfaceDesc texDesc;
+ texDesc.fWidth = size;
+ texDesc.fHeight = 1;
+ texDesc.fConfig = kAlpha_8_GrPixelConfig;
+
+ 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(halfWH);
+ builder.finish();
+
+ GrTexture *blurProfile = textureProvider->findAndRefTextureByUniqueKey(key);
+
+ if (!blurProfile) {
+ SkAutoTDeleteArray<uint8_t> profile(create_profile(halfWH, sigma));
+
+ blurProfile = textureProvider->createTexture(texDesc, true, profile.get(), 0);
+ if (blurProfile) {
+ textureProvider->assignUniqueKeyToTexture(key, blurProfile);
+ }
+ }
+
+ return blurProfile;
+}
+
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrCircleBlurFragmentProcessor);
+
+const GrFragmentProcessor* GrCircleBlurFragmentProcessor::TestCreate(GrProcessorTestData* d) {
+ SkScalar wh = d->fRandom->nextRangeScalar(100.f, 1000.f);
+ SkScalar sigma = d->fRandom->nextRangeF(1.f,10.f);
+ SkRect circle = SkRect::MakeWH(wh, wh);
+ return GrCircleBlurFragmentProcessor::Create(d->fContext->textureProvider(), circle, sigma);
+}
+
+#endif
« no previous file with comments | « src/effects/GrCircleBlurFragmentProcessor.h ('k') | src/effects/SkBlurMask.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698