diff options
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/SkBlurImageFilter.cpp | 45 | ||||
-rw-r--r-- | src/core/SkGpuBlurUtils.cpp | 99 | ||||
-rw-r--r-- | src/core/SkGpuBlurUtils.h | 20 |
3 files changed, 108 insertions, 56 deletions
diff --git a/src/core/SkBlurImageFilter.cpp b/src/core/SkBlurImageFilter.cpp index 6c18962fba..428b9375fb 100644 --- a/src/core/SkBlurImageFilter.cpp +++ b/src/core/SkBlurImageFilter.cpp @@ -26,7 +26,8 @@ public: SkBlurImageFilterImpl(SkScalar sigmaX, SkScalar sigmaY, sk_sp<SkImageFilter> input, - const CropRect* cropRect); + const CropRect* cropRect, + SkBlurImageFilter::TileMode tileMode); SkRect computeFastBounds(const SkRect&) const override; @@ -42,6 +43,7 @@ protected: private: SkSize fSigma; + SkBlurImageFilter::TileMode fTileMode; typedef SkImageFilter INHERITED; friend class SkImageFilter; @@ -55,11 +57,13 @@ SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END sk_sp<SkImageFilter> SkBlurImageFilter::Make(SkScalar sigmaX, SkScalar sigmaY, sk_sp<SkImageFilter> input, - const SkImageFilter::CropRect* cropRect) { + const SkImageFilter::CropRect* cropRect, + TileMode tileMode) { if (0 == sigmaX && 0 == sigmaY && !cropRect) { return input; } - return sk_sp<SkImageFilter>(new SkBlurImageFilterImpl(sigmaX, sigmaY, input, cropRect)); + return sk_sp<SkImageFilter>( + new SkBlurImageFilterImpl(sigmaX, sigmaY, input, cropRect, tileMode)); } // This rather arbitrary-looking value results in a maximum box blur kernel size @@ -77,9 +81,12 @@ static SkVector map_sigma(const SkSize& localSigma, const SkMatrix& ctm) { return sigma; } -SkBlurImageFilterImpl::SkBlurImageFilterImpl( - SkScalar sigmaX, SkScalar sigmaY, sk_sp<SkImageFilter> input, const CropRect* cropRect) - : INHERITED(&input, 1, cropRect), fSigma{sigmaX, sigmaY} {} +SkBlurImageFilterImpl::SkBlurImageFilterImpl(SkScalar sigmaX, + SkScalar sigmaY, + sk_sp<SkImageFilter> input, + const CropRect* cropRect, + SkBlurImageFilter::TileMode tileMode) + : INHERITED(&input, 1, cropRect), fSigma{sigmaX, sigmaY}, fTileMode(tileMode) {} sk_sp<SkFlattenable> SkBlurImageFilterImpl::CreateProc(SkReadBuffer& buffer) { SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1); @@ -94,6 +101,24 @@ void SkBlurImageFilterImpl::flatten(SkWriteBuffer& buffer) const { buffer.writeScalar(fSigma.fHeight); } +#if SK_SUPPORT_GPU +static GrTextureDomain::Mode to_texture_domain_mode(SkBlurImageFilter::TileMode tileMode) { + switch (tileMode) { + case SkBlurImageFilter::TileMode::kIgnore_TileMode: + return GrTextureDomain::kIgnore_Mode; + case SkBlurImageFilter::TileMode::kClamp_TileMode: + return GrTextureDomain::kClamp_Mode; + case SkBlurImageFilter::TileMode::kClampToBlack_TileMode: + return GrTextureDomain::kDecal_Mode; + case SkBlurImageFilter::TileMode::kRepeat_TileMode: + return GrTextureDomain::kRepeat_Mode; + default: + SkFAIL("Unsupported tile mode."); + return GrTextureDomain::kDecal_Mode; + } +} +#endif + static void get_box3_params(SkScalar s, int *kernelSize, int* kernelSize3, int *lowOffset, int *highOffset) { float pi = SkScalarToFloat(SK_ScalarPI); @@ -165,9 +190,10 @@ sk_sp<SkSpecialImage> SkBlurImageFilterImpl::onFilterImage(SkSpecialImage* sourc std::move(inputTexture), sk_ref_sp(input->getColorSpace()), dstBounds, - &inputBounds, + inputBounds, sigma.x(), - sigma.y())); + sigma.y(), + to_texture_domain_mode(fTileMode))); if (!renderTargetContext) { return nullptr; } @@ -182,6 +208,7 @@ sk_sp<SkSpecialImage> SkBlurImageFilterImpl::onFilterImage(SkSpecialImage* sourc } #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); @@ -279,7 +306,7 @@ const { sk_sp<SkImageFilter> input = this->getInput(0)->makeColorSpace(xformer); return SkBlurImageFilter::Make(fSigma.width(), fSigma.height(), std::move(input), - this->getCropRectIfSet()); + this->getCropRectIfSet(), fTileMode); } SkRect SkBlurImageFilterImpl::computeFastBounds(const SkRect& src) const { diff --git a/src/core/SkGpuBlurUtils.cpp b/src/core/SkGpuBlurUtils.cpp index 40a6c24706..129a8fbfb7 100644 --- a/src/core/SkGpuBlurUtils.cpp +++ b/src/core/SkGpuBlurUtils.cpp @@ -74,13 +74,13 @@ static void convolve_gaussian_1d(GrRenderTargetContext* renderTargetContext, Gr1DKernelEffect::Direction direction, int radius, float sigma, - bool useBounds, + GrTextureDomain::Mode mode, int bounds[2]) { GrPaint paint; paint.setGammaCorrect(renderTargetContext->isGammaCorrect()); sk_sp<GrFragmentProcessor> conv(GrGaussianConvolutionFragmentProcessor::Make( - std::move(proxy), direction, radius, sigma, useBounds, bounds)); + std::move(proxy), direction, radius, sigma, mode, bounds)); paint.addColorFragmentProcessor(std::move(conv)); paint.setPorterDuffXPFactory(SkBlendMode::kSrc); SkMatrix localMatrix = SkMatrix::MakeTrans(-SkIntToScalar(srcOffset.x()), @@ -98,19 +98,18 @@ static void convolve_gaussian_2d(GrRenderTargetContext* renderTargetContext, int radiusY, SkScalar sigmaX, SkScalar sigmaY, - const SkIRect* srcBounds) { + const SkIRect& srcBounds, + GrTextureDomain::Mode mode) { SkMatrix localMatrix = SkMatrix::MakeTrans(-SkIntToScalar(srcOffset.x()), -SkIntToScalar(srcOffset.y())); SkISize size = SkISize::Make(2 * radiusX + 1, 2 * radiusY + 1); SkIPoint kernelOffset = SkIPoint::Make(radiusX, radiusY); GrPaint paint; paint.setGammaCorrect(renderTargetContext->isGammaCorrect()); - SkIRect bounds = srcBounds ? *srcBounds : SkIRect::EmptyIRect(); sk_sp<GrFragmentProcessor> conv(GrMatrixConvolutionEffect::MakeGaussian( - std::move(proxy), bounds, size, 1.0, 0.0, kernelOffset, - srcBounds ? GrTextureDomain::kDecal_Mode : GrTextureDomain::kIgnore_Mode, - true, sigmaX, sigmaY)); + std::move(proxy), srcBounds, size, 1.0, 0.0, kernelOffset, + mode, true, sigmaX, sigmaY)); paint.addColorFragmentProcessor(std::move(conv)); paint.setPorterDuffXPFactory(SkBlendMode::kSrc); renderTargetContext->fillRectWithLocalMatrix(clip, std::move(paint), GrAA::kNo, SkMatrix::I(), @@ -124,21 +123,23 @@ static void convolve_gaussian(GrRenderTargetContext* renderTargetContext, Gr1DKernelEffect::Direction direction, int radius, float sigma, - const SkIRect* srcBounds, - const SkIPoint& srcOffset) { + const SkIRect& srcBounds, + const SkIPoint& srcOffset, + GrTextureDomain::Mode mode) { int bounds[2] = { 0, 0 }; SkIRect dstRect = SkIRect::MakeWH(srcRect.width(), srcRect.height()); - if (!srcBounds) { + if (GrTextureDomain::kIgnore_Mode == mode) { convolve_gaussian_1d(renderTargetContext, clip, dstRect, srcOffset, - std::move(proxy), direction, radius, sigma, false, bounds); + std::move(proxy), direction, radius, sigma, + GrTextureDomain::kIgnore_Mode, bounds); return; } - SkIRect midRect = *srcBounds, leftRect, rightRect; + SkIRect midRect = srcBounds, leftRect, rightRect; midRect.offset(srcOffset); SkIRect topRect, bottomRect; if (direction == Gr1DKernelEffect::kX_Direction) { - bounds[0] = srcBounds->left(); - bounds[1] = srcBounds->right(); + bounds[0] = srcBounds.left(); + bounds[1] = srcBounds.right(); topRect = SkIRect::MakeLTRB(0, 0, dstRect.right(), midRect.top()); bottomRect = SkIRect::MakeLTRB(0, midRect.bottom(), dstRect.right(), dstRect.bottom()); midRect.inset(radius, 0); @@ -148,8 +149,8 @@ static void convolve_gaussian(GrRenderTargetContext* renderTargetContext, dstRect.fTop = midRect.top(); dstRect.fBottom = midRect.bottom(); } else { - bounds[0] = srcBounds->top(); - bounds[1] = srcBounds->bottom(); + bounds[0] = srcBounds.top(); + bounds[1] = srcBounds.bottom(); topRect = SkIRect::MakeLTRB(0, 0, midRect.left(), dstRect.bottom()); bottomRect = SkIRect::MakeLTRB(midRect.right(), 0, dstRect.right(), dstRect.bottom()); midRect.inset(0, radius); @@ -166,18 +167,20 @@ static void convolve_gaussian(GrRenderTargetContext* renderTargetContext, if (!bottomRect.isEmpty()) { renderTargetContext->clear(&bottomRect, 0, false); } + if (midRect.isEmpty()) { // Blur radius covers srcBounds; use bounds over entire draw convolve_gaussian_1d(renderTargetContext, clip, dstRect, srcOffset, - std::move(proxy), direction, radius, sigma, true, bounds); + std::move(proxy), direction, radius, sigma, mode, bounds); } else { // Draw right and left margins with bounds; middle without. convolve_gaussian_1d(renderTargetContext, clip, leftRect, srcOffset, - proxy, direction, radius, sigma, true, bounds); + proxy, direction, radius, sigma, mode, bounds); convolve_gaussian_1d(renderTargetContext, clip, rightRect, srcOffset, - proxy, direction, radius, sigma, true, bounds); + proxy, direction, radius, sigma, mode, bounds); convolve_gaussian_1d(renderTargetContext, clip, midRect, srcOffset, - std::move(proxy), direction, radius, sigma, false, bounds); + std::move(proxy), direction, radius, sigma, + GrTextureDomain::kIgnore_Mode, bounds); } } @@ -187,12 +190,12 @@ sk_sp<GrRenderTargetContext> GaussianBlur(GrContext* context, sk_sp<GrTextureProxy> srcProxy, sk_sp<SkColorSpace> colorSpace, const SkIRect& dstBounds, - const SkIRect* srcBounds, + const SkIRect& srcBounds, float sigmaX, float sigmaY, + GrTextureDomain::Mode mode, SkBackingFit fit) { SkASSERT(context); - SkIRect clearRect; int scaleFactorX, radiusX; int scaleFactorY, radiusY; @@ -205,12 +208,11 @@ sk_sp<GrRenderTargetContext> GaussianBlur(GrContext* context, SkIRect localDstBounds = SkIRect::MakeWH(dstBounds.width(), dstBounds.height()); SkIRect localSrcBounds; SkIRect srcRect; - if (srcBounds) { - srcRect = localSrcBounds = *srcBounds; - srcRect.offset(srcOffset); - srcBounds = &localSrcBounds; - } else { + if (GrTextureDomain::kIgnore_Mode == mode) { srcRect = localDstBounds; + } else { + srcRect = localSrcBounds = srcBounds; + srcRect.offset(srcOffset); } scale_irect_roundout(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY); @@ -241,11 +243,12 @@ sk_sp<GrRenderTargetContext> GaussianBlur(GrContext* context, // We shouldn't be scaling because this is a small size blur SkASSERT((1 == scaleFactorX) && (1 == scaleFactorY)); - convolve_gaussian_2d(dstRenderTargetContext.get(), clip, localDstBounds, srcOffset, - std::move(srcProxy), radiusX, radiusY, sigmaX, sigmaY, srcBounds); + convolve_gaussian_2d(dstRenderTargetContext.get(), + clip, localDstBounds, srcOffset, std::move(srcProxy), + radiusX, radiusY, sigmaX, sigmaY, srcBounds, mode); return dstRenderTargetContext; - } + } sk_sp<GrRenderTargetContext> tmpRenderTargetContext(context->makeDeferredRenderTargetContext( fit, width, height, config, colorSpace, 0, kBottomLeft_GrSurfaceOrigin)); @@ -257,12 +260,15 @@ sk_sp<GrRenderTargetContext> GaussianBlur(GrContext* context, SkASSERT(SkIsPow2(scaleFactorX) && SkIsPow2(scaleFactorY)); + // GrTextureDomainEffect does not support kRepeat_Mode with GrSamplerParams::FilterMode. + GrTextureDomain::Mode modeForScaling = + GrTextureDomain::kRepeat_Mode == mode ? GrTextureDomain::kDecal_Mode : mode; for (int i = 1; i < scaleFactorX || i < scaleFactorY; i *= 2) { GrPaint paint; paint.setGammaCorrect(dstRenderTargetContext->isGammaCorrect()); SkIRect dstRect(srcRect); - if (srcBounds && i == 1) { - SkRect domain = SkRect::Make(*srcBounds); + if (GrTextureDomain::kIgnore_Mode != mode && i == 1) { + SkRect domain = SkRect::Make(localSrcBounds); domain.inset((i < scaleFactorX) ? SK_ScalarHalf : 0.0f, (i < scaleFactorY) ? SK_ScalarHalf : 0.0f); sk_sp<GrFragmentProcessor> fp(GrTextureDomainEffect::Make( @@ -270,7 +276,7 @@ sk_sp<GrRenderTargetContext> GaussianBlur(GrContext* context, nullptr, SkMatrix::I(), domain, - GrTextureDomain::kDecal_Mode, + modeForScaling, GrSamplerParams::kBilerp_FilterMode)); paint.addColorFragmentProcessor(std::move(fp)); srcRect.offset(-srcOffset); @@ -311,7 +317,7 @@ sk_sp<GrRenderTargetContext> GaussianBlur(GrContext* context, convolve_gaussian(dstRenderTargetContext.get(), clip, srcRect, std::move(srcProxy), Gr1DKernelEffect::kX_Direction, radiusX, sigmaX, - srcBounds, srcOffset); + localSrcBounds, srcOffset, mode); srcRenderTargetContext = dstRenderTargetContext; srcProxy = srcRenderTargetContext->asTextureProxyRef(); if (!srcProxy) { @@ -320,6 +326,10 @@ sk_sp<GrRenderTargetContext> GaussianBlur(GrContext* context, srcRect.offsetTo(0, 0); dstRenderTargetContext.swap(tmpRenderTargetContext); localSrcBounds = srcRect; + if (GrTextureDomain::kClamp_Mode == mode) { + // We need to adjust bounds because we only fill part of the srcRect in x-pass. + localSrcBounds.inset(0, radiusY); + } srcOffset.set(0, 0); } @@ -336,7 +346,7 @@ sk_sp<GrRenderTargetContext> GaussianBlur(GrContext* context, convolve_gaussian(dstRenderTargetContext.get(), clip, srcRect, std::move(srcProxy), Gr1DKernelEffect::kY_Direction, radiusY, sigmaY, - srcBounds, srcOffset); + localSrcBounds, srcOffset, mode); srcRenderTargetContext = dstRenderTargetContext; srcRect.offsetTo(0, 0); @@ -357,15 +367,26 @@ sk_sp<GrRenderTargetContext> GaussianBlur(GrContext* context, GrPaint paint; paint.setGammaCorrect(dstRenderTargetContext->isGammaCorrect()); - // FIXME: this should be mitchell, not bilinear. - GrSamplerParams params(SkShader::kClamp_TileMode, GrSamplerParams::kBilerp_FilterMode); sk_sp<GrTextureProxy> proxy(srcRenderTargetContext->asTextureProxyRef()); if (!proxy) { return nullptr; } - paint.addColorTextureProcessor(std::move(proxy), - nullptr, SkMatrix::I(), params); + if (GrTextureDomain::kIgnore_Mode != mode) { + SkRect domain = SkRect::Make(localSrcBounds); + sk_sp<GrFragmentProcessor> fp(GrTextureDomainEffect::Make( + std::move(proxy), + nullptr, + SkMatrix::I(), + domain, + modeForScaling, + GrSamplerParams::kBilerp_FilterMode)); + paint.addColorFragmentProcessor(std::move(fp)); + } else { + // FIXME: this should be mitchell, not bilinear. + GrSamplerParams params(SkShader::kClamp_TileMode, GrSamplerParams::kBilerp_FilterMode); + paint.addColorTextureProcessor(std::move(proxy), nullptr, SkMatrix::I(), params); + } paint.setPorterDuffXPFactory(SkBlendMode::kSrc); SkIRect dstRect(srcRect); diff --git a/src/core/SkGpuBlurUtils.h b/src/core/SkGpuBlurUtils.h index c6c56b61f7..8a6f7b1922 100644 --- a/src/core/SkGpuBlurUtils.h +++ b/src/core/SkGpuBlurUtils.h @@ -10,6 +10,7 @@ #if SK_SUPPORT_GPU #include "GrRenderTargetContext.h" +#include "effects/GrTextureDomain.h" class GrContext; class GrTexture; @@ -30,17 +31,20 @@ namespace SkGpuBlurUtils { * no pixels will be sampled outside of this rectangle. * @param sigmaX The blur's standard deviation in X. * @param sigmaY The blur's standard deviation in Y. + * @param mode The mode to handle samples outside bounds. * @param fit backing fit for the returned render target context * @return The renderTargetContext containing the blurred result. */ - sk_sp<GrRenderTargetContext> GaussianBlur(GrContext* context, - sk_sp<GrTextureProxy> src, - sk_sp<SkColorSpace> colorSpace, - const SkIRect& dstBounds, - const SkIRect* srcBounds, - float sigmaX, - float sigmaY, - SkBackingFit fit = SkBackingFit::kApprox); + sk_sp<GrRenderTargetContext> GaussianBlur( + GrContext* context, + sk_sp<GrTextureProxy> src, + sk_sp<SkColorSpace> colorSpace, + const SkIRect& dstBounds, + const SkIRect& srcBounds, + float sigmaX, + float sigmaY, + GrTextureDomain::Mode mode, + SkBackingFit fit = SkBackingFit::kApprox); }; #endif |