diff options
-rw-r--r-- | include/core/SkMaskFilter.h | 3 | ||||
-rw-r--r-- | include/effects/SkBlurMaskFilter.h | 10 | ||||
-rw-r--r-- | src/core/SkMaskFilter.cpp | 2 | ||||
-rw-r--r-- | src/effects/SkBlurMaskFilter.cpp | 173 | ||||
-rw-r--r-- | src/effects/SkGpuBlurUtils.cpp | 7 | ||||
-rw-r--r-- | src/effects/SkGpuBlurUtils.h | 4 | ||||
-rw-r--r-- | src/gpu/SkGpuDevice.cpp | 2 | ||||
-rw-r--r-- | src/gpu/SkGpuDevice_drawTexture.cpp | 2 | ||||
-rw-r--r-- | tests/BlurTest.cpp | 62 |
9 files changed, 203 insertions, 62 deletions
diff --git a/include/core/SkMaskFilter.h b/include/core/SkMaskFilter.h index 908226c8ad..e30fc62548 100644 --- a/include/core/SkMaskFilter.h +++ b/include/core/SkMaskFilter.h @@ -17,6 +17,7 @@ #include "SkStrokeRec.h" class GrClip; +class GrContext; class GrDrawContext; class GrPaint; class GrRenderTarget; @@ -122,7 +123,7 @@ public: * Try to directly render a rounded rect mask filter into the target. Returns * true if drawing was successful. */ - virtual bool directFilterRRectMaskGPU(GrTextureProvider* texProvider, + virtual bool directFilterRRectMaskGPU(GrContext*, GrDrawContext* drawContext, GrPaint* grp, const GrClip&, diff --git a/include/effects/SkBlurMaskFilter.h b/include/effects/SkBlurMaskFilter.h index 3ba91774f0..4b037e70cd 100644 --- a/include/effects/SkBlurMaskFilter.h +++ b/include/effects/SkBlurMaskFilter.h @@ -65,6 +65,16 @@ public: SkScalar blurRadius); #endif + static bool ComputeBlurredRRectParams(const SkRRect& rrect, + SkScalar sigma, + SkRRect* rrectToDraw, + SkISize* widthHeight, + SkScalar xs[4], + int* numXs, + SkScalar ys[4], + int* numYs); + + SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP() private: diff --git a/src/core/SkMaskFilter.cpp b/src/core/SkMaskFilter.cpp index fccce45a77..352cf71db9 100644 --- a/src/core/SkMaskFilter.cpp +++ b/src/core/SkMaskFilter.cpp @@ -324,7 +324,7 @@ bool SkMaskFilter::canFilterMaskGPU(const SkRRect& devRRect, } -bool SkMaskFilter::directFilterRRectMaskGPU(GrTextureProvider* texProvider, +bool SkMaskFilter::directFilterRRectMaskGPU(GrContext*, GrDrawContext* drawContext, GrPaint* grp, const GrClip&, diff --git a/src/effects/SkBlurMaskFilter.cpp b/src/effects/SkBlurMaskFilter.cpp index 4215733492..f66cb9c8db 100644 --- a/src/effects/SkBlurMaskFilter.cpp +++ b/src/effects/SkBlurMaskFilter.cpp @@ -22,7 +22,7 @@ #include "GrTexture.h" #include "GrFragmentProcessor.h" #include "GrInvariantOutput.h" -#include "SkDraw.h" +#include "GrStyle.h" #include "effects/GrSimpleTextureEffect.h" #include "glsl/GrGLSLFragmentProcessor.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" @@ -56,7 +56,7 @@ public: const SkMatrix& viewMatrix, const SkStrokeRec& strokeRec, const SkPath& path) const override; - bool directFilterRRectMaskGPU(GrTextureProvider* texProvider, + bool directFilterRRectMaskGPU(GrContext*, GrDrawContext* drawContext, GrPaint* grp, const GrClip&, @@ -136,6 +136,64 @@ sk_sp<SkMaskFilter> SkBlurMaskFilter::Make(SkBlurStyle style, SkScalar sigma, ui return sk_sp<SkMaskFilter>(new SkBlurMaskFilterImpl(sigma, style, flags)); } +bool SkBlurMaskFilter::ComputeBlurredRRectParams(const SkRRect& rrect, + SkScalar sigma, + SkRRect* rrectToDraw, + SkISize* widthHeight, + SkScalar xs[4], + int* numXs, + SkScalar ys[4], + int* numYs) { + unsigned int blurRadius = 3*SkScalarCeilToInt(sigma-1/6.0f); + + const SkRect& orig = rrect.getBounds(); + const SkVector& radiiUL = rrect.radii(SkRRect::kUpperLeft_Corner); + const SkVector& radiiUR = rrect.radii(SkRRect::kUpperRight_Corner); + const SkVector& radiiLR = rrect.radii(SkRRect::kLowerRight_Corner); + const SkVector& radiiLL = rrect.radii(SkRRect::kLowerLeft_Corner); + + const int left = SkScalarCeilToInt(SkTMax<SkScalar>(radiiUL.fX, radiiLL.fX)); + const int top = SkScalarCeilToInt(SkTMax<SkScalar>(radiiUL.fY, radiiUR.fY)); + const int right = SkScalarCeilToInt(SkTMax<SkScalar>(radiiUR.fX, radiiLR.fX)); + const int bot = SkScalarCeilToInt(SkTMax<SkScalar>(radiiLL.fY, radiiLR.fY)); + + // This is a conservative check for nine-patchability + if (orig.fLeft + left + blurRadius >= orig.fRight - right - blurRadius || + orig.fTop + top + blurRadius >= orig.fBottom - bot - blurRadius) { + return false; + } + + int newRRWidth, newRRHeight; + + // 3x3 case + newRRWidth = 2*blurRadius + left + right + 1; + newRRHeight = 2*blurRadius + top + bot + 1; + widthHeight->fWidth = newRRWidth + 2 * blurRadius; + widthHeight->fHeight = newRRHeight + 2 * blurRadius; + // TODO: need to return non-normalized indices + xs[0] = 0.0f; + xs[1] = (blurRadius + left) / (float) widthHeight->fWidth; + xs[2] = (blurRadius + left + 1.0f) / widthHeight->fWidth; + xs[3] = 1.0f; + *numXs = 4; + ys[0] = 0.0f; + ys[1] = (blurRadius + top) / (float) widthHeight->fHeight; + ys[2] = (blurRadius + top + 1.0f) / widthHeight->fHeight; + ys[3] = 1.0f; + *numYs = 4; + + const SkRect newRect = SkRect::MakeXYWH(SkIntToScalar(blurRadius), SkIntToScalar(blurRadius), + SkIntToScalar(newRRWidth), SkIntToScalar(newRRHeight)); + SkVector newRadii[4]; + newRadii[0] = { SkScalarCeilToScalar(radiiUL.fX), SkScalarCeilToScalar(radiiUL.fY) }; + newRadii[1] = { SkScalarCeilToScalar(radiiUR.fX), SkScalarCeilToScalar(radiiUR.fY) }; + newRadii[2] = { SkScalarCeilToScalar(radiiLR.fX), SkScalarCeilToScalar(radiiLR.fY) }; + newRadii[3] = { SkScalarCeilToScalar(radiiLL.fX), SkScalarCeilToScalar(radiiLL.fY) }; + + rrectToDraw->setRectRadii(newRect, newRadii); + return true; +} + /////////////////////////////////////////////////////////////////////////////// SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkScalar sigma, SkBlurStyle style, uint32_t flags) @@ -599,6 +657,7 @@ void SkBlurMaskFilterImpl::flatten(SkWriteBuffer& buffer) const { buffer.writeUInt(fBlurFlags); } + #if SK_SUPPORT_GPU class GrGLRectBlurEffect; @@ -910,7 +969,7 @@ bool SkBlurMaskFilterImpl::directFilterMaskGPU(GrTextureProvider* texProvider, class GrRRectBlurEffect : public GrFragmentProcessor { public: - static sk_sp<GrFragmentProcessor> Make(GrTextureProvider*, float sigma, const SkRRect&); + static sk_sp<GrFragmentProcessor> Make(GrContext*, float sigma, const SkRRect&); virtual ~GrRRectBlurEffect() {}; const char* name() const override { return "GrRRectBlur"; } @@ -939,13 +998,55 @@ private: typedef GrFragmentProcessor INHERITED; }; +static sk_sp<GrTexture> make_rrect_blur_mask(GrContext* context, + const SkRRect& rrect, + float sigma, + bool doAA) { + SkRRect rrectToDraw; + SkISize size; + SkScalar xs[4], ys[4]; + int numXs, numYs; + + SkBlurMaskFilter::ComputeBlurredRRectParams(rrect, sigma, &rrectToDraw, &size, + xs, &numXs, ys, &numYs); + + // TODO: this could be approx but the texture coords will need to be updated + sk_sp<GrDrawContext> dc(context->makeDrawContext(SkBackingFit::kExact, + size.fWidth, size.fHeight, + kAlpha_8_GrPixelConfig, nullptr)); + if (!dc) { + return nullptr; + } + + GrPaint grPaint; + grPaint.setAntiAlias(doAA); + + dc->clear(nullptr, 0x0, true); + dc->drawRRect(GrNoClip(), grPaint, SkMatrix::I(), rrectToDraw, GrStyle::SimpleFill()); + + sk_sp<GrTexture> tex(dc->asTexture()); + sk_sp<GrDrawContext> dc2(SkGpuBlurUtils::GaussianBlur(context, + tex.get(), + nullptr, + SkIRect::MakeWH(size.fWidth, + size.fHeight), + nullptr, + sigma, sigma, SkBackingFit::kExact)); + if (!dc2) { + return nullptr; + } -sk_sp<GrFragmentProcessor> GrRRectBlurEffect::Make(GrTextureProvider* texProvider, float sigma, - const SkRRect& rrect) { + return dc2->asTexture(); +} + +sk_sp<GrFragmentProcessor> GrRRectBlurEffect::Make(GrContext* context, float sigma, + const SkRRect& rrect) { if (rrect.isCircle()) { - return GrCircleBlurFragmentProcessor::Make(texProvider, rrect.rect(), sigma); + return GrCircleBlurFragmentProcessor::Make(context->textureProvider(), + rrect.rect(), sigma); } + // TODO: loosen this up if (!rrect.isSimpleCircular()) { return nullptr; } @@ -968,55 +1069,17 @@ sk_sp<GrFragmentProcessor> GrRRectBlurEffect::Make(GrTextureProvider* texProvide builder[1] = cornerRadius; builder.finish(); - SkAutoTUnref<GrTexture> blurNinePatchTexture(texProvider->findAndRefTextureByUniqueKey(key)); + sk_sp<GrTexture> blurNinePatchTexture( + context->textureProvider()->findAndRefTextureByUniqueKey(key)); if (!blurNinePatchTexture) { - SkMask mask; - - unsigned int smallRectSide = 2*(blurRadius + cornerRadius) + 1; - - mask.fBounds = SkIRect::MakeWH(smallRectSide, smallRectSide); - mask.fFormat = SkMask::kA8_Format; - mask.fRowBytes = mask.fBounds.width(); - mask.fImage = SkMask::AllocImage(mask.computeTotalImageSize()); - SkAutoMaskFreeImage amfi(mask.fImage); - - memset(mask.fImage, 0, mask.computeTotalImageSize()); - - SkRect smallRect; - smallRect.setWH(SkIntToScalar(smallRectSide), SkIntToScalar(smallRectSide)); - - SkRRect smallRRect; - smallRRect.setRectXY(smallRect, SkIntToScalar(cornerRadius), SkIntToScalar(cornerRadius)); - - SkPath path; - path.addRRect(smallRRect); - - SkDraw::DrawToMask(path, &mask.fBounds, nullptr, nullptr, &mask, - SkMask::kJustRenderImage_CreateMode, SkStrokeRec::kFill_InitStyle); - - SkMask blurredMask; - if (!SkBlurMask::BoxBlur(&blurredMask, mask, sigma, kNormal_SkBlurStyle, - kHigh_SkBlurQuality, nullptr, true)) { - return nullptr; - } - - unsigned int texSide = smallRectSide + 2*blurRadius; - GrSurfaceDesc texDesc; - texDesc.fWidth = texSide; - texDesc.fHeight = texSide; - texDesc.fConfig = kAlpha_8_GrPixelConfig; - texDesc.fIsMipMapped = false; - - blurNinePatchTexture.reset( - texProvider->createTexture(texDesc, SkBudgeted::kYes , blurredMask.fImage, 0)); - SkMask::FreeImage(blurredMask.fImage); + blurNinePatchTexture = make_rrect_blur_mask(context, rrect, sigma, true); if (!blurNinePatchTexture) { return nullptr; } - texProvider->assignUniqueKeyToTexture(key, blurNinePatchTexture); + context->textureProvider()->assignUniqueKeyToTexture(key, blurNinePatchTexture.get()); } - return sk_sp<GrFragmentProcessor>(new GrRRectBlurEffect(sigma, rrect, blurNinePatchTexture)); + return sk_sp<GrFragmentProcessor>(new GrRRectBlurEffect(sigma, rrect, blurNinePatchTexture.get())); } void GrRRectBlurEffect::onComputeInvariantOutput(GrInvariantOutput* inout) const { @@ -1050,7 +1113,7 @@ sk_sp<GrFragmentProcessor> GrRRectBlurEffect::TestCreate(GrProcessorTestData* d) SkScalar sigma = d->fRandom->nextRangeF(1.f,10.f); SkRRect rrect; rrect.setRectXY(SkRect::MakeWH(w, h), r, r); - return GrRRectBlurEffect::Make(d->fContext->textureProvider(), sigma, rrect); + return GrRRectBlurEffect::Make(d->fContext, sigma, rrect); } ////////////////////////////////////////////////////////////////////////////// @@ -1153,7 +1216,7 @@ GrGLSLFragmentProcessor* GrRRectBlurEffect::onCreateGLSLInstance() const { return new GrGLRRectBlurEffect; } -bool SkBlurMaskFilterImpl::directFilterRRectMaskGPU(GrTextureProvider* texProvider, +bool SkBlurMaskFilterImpl::directFilterRRectMaskGPU(GrContext* context, GrDrawContext* drawContext, GrPaint* grp, const GrClip& clip, @@ -1172,12 +1235,14 @@ bool SkBlurMaskFilterImpl::directFilterRRectMaskGPU(GrTextureProvider* texProvid SkScalar xformedSigma = this->computeXformedSigma(viewMatrix); - sk_sp<GrFragmentProcessor> fp(GrRRectBlurEffect::Make(texProvider, xformedSigma, rrect)); + sk_sp<GrFragmentProcessor> fp(GrRRectBlurEffect::Make(context, xformedSigma, rrect)); if (!fp) { return false; } - grp->addCoverageFragmentProcessor(std::move(fp)); + GrPaint newPaint(*grp); + newPaint.addCoverageFragmentProcessor(std::move(fp)); + newPaint.setAntiAlias(false); SkMatrix inverse; if (!viewMatrix.invert(&inverse)) { @@ -1189,7 +1254,7 @@ bool SkBlurMaskFilterImpl::directFilterRRectMaskGPU(GrTextureProvider* texProvid SkRect proxyRect = rrect.rect(); proxyRect.outset(extra, extra); - drawContext->fillRectWithLocalMatrix(clip, *grp, SkMatrix::I(), proxyRect, inverse); + drawContext->fillRectWithLocalMatrix(clip, newPaint, SkMatrix::I(), proxyRect, inverse); return true; } diff --git a/src/effects/SkGpuBlurUtils.cpp b/src/effects/SkGpuBlurUtils.cpp index 869cd76c5b..ebb480d3df 100644 --- a/src/effects/SkGpuBlurUtils.cpp +++ b/src/effects/SkGpuBlurUtils.cpp @@ -186,7 +186,8 @@ sk_sp<GrDrawContext> GaussianBlur(GrContext* context, const SkIRect& dstBounds, const SkIRect* srcBounds, float sigmaX, - float sigmaY) { + float sigmaY, + SkBackingFit fit) { SkASSERT(context); SkIRect clearRect; int scaleFactorX, radiusX; @@ -226,7 +227,7 @@ sk_sp<GrDrawContext> GaussianBlur(GrContext* context, const int height = dstBounds.height(); const GrPixelConfig config = srcTexture->config(); - sk_sp<GrDrawContext> dstDrawContext(context->makeDrawContext(SkBackingFit::kApprox, + sk_sp<GrDrawContext> dstDrawContext(context->makeDrawContext(fit, width, height, config, colorSpace, 0, kDefault_GrSurfaceOrigin)); if (!dstDrawContext) { @@ -246,7 +247,7 @@ sk_sp<GrDrawContext> GaussianBlur(GrContext* context, return dstDrawContext; } - sk_sp<GrDrawContext> tmpDrawContext(context->makeDrawContext(SkBackingFit::kApprox, + sk_sp<GrDrawContext> tmpDrawContext(context->makeDrawContext(fit, width, height, config, colorSpace, 0, kDefault_GrSurfaceOrigin)); if (!tmpDrawContext) { diff --git a/src/effects/SkGpuBlurUtils.h b/src/effects/SkGpuBlurUtils.h index a5de6a242a..a12a08873c 100644 --- a/src/effects/SkGpuBlurUtils.h +++ b/src/effects/SkGpuBlurUtils.h @@ -29,6 +29,7 @@ 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 fit backing fit for the returned draw context * @return The drawContext containing the blurred result. */ sk_sp<GrDrawContext> GaussianBlur(GrContext* context, @@ -37,7 +38,8 @@ namespace SkGpuBlurUtils { const SkIRect& dstBounds, const SkIRect* srcBounds, float sigmaX, - float sigmaY); + float sigmaY, + SkBackingFit fit = SkBackingFit::kApprox); }; #endif diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index cd34b1feba..fb513f4483 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -436,7 +436,7 @@ void SkGpuDevice::drawRRect(const SkDraw& draw, const SkRRect& rrect, // clipped out return; } - if (paint.getMaskFilter()->directFilterRRectMaskGPU(fContext->textureProvider(), + if (paint.getMaskFilter()->directFilterRRectMaskGPU(fContext, fDrawContext.get(), &grPaint, fClip, diff --git a/src/gpu/SkGpuDevice_drawTexture.cpp b/src/gpu/SkGpuDevice_drawTexture.cpp index 2843c31996..e141cc2d62 100644 --- a/src/gpu/SkGpuDevice_drawTexture.cpp +++ b/src/gpu/SkGpuDevice_drawTexture.cpp @@ -226,7 +226,7 @@ void SkGpuDevice::drawTextureProducerImpl(GrTextureProducer* producer, // First see if we can do the draw + mask filter direct to the dst. SkStrokeRec rec(SkStrokeRec::kFill_InitStyle); - if (mf->directFilterRRectMaskGPU(fContext->textureProvider(), + if (mf->directFilterRRectMaskGPU(fContext, fDrawContext.get(), &grPaint, clip, diff --git a/tests/BlurTest.cpp b/tests/BlurTest.cpp index 6ccb0471aa..32e2930171 100644 --- a/tests/BlurTest.cpp +++ b/tests/BlurTest.cpp @@ -574,4 +574,66 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SmallBoxBlurBug, reporter, ctxInfo) { #endif + +DEF_TEST(BlurredRRectNinePatchComputation, reporter) { + const SkRect r = SkRect::MakeXYWH(10, 10, 100, 100); + + bool ninePatchable; + SkRRect rrectToDraw; + SkISize size; + SkScalar xs[4], ys[4]; + int numXs, numYs; + + // not nine-patchable + { + SkVector radii[4] = { { 100, 100 }, { 0, 0 }, { 100, 100 }, { 0, 0 } }; + + SkRRect rr; + rr.setRectRadii(r, radii); + + ninePatchable = SkBlurMaskFilter::ComputeBlurredRRectParams(rr, 3.0f, &rrectToDraw, &size, + xs, &numXs, ys, &numYs); + REPORTER_ASSERT(reporter, !ninePatchable); + } + + // simple circular + { + SkRRect rr; + rr.setRectXY(r, 10, 10); + + ninePatchable = SkBlurMaskFilter::ComputeBlurredRRectParams(rr, 3.0f, &rrectToDraw, &size, + xs, &numXs, ys, &numYs); + REPORTER_ASSERT(reporter, ninePatchable); + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SkIntToScalar(size.fWidth), 57.0f)); + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SkIntToScalar(size.fHeight), 57.0)); + REPORTER_ASSERT(reporter, 4 == numXs && 4 == numYs); + for (int i = 0; i < numXs; ++i) { + REPORTER_ASSERT(reporter, xs[i] >= 0.0f && xs[i] <= 1.0f); + } + for (int i = 0; i < numYs; ++i) { + REPORTER_ASSERT(reporter, ys[i] >= 0.0f && ys[i] <= 1.0f); + } + } + + // simple elliptical + { + SkRRect rr; + rr.setRectXY(r, 2, 10); + + ninePatchable = SkBlurMaskFilter::ComputeBlurredRRectParams(rr, 3.0f, &rrectToDraw, &size, + xs, &numXs, ys, &numYs); + REPORTER_ASSERT(reporter, ninePatchable); + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SkIntToScalar(size.fWidth), 41.0f)); + REPORTER_ASSERT(reporter, SkScalarNearlyEqual(SkIntToScalar(size.fHeight), 57.0)); + REPORTER_ASSERT(reporter, 4 == numXs && 4 == numYs); + for (int i = 0; i < numXs; ++i) { + REPORTER_ASSERT(reporter, xs[i] >= 0.0f && xs[i] <= 1.0f); + } + for (int i = 0; i < numYs; ++i) { + REPORTER_ASSERT(reporter, ys[i] >= 0.0f && ys[i] <= 1.0f); + } + } + +} + /////////////////////////////////////////////////////////////////////////////////////////// |