aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/effects
diff options
context:
space:
mode:
authorGravatar Mike Reed <reed@google.com>2018-03-12 14:03:47 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2018-03-12 18:48:18 +0000
commit18e7556371506fd08bd0767a270c5db5a7804103 (patch)
treedbb3791194125d85970936ae37168770a0e8cc54 /src/effects
parent05cb229e58efa052db5acf33be0441f4b4a28e7c (diff)
move blur impl into core
Ever since we added drawShadow to the public api, blurs have necessarily part of the core. This CL just formalizes that. This should also allow us to have builds that exclude all of /effects (for code size) and still be valid. Will follow-up with a change to deprecate SkBlurMaskFilter and SkBlurQuality (both no longer needed). Bug: skia: Change-Id: Ifbbd8b47a30a0386d215726b67bcf1e8b84fb8f5 Reviewed-on: https://skia-review.googlesource.com/113713 Reviewed-by: Mike Reed <reed@google.com> Commit-Queue: Mike Reed <reed@google.com>
Diffstat (limited to 'src/effects')
-rw-r--r--src/effects/GrCircleBlurFragmentProcessor.cpp349
-rw-r--r--src/effects/GrCircleBlurFragmentProcessor.fp292
-rw-r--r--src/effects/GrCircleBlurFragmentProcessor.h51
-rw-r--r--src/effects/SkBlurMaskFilter.cpp1032
-rw-r--r--src/effects/SkLayerDrawLooper.cpp4
5 files changed, 4 insertions, 1724 deletions
diff --git a/src/effects/GrCircleBlurFragmentProcessor.cpp b/src/effects/GrCircleBlurFragmentProcessor.cpp
deleted file mode 100644
index d44f431dd3..0000000000
--- a/src/effects/GrCircleBlurFragmentProcessor.cpp
+++ /dev/null
@@ -1,349 +0,0 @@
-/*
- * Copyright 2018 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-/**************************************************************************************************
- *** This file was autogenerated from GrCircleBlurFragmentProcessor.fp; do not modify.
- **************************************************************************************************/
-#include "GrCircleBlurFragmentProcessor.h"
-#if SK_SUPPORT_GPU
-
-#include "GrProxyProvider.h"
-
-// Computes an unnormalized half kernel (right side). Returns the summation of all the half
-// kernel values.
-static float make_unnormalized_half_kernel(float* halfKernel, int halfKernelSize, float sigma) {
- const float invSigma = 1.f / sigma;
- const float b = -0.5f * invSigma * invSigma;
- float tot = 0.0f;
- // Compute half kernel values at half pixel steps out from the center.
- float t = 0.5f;
- for (int i = 0; i < halfKernelSize; ++i) {
- float value = expf(t * t * b);
- tot += value;
- halfKernel[i] = value;
- t += 1.f;
- }
- return tot;
-}
-
-// Create a Gaussian half-kernel (right side) and a summed area table given a sigma and number
-// of discrete steps. The half kernel is normalized to sum to 0.5.
-static void make_half_kernel_and_summed_table(float* halfKernel, float* summedHalfKernel,
- int halfKernelSize, float sigma) {
- // The half kernel should sum to 0.5 not 1.0.
- const float tot = 2.f * make_unnormalized_half_kernel(halfKernel, halfKernelSize, sigma);
- float sum = 0.f;
- for (int i = 0; i < halfKernelSize; ++i) {
- halfKernel[i] /= tot;
- sum += halfKernel[i];
- summedHalfKernel[i] = sum;
- }
-}
-
-// Applies the 1D half kernel vertically at points along the x axis to a circle centered at the
-// origin with radius circleR.
-void apply_kernel_in_y(float* results, int numSteps, float firstX, float circleR,
- int halfKernelSize, const float* summedHalfKernelTable) {
- float x = firstX;
- for (int i = 0; i < numSteps; ++i, x += 1.f) {
- if (x < -circleR || x > circleR) {
- results[i] = 0;
- continue;
- }
- float y = sqrtf(circleR * circleR - x * x);
- // In the column at x we exit the circle at +y and -y
- // The summed table entry j is actually reflects an offset of j + 0.5.
- y -= 0.5f;
- int yInt = SkScalarFloorToInt(y);
- SkASSERT(yInt >= -1);
- if (y < 0) {
- results[i] = (y + 0.5f) * summedHalfKernelTable[0];
- } else if (yInt >= halfKernelSize - 1) {
- results[i] = 0.5f;
- } else {
- float yFrac = y - yInt;
- results[i] = (1.f - yFrac) * summedHalfKernelTable[yInt] +
- yFrac * summedHalfKernelTable[yInt + 1];
- }
- }
-}
-
-// Apply a Gaussian at point (evalX, 0) to a circle centered at the origin with radius circleR.
-// This relies on having a half kernel computed for the Gaussian and a table of applications of
-// the half kernel in y to columns at (evalX - halfKernel, evalX - halfKernel + 1, ..., evalX +
-// halfKernel) passed in as yKernelEvaluations.
-static uint8_t eval_at(float evalX, float circleR, const float* halfKernel, int halfKernelSize,
- const float* yKernelEvaluations) {
- float acc = 0;
-
- float x = evalX - halfKernelSize;
- for (int i = 0; i < halfKernelSize; ++i, x += 1.f) {
- if (x < -circleR || x > circleR) {
- continue;
- }
- float verticalEval = yKernelEvaluations[i];
- acc += verticalEval * halfKernel[halfKernelSize - i - 1];
- }
- for (int i = 0; i < halfKernelSize; ++i, x += 1.f) {
- if (x < -circleR || x > circleR) {
- continue;
- }
- float verticalEval = yKernelEvaluations[i + halfKernelSize];
- acc += verticalEval * halfKernel[i];
- }
- // Since we applied a half kernel in y we multiply acc by 2 (the circle is symmetric about
- // the x axis).
- return SkUnitScalarClampToByte(2.f * acc);
-}
-
-// 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 of y evaluations has 2 * k + n entries where k is the size of the half kernel and n is
-// the size 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 void create_circle_profile(uint8_t* weights, float sigma, float circleR,
- int profileTextureWidth) {
- const int numSteps = profileTextureWidth;
-
- // The full kernel is 6 sigmas wide.
- int halfKernelSize = SkScalarCeilToInt(6.0f * sigma);
- // round up to next multiple of 2 and then divide by 2
- halfKernelSize = ((halfKernelSize + 1) & ~1) >> 1;
-
- // Number of x steps at which to apply kernel in y to cover all the profile samples in x.
- int numYSteps = numSteps + 2 * halfKernelSize;
-
- SkAutoTArray<float> bulkAlloc(halfKernelSize + halfKernelSize + numYSteps);
- float* halfKernel = bulkAlloc.get();
- float* summedKernel = bulkAlloc.get() + halfKernelSize;
- float* yEvals = bulkAlloc.get() + 2 * halfKernelSize;
- make_half_kernel_and_summed_table(halfKernel, summedKernel, halfKernelSize, sigma);
-
- float firstX = -halfKernelSize + 0.5f;
- apply_kernel_in_y(yEvals, numYSteps, firstX, circleR, halfKernelSize, summedKernel);
-
- for (int i = 0; i < numSteps - 1; ++i) {
- float evalX = i + 0.5f;
- weights[i] = eval_at(evalX, circleR, halfKernel, halfKernelSize, yEvals + i);
- }
- // Ensure the tail of the Gaussian goes to zero.
- weights[numSteps - 1] = 0;
-}
-
-static void create_half_plane_profile(uint8_t* profile, int profileWidth) {
- SkASSERT(!(profileWidth & 0x1));
- // The full kernel is 6 sigmas wide.
- float sigma = profileWidth / 6.f;
- int halfKernelSize = profileWidth / 2;
-
- SkAutoTArray<float> halfKernel(halfKernelSize);
-
- // The half kernel should sum to 0.5.
- const float tot = 2.f * make_unnormalized_half_kernel(halfKernel.get(), halfKernelSize, sigma);
- float sum = 0.f;
- // Populate the profile from the right edge to the middle.
- for (int i = 0; i < halfKernelSize; ++i) {
- halfKernel[halfKernelSize - i - 1] /= tot;
- sum += halfKernel[halfKernelSize - i - 1];
- profile[profileWidth - i - 1] = SkUnitScalarClampToByte(sum);
- }
- // Populate the profile from the middle to the left edge (by flipping the half kernel and
- // continuing the summation).
- for (int i = 0; i < halfKernelSize; ++i) {
- sum += halfKernel[i];
- profile[halfKernelSize - i - 1] = SkUnitScalarClampToByte(sum);
- }
- // Ensure tail goes to 0.
- profile[profileWidth - 1] = 0;
-}
-
-static sk_sp<GrTextureProxy> create_profile_texture(GrProxyProvider* proxyProvider,
- const SkRect& circle, float sigma,
- float* solidRadius, float* textureRadius) {
- float circleR = circle.width() / 2.0f;
- if (circleR < SK_ScalarNearlyZero) {
- return nullptr;
- }
- // 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. 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. However, we haven't yet
- // implemented this latter optimization.
- sigmaToCircleRRatio = SkTMin(sigmaToCircleRRatio, 8.f);
- SkFixed sigmaToCircleRRatioFixed;
- static const SkScalar kHalfPlaneThreshold = 0.1f;
- bool useHalfPlaneApprox = false;
- if (sigmaToCircleRRatio <= kHalfPlaneThreshold) {
- useHalfPlaneApprox = true;
- sigmaToCircleRRatioFixed = 0;
- *solidRadius = circleR - 3 * sigma;
- *textureRadius = 6 * sigma;
- } else {
- // Convert to fixed point for the key.
- 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;
- sigmaToCircleRRatio = SkFixedToScalar(sigmaToCircleRRatioFixed);
- sigma = circleR * sigmaToCircleRRatio;
- *solidRadius = 0;
- *textureRadius = circleR + 3 * sigma;
- }
-
- static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
- GrUniqueKey key;
- GrUniqueKey::Builder builder(&key, kDomain, 1);
- builder[0] = sigmaToCircleRRatioFixed;
- builder.finish();
-
- sk_sp<GrTextureProxy> blurProfile =
- proxyProvider->findOrCreateProxyByUniqueKey(key, kTopLeft_GrSurfaceOrigin);
- if (!blurProfile) {
- static constexpr int kProfileTextureWidth = 512;
-
- SkBitmap bm;
- if (!bm.tryAllocPixels(SkImageInfo::MakeA8(kProfileTextureWidth, 1))) {
- return nullptr;
- }
-
- if (useHalfPlaneApprox) {
- create_half_plane_profile(bm.getAddr8(0, 0), kProfileTextureWidth);
- } else {
- // Rescale params to the size of the texture we're creating.
- SkScalar scale = kProfileTextureWidth / *textureRadius;
- create_circle_profile(bm.getAddr8(0, 0), sigma * scale, circleR * scale,
- kProfileTextureWidth);
- }
-
- bm.setImmutable();
- sk_sp<SkImage> image = SkImage::MakeFromBitmap(bm);
-
- blurProfile = proxyProvider->createTextureProxy(std::move(image), kNone_GrSurfaceFlags, 1,
- SkBudgeted::kYes, SkBackingFit::kExact);
- if (!blurProfile) {
- return nullptr;
- }
-
- SkASSERT(blurProfile->origin() == kTopLeft_GrSurfaceOrigin);
- proxyProvider->assignUniqueKeyToProxy(key, blurProfile.get());
- }
-
- return blurProfile;
-}
-
-std::unique_ptr<GrFragmentProcessor> GrCircleBlurFragmentProcessor::Make(
- GrProxyProvider* proxyProvider, const SkRect& circle, float sigma) {
- float solidRadius;
- float textureRadius;
- sk_sp<GrTextureProxy> profile(
- create_profile_texture(proxyProvider, circle, sigma, &solidRadius, &textureRadius));
- if (!profile) {
- return nullptr;
- }
- return std::unique_ptr<GrFragmentProcessor>(new GrCircleBlurFragmentProcessor(
- circle, textureRadius, solidRadius, std::move(profile)));
-}
-#include "glsl/GrGLSLFragmentProcessor.h"
-#include "glsl/GrGLSLFragmentShaderBuilder.h"
-#include "glsl/GrGLSLProgramBuilder.h"
-#include "GrTexture.h"
-#include "SkSLCPP.h"
-#include "SkSLUtil.h"
-class GrGLSLCircleBlurFragmentProcessor : public GrGLSLFragmentProcessor {
-public:
- GrGLSLCircleBlurFragmentProcessor() {}
- void emitCode(EmitArgs& args) override {
- GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
- const GrCircleBlurFragmentProcessor& _outer =
- args.fFp.cast<GrCircleBlurFragmentProcessor>();
- (void)_outer;
- auto circleRect = _outer.circleRect();
- (void)circleRect;
- auto textureRadius = _outer.textureRadius();
- (void)textureRadius;
- auto solidRadius = _outer.solidRadius();
- (void)solidRadius;
- fCircleDataVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType,
- kDefault_GrSLPrecision, "circleData");
- fragBuilder->codeAppendf(
- "half2 vec = half2(half((sk_FragCoord.x - float(%s.x)) * float(%s.w)), "
- "half((sk_FragCoord.y - float(%s.y)) * float(%s.w)));\nhalf dist = "
- "float(length(vec)) + (0.5 - float(%s.z)) * float(%s.w);\n%s = %s * texture(%s, "
- "float2(half2(dist, 0.5))).%s.w;\n",
- args.fUniformHandler->getUniformCStr(fCircleDataVar),
- args.fUniformHandler->getUniformCStr(fCircleDataVar),
- args.fUniformHandler->getUniformCStr(fCircleDataVar),
- args.fUniformHandler->getUniformCStr(fCircleDataVar),
- args.fUniformHandler->getUniformCStr(fCircleDataVar),
- args.fUniformHandler->getUniformCStr(fCircleDataVar), args.fOutputColor,
- args.fInputColor ? args.fInputColor : "half4(1)",
- fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[0]).c_str(),
- fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[0]).c_str());
- }
-
-private:
- void onSetData(const GrGLSLProgramDataManager& data,
- const GrFragmentProcessor& _proc) override {
- const GrCircleBlurFragmentProcessor& _outer = _proc.cast<GrCircleBlurFragmentProcessor>();
- auto circleRect = _outer.circleRect();
- (void)circleRect;
- auto textureRadius = _outer.textureRadius();
- (void)textureRadius;
- auto solidRadius = _outer.solidRadius();
- (void)solidRadius;
- GrSurfaceProxy& blurProfileSamplerProxy = *_outer.textureSampler(0).proxy();
- GrTexture& blurProfileSampler = *blurProfileSamplerProxy.priv().peekTexture();
- (void)blurProfileSampler;
- UniformHandle& circleData = fCircleDataVar;
- (void)circleData;
-
- data.set4f(circleData, circleRect.centerX(), circleRect.centerY(), solidRadius,
- 1.f / textureRadius);
- }
- UniformHandle fCircleDataVar;
-};
-GrGLSLFragmentProcessor* GrCircleBlurFragmentProcessor::onCreateGLSLInstance() const {
- return new GrGLSLCircleBlurFragmentProcessor();
-}
-void GrCircleBlurFragmentProcessor::onGetGLSLProcessorKey(const GrShaderCaps& caps,
- GrProcessorKeyBuilder* b) const {}
-bool GrCircleBlurFragmentProcessor::onIsEqual(const GrFragmentProcessor& other) const {
- const GrCircleBlurFragmentProcessor& that = other.cast<GrCircleBlurFragmentProcessor>();
- (void)that;
- if (fCircleRect != that.fCircleRect) return false;
- if (fTextureRadius != that.fTextureRadius) return false;
- if (fSolidRadius != that.fSolidRadius) return false;
- if (fBlurProfileSampler != that.fBlurProfileSampler) return false;
- return true;
-}
-GrCircleBlurFragmentProcessor::GrCircleBlurFragmentProcessor(
- const GrCircleBlurFragmentProcessor& src)
- : INHERITED(kGrCircleBlurFragmentProcessor_ClassID, src.optimizationFlags())
- , fCircleRect(src.fCircleRect)
- , fTextureRadius(src.fTextureRadius)
- , fSolidRadius(src.fSolidRadius)
- , fBlurProfileSampler(src.fBlurProfileSampler) {
- this->addTextureSampler(&fBlurProfileSampler);
-}
-std::unique_ptr<GrFragmentProcessor> GrCircleBlurFragmentProcessor::clone() const {
- return std::unique_ptr<GrFragmentProcessor>(new GrCircleBlurFragmentProcessor(*this));
-}
-GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrCircleBlurFragmentProcessor);
-#if GR_TEST_UTILS
-std::unique_ptr<GrFragmentProcessor> GrCircleBlurFragmentProcessor::TestCreate(
- GrProcessorTestData* testData) {
- SkScalar wh = testData->fRandom->nextRangeScalar(100.f, 1000.f);
- SkScalar sigma = testData->fRandom->nextRangeF(1.f, 10.f);
- SkRect circle = SkRect::MakeWH(wh, wh);
- return GrCircleBlurFragmentProcessor::Make(testData->proxyProvider(), circle, sigma);
-}
-#endif
-#endif
diff --git a/src/effects/GrCircleBlurFragmentProcessor.fp b/src/effects/GrCircleBlurFragmentProcessor.fp
deleted file mode 100644
index 71e5937729..0000000000
--- a/src/effects/GrCircleBlurFragmentProcessor.fp
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- * Copyright 2018 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-in half4 circleRect;
-in half textureRadius;
-in half solidRadius;
-in uniform sampler2D blurProfileSampler;
-
-// The data is formatted as:
-// x, y - the center of the circle
-// z - inner radius that should map to 0th entry in the texture.
-// w - the inverse of the distance over which the texture is stretched.
-uniform half4 circleData;
-
-@optimizationFlags {
- kCompatibleWithCoverageAsAlpha_OptimizationFlag
-}
-
-@make {
- static std::unique_ptr<GrFragmentProcessor> Make(GrProxyProvider*,
- const SkRect& circle, float sigma);
-}
-
-@setData(data) {
- data.set4f(circleData, circleRect.centerX(), circleRect.centerY(), solidRadius,
- 1.f / textureRadius);
-}
-
-@cpp {
- #include "GrProxyProvider.h"
-
- // Computes an unnormalized half kernel (right side). Returns the summation of all the half
- // kernel values.
- static float make_unnormalized_half_kernel(float* halfKernel, int halfKernelSize, float sigma) {
- const float invSigma = 1.f / sigma;
- const float b = -0.5f * invSigma * invSigma;
- float tot = 0.0f;
- // Compute half kernel values at half pixel steps out from the center.
- float t = 0.5f;
- for (int i = 0; i < halfKernelSize; ++i) {
- float value = expf(t * t * b);
- tot += value;
- halfKernel[i] = value;
- t += 1.f;
- }
- return tot;
- }
-
- // Create a Gaussian half-kernel (right side) and a summed area table given a sigma and number
- // of discrete steps. The half kernel is normalized to sum to 0.5.
- static void make_half_kernel_and_summed_table(float* halfKernel, float* summedHalfKernel,
- int halfKernelSize, float sigma) {
- // The half kernel should sum to 0.5 not 1.0.
- const float tot = 2.f * make_unnormalized_half_kernel(halfKernel, halfKernelSize, sigma);
- float sum = 0.f;
- for (int i = 0; i < halfKernelSize; ++i) {
- halfKernel[i] /= tot;
- sum += halfKernel[i];
- summedHalfKernel[i] = sum;
- }
- }
-
- // Applies the 1D half kernel vertically at points along the x axis to a circle centered at the
- // origin with radius circleR.
- void apply_kernel_in_y(float* results, int numSteps, float firstX, float circleR,
- int halfKernelSize, const float* summedHalfKernelTable) {
- float x = firstX;
- for (int i = 0; i < numSteps; ++i, x += 1.f) {
- if (x < -circleR || x > circleR) {
- results[i] = 0;
- continue;
- }
- float y = sqrtf(circleR * circleR - x * x);
- // In the column at x we exit the circle at +y and -y
- // The summed table entry j is actually reflects an offset of j + 0.5.
- y -= 0.5f;
- int yInt = SkScalarFloorToInt(y);
- SkASSERT(yInt >= -1);
- if (y < 0) {
- results[i] = (y + 0.5f) * summedHalfKernelTable[0];
- } else if (yInt >= halfKernelSize - 1) {
- results[i] = 0.5f;
- } else {
- float yFrac = y - yInt;
- results[i] = (1.f - yFrac) * summedHalfKernelTable[yInt] +
- yFrac * summedHalfKernelTable[yInt + 1];
- }
- }
- }
-
- // Apply a Gaussian at point (evalX, 0) to a circle centered at the origin with radius circleR.
- // This relies on having a half kernel computed for the Gaussian and a table of applications of
- // the half kernel in y to columns at (evalX - halfKernel, evalX - halfKernel + 1, ..., evalX +
- // halfKernel) passed in as yKernelEvaluations.
- static uint8_t eval_at(float evalX, float circleR, const float* halfKernel, int halfKernelSize,
- const float* yKernelEvaluations) {
- float acc = 0;
-
- float x = evalX - halfKernelSize;
- for (int i = 0; i < halfKernelSize; ++i, x += 1.f) {
- if (x < -circleR || x > circleR) {
- continue;
- }
- float verticalEval = yKernelEvaluations[i];
- acc += verticalEval * halfKernel[halfKernelSize - i - 1];
- }
- for (int i = 0; i < halfKernelSize; ++i, x += 1.f) {
- if (x < -circleR || x > circleR) {
- continue;
- }
- float verticalEval = yKernelEvaluations[i + halfKernelSize];
- acc += verticalEval * halfKernel[i];
- }
- // Since we applied a half kernel in y we multiply acc by 2 (the circle is symmetric about
- // the x axis).
- return SkUnitScalarClampToByte(2.f * acc);
- }
-
- // 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 of y evaluations has 2 * k + n entries where k is the size of the half kernel and n is
- // the size 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 void create_circle_profile(uint8_t* weights, float sigma, float circleR,
- int profileTextureWidth) {
- const int numSteps = profileTextureWidth;
-
- // The full kernel is 6 sigmas wide.
- int halfKernelSize = SkScalarCeilToInt(6.0f * sigma);
- // round up to next multiple of 2 and then divide by 2
- halfKernelSize = ((halfKernelSize + 1) & ~1) >> 1;
-
- // Number of x steps at which to apply kernel in y to cover all the profile samples in x.
- int numYSteps = numSteps + 2 * halfKernelSize;
-
- SkAutoTArray<float> bulkAlloc(halfKernelSize + halfKernelSize + numYSteps);
- float* halfKernel = bulkAlloc.get();
- float* summedKernel = bulkAlloc.get() + halfKernelSize;
- float* yEvals = bulkAlloc.get() + 2 * halfKernelSize;
- make_half_kernel_and_summed_table(halfKernel, summedKernel, halfKernelSize, sigma);
-
- float firstX = -halfKernelSize + 0.5f;
- apply_kernel_in_y(yEvals, numYSteps, firstX, circleR, halfKernelSize, summedKernel);
-
- for (int i = 0; i < numSteps - 1; ++i) {
- float evalX = i + 0.5f;
- weights[i] = eval_at(evalX, circleR, halfKernel, halfKernelSize, yEvals + i);
- }
- // Ensure the tail of the Gaussian goes to zero.
- weights[numSteps - 1] = 0;
- }
-
- static void create_half_plane_profile(uint8_t* profile, int profileWidth) {
- SkASSERT(!(profileWidth & 0x1));
- // The full kernel is 6 sigmas wide.
- float sigma = profileWidth / 6.f;
- int halfKernelSize = profileWidth / 2;
-
- SkAutoTArray<float> halfKernel(halfKernelSize);
-
- // The half kernel should sum to 0.5.
- const float tot = 2.f * make_unnormalized_half_kernel(halfKernel.get(), halfKernelSize,
- sigma);
- float sum = 0.f;
- // Populate the profile from the right edge to the middle.
- for (int i = 0; i < halfKernelSize; ++i) {
- halfKernel[halfKernelSize - i - 1] /= tot;
- sum += halfKernel[halfKernelSize - i - 1];
- profile[profileWidth - i - 1] = SkUnitScalarClampToByte(sum);
- }
- // Populate the profile from the middle to the left edge (by flipping the half kernel and
- // continuing the summation).
- for (int i = 0; i < halfKernelSize; ++i) {
- sum += halfKernel[i];
- profile[halfKernelSize - i - 1] = SkUnitScalarClampToByte(sum);
- }
- // Ensure tail goes to 0.
- profile[profileWidth - 1] = 0;
- }
-
- static sk_sp<GrTextureProxy> create_profile_texture(GrProxyProvider* proxyProvider,
- const SkRect& circle,
- float sigma,
- float* solidRadius, float* textureRadius) {
- float circleR = circle.width() / 2.0f;
- if (circleR < SK_ScalarNearlyZero) {
- return nullptr;
- }
- // 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. 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. However, we haven't yet
- // implemented this latter optimization.
- sigmaToCircleRRatio = SkTMin(sigmaToCircleRRatio, 8.f);
- SkFixed sigmaToCircleRRatioFixed;
- static const SkScalar kHalfPlaneThreshold = 0.1f;
- bool useHalfPlaneApprox = false;
- if (sigmaToCircleRRatio <= kHalfPlaneThreshold) {
- useHalfPlaneApprox = true;
- sigmaToCircleRRatioFixed = 0;
- *solidRadius = circleR - 3 * sigma;
- *textureRadius = 6 * sigma;
- } else {
- // Convert to fixed point for the key.
- 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;
- sigmaToCircleRRatio = SkFixedToScalar(sigmaToCircleRRatioFixed);
- sigma = circleR * sigmaToCircleRRatio;
- *solidRadius = 0;
- *textureRadius = circleR + 3 * sigma;
- }
-
- static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
- GrUniqueKey key;
- GrUniqueKey::Builder builder(&key, kDomain, 1);
- builder[0] = sigmaToCircleRRatioFixed;
- builder.finish();
-
- sk_sp<GrTextureProxy> blurProfile =
- proxyProvider->findOrCreateProxyByUniqueKey(key, kTopLeft_GrSurfaceOrigin);
- if (!blurProfile) {
- static constexpr int kProfileTextureWidth = 512;
-
- SkBitmap bm;
- if (!bm.tryAllocPixels(SkImageInfo::MakeA8(kProfileTextureWidth, 1))) {
- return nullptr;
- }
-
- if (useHalfPlaneApprox) {
- create_half_plane_profile(bm.getAddr8(0, 0), kProfileTextureWidth);
- } else {
- // Rescale params to the size of the texture we're creating.
- SkScalar scale = kProfileTextureWidth / *textureRadius;
- create_circle_profile(bm.getAddr8(0, 0), sigma * scale, circleR * scale,
- kProfileTextureWidth);
- }
-
- bm.setImmutable();
- sk_sp<SkImage> image = SkImage::MakeFromBitmap(bm);
-
- blurProfile = proxyProvider->createTextureProxy(std::move(image), kNone_GrSurfaceFlags, 1,
- SkBudgeted::kYes, SkBackingFit::kExact);
- if (!blurProfile) {
- return nullptr;
- }
-
- SkASSERT(blurProfile->origin() == kTopLeft_GrSurfaceOrigin);
- proxyProvider->assignUniqueKeyToProxy(key, blurProfile.get());
- }
-
- return blurProfile;
- }
-
- std::unique_ptr<GrFragmentProcessor> GrCircleBlurFragmentProcessor::Make(
- GrProxyProvider* proxyProvider, const SkRect& circle, float sigma) {
- float solidRadius;
- float textureRadius;
- sk_sp<GrTextureProxy> profile(create_profile_texture(proxyProvider, circle, sigma,
- &solidRadius, &textureRadius));
- if (!profile) {
- return nullptr;
- }
- return std::unique_ptr<GrFragmentProcessor>(new GrCircleBlurFragmentProcessor(
- circle, textureRadius, solidRadius, std::move(profile)));
- }
-}
-
-void main() {
- // We just want to compute "(length(vec) - circleData.z + 0.5) * circleData.w" but need to
- // rearrange for precision.
- half2 vec = half2((sk_FragCoord.x - circleData.x) * circleData.w,
- (sk_FragCoord.y - circleData.y) * circleData.w);
- half dist = length(vec) + (0.5 - circleData.z) * circleData.w;
- sk_OutColor = sk_InColor * texture(blurProfileSampler, half2(dist, 0.5)).a;
-}
-
-@test(testData) {
- SkScalar wh = testData->fRandom->nextRangeScalar(100.f, 1000.f);
- SkScalar sigma = testData->fRandom->nextRangeF(1.f,10.f);
- SkRect circle = SkRect::MakeWH(wh, wh);
- return GrCircleBlurFragmentProcessor::Make(testData->proxyProvider(), circle, sigma);
-}
diff --git a/src/effects/GrCircleBlurFragmentProcessor.h b/src/effects/GrCircleBlurFragmentProcessor.h
deleted file mode 100644
index 25dcb9ade6..0000000000
--- a/src/effects/GrCircleBlurFragmentProcessor.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2018 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-/**************************************************************************************************
- *** This file was autogenerated from GrCircleBlurFragmentProcessor.fp; do not modify.
- **************************************************************************************************/
-#ifndef GrCircleBlurFragmentProcessor_DEFINED
-#define GrCircleBlurFragmentProcessor_DEFINED
-#include "SkTypes.h"
-#if SK_SUPPORT_GPU
-#include "GrFragmentProcessor.h"
-#include "GrCoordTransform.h"
-class GrCircleBlurFragmentProcessor : public GrFragmentProcessor {
-public:
- SkRect circleRect() const { return fCircleRect; }
- float textureRadius() const { return fTextureRadius; }
- float solidRadius() const { return fSolidRadius; }
-
- static std::unique_ptr<GrFragmentProcessor> Make(GrProxyProvider*, const SkRect& circle,
- float sigma);
- GrCircleBlurFragmentProcessor(const GrCircleBlurFragmentProcessor& src);
- std::unique_ptr<GrFragmentProcessor> clone() const override;
- const char* name() const override { return "CircleBlurFragmentProcessor"; }
-
-private:
- GrCircleBlurFragmentProcessor(SkRect circleRect, float textureRadius, float solidRadius,
- sk_sp<GrTextureProxy> blurProfileSampler)
- : INHERITED(kGrCircleBlurFragmentProcessor_ClassID,
- (OptimizationFlags)kCompatibleWithCoverageAsAlpha_OptimizationFlag)
- , fCircleRect(circleRect)
- , fTextureRadius(textureRadius)
- , fSolidRadius(solidRadius)
- , fBlurProfileSampler(std::move(blurProfileSampler)) {
- this->addTextureSampler(&fBlurProfileSampler);
- }
- GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
- void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
- bool onIsEqual(const GrFragmentProcessor&) const override;
- GR_DECLARE_FRAGMENT_PROCESSOR_TEST
- SkRect fCircleRect;
- float fTextureRadius;
- float fSolidRadius;
- TextureSampler fBlurProfileSampler;
- typedef GrFragmentProcessor INHERITED;
-};
-#endif
-#endif
diff --git a/src/effects/SkBlurMaskFilter.cpp b/src/effects/SkBlurMaskFilter.cpp
index c71cbc6491..64a880cc8f 100644
--- a/src/effects/SkBlurMaskFilter.cpp
+++ b/src/effects/SkBlurMaskFilter.cpp
@@ -6,1037 +6,9 @@
*/
#include "SkBlurMaskFilter.h"
-#include "SkBlurMask.h"
-#include "SkBlurPriv.h"
-#include "SkGpuBlurUtils.h"
-#include "SkMaskFilterBase.h"
-#include "SkReadBuffer.h"
-#include "SkRRectPriv.h"
-#include "SkWriteBuffer.h"
-#include "SkMaskFilter.h"
-#include "SkRRect.h"
-#include "SkStringUtils.h"
-#include "SkStrokeRec.h"
-#include "SkVertices.h"
-
-#if SK_SUPPORT_GPU
-#include "GrCircleBlurFragmentProcessor.h"
-#include "GrClip.h"
-#include "GrContext.h"
-#include "GrFragmentProcessor.h"
-#include "GrRenderTargetContext.h"
-#include "GrResourceProvider.h"
-#include "GrShaderCaps.h"
-#include "GrStyle.h"
-#include "GrTextureProxy.h"
-#include "effects/GrRectBlurEffect.h"
-#include "effects/GrRRectBlurEffect.h"
-#include "effects/GrSimpleTextureEffect.h"
-#include "effects/GrTextureDomain.h"
-#include "glsl/GrGLSLFragmentProcessor.h"
-#include "glsl/GrGLSLFragmentShaderBuilder.h"
-#include "glsl/GrGLSLProgramDataManager.h"
-#include "glsl/GrGLSLUniformHandler.h"
-#endif
-
-class SkBlurMaskFilterImpl : public SkMaskFilterBase {
-public:
- SkBlurMaskFilterImpl(SkScalar sigma, SkBlurStyle, const SkRect& occluder, uint32_t flags);
-
- // overrides from SkMaskFilter
- SkMask::Format getFormat() const override;
- bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&,
- SkIPoint* margin) const override;
-
-#if SK_SUPPORT_GPU
- bool canFilterMaskGPU(const SkRRect& devRRect,
- const SkIRect& clipBounds,
- const SkMatrix& ctm,
- SkRect* maskRect) const override;
- bool directFilterMaskGPU(GrContext*,
- GrRenderTargetContext* renderTargetContext,
- GrPaint&&,
- const GrClip&,
- const SkMatrix& viewMatrix,
- const SkStrokeRec& strokeRec,
- const SkPath& path) const override;
- bool directFilterRRectMaskGPU(GrContext*,
- GrRenderTargetContext* renderTargetContext,
- GrPaint&&,
- const GrClip&,
- const SkMatrix& viewMatrix,
- const SkStrokeRec& strokeRec,
- const SkRRect& rrect,
- const SkRRect& devRRect) const override;
- sk_sp<GrTextureProxy> filterMaskGPU(GrContext*,
- sk_sp<GrTextureProxy> srcProxy,
- const SkMatrix& ctm,
- const SkIRect& maskRect) const override;
-#endif
-
- void computeFastBounds(const SkRect&, SkRect*) const override;
- bool asABlur(BlurRec*) const override;
-
- SK_TO_STRING_OVERRIDE()
- SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurMaskFilterImpl)
-
-protected:
- FilterReturn filterRectsToNine(const SkRect[], int count, const SkMatrix&,
- const SkIRect& clipBounds,
- NinePatch*) const override;
-
- FilterReturn filterRRectToNine(const SkRRect&, const SkMatrix&,
- const SkIRect& clipBounds,
- NinePatch*) const override;
-
- bool filterRectMask(SkMask* dstM, const SkRect& r, const SkMatrix& matrix,
- SkIPoint* margin, SkMask::CreateMode createMode) const;
- bool filterRRectMask(SkMask* dstM, const SkRRect& r, const SkMatrix& matrix,
- SkIPoint* margin, SkMask::CreateMode createMode) const;
-
- bool ignoreXform() const {
- return SkToBool(fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag);
- }
-
-private:
- // To avoid unseemly allocation requests (esp. for finite platforms like
- // handset) we limit the radius so something manageable. (as opposed to
- // a request like 10,000)
- static const SkScalar kMAX_BLUR_SIGMA;
-
- SkScalar fSigma;
- SkBlurStyle fBlurStyle;
- SkRect fOccluder;
- uint32_t fBlurFlags;
-
- SkBlurQuality getQuality() const {
- return (fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag) ?
- kHigh_SkBlurQuality : kLow_SkBlurQuality;
- }
-
- SkBlurMaskFilterImpl(SkReadBuffer&);
- void flatten(SkWriteBuffer&) const override;
-
- SkScalar computeXformedSigma(const SkMatrix& ctm) const {
- SkScalar xformedSigma = this->ignoreXform() ? fSigma : ctm.mapRadius(fSigma);
- return SkMinScalar(xformedSigma, kMAX_BLUR_SIGMA);
- }
-
- friend class SkBlurMaskFilter;
-
- typedef SkMaskFilter INHERITED;
-};
-
-const SkScalar SkBlurMaskFilterImpl::kMAX_BLUR_SIGMA = SkIntToScalar(128);
sk_sp<SkMaskFilter> SkBlurMaskFilter::Make(SkBlurStyle style, SkScalar sigma,
const SkRect& occluder, uint32_t flags) {
- SkASSERT(!(flags & ~SkBlurMaskFilter::kAll_BlurFlag));
- SkASSERT(style <= kLastEnum_SkBlurStyle);
-
- if (!SkScalarIsFinite(sigma) || sigma <= 0) {
- return nullptr;
- }
-
- return sk_sp<SkMaskFilter>(new SkBlurMaskFilterImpl(sigma, style, occluder, flags));
-}
-
-// linearly interpolate between y1 & y3 to match x2's position between x1 & x3
-static SkScalar interp(SkScalar x1, SkScalar x2, SkScalar x3, SkScalar y1, SkScalar y3) {
- SkASSERT(x1 <= x2 && x2 <= x3);
- SkASSERT(y1 <= y3);
-
- SkScalar t = (x2 - x1) / (x3 - x1);
- return y1 + t * (y3 - y1);
-}
-
-// Insert 'lower' and 'higher' into 'array1' and insert a new value at each matching insertion
-// point in 'array2' that linearly interpolates between the existing values.
-// Return a bit mask which contains a copy of 'inputMask' for all the cells between the two
-// insertion points.
-static uint32_t insert_into_arrays(SkScalar* array1, SkScalar* array2,
- SkScalar lower, SkScalar higher,
- int* num, uint32_t inputMask, int maskSize) {
- SkASSERT(lower < higher);
- SkASSERT(lower >= array1[0] && higher <= array1[*num-1]);
-
- int32_t skipMask = 0x0;
- int i;
- for (i = 0; i < *num; ++i) {
- if (lower >= array1[i] && lower < array1[i+1]) {
- if (!SkScalarNearlyEqual(lower, array1[i])) {
- memmove(&array1[i+2], &array1[i+1], (*num-i-1)*sizeof(SkScalar));
- array1[i+1] = lower;
- memmove(&array2[i+2], &array2[i+1], (*num-i-1)*sizeof(SkScalar));
- array2[i+1] = interp(array1[i], lower, array1[i+2], array2[i], array2[i+2]);
- i++;
- (*num)++;
- }
- break;
- }
- }
- for ( ; i < *num; ++i) {
- skipMask |= inputMask << (i*maskSize);
- if (higher > array1[i] && higher <= array1[i+1]) {
- if (!SkScalarNearlyEqual(higher, array1[i+1])) {
- memmove(&array1[i+2], &array1[i+1], (*num-i-1)*sizeof(SkScalar));
- array1[i+1] = higher;
- memmove(&array2[i+2], &array2[i+1], (*num-i-1)*sizeof(SkScalar));
- array2[i+1] = interp(array1[i], higher, array1[i+2], array2[i], array2[i+2]);
- (*num)++;
- }
- break;
- }
- }
-
- return skipMask;
-}
-
-bool SkComputeBlurredRRectParams(const SkRRect& srcRRect, const SkRRect& devRRect,
- const SkRect& occluder,
- SkScalar sigma, SkScalar xformedSigma,
- SkRRect* rrectToDraw,
- SkISize* widthHeight,
- SkScalar rectXs[kSkBlurRRectMaxDivisions],
- SkScalar rectYs[kSkBlurRRectMaxDivisions],
- SkScalar texXs[kSkBlurRRectMaxDivisions],
- SkScalar texYs[kSkBlurRRectMaxDivisions],
- int* numXs, int* numYs, uint32_t* skipMask) {
- unsigned int devBlurRadius = 3*SkScalarCeilToInt(xformedSigma-1/6.0f);
- SkScalar srcBlurRadius = 3.0f * sigma;
-
- const SkRect& devOrig = devRRect.getBounds();
- const SkVector& devRadiiUL = devRRect.radii(SkRRect::kUpperLeft_Corner);
- const SkVector& devRadiiUR = devRRect.radii(SkRRect::kUpperRight_Corner);
- const SkVector& devRadiiLR = devRRect.radii(SkRRect::kLowerRight_Corner);
- const SkVector& devRadiiLL = devRRect.radii(SkRRect::kLowerLeft_Corner);
-
- const int devLeft = SkScalarCeilToInt(SkTMax<SkScalar>(devRadiiUL.fX, devRadiiLL.fX));
- const int devTop = SkScalarCeilToInt(SkTMax<SkScalar>(devRadiiUL.fY, devRadiiUR.fY));
- const int devRight = SkScalarCeilToInt(SkTMax<SkScalar>(devRadiiUR.fX, devRadiiLR.fX));
- const int devBot = SkScalarCeilToInt(SkTMax<SkScalar>(devRadiiLL.fY, devRadiiLR.fY));
-
- // This is a conservative check for nine-patchability
- if (devOrig.fLeft + devLeft + devBlurRadius >= devOrig.fRight - devRight - devBlurRadius ||
- devOrig.fTop + devTop + devBlurRadius >= devOrig.fBottom - devBot - devBlurRadius) {
- return false;
- }
-
- const SkVector& srcRadiiUL = srcRRect.radii(SkRRect::kUpperLeft_Corner);
- const SkVector& srcRadiiUR = srcRRect.radii(SkRRect::kUpperRight_Corner);
- const SkVector& srcRadiiLR = srcRRect.radii(SkRRect::kLowerRight_Corner);
- const SkVector& srcRadiiLL = srcRRect.radii(SkRRect::kLowerLeft_Corner);
-
- const SkScalar srcLeft = SkTMax<SkScalar>(srcRadiiUL.fX, srcRadiiLL.fX);
- const SkScalar srcTop = SkTMax<SkScalar>(srcRadiiUL.fY, srcRadiiUR.fY);
- const SkScalar srcRight = SkTMax<SkScalar>(srcRadiiUR.fX, srcRadiiLR.fX);
- const SkScalar srcBot = SkTMax<SkScalar>(srcRadiiLL.fY, srcRadiiLR.fY);
-
- int newRRWidth = 2*devBlurRadius + devLeft + devRight + 1;
- int newRRHeight = 2*devBlurRadius + devTop + devBot + 1;
- widthHeight->fWidth = newRRWidth + 2 * devBlurRadius;
- widthHeight->fHeight = newRRHeight + 2 * devBlurRadius;
-
- const SkRect srcProxyRect = srcRRect.getBounds().makeOutset(srcBlurRadius, srcBlurRadius);
-
- rectXs[0] = srcProxyRect.fLeft;
- rectXs[1] = srcProxyRect.fLeft + 2*srcBlurRadius + srcLeft;
- rectXs[2] = srcProxyRect.fRight - 2*srcBlurRadius - srcRight;
- rectXs[3] = srcProxyRect.fRight;
-
- rectYs[0] = srcProxyRect.fTop;
- rectYs[1] = srcProxyRect.fTop + 2*srcBlurRadius + srcTop;
- rectYs[2] = srcProxyRect.fBottom - 2*srcBlurRadius - srcBot;
- rectYs[3] = srcProxyRect.fBottom;
-
- texXs[0] = 0.0f;
- texXs[1] = 2.0f*devBlurRadius + devLeft;
- texXs[2] = 2.0f*devBlurRadius + devLeft + 1;
- texXs[3] = SkIntToScalar(widthHeight->fWidth);
-
- texYs[0] = 0.0f;
- texYs[1] = 2.0f*devBlurRadius + devTop;
- texYs[2] = 2.0f*devBlurRadius + devTop + 1;
- texYs[3] = SkIntToScalar(widthHeight->fHeight);
-
- SkRect temp = occluder;
-
- *numXs = 4;
- *numYs = 4;
- *skipMask = 0;
- if (!temp.isEmpty() && (srcProxyRect.contains(temp) || temp.intersect(srcProxyRect))) {
- *skipMask = insert_into_arrays(rectXs, texXs, temp.fLeft, temp.fRight, numXs, 0x1, 1);
- *skipMask = insert_into_arrays(rectYs, texYs, temp.fTop, temp.fBottom,
- numYs, *skipMask, *numXs-1);
- }
-
- const SkRect newRect = SkRect::MakeXYWH(SkIntToScalar(devBlurRadius),
- SkIntToScalar(devBlurRadius),
- SkIntToScalar(newRRWidth),
- SkIntToScalar(newRRHeight));
- SkVector newRadii[4];
- newRadii[0] = { SkScalarCeilToScalar(devRadiiUL.fX), SkScalarCeilToScalar(devRadiiUL.fY) };
- newRadii[1] = { SkScalarCeilToScalar(devRadiiUR.fX), SkScalarCeilToScalar(devRadiiUR.fY) };
- newRadii[2] = { SkScalarCeilToScalar(devRadiiLR.fX), SkScalarCeilToScalar(devRadiiLR.fY) };
- newRadii[3] = { SkScalarCeilToScalar(devRadiiLL.fX), SkScalarCeilToScalar(devRadiiLL.fY) };
-
- rrectToDraw->setRectRadii(newRect, newRadii);
- return true;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkScalar sigma, SkBlurStyle style,
- const SkRect& occluder, uint32_t flags)
- : fSigma(sigma)
- , fBlurStyle(style)
- , fOccluder(occluder)
- , fBlurFlags(flags) {
- SkASSERT(fSigma > 0);
- SkASSERT((unsigned)style <= kLastEnum_SkBlurStyle);
- SkASSERT(flags <= SkBlurMaskFilter::kAll_BlurFlag);
-}
-
-SkMask::Format SkBlurMaskFilterImpl::getFormat() const {
- return SkMask::kA8_Format;
+ bool respectCTM = !(flags & kIgnoreTransform_BlurFlag);
+ return SkMaskFilter::MakeBlur(style, sigma, occluder, respectCTM);
}
-
-bool SkBlurMaskFilterImpl::asABlur(BlurRec* rec) const {
- if (this->ignoreXform()) {
- return false;
- }
-
- if (rec) {
- rec->fSigma = fSigma;
- rec->fStyle = fBlurStyle;
- rec->fQuality = this->getQuality();
- }
- return true;
-}
-
-bool SkBlurMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src,
- const SkMatrix& matrix,
- SkIPoint* margin) const {
- SkScalar sigma = this->computeXformedSigma(matrix);
- return SkBlurMask::BoxBlur(dst, src, sigma, fBlurStyle, this->getQuality(), margin);
-}
-
-bool SkBlurMaskFilterImpl::filterRectMask(SkMask* dst, const SkRect& r,
- const SkMatrix& matrix,
- SkIPoint* margin, SkMask::CreateMode createMode) const {
- SkScalar sigma = computeXformedSigma(matrix);
-
- return SkBlurMask::BlurRect(sigma, dst, r, fBlurStyle, margin, createMode);
-}
-
-bool SkBlurMaskFilterImpl::filterRRectMask(SkMask* dst, const SkRRect& r,
- const SkMatrix& matrix,
- SkIPoint* margin, SkMask::CreateMode createMode) const {
- SkScalar sigma = computeXformedSigma(matrix);
-
- return SkBlurMask::BlurRRect(sigma, dst, r, fBlurStyle, margin, createMode);
-}
-
-#include "SkCanvas.h"
-
-static bool prepare_to_draw_into_mask(const SkRect& bounds, SkMask* mask) {
- SkASSERT(mask != nullptr);
-
- mask->fBounds = bounds.roundOut();
- mask->fRowBytes = SkAlign4(mask->fBounds.width());
- mask->fFormat = SkMask::kA8_Format;
- const size_t size = mask->computeImageSize();
- mask->fImage = SkMask::AllocImage(size, SkMask::kZeroInit_Alloc);
- if (nullptr == mask->fImage) {
- return false;
- }
- return true;
-}
-
-static bool draw_rrect_into_mask(const SkRRect rrect, SkMask* mask) {
- if (!prepare_to_draw_into_mask(rrect.rect(), mask)) {
- return false;
- }
-
- // FIXME: This code duplicates code in draw_rects_into_mask, below. Is there a
- // clean way to share more code?
- SkBitmap bitmap;
- bitmap.installMaskPixels(*mask);
-
- SkCanvas canvas(bitmap);
- canvas.translate(-SkIntToScalar(mask->fBounds.left()),
- -SkIntToScalar(mask->fBounds.top()));
-
- SkPaint paint;
- paint.setAntiAlias(true);
- canvas.drawRRect(rrect, paint);
- return true;
-}
-
-static bool draw_rects_into_mask(const SkRect rects[], int count, SkMask* mask) {
- if (!prepare_to_draw_into_mask(rects[0], mask)) {
- return false;
- }
-
- SkBitmap bitmap;
- bitmap.installPixels(SkImageInfo::Make(mask->fBounds.width(),
- mask->fBounds.height(),
- kAlpha_8_SkColorType,
- kPremul_SkAlphaType),
- mask->fImage, mask->fRowBytes);
-
- SkCanvas canvas(bitmap);
- canvas.translate(-SkIntToScalar(mask->fBounds.left()),
- -SkIntToScalar(mask->fBounds.top()));
-
- SkPaint paint;
- paint.setAntiAlias(true);
-
- if (1 == count) {
- canvas.drawRect(rects[0], paint);
- } else {
- // todo: do I need a fast way to do this?
- SkPath path;
- path.addRect(rects[0]);
- path.addRect(rects[1]);
- path.setFillType(SkPath::kEvenOdd_FillType);
- canvas.drawPath(path, paint);
- }
- return true;
-}
-
-static bool rect_exceeds(const SkRect& r, SkScalar v) {
- return r.fLeft < -v || r.fTop < -v || r.fRight > v || r.fBottom > v ||
- r.width() > v || r.height() > v;
-}
-
-#include "SkMaskCache.h"
-
-static SkCachedData* copy_mask_to_cacheddata(SkMask* mask) {
- const size_t size = mask->computeTotalImageSize();
- SkCachedData* data = SkResourceCache::NewCachedData(size);
- if (data) {
- memcpy(data->writable_data(), mask->fImage, size);
- SkMask::FreeImage(mask->fImage);
- mask->fImage = (uint8_t*)data->data();
- }
- return data;
-}
-
-static SkCachedData* find_cached_rrect(SkMask* mask, SkScalar sigma, SkBlurStyle style,
- SkBlurQuality quality, const SkRRect& rrect) {
- return SkMaskCache::FindAndRef(sigma, style, quality, rrect, mask);
-}
-
-static SkCachedData* add_cached_rrect(SkMask* mask, SkScalar sigma, SkBlurStyle style,
- SkBlurQuality quality, const SkRRect& rrect) {
- SkCachedData* cache = copy_mask_to_cacheddata(mask);
- if (cache) {
- SkMaskCache::Add(sigma, style, quality, rrect, *mask, cache);
- }
- return cache;
-}
-
-static SkCachedData* find_cached_rects(SkMask* mask, SkScalar sigma, SkBlurStyle style,
- SkBlurQuality quality, const SkRect rects[], int count) {
- return SkMaskCache::FindAndRef(sigma, style, quality, rects, count, mask);
-}
-
-static SkCachedData* add_cached_rects(SkMask* mask, SkScalar sigma, SkBlurStyle style,
- SkBlurQuality quality, const SkRect rects[], int count) {
- SkCachedData* cache = copy_mask_to_cacheddata(mask);
- if (cache) {
- SkMaskCache::Add(sigma, style, quality, rects, count, *mask, cache);
- }
- return cache;
-}
-
-#ifdef SK_IGNORE_FAST_RRECT_BLUR
- // Use the faster analytic blur approach for ninepatch round rects
- static const bool c_analyticBlurRRect{false};
-#else
- static const bool c_analyticBlurRRect{true};
-#endif
-
-SkMaskFilterBase::FilterReturn
-SkBlurMaskFilterImpl::filterRRectToNine(const SkRRect& rrect, const SkMatrix& matrix,
- const SkIRect& clipBounds,
- NinePatch* patch) const {
- SkASSERT(patch != nullptr);
- switch (rrect.getType()) {
- case SkRRect::kEmpty_Type:
- // Nothing to draw.
- return kFalse_FilterReturn;
-
- case SkRRect::kRect_Type:
- // We should have caught this earlier.
- SkASSERT(false);
- // Fall through.
- case SkRRect::kOval_Type:
- // The nine patch special case does not handle ovals, and we
- // already have code for rectangles.
- return kUnimplemented_FilterReturn;
-
- // These three can take advantage of this fast path.
- case SkRRect::kSimple_Type:
- case SkRRect::kNinePatch_Type:
- case SkRRect::kComplex_Type:
- break;
- }
-
- // TODO: report correct metrics for innerstyle, where we do not grow the
- // total bounds, but we do need an inset the size of our blur-radius
- if (kInner_SkBlurStyle == fBlurStyle) {
- return kUnimplemented_FilterReturn;
- }
-
- // TODO: take clipBounds into account to limit our coordinates up front
- // for now, just skip too-large src rects (to take the old code path).
- if (rect_exceeds(rrect.rect(), SkIntToScalar(32767))) {
- return kUnimplemented_FilterReturn;
- }
-
- SkIPoint margin;
- SkMask srcM, dstM;
- srcM.fBounds = rrect.rect().roundOut();
- srcM.fFormat = SkMask::kA8_Format;
- srcM.fRowBytes = 0;
-
- bool filterResult = false;
- if (c_analyticBlurRRect) {
- // special case for fast round rect blur
- // don't actually do the blur the first time, just compute the correct size
- filterResult = this->filterRRectMask(&dstM, rrect, matrix, &margin,
- SkMask::kJustComputeBounds_CreateMode);
- }
-
- if (!filterResult) {
- filterResult = this->filterMask(&dstM, srcM, matrix, &margin);
- }
-
- if (!filterResult) {
- return kFalse_FilterReturn;
- }
-
- // Now figure out the appropriate width and height of the smaller round rectangle
- // to stretch. It will take into account the larger radius per side as well as double
- // the margin, to account for inner and outer blur.
- const SkVector& UL = rrect.radii(SkRRect::kUpperLeft_Corner);
- const SkVector& UR = rrect.radii(SkRRect::kUpperRight_Corner);
- const SkVector& LR = rrect.radii(SkRRect::kLowerRight_Corner);
- const SkVector& LL = rrect.radii(SkRRect::kLowerLeft_Corner);
-
- const SkScalar leftUnstretched = SkTMax(UL.fX, LL.fX) + SkIntToScalar(2 * margin.fX);
- const SkScalar rightUnstretched = SkTMax(UR.fX, LR.fX) + SkIntToScalar(2 * margin.fX);
-
- // Extra space in the middle to ensure an unchanging piece for stretching. Use 3 to cover
- // any fractional space on either side plus 1 for the part to stretch.
- const SkScalar stretchSize = SkIntToScalar(3);
-
- const SkScalar totalSmallWidth = leftUnstretched + rightUnstretched + stretchSize;
- if (totalSmallWidth >= rrect.rect().width()) {
- // There is no valid piece to stretch.
- return kUnimplemented_FilterReturn;
- }
-
- const SkScalar topUnstretched = SkTMax(UL.fY, UR.fY) + SkIntToScalar(2 * margin.fY);
- const SkScalar bottomUnstretched = SkTMax(LL.fY, LR.fY) + SkIntToScalar(2 * margin.fY);
-
- const SkScalar totalSmallHeight = topUnstretched + bottomUnstretched + stretchSize;
- if (totalSmallHeight >= rrect.rect().height()) {
- // There is no valid piece to stretch.
- return kUnimplemented_FilterReturn;
- }
-
- SkRect smallR = SkRect::MakeWH(totalSmallWidth, totalSmallHeight);
-
- SkRRect smallRR;
- SkVector radii[4];
- radii[SkRRect::kUpperLeft_Corner] = UL;
- radii[SkRRect::kUpperRight_Corner] = UR;
- radii[SkRRect::kLowerRight_Corner] = LR;
- radii[SkRRect::kLowerLeft_Corner] = LL;
- smallRR.setRectRadii(smallR, radii);
-
- const SkScalar sigma = this->computeXformedSigma(matrix);
- SkCachedData* cache = find_cached_rrect(&patch->fMask, sigma, fBlurStyle,
- this->getQuality(), smallRR);
- if (!cache) {
- bool analyticBlurWorked = false;
- if (c_analyticBlurRRect) {
- analyticBlurWorked =
- this->filterRRectMask(&patch->fMask, smallRR, matrix, &margin,
- SkMask::kComputeBoundsAndRenderImage_CreateMode);
- }
-
- if (!analyticBlurWorked) {
- if (!draw_rrect_into_mask(smallRR, &srcM)) {
- return kFalse_FilterReturn;
- }
-
- SkAutoMaskFreeImage amf(srcM.fImage);
-
- if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) {
- return kFalse_FilterReturn;
- }
- }
- cache = add_cached_rrect(&patch->fMask, sigma, fBlurStyle, this->getQuality(), smallRR);
- }
-
- patch->fMask.fBounds.offsetTo(0, 0);
- patch->fOuterRect = dstM.fBounds;
- patch->fCenter.fX = SkScalarCeilToInt(leftUnstretched) + 1;
- patch->fCenter.fY = SkScalarCeilToInt(topUnstretched) + 1;
- SkASSERT(nullptr == patch->fCache);
- patch->fCache = cache; // transfer ownership to patch
- return kTrue_FilterReturn;
-}
-
-// Use the faster analytic blur approach for ninepatch rects
-static const bool c_analyticBlurNinepatch{true};
-
-SkMaskFilterBase::FilterReturn
-SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count,
- const SkMatrix& matrix,
- const SkIRect& clipBounds,
- NinePatch* patch) const {
- if (count < 1 || count > 2) {
- return kUnimplemented_FilterReturn;
- }
-
- // TODO: report correct metrics for innerstyle, where we do not grow the
- // total bounds, but we do need an inset the size of our blur-radius
- if (kInner_SkBlurStyle == fBlurStyle || kOuter_SkBlurStyle == fBlurStyle) {
- return kUnimplemented_FilterReturn;
- }
-
- // TODO: take clipBounds into account to limit our coordinates up front
- // for now, just skip too-large src rects (to take the old code path).
- if (rect_exceeds(rects[0], SkIntToScalar(32767))) {
- return kUnimplemented_FilterReturn;
- }
-
- SkIPoint margin;
- SkMask srcM, dstM;
- srcM.fBounds = rects[0].roundOut();
- srcM.fFormat = SkMask::kA8_Format;
- srcM.fRowBytes = 0;
-
- bool filterResult = false;
- if (count == 1 && c_analyticBlurNinepatch) {
- // special case for fast rect blur
- // don't actually do the blur the first time, just compute the correct size
- filterResult = this->filterRectMask(&dstM, rects[0], matrix, &margin,
- SkMask::kJustComputeBounds_CreateMode);
- } else {
- filterResult = this->filterMask(&dstM, srcM, matrix, &margin);
- }
-
- if (!filterResult) {
- return kFalse_FilterReturn;
- }
-
- /*
- * smallR is the smallest version of 'rect' that will still guarantee that
- * we get the same blur results on all edges, plus 1 center row/col that is
- * representative of the extendible/stretchable edges of the ninepatch.
- * Since our actual edge may be fractional we inset 1 more to be sure we
- * don't miss any interior blur.
- * x is an added pixel of blur, and { and } are the (fractional) edge
- * pixels from the original rect.
- *
- * x x { x x .... x x } x x
- *
- * Thus, in this case, we inset by a total of 5 (on each side) beginning
- * with our outer-rect (dstM.fBounds)
- */
- SkRect smallR[2];
- SkIPoint center;
-
- // +2 is from +1 for each edge (to account for possible fractional edges
- int smallW = dstM.fBounds.width() - srcM.fBounds.width() + 2;
- int smallH = dstM.fBounds.height() - srcM.fBounds.height() + 2;
- SkIRect innerIR;
-
- if (1 == count) {
- innerIR = srcM.fBounds;
- center.set(smallW, smallH);
- } else {
- SkASSERT(2 == count);
- rects[1].roundIn(&innerIR);
- center.set(smallW + (innerIR.left() - srcM.fBounds.left()),
- smallH + (innerIR.top() - srcM.fBounds.top()));
- }
-
- // +1 so we get a clean, stretchable, center row/col
- smallW += 1;
- smallH += 1;
-
- // we want the inset amounts to be integral, so we don't change any
- // fractional phase on the fRight or fBottom of our smallR.
- const SkScalar dx = SkIntToScalar(innerIR.width() - smallW);
- const SkScalar dy = SkIntToScalar(innerIR.height() - smallH);
- if (dx < 0 || dy < 0) {
- // we're too small, relative to our blur, to break into nine-patch,
- // so we ask to have our normal filterMask() be called.
- return kUnimplemented_FilterReturn;
- }
-
- smallR[0].set(rects[0].left(), rects[0].top(), rects[0].right() - dx, rects[0].bottom() - dy);
- if (smallR[0].width() < 2 || smallR[0].height() < 2) {
- return kUnimplemented_FilterReturn;
- }
- if (2 == count) {
- smallR[1].set(rects[1].left(), rects[1].top(),
- rects[1].right() - dx, rects[1].bottom() - dy);
- SkASSERT(!smallR[1].isEmpty());
- }
-
- const SkScalar sigma = this->computeXformedSigma(matrix);
- SkCachedData* cache = find_cached_rects(&patch->fMask, sigma, fBlurStyle,
- this->getQuality(), smallR, count);
- if (!cache) {
- if (count > 1 || !c_analyticBlurNinepatch) {
- if (!draw_rects_into_mask(smallR, count, &srcM)) {
- return kFalse_FilterReturn;
- }
-
- SkAutoMaskFreeImage amf(srcM.fImage);
-
- if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) {
- return kFalse_FilterReturn;
- }
- } else {
- if (!this->filterRectMask(&patch->fMask, smallR[0], matrix, &margin,
- SkMask::kComputeBoundsAndRenderImage_CreateMode)) {
- return kFalse_FilterReturn;
- }
- }
- cache = add_cached_rects(&patch->fMask, sigma, fBlurStyle, this->getQuality(), smallR, count);
- }
- patch->fMask.fBounds.offsetTo(0, 0);
- patch->fOuterRect = dstM.fBounds;
- patch->fCenter = center;
- SkASSERT(nullptr == patch->fCache);
- patch->fCache = cache; // transfer ownership to patch
- return kTrue_FilterReturn;
-}
-
-void SkBlurMaskFilterImpl::computeFastBounds(const SkRect& src,
- SkRect* dst) const {
- SkScalar pad = 3.0f * fSigma;
-
- dst->set(src.fLeft - pad, src.fTop - pad,
- src.fRight + pad, src.fBottom + pad);
-}
-
-sk_sp<SkFlattenable> SkBlurMaskFilterImpl::CreateProc(SkReadBuffer& buffer) {
- const SkScalar sigma = buffer.readScalar();
- SkBlurStyle style = buffer.read32LE(kLastEnum_SkBlurStyle);
- unsigned flags = buffer.read32LE(SkBlurMaskFilter::kAll_BlurFlag);
-
- SkRect occluder;
- buffer.readRect(&occluder);
-
- return SkBlurMaskFilter::Make((SkBlurStyle)style, sigma, occluder, flags);
-}
-
-void SkBlurMaskFilterImpl::flatten(SkWriteBuffer& buffer) const {
- buffer.writeScalar(fSigma);
- buffer.writeUInt(fBlurStyle);
- buffer.writeUInt(fBlurFlags);
- buffer.writeRect(fOccluder);
-}
-
-
-#if SK_SUPPORT_GPU
-
-bool SkBlurMaskFilterImpl::directFilterMaskGPU(GrContext* context,
- GrRenderTargetContext* renderTargetContext,
- GrPaint&& paint,
- const GrClip& clip,
- const SkMatrix& viewMatrix,
- const SkStrokeRec& strokeRec,
- const SkPath& path) const {
- SkASSERT(renderTargetContext);
-
- if (fBlurStyle != kNormal_SkBlurStyle) {
- return false;
- }
-
- // TODO: we could handle blurred stroked circles
- if (!strokeRec.isFillStyle()) {
- return false;
- }
-
- SkScalar xformedSigma = this->computeXformedSigma(viewMatrix);
-
- GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
- std::unique_ptr<GrFragmentProcessor> fp;
-
- SkRect rect;
- if (path.isRect(&rect)) {
- SkScalar pad = 3.0f * xformedSigma;
- rect.outset(pad, pad);
-
- fp = GrRectBlurEffect::Make(proxyProvider, rect, xformedSigma);
- } else if (path.isOval(&rect) && SkScalarNearlyEqual(rect.width(), rect.height())) {
- fp = GrCircleBlurFragmentProcessor::Make(proxyProvider, rect, xformedSigma);
-
- // expand the rect for the coverage geometry
- int pad = SkScalarCeilToInt(6*xformedSigma)/2;
- rect.outset(SkIntToScalar(pad), SkIntToScalar(pad));
- } else {
- return false;
- }
-
- if (!fp) {
- return false;
- }
-
- SkMatrix inverse;
- if (!viewMatrix.invert(&inverse)) {
- return false;
- }
-
- paint.addCoverageFragmentProcessor(std::move(fp));
- renderTargetContext->fillRectWithLocalMatrix(clip, std::move(paint), GrAA::kNo, SkMatrix::I(),
- rect, inverse);
- return true;
-}
-
-bool SkBlurMaskFilterImpl::directFilterRRectMaskGPU(GrContext* context,
- GrRenderTargetContext* renderTargetContext,
- GrPaint&& paint,
- const GrClip& clip,
- const SkMatrix& viewMatrix,
- const SkStrokeRec& strokeRec,
- const SkRRect& srcRRect,
- const SkRRect& devRRect) const {
- SkASSERT(renderTargetContext);
-
- if (fBlurStyle != kNormal_SkBlurStyle) {
- return false;
- }
-
- if (!strokeRec.isFillStyle()) {
- return false;
- }
-
- GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
- SkScalar xformedSigma = this->computeXformedSigma(viewMatrix);
-
- if (devRRect.isRect() || SkRRectPriv::IsCircle(devRRect)) {
- std::unique_ptr<GrFragmentProcessor> fp;
- if (devRRect.isRect()) {
- SkScalar pad = 3.0f * xformedSigma;
- const SkRect dstCoverageRect = devRRect.rect().makeOutset(pad, pad);
-
- fp = GrRectBlurEffect::Make(proxyProvider, dstCoverageRect, xformedSigma);
- } else {
- fp = GrCircleBlurFragmentProcessor::Make(proxyProvider,
- devRRect.rect(), xformedSigma);
- }
-
- if (!fp) {
- return false;
- }
- paint.addCoverageFragmentProcessor(std::move(fp));
-
- SkRect srcProxyRect = srcRRect.rect();
- SkScalar outsetX = 3.0f*fSigma;
- SkScalar outsetY = 3.0f*fSigma;
- if (this->ignoreXform()) {
- // When we're ignoring the CTM the padding added to the source rect also needs to ignore
- // the CTM. The matrix passed in here is guaranteed to be just scale and translate so we
- // can just grab the X and Y scales off the matrix and pre-undo the scale.
- outsetX /= viewMatrix.getScaleX();
- outsetY /= viewMatrix.getScaleY();
- }
- srcProxyRect.outset(outsetX, outsetY);
-
- renderTargetContext->drawRect(clip, std::move(paint), GrAA::kNo, viewMatrix, srcProxyRect);
- return true;
- }
-
- auto fp = GrRRectBlurEffect::Make(context, fSigma, xformedSigma, srcRRect, devRRect);
- if (!fp) {
- return false;
- }
-
- if (!this->ignoreXform()) {
- SkRect srcProxyRect = srcRRect.rect();
- srcProxyRect.outset(3.0f*fSigma, 3.0f*fSigma);
-
- sk_sp<SkVertices> vertices = nullptr;
- SkRect temp = fOccluder;
-
- if (!temp.isEmpty() && (srcProxyRect.contains(temp) || temp.intersect(srcProxyRect))) {
- SkVertices::Builder builder(SkVertices::kTriangles_VertexMode, 8, 24, 0);
- srcProxyRect.toQuad(builder.positions());
- temp.toQuad(builder.positions() + 4);
-
- static const uint16_t ringI[24] = { 0, 1, 5, 5, 4, 0,
- 1, 2, 6, 6, 5, 1,
- 2, 3, 7, 7, 6, 2,
- 3, 0, 4, 4, 7, 3 };
- memcpy(builder.indices(), ringI, sizeof(ringI));
- vertices = builder.detach();
- } else {
- // full rect case
- SkVertices::Builder builder(SkVertices::kTriangles_VertexMode, 4, 6, 0);
- srcProxyRect.toQuad(builder.positions());
-
- static const uint16_t fullI[6] = { 0, 1, 2, 0, 2, 3 };
- memcpy(builder.indices(), fullI, sizeof(fullI));
- vertices = builder.detach();
- }
-
- paint.addCoverageFragmentProcessor(std::move(fp));
- renderTargetContext->drawVertices(clip, std::move(paint), viewMatrix, std::move(vertices));
- } else {
- SkMatrix inverse;
- if (!viewMatrix.invert(&inverse)) {
- return false;
- }
-
- float extra=3.f*SkScalarCeilToScalar(xformedSigma-1/6.0f);
- SkRect proxyRect = devRRect.rect();
- proxyRect.outset(extra, extra);
-
- paint.addCoverageFragmentProcessor(std::move(fp));
- renderTargetContext->fillRectWithLocalMatrix(clip, std::move(paint), GrAA::kNo,
- SkMatrix::I(), proxyRect, inverse);
- }
-
- return true;
-}
-
-bool SkBlurMaskFilterImpl::canFilterMaskGPU(const SkRRect& devRRect,
- const SkIRect& clipBounds,
- const SkMatrix& ctm,
- SkRect* maskRect) const {
- SkScalar xformedSigma = this->computeXformedSigma(ctm);
- if (xformedSigma <= 0) {
- return false;
- }
-
- // We always do circles and simple circular rrects on the GPU
- if (!SkRRectPriv::IsCircle(devRRect) && !SkRRectPriv::IsSimpleCircular(devRRect)) {
- static const SkScalar kMIN_GPU_BLUR_SIZE = SkIntToScalar(64);
- static const SkScalar kMIN_GPU_BLUR_SIGMA = SkIntToScalar(32);
-
- if (devRRect.width() <= kMIN_GPU_BLUR_SIZE &&
- devRRect.height() <= kMIN_GPU_BLUR_SIZE &&
- xformedSigma <= kMIN_GPU_BLUR_SIGMA) {
- // We prefer to blur small rects with small radii on the CPU.
- return false;
- }
- }
-
- if (nullptr == maskRect) {
- // don't need to compute maskRect
- return true;
- }
-
- float sigma3 = 3 * SkScalarToFloat(xformedSigma);
-
- SkRect clipRect = SkRect::Make(clipBounds);
- SkRect srcRect(devRRect.rect());
-
- // Outset srcRect and clipRect by 3 * sigma, to compute affected blur area.
- srcRect.outset(sigma3, sigma3);
- clipRect.outset(sigma3, sigma3);
- if (!srcRect.intersect(clipRect)) {
- srcRect.setEmpty();
- }
- *maskRect = srcRect;
- return true;
-}
-
-sk_sp<GrTextureProxy> SkBlurMaskFilterImpl::filterMaskGPU(GrContext* context,
- sk_sp<GrTextureProxy> srcProxy,
- const SkMatrix& ctm,
- const SkIRect& maskRect) const {
- // 'maskRect' isn't snapped to the UL corner but the mask in 'src' is.
- const SkIRect clipRect = SkIRect::MakeWH(maskRect.width(), maskRect.height());
-
- SkScalar xformedSigma = this->computeXformedSigma(ctm);
- SkASSERT(xformedSigma > 0);
-
- // If we're doing a normal blur, we can clobber the pathTexture in the
- // gaussianBlur. Otherwise, we need to save it for later compositing.
- bool isNormalBlur = (kNormal_SkBlurStyle == fBlurStyle);
- sk_sp<GrRenderTargetContext> renderTargetContext(
- SkGpuBlurUtils::GaussianBlur(context,
- srcProxy,
- nullptr,
- clipRect,
- SkIRect::EmptyIRect(),
- xformedSigma,
- xformedSigma,
- GrTextureDomain::kIgnore_Mode));
- if (!renderTargetContext) {
- return nullptr;
- }
-
- if (!isNormalBlur) {
- GrPaint paint;
- // Blend pathTexture over blurTexture.
- paint.addCoverageFragmentProcessor(GrSimpleTextureEffect::Make(std::move(srcProxy),
- SkMatrix::I()));
- if (kInner_SkBlurStyle == fBlurStyle) {
- // inner: dst = dst * src
- paint.setCoverageSetOpXPFactory(SkRegion::kIntersect_Op);
- } else if (kSolid_SkBlurStyle == fBlurStyle) {
- // solid: dst = src + dst - src * dst
- // = src + (1 - src) * dst
- paint.setCoverageSetOpXPFactory(SkRegion::kUnion_Op);
- } else if (kOuter_SkBlurStyle == fBlurStyle) {
- // outer: dst = dst * (1 - src)
- // = 0 * src + (1 - src) * dst
- paint.setCoverageSetOpXPFactory(SkRegion::kDifference_Op);
- } else {
- paint.setCoverageSetOpXPFactory(SkRegion::kReplace_Op);
- }
-
- renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(),
- SkRect::Make(clipRect));
- }
-
- return renderTargetContext->asTextureProxyRef();
-}
-
-#endif // SK_SUPPORT_GPU
-
-
-#ifndef SK_IGNORE_TO_STRING
-void SkBlurMaskFilterImpl::toString(SkString* str) const {
- str->append("SkBlurMaskFilterImpl: (");
-
- str->append("sigma: ");
- str->appendScalar(fSigma);
- str->append(" ");
-
- static const char* gStyleName[kLastEnum_SkBlurStyle + 1] = {
- "normal", "solid", "outer", "inner"
- };
-
- str->appendf("style: %s ", gStyleName[fBlurStyle]);
- str->append("flags: (");
- if (fBlurFlags) {
- bool needSeparator = false;
- SkAddFlagToString(str, this->ignoreXform(), "IgnoreXform", &needSeparator);
- SkAddFlagToString(str,
- SkToBool(fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag),
- "HighQuality", &needSeparator);
- } else {
- str->append("None");
- }
- str->append("))");
-}
-#endif
-
-SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkBlurMaskFilter)
- SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurMaskFilterImpl)
-SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
diff --git a/src/effects/SkLayerDrawLooper.cpp b/src/effects/SkLayerDrawLooper.cpp
index 17aa7a123d..3cd661c640 100644
--- a/src/effects/SkLayerDrawLooper.cpp
+++ b/src/effects/SkLayerDrawLooper.cpp
@@ -6,7 +6,7 @@
*/
#include "SkArenaAlloc.h"
#include "SkBlurDrawLooper.h"
-#include "SkBlurMaskFilter.h"
+#include "SkMaskFilter.h"
#include "SkCanvas.h"
#include "SkColorSpaceXformer.h"
#include "SkColor.h"
@@ -404,7 +404,7 @@ sk_sp<SkDrawLooper> SkBlurDrawLooper::Make(SkColor color, SkScalar sigma, SkScal
{
sk_sp<SkMaskFilter> blur = nullptr;
if (sigma > 0.0f) {
- blur = SkBlurMaskFilter::Make(kNormal_SkBlurStyle, sigma, SkBlurMaskFilter::kNone_BlurFlag);
+ blur = SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma, true);
}
SkLayerDrawLooper::Builder builder;