Replace mask blur FPs with GrSkSLFP
Change-Id: Ie257eca4357b7ab66845af046329ad89857cdaa5
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/423025
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
diff --git a/src/gpu/GrProcessor.h b/src/gpu/GrProcessor.h
index e3214bd..d216ffbe 100644
--- a/src/gpu/GrProcessor.h
+++ b/src/gpu/GrProcessor.h
@@ -55,7 +55,6 @@
kVertexColorSpaceBenchGP_ClassID,
kGrBicubicEffect_ClassID,
kGrBitmapTextGeoProc_ClassID,
- kGrCircleBlurFragmentProcessor_ClassID,
kGrColorSpaceXformEffect_ClassID,
kGrConfigConversionEffect_ClassID,
kGrConicEffect_ClassID,
@@ -82,8 +81,6 @@
kGrPerlinNoise2Effect_ClassID,
kGrPipelineDynamicStateTestProcessor_ClassID,
kGrQuadEffect_ClassID,
- kGrRectBlurEffect_ClassID,
- kGrRRectBlurEffect_ClassID,
kGrRRectShadowGeoProc_ClassID,
kGrSkSLFP_ClassID,
kGrSpecularLightingEffect_ClassID,
diff --git a/src/gpu/GrProcessorUnitTest.cpp b/src/gpu/GrProcessorUnitTest.cpp
index 3df9cea..3a6f0d1 100644
--- a/src/gpu/GrProcessorUnitTest.cpp
+++ b/src/gpu/GrProcessorUnitTest.cpp
@@ -146,7 +146,7 @@
* we verify the count is as expected. If a new factory is added, then these numbers must be
* manually adjusted.
*/
-static constexpr int kFPFactoryCount = 21;
+static constexpr int kFPFactoryCount = 18;
static constexpr int kGPFactoryCount = 14;
static constexpr int kXPFactoryCount = 4;
diff --git a/src/gpu/effects/GrCircleBlurFragmentProcessor.fp b/src/gpu/effects/GrCircleBlurFragmentProcessor.fp
deleted file mode 100644
index 86d7cf6..0000000
--- a/src/gpu/effects/GrCircleBlurFragmentProcessor.fp
+++ /dev/null
@@ -1,315 +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 fragmentProcessor inputFP;
-in half4 circleRect;
-in half solidRadius;
-in half textureRadius;
-in fragmentProcessor blurProfile;
-
-@header {
- #include "src/gpu/effects/GrTextureEffect.h"
-}
-
-// 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 {
- ProcessorOptimizationFlags(inputFP.get()) & kCompatibleWithCoverageAsAlpha_OptimizationFlag
-}
-
-@make {
- static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> inputFP,
- GrRecordingContext*, const SkRect& circle,
- float sigma);
-}
-
-@setData(data) {
- data.set4f(circleData, circleRect.centerX(), circleRect.centerY(), solidRadius,
- 1.f / textureRadius);
-}
-
-@cpp {
- #include "include/gpu/GrRecordingContext.h"
- #include "src/core/SkGpuBlurUtils.h"
- #include "src/gpu/GrProxyProvider.h"
- #include "src/gpu/GrRecordingContextPriv.h"
- #include "src/gpu/GrThreadSafeCache.h"
- #include "src/gpu/SkGr.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 std::unique_ptr<GrFragmentProcessor> create_profile_effect(GrRecordingContext* rContext,
- const SkRect& circle,
- float sigma,
- float* solidRadius,
- float* textureRadius) {
- float circleR = circle.width() / 2.0f;
- if (!sk_float_isfinite(circleR) || circleR < SK_ScalarNearlyZero) {
- return nullptr;
- }
-
- auto threadSafeCache = rContext->priv().threadSafeCache();
-
- // 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 = std::min(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 constexpr int kProfileTextureWidth = 512;
- // This would be kProfileTextureWidth/textureRadius if it weren't for the fact that we do
- // the calculation of the profile coord in a coord space that has already been scaled by
- // 1 / textureRadius. This is done to avoid overflow in length().
- SkMatrix texM = SkMatrix::Scale(kProfileTextureWidth, 1.f);
-
- static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
- GrUniqueKey key;
- GrUniqueKey::Builder builder(&key, kDomain, 1, "1-D Circular Blur");
- builder[0] = sigmaToCircleRRatioFixed;
- builder.finish();
-
- GrSurfaceProxyView profileView = threadSafeCache->find(key);
- if (profileView) {
- SkASSERT(profileView.asTextureProxy());
- SkASSERT(profileView.origin() == kTopLeft_GrSurfaceOrigin);
- return GrTextureEffect::Make(std::move(profileView), kPremul_SkAlphaType, texM);
- }
-
- 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();
-
- profileView = std::get<0>(GrMakeUncachedBitmapProxyView(rContext, bm));
- if (!profileView) {
- return nullptr;
- }
-
- profileView = threadSafeCache->add(key, profileView);
- return GrTextureEffect::Make(std::move(profileView), kPremul_SkAlphaType, texM);
- }
-
- std::unique_ptr<GrFragmentProcessor> GrCircleBlurFragmentProcessor::Make(
- std::unique_ptr<GrFragmentProcessor> inputFP, GrRecordingContext* context,
- const SkRect& circle, float sigma) {
- if (SkGpuBlurUtils::IsEffectivelyZeroSigma(sigma)) {
- return inputFP;
- }
-
- float solidRadius;
- float textureRadius;
- std::unique_ptr<GrFragmentProcessor> profile = create_profile_effect(context, circle, sigma,
- &solidRadius, &textureRadius);
- if (!profile) {
- return nullptr;
- }
- return std::unique_ptr<GrFragmentProcessor>(new GrCircleBlurFragmentProcessor(
- std::move(inputFP), circle, solidRadius, textureRadius, std::move(profile)));
- }
-}
-
-half4 main() {
- // We just want to compute "(length(vec) - circleData.z + 0.5) * circleData.w" but need to
- // rearrange to avoid passing large values to length() that would overflow.
- half2 vec = half2((sk_FragCoord.xy - circleData.xy) * circleData.w);
- half dist = length(vec) + (0.5 - circleData.z) * circleData.w;
- half4 inputColor = sample(inputFP);
- return inputColor * sample(blurProfile, 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->inputFP(), testData->context(),
- circle, sigma);
-}
diff --git a/src/gpu/effects/GrRRectBlurEffect.fp b/src/gpu/effects/GrRRectBlurEffect.fp
deleted file mode 100644
index 7edf8b8..0000000
--- a/src/gpu/effects/GrRRectBlurEffect.fp
+++ /dev/null
@@ -1,424 +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 fragmentProcessor inputFP;
-in float sigma;
-in float4 rect;
-in uniform half cornerRadius;
-in fragmentProcessor ninePatchFP;
-uniform float4 proxyRect;
-uniform half blurRadius;
-
-@header {
- #include "include/core/SkRect.h"
- class GrRecordingContext;
- class SkRRect;
-}
-
-@optimizationFlags {
- ProcessorOptimizationFlags(inputFP.get()) & kCompatibleWithCoverageAsAlpha_OptimizationFlag
-}
-
-@make {
- static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> inputFP,
- GrRecordingContext* context,
- float sigma,
- float xformedSigma,
- const SkRRect& srcRRect,
- const SkRRect& devRRect);
-}
-
-@cpp {
- #include "include/gpu/GrDirectContext.h"
- #include "include/gpu/GrRecordingContext.h"
- #include "src/core/SkAutoMalloc.h"
- #include "src/core/SkGpuBlurUtils.h"
- #include "src/core/SkRRectPriv.h"
- #include "src/gpu/GrCaps.h"
- #include "src/gpu/GrDirectContextPriv.h"
- #include "src/gpu/GrPaint.h"
- #include "src/gpu/GrProxyProvider.h"
- #include "src/gpu/GrRecordingContextPriv.h"
- #include "src/gpu/GrStyle.h"
- #include "src/gpu/GrSurfaceDrawContext.h"
- #include "src/gpu/GrThreadSafeCache.h"
- #include "src/gpu/SkGr.h"
- #include "src/gpu/effects/GrTextureEffect.h"
-
- static constexpr auto kBlurredRRectMaskOrigin = kTopLeft_GrSurfaceOrigin;
-
- static void make_blurred_rrect_key(GrUniqueKey* key,
- const SkRRect& rrectToDraw,
- float xformedSigma) {
- SkASSERT(!SkGpuBlurUtils::IsEffectivelyZeroSigma(xformedSigma));
- static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
-
- GrUniqueKey::Builder builder(key, kDomain, 9, "RoundRect Blur Mask");
- builder[0] = SkScalarCeilToInt(xformedSigma-1/6.0f);
-
- int index = 1;
- // TODO: this is overkill for _simple_ circular rrects
- for (auto c : { SkRRect::kUpperLeft_Corner, SkRRect::kUpperRight_Corner,
- SkRRect::kLowerRight_Corner, SkRRect::kLowerLeft_Corner }) {
- SkASSERT(SkScalarIsInt(rrectToDraw.radii(c).fX) &&
- SkScalarIsInt(rrectToDraw.radii(c).fY));
- builder[index++] = SkScalarCeilToInt(rrectToDraw.radii(c).fX);
- builder[index++] = SkScalarCeilToInt(rrectToDraw.radii(c).fY);
- }
- builder.finish();
- }
-
- static bool fillin_view_on_gpu(
- GrDirectContext* dContext,
- const GrSurfaceProxyView& lazyView,
- sk_sp<GrThreadSafeCache::Trampoline> trampoline,
- const SkRRect& rrectToDraw,
- const SkISize& dimensions,
- float xformedSigma) {
-#if GR_OGA
- SkASSERT(!SkGpuBlurUtils::IsEffectivelyZeroSigma(xformedSigma));
-
- // We cache blur masks. Use default surface props here so we can use the same cached mask
- // regardless of the final dst surface.
- SkSurfaceProps defaultSurfaceProps;
-
- std::unique_ptr<GrSurfaceDrawContext> rtc = GrSurfaceDrawContext::MakeWithFallback(
- dContext, GrColorType::kAlpha_8, nullptr, SkBackingFit::kExact, dimensions,
- defaultSurfaceProps, 1, GrMipmapped::kNo, GrProtected::kNo,
- kBlurredRRectMaskOrigin);
- if (!rtc) {
- return false;
- }
-
- GrPaint paint;
-
- rtc->clear(SK_PMColor4fTRANSPARENT);
- rtc->drawRRect(nullptr, std::move(paint), GrAA::kYes, SkMatrix::I(), rrectToDraw,
- GrStyle::SimpleFill());
-
- GrSurfaceProxyView srcView = rtc->readSurfaceView();
- SkASSERT(srcView.asTextureProxy());
- auto rtc2 = SkGpuBlurUtils::GaussianBlur(dContext,
- std::move(srcView),
- rtc->colorInfo().colorType(),
- rtc->colorInfo().alphaType(),
- nullptr,
- SkIRect::MakeSize(dimensions),
- SkIRect::MakeSize(dimensions),
- xformedSigma,
- xformedSigma,
- SkTileMode::kClamp,
- SkBackingFit::kExact);
- if (!rtc2 || !rtc2->readSurfaceView()) {
- return false;
- }
-
- auto view = rtc2->readSurfaceView();
- SkASSERT(view.swizzle() == lazyView.swizzle());
- SkASSERT(view.origin() == lazyView.origin());
- trampoline->fProxy = view.asTextureProxyRef();
-
- return true;
-#else
- return false;
-#endif
- }
-
- // Evaluate the vertical blur at the specified 'y' value given the location of the top of the
- // rrect.
- static uint8_t eval_V(float top, int y,
- const uint8_t* integral, int integralSize, float sixSigma) {
- if (top < 0) {
- return 0; // an empty column
- }
-
- float fT = (top - y - 0.5f) * (integralSize/sixSigma);
- if (fT < 0) {
- return 255;
- } else if (fT >= integralSize-1) {
- return 0;
- }
-
- int lower = (int) fT;
- float frac = fT - lower;
-
- SkASSERT(lower+1 < integralSize);
-
- return integral[lower] * (1.0f-frac) + integral[lower+1] * frac;
- }
-
- // Apply a gaussian 'kernel' horizontally at the specified 'x', 'y' location.
- static uint8_t eval_H(int x, int y, const std::vector<float>& topVec,
- const float* kernel, int kernelSize,
- const uint8_t* integral, int integralSize, float sixSigma) {
- SkASSERT(0 <= x && x < (int) topVec.size());
- SkASSERT(kernelSize % 2);
-
- float accum = 0.0f;
-
- int xSampleLoc = x - (kernelSize / 2);
- for (int i = 0; i < kernelSize; ++i, ++xSampleLoc) {
- if (xSampleLoc < 0 || xSampleLoc >= (int) topVec.size()) {
- continue;
- }
-
- accum += kernel[i] * eval_V(topVec[xSampleLoc], y, integral, integralSize, sixSigma);
- }
-
- return accum + 0.5f;
- }
-
- // Create a cpu-side blurred-rrect mask that is close to the version the gpu would've produced.
- // The match needs to be close bc the cpu- and gpu-generated version must be interchangeable.
- static GrSurfaceProxyView create_mask_on_cpu(GrRecordingContext* rContext,
- const SkRRect& rrectToDraw,
- const SkISize& dimensions,
- float xformedSigma) {
- SkASSERT(!SkGpuBlurUtils::IsEffectivelyZeroSigma(xformedSigma));
- int radius = SkGpuBlurUtils::SigmaRadius(xformedSigma);
- int kernelSize = 2*radius + 1;
-
- SkASSERT(kernelSize %2);
- SkASSERT(dimensions.width() % 2);
- SkASSERT(dimensions.height() % 2);
-
- SkVector radii = rrectToDraw.getSimpleRadii();
- SkASSERT(SkScalarNearlyEqual(radii.fX, radii.fY));
-
- const int halfWidthPlus1 = (dimensions.width() / 2) + 1;
- const int halfHeightPlus1 = (dimensions.height() / 2) + 1;
-
- std::unique_ptr<float[]> kernel(new float[kernelSize]);
-
- SkGpuBlurUtils::Compute1DGaussianKernel(kernel.get(), xformedSigma, radius);
-
- SkBitmap integral;
- if (!SkGpuBlurUtils::CreateIntegralTable(6*xformedSigma, &integral)) {
- return {};
- }
-
- SkBitmap result;
- if (!result.tryAllocPixels(SkImageInfo::MakeA8(dimensions.width(), dimensions.height()))) {
- return {};
- }
-
- std::vector<float> topVec;
- topVec.reserve(dimensions.width());
- for (int x = 0; x < dimensions.width(); ++x) {
- if (x < rrectToDraw.rect().fLeft || x > rrectToDraw.rect().fRight) {
- topVec.push_back(-1);
- } else {
- if (x+0.5f < rrectToDraw.rect().fLeft + radii.fX) { // in the circular section
- float xDist = rrectToDraw.rect().fLeft + radii.fX - x - 0.5f;
- float h = sqrtf(radii.fX * radii.fX - xDist * xDist);
- SkASSERT(0 <= h && h < radii.fY);
- topVec.push_back(rrectToDraw.rect().fTop+radii.fX-h + 3*xformedSigma);
- } else {
- topVec.push_back(rrectToDraw.rect().fTop + 3*xformedSigma);
- }
- }
- }
-
- for (int y = 0; y < halfHeightPlus1; ++y) {
- uint8_t* scanline = result.getAddr8(0, y);
-
- for (int x = 0; x < halfWidthPlus1; ++x) {
- scanline[x] = eval_H(x, y, topVec,
- kernel.get(), kernelSize,
- integral.getAddr8(0, 0), integral.width(), 6*xformedSigma);
- scanline[dimensions.width()-x-1] = scanline[x];
- }
-
- memcpy(result.getAddr8(0, dimensions.height()-y-1), scanline, result.rowBytes());
- }
-
- result.setImmutable();
-
- auto view = std::get<0>(GrMakeUncachedBitmapProxyView(rContext, result));
- if (!view) {
- return {};
- }
-
- SkASSERT(view.origin() == kBlurredRRectMaskOrigin);
- return view;
- }
-
- static std::unique_ptr<GrFragmentProcessor> find_or_create_rrect_blur_mask_fp(
- GrRecordingContext* rContext,
- const SkRRect& rrectToDraw,
- const SkISize& dimensions,
- float xformedSigma) {
- SkASSERT(!SkGpuBlurUtils::IsEffectivelyZeroSigma(xformedSigma));
- GrUniqueKey key;
- make_blurred_rrect_key(&key, rrectToDraw, xformedSigma);
-
- auto threadSafeCache = rContext->priv().threadSafeCache();
-
- // It seems like we could omit this matrix and modify the shader code to not normalize
- // the coords used to sample the texture effect. However, the "proxyDims" value in the
- // shader is not always the actual the proxy dimensions. This is because 'dimensions' here
- // was computed using integer corner radii as determined in
- // SkComputeBlurredRRectParams whereas the shader code uses the float radius to compute
- // 'proxyDims'. Why it draws correctly with these unequal values is a mystery for the ages.
- auto m = SkMatrix::Scale(dimensions.width(), dimensions.height());
-
- GrSurfaceProxyView view;
-
- if (GrDirectContext* dContext = rContext->asDirectContext()) {
- // The gpu thread gets priority over the recording threads. If the gpu thread is first,
- // it crams a lazy proxy into the cache and then fills it in later.
- auto[lazyView, trampoline] = GrThreadSafeCache::CreateLazyView(
- dContext, GrColorType::kAlpha_8, dimensions,
- kBlurredRRectMaskOrigin, SkBackingFit::kExact);
- if (!lazyView) {
- return nullptr;
- }
-
- view = threadSafeCache->findOrAdd(key, lazyView);
- if (view != lazyView) {
- SkASSERT(view.asTextureProxy());
- SkASSERT(view.origin() == kBlurredRRectMaskOrigin);
- return GrTextureEffect::Make(std::move(view), kPremul_SkAlphaType, m);
- }
-
- if (!fillin_view_on_gpu(dContext, lazyView, std::move(trampoline),
- rrectToDraw, dimensions, xformedSigma)) {
- // In this case something has gone disastrously wrong so set up to drop the draw
- // that needed this resource and reduce future pollution of the cache.
- threadSafeCache->remove(key);
- return nullptr;
- }
- } else {
- view = threadSafeCache->find(key);
- if (view) {
- SkASSERT(view.asTextureProxy());
- SkASSERT(view.origin() == kBlurredRRectMaskOrigin);
- return GrTextureEffect::Make(std::move(view), kPremul_SkAlphaType, m);
- }
-
- view = create_mask_on_cpu(rContext, rrectToDraw, dimensions, xformedSigma);
- if (!view) {
- return nullptr;
- }
-
- view = threadSafeCache->add(key, view);
- }
-
- SkASSERT(view.asTextureProxy());
- SkASSERT(view.origin() == kBlurredRRectMaskOrigin);
- return GrTextureEffect::Make(std::move(view), kPremul_SkAlphaType, m);
- }
-
- std::unique_ptr<GrFragmentProcessor> GrRRectBlurEffect::Make(
- std::unique_ptr<GrFragmentProcessor> inputFP,
- GrRecordingContext* context,
- float sigma,
- float xformedSigma,
- const SkRRect& srcRRect,
- const SkRRect& devRRect) {
- // Should've been caught up-stream
-#ifdef SK_DEBUG
- SkASSERTF(!SkRRectPriv::IsCircle(devRRect), "Unexpected circle. %d\n\t%s\n\t%s",
- SkRRectPriv::IsCircle(srcRRect),
- srcRRect.dumpToString(true).c_str(), devRRect.dumpToString(true).c_str());
- SkASSERTF(!devRRect.isRect(), "Unexpected rect. %d\n\t%s\n\t%s",
- srcRRect.isRect(),
- srcRRect.dumpToString(true).c_str(), devRRect.dumpToString(true).c_str());
-#endif
- // TODO: loosen this up
- if (!SkRRectPriv::IsSimpleCircular(devRRect)) {
- return nullptr;
- }
-
- if (SkGpuBlurUtils::IsEffectivelyZeroSigma(xformedSigma)) {
- return inputFP;
- }
-
- // Make sure we can successfully ninepatch this rrect -- the blur sigma has to be
- // sufficiently small relative to both the size of the corner radius and the
- // width (and height) of the rrect.
- SkRRect rrectToDraw;
- SkISize dimensions;
- SkScalar ignored[SkGpuBlurUtils::kBlurRRectMaxDivisions];
-
- bool ninePatchable = SkGpuBlurUtils::ComputeBlurredRRectParams(srcRRect, devRRect,
- sigma, xformedSigma,
- &rrectToDraw, &dimensions,
- ignored, ignored,
- ignored, ignored);
- if (!ninePatchable) {
- return nullptr;
- }
-
- std::unique_ptr<GrFragmentProcessor> maskFP = find_or_create_rrect_blur_mask_fp(
- context, rrectToDraw, dimensions, xformedSigma);
- if (!maskFP) {
- return nullptr;
- }
-
- return std::unique_ptr<GrFragmentProcessor>(
- new GrRRectBlurEffect(std::move(inputFP), xformedSigma, devRRect.getBounds(),
- SkRRectPriv::GetSimpleRadii(devRRect).fX, std::move(maskFP)));
- }
-}
-
-@test(d) {
- SkScalar w = d->fRandom->nextRangeScalar(100.f, 1000.f);
- SkScalar h = d->fRandom->nextRangeScalar(100.f, 1000.f);
- SkScalar r = d->fRandom->nextRangeF(1.f, 9.f);
- SkScalar sigma = d->fRandom->nextRangeF(1.f,10.f);
- SkRRect rrect;
- rrect.setRectXY(SkRect::MakeWH(w, h), r, r);
- return GrRRectBlurEffect::Make(d->inputFP(), d->context(), sigma, sigma, rrect, rrect);
-}
-
-half4 main() {
- // Warp the fragment position to the appropriate part of the 9-patch blur texture by snipping
- // out the middle section of the proxy rect.
- float2 translatedFragPosFloat = sk_FragCoord.xy - proxyRect.LT;
- float2 proxyCenter = (proxyRect.RB - proxyRect.LT) * 0.5;
- half edgeSize = 2.0 * blurRadius + cornerRadius + 0.5;
-
- // Position the fragment so that (0, 0) marks the center of the proxy rectangle.
- // Negative coordinates are on the left/top side and positive numbers are on the right/bottom.
- translatedFragPosFloat -= proxyCenter;
-
- // Temporarily strip off the fragment's sign. x/y are now strictly increasing as we move away
- // from the center.
- half2 fragDirection = half2(sign(translatedFragPosFloat));
- translatedFragPosFloat = abs(translatedFragPosFloat);
-
- // Our goal is to snip out the "middle section" of the proxy rect (everything but the edge).
- // We've repositioned our fragment position so that (0, 0) is the centerpoint and x/y are always
- // positive, so we can subtract here and interpret negative results as being within the middle
- // section.
- half2 translatedFragPosHalf = half2(translatedFragPosFloat - (proxyCenter - edgeSize));
-
- // Remove the middle section by clamping to zero.
- translatedFragPosHalf = max(translatedFragPosHalf, 0);
-
- // Reapply the fragment's sign, so that negative coordinates once again mean left/top side and
- // positive means bottom/right side.
- translatedFragPosHalf *= fragDirection;
-
- // Offset the fragment so that (0, 0) marks the upper-left again, instead of the center point.
- translatedFragPosHalf += half2(edgeSize);
-
- half2 proxyDims = half2(2.0 * edgeSize);
- half2 texCoord = translatedFragPosHalf / proxyDims;
-
- return sample(inputFP) * sample(ninePatchFP, texCoord).a;
-}
-
-@setData(pdman) {
- float blurRadiusValue = 3.f * SkScalarCeilToScalar(sigma - 1 / 6.0f);
- pdman.set1f(blurRadius, blurRadiusValue);
-
- SkRect outset = rect;
- outset.outset(blurRadiusValue, blurRadiusValue);
- pdman.set4f(proxyRect, outset.fLeft, outset.fTop, outset.fRight, outset.fBottom);
-}
diff --git a/src/gpu/effects/GrRectBlurEffect.fp b/src/gpu/effects/GrRectBlurEffect.fp
deleted file mode 100644
index fc4d632..0000000
--- a/src/gpu/effects/GrRectBlurEffect.fp
+++ /dev/null
@@ -1,215 +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.
- */
-
-@header {
-#include <cmath>
-#include "include/core/SkRect.h"
-#include "include/core/SkScalar.h"
-#include "include/gpu/GrRecordingContext.h"
-#include "src/core/SkBlurMask.h"
-#include "src/core/SkGpuBlurUtils.h"
-#include "src/core/SkMathPriv.h"
-#include "src/gpu/GrProxyProvider.h"
-#include "src/gpu/GrRecordingContextPriv.h"
-#include "src/gpu/GrShaderCaps.h"
-#include "src/gpu/GrThreadSafeCache.h"
-#include "src/gpu/SkGr.h"
-#include "src/gpu/effects/GrMatrixEffect.h"
-#include "src/gpu/effects/GrTextureEffect.h"
-}
-
-in uniform float4 rect;
-
-// Effect that is a LUT for integral of normal distribution. The value at x:[0,6*sigma] is the
-// integral from -inf to (3*sigma - x). I.e. x is mapped from [0, 6*sigma] to [3*sigma to -3*sigma].
-// The flip saves a reversal in the shader.
-in fragmentProcessor integral;
-
-// There is a fast variant of the effect that does 2 texture lookups and a more general one for
-// wider blurs relative to rect sizes that does 4.
-layout(key) in bool isFast;
-
-@optimizationFlags {
- kCompatibleWithCoverageAsAlpha_OptimizationFlag
-}
-
-@class {
-static std::unique_ptr<GrFragmentProcessor> MakeIntegralFP(GrRecordingContext* rContext,
- float sixSigma) {
- SkASSERT(!SkGpuBlurUtils::IsEffectivelyZeroSigma(sixSigma / 6.f));
- auto threadSafeCache = rContext->priv().threadSafeCache();
-
- int width = SkGpuBlurUtils::CreateIntegralTable(sixSigma, nullptr);
-
- static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
- GrUniqueKey key;
- GrUniqueKey::Builder builder(&key, kDomain, 1, "Rect Blur Mask");
- builder[0] = width;
- builder.finish();
-
- SkMatrix m = SkMatrix::Scale(width/sixSigma, 1.f);
-
- GrSurfaceProxyView view = threadSafeCache->find(key);
-
- if (view) {
- SkASSERT(view.origin() == kTopLeft_GrSurfaceOrigin);
- return GrTextureEffect::Make(
- std::move(view), kPremul_SkAlphaType, m, GrSamplerState::Filter::kLinear);
- }
-
- SkBitmap bitmap;
- if (!SkGpuBlurUtils::CreateIntegralTable(sixSigma, &bitmap)) {
- return {};
- }
-
- view = std::get<0>(GrMakeUncachedBitmapProxyView(rContext, bitmap));
- if (!view) {
- return {};
- }
-
- view = threadSafeCache->add(key, view);
-
- SkASSERT(view.origin() == kTopLeft_GrSurfaceOrigin);
- return GrTextureEffect::Make(
- std::move(view), kPremul_SkAlphaType, m, GrSamplerState::Filter::kLinear);
-}
-}
-
-@make {
- static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> inputFP,
- GrRecordingContext* context,
- const GrShaderCaps& caps,
- const SkRect& srcRect,
- const SkMatrix& viewMatrix,
- float transformedSigma) {
- SkASSERT(viewMatrix.preservesRightAngles());
- SkASSERT(srcRect.isSorted());
-
- if (SkGpuBlurUtils::IsEffectivelyZeroSigma(transformedSigma)) {
- // No need to blur the rect
- return inputFP;
- }
-
- SkMatrix invM;
- SkRect rect;
- if (viewMatrix.rectStaysRect()) {
- invM = SkMatrix::I();
- // We can do everything in device space when the src rect projects to a rect in device
- // space.
- SkAssertResult(viewMatrix.mapRect(&rect, srcRect));
- } else {
- // The view matrix may scale, perhaps anisotropically. But we want to apply our device
- // space "transformedSigma" to the delta of frag coord from the rect edges. Factor out
- // the scaling to define a space that is purely rotation/translation from device space
- // (and scale from src space) We'll meet in the middle: pre-scale the src rect to be in
- // this space and then apply the inverse of the rotation/translation portion to the
- // frag coord.
- SkMatrix m;
- SkSize scale;
- if (!viewMatrix.decomposeScale(&scale, &m)) {
- return nullptr;
- }
- if (!m.invert(&invM)) {
- return nullptr;
- }
- rect = {srcRect.left() * scale.width(),
- srcRect.top() * scale.height(),
- srcRect.right() * scale.width(),
- srcRect.bottom() * scale.height()};
- }
-
- if (!caps.floatIs32Bits()) {
- // We promote the math that gets us into the Gaussian space to full float when the rect
- // coords are large. If we don't have full float then fail. We could probably clip the
- // rect to an outset device bounds instead.
- if (SkScalarAbs(rect.fLeft) > 16000.f ||
- SkScalarAbs(rect.fTop) > 16000.f ||
- SkScalarAbs(rect.fRight) > 16000.f ||
- SkScalarAbs(rect.fBottom) > 16000.f) {
- return nullptr;
- }
- }
-
- const float sixSigma = 6 * transformedSigma;
- std::unique_ptr<GrFragmentProcessor> integral = MakeIntegralFP(context, sixSigma);
- if (!integral) {
- return nullptr;
- }
-
- // In the fast variant we think of the midpoint of the integral texture as aligning
- // with the closest rect edge both in x and y. To simplify texture coord calculation we
- // inset the rect so that the edge of the inset rect corresponds to t = 0 in the texture.
- // It actually simplifies things a bit in the !isFast case, too.
- float threeSigma = sixSigma / 2;
- SkRect insetRect = {rect.left() + threeSigma,
- rect.top() + threeSigma,
- rect.right() - threeSigma,
- rect.bottom() - threeSigma};
-
- // In our fast variant we find the nearest horizontal and vertical edges and for each
- // do a lookup in the integral texture for each and multiply them. When the rect is
- // less than 6 sigma wide then things aren't so simple and we have to consider both the
- // left and right edge of the rectangle (and similar in y).
- bool isFast = insetRect.isSorted();
- std::unique_ptr<GrFragmentProcessor> fp(new GrRectBlurEffect(insetRect,
- std::move(integral),
- isFast));
- if (!invM.isIdentity()) {
- fp = GrMatrixEffect::Make(invM, std::move(fp));
- }
- fp = GrFragmentProcessor::DeviceSpace(std::move(fp));
- return GrFragmentProcessor::MulInputByChildAlpha(std::move(fp));
- }
-}
-
-half4 main(float2 pos) {
- half xCoverage, yCoverage;
- @if (isFast) {
- // Get the smaller of the signed distance from the frag coord to the left and right
- // edges and similar for y.
- // The integral texture goes "backwards" (from 3*sigma to -3*sigma), So, the below
- // computations align the left edge of the integral texture with the inset rect's edge
- // extending outward 6 * sigma from the inset rect.
- half2 xy = max(half2(rect.LT - pos), half2(pos - rect.RB));
- xCoverage = sample(integral, half2(xy.x, 0.5)).a;
- yCoverage = sample(integral, half2(xy.y, 0.5)).a;
- } else {
- // We just consider just the x direction here. In practice we compute x and y separately
- // and multiply them together.
- // We define our coord system so that the point at which we're evaluating a kernel
- // defined by the normal distribution (K) at 0. In this coord system let L be left
- // edge and R be the right edge of the rectangle.
- // We can calculate C by integrating K with the half infinite ranges outside the L to R
- // range and subtracting from 1:
- // C = 1 - <integral of K from from -inf to L> - <integral of K from R to inf>
- // K is symmetric about x=0 so:
- // C = 1 - <integral of K from from -inf to L> - <integral of K from -inf to -R>
-
- // The integral texture goes "backwards" (from 3*sigma to -3*sigma) which is factored
- // in to the below calculations.
- // Also, our rect uniform was pre-inset by 3 sigma from the actual rect being blurred,
- // also factored in.
- half4 rect = half4(half2(rect.LT - pos), half2(pos - rect.RB));
- xCoverage = 1 - sample(integral, half2(rect.L, 0.5)).a
- - sample(integral, half2(rect.R, 0.5)).a;
- yCoverage = 1 - sample(integral, half2(rect.T, 0.5)).a
- - sample(integral, half2(rect.B, 0.5)).a;
- }
- return half4(xCoverage * yCoverage);
-}
-
-@test(data) {
- float sigma = data->fRandom->nextRangeF(3, 8);
- int x = data->fRandom->nextRangeF(1, 200);
- int y = data->fRandom->nextRangeF(1, 200);
- float width = data->fRandom->nextRangeF(200, 300);
- float height = data->fRandom->nextRangeF(200, 300);
- SkMatrix vm = GrTest::TestMatrixPreservesRightAngles(data->fRandom);
- auto rect = SkRect::MakeXYWH(x, y, width, height);
- return GrRectBlurEffect::Make(data->inputFP(), data->context(), *data->caps()->shaderCaps(),
- rect, vm, sigma);
-}
diff --git a/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.cpp b/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.cpp
deleted file mode 100644
index 81c4527..0000000
--- a/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.cpp
+++ /dev/null
@@ -1,393 +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"
-
-#include "include/gpu/GrRecordingContext.h"
-#include "src/core/SkGpuBlurUtils.h"
-#include "src/gpu/GrProxyProvider.h"
-#include "src/gpu/GrRecordingContextPriv.h"
-#include "src/gpu/GrThreadSafeCache.h"
-#include "src/gpu/SkGr.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 std::unique_ptr<GrFragmentProcessor> create_profile_effect(GrRecordingContext* rContext,
- const SkRect& circle,
- float sigma,
- float* solidRadius,
- float* textureRadius) {
- float circleR = circle.width() / 2.0f;
- if (!sk_float_isfinite(circleR) || circleR < SK_ScalarNearlyZero) {
- return nullptr;
- }
-
- auto threadSafeCache = rContext->priv().threadSafeCache();
-
- // 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 = std::min(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 constexpr int kProfileTextureWidth = 512;
- // This would be kProfileTextureWidth/textureRadius if it weren't for the fact that we do
- // the calculation of the profile coord in a coord space that has already been scaled by
- // 1 / textureRadius. This is done to avoid overflow in length().
- SkMatrix texM = SkMatrix::Scale(kProfileTextureWidth, 1.f);
-
- static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
- GrUniqueKey key;
- GrUniqueKey::Builder builder(&key, kDomain, 1, "1-D Circular Blur");
- builder[0] = sigmaToCircleRRatioFixed;
- builder.finish();
-
- GrSurfaceProxyView profileView = threadSafeCache->find(key);
- if (profileView) {
- SkASSERT(profileView.asTextureProxy());
- SkASSERT(profileView.origin() == kTopLeft_GrSurfaceOrigin);
- return GrTextureEffect::Make(std::move(profileView), kPremul_SkAlphaType, texM);
- }
-
- 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();
-
- profileView = std::get<0>(GrMakeUncachedBitmapProxyView(rContext, bm));
- if (!profileView) {
- return nullptr;
- }
-
- profileView = threadSafeCache->add(key, profileView);
- return GrTextureEffect::Make(std::move(profileView), kPremul_SkAlphaType, texM);
-}
-
-std::unique_ptr<GrFragmentProcessor> GrCircleBlurFragmentProcessor::Make(
- std::unique_ptr<GrFragmentProcessor> inputFP,
- GrRecordingContext* context,
- const SkRect& circle,
- float sigma) {
- if (SkGpuBlurUtils::IsEffectivelyZeroSigma(sigma)) {
- return inputFP;
- }
-
- float solidRadius;
- float textureRadius;
- std::unique_ptr<GrFragmentProcessor> profile =
- create_profile_effect(context, circle, sigma, &solidRadius, &textureRadius);
- if (!profile) {
- return nullptr;
- }
- return std::unique_ptr<GrFragmentProcessor>(new GrCircleBlurFragmentProcessor(
- std::move(inputFP), circle, solidRadius, textureRadius, std::move(profile)));
-}
-#include "src/core/SkUtils.h"
-#include "src/gpu/GrTexture.h"
-#include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
-#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
-#include "src/gpu/glsl/GrGLSLProgramBuilder.h"
-#include "src/sksl/SkSLCPP.h"
-#include "src/sksl/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 solidRadius = _outer.solidRadius;
- (void)solidRadius;
- auto textureRadius = _outer.textureRadius;
- (void)textureRadius;
- circleDataVar = args.fUniformHandler->addUniform(
- &_outer, kFragment_GrShaderFlag, kHalf4_GrSLType, "circleData");
- fragBuilder->codeAppendf(
- R"SkSL(half2 vec = half2((sk_FragCoord.xy - float2(%s.xy)) * float(%s.w));
-half dist = length(vec) + (0.5 - %s.z) * %s.w;)SkSL",
- args.fUniformHandler->getUniformCStr(circleDataVar),
- args.fUniformHandler->getUniformCStr(circleDataVar),
- args.fUniformHandler->getUniformCStr(circleDataVar),
- args.fUniformHandler->getUniformCStr(circleDataVar));
- SkString _sample0 = this->invokeChild(0, args);
- fragBuilder->codeAppendf(
- R"SkSL(
-half4 inputColor = %s;)SkSL",
- _sample0.c_str());
- SkString _coords1("float2(half2(dist, 0.5))");
- SkString _sample1 = this->invokeChild(1, args, _coords1.c_str());
- fragBuilder->codeAppendf(
- R"SkSL(
-return inputColor * %s.w;
-)SkSL",
- _sample1.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 solidRadius = _outer.solidRadius;
- (void)solidRadius;
- auto textureRadius = _outer.textureRadius;
- (void)textureRadius;
- UniformHandle& circleData = circleDataVar;
- (void)circleData;
-
- data.set4f(circleData,
- circleRect.centerX(),
- circleRect.centerY(),
- solidRadius,
- 1.f / textureRadius);
- }
- UniformHandle circleDataVar;
-};
-std::unique_ptr<GrGLSLFragmentProcessor> GrCircleBlurFragmentProcessor::onMakeProgramImpl() const {
- return std::make_unique<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 (circleRect != that.circleRect) return false;
- if (solidRadius != that.solidRadius) return false;
- if (textureRadius != that.textureRadius) return false;
- return true;
-}
-GrCircleBlurFragmentProcessor::GrCircleBlurFragmentProcessor(
- const GrCircleBlurFragmentProcessor& src)
- : INHERITED(kGrCircleBlurFragmentProcessor_ClassID, src.optimizationFlags())
- , circleRect(src.circleRect)
- , solidRadius(src.solidRadius)
- , textureRadius(src.textureRadius) {
- this->cloneAndRegisterAllChildProcessors(src);
-}
-std::unique_ptr<GrFragmentProcessor> GrCircleBlurFragmentProcessor::clone() const {
- return std::make_unique<GrCircleBlurFragmentProcessor>(*this);
-}
-#if GR_TEST_UTILS
-SkString GrCircleBlurFragmentProcessor::onDumpInfo() const {
- return SkStringPrintf("(circleRect=half4(%f, %f, %f, %f), solidRadius=%f, textureRadius=%f)",
- circleRect.left(),
- circleRect.top(),
- circleRect.right(),
- circleRect.bottom(),
- solidRadius,
- textureRadius);
-}
-#endif
-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->inputFP(), testData->context(), circle, sigma);
-}
-#endif
diff --git a/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.h b/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.h
deleted file mode 100644
index b3cce4c..0000000
--- a/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.h
+++ /dev/null
@@ -1,58 +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 "include/core/SkM44.h"
-#include "include/core/SkTypes.h"
-
-#include "src/gpu/effects/GrTextureEffect.h"
-
-#include "src/gpu/GrFragmentProcessor.h"
-
-class GrCircleBlurFragmentProcessor : public GrFragmentProcessor {
-public:
- static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> inputFP,
- GrRecordingContext*,
- const SkRect& circle,
- float sigma);
- GrCircleBlurFragmentProcessor(const GrCircleBlurFragmentProcessor& src);
- std::unique_ptr<GrFragmentProcessor> clone() const override;
- const char* name() const override { return "CircleBlurFragmentProcessor"; }
- SkRect circleRect;
- float solidRadius;
- float textureRadius;
-
-private:
- GrCircleBlurFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,
- SkRect circleRect,
- float solidRadius,
- float textureRadius,
- std::unique_ptr<GrFragmentProcessor> blurProfile)
- : INHERITED(kGrCircleBlurFragmentProcessor_ClassID,
- (OptimizationFlags)ProcessorOptimizationFlags(inputFP.get()) &
- kCompatibleWithCoverageAsAlpha_OptimizationFlag)
- , circleRect(circleRect)
- , solidRadius(solidRadius)
- , textureRadius(textureRadius) {
- this->registerChild(std::move(inputFP), SkSL::SampleUsage::PassThrough());
- this->registerChild(std::move(blurProfile), SkSL::SampleUsage::Explicit());
- }
- std::unique_ptr<GrGLSLFragmentProcessor> onMakeProgramImpl() const override;
- void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
- bool onIsEqual(const GrFragmentProcessor&) const override;
-#if GR_TEST_UTILS
- SkString onDumpInfo() const override;
-#endif
- GR_DECLARE_FRAGMENT_PROCESSOR_TEST
- using INHERITED = GrFragmentProcessor;
-};
-#endif
diff --git a/src/gpu/effects/generated/GrRRectBlurEffect.cpp b/src/gpu/effects/generated/GrRRectBlurEffect.cpp
deleted file mode 100644
index 5f74cca..0000000
--- a/src/gpu/effects/generated/GrRRectBlurEffect.cpp
+++ /dev/null
@@ -1,509 +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 GrRRectBlurEffect.fp; do not modify.
- **************************************************************************************************/
-#include "GrRRectBlurEffect.h"
-
-#include "include/gpu/GrDirectContext.h"
-#include "include/gpu/GrRecordingContext.h"
-#include "src/core/SkAutoMalloc.h"
-#include "src/core/SkGpuBlurUtils.h"
-#include "src/core/SkRRectPriv.h"
-#include "src/gpu/GrCaps.h"
-#include "src/gpu/GrDirectContextPriv.h"
-#include "src/gpu/GrPaint.h"
-#include "src/gpu/GrProxyProvider.h"
-#include "src/gpu/GrRecordingContextPriv.h"
-#include "src/gpu/GrStyle.h"
-#include "src/gpu/GrSurfaceDrawContext.h"
-#include "src/gpu/GrThreadSafeCache.h"
-#include "src/gpu/SkGr.h"
-#include "src/gpu/effects/GrTextureEffect.h"
-
-static constexpr auto kBlurredRRectMaskOrigin = kTopLeft_GrSurfaceOrigin;
-
-static void make_blurred_rrect_key(GrUniqueKey* key,
- const SkRRect& rrectToDraw,
- float xformedSigma) {
- SkASSERT(!SkGpuBlurUtils::IsEffectivelyZeroSigma(xformedSigma));
- static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
-
- GrUniqueKey::Builder builder(key, kDomain, 9, "RoundRect Blur Mask");
- builder[0] = SkScalarCeilToInt(xformedSigma - 1 / 6.0f);
-
- int index = 1;
- // TODO: this is overkill for _simple_ circular rrects
- for (auto c : {SkRRect::kUpperLeft_Corner,
- SkRRect::kUpperRight_Corner,
- SkRRect::kLowerRight_Corner,
- SkRRect::kLowerLeft_Corner}) {
- SkASSERT(SkScalarIsInt(rrectToDraw.radii(c).fX) && SkScalarIsInt(rrectToDraw.radii(c).fY));
- builder[index++] = SkScalarCeilToInt(rrectToDraw.radii(c).fX);
- builder[index++] = SkScalarCeilToInt(rrectToDraw.radii(c).fY);
- }
- builder.finish();
-}
-
-static bool fillin_view_on_gpu(GrDirectContext* dContext,
- const GrSurfaceProxyView& lazyView,
- sk_sp<GrThreadSafeCache::Trampoline> trampoline,
- const SkRRect& rrectToDraw,
- const SkISize& dimensions,
- float xformedSigma) {
-#if GR_OGA
- SkASSERT(!SkGpuBlurUtils::IsEffectivelyZeroSigma(xformedSigma));
-
- // We cache blur masks. Use default surface props here so we can use the same cached mask
- // regardless of the final dst surface.
- SkSurfaceProps defaultSurfaceProps;
-
- std::unique_ptr<GrSurfaceDrawContext> rtc =
- GrSurfaceDrawContext::MakeWithFallback(dContext,
- GrColorType::kAlpha_8,
- nullptr,
- SkBackingFit::kExact,
- dimensions,
- defaultSurfaceProps,
- 1,
- GrMipmapped::kNo,
- GrProtected::kNo,
- kBlurredRRectMaskOrigin);
- if (!rtc) {
- return false;
- }
-
- GrPaint paint;
-
- rtc->clear(SK_PMColor4fTRANSPARENT);
- rtc->drawRRect(nullptr,
- std::move(paint),
- GrAA::kYes,
- SkMatrix::I(),
- rrectToDraw,
- GrStyle::SimpleFill());
-
- GrSurfaceProxyView srcView = rtc->readSurfaceView();
- SkASSERT(srcView.asTextureProxy());
- auto rtc2 = SkGpuBlurUtils::GaussianBlur(dContext,
- std::move(srcView),
- rtc->colorInfo().colorType(),
- rtc->colorInfo().alphaType(),
- nullptr,
- SkIRect::MakeSize(dimensions),
- SkIRect::MakeSize(dimensions),
- xformedSigma,
- xformedSigma,
- SkTileMode::kClamp,
- SkBackingFit::kExact);
- if (!rtc2 || !rtc2->readSurfaceView()) {
- return false;
- }
-
- auto view = rtc2->readSurfaceView();
- SkASSERT(view.swizzle() == lazyView.swizzle());
- SkASSERT(view.origin() == lazyView.origin());
- trampoline->fProxy = view.asTextureProxyRef();
-
- return true;
-#else
- return false;
-#endif
-}
-
-// Evaluate the vertical blur at the specified 'y' value given the location of the top of the
-// rrect.
-static uint8_t eval_V(float top, int y, const uint8_t* integral, int integralSize, float sixSigma) {
- if (top < 0) {
- return 0; // an empty column
- }
-
- float fT = (top - y - 0.5f) * (integralSize / sixSigma);
- if (fT < 0) {
- return 255;
- } else if (fT >= integralSize - 1) {
- return 0;
- }
-
- int lower = (int)fT;
- float frac = fT - lower;
-
- SkASSERT(lower + 1 < integralSize);
-
- return integral[lower] * (1.0f - frac) + integral[lower + 1] * frac;
-}
-
-// Apply a gaussian 'kernel' horizontally at the specified 'x', 'y' location.
-static uint8_t eval_H(int x,
- int y,
- const std::vector<float>& topVec,
- const float* kernel,
- int kernelSize,
- const uint8_t* integral,
- int integralSize,
- float sixSigma) {
- SkASSERT(0 <= x && x < (int)topVec.size());
- SkASSERT(kernelSize % 2);
-
- float accum = 0.0f;
-
- int xSampleLoc = x - (kernelSize / 2);
- for (int i = 0; i < kernelSize; ++i, ++xSampleLoc) {
- if (xSampleLoc < 0 || xSampleLoc >= (int)topVec.size()) {
- continue;
- }
-
- accum += kernel[i] * eval_V(topVec[xSampleLoc], y, integral, integralSize, sixSigma);
- }
-
- return accum + 0.5f;
-}
-
-// Create a cpu-side blurred-rrect mask that is close to the version the gpu would've produced.
-// The match needs to be close bc the cpu- and gpu-generated version must be interchangeable.
-static GrSurfaceProxyView create_mask_on_cpu(GrRecordingContext* rContext,
- const SkRRect& rrectToDraw,
- const SkISize& dimensions,
- float xformedSigma) {
- SkASSERT(!SkGpuBlurUtils::IsEffectivelyZeroSigma(xformedSigma));
- int radius = SkGpuBlurUtils::SigmaRadius(xformedSigma);
- int kernelSize = 2 * radius + 1;
-
- SkASSERT(kernelSize % 2);
- SkASSERT(dimensions.width() % 2);
- SkASSERT(dimensions.height() % 2);
-
- SkVector radii = rrectToDraw.getSimpleRadii();
- SkASSERT(SkScalarNearlyEqual(radii.fX, radii.fY));
-
- const int halfWidthPlus1 = (dimensions.width() / 2) + 1;
- const int halfHeightPlus1 = (dimensions.height() / 2) + 1;
-
- std::unique_ptr<float[]> kernel(new float[kernelSize]);
-
- SkGpuBlurUtils::Compute1DGaussianKernel(kernel.get(), xformedSigma, radius);
-
- SkBitmap integral;
- if (!SkGpuBlurUtils::CreateIntegralTable(6 * xformedSigma, &integral)) {
- return {};
- }
-
- SkBitmap result;
- if (!result.tryAllocPixels(SkImageInfo::MakeA8(dimensions.width(), dimensions.height()))) {
- return {};
- }
-
- std::vector<float> topVec;
- topVec.reserve(dimensions.width());
- for (int x = 0; x < dimensions.width(); ++x) {
- if (x < rrectToDraw.rect().fLeft || x > rrectToDraw.rect().fRight) {
- topVec.push_back(-1);
- } else {
- if (x + 0.5f < rrectToDraw.rect().fLeft + radii.fX) { // in the circular section
- float xDist = rrectToDraw.rect().fLeft + radii.fX - x - 0.5f;
- float h = sqrtf(radii.fX * radii.fX - xDist * xDist);
- SkASSERT(0 <= h && h < radii.fY);
- topVec.push_back(rrectToDraw.rect().fTop + radii.fX - h + 3 * xformedSigma);
- } else {
- topVec.push_back(rrectToDraw.rect().fTop + 3 * xformedSigma);
- }
- }
- }
-
- for (int y = 0; y < halfHeightPlus1; ++y) {
- uint8_t* scanline = result.getAddr8(0, y);
-
- for (int x = 0; x < halfWidthPlus1; ++x) {
- scanline[x] = eval_H(x,
- y,
- topVec,
- kernel.get(),
- kernelSize,
- integral.getAddr8(0, 0),
- integral.width(),
- 6 * xformedSigma);
- scanline[dimensions.width() - x - 1] = scanline[x];
- }
-
- memcpy(result.getAddr8(0, dimensions.height() - y - 1), scanline, result.rowBytes());
- }
-
- result.setImmutable();
-
- auto view = std::get<0>(GrMakeUncachedBitmapProxyView(rContext, result));
- if (!view) {
- return {};
- }
-
- SkASSERT(view.origin() == kBlurredRRectMaskOrigin);
- return view;
-}
-
-static std::unique_ptr<GrFragmentProcessor> find_or_create_rrect_blur_mask_fp(
- GrRecordingContext* rContext,
- const SkRRect& rrectToDraw,
- const SkISize& dimensions,
- float xformedSigma) {
- SkASSERT(!SkGpuBlurUtils::IsEffectivelyZeroSigma(xformedSigma));
- GrUniqueKey key;
- make_blurred_rrect_key(&key, rrectToDraw, xformedSigma);
-
- auto threadSafeCache = rContext->priv().threadSafeCache();
-
- // It seems like we could omit this matrix and modify the shader code to not normalize
- // the coords used to sample the texture effect. However, the "proxyDims" value in the
- // shader is not always the actual the proxy dimensions. This is because 'dimensions' here
- // was computed using integer corner radii as determined in
- // SkComputeBlurredRRectParams whereas the shader code uses the float radius to compute
- // 'proxyDims'. Why it draws correctly with these unequal values is a mystery for the ages.
- auto m = SkMatrix::Scale(dimensions.width(), dimensions.height());
-
- GrSurfaceProxyView view;
-
- if (GrDirectContext* dContext = rContext->asDirectContext()) {
- // The gpu thread gets priority over the recording threads. If the gpu thread is first,
- // it crams a lazy proxy into the cache and then fills it in later.
- auto[lazyView, trampoline] = GrThreadSafeCache::CreateLazyView(dContext,
- GrColorType::kAlpha_8,
- dimensions,
- kBlurredRRectMaskOrigin,
- SkBackingFit::kExact);
- if (!lazyView) {
- return nullptr;
- }
-
- view = threadSafeCache->findOrAdd(key, lazyView);
- if (view != lazyView) {
- SkASSERT(view.asTextureProxy());
- SkASSERT(view.origin() == kBlurredRRectMaskOrigin);
- return GrTextureEffect::Make(std::move(view), kPremul_SkAlphaType, m);
- }
-
- if (!fillin_view_on_gpu(dContext,
- lazyView,
- std::move(trampoline),
- rrectToDraw,
- dimensions,
- xformedSigma)) {
- // In this case something has gone disastrously wrong so set up to drop the draw
- // that needed this resource and reduce future pollution of the cache.
- threadSafeCache->remove(key);
- return nullptr;
- }
- } else {
- view = threadSafeCache->find(key);
- if (view) {
- SkASSERT(view.asTextureProxy());
- SkASSERT(view.origin() == kBlurredRRectMaskOrigin);
- return GrTextureEffect::Make(std::move(view), kPremul_SkAlphaType, m);
- }
-
- view = create_mask_on_cpu(rContext, rrectToDraw, dimensions, xformedSigma);
- if (!view) {
- return nullptr;
- }
-
- view = threadSafeCache->add(key, view);
- }
-
- SkASSERT(view.asTextureProxy());
- SkASSERT(view.origin() == kBlurredRRectMaskOrigin);
- return GrTextureEffect::Make(std::move(view), kPremul_SkAlphaType, m);
-}
-
-std::unique_ptr<GrFragmentProcessor> GrRRectBlurEffect::Make(
- std::unique_ptr<GrFragmentProcessor> inputFP,
- GrRecordingContext* context,
- float sigma,
- float xformedSigma,
- const SkRRect& srcRRect,
- const SkRRect& devRRect) {
-// Should've been caught up-stream
-#ifdef SK_DEBUG
- SkASSERTF(!SkRRectPriv::IsCircle(devRRect),
- "Unexpected circle. %d\n\t%s\n\t%s",
- SkRRectPriv::IsCircle(srcRRect),
- srcRRect.dumpToString(true).c_str(),
- devRRect.dumpToString(true).c_str());
- SkASSERTF(!devRRect.isRect(),
- "Unexpected rect. %d\n\t%s\n\t%s",
- srcRRect.isRect(),
- srcRRect.dumpToString(true).c_str(),
- devRRect.dumpToString(true).c_str());
-#endif
- // TODO: loosen this up
- if (!SkRRectPriv::IsSimpleCircular(devRRect)) {
- return nullptr;
- }
-
- if (SkGpuBlurUtils::IsEffectivelyZeroSigma(xformedSigma)) {
- return inputFP;
- }
-
- // Make sure we can successfully ninepatch this rrect -- the blur sigma has to be
- // sufficiently small relative to both the size of the corner radius and the
- // width (and height) of the rrect.
- SkRRect rrectToDraw;
- SkISize dimensions;
- SkScalar ignored[SkGpuBlurUtils::kBlurRRectMaxDivisions];
-
- bool ninePatchable = SkGpuBlurUtils::ComputeBlurredRRectParams(srcRRect,
- devRRect,
- sigma,
- xformedSigma,
- &rrectToDraw,
- &dimensions,
- ignored,
- ignored,
- ignored,
- ignored);
- if (!ninePatchable) {
- return nullptr;
- }
-
- std::unique_ptr<GrFragmentProcessor> maskFP =
- find_or_create_rrect_blur_mask_fp(context, rrectToDraw, dimensions, xformedSigma);
- if (!maskFP) {
- return nullptr;
- }
-
- return std::unique_ptr<GrFragmentProcessor>(
- new GrRRectBlurEffect(std::move(inputFP),
- xformedSigma,
- devRRect.getBounds(),
- SkRRectPriv::GetSimpleRadii(devRRect).fX,
- std::move(maskFP)));
-}
-#include "src/core/SkUtils.h"
-#include "src/gpu/GrTexture.h"
-#include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
-#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
-#include "src/gpu/glsl/GrGLSLProgramBuilder.h"
-#include "src/sksl/SkSLCPP.h"
-#include "src/sksl/SkSLUtil.h"
-class GrGLSLRRectBlurEffect : public GrGLSLFragmentProcessor {
-public:
- GrGLSLRRectBlurEffect() {}
- void emitCode(EmitArgs& args) override {
- GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
- const GrRRectBlurEffect& _outer = args.fFp.cast<GrRRectBlurEffect>();
- (void)_outer;
- auto sigma = _outer.sigma;
- (void)sigma;
- auto rect = _outer.rect;
- (void)rect;
- auto cornerRadius = _outer.cornerRadius;
- (void)cornerRadius;
- cornerRadiusVar = args.fUniformHandler->addUniform(
- &_outer, kFragment_GrShaderFlag, kHalf_GrSLType, "cornerRadius");
- proxyRectVar = args.fUniformHandler->addUniform(
- &_outer, kFragment_GrShaderFlag, kFloat4_GrSLType, "proxyRect");
- blurRadiusVar = args.fUniformHandler->addUniform(
- &_outer, kFragment_GrShaderFlag, kHalf_GrSLType, "blurRadius");
- fragBuilder->codeAppendf(
- R"SkSL(float2 translatedFragPosFloat = sk_FragCoord.xy - %s.xy;
-float2 proxyCenter = (%s.zw - %s.xy) * 0.5;
-half edgeSize = (2.0 * %s + %s) + 0.5;
-translatedFragPosFloat -= proxyCenter;
-half2 fragDirection = half2(sign(translatedFragPosFloat));
-translatedFragPosFloat = abs(translatedFragPosFloat);
-half2 translatedFragPosHalf = half2(translatedFragPosFloat - (proxyCenter - float(edgeSize)));
-translatedFragPosHalf = max(translatedFragPosHalf, 0.0);
-translatedFragPosHalf *= fragDirection;
-translatedFragPosHalf += half2(edgeSize);
-half2 proxyDims = half2(2.0 * edgeSize);
-half2 texCoord = translatedFragPosHalf / proxyDims;)SkSL",
- args.fUniformHandler->getUniformCStr(proxyRectVar),
- args.fUniformHandler->getUniformCStr(proxyRectVar),
- args.fUniformHandler->getUniformCStr(proxyRectVar),
- args.fUniformHandler->getUniformCStr(blurRadiusVar),
- args.fUniformHandler->getUniformCStr(cornerRadiusVar));
- SkString _sample0 = this->invokeChild(0, args);
- SkString _coords1("float2(texCoord)");
- SkString _sample1 = this->invokeChild(1, args, _coords1.c_str());
- fragBuilder->codeAppendf(
- R"SkSL(
-return %s * %s.w;
-)SkSL",
- _sample0.c_str(),
- _sample1.c_str());
- }
-
-private:
- void onSetData(const GrGLSLProgramDataManager& pdman,
- const GrFragmentProcessor& _proc) override {
- const GrRRectBlurEffect& _outer = _proc.cast<GrRRectBlurEffect>();
- { pdman.set1f(cornerRadiusVar, _outer.cornerRadius); }
- auto sigma = _outer.sigma;
- (void)sigma;
- auto rect = _outer.rect;
- (void)rect;
- UniformHandle& cornerRadius = cornerRadiusVar;
- (void)cornerRadius;
- UniformHandle& proxyRect = proxyRectVar;
- (void)proxyRect;
- UniformHandle& blurRadius = blurRadiusVar;
- (void)blurRadius;
-
- float blurRadiusValue = 3.f * SkScalarCeilToScalar(sigma - 1 / 6.0f);
- pdman.set1f(blurRadius, blurRadiusValue);
-
- SkRect outset = rect;
- outset.outset(blurRadiusValue, blurRadiusValue);
- pdman.set4f(proxyRect, outset.fLeft, outset.fTop, outset.fRight, outset.fBottom);
- }
- UniformHandle proxyRectVar;
- UniformHandle blurRadiusVar;
- UniformHandle cornerRadiusVar;
-};
-std::unique_ptr<GrGLSLFragmentProcessor> GrRRectBlurEffect::onMakeProgramImpl() const {
- return std::make_unique<GrGLSLRRectBlurEffect>();
-}
-void GrRRectBlurEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
- GrProcessorKeyBuilder* b) const {}
-bool GrRRectBlurEffect::onIsEqual(const GrFragmentProcessor& other) const {
- const GrRRectBlurEffect& that = other.cast<GrRRectBlurEffect>();
- (void)that;
- if (sigma != that.sigma) return false;
- if (rect != that.rect) return false;
- if (cornerRadius != that.cornerRadius) return false;
- return true;
-}
-GrRRectBlurEffect::GrRRectBlurEffect(const GrRRectBlurEffect& src)
- : INHERITED(kGrRRectBlurEffect_ClassID, src.optimizationFlags())
- , sigma(src.sigma)
- , rect(src.rect)
- , cornerRadius(src.cornerRadius) {
- this->cloneAndRegisterAllChildProcessors(src);
-}
-std::unique_ptr<GrFragmentProcessor> GrRRectBlurEffect::clone() const {
- return std::make_unique<GrRRectBlurEffect>(*this);
-}
-#if GR_TEST_UTILS
-SkString GrRRectBlurEffect::onDumpInfo() const {
- return SkStringPrintf("(sigma=%f, rect=float4(%f, %f, %f, %f), cornerRadius=%f)",
- sigma,
- rect.left(),
- rect.top(),
- rect.right(),
- rect.bottom(),
- cornerRadius);
-}
-#endif
-GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRRectBlurEffect);
-#if GR_TEST_UTILS
-std::unique_ptr<GrFragmentProcessor> GrRRectBlurEffect::TestCreate(GrProcessorTestData* d) {
- SkScalar w = d->fRandom->nextRangeScalar(100.f, 1000.f);
- SkScalar h = d->fRandom->nextRangeScalar(100.f, 1000.f);
- SkScalar r = d->fRandom->nextRangeF(1.f, 9.f);
- SkScalar sigma = d->fRandom->nextRangeF(1.f, 10.f);
- SkRRect rrect;
- rrect.setRectXY(SkRect::MakeWH(w, h), r, r);
- return GrRRectBlurEffect::Make(d->inputFP(), d->context(), sigma, sigma, rrect, rrect);
-}
-#endif
diff --git a/src/gpu/effects/generated/GrRRectBlurEffect.h b/src/gpu/effects/generated/GrRRectBlurEffect.h
deleted file mode 100644
index bad6fcf..0000000
--- a/src/gpu/effects/generated/GrRRectBlurEffect.h
+++ /dev/null
@@ -1,62 +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 GrRRectBlurEffect.fp; do not modify.
- **************************************************************************************************/
-#ifndef GrRRectBlurEffect_DEFINED
-#define GrRRectBlurEffect_DEFINED
-
-#include "include/core/SkM44.h"
-#include "include/core/SkTypes.h"
-
-#include "include/core/SkRect.h"
-class GrRecordingContext;
-class SkRRect;
-
-#include "src/gpu/GrFragmentProcessor.h"
-
-class GrRRectBlurEffect : public GrFragmentProcessor {
-public:
- static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> inputFP,
- GrRecordingContext* context,
- float sigma,
- float xformedSigma,
- const SkRRect& srcRRect,
- const SkRRect& devRRect);
- GrRRectBlurEffect(const GrRRectBlurEffect& src);
- std::unique_ptr<GrFragmentProcessor> clone() const override;
- const char* name() const override { return "RRectBlurEffect"; }
- float sigma;
- SkRect rect;
- float cornerRadius;
-
-private:
- GrRRectBlurEffect(std::unique_ptr<GrFragmentProcessor> inputFP,
- float sigma,
- SkRect rect,
- float cornerRadius,
- std::unique_ptr<GrFragmentProcessor> ninePatchFP)
- : INHERITED(kGrRRectBlurEffect_ClassID,
- (OptimizationFlags)ProcessorOptimizationFlags(inputFP.get()) &
- kCompatibleWithCoverageAsAlpha_OptimizationFlag)
- , sigma(sigma)
- , rect(rect)
- , cornerRadius(cornerRadius) {
- this->registerChild(std::move(inputFP), SkSL::SampleUsage::PassThrough());
- this->registerChild(std::move(ninePatchFP), SkSL::SampleUsage::Explicit());
- }
- std::unique_ptr<GrGLSLFragmentProcessor> onMakeProgramImpl() const override;
- void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
- bool onIsEqual(const GrFragmentProcessor&) const override;
-#if GR_TEST_UTILS
- SkString onDumpInfo() const override;
-#endif
- GR_DECLARE_FRAGMENT_PROCESSOR_TEST
- using INHERITED = GrFragmentProcessor;
-};
-#endif
diff --git a/src/gpu/effects/generated/GrRectBlurEffect.cpp b/src/gpu/effects/generated/GrRectBlurEffect.cpp
deleted file mode 100644
index 3444d03..0000000
--- a/src/gpu/effects/generated/GrRectBlurEffect.cpp
+++ /dev/null
@@ -1,140 +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 GrRectBlurEffect.fp; do not modify.
- **************************************************************************************************/
-#include "GrRectBlurEffect.h"
-
-#include "src/core/SkUtils.h"
-#include "src/gpu/GrTexture.h"
-#include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
-#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
-#include "src/gpu/glsl/GrGLSLProgramBuilder.h"
-#include "src/sksl/SkSLCPP.h"
-#include "src/sksl/SkSLUtil.h"
-class GrGLSLRectBlurEffect : public GrGLSLFragmentProcessor {
-public:
- GrGLSLRectBlurEffect() {}
- void emitCode(EmitArgs& args) override {
- GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
- const GrRectBlurEffect& _outer = args.fFp.cast<GrRectBlurEffect>();
- (void)_outer;
- auto rect = _outer.rect;
- (void)rect;
- auto isFast = _outer.isFast;
- (void)isFast;
- rectVar = args.fUniformHandler->addUniform(
- &_outer, kFragment_GrShaderFlag, kFloat4_GrSLType, "rect");
- fragBuilder->codeAppendf(
- R"SkSL(half xCoverage;
-half yCoverage;
-
-@if (%s) {
- half2 xy = max(half2(%s.xy - %s), half2(%s - %s.zw));)SkSL",
- (_outer.isFast ? "true" : "false"),
- args.fUniformHandler->getUniformCStr(rectVar),
- args.fSampleCoord,
- args.fSampleCoord,
- args.fUniformHandler->getUniformCStr(rectVar));
- SkString _coords0("float2(half2(xy.x, 0.5))");
- SkString _sample0 = this->invokeChild(0, args, _coords0.c_str());
- fragBuilder->codeAppendf(
- R"SkSL(
- xCoverage = %s.w;)SkSL",
- _sample0.c_str());
- SkString _coords1("float2(half2(xy.y, 0.5))");
- SkString _sample1 = this->invokeChild(0, args, _coords1.c_str());
- fragBuilder->codeAppendf(
- R"SkSL(
- yCoverage = %s.w;
-} else {
- half4 rect = half4(half2(%s.xy - %s), half2(%s - %s.zw));)SkSL",
- _sample1.c_str(),
- args.fUniformHandler->getUniformCStr(rectVar),
- args.fSampleCoord,
- args.fSampleCoord,
- args.fUniformHandler->getUniformCStr(rectVar));
- SkString _coords2("float2(half2(rect.x, 0.5))");
- SkString _sample2 = this->invokeChild(0, args, _coords2.c_str());
- SkString _coords3("float2(half2(rect.z, 0.5))");
- SkString _sample3 = this->invokeChild(0, args, _coords3.c_str());
- fragBuilder->codeAppendf(
- R"SkSL(
- xCoverage = (1.0 - %s.w) - %s.w;)SkSL",
- _sample2.c_str(),
- _sample3.c_str());
- SkString _coords4("float2(half2(rect.y, 0.5))");
- SkString _sample4 = this->invokeChild(0, args, _coords4.c_str());
- SkString _coords5("float2(half2(rect.w, 0.5))");
- SkString _sample5 = this->invokeChild(0, args, _coords5.c_str());
- fragBuilder->codeAppendf(
- R"SkSL(
- yCoverage = (1.0 - %s.w) - %s.w;
-}
-return half4(xCoverage * yCoverage);
-)SkSL",
- _sample4.c_str(),
- _sample5.c_str());
- }
-
-private:
- void onSetData(const GrGLSLProgramDataManager& pdman,
- const GrFragmentProcessor& _proc) override {
- const GrRectBlurEffect& _outer = _proc.cast<GrRectBlurEffect>();
- { pdman.set4fv(rectVar, 1, reinterpret_cast<const float*>(&_outer.rect)); }
- }
- UniformHandle rectVar;
-};
-std::unique_ptr<GrGLSLFragmentProcessor> GrRectBlurEffect::onMakeProgramImpl() const {
- return std::make_unique<GrGLSLRectBlurEffect>();
-}
-void GrRectBlurEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
- GrProcessorKeyBuilder* b) const {
- b->addBool(isFast, "isFast");
-}
-bool GrRectBlurEffect::onIsEqual(const GrFragmentProcessor& other) const {
- const GrRectBlurEffect& that = other.cast<GrRectBlurEffect>();
- (void)that;
- if (rect != that.rect) return false;
- if (isFast != that.isFast) return false;
- return true;
-}
-GrRectBlurEffect::GrRectBlurEffect(const GrRectBlurEffect& src)
- : INHERITED(kGrRectBlurEffect_ClassID, src.optimizationFlags())
- , rect(src.rect)
- , isFast(src.isFast) {
- this->cloneAndRegisterAllChildProcessors(src);
- this->setUsesSampleCoordsDirectly();
-}
-std::unique_ptr<GrFragmentProcessor> GrRectBlurEffect::clone() const {
- return std::make_unique<GrRectBlurEffect>(*this);
-}
-#if GR_TEST_UTILS
-SkString GrRectBlurEffect::onDumpInfo() const {
- return SkStringPrintf("(rect=float4(%f, %f, %f, %f), isFast=%s)",
- rect.left(),
- rect.top(),
- rect.right(),
- rect.bottom(),
- (isFast ? "true" : "false"));
-}
-#endif
-GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRectBlurEffect);
-#if GR_TEST_UTILS
-std::unique_ptr<GrFragmentProcessor> GrRectBlurEffect::TestCreate(GrProcessorTestData* data) {
- float sigma = data->fRandom->nextRangeF(3, 8);
- int x = data->fRandom->nextRangeF(1, 200);
- int y = data->fRandom->nextRangeF(1, 200);
- float width = data->fRandom->nextRangeF(200, 300);
- float height = data->fRandom->nextRangeF(200, 300);
- SkMatrix vm = GrTest::TestMatrixPreservesRightAngles(data->fRandom);
- auto rect = SkRect::MakeXYWH(x, y, width, height);
- return GrRectBlurEffect::Make(
- data->inputFP(), data->context(), *data->caps()->shaderCaps(), rect, vm, sigma);
-}
-#endif
diff --git a/src/gpu/effects/generated/GrRectBlurEffect.h b/src/gpu/effects/generated/GrRectBlurEffect.h
deleted file mode 100644
index 279c924..0000000
--- a/src/gpu/effects/generated/GrRectBlurEffect.h
+++ /dev/null
@@ -1,181 +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 GrRectBlurEffect.fp; do not modify.
- **************************************************************************************************/
-#ifndef GrRectBlurEffect_DEFINED
-#define GrRectBlurEffect_DEFINED
-
-#include "include/core/SkM44.h"
-#include "include/core/SkTypes.h"
-
-#include <cmath>
-#include "include/core/SkRect.h"
-#include "include/core/SkScalar.h"
-#include "include/gpu/GrRecordingContext.h"
-#include "src/core/SkBlurMask.h"
-#include "src/core/SkGpuBlurUtils.h"
-#include "src/core/SkMathPriv.h"
-#include "src/gpu/GrProxyProvider.h"
-#include "src/gpu/GrRecordingContextPriv.h"
-#include "src/gpu/GrShaderCaps.h"
-#include "src/gpu/GrThreadSafeCache.h"
-#include "src/gpu/SkGr.h"
-#include "src/gpu/effects/GrMatrixEffect.h"
-#include "src/gpu/effects/GrTextureEffect.h"
-
-#include "src/gpu/GrFragmentProcessor.h"
-
-class GrRectBlurEffect : public GrFragmentProcessor {
-public:
- static std::unique_ptr<GrFragmentProcessor> MakeIntegralFP(GrRecordingContext* rContext,
- float sixSigma) {
- SkASSERT(!SkGpuBlurUtils::IsEffectivelyZeroSigma(sixSigma / 6.f));
- auto threadSafeCache = rContext->priv().threadSafeCache();
-
- int width = SkGpuBlurUtils::CreateIntegralTable(sixSigma, nullptr);
-
- static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
- GrUniqueKey key;
- GrUniqueKey::Builder builder(&key, kDomain, 1, "Rect Blur Mask");
- builder[0] = width;
- builder.finish();
-
- SkMatrix m = SkMatrix::Scale(width / sixSigma, 1.f);
-
- GrSurfaceProxyView view = threadSafeCache->find(key);
-
- if (view) {
- SkASSERT(view.origin() == kTopLeft_GrSurfaceOrigin);
- return GrTextureEffect::Make(
- std::move(view), kPremul_SkAlphaType, m, GrSamplerState::Filter::kLinear);
- }
-
- SkBitmap bitmap;
- if (!SkGpuBlurUtils::CreateIntegralTable(sixSigma, &bitmap)) {
- return {};
- }
-
- view = std::get<0>(GrMakeUncachedBitmapProxyView(rContext, bitmap));
- if (!view) {
- return {};
- }
-
- view = threadSafeCache->add(key, view);
-
- SkASSERT(view.origin() == kTopLeft_GrSurfaceOrigin);
- return GrTextureEffect::Make(
- std::move(view), kPremul_SkAlphaType, m, GrSamplerState::Filter::kLinear);
- }
-
- static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> inputFP,
- GrRecordingContext* context,
- const GrShaderCaps& caps,
- const SkRect& srcRect,
- const SkMatrix& viewMatrix,
- float transformedSigma) {
- SkASSERT(viewMatrix.preservesRightAngles());
- SkASSERT(srcRect.isSorted());
-
- if (SkGpuBlurUtils::IsEffectivelyZeroSigma(transformedSigma)) {
- // No need to blur the rect
- return inputFP;
- }
-
- SkMatrix invM;
- SkRect rect;
- if (viewMatrix.rectStaysRect()) {
- invM = SkMatrix::I();
- // We can do everything in device space when the src rect projects to a rect in device
- // space.
- SkAssertResult(viewMatrix.mapRect(&rect, srcRect));
- } else {
- // The view matrix may scale, perhaps anisotropically. But we want to apply our device
- // space "transformedSigma" to the delta of frag coord from the rect edges. Factor out
- // the scaling to define a space that is purely rotation/translation from device space
- // (and scale from src space) We'll meet in the middle: pre-scale the src rect to be in
- // this space and then apply the inverse of the rotation/translation portion to the
- // frag coord.
- SkMatrix m;
- SkSize scale;
- if (!viewMatrix.decomposeScale(&scale, &m)) {
- return nullptr;
- }
- if (!m.invert(&invM)) {
- return nullptr;
- }
- rect = {srcRect.left() * scale.width(),
- srcRect.top() * scale.height(),
- srcRect.right() * scale.width(),
- srcRect.bottom() * scale.height()};
- }
-
- if (!caps.floatIs32Bits()) {
- // We promote the math that gets us into the Gaussian space to full float when the rect
- // coords are large. If we don't have full float then fail. We could probably clip the
- // rect to an outset device bounds instead.
- if (SkScalarAbs(rect.fLeft) > 16000.f || SkScalarAbs(rect.fTop) > 16000.f ||
- SkScalarAbs(rect.fRight) > 16000.f || SkScalarAbs(rect.fBottom) > 16000.f) {
- return nullptr;
- }
- }
-
- const float sixSigma = 6 * transformedSigma;
- std::unique_ptr<GrFragmentProcessor> integral = MakeIntegralFP(context, sixSigma);
- if (!integral) {
- return nullptr;
- }
-
- // In the fast variant we think of the midpoint of the integral texture as aligning
- // with the closest rect edge both in x and y. To simplify texture coord calculation we
- // inset the rect so that the edge of the inset rect corresponds to t = 0 in the texture.
- // It actually simplifies things a bit in the !isFast case, too.
- float threeSigma = sixSigma / 2;
- SkRect insetRect = {rect.left() + threeSigma,
- rect.top() + threeSigma,
- rect.right() - threeSigma,
- rect.bottom() - threeSigma};
-
- // In our fast variant we find the nearest horizontal and vertical edges and for each
- // do a lookup in the integral texture for each and multiply them. When the rect is
- // less than 6 sigma wide then things aren't so simple and we have to consider both the
- // left and right edge of the rectangle (and similar in y).
- bool isFast = insetRect.isSorted();
- std::unique_ptr<GrFragmentProcessor> fp(
- new GrRectBlurEffect(insetRect, std::move(integral), isFast));
- if (!invM.isIdentity()) {
- fp = GrMatrixEffect::Make(invM, std::move(fp));
- }
- fp = GrFragmentProcessor::DeviceSpace(std::move(fp));
- return GrFragmentProcessor::MulInputByChildAlpha(std::move(fp));
- }
- GrRectBlurEffect(const GrRectBlurEffect& src);
- std::unique_ptr<GrFragmentProcessor> clone() const override;
- const char* name() const override { return "RectBlurEffect"; }
- SkRect rect;
- bool isFast;
-
-private:
- GrRectBlurEffect(SkRect rect, std::unique_ptr<GrFragmentProcessor> integral, bool isFast)
- : INHERITED(kGrRectBlurEffect_ClassID,
- (OptimizationFlags)kCompatibleWithCoverageAsAlpha_OptimizationFlag)
- , rect(rect)
- , isFast(isFast) {
- this->setUsesSampleCoordsDirectly();
- this->registerChild(std::move(integral), SkSL::SampleUsage::Explicit());
- }
- std::unique_ptr<GrGLSLFragmentProcessor> onMakeProgramImpl() const override;
- void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
- bool onIsEqual(const GrFragmentProcessor&) const override;
-#if GR_TEST_UTILS
- SkString onDumpInfo() const override;
-#endif
- GR_DECLARE_FRAGMENT_PROCESSOR_TEST
- using INHERITED = GrFragmentProcessor;
-};
-#endif