aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/SkBlurImageFilter.cpp
diff options
context:
space:
mode:
authorGravatar Herb Derby <herb@google.com>2017-09-28 16:11:20 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-09-28 20:39:52 +0000
commit5a4fb6eb335d29f24a1b239d1c0eb11b5d2ee59f (patch)
treee430b9f7c4fd0df844e7fd0c60b42e8a0e1fa683 /src/core/SkBlurImageFilter.cpp
parent4cdbf6056de29e8c12c3b1b4c2c2fa286cf68049 (diff)
Reorganize blur filter to insert new implementation
Change-Id: Ie9afd2de2bfb9bcd04b5f8b7f319a24025520bef Reviewed-on: https://skia-review.googlesource.com/52400 Commit-Queue: Herb Derby <herb@google.com> Reviewed-by: Stephen White <senorblanco@chromium.org>
Diffstat (limited to 'src/core/SkBlurImageFilter.cpp')
-rw-r--r--src/core/SkBlurImageFilter.cpp194
1 files changed, 122 insertions, 72 deletions
diff --git a/src/core/SkBlurImageFilter.cpp b/src/core/SkBlurImageFilter.cpp
index 18bbff2a7e..5d9a1cf33a 100644
--- a/src/core/SkBlurImageFilter.cpp
+++ b/src/core/SkBlurImageFilter.cpp
@@ -10,6 +10,7 @@
#include "SkAutoPixmapStorage.h"
#include "SkColorData.h"
#include "SkColorSpaceXformer.h"
+#include "SkTFitsIn.h"
#include "SkGpuBlurUtils.h"
#include "SkOpts.h"
#include "SkReadBuffer.h"
@@ -22,7 +23,7 @@
#include "SkGr.h"
#endif
-class SkBlurImageFilterImpl : public SkImageFilter {
+class SkBlurImageFilterImpl final : public SkImageFilter {
public:
SkBlurImageFilterImpl(SkScalar sigmaX,
SkScalar sigmaY,
@@ -43,11 +44,23 @@ protected:
SkIRect onFilterNodeBounds(const SkIRect& src, const SkMatrix&, MapDirection) const override;
private:
- SkSize fSigma;
- SkBlurImageFilter::TileMode fTileMode;
typedef SkImageFilter INHERITED;
-
friend class SkImageFilter;
+
+ #if SK_SUPPORT_GPU
+ sk_sp<SkSpecialImage> gpuFilter(
+ SkSpecialImage *source,
+ SkVector sigma, const sk_sp<SkSpecialImage> &input,
+ SkIRect inputBounds, SkIRect dstBounds) const;
+ #endif
+
+ sk_sp<SkSpecialImage> cpuFilter(
+ SkSpecialImage *source,
+ SkVector sigma, const sk_sp<SkSpecialImage> &input,
+ SkIRect inputBounds, SkIRect dstBounds) const;
+
+ SkSize fSigma;
+ SkBlurImageFilter::TileMode fTileMode;
};
SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkImageFilter)
@@ -162,6 +175,7 @@ sk_sp<SkSpecialImage> SkBlurImageFilterImpl::onFilterImage(SkSpecialImage* sourc
SkIRect inputBounds = SkIRect::MakeXYWH(inputOffset.fX, inputOffset.fY,
input->width(), input->height());
+ // Calculate the destination bounds.
SkIRect dstBounds;
if (!this->applyCropRect(this->mapContext(ctx), inputBounds, &dstBounds)) {
return nullptr;
@@ -170,83 +184,117 @@ sk_sp<SkSpecialImage> SkBlurImageFilterImpl::onFilterImage(SkSpecialImage* sourc
return nullptr;
}
+ // Save the offset in preparation to make all rectangles relative to the inputOffset.
+ SkIPoint resultOffset = SkIPoint::Make(dstBounds.fLeft, dstBounds.fTop);
+
+ // Make all bounds relative to the inputOffset.
+ inputBounds.offset(-inputOffset);
+ dstBounds.offset(-inputOffset);
+
const SkVector sigma = map_sigma(fSigma, ctx.ctm());
+ if (sigma.x() < 0 || sigma.y() < 0) {
+ return nullptr;
+ }
+ sk_sp<SkSpecialImage> result;
#if SK_SUPPORT_GPU
if (source->isTextureBacked()) {
- GrContext* context = source->getContext();
-
// Ensure the input is in the destination's gamut. This saves us from having to do the
// xform during the filter itself.
input = ImageToColorSpace(input.get(), ctx.outputProperties());
- sk_sp<GrTextureProxy> inputTexture(input->asTextureProxyRef(context));
- if (!inputTexture) {
- return nullptr;
- }
-
- if (0 == sigma.x() && 0 == sigma.y()) {
- offset->fX = inputBounds.x();
- offset->fY = inputBounds.y();
- return input->makeSubset(inputBounds.makeOffset(-inputOffset.x(),
- -inputOffset.y()));
- }
-
- offset->fX = dstBounds.fLeft;
- offset->fY = dstBounds.fTop;
- inputBounds.offset(-inputOffset);
- dstBounds.offset(-inputOffset);
- // Typically, we would create the RTC with the output's color space (from ctx), but we
- // always blur in the PixelConfig of the *input*. Those might not be compatible (if they
- // have different transfer functions). We've already guaranteed that those color spaces
- // have the same gamut, so in this case, we do everything in the input's color space.
- sk_sp<GrRenderTargetContext> renderTargetContext(SkGpuBlurUtils::GaussianBlur(
- context,
- std::move(inputTexture),
- sk_ref_sp(input->getColorSpace()),
- dstBounds,
- inputBounds,
- sigma.x(),
- sigma.y(),
- to_texture_domain_mode(fTileMode)));
- if (!renderTargetContext) {
- return nullptr;
- }
-
- return SkSpecialImage::MakeDeferredFromGpu(context,
- SkIRect::MakeWH(dstBounds.width(),
- dstBounds.height()),
- kNeedNewImageUniqueID_SpecialImage,
- renderTargetContext->asTextureProxyRef(),
- renderTargetContext->refColorSpace(),
- &source->props());
+ result = this->gpuFilter(source, sigma, input, inputBounds, dstBounds);
+ } else
+ #endif
+ {
+ #if defined(SK_SUPPORT_LEGACY_BLUR_IMAGE)
+ result = this->cpuFilter(source, sigma, input, inputBounds, dstBounds);
+ #else
+ // The new code will go here.
+ result = this->cpuFilter(source, sigma, input, inputBounds, dstBounds);
+ #endif
}
-#endif
- // TODO: Implement CPU backend for different fTileMode.
- int kernelSizeX, kernelSizeX3, lowOffsetX, highOffsetX;
- int kernelSizeY, kernelSizeY3, lowOffsetY, highOffsetY;
- get_box3_params(sigma.x(), &kernelSizeX, &kernelSizeX3, &lowOffsetX, &highOffsetX);
- get_box3_params(sigma.y(), &kernelSizeY, &kernelSizeY3, &lowOffsetY, &highOffsetY);
+ // Return the resultOffset if the blur succeeded.
+ if (result != nullptr) {
+ *offset = resultOffset;
+ }
+ return result;
+}
- if (kernelSizeX < 0 || kernelSizeY < 0) {
+#if SK_SUPPORT_GPU
+sk_sp<SkSpecialImage> SkBlurImageFilterImpl::gpuFilter(
+ SkSpecialImage *source,
+ SkVector sigma, const sk_sp<SkSpecialImage> &input,
+ SkIRect inputBounds, SkIRect dstBounds) const
+{
+ // If both sigmas produce arms of the cross that are less than 1/2048, then they
+ // do not contribute to the sum of the filter in a way to change a gamma corrected result.
+ // Let s = 1/(2*sigma^2)
+ // The normalizing value n = 1 + 4*E^(-s) + 4*E^(-2s)
+ // The raw cross arm value c = E^-s
+ // The normalized cross arm value = c/n
+ // N[Solve[{c/n == 1/2048, sigma > 0}, sigma], 16]
+ static constexpr double kCrossTooSmall = 0.2561130112451658;
+ if (sigma.x() < kCrossTooSmall && sigma.y() < kCrossTooSmall) {
+ return input->makeSubset(inputBounds);
+ }
+
+ GrContext* context = source->getContext();
+
+ sk_sp<GrTextureProxy> inputTexture(input->asTextureProxyRef(context));
+ if (!inputTexture) {
return nullptr;
}
- if (kernelSizeX == 0 && kernelSizeY == 0) {
- offset->fX = inputBounds.x();
- offset->fY = inputBounds.y();
- return input->makeSubset(inputBounds.makeOffset(-inputOffset.x(),
- -inputOffset.y()));
+ // Typically, we would create the RTC with the output's color space (from ctx), but we
+ // always blur in the PixelConfig of the *input*. Those might not be compatible (if they
+ // have different transfer functions). We've already guaranteed that those color spaces
+ // have the same gamut, so in this case, we do everything in the input's color space.
+ sk_sp<GrRenderTargetContext> renderTargetContext(SkGpuBlurUtils::GaussianBlur(
+ context,
+ std::move(inputTexture),
+ sk_ref_sp(input->getColorSpace()),
+ dstBounds,
+ inputBounds,
+ sigma.x(),
+ sigma.y(),
+ to_texture_domain_mode(fTileMode)));
+ if (!renderTargetContext) {
+ return nullptr;
}
- SkBitmap inputBM;
+ return SkSpecialImage::MakeDeferredFromGpu(context,
+ SkIRect::MakeWH(dstBounds.width(),
+ dstBounds.height()),
+ kNeedNewImageUniqueID_SpecialImage,
+ renderTargetContext->asTextureProxyRef(),
+ renderTargetContext->refColorSpace(),
+ &source->props());
+}
+#endif
- if (!input->getROPixels(&inputBM)) {
- return nullptr;
+// TODO: Implement CPU backend for different fTileMode.
+sk_sp<SkSpecialImage> SkBlurImageFilterImpl::cpuFilter(
+ SkSpecialImage *source,
+ SkVector sigma, const sk_sp<SkSpecialImage> &input,
+ SkIRect inputBounds, SkIRect dstBounds) const
+{
+ // If both sigmas will result in a zero width window, there is nothing to do.
+ // N[Solve[sigma*3*Sqrt[2 Pi]/4 == 1/2, sigma], 16]
+ static constexpr double kZeroWindow = 0.2659615202676218;
+ if (sigma.x() < kZeroWindow && sigma.y() < kZeroWindow) {
+ return input->makeSubset(inputBounds);
}
- if (inputBM.colorType() != kN32_SkColorType) {
+ int kernelSizeX, kernelSizeX3, lowOffsetX, highOffsetX;
+ int kernelSizeY, kernelSizeY3, lowOffsetY, highOffsetY;
+ get_box3_params(sigma.x(), &kernelSizeX, &kernelSizeX3, &lowOffsetX, &highOffsetX);
+ get_box3_params(sigma.y(), &kernelSizeY, &kernelSizeY3, &lowOffsetY, &highOffsetY);
+
+ SkBitmap inputBM;
+
+ if (!input->getROPixels(&inputBM) && inputBM.colorType() != kN32_SkColorType) {
return nullptr;
}
@@ -258,19 +306,22 @@ sk_sp<SkSpecialImage> SkBlurImageFilterImpl::onFilterImage(SkSpecialImage* sourc
return nullptr;
}
- offset->fX = dstBounds.fLeft;
- offset->fY = dstBounds.fTop;
- SkPMColor* t = tmp.getAddr32(0, 0);
- SkPMColor* d = dst.getAddr32(0, 0);
- int w = dstBounds.width(), h = dstBounds.height();
- const SkPMColor* s = inputBM.getAddr32(inputBounds.x() - inputOffset.x(),
- inputBounds.y() - inputOffset.y());
+ // Get ready to blur.
+ const SkPMColor* s = inputBM.getAddr32(inputBounds.x(), inputBounds.y());
+ SkPMColor* t = tmp.getAddr32(0, 0);
+ SkPMColor* d = dst.getAddr32(0, 0);
+
+ // Shift everything from being relative to the orignal input bounds to the destination bounds.
inputBounds.offset(-dstBounds.x(), -dstBounds.y());
dstBounds.offset(-dstBounds.x(), -dstBounds.y());
+
+ int w = dstBounds.width(),
+ h = dstBounds.height(),
+ sw = inputBM.rowBytesAsPixels();
+
SkIRect inputBoundsT = SkIRect::MakeLTRB(inputBounds.top(), inputBounds.left(),
inputBounds.bottom(), inputBounds.right());
SkIRect dstBoundsT = SkIRect::MakeWH(dstBounds.height(), dstBounds.width());
- int sw = int(inputBM.rowBytes() >> 2);
/**
*
@@ -307,8 +358,7 @@ sk_sp<SkSpecialImage> SkBlurImageFilterImpl::onFilterImage(SkSpecialImage* sourc
SkOpts::box_blur_xy(t, h, dstBoundsT, d, kernelSizeY3, highOffsetY, highOffsetY, h, w);
}
- return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(dstBounds.width(),
- dstBounds.height()),
+ return SkSpecialImage::MakeFromRaster(SkIRect::MakeSize(dstBounds.size()),
dst, &source->props());
}