diff options
author | 2017-09-28 16:11:20 -0400 | |
---|---|---|
committer | 2017-09-28 20:39:52 +0000 | |
commit | 5a4fb6eb335d29f24a1b239d1c0eb11b5d2ee59f (patch) | |
tree | e430b9f7c4fd0df844e7fd0c60b42e8a0e1fa683 /src/core/SkBlurImageFilter.cpp | |
parent | 4cdbf6056de29e8c12c3b1b4c2c2fa286cf68049 (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.cpp | 194 |
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()); } |