From 18e7556371506fd08bd0767a270c5db5a7804103 Mon Sep 17 00:00:00 2001 From: Mike Reed Date: Mon, 12 Mar 2018 14:03:47 -0400 Subject: [PATCH] move blur impl into core Ever since we added drawShadow to the public api, blurs have necessarily part of the core. This CL just formalizes that. This should also allow us to have builds that exclude all of /effects (for code size) and still be valid. Will follow-up with a change to deprecate SkBlurMaskFilter and SkBlurQuality (both no longer needed). Bug: skia: Change-Id: Ifbbd8b47a30a0386d215726b67bcf1e8b84fb8f5 Reviewed-on: https://skia-review.googlesource.com/113713 Reviewed-by: Mike Reed Commit-Queue: Mike Reed --- gn/core.gni | 1 + gn/effects.gni | 2 - gn/gpu.gni | 2 + gn/sksl.gni | 2 +- include/core/SkBlurTypes.h | 1 + include/core/SkMaskFilter.h | 17 + include/effects/SkBlurMaskFilter.h | 2 - src/c/sk_effects.cpp | 4 +- src/core/SkBlurMF.cpp | 1029 ++++++++++++++++ src/core/SkBlurPriv.h | 1 + src/core/SkMaskFilter.cpp | 2 + src/effects/SkBlurMaskFilter.cpp | 1032 +---------------- src/effects/SkLayerDrawLooper.cpp | 4 +- .../effects/GrCircleBlurFragmentProcessor.cpp | 0 .../effects/GrCircleBlurFragmentProcessor.fp | 0 .../effects/GrCircleBlurFragmentProcessor.h | 0 src/ports/SkGlobalInitialization_default.cpp | 2 - src/utils/SkShadowUtils.cpp | 10 +- tests/BlurTest.cpp | 56 +- 19 files changed, 1087 insertions(+), 1080 deletions(-) create mode 100644 src/core/SkBlurMF.cpp rename src/{ => gpu}/effects/GrCircleBlurFragmentProcessor.cpp (100%) rename src/{ => gpu}/effects/GrCircleBlurFragmentProcessor.fp (100%) rename src/{ => gpu}/effects/GrCircleBlurFragmentProcessor.h (100%) diff --git a/gn/core.gni b/gn/core.gni index 6adeab35df..922aa35c46 100644 --- a/gn/core.gni +++ b/gn/core.gni @@ -58,6 +58,7 @@ skia_core_sources = [ "$_src/core/SkBlurImageFilter.cpp", "$_src/core/SkBlurMask.cpp", "$_src/core/SkBlurMask.h", + "$_src/core/SkBlurMF.cpp", "$_src/core/SkBuffer.cpp", "$_src/core/SkCachedData.cpp", "$_src/core/SkCanvas.cpp", diff --git a/gn/effects.gni b/gn/effects.gni index aeaf766ace..4208306df8 100644 --- a/gn/effects.gni +++ b/gn/effects.gni @@ -10,8 +10,6 @@ _include = get_path_info("../include", "abspath") skia_effects_sources = [ "$_src/c/sk_effects.cpp", - "$_src/effects/GrCircleBlurFragmentProcessor.cpp", - "$_src/effects/GrCircleBlurFragmentProcessor.h", "$_src/effects/GrAlphaThresholdFragmentProcessor.cpp", "$_src/effects/GrAlphaThresholdFragmentProcessor.h", diff --git a/gn/gpu.gni b/gn/gpu.gni index ac52fe2bc1..31396d4f77 100644 --- a/gn/gpu.gni +++ b/gn/gpu.gni @@ -328,6 +328,8 @@ skia_gpu_sources = [ "$_src/gpu/effects/GrArithmeticFP.h", "$_src/gpu/effects/GrBlurredEdgeFragmentProcessor.cpp", "$_src/gpu/effects/GrBlurredEdgeFragmentProcessor.h", + "$_src/gpu/effects/GrCircleBlurFragmentProcessor.cpp", + "$_src/gpu/effects/GrCircleBlurFragmentProcessor.h", "$_src/gpu/effects/GrCircleEffect.cpp", "$_src/gpu/effects/GrCircleEffect.h", "$_src/gpu/effects/GrConfigConversionEffect.cpp", diff --git a/gn/sksl.gni b/gn/sksl.gni index 6248e695e3..974ac16ae8 100644 --- a/gn/sksl.gni +++ b/gn/sksl.gni @@ -27,10 +27,10 @@ skia_sksl_sources = [ skia_gpu_processor_sources = [ "$_src/effects/GrAlphaThresholdFragmentProcessor.fp", - "$_src/effects/GrCircleBlurFragmentProcessor.fp", "$_src/gpu/effects/GrAARectEffect.fp", "$_src/gpu/effects/GrArithmeticFP.fp", "$_src/gpu/effects/GrBlurredEdgeFragmentProcessor.fp", + "$_src/gpu/effects/GrCircleBlurFragmentProcessor.fp", "$_src/gpu/effects/GrCircleEffect.fp", "$_src/gpu/effects/GrConfigConversionEffect.fp", "$_src/gpu/effects/GrConstColorProcessor.fp", diff --git a/include/core/SkBlurTypes.h b/include/core/SkBlurTypes.h index afbec19b6c..b87ad0823e 100644 --- a/include/core/SkBlurTypes.h +++ b/include/core/SkBlurTypes.h @@ -19,6 +19,7 @@ enum SkBlurStyle { kLastEnum_SkBlurStyle = kInner_SkBlurStyle }; +// DEPRECATED enum SkBlurQuality { kLow_SkBlurQuality, //!< e.g. box filter kHigh_SkBlurQuality, //!< e.g. 3-pass similar to gaussian diff --git a/include/core/SkMaskFilter.h b/include/core/SkMaskFilter.h index 8c6e5a9e51..b506779d01 100644 --- a/include/core/SkMaskFilter.h +++ b/include/core/SkMaskFilter.h @@ -8,10 +8,13 @@ #ifndef SkMaskFilter_DEFINED #define SkMaskFilter_DEFINED +#include "SkBlurTypes.h" #include "SkCoverageMode.h" #include "SkFlattenable.h" +#include "SkScalar.h" class SkMatrix; +struct SkRect; class SkString; /** \class SkMaskFilter @@ -21,6 +24,20 @@ class SkString; */ class SK_API SkMaskFilter : public SkFlattenable { public: + /** Create a blur maskfilter. + * @param style The SkBlurStyle to use + * @param sigma Standard deviation of the Gaussian blur to apply. Must be > 0. + * @param occluder The rect for which no pixels need be drawn (b.c. it will be overdrawn + * with some opaque object. This is just a hint which backends are free to + * ignore. + * @param respectCTM if true the blur's sigma is modified by the CTM. + * @return The new blur maskfilter + */ + static sk_sp MakeBlur(SkBlurStyle style, SkScalar sigma, const SkRect& occluder, + bool respectCTM = true); + static sk_sp MakeBlur(SkBlurStyle style, SkScalar sigma, + bool respectCTM = true); + /** * Construct a maskfilter whose effect is to first apply the inner filter and then apply * the outer filter to the result of the inner's. Returns nullptr on failure. diff --git a/include/effects/SkBlurMaskFilter.h b/include/effects/SkBlurMaskFilter.h index bc5e1a2dcc..bc4ee8049f 100644 --- a/include/effects/SkBlurMaskFilter.h +++ b/include/effects/SkBlurMaskFilter.h @@ -58,8 +58,6 @@ public: SkScalar ambient, SkScalar specular); #endif - SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP() - private: SkBlurMaskFilter(); // can't be instantiated }; diff --git a/src/c/sk_effects.cpp b/src/c/sk_effects.cpp index d3bb62cb5e..860709a826 100644 --- a/src/c/sk_effects.cpp +++ b/src/c/sk_effects.cpp @@ -144,8 +144,8 @@ sk_shader_t* sk_shader_new_two_point_conical_gradient(const sk_point_t* start, /////////////////////////////////////////////////////////////////////////////////////////// -#include "../../include/effects/SkBlurMaskFilter.h" #include "sk_maskfilter.h" +#include "SkMaskFilter.h" const struct { sk_blurstyle_t fC; @@ -182,5 +182,5 @@ sk_maskfilter_t* sk_maskfilter_new_blur(sk_blurstyle_t cstyle, float sigma) { if (!find_blurstyle(cstyle, &style)) { return nullptr; } - return ToMaskFilter(SkBlurMaskFilter::Make(style, sigma).release()); + return ToMaskFilter(SkMaskFilter::MakeBlur(style, sigma).release()); } diff --git a/src/core/SkBlurMF.cpp b/src/core/SkBlurMF.cpp new file mode 100644 index 0000000000..c9599cc494 --- /dev/null +++ b/src/core/SkBlurMF.cpp @@ -0,0 +1,1029 @@ +/* + * Copyright 2006 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkBlurMask.h" +#include "SkBlurPriv.h" +#include "SkGpuBlurUtils.h" +#include "SkMaskFilterBase.h" +#include "SkReadBuffer.h" +#include "SkRRectPriv.h" +#include "SkWriteBuffer.h" +#include "SkMaskFilter.h" +#include "SkRRect.h" +#include "SkStringUtils.h" +#include "SkStrokeRec.h" +#include "SkVertices.h" + +#if SK_SUPPORT_GPU +#include "GrClip.h" +#include "GrContext.h" +#include "GrFragmentProcessor.h" +#include "GrRenderTargetContext.h" +#include "GrResourceProvider.h" +#include "GrShaderCaps.h" +#include "GrStyle.h" +#include "GrTextureProxy.h" +#include "effects/GrCircleBlurFragmentProcessor.h" +#include "effects/GrRectBlurEffect.h" +#include "effects/GrRRectBlurEffect.h" +#include "effects/GrSimpleTextureEffect.h" +#include "effects/GrTextureDomain.h" +#include "glsl/GrGLSLFragmentProcessor.h" +#include "glsl/GrGLSLFragmentShaderBuilder.h" +#include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" +#endif + +class SkBlurMaskFilterImpl : public SkMaskFilterBase { +public: + SkBlurMaskFilterImpl(SkScalar sigma, SkBlurStyle, const SkRect& occluder, bool respectCTM); + + // overrides from SkMaskFilter + SkMask::Format getFormat() const override; + bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&, + SkIPoint* margin) const override; + +#if SK_SUPPORT_GPU + bool canFilterMaskGPU(const SkRRect& devRRect, + const SkIRect& clipBounds, + const SkMatrix& ctm, + SkRect* maskRect) const override; + bool directFilterMaskGPU(GrContext*, + GrRenderTargetContext* renderTargetContext, + GrPaint&&, + const GrClip&, + const SkMatrix& viewMatrix, + const SkStrokeRec& strokeRec, + const SkPath& path) const override; + bool directFilterRRectMaskGPU(GrContext*, + GrRenderTargetContext* renderTargetContext, + GrPaint&&, + const GrClip&, + const SkMatrix& viewMatrix, + const SkStrokeRec& strokeRec, + const SkRRect& rrect, + const SkRRect& devRRect) const override; + sk_sp filterMaskGPU(GrContext*, + sk_sp srcProxy, + const SkMatrix& ctm, + const SkIRect& maskRect) const override; +#endif + + void computeFastBounds(const SkRect&, SkRect*) const override; + bool asABlur(BlurRec*) const override; + + SK_TO_STRING_OVERRIDE() + SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurMaskFilterImpl) + +protected: + FilterReturn filterRectsToNine(const SkRect[], int count, const SkMatrix&, + const SkIRect& clipBounds, + NinePatch*) const override; + + FilterReturn filterRRectToNine(const SkRRect&, const SkMatrix&, + const SkIRect& clipBounds, + NinePatch*) const override; + + bool filterRectMask(SkMask* dstM, const SkRect& r, const SkMatrix& matrix, + SkIPoint* margin, SkMask::CreateMode createMode) const; + bool filterRRectMask(SkMask* dstM, const SkRRect& r, const SkMatrix& matrix, + SkIPoint* margin, SkMask::CreateMode createMode) const; + + bool ignoreXform() const { return !fRespectCTM; } + +private: + // To avoid unseemly allocation requests (esp. for finite platforms like + // handset) we limit the radius so something manageable. (as opposed to + // a request like 10,000) + static const SkScalar kMAX_BLUR_SIGMA; + + SkScalar fSigma; + SkBlurStyle fBlurStyle; + SkRect fOccluder; + bool fRespectCTM; + + SkBlurQuality getQuality() const { return kHigh_SkBlurQuality; } + + SkBlurMaskFilterImpl(SkReadBuffer&); + void flatten(SkWriteBuffer&) const override; + + SkScalar computeXformedSigma(const SkMatrix& ctm) const { + SkScalar xformedSigma = this->ignoreXform() ? fSigma : ctm.mapRadius(fSigma); + return SkMinScalar(xformedSigma, kMAX_BLUR_SIGMA); + } + + friend class SkBlurMaskFilter; + + typedef SkMaskFilter INHERITED; + friend void sk_register_blur_maskfilter_createproc(); +}; + +const SkScalar SkBlurMaskFilterImpl::kMAX_BLUR_SIGMA = SkIntToScalar(128); + +// linearly interpolate between y1 & y3 to match x2's position between x1 & x3 +static SkScalar interp(SkScalar x1, SkScalar x2, SkScalar x3, SkScalar y1, SkScalar y3) { + SkASSERT(x1 <= x2 && x2 <= x3); + SkASSERT(y1 <= y3); + + SkScalar t = (x2 - x1) / (x3 - x1); + return y1 + t * (y3 - y1); +} + +// Insert 'lower' and 'higher' into 'array1' and insert a new value at each matching insertion +// point in 'array2' that linearly interpolates between the existing values. +// Return a bit mask which contains a copy of 'inputMask' for all the cells between the two +// insertion points. +static uint32_t insert_into_arrays(SkScalar* array1, SkScalar* array2, + SkScalar lower, SkScalar higher, + int* num, uint32_t inputMask, int maskSize) { + SkASSERT(lower < higher); + SkASSERT(lower >= array1[0] && higher <= array1[*num-1]); + + int32_t skipMask = 0x0; + int i; + for (i = 0; i < *num; ++i) { + if (lower >= array1[i] && lower < array1[i+1]) { + if (!SkScalarNearlyEqual(lower, array1[i])) { + memmove(&array1[i+2], &array1[i+1], (*num-i-1)*sizeof(SkScalar)); + array1[i+1] = lower; + memmove(&array2[i+2], &array2[i+1], (*num-i-1)*sizeof(SkScalar)); + array2[i+1] = interp(array1[i], lower, array1[i+2], array2[i], array2[i+2]); + i++; + (*num)++; + } + break; + } + } + for ( ; i < *num; ++i) { + skipMask |= inputMask << (i*maskSize); + if (higher > array1[i] && higher <= array1[i+1]) { + if (!SkScalarNearlyEqual(higher, array1[i+1])) { + memmove(&array1[i+2], &array1[i+1], (*num-i-1)*sizeof(SkScalar)); + array1[i+1] = higher; + memmove(&array2[i+2], &array2[i+1], (*num-i-1)*sizeof(SkScalar)); + array2[i+1] = interp(array1[i], higher, array1[i+2], array2[i], array2[i+2]); + (*num)++; + } + break; + } + } + + return skipMask; +} + +bool SkComputeBlurredRRectParams(const SkRRect& srcRRect, const SkRRect& devRRect, + const SkRect& occluder, + SkScalar sigma, SkScalar xformedSigma, + SkRRect* rrectToDraw, + SkISize* widthHeight, + SkScalar rectXs[kSkBlurRRectMaxDivisions], + SkScalar rectYs[kSkBlurRRectMaxDivisions], + SkScalar texXs[kSkBlurRRectMaxDivisions], + SkScalar texYs[kSkBlurRRectMaxDivisions], + int* numXs, int* numYs, uint32_t* skipMask) { + unsigned int devBlurRadius = 3*SkScalarCeilToInt(xformedSigma-1/6.0f); + SkScalar srcBlurRadius = 3.0f * sigma; + + const SkRect& devOrig = devRRect.getBounds(); + const SkVector& devRadiiUL = devRRect.radii(SkRRect::kUpperLeft_Corner); + const SkVector& devRadiiUR = devRRect.radii(SkRRect::kUpperRight_Corner); + const SkVector& devRadiiLR = devRRect.radii(SkRRect::kLowerRight_Corner); + const SkVector& devRadiiLL = devRRect.radii(SkRRect::kLowerLeft_Corner); + + const int devLeft = SkScalarCeilToInt(SkTMax(devRadiiUL.fX, devRadiiLL.fX)); + const int devTop = SkScalarCeilToInt(SkTMax(devRadiiUL.fY, devRadiiUR.fY)); + const int devRight = SkScalarCeilToInt(SkTMax(devRadiiUR.fX, devRadiiLR.fX)); + const int devBot = SkScalarCeilToInt(SkTMax(devRadiiLL.fY, devRadiiLR.fY)); + + // This is a conservative check for nine-patchability + if (devOrig.fLeft + devLeft + devBlurRadius >= devOrig.fRight - devRight - devBlurRadius || + devOrig.fTop + devTop + devBlurRadius >= devOrig.fBottom - devBot - devBlurRadius) { + return false; + } + + const SkVector& srcRadiiUL = srcRRect.radii(SkRRect::kUpperLeft_Corner); + const SkVector& srcRadiiUR = srcRRect.radii(SkRRect::kUpperRight_Corner); + const SkVector& srcRadiiLR = srcRRect.radii(SkRRect::kLowerRight_Corner); + const SkVector& srcRadiiLL = srcRRect.radii(SkRRect::kLowerLeft_Corner); + + const SkScalar srcLeft = SkTMax(srcRadiiUL.fX, srcRadiiLL.fX); + const SkScalar srcTop = SkTMax(srcRadiiUL.fY, srcRadiiUR.fY); + const SkScalar srcRight = SkTMax(srcRadiiUR.fX, srcRadiiLR.fX); + const SkScalar srcBot = SkTMax(srcRadiiLL.fY, srcRadiiLR.fY); + + int newRRWidth = 2*devBlurRadius + devLeft + devRight + 1; + int newRRHeight = 2*devBlurRadius + devTop + devBot + 1; + widthHeight->fWidth = newRRWidth + 2 * devBlurRadius; + widthHeight->fHeight = newRRHeight + 2 * devBlurRadius; + + const SkRect srcProxyRect = srcRRect.getBounds().makeOutset(srcBlurRadius, srcBlurRadius); + + rectXs[0] = srcProxyRect.fLeft; + rectXs[1] = srcProxyRect.fLeft + 2*srcBlurRadius + srcLeft; + rectXs[2] = srcProxyRect.fRight - 2*srcBlurRadius - srcRight; + rectXs[3] = srcProxyRect.fRight; + + rectYs[0] = srcProxyRect.fTop; + rectYs[1] = srcProxyRect.fTop + 2*srcBlurRadius + srcTop; + rectYs[2] = srcProxyRect.fBottom - 2*srcBlurRadius - srcBot; + rectYs[3] = srcProxyRect.fBottom; + + texXs[0] = 0.0f; + texXs[1] = 2.0f*devBlurRadius + devLeft; + texXs[2] = 2.0f*devBlurRadius + devLeft + 1; + texXs[3] = SkIntToScalar(widthHeight->fWidth); + + texYs[0] = 0.0f; + texYs[1] = 2.0f*devBlurRadius + devTop; + texYs[2] = 2.0f*devBlurRadius + devTop + 1; + texYs[3] = SkIntToScalar(widthHeight->fHeight); + + SkRect temp = occluder; + + *numXs = 4; + *numYs = 4; + *skipMask = 0; + if (!temp.isEmpty() && (srcProxyRect.contains(temp) || temp.intersect(srcProxyRect))) { + *skipMask = insert_into_arrays(rectXs, texXs, temp.fLeft, temp.fRight, numXs, 0x1, 1); + *skipMask = insert_into_arrays(rectYs, texYs, temp.fTop, temp.fBottom, + numYs, *skipMask, *numXs-1); + } + + const SkRect newRect = SkRect::MakeXYWH(SkIntToScalar(devBlurRadius), + SkIntToScalar(devBlurRadius), + SkIntToScalar(newRRWidth), + SkIntToScalar(newRRHeight)); + SkVector newRadii[4]; + newRadii[0] = { SkScalarCeilToScalar(devRadiiUL.fX), SkScalarCeilToScalar(devRadiiUL.fY) }; + newRadii[1] = { SkScalarCeilToScalar(devRadiiUR.fX), SkScalarCeilToScalar(devRadiiUR.fY) }; + newRadii[2] = { SkScalarCeilToScalar(devRadiiLR.fX), SkScalarCeilToScalar(devRadiiLR.fY) }; + newRadii[3] = { SkScalarCeilToScalar(devRadiiLL.fX), SkScalarCeilToScalar(devRadiiLL.fY) }; + + rrectToDraw->setRectRadii(newRect, newRadii); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkScalar sigma, SkBlurStyle style, + const SkRect& occluder, bool respectCTM) + : fSigma(sigma) + , fBlurStyle(style) + , fOccluder(occluder) + , fRespectCTM(respectCTM) { + SkASSERT(fSigma > 0); + SkASSERT((unsigned)style <= kLastEnum_SkBlurStyle); +} + +SkMask::Format SkBlurMaskFilterImpl::getFormat() const { + return SkMask::kA8_Format; +} + +bool SkBlurMaskFilterImpl::asABlur(BlurRec* rec) const { + if (this->ignoreXform()) { + return false; + } + + if (rec) { + rec->fSigma = fSigma; + rec->fStyle = fBlurStyle; + rec->fQuality = this->getQuality(); + } + return true; +} + +bool SkBlurMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src, + const SkMatrix& matrix, + SkIPoint* margin) const { + SkScalar sigma = this->computeXformedSigma(matrix); + return SkBlurMask::BoxBlur(dst, src, sigma, fBlurStyle, this->getQuality(), margin); +} + +bool SkBlurMaskFilterImpl::filterRectMask(SkMask* dst, const SkRect& r, + const SkMatrix& matrix, + SkIPoint* margin, SkMask::CreateMode createMode) const { + SkScalar sigma = computeXformedSigma(matrix); + + return SkBlurMask::BlurRect(sigma, dst, r, fBlurStyle, margin, createMode); +} + +bool SkBlurMaskFilterImpl::filterRRectMask(SkMask* dst, const SkRRect& r, + const SkMatrix& matrix, + SkIPoint* margin, SkMask::CreateMode createMode) const { + SkScalar sigma = computeXformedSigma(matrix); + + return SkBlurMask::BlurRRect(sigma, dst, r, fBlurStyle, margin, createMode); +} + +#include "SkCanvas.h" + +static bool prepare_to_draw_into_mask(const SkRect& bounds, SkMask* mask) { + SkASSERT(mask != nullptr); + + mask->fBounds = bounds.roundOut(); + mask->fRowBytes = SkAlign4(mask->fBounds.width()); + mask->fFormat = SkMask::kA8_Format; + const size_t size = mask->computeImageSize(); + mask->fImage = SkMask::AllocImage(size, SkMask::kZeroInit_Alloc); + if (nullptr == mask->fImage) { + return false; + } + return true; +} + +static bool draw_rrect_into_mask(const SkRRect rrect, SkMask* mask) { + if (!prepare_to_draw_into_mask(rrect.rect(), mask)) { + return false; + } + + // FIXME: This code duplicates code in draw_rects_into_mask, below. Is there a + // clean way to share more code? + SkBitmap bitmap; + bitmap.installMaskPixels(*mask); + + SkCanvas canvas(bitmap); + canvas.translate(-SkIntToScalar(mask->fBounds.left()), + -SkIntToScalar(mask->fBounds.top())); + + SkPaint paint; + paint.setAntiAlias(true); + canvas.drawRRect(rrect, paint); + return true; +} + +static bool draw_rects_into_mask(const SkRect rects[], int count, SkMask* mask) { + if (!prepare_to_draw_into_mask(rects[0], mask)) { + return false; + } + + SkBitmap bitmap; + bitmap.installPixels(SkImageInfo::Make(mask->fBounds.width(), + mask->fBounds.height(), + kAlpha_8_SkColorType, + kPremul_SkAlphaType), + mask->fImage, mask->fRowBytes); + + SkCanvas canvas(bitmap); + canvas.translate(-SkIntToScalar(mask->fBounds.left()), + -SkIntToScalar(mask->fBounds.top())); + + SkPaint paint; + paint.setAntiAlias(true); + + if (1 == count) { + canvas.drawRect(rects[0], paint); + } else { + // todo: do I need a fast way to do this? + SkPath path; + path.addRect(rects[0]); + path.addRect(rects[1]); + path.setFillType(SkPath::kEvenOdd_FillType); + canvas.drawPath(path, paint); + } + return true; +} + +static bool rect_exceeds(const SkRect& r, SkScalar v) { + return r.fLeft < -v || r.fTop < -v || r.fRight > v || r.fBottom > v || + r.width() > v || r.height() > v; +} + +#include "SkMaskCache.h" + +static SkCachedData* copy_mask_to_cacheddata(SkMask* mask) { + const size_t size = mask->computeTotalImageSize(); + SkCachedData* data = SkResourceCache::NewCachedData(size); + if (data) { + memcpy(data->writable_data(), mask->fImage, size); + SkMask::FreeImage(mask->fImage); + mask->fImage = (uint8_t*)data->data(); + } + return data; +} + +static SkCachedData* find_cached_rrect(SkMask* mask, SkScalar sigma, SkBlurStyle style, + SkBlurQuality quality, const SkRRect& rrect) { + return SkMaskCache::FindAndRef(sigma, style, quality, rrect, mask); +} + +static SkCachedData* add_cached_rrect(SkMask* mask, SkScalar sigma, SkBlurStyle style, + SkBlurQuality quality, const SkRRect& rrect) { + SkCachedData* cache = copy_mask_to_cacheddata(mask); + if (cache) { + SkMaskCache::Add(sigma, style, quality, rrect, *mask, cache); + } + return cache; +} + +static SkCachedData* find_cached_rects(SkMask* mask, SkScalar sigma, SkBlurStyle style, + SkBlurQuality quality, const SkRect rects[], int count) { + return SkMaskCache::FindAndRef(sigma, style, quality, rects, count, mask); +} + +static SkCachedData* add_cached_rects(SkMask* mask, SkScalar sigma, SkBlurStyle style, + SkBlurQuality quality, const SkRect rects[], int count) { + SkCachedData* cache = copy_mask_to_cacheddata(mask); + if (cache) { + SkMaskCache::Add(sigma, style, quality, rects, count, *mask, cache); + } + return cache; +} + +#ifdef SK_IGNORE_FAST_RRECT_BLUR + // Use the faster analytic blur approach for ninepatch round rects + static const bool c_analyticBlurRRect{false}; +#else + static const bool c_analyticBlurRRect{true}; +#endif + +SkMaskFilterBase::FilterReturn +SkBlurMaskFilterImpl::filterRRectToNine(const SkRRect& rrect, const SkMatrix& matrix, + const SkIRect& clipBounds, + NinePatch* patch) const { + SkASSERT(patch != nullptr); + switch (rrect.getType()) { + case SkRRect::kEmpty_Type: + // Nothing to draw. + return kFalse_FilterReturn; + + case SkRRect::kRect_Type: + // We should have caught this earlier. + SkASSERT(false); + // Fall through. + case SkRRect::kOval_Type: + // The nine patch special case does not handle ovals, and we + // already have code for rectangles. + return kUnimplemented_FilterReturn; + + // These three can take advantage of this fast path. + case SkRRect::kSimple_Type: + case SkRRect::kNinePatch_Type: + case SkRRect::kComplex_Type: + break; + } + + // TODO: report correct metrics for innerstyle, where we do not grow the + // total bounds, but we do need an inset the size of our blur-radius + if (kInner_SkBlurStyle == fBlurStyle) { + return kUnimplemented_FilterReturn; + } + + // TODO: take clipBounds into account to limit our coordinates up front + // for now, just skip too-large src rects (to take the old code path). + if (rect_exceeds(rrect.rect(), SkIntToScalar(32767))) { + return kUnimplemented_FilterReturn; + } + + SkIPoint margin; + SkMask srcM, dstM; + srcM.fBounds = rrect.rect().roundOut(); + srcM.fFormat = SkMask::kA8_Format; + srcM.fRowBytes = 0; + + bool filterResult = false; + if (c_analyticBlurRRect) { + // special case for fast round rect blur + // don't actually do the blur the first time, just compute the correct size + filterResult = this->filterRRectMask(&dstM, rrect, matrix, &margin, + SkMask::kJustComputeBounds_CreateMode); + } + + if (!filterResult) { + filterResult = this->filterMask(&dstM, srcM, matrix, &margin); + } + + if (!filterResult) { + return kFalse_FilterReturn; + } + + // Now figure out the appropriate width and height of the smaller round rectangle + // to stretch. It will take into account the larger radius per side as well as double + // the margin, to account for inner and outer blur. + const SkVector& UL = rrect.radii(SkRRect::kUpperLeft_Corner); + const SkVector& UR = rrect.radii(SkRRect::kUpperRight_Corner); + const SkVector& LR = rrect.radii(SkRRect::kLowerRight_Corner); + const SkVector& LL = rrect.radii(SkRRect::kLowerLeft_Corner); + + const SkScalar leftUnstretched = SkTMax(UL.fX, LL.fX) + SkIntToScalar(2 * margin.fX); + const SkScalar rightUnstretched = SkTMax(UR.fX, LR.fX) + SkIntToScalar(2 * margin.fX); + + // Extra space in the middle to ensure an unchanging piece for stretching. Use 3 to cover + // any fractional space on either side plus 1 for the part to stretch. + const SkScalar stretchSize = SkIntToScalar(3); + + const SkScalar totalSmallWidth = leftUnstretched + rightUnstretched + stretchSize; + if (totalSmallWidth >= rrect.rect().width()) { + // There is no valid piece to stretch. + return kUnimplemented_FilterReturn; + } + + const SkScalar topUnstretched = SkTMax(UL.fY, UR.fY) + SkIntToScalar(2 * margin.fY); + const SkScalar bottomUnstretched = SkTMax(LL.fY, LR.fY) + SkIntToScalar(2 * margin.fY); + + const SkScalar totalSmallHeight = topUnstretched + bottomUnstretched + stretchSize; + if (totalSmallHeight >= rrect.rect().height()) { + // There is no valid piece to stretch. + return kUnimplemented_FilterReturn; + } + + SkRect smallR = SkRect::MakeWH(totalSmallWidth, totalSmallHeight); + + SkRRect smallRR; + SkVector radii[4]; + radii[SkRRect::kUpperLeft_Corner] = UL; + radii[SkRRect::kUpperRight_Corner] = UR; + radii[SkRRect::kLowerRight_Corner] = LR; + radii[SkRRect::kLowerLeft_Corner] = LL; + smallRR.setRectRadii(smallR, radii); + + const SkScalar sigma = this->computeXformedSigma(matrix); + SkCachedData* cache = find_cached_rrect(&patch->fMask, sigma, fBlurStyle, + this->getQuality(), smallRR); + if (!cache) { + bool analyticBlurWorked = false; + if (c_analyticBlurRRect) { + analyticBlurWorked = + this->filterRRectMask(&patch->fMask, smallRR, matrix, &margin, + SkMask::kComputeBoundsAndRenderImage_CreateMode); + } + + if (!analyticBlurWorked) { + if (!draw_rrect_into_mask(smallRR, &srcM)) { + return kFalse_FilterReturn; + } + + SkAutoMaskFreeImage amf(srcM.fImage); + + if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) { + return kFalse_FilterReturn; + } + } + cache = add_cached_rrect(&patch->fMask, sigma, fBlurStyle, this->getQuality(), smallRR); + } + + patch->fMask.fBounds.offsetTo(0, 0); + patch->fOuterRect = dstM.fBounds; + patch->fCenter.fX = SkScalarCeilToInt(leftUnstretched) + 1; + patch->fCenter.fY = SkScalarCeilToInt(topUnstretched) + 1; + SkASSERT(nullptr == patch->fCache); + patch->fCache = cache; // transfer ownership to patch + return kTrue_FilterReturn; +} + +// Use the faster analytic blur approach for ninepatch rects +static const bool c_analyticBlurNinepatch{true}; + +SkMaskFilterBase::FilterReturn +SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count, + const SkMatrix& matrix, + const SkIRect& clipBounds, + NinePatch* patch) const { + if (count < 1 || count > 2) { + return kUnimplemented_FilterReturn; + } + + // TODO: report correct metrics for innerstyle, where we do not grow the + // total bounds, but we do need an inset the size of our blur-radius + if (kInner_SkBlurStyle == fBlurStyle || kOuter_SkBlurStyle == fBlurStyle) { + return kUnimplemented_FilterReturn; + } + + // TODO: take clipBounds into account to limit our coordinates up front + // for now, just skip too-large src rects (to take the old code path). + if (rect_exceeds(rects[0], SkIntToScalar(32767))) { + return kUnimplemented_FilterReturn; + } + + SkIPoint margin; + SkMask srcM, dstM; + srcM.fBounds = rects[0].roundOut(); + srcM.fFormat = SkMask::kA8_Format; + srcM.fRowBytes = 0; + + bool filterResult = false; + if (count == 1 && c_analyticBlurNinepatch) { + // special case for fast rect blur + // don't actually do the blur the first time, just compute the correct size + filterResult = this->filterRectMask(&dstM, rects[0], matrix, &margin, + SkMask::kJustComputeBounds_CreateMode); + } else { + filterResult = this->filterMask(&dstM, srcM, matrix, &margin); + } + + if (!filterResult) { + return kFalse_FilterReturn; + } + + /* + * smallR is the smallest version of 'rect' that will still guarantee that + * we get the same blur results on all edges, plus 1 center row/col that is + * representative of the extendible/stretchable edges of the ninepatch. + * Since our actual edge may be fractional we inset 1 more to be sure we + * don't miss any interior blur. + * x is an added pixel of blur, and { and } are the (fractional) edge + * pixels from the original rect. + * + * x x { x x .... x x } x x + * + * Thus, in this case, we inset by a total of 5 (on each side) beginning + * with our outer-rect (dstM.fBounds) + */ + SkRect smallR[2]; + SkIPoint center; + + // +2 is from +1 for each edge (to account for possible fractional edges + int smallW = dstM.fBounds.width() - srcM.fBounds.width() + 2; + int smallH = dstM.fBounds.height() - srcM.fBounds.height() + 2; + SkIRect innerIR; + + if (1 == count) { + innerIR = srcM.fBounds; + center.set(smallW, smallH); + } else { + SkASSERT(2 == count); + rects[1].roundIn(&innerIR); + center.set(smallW + (innerIR.left() - srcM.fBounds.left()), + smallH + (innerIR.top() - srcM.fBounds.top())); + } + + // +1 so we get a clean, stretchable, center row/col + smallW += 1; + smallH += 1; + + // we want the inset amounts to be integral, so we don't change any + // fractional phase on the fRight or fBottom of our smallR. + const SkScalar dx = SkIntToScalar(innerIR.width() - smallW); + const SkScalar dy = SkIntToScalar(innerIR.height() - smallH); + if (dx < 0 || dy < 0) { + // we're too small, relative to our blur, to break into nine-patch, + // so we ask to have our normal filterMask() be called. + return kUnimplemented_FilterReturn; + } + + smallR[0].set(rects[0].left(), rects[0].top(), rects[0].right() - dx, rects[0].bottom() - dy); + if (smallR[0].width() < 2 || smallR[0].height() < 2) { + return kUnimplemented_FilterReturn; + } + if (2 == count) { + smallR[1].set(rects[1].left(), rects[1].top(), + rects[1].right() - dx, rects[1].bottom() - dy); + SkASSERT(!smallR[1].isEmpty()); + } + + const SkScalar sigma = this->computeXformedSigma(matrix); + SkCachedData* cache = find_cached_rects(&patch->fMask, sigma, fBlurStyle, + this->getQuality(), smallR, count); + if (!cache) { + if (count > 1 || !c_analyticBlurNinepatch) { + if (!draw_rects_into_mask(smallR, count, &srcM)) { + return kFalse_FilterReturn; + } + + SkAutoMaskFreeImage amf(srcM.fImage); + + if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) { + return kFalse_FilterReturn; + } + } else { + if (!this->filterRectMask(&patch->fMask, smallR[0], matrix, &margin, + SkMask::kComputeBoundsAndRenderImage_CreateMode)) { + return kFalse_FilterReturn; + } + } + cache = add_cached_rects(&patch->fMask, sigma, fBlurStyle, this->getQuality(), smallR, count); + } + patch->fMask.fBounds.offsetTo(0, 0); + patch->fOuterRect = dstM.fBounds; + patch->fCenter = center; + SkASSERT(nullptr == patch->fCache); + patch->fCache = cache; // transfer ownership to patch + return kTrue_FilterReturn; +} + +void SkBlurMaskFilterImpl::computeFastBounds(const SkRect& src, + SkRect* dst) const { + SkScalar pad = 3.0f * fSigma; + + dst->set(src.fLeft - pad, src.fTop - pad, + src.fRight + pad, src.fBottom + pad); +} + +sk_sp SkBlurMaskFilterImpl::CreateProc(SkReadBuffer& buffer) { + const SkScalar sigma = buffer.readScalar(); + SkBlurStyle style = buffer.read32LE(kLastEnum_SkBlurStyle); + + uint32_t flags = buffer.read32LE(0x3); // historically we only recorded 2 bits + bool respectCTM = !(flags & 1); // historically we stored ignoreCTM in low bit + + SkRect occluder; + buffer.readRect(&occluder); + + return SkMaskFilter::MakeBlur((SkBlurStyle)style, sigma, occluder, respectCTM); +} + +void SkBlurMaskFilterImpl::flatten(SkWriteBuffer& buffer) const { + buffer.writeScalar(fSigma); + buffer.writeUInt(fBlurStyle); + buffer.writeUInt(!fRespectCTM); // historically we recorded ignoreCTM + buffer.writeRect(fOccluder); +} + + +#if SK_SUPPORT_GPU + +bool SkBlurMaskFilterImpl::directFilterMaskGPU(GrContext* context, + GrRenderTargetContext* renderTargetContext, + GrPaint&& paint, + const GrClip& clip, + const SkMatrix& viewMatrix, + const SkStrokeRec& strokeRec, + const SkPath& path) const { + SkASSERT(renderTargetContext); + + if (fBlurStyle != kNormal_SkBlurStyle) { + return false; + } + + // TODO: we could handle blurred stroked circles + if (!strokeRec.isFillStyle()) { + return false; + } + + SkScalar xformedSigma = this->computeXformedSigma(viewMatrix); + + GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider(); + std::unique_ptr fp; + + SkRect rect; + if (path.isRect(&rect)) { + SkScalar pad = 3.0f * xformedSigma; + rect.outset(pad, pad); + + fp = GrRectBlurEffect::Make(proxyProvider, rect, xformedSigma); + } else if (path.isOval(&rect) && SkScalarNearlyEqual(rect.width(), rect.height())) { + fp = GrCircleBlurFragmentProcessor::Make(proxyProvider, rect, xformedSigma); + + // expand the rect for the coverage geometry + int pad = SkScalarCeilToInt(6*xformedSigma)/2; + rect.outset(SkIntToScalar(pad), SkIntToScalar(pad)); + } else { + return false; + } + + if (!fp) { + return false; + } + + SkMatrix inverse; + if (!viewMatrix.invert(&inverse)) { + return false; + } + + paint.addCoverageFragmentProcessor(std::move(fp)); + renderTargetContext->fillRectWithLocalMatrix(clip, std::move(paint), GrAA::kNo, SkMatrix::I(), + rect, inverse); + return true; +} + +bool SkBlurMaskFilterImpl::directFilterRRectMaskGPU(GrContext* context, + GrRenderTargetContext* renderTargetContext, + GrPaint&& paint, + const GrClip& clip, + const SkMatrix& viewMatrix, + const SkStrokeRec& strokeRec, + const SkRRect& srcRRect, + const SkRRect& devRRect) const { + SkASSERT(renderTargetContext); + + if (fBlurStyle != kNormal_SkBlurStyle) { + return false; + } + + if (!strokeRec.isFillStyle()) { + return false; + } + + GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider(); + SkScalar xformedSigma = this->computeXformedSigma(viewMatrix); + + if (devRRect.isRect() || SkRRectPriv::IsCircle(devRRect)) { + std::unique_ptr fp; + if (devRRect.isRect()) { + SkScalar pad = 3.0f * xformedSigma; + const SkRect dstCoverageRect = devRRect.rect().makeOutset(pad, pad); + + fp = GrRectBlurEffect::Make(proxyProvider, dstCoverageRect, xformedSigma); + } else { + fp = GrCircleBlurFragmentProcessor::Make(proxyProvider, + devRRect.rect(), xformedSigma); + } + + if (!fp) { + return false; + } + paint.addCoverageFragmentProcessor(std::move(fp)); + + SkRect srcProxyRect = srcRRect.rect(); + SkScalar outsetX = 3.0f*fSigma; + SkScalar outsetY = 3.0f*fSigma; + if (this->ignoreXform()) { + // When we're ignoring the CTM the padding added to the source rect also needs to ignore + // the CTM. The matrix passed in here is guaranteed to be just scale and translate so we + // can just grab the X and Y scales off the matrix and pre-undo the scale. + outsetX /= viewMatrix.getScaleX(); + outsetY /= viewMatrix.getScaleY(); + } + srcProxyRect.outset(outsetX, outsetY); + + renderTargetContext->drawRect(clip, std::move(paint), GrAA::kNo, viewMatrix, srcProxyRect); + return true; + } + + auto fp = GrRRectBlurEffect::Make(context, fSigma, xformedSigma, srcRRect, devRRect); + if (!fp) { + return false; + } + + if (!this->ignoreXform()) { + SkRect srcProxyRect = srcRRect.rect(); + srcProxyRect.outset(3.0f*fSigma, 3.0f*fSigma); + + sk_sp vertices = nullptr; + SkRect temp = fOccluder; + + if (!temp.isEmpty() && (srcProxyRect.contains(temp) || temp.intersect(srcProxyRect))) { + SkVertices::Builder builder(SkVertices::kTriangles_VertexMode, 8, 24, 0); + srcProxyRect.toQuad(builder.positions()); + temp.toQuad(builder.positions() + 4); + + static const uint16_t ringI[24] = { 0, 1, 5, 5, 4, 0, + 1, 2, 6, 6, 5, 1, + 2, 3, 7, 7, 6, 2, + 3, 0, 4, 4, 7, 3 }; + memcpy(builder.indices(), ringI, sizeof(ringI)); + vertices = builder.detach(); + } else { + // full rect case + SkVertices::Builder builder(SkVertices::kTriangles_VertexMode, 4, 6, 0); + srcProxyRect.toQuad(builder.positions()); + + static const uint16_t fullI[6] = { 0, 1, 2, 0, 2, 3 }; + memcpy(builder.indices(), fullI, sizeof(fullI)); + vertices = builder.detach(); + } + + paint.addCoverageFragmentProcessor(std::move(fp)); + renderTargetContext->drawVertices(clip, std::move(paint), viewMatrix, std::move(vertices)); + } else { + SkMatrix inverse; + if (!viewMatrix.invert(&inverse)) { + return false; + } + + float extra=3.f*SkScalarCeilToScalar(xformedSigma-1/6.0f); + SkRect proxyRect = devRRect.rect(); + proxyRect.outset(extra, extra); + + paint.addCoverageFragmentProcessor(std::move(fp)); + renderTargetContext->fillRectWithLocalMatrix(clip, std::move(paint), GrAA::kNo, + SkMatrix::I(), proxyRect, inverse); + } + + return true; +} + +bool SkBlurMaskFilterImpl::canFilterMaskGPU(const SkRRect& devRRect, + const SkIRect& clipBounds, + const SkMatrix& ctm, + SkRect* maskRect) const { + SkScalar xformedSigma = this->computeXformedSigma(ctm); + if (xformedSigma <= 0) { + return false; + } + + // We always do circles and simple circular rrects on the GPU + if (!SkRRectPriv::IsCircle(devRRect) && !SkRRectPriv::IsSimpleCircular(devRRect)) { + static const SkScalar kMIN_GPU_BLUR_SIZE = SkIntToScalar(64); + static const SkScalar kMIN_GPU_BLUR_SIGMA = SkIntToScalar(32); + + if (devRRect.width() <= kMIN_GPU_BLUR_SIZE && + devRRect.height() <= kMIN_GPU_BLUR_SIZE && + xformedSigma <= kMIN_GPU_BLUR_SIGMA) { + // We prefer to blur small rects with small radii on the CPU. + return false; + } + } + + if (nullptr == maskRect) { + // don't need to compute maskRect + return true; + } + + float sigma3 = 3 * SkScalarToFloat(xformedSigma); + + SkRect clipRect = SkRect::Make(clipBounds); + SkRect srcRect(devRRect.rect()); + + // Outset srcRect and clipRect by 3 * sigma, to compute affected blur area. + srcRect.outset(sigma3, sigma3); + clipRect.outset(sigma3, sigma3); + if (!srcRect.intersect(clipRect)) { + srcRect.setEmpty(); + } + *maskRect = srcRect; + return true; +} + +sk_sp SkBlurMaskFilterImpl::filterMaskGPU(GrContext* context, + sk_sp srcProxy, + const SkMatrix& ctm, + const SkIRect& maskRect) const { + // 'maskRect' isn't snapped to the UL corner but the mask in 'src' is. + const SkIRect clipRect = SkIRect::MakeWH(maskRect.width(), maskRect.height()); + + SkScalar xformedSigma = this->computeXformedSigma(ctm); + SkASSERT(xformedSigma > 0); + + // If we're doing a normal blur, we can clobber the pathTexture in the + // gaussianBlur. Otherwise, we need to save it for later compositing. + bool isNormalBlur = (kNormal_SkBlurStyle == fBlurStyle); + sk_sp renderTargetContext( + SkGpuBlurUtils::GaussianBlur(context, + srcProxy, + nullptr, + clipRect, + SkIRect::EmptyIRect(), + xformedSigma, + xformedSigma, + GrTextureDomain::kIgnore_Mode)); + if (!renderTargetContext) { + return nullptr; + } + + if (!isNormalBlur) { + GrPaint paint; + // Blend pathTexture over blurTexture. + paint.addCoverageFragmentProcessor(GrSimpleTextureEffect::Make(std::move(srcProxy), + SkMatrix::I())); + if (kInner_SkBlurStyle == fBlurStyle) { + // inner: dst = dst * src + paint.setCoverageSetOpXPFactory(SkRegion::kIntersect_Op); + } else if (kSolid_SkBlurStyle == fBlurStyle) { + // solid: dst = src + dst - src * dst + // = src + (1 - src) * dst + paint.setCoverageSetOpXPFactory(SkRegion::kUnion_Op); + } else if (kOuter_SkBlurStyle == fBlurStyle) { + // outer: dst = dst * (1 - src) + // = 0 * src + (1 - src) * dst + paint.setCoverageSetOpXPFactory(SkRegion::kDifference_Op); + } else { + paint.setCoverageSetOpXPFactory(SkRegion::kReplace_Op); + } + + renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), + SkRect::Make(clipRect)); + } + + return renderTargetContext->asTextureProxyRef(); +} + +#endif // SK_SUPPORT_GPU + + +#ifndef SK_IGNORE_TO_STRING +void SkBlurMaskFilterImpl::toString(SkString* str) const { + str->append("SkBlurMaskFilterImpl: ("); + + str->append("sigma: "); + str->appendScalar(fSigma); + str->append(" "); + + static const char* gStyleName[kLastEnum_SkBlurStyle + 1] = { + "normal", "solid", "outer", "inner" + }; + + str->appendf("style: %s ", gStyleName[fBlurStyle]); + str->appendf("respectCTM: %s ", fRespectCTM ? "true" : "false"); + str->append(")"); +} +#endif + +void sk_register_blur_maskfilter_createproc() { + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurMaskFilterImpl) +} + +sk_sp SkMaskFilter::MakeBlur(SkBlurStyle style, SkScalar sigma, + const SkRect& occluder, bool respectCTM) { + if (SkScalarIsFinite(sigma) && sigma > 0) { + return sk_sp(new SkBlurMaskFilterImpl(sigma, style, occluder, respectCTM)); + } + return nullptr; +} + +sk_sp SkMaskFilter::MakeBlur(SkBlurStyle style, SkScalar sigma, bool respectCTM) { + return MakeBlur(style, sigma, SkRect::MakeEmpty(), respectCTM); +} diff --git a/src/core/SkBlurPriv.h b/src/core/SkBlurPriv.h index d7a5c05b96..38daa192c5 100644 --- a/src/core/SkBlurPriv.h +++ b/src/core/SkBlurPriv.h @@ -33,5 +33,6 @@ bool SkComputeBlurredRRectParams(const SkRRect& srcRRect, const SkRRect& devRRec SkScalar texYs[kSkBlurRRectMaxDivisions], int* numXs, int* numYs, uint32_t* skipMask); +extern void sk_register_blur_maskfilter_createproc(); #endif diff --git a/src/core/SkMaskFilter.cpp b/src/core/SkMaskFilter.cpp index 347e828d0e..747e3abdff 100644 --- a/src/core/SkMaskFilter.cpp +++ b/src/core/SkMaskFilter.cpp @@ -9,6 +9,7 @@ #include "SkAutoMalloc.h" #include "SkBlitter.h" +#include "SkBlurPriv.h" #include "SkCachedData.h" #include "SkCoverageModePriv.h" #include "SkDraw.h" @@ -752,5 +753,6 @@ void SkMaskFilter::InitializeFlattenables() { SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLocalMatrixMF) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkComposeMF) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkCombineMF) + sk_register_blur_maskfilter_createproc(); } diff --git a/src/effects/SkBlurMaskFilter.cpp b/src/effects/SkBlurMaskFilter.cpp index c71cbc6491..64a880cc8f 100644 --- a/src/effects/SkBlurMaskFilter.cpp +++ b/src/effects/SkBlurMaskFilter.cpp @@ -6,1037 +6,9 @@ */ #include "SkBlurMaskFilter.h" -#include "SkBlurMask.h" -#include "SkBlurPriv.h" -#include "SkGpuBlurUtils.h" -#include "SkMaskFilterBase.h" -#include "SkReadBuffer.h" -#include "SkRRectPriv.h" -#include "SkWriteBuffer.h" -#include "SkMaskFilter.h" -#include "SkRRect.h" -#include "SkStringUtils.h" -#include "SkStrokeRec.h" -#include "SkVertices.h" - -#if SK_SUPPORT_GPU -#include "GrCircleBlurFragmentProcessor.h" -#include "GrClip.h" -#include "GrContext.h" -#include "GrFragmentProcessor.h" -#include "GrRenderTargetContext.h" -#include "GrResourceProvider.h" -#include "GrShaderCaps.h" -#include "GrStyle.h" -#include "GrTextureProxy.h" -#include "effects/GrRectBlurEffect.h" -#include "effects/GrRRectBlurEffect.h" -#include "effects/GrSimpleTextureEffect.h" -#include "effects/GrTextureDomain.h" -#include "glsl/GrGLSLFragmentProcessor.h" -#include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLProgramDataManager.h" -#include "glsl/GrGLSLUniformHandler.h" -#endif - -class SkBlurMaskFilterImpl : public SkMaskFilterBase { -public: - SkBlurMaskFilterImpl(SkScalar sigma, SkBlurStyle, const SkRect& occluder, uint32_t flags); - - // overrides from SkMaskFilter - SkMask::Format getFormat() const override; - bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&, - SkIPoint* margin) const override; - -#if SK_SUPPORT_GPU - bool canFilterMaskGPU(const SkRRect& devRRect, - const SkIRect& clipBounds, - const SkMatrix& ctm, - SkRect* maskRect) const override; - bool directFilterMaskGPU(GrContext*, - GrRenderTargetContext* renderTargetContext, - GrPaint&&, - const GrClip&, - const SkMatrix& viewMatrix, - const SkStrokeRec& strokeRec, - const SkPath& path) const override; - bool directFilterRRectMaskGPU(GrContext*, - GrRenderTargetContext* renderTargetContext, - GrPaint&&, - const GrClip&, - const SkMatrix& viewMatrix, - const SkStrokeRec& strokeRec, - const SkRRect& rrect, - const SkRRect& devRRect) const override; - sk_sp filterMaskGPU(GrContext*, - sk_sp srcProxy, - const SkMatrix& ctm, - const SkIRect& maskRect) const override; -#endif - - void computeFastBounds(const SkRect&, SkRect*) const override; - bool asABlur(BlurRec*) const override; - - SK_TO_STRING_OVERRIDE() - SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurMaskFilterImpl) - -protected: - FilterReturn filterRectsToNine(const SkRect[], int count, const SkMatrix&, - const SkIRect& clipBounds, - NinePatch*) const override; - - FilterReturn filterRRectToNine(const SkRRect&, const SkMatrix&, - const SkIRect& clipBounds, - NinePatch*) const override; - - bool filterRectMask(SkMask* dstM, const SkRect& r, const SkMatrix& matrix, - SkIPoint* margin, SkMask::CreateMode createMode) const; - bool filterRRectMask(SkMask* dstM, const SkRRect& r, const SkMatrix& matrix, - SkIPoint* margin, SkMask::CreateMode createMode) const; - - bool ignoreXform() const { - return SkToBool(fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag); - } - -private: - // To avoid unseemly allocation requests (esp. for finite platforms like - // handset) we limit the radius so something manageable. (as opposed to - // a request like 10,000) - static const SkScalar kMAX_BLUR_SIGMA; - - SkScalar fSigma; - SkBlurStyle fBlurStyle; - SkRect fOccluder; - uint32_t fBlurFlags; - - SkBlurQuality getQuality() const { - return (fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag) ? - kHigh_SkBlurQuality : kLow_SkBlurQuality; - } - - SkBlurMaskFilterImpl(SkReadBuffer&); - void flatten(SkWriteBuffer&) const override; - - SkScalar computeXformedSigma(const SkMatrix& ctm) const { - SkScalar xformedSigma = this->ignoreXform() ? fSigma : ctm.mapRadius(fSigma); - return SkMinScalar(xformedSigma, kMAX_BLUR_SIGMA); - } - - friend class SkBlurMaskFilter; - - typedef SkMaskFilter INHERITED; -}; - -const SkScalar SkBlurMaskFilterImpl::kMAX_BLUR_SIGMA = SkIntToScalar(128); sk_sp SkBlurMaskFilter::Make(SkBlurStyle style, SkScalar sigma, const SkRect& occluder, uint32_t flags) { - SkASSERT(!(flags & ~SkBlurMaskFilter::kAll_BlurFlag)); - SkASSERT(style <= kLastEnum_SkBlurStyle); - - if (!SkScalarIsFinite(sigma) || sigma <= 0) { - return nullptr; - } - - return sk_sp(new SkBlurMaskFilterImpl(sigma, style, occluder, flags)); + bool respectCTM = !(flags & kIgnoreTransform_BlurFlag); + return SkMaskFilter::MakeBlur(style, sigma, occluder, respectCTM); } - -// linearly interpolate between y1 & y3 to match x2's position between x1 & x3 -static SkScalar interp(SkScalar x1, SkScalar x2, SkScalar x3, SkScalar y1, SkScalar y3) { - SkASSERT(x1 <= x2 && x2 <= x3); - SkASSERT(y1 <= y3); - - SkScalar t = (x2 - x1) / (x3 - x1); - return y1 + t * (y3 - y1); -} - -// Insert 'lower' and 'higher' into 'array1' and insert a new value at each matching insertion -// point in 'array2' that linearly interpolates between the existing values. -// Return a bit mask which contains a copy of 'inputMask' for all the cells between the two -// insertion points. -static uint32_t insert_into_arrays(SkScalar* array1, SkScalar* array2, - SkScalar lower, SkScalar higher, - int* num, uint32_t inputMask, int maskSize) { - SkASSERT(lower < higher); - SkASSERT(lower >= array1[0] && higher <= array1[*num-1]); - - int32_t skipMask = 0x0; - int i; - for (i = 0; i < *num; ++i) { - if (lower >= array1[i] && lower < array1[i+1]) { - if (!SkScalarNearlyEqual(lower, array1[i])) { - memmove(&array1[i+2], &array1[i+1], (*num-i-1)*sizeof(SkScalar)); - array1[i+1] = lower; - memmove(&array2[i+2], &array2[i+1], (*num-i-1)*sizeof(SkScalar)); - array2[i+1] = interp(array1[i], lower, array1[i+2], array2[i], array2[i+2]); - i++; - (*num)++; - } - break; - } - } - for ( ; i < *num; ++i) { - skipMask |= inputMask << (i*maskSize); - if (higher > array1[i] && higher <= array1[i+1]) { - if (!SkScalarNearlyEqual(higher, array1[i+1])) { - memmove(&array1[i+2], &array1[i+1], (*num-i-1)*sizeof(SkScalar)); - array1[i+1] = higher; - memmove(&array2[i+2], &array2[i+1], (*num-i-1)*sizeof(SkScalar)); - array2[i+1] = interp(array1[i], higher, array1[i+2], array2[i], array2[i+2]); - (*num)++; - } - break; - } - } - - return skipMask; -} - -bool SkComputeBlurredRRectParams(const SkRRect& srcRRect, const SkRRect& devRRect, - const SkRect& occluder, - SkScalar sigma, SkScalar xformedSigma, - SkRRect* rrectToDraw, - SkISize* widthHeight, - SkScalar rectXs[kSkBlurRRectMaxDivisions], - SkScalar rectYs[kSkBlurRRectMaxDivisions], - SkScalar texXs[kSkBlurRRectMaxDivisions], - SkScalar texYs[kSkBlurRRectMaxDivisions], - int* numXs, int* numYs, uint32_t* skipMask) { - unsigned int devBlurRadius = 3*SkScalarCeilToInt(xformedSigma-1/6.0f); - SkScalar srcBlurRadius = 3.0f * sigma; - - const SkRect& devOrig = devRRect.getBounds(); - const SkVector& devRadiiUL = devRRect.radii(SkRRect::kUpperLeft_Corner); - const SkVector& devRadiiUR = devRRect.radii(SkRRect::kUpperRight_Corner); - const SkVector& devRadiiLR = devRRect.radii(SkRRect::kLowerRight_Corner); - const SkVector& devRadiiLL = devRRect.radii(SkRRect::kLowerLeft_Corner); - - const int devLeft = SkScalarCeilToInt(SkTMax(devRadiiUL.fX, devRadiiLL.fX)); - const int devTop = SkScalarCeilToInt(SkTMax(devRadiiUL.fY, devRadiiUR.fY)); - const int devRight = SkScalarCeilToInt(SkTMax(devRadiiUR.fX, devRadiiLR.fX)); - const int devBot = SkScalarCeilToInt(SkTMax(devRadiiLL.fY, devRadiiLR.fY)); - - // This is a conservative check for nine-patchability - if (devOrig.fLeft + devLeft + devBlurRadius >= devOrig.fRight - devRight - devBlurRadius || - devOrig.fTop + devTop + devBlurRadius >= devOrig.fBottom - devBot - devBlurRadius) { - return false; - } - - const SkVector& srcRadiiUL = srcRRect.radii(SkRRect::kUpperLeft_Corner); - const SkVector& srcRadiiUR = srcRRect.radii(SkRRect::kUpperRight_Corner); - const SkVector& srcRadiiLR = srcRRect.radii(SkRRect::kLowerRight_Corner); - const SkVector& srcRadiiLL = srcRRect.radii(SkRRect::kLowerLeft_Corner); - - const SkScalar srcLeft = SkTMax(srcRadiiUL.fX, srcRadiiLL.fX); - const SkScalar srcTop = SkTMax(srcRadiiUL.fY, srcRadiiUR.fY); - const SkScalar srcRight = SkTMax(srcRadiiUR.fX, srcRadiiLR.fX); - const SkScalar srcBot = SkTMax(srcRadiiLL.fY, srcRadiiLR.fY); - - int newRRWidth = 2*devBlurRadius + devLeft + devRight + 1; - int newRRHeight = 2*devBlurRadius + devTop + devBot + 1; - widthHeight->fWidth = newRRWidth + 2 * devBlurRadius; - widthHeight->fHeight = newRRHeight + 2 * devBlurRadius; - - const SkRect srcProxyRect = srcRRect.getBounds().makeOutset(srcBlurRadius, srcBlurRadius); - - rectXs[0] = srcProxyRect.fLeft; - rectXs[1] = srcProxyRect.fLeft + 2*srcBlurRadius + srcLeft; - rectXs[2] = srcProxyRect.fRight - 2*srcBlurRadius - srcRight; - rectXs[3] = srcProxyRect.fRight; - - rectYs[0] = srcProxyRect.fTop; - rectYs[1] = srcProxyRect.fTop + 2*srcBlurRadius + srcTop; - rectYs[2] = srcProxyRect.fBottom - 2*srcBlurRadius - srcBot; - rectYs[3] = srcProxyRect.fBottom; - - texXs[0] = 0.0f; - texXs[1] = 2.0f*devBlurRadius + devLeft; - texXs[2] = 2.0f*devBlurRadius + devLeft + 1; - texXs[3] = SkIntToScalar(widthHeight->fWidth); - - texYs[0] = 0.0f; - texYs[1] = 2.0f*devBlurRadius + devTop; - texYs[2] = 2.0f*devBlurRadius + devTop + 1; - texYs[3] = SkIntToScalar(widthHeight->fHeight); - - SkRect temp = occluder; - - *numXs = 4; - *numYs = 4; - *skipMask = 0; - if (!temp.isEmpty() && (srcProxyRect.contains(temp) || temp.intersect(srcProxyRect))) { - *skipMask = insert_into_arrays(rectXs, texXs, temp.fLeft, temp.fRight, numXs, 0x1, 1); - *skipMask = insert_into_arrays(rectYs, texYs, temp.fTop, temp.fBottom, - numYs, *skipMask, *numXs-1); - } - - const SkRect newRect = SkRect::MakeXYWH(SkIntToScalar(devBlurRadius), - SkIntToScalar(devBlurRadius), - SkIntToScalar(newRRWidth), - SkIntToScalar(newRRHeight)); - SkVector newRadii[4]; - newRadii[0] = { SkScalarCeilToScalar(devRadiiUL.fX), SkScalarCeilToScalar(devRadiiUL.fY) }; - newRadii[1] = { SkScalarCeilToScalar(devRadiiUR.fX), SkScalarCeilToScalar(devRadiiUR.fY) }; - newRadii[2] = { SkScalarCeilToScalar(devRadiiLR.fX), SkScalarCeilToScalar(devRadiiLR.fY) }; - newRadii[3] = { SkScalarCeilToScalar(devRadiiLL.fX), SkScalarCeilToScalar(devRadiiLL.fY) }; - - rrectToDraw->setRectRadii(newRect, newRadii); - return true; -} - -/////////////////////////////////////////////////////////////////////////////// - -SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkScalar sigma, SkBlurStyle style, - const SkRect& occluder, uint32_t flags) - : fSigma(sigma) - , fBlurStyle(style) - , fOccluder(occluder) - , fBlurFlags(flags) { - SkASSERT(fSigma > 0); - SkASSERT((unsigned)style <= kLastEnum_SkBlurStyle); - SkASSERT(flags <= SkBlurMaskFilter::kAll_BlurFlag); -} - -SkMask::Format SkBlurMaskFilterImpl::getFormat() const { - return SkMask::kA8_Format; -} - -bool SkBlurMaskFilterImpl::asABlur(BlurRec* rec) const { - if (this->ignoreXform()) { - return false; - } - - if (rec) { - rec->fSigma = fSigma; - rec->fStyle = fBlurStyle; - rec->fQuality = this->getQuality(); - } - return true; -} - -bool SkBlurMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src, - const SkMatrix& matrix, - SkIPoint* margin) const { - SkScalar sigma = this->computeXformedSigma(matrix); - return SkBlurMask::BoxBlur(dst, src, sigma, fBlurStyle, this->getQuality(), margin); -} - -bool SkBlurMaskFilterImpl::filterRectMask(SkMask* dst, const SkRect& r, - const SkMatrix& matrix, - SkIPoint* margin, SkMask::CreateMode createMode) const { - SkScalar sigma = computeXformedSigma(matrix); - - return SkBlurMask::BlurRect(sigma, dst, r, fBlurStyle, margin, createMode); -} - -bool SkBlurMaskFilterImpl::filterRRectMask(SkMask* dst, const SkRRect& r, - const SkMatrix& matrix, - SkIPoint* margin, SkMask::CreateMode createMode) const { - SkScalar sigma = computeXformedSigma(matrix); - - return SkBlurMask::BlurRRect(sigma, dst, r, fBlurStyle, margin, createMode); -} - -#include "SkCanvas.h" - -static bool prepare_to_draw_into_mask(const SkRect& bounds, SkMask* mask) { - SkASSERT(mask != nullptr); - - mask->fBounds = bounds.roundOut(); - mask->fRowBytes = SkAlign4(mask->fBounds.width()); - mask->fFormat = SkMask::kA8_Format; - const size_t size = mask->computeImageSize(); - mask->fImage = SkMask::AllocImage(size, SkMask::kZeroInit_Alloc); - if (nullptr == mask->fImage) { - return false; - } - return true; -} - -static bool draw_rrect_into_mask(const SkRRect rrect, SkMask* mask) { - if (!prepare_to_draw_into_mask(rrect.rect(), mask)) { - return false; - } - - // FIXME: This code duplicates code in draw_rects_into_mask, below. Is there a - // clean way to share more code? - SkBitmap bitmap; - bitmap.installMaskPixels(*mask); - - SkCanvas canvas(bitmap); - canvas.translate(-SkIntToScalar(mask->fBounds.left()), - -SkIntToScalar(mask->fBounds.top())); - - SkPaint paint; - paint.setAntiAlias(true); - canvas.drawRRect(rrect, paint); - return true; -} - -static bool draw_rects_into_mask(const SkRect rects[], int count, SkMask* mask) { - if (!prepare_to_draw_into_mask(rects[0], mask)) { - return false; - } - - SkBitmap bitmap; - bitmap.installPixels(SkImageInfo::Make(mask->fBounds.width(), - mask->fBounds.height(), - kAlpha_8_SkColorType, - kPremul_SkAlphaType), - mask->fImage, mask->fRowBytes); - - SkCanvas canvas(bitmap); - canvas.translate(-SkIntToScalar(mask->fBounds.left()), - -SkIntToScalar(mask->fBounds.top())); - - SkPaint paint; - paint.setAntiAlias(true); - - if (1 == count) { - canvas.drawRect(rects[0], paint); - } else { - // todo: do I need a fast way to do this? - SkPath path; - path.addRect(rects[0]); - path.addRect(rects[1]); - path.setFillType(SkPath::kEvenOdd_FillType); - canvas.drawPath(path, paint); - } - return true; -} - -static bool rect_exceeds(const SkRect& r, SkScalar v) { - return r.fLeft < -v || r.fTop < -v || r.fRight > v || r.fBottom > v || - r.width() > v || r.height() > v; -} - -#include "SkMaskCache.h" - -static SkCachedData* copy_mask_to_cacheddata(SkMask* mask) { - const size_t size = mask->computeTotalImageSize(); - SkCachedData* data = SkResourceCache::NewCachedData(size); - if (data) { - memcpy(data->writable_data(), mask->fImage, size); - SkMask::FreeImage(mask->fImage); - mask->fImage = (uint8_t*)data->data(); - } - return data; -} - -static SkCachedData* find_cached_rrect(SkMask* mask, SkScalar sigma, SkBlurStyle style, - SkBlurQuality quality, const SkRRect& rrect) { - return SkMaskCache::FindAndRef(sigma, style, quality, rrect, mask); -} - -static SkCachedData* add_cached_rrect(SkMask* mask, SkScalar sigma, SkBlurStyle style, - SkBlurQuality quality, const SkRRect& rrect) { - SkCachedData* cache = copy_mask_to_cacheddata(mask); - if (cache) { - SkMaskCache::Add(sigma, style, quality, rrect, *mask, cache); - } - return cache; -} - -static SkCachedData* find_cached_rects(SkMask* mask, SkScalar sigma, SkBlurStyle style, - SkBlurQuality quality, const SkRect rects[], int count) { - return SkMaskCache::FindAndRef(sigma, style, quality, rects, count, mask); -} - -static SkCachedData* add_cached_rects(SkMask* mask, SkScalar sigma, SkBlurStyle style, - SkBlurQuality quality, const SkRect rects[], int count) { - SkCachedData* cache = copy_mask_to_cacheddata(mask); - if (cache) { - SkMaskCache::Add(sigma, style, quality, rects, count, *mask, cache); - } - return cache; -} - -#ifdef SK_IGNORE_FAST_RRECT_BLUR - // Use the faster analytic blur approach for ninepatch round rects - static const bool c_analyticBlurRRect{false}; -#else - static const bool c_analyticBlurRRect{true}; -#endif - -SkMaskFilterBase::FilterReturn -SkBlurMaskFilterImpl::filterRRectToNine(const SkRRect& rrect, const SkMatrix& matrix, - const SkIRect& clipBounds, - NinePatch* patch) const { - SkASSERT(patch != nullptr); - switch (rrect.getType()) { - case SkRRect::kEmpty_Type: - // Nothing to draw. - return kFalse_FilterReturn; - - case SkRRect::kRect_Type: - // We should have caught this earlier. - SkASSERT(false); - // Fall through. - case SkRRect::kOval_Type: - // The nine patch special case does not handle ovals, and we - // already have code for rectangles. - return kUnimplemented_FilterReturn; - - // These three can take advantage of this fast path. - case SkRRect::kSimple_Type: - case SkRRect::kNinePatch_Type: - case SkRRect::kComplex_Type: - break; - } - - // TODO: report correct metrics for innerstyle, where we do not grow the - // total bounds, but we do need an inset the size of our blur-radius - if (kInner_SkBlurStyle == fBlurStyle) { - return kUnimplemented_FilterReturn; - } - - // TODO: take clipBounds into account to limit our coordinates up front - // for now, just skip too-large src rects (to take the old code path). - if (rect_exceeds(rrect.rect(), SkIntToScalar(32767))) { - return kUnimplemented_FilterReturn; - } - - SkIPoint margin; - SkMask srcM, dstM; - srcM.fBounds = rrect.rect().roundOut(); - srcM.fFormat = SkMask::kA8_Format; - srcM.fRowBytes = 0; - - bool filterResult = false; - if (c_analyticBlurRRect) { - // special case for fast round rect blur - // don't actually do the blur the first time, just compute the correct size - filterResult = this->filterRRectMask(&dstM, rrect, matrix, &margin, - SkMask::kJustComputeBounds_CreateMode); - } - - if (!filterResult) { - filterResult = this->filterMask(&dstM, srcM, matrix, &margin); - } - - if (!filterResult) { - return kFalse_FilterReturn; - } - - // Now figure out the appropriate width and height of the smaller round rectangle - // to stretch. It will take into account the larger radius per side as well as double - // the margin, to account for inner and outer blur. - const SkVector& UL = rrect.radii(SkRRect::kUpperLeft_Corner); - const SkVector& UR = rrect.radii(SkRRect::kUpperRight_Corner); - const SkVector& LR = rrect.radii(SkRRect::kLowerRight_Corner); - const SkVector& LL = rrect.radii(SkRRect::kLowerLeft_Corner); - - const SkScalar leftUnstretched = SkTMax(UL.fX, LL.fX) + SkIntToScalar(2 * margin.fX); - const SkScalar rightUnstretched = SkTMax(UR.fX, LR.fX) + SkIntToScalar(2 * margin.fX); - - // Extra space in the middle to ensure an unchanging piece for stretching. Use 3 to cover - // any fractional space on either side plus 1 for the part to stretch. - const SkScalar stretchSize = SkIntToScalar(3); - - const SkScalar totalSmallWidth = leftUnstretched + rightUnstretched + stretchSize; - if (totalSmallWidth >= rrect.rect().width()) { - // There is no valid piece to stretch. - return kUnimplemented_FilterReturn; - } - - const SkScalar topUnstretched = SkTMax(UL.fY, UR.fY) + SkIntToScalar(2 * margin.fY); - const SkScalar bottomUnstretched = SkTMax(LL.fY, LR.fY) + SkIntToScalar(2 * margin.fY); - - const SkScalar totalSmallHeight = topUnstretched + bottomUnstretched + stretchSize; - if (totalSmallHeight >= rrect.rect().height()) { - // There is no valid piece to stretch. - return kUnimplemented_FilterReturn; - } - - SkRect smallR = SkRect::MakeWH(totalSmallWidth, totalSmallHeight); - - SkRRect smallRR; - SkVector radii[4]; - radii[SkRRect::kUpperLeft_Corner] = UL; - radii[SkRRect::kUpperRight_Corner] = UR; - radii[SkRRect::kLowerRight_Corner] = LR; - radii[SkRRect::kLowerLeft_Corner] = LL; - smallRR.setRectRadii(smallR, radii); - - const SkScalar sigma = this->computeXformedSigma(matrix); - SkCachedData* cache = find_cached_rrect(&patch->fMask, sigma, fBlurStyle, - this->getQuality(), smallRR); - if (!cache) { - bool analyticBlurWorked = false; - if (c_analyticBlurRRect) { - analyticBlurWorked = - this->filterRRectMask(&patch->fMask, smallRR, matrix, &margin, - SkMask::kComputeBoundsAndRenderImage_CreateMode); - } - - if (!analyticBlurWorked) { - if (!draw_rrect_into_mask(smallRR, &srcM)) { - return kFalse_FilterReturn; - } - - SkAutoMaskFreeImage amf(srcM.fImage); - - if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) { - return kFalse_FilterReturn; - } - } - cache = add_cached_rrect(&patch->fMask, sigma, fBlurStyle, this->getQuality(), smallRR); - } - - patch->fMask.fBounds.offsetTo(0, 0); - patch->fOuterRect = dstM.fBounds; - patch->fCenter.fX = SkScalarCeilToInt(leftUnstretched) + 1; - patch->fCenter.fY = SkScalarCeilToInt(topUnstretched) + 1; - SkASSERT(nullptr == patch->fCache); - patch->fCache = cache; // transfer ownership to patch - return kTrue_FilterReturn; -} - -// Use the faster analytic blur approach for ninepatch rects -static const bool c_analyticBlurNinepatch{true}; - -SkMaskFilterBase::FilterReturn -SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count, - const SkMatrix& matrix, - const SkIRect& clipBounds, - NinePatch* patch) const { - if (count < 1 || count > 2) { - return kUnimplemented_FilterReturn; - } - - // TODO: report correct metrics for innerstyle, where we do not grow the - // total bounds, but we do need an inset the size of our blur-radius - if (kInner_SkBlurStyle == fBlurStyle || kOuter_SkBlurStyle == fBlurStyle) { - return kUnimplemented_FilterReturn; - } - - // TODO: take clipBounds into account to limit our coordinates up front - // for now, just skip too-large src rects (to take the old code path). - if (rect_exceeds(rects[0], SkIntToScalar(32767))) { - return kUnimplemented_FilterReturn; - } - - SkIPoint margin; - SkMask srcM, dstM; - srcM.fBounds = rects[0].roundOut(); - srcM.fFormat = SkMask::kA8_Format; - srcM.fRowBytes = 0; - - bool filterResult = false; - if (count == 1 && c_analyticBlurNinepatch) { - // special case for fast rect blur - // don't actually do the blur the first time, just compute the correct size - filterResult = this->filterRectMask(&dstM, rects[0], matrix, &margin, - SkMask::kJustComputeBounds_CreateMode); - } else { - filterResult = this->filterMask(&dstM, srcM, matrix, &margin); - } - - if (!filterResult) { - return kFalse_FilterReturn; - } - - /* - * smallR is the smallest version of 'rect' that will still guarantee that - * we get the same blur results on all edges, plus 1 center row/col that is - * representative of the extendible/stretchable edges of the ninepatch. - * Since our actual edge may be fractional we inset 1 more to be sure we - * don't miss any interior blur. - * x is an added pixel of blur, and { and } are the (fractional) edge - * pixels from the original rect. - * - * x x { x x .... x x } x x - * - * Thus, in this case, we inset by a total of 5 (on each side) beginning - * with our outer-rect (dstM.fBounds) - */ - SkRect smallR[2]; - SkIPoint center; - - // +2 is from +1 for each edge (to account for possible fractional edges - int smallW = dstM.fBounds.width() - srcM.fBounds.width() + 2; - int smallH = dstM.fBounds.height() - srcM.fBounds.height() + 2; - SkIRect innerIR; - - if (1 == count) { - innerIR = srcM.fBounds; - center.set(smallW, smallH); - } else { - SkASSERT(2 == count); - rects[1].roundIn(&innerIR); - center.set(smallW + (innerIR.left() - srcM.fBounds.left()), - smallH + (innerIR.top() - srcM.fBounds.top())); - } - - // +1 so we get a clean, stretchable, center row/col - smallW += 1; - smallH += 1; - - // we want the inset amounts to be integral, so we don't change any - // fractional phase on the fRight or fBottom of our smallR. - const SkScalar dx = SkIntToScalar(innerIR.width() - smallW); - const SkScalar dy = SkIntToScalar(innerIR.height() - smallH); - if (dx < 0 || dy < 0) { - // we're too small, relative to our blur, to break into nine-patch, - // so we ask to have our normal filterMask() be called. - return kUnimplemented_FilterReturn; - } - - smallR[0].set(rects[0].left(), rects[0].top(), rects[0].right() - dx, rects[0].bottom() - dy); - if (smallR[0].width() < 2 || smallR[0].height() < 2) { - return kUnimplemented_FilterReturn; - } - if (2 == count) { - smallR[1].set(rects[1].left(), rects[1].top(), - rects[1].right() - dx, rects[1].bottom() - dy); - SkASSERT(!smallR[1].isEmpty()); - } - - const SkScalar sigma = this->computeXformedSigma(matrix); - SkCachedData* cache = find_cached_rects(&patch->fMask, sigma, fBlurStyle, - this->getQuality(), smallR, count); - if (!cache) { - if (count > 1 || !c_analyticBlurNinepatch) { - if (!draw_rects_into_mask(smallR, count, &srcM)) { - return kFalse_FilterReturn; - } - - SkAutoMaskFreeImage amf(srcM.fImage); - - if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) { - return kFalse_FilterReturn; - } - } else { - if (!this->filterRectMask(&patch->fMask, smallR[0], matrix, &margin, - SkMask::kComputeBoundsAndRenderImage_CreateMode)) { - return kFalse_FilterReturn; - } - } - cache = add_cached_rects(&patch->fMask, sigma, fBlurStyle, this->getQuality(), smallR, count); - } - patch->fMask.fBounds.offsetTo(0, 0); - patch->fOuterRect = dstM.fBounds; - patch->fCenter = center; - SkASSERT(nullptr == patch->fCache); - patch->fCache = cache; // transfer ownership to patch - return kTrue_FilterReturn; -} - -void SkBlurMaskFilterImpl::computeFastBounds(const SkRect& src, - SkRect* dst) const { - SkScalar pad = 3.0f * fSigma; - - dst->set(src.fLeft - pad, src.fTop - pad, - src.fRight + pad, src.fBottom + pad); -} - -sk_sp SkBlurMaskFilterImpl::CreateProc(SkReadBuffer& buffer) { - const SkScalar sigma = buffer.readScalar(); - SkBlurStyle style = buffer.read32LE(kLastEnum_SkBlurStyle); - unsigned flags = buffer.read32LE(SkBlurMaskFilter::kAll_BlurFlag); - - SkRect occluder; - buffer.readRect(&occluder); - - return SkBlurMaskFilter::Make((SkBlurStyle)style, sigma, occluder, flags); -} - -void SkBlurMaskFilterImpl::flatten(SkWriteBuffer& buffer) const { - buffer.writeScalar(fSigma); - buffer.writeUInt(fBlurStyle); - buffer.writeUInt(fBlurFlags); - buffer.writeRect(fOccluder); -} - - -#if SK_SUPPORT_GPU - -bool SkBlurMaskFilterImpl::directFilterMaskGPU(GrContext* context, - GrRenderTargetContext* renderTargetContext, - GrPaint&& paint, - const GrClip& clip, - const SkMatrix& viewMatrix, - const SkStrokeRec& strokeRec, - const SkPath& path) const { - SkASSERT(renderTargetContext); - - if (fBlurStyle != kNormal_SkBlurStyle) { - return false; - } - - // TODO: we could handle blurred stroked circles - if (!strokeRec.isFillStyle()) { - return false; - } - - SkScalar xformedSigma = this->computeXformedSigma(viewMatrix); - - GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider(); - std::unique_ptr fp; - - SkRect rect; - if (path.isRect(&rect)) { - SkScalar pad = 3.0f * xformedSigma; - rect.outset(pad, pad); - - fp = GrRectBlurEffect::Make(proxyProvider, rect, xformedSigma); - } else if (path.isOval(&rect) && SkScalarNearlyEqual(rect.width(), rect.height())) { - fp = GrCircleBlurFragmentProcessor::Make(proxyProvider, rect, xformedSigma); - - // expand the rect for the coverage geometry - int pad = SkScalarCeilToInt(6*xformedSigma)/2; - rect.outset(SkIntToScalar(pad), SkIntToScalar(pad)); - } else { - return false; - } - - if (!fp) { - return false; - } - - SkMatrix inverse; - if (!viewMatrix.invert(&inverse)) { - return false; - } - - paint.addCoverageFragmentProcessor(std::move(fp)); - renderTargetContext->fillRectWithLocalMatrix(clip, std::move(paint), GrAA::kNo, SkMatrix::I(), - rect, inverse); - return true; -} - -bool SkBlurMaskFilterImpl::directFilterRRectMaskGPU(GrContext* context, - GrRenderTargetContext* renderTargetContext, - GrPaint&& paint, - const GrClip& clip, - const SkMatrix& viewMatrix, - const SkStrokeRec& strokeRec, - const SkRRect& srcRRect, - const SkRRect& devRRect) const { - SkASSERT(renderTargetContext); - - if (fBlurStyle != kNormal_SkBlurStyle) { - return false; - } - - if (!strokeRec.isFillStyle()) { - return false; - } - - GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider(); - SkScalar xformedSigma = this->computeXformedSigma(viewMatrix); - - if (devRRect.isRect() || SkRRectPriv::IsCircle(devRRect)) { - std::unique_ptr fp; - if (devRRect.isRect()) { - SkScalar pad = 3.0f * xformedSigma; - const SkRect dstCoverageRect = devRRect.rect().makeOutset(pad, pad); - - fp = GrRectBlurEffect::Make(proxyProvider, dstCoverageRect, xformedSigma); - } else { - fp = GrCircleBlurFragmentProcessor::Make(proxyProvider, - devRRect.rect(), xformedSigma); - } - - if (!fp) { - return false; - } - paint.addCoverageFragmentProcessor(std::move(fp)); - - SkRect srcProxyRect = srcRRect.rect(); - SkScalar outsetX = 3.0f*fSigma; - SkScalar outsetY = 3.0f*fSigma; - if (this->ignoreXform()) { - // When we're ignoring the CTM the padding added to the source rect also needs to ignore - // the CTM. The matrix passed in here is guaranteed to be just scale and translate so we - // can just grab the X and Y scales off the matrix and pre-undo the scale. - outsetX /= viewMatrix.getScaleX(); - outsetY /= viewMatrix.getScaleY(); - } - srcProxyRect.outset(outsetX, outsetY); - - renderTargetContext->drawRect(clip, std::move(paint), GrAA::kNo, viewMatrix, srcProxyRect); - return true; - } - - auto fp = GrRRectBlurEffect::Make(context, fSigma, xformedSigma, srcRRect, devRRect); - if (!fp) { - return false; - } - - if (!this->ignoreXform()) { - SkRect srcProxyRect = srcRRect.rect(); - srcProxyRect.outset(3.0f*fSigma, 3.0f*fSigma); - - sk_sp vertices = nullptr; - SkRect temp = fOccluder; - - if (!temp.isEmpty() && (srcProxyRect.contains(temp) || temp.intersect(srcProxyRect))) { - SkVertices::Builder builder(SkVertices::kTriangles_VertexMode, 8, 24, 0); - srcProxyRect.toQuad(builder.positions()); - temp.toQuad(builder.positions() + 4); - - static const uint16_t ringI[24] = { 0, 1, 5, 5, 4, 0, - 1, 2, 6, 6, 5, 1, - 2, 3, 7, 7, 6, 2, - 3, 0, 4, 4, 7, 3 }; - memcpy(builder.indices(), ringI, sizeof(ringI)); - vertices = builder.detach(); - } else { - // full rect case - SkVertices::Builder builder(SkVertices::kTriangles_VertexMode, 4, 6, 0); - srcProxyRect.toQuad(builder.positions()); - - static const uint16_t fullI[6] = { 0, 1, 2, 0, 2, 3 }; - memcpy(builder.indices(), fullI, sizeof(fullI)); - vertices = builder.detach(); - } - - paint.addCoverageFragmentProcessor(std::move(fp)); - renderTargetContext->drawVertices(clip, std::move(paint), viewMatrix, std::move(vertices)); - } else { - SkMatrix inverse; - if (!viewMatrix.invert(&inverse)) { - return false; - } - - float extra=3.f*SkScalarCeilToScalar(xformedSigma-1/6.0f); - SkRect proxyRect = devRRect.rect(); - proxyRect.outset(extra, extra); - - paint.addCoverageFragmentProcessor(std::move(fp)); - renderTargetContext->fillRectWithLocalMatrix(clip, std::move(paint), GrAA::kNo, - SkMatrix::I(), proxyRect, inverse); - } - - return true; -} - -bool SkBlurMaskFilterImpl::canFilterMaskGPU(const SkRRect& devRRect, - const SkIRect& clipBounds, - const SkMatrix& ctm, - SkRect* maskRect) const { - SkScalar xformedSigma = this->computeXformedSigma(ctm); - if (xformedSigma <= 0) { - return false; - } - - // We always do circles and simple circular rrects on the GPU - if (!SkRRectPriv::IsCircle(devRRect) && !SkRRectPriv::IsSimpleCircular(devRRect)) { - static const SkScalar kMIN_GPU_BLUR_SIZE = SkIntToScalar(64); - static const SkScalar kMIN_GPU_BLUR_SIGMA = SkIntToScalar(32); - - if (devRRect.width() <= kMIN_GPU_BLUR_SIZE && - devRRect.height() <= kMIN_GPU_BLUR_SIZE && - xformedSigma <= kMIN_GPU_BLUR_SIGMA) { - // We prefer to blur small rects with small radii on the CPU. - return false; - } - } - - if (nullptr == maskRect) { - // don't need to compute maskRect - return true; - } - - float sigma3 = 3 * SkScalarToFloat(xformedSigma); - - SkRect clipRect = SkRect::Make(clipBounds); - SkRect srcRect(devRRect.rect()); - - // Outset srcRect and clipRect by 3 * sigma, to compute affected blur area. - srcRect.outset(sigma3, sigma3); - clipRect.outset(sigma3, sigma3); - if (!srcRect.intersect(clipRect)) { - srcRect.setEmpty(); - } - *maskRect = srcRect; - return true; -} - -sk_sp SkBlurMaskFilterImpl::filterMaskGPU(GrContext* context, - sk_sp srcProxy, - const SkMatrix& ctm, - const SkIRect& maskRect) const { - // 'maskRect' isn't snapped to the UL corner but the mask in 'src' is. - const SkIRect clipRect = SkIRect::MakeWH(maskRect.width(), maskRect.height()); - - SkScalar xformedSigma = this->computeXformedSigma(ctm); - SkASSERT(xformedSigma > 0); - - // If we're doing a normal blur, we can clobber the pathTexture in the - // gaussianBlur. Otherwise, we need to save it for later compositing. - bool isNormalBlur = (kNormal_SkBlurStyle == fBlurStyle); - sk_sp renderTargetContext( - SkGpuBlurUtils::GaussianBlur(context, - srcProxy, - nullptr, - clipRect, - SkIRect::EmptyIRect(), - xformedSigma, - xformedSigma, - GrTextureDomain::kIgnore_Mode)); - if (!renderTargetContext) { - return nullptr; - } - - if (!isNormalBlur) { - GrPaint paint; - // Blend pathTexture over blurTexture. - paint.addCoverageFragmentProcessor(GrSimpleTextureEffect::Make(std::move(srcProxy), - SkMatrix::I())); - if (kInner_SkBlurStyle == fBlurStyle) { - // inner: dst = dst * src - paint.setCoverageSetOpXPFactory(SkRegion::kIntersect_Op); - } else if (kSolid_SkBlurStyle == fBlurStyle) { - // solid: dst = src + dst - src * dst - // = src + (1 - src) * dst - paint.setCoverageSetOpXPFactory(SkRegion::kUnion_Op); - } else if (kOuter_SkBlurStyle == fBlurStyle) { - // outer: dst = dst * (1 - src) - // = 0 * src + (1 - src) * dst - paint.setCoverageSetOpXPFactory(SkRegion::kDifference_Op); - } else { - paint.setCoverageSetOpXPFactory(SkRegion::kReplace_Op); - } - - renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), - SkRect::Make(clipRect)); - } - - return renderTargetContext->asTextureProxyRef(); -} - -#endif // SK_SUPPORT_GPU - - -#ifndef SK_IGNORE_TO_STRING -void SkBlurMaskFilterImpl::toString(SkString* str) const { - str->append("SkBlurMaskFilterImpl: ("); - - str->append("sigma: "); - str->appendScalar(fSigma); - str->append(" "); - - static const char* gStyleName[kLastEnum_SkBlurStyle + 1] = { - "normal", "solid", "outer", "inner" - }; - - str->appendf("style: %s ", gStyleName[fBlurStyle]); - str->append("flags: ("); - if (fBlurFlags) { - bool needSeparator = false; - SkAddFlagToString(str, this->ignoreXform(), "IgnoreXform", &needSeparator); - SkAddFlagToString(str, - SkToBool(fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag), - "HighQuality", &needSeparator); - } else { - str->append("None"); - } - str->append("))"); -} -#endif - -SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkBlurMaskFilter) - SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurMaskFilterImpl) -SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END diff --git a/src/effects/SkLayerDrawLooper.cpp b/src/effects/SkLayerDrawLooper.cpp index 17aa7a123d..3cd661c640 100644 --- a/src/effects/SkLayerDrawLooper.cpp +++ b/src/effects/SkLayerDrawLooper.cpp @@ -6,7 +6,7 @@ */ #include "SkArenaAlloc.h" #include "SkBlurDrawLooper.h" -#include "SkBlurMaskFilter.h" +#include "SkMaskFilter.h" #include "SkCanvas.h" #include "SkColorSpaceXformer.h" #include "SkColor.h" @@ -404,7 +404,7 @@ sk_sp SkBlurDrawLooper::Make(SkColor color, SkScalar sigma, SkScal { sk_sp blur = nullptr; if (sigma > 0.0f) { - blur = SkBlurMaskFilter::Make(kNormal_SkBlurStyle, sigma, SkBlurMaskFilter::kNone_BlurFlag); + blur = SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma, true); } SkLayerDrawLooper::Builder builder; diff --git a/src/effects/GrCircleBlurFragmentProcessor.cpp b/src/gpu/effects/GrCircleBlurFragmentProcessor.cpp similarity index 100% rename from src/effects/GrCircleBlurFragmentProcessor.cpp rename to src/gpu/effects/GrCircleBlurFragmentProcessor.cpp diff --git a/src/effects/GrCircleBlurFragmentProcessor.fp b/src/gpu/effects/GrCircleBlurFragmentProcessor.fp similarity index 100% rename from src/effects/GrCircleBlurFragmentProcessor.fp rename to src/gpu/effects/GrCircleBlurFragmentProcessor.fp diff --git a/src/effects/GrCircleBlurFragmentProcessor.h b/src/gpu/effects/GrCircleBlurFragmentProcessor.h similarity index 100% rename from src/effects/GrCircleBlurFragmentProcessor.h rename to src/gpu/effects/GrCircleBlurFragmentProcessor.h diff --git a/src/ports/SkGlobalInitialization_default.cpp b/src/ports/SkGlobalInitialization_default.cpp index ebfedb276d..793875ec1e 100644 --- a/src/ports/SkGlobalInitialization_default.cpp +++ b/src/ports/SkGlobalInitialization_default.cpp @@ -9,7 +9,6 @@ #include "Sk2DPathEffect.h" #include "SkAlphaThresholdFilter.h" #include "SkBlurImageFilter.h" -#include "SkBlurMaskFilter.h" #include "SkColorFilterImageFilter.h" #include "SkColorMatrixFilterRowMajor255.h" #include "SkComposeImageFilter.h" @@ -64,7 +63,6 @@ void SkFlattenable::PrivateInitializer::InitEffects() { // MaskFilter SkMaskFilter::InitializeFlattenables(); SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkEmbossMaskFilter) - SkBlurMaskFilter::InitializeFlattenables(); SkRRectsGaussianEdgeMaskFilter::InitializeFlattenables(); SkShaderMaskFilter::InitializeFlattenables(); diff --git a/src/utils/SkShadowUtils.cpp b/src/utils/SkShadowUtils.cpp index 928bdee103..348edb7790 100644 --- a/src/utils/SkShadowUtils.cpp +++ b/src/utils/SkShadowUtils.cpp @@ -7,12 +7,12 @@ #include "SkShadowUtils.h" #include "SkBlurMask.h" -#include "SkBlurMaskFilter.h" #include "SkCanvas.h" #include "SkColorFilter.h" #include "SkColorData.h" #include "SkDevice.h" #include "SkDrawShadowInfo.h" +#include "SkMaskFilter.h" #include "SkPath.h" #include "SkPM4f.h" #include "SkRandom.h" @@ -642,8 +642,8 @@ void SkBaseDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) { paint.setStrokeWidth(strokeWidth); paint.setStyle(SkPaint::kStrokeAndFill_Style); SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(blurRadius); - uint32_t flags = SkBlurMaskFilter::kIgnoreTransform_BlurFlag; - paint.setMaskFilter(SkBlurMaskFilter::Make(kNormal_SkBlurStyle, sigma, flags)); + bool respectCTM = false; + paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma, respectCTM)); this->drawPath(devSpacePath, paint); } } @@ -727,8 +727,8 @@ void SkBaseDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) { SkPaint paint; paint.setColor(rec.fSpotColor); SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius); - uint32_t flags = SkBlurMaskFilter::kIgnoreTransform_BlurFlag; - paint.setMaskFilter(SkBlurMaskFilter::Make(kNormal_SkBlurStyle, sigma, flags)); + bool respectCTM = false; + paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma, respectCTM)); this->drawPath(path, paint); } } diff --git a/tests/BlurTest.cpp b/tests/BlurTest.cpp index 31872a09fa..75c6d819cf 100644 --- a/tests/BlurTest.cpp +++ b/tests/BlurTest.cpp @@ -9,7 +9,6 @@ #include "SkBlendMode.h" #include "SkBlurDrawLooper.h" #include "SkBlurMask.h" -#include "SkBlurMaskFilter.h" #include "SkBlurPriv.h" #include "SkBlurTypes.h" #include "SkCanvas.h" @@ -133,9 +132,8 @@ DEF_TEST(BlurDrawing, reporter) { for (int style = 0; style <= kLastEnum_SkBlurStyle; ++style) { SkBlurStyle blurStyle = static_cast(style); - const uint32_t flagPermutations = SkBlurMaskFilter::kAll_BlurFlag; - for (uint32_t flags = 0; flags < flagPermutations; ++flags) { - paint.setMaskFilter(SkBlurMaskFilter::Make(blurStyle, sigma, flags)); + for (bool respectCTM : { false, true }) { + paint.setMaskFilter(SkMaskFilter::MakeBlur(blurStyle, sigma, respectCTM)); for (size_t test = 0; test < SK_ARRAY_COUNT(tests); ++test) { SkPath path; @@ -254,8 +252,7 @@ static void blur_path(SkCanvas* canvas, const SkPath& path, SkPaint blurPaint; blurPaint.setColor(SK_ColorWHITE); - blurPaint.setMaskFilter(SkBlurMaskFilter::Make(kNormal_SkBlurStyle, gaussianSigma, - SkBlurMaskFilter::kHighQuality_BlurFlag)); + blurPaint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, gaussianSigma)); canvas->drawColor(SK_ColorBLACK); canvas->drawPath(path, blurPaint); @@ -363,13 +360,7 @@ DEF_TEST(BlurSigmaRange, reporter) { /////////////////////////////////////////////////////////////////////////////////////////// -static SkBlurQuality blurMaskFilterFlags_as_quality(uint32_t blurMaskFilterFlags) { - return (blurMaskFilterFlags & SkBlurMaskFilter::kHighQuality_BlurFlag) ? - kHigh_SkBlurQuality : kLow_SkBlurQuality; -} - -static void test_blurDrawLooper(skiatest::Reporter* reporter, SkScalar sigma, - SkBlurStyle style, uint32_t blurMaskFilterFlags) { +static void test_blurDrawLooper(skiatest::Reporter* reporter, SkScalar sigma, SkBlurStyle style) { if (kNormal_SkBlurStyle != style) { return; // blurdrawlooper only supports normal } @@ -392,13 +383,13 @@ static void test_blurDrawLooper(skiatest::Reporter* reporter, SkScalar sigma, REPORTER_ASSERT(reporter, rec.fOffset.y() == dy); REPORTER_ASSERT(reporter, rec.fColor == color); REPORTER_ASSERT(reporter, rec.fStyle == style); - REPORTER_ASSERT(reporter, rec.fQuality == kLow_SkBlurQuality); + REPORTER_ASSERT(reporter, rec.fQuality == kHigh_SkBlurQuality); } } } static void test_looper(skiatest::Reporter* reporter, sk_sp lp, SkScalar sigma, - SkBlurStyle style, SkBlurQuality quality, bool expectSuccess) { + SkBlurStyle style, bool expectSuccess) { SkDrawLooper::BlurShadowRec rec; bool success = lp->asABlurShadow(&rec); REPORTER_ASSERT(reporter, success == expectSuccess); @@ -408,7 +399,7 @@ static void test_looper(skiatest::Reporter* reporter, sk_sp lp, Sk if (success) { REPORTER_ASSERT(reporter, rec.fSigma == sigma); REPORTER_ASSERT(reporter, rec.fStyle == style); - REPORTER_ASSERT(reporter, rec.fQuality == quality); + REPORTER_ASSERT(reporter, rec.fQuality == kHigh_SkBlurQuality); } } @@ -430,31 +421,30 @@ static void make_blur_layer(SkLayerDrawLooper::Builder* builder, sk_sp mf, - SkScalar sigma, SkBlurStyle style, SkBlurQuality quality, - bool expectSuccess) { + SkScalar sigma, SkBlurStyle style, bool expectSuccess) { SkLayerDrawLooper::LayerInfo info; SkLayerDrawLooper::Builder builder; // 1 layer is too few make_noop_layer(&builder); - test_looper(reporter, builder.detach(), sigma, style, quality, false); + test_looper(reporter, builder.detach(), sigma, style, false); // 2 layers is good, but need blur make_noop_layer(&builder); make_noop_layer(&builder); - test_looper(reporter, builder.detach(), sigma, style, quality, false); + test_looper(reporter, builder.detach(), sigma, style, false); // 2 layers is just right make_noop_layer(&builder); make_blur_layer(&builder, mf); - test_looper(reporter, builder.detach(), sigma, style, quality, expectSuccess); + test_looper(reporter, builder.detach(), sigma, style, expectSuccess); // 3 layers is too many make_noop_layer(&builder); make_blur_layer(&builder, mf); make_noop_layer(&builder); - test_looper(reporter, builder.detach(), sigma, style, quality, false); + test_looper(reporter, builder.detach(), sigma, style, false); } DEF_TEST(BlurAsABlur, reporter) { @@ -472,27 +462,25 @@ DEF_TEST(BlurAsABlur, reporter) { const SkBlurStyle style = styles[i]; for (size_t j = 0; j < SK_ARRAY_COUNT(sigmas); ++j) { const SkScalar sigma = sigmas[j]; - for (int flags = 0; flags <= SkBlurMaskFilter::kAll_BlurFlag; ++flags) { - const SkBlurQuality quality = blurMaskFilterFlags_as_quality(flags); - - sk_sp mf(SkBlurMaskFilter::Make(style, sigma, flags)); + for (bool respectCTM : { false, true }) { + sk_sp mf(SkMaskFilter::MakeBlur(style, sigma, respectCTM)); if (nullptr == mf.get()) { REPORTER_ASSERT(reporter, sigma <= 0); } else { REPORTER_ASSERT(reporter, sigma > 0); SkMaskFilterBase::BlurRec rec; bool success = as_MFB(mf)->asABlur(&rec); - if (flags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag) { - REPORTER_ASSERT(reporter, !success); - } else { + if (respectCTM) { REPORTER_ASSERT(reporter, success); REPORTER_ASSERT(reporter, rec.fSigma == sigma); REPORTER_ASSERT(reporter, rec.fStyle == style); - REPORTER_ASSERT(reporter, rec.fQuality == quality); + REPORTER_ASSERT(reporter, rec.fQuality == kHigh_SkBlurQuality); + } else { + REPORTER_ASSERT(reporter, !success); } - test_layerDrawLooper(reporter, std::move(mf), sigma, style, quality, success); + test_layerDrawLooper(reporter, std::move(mf), sigma, style, success); } - test_blurDrawLooper(reporter, sigma, style, flags); + test_blurDrawLooper(reporter, sigma, style); } } } @@ -529,7 +517,7 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SmallBoxBlurBug, reporter, ctxInfo) { SkRRect rr = SkRRect::MakeRectXY(r, 10, 10); SkPaint p; - p.setMaskFilter(SkBlurMaskFilter::Make(kNormal_SkBlurStyle, 0.01f)); + p.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 0.01f)); canvas->drawRRect(rr, p); } @@ -706,7 +694,7 @@ DEF_TEST(BlurZeroSigma, reporter) { // if sigma is zero (or nearly so), we need to draw correctly (unblurred) and not crash // or assert. for (auto sigma : sigmas) { - paint.setMaskFilter(SkBlurMaskFilter::Make(kNormal_SkBlurStyle, sigma)); + paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma)); surf->getCanvas()->drawRect(r, paint); sk_tool_utils::PixelIter iter(surf.get());