| Index: src/effects/SkBlurMaskFilter.cpp
|
| diff --git a/src/effects/SkBlurMaskFilter.cpp b/src/effects/SkBlurMaskFilter.cpp
|
| index 0e6de9e8a1638bfe2b3f881dd98b252b57cce68c..c044333efb289fbd7efe8871998cc139b8ac60fa 100644
|
| --- a/src/effects/SkBlurMaskFilter.cpp
|
| +++ b/src/effects/SkBlurMaskFilter.cpp
|
| @@ -139,59 +139,144 @@ sk_sp<SkMaskFilter> SkBlurMaskFilter::Make(SkBlurStyle style, SkScalar sigma,
|
| return sk_sp<SkMaskFilter>(new SkBlurMaskFilterImpl(sigma, style, occluder, flags));
|
| }
|
|
|
| -bool SkBlurMaskFilter::ComputeBlurredRRectParams(const SkRRect& rrect,
|
| - SkScalar sigma,
|
| +// 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 SkBlurMaskFilter::ComputeBlurredRRectParams(const SkRRect& srcRRect, const SkRRect& devRRect,
|
| + const SkRect& occluder,
|
| + SkScalar sigma, SkScalar xformedSigma,
|
| 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));
|
| + SkScalar rectXs[kMaxDivisions],
|
| + SkScalar rectYs[kMaxDivisions],
|
| + SkScalar texXs[kMaxDivisions],
|
| + SkScalar texYs[kMaxDivisions],
|
| + 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<SkScalar>(devRadiiUL.fX, devRadiiLL.fX));
|
| + const int devTop = SkScalarCeilToInt(SkTMax<SkScalar>(devRadiiUL.fY, devRadiiUR.fY));
|
| + const int devRight = SkScalarCeilToInt(SkTMax<SkScalar>(devRadiiUR.fX, devRadiiLR.fX));
|
| + const int devBot = SkScalarCeilToInt(SkTMax<SkScalar>(devRadiiLL.fY, devRadiiLR.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) {
|
| + if (devOrig.fLeft + devLeft + devBlurRadius >= devOrig.fRight - devRight - devBlurRadius ||
|
| + devOrig.fTop + devTop + devBlurRadius >= devOrig.fBottom - devBot - devBlurRadius) {
|
| return false;
|
| }
|
|
|
| - int newRRWidth, newRRHeight;
|
| + 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<SkScalar>(srcRadiiUL.fX, srcRadiiLL.fX);
|
| + const SkScalar srcTop = SkTMax<SkScalar>(srcRadiiUL.fY, srcRadiiUR.fY);
|
| + const SkScalar srcRight = SkTMax<SkScalar>(srcRadiiUR.fX, srcRadiiLR.fX);
|
| + const SkScalar srcBot = SkTMax<SkScalar>(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;
|
|
|
| - // 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;
|
| + *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(blurRadius), SkIntToScalar(blurRadius),
|
| - SkIntToScalar(newRRWidth), SkIntToScalar(newRRHeight));
|
| + const SkRect newRect = SkRect::MakeXYWH(SkIntToScalar(devBlurRadius),
|
| + SkIntToScalar(devBlurRadius),
|
| + 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) };
|
| + 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;
|
| @@ -983,7 +1068,9 @@ bool SkBlurMaskFilterImpl::directFilterMaskGPU(GrTextureProvider* texProvider,
|
| class GrRRectBlurEffect : public GrFragmentProcessor {
|
| public:
|
|
|
| - static sk_sp<GrFragmentProcessor> Make(GrContext*, float sigma, const SkRRect&);
|
| + static sk_sp<GrFragmentProcessor> Make(GrContext*,
|
| + float sigma, float xformedSigma,
|
| + const SkRRect& srcRRect, const SkRRect& devRRect);
|
|
|
| virtual ~GrRRectBlurEffect() {};
|
| const char* name() const override { return "GrRRectBlur"; }
|
| @@ -1069,8 +1156,8 @@ static sk_sp<GrTexture> find_or_create_rrect_blur_mask(GrContext* context,
|
| }
|
|
|
| sk_sp<GrFragmentProcessor> GrRRectBlurEffect::Make(GrContext* context,
|
| - float xformedSigma,
|
| - const SkRRect& devRRect) {
|
| + float sigma, float xformedSigma,
|
| + const SkRRect& srcRRect, const SkRRect& devRRect) {
|
| SkASSERT(!devRRect.isCircle()); // Should've been caught up-stream
|
|
|
| // TODO: loosen this up
|
| @@ -1083,13 +1170,18 @@ sk_sp<GrFragmentProcessor> GrRRectBlurEffect::Make(GrContext* context,
|
| // width (and height) of the rrect.
|
| SkRRect rrectToDraw;
|
| SkISize size;
|
| - SkScalar ignored[4];
|
| + SkScalar ignored[SkBlurMaskFilter::kMaxDivisions];
|
| int ignoredSize;
|
| + uint32_t ignored32;
|
|
|
| - bool ninePatchable = SkBlurMaskFilter::ComputeBlurredRRectParams(devRRect, xformedSigma,
|
| + bool ninePatchable = SkBlurMaskFilter::ComputeBlurredRRectParams(srcRRect, devRRect,
|
| + SkRect::MakeEmpty(),
|
| + sigma, xformedSigma,
|
| &rrectToDraw, &size,
|
| - ignored, &ignoredSize,
|
| - ignored, &ignoredSize);
|
| + ignored, ignored,
|
| + ignored, ignored,
|
| + &ignoredSize, &ignoredSize,
|
| + &ignored32);
|
| if (!ninePatchable) {
|
| return nullptr;
|
| }
|
| @@ -1136,7 +1228,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, sigma, rrect);
|
| + return GrRRectBlurEffect::Make(d->fContext, sigma, sigma, rrect, rrect);
|
| }
|
|
|
| //////////////////////////////////////////////////////////////////////////////
|
| @@ -1279,7 +1371,8 @@ bool SkBlurMaskFilterImpl::directFilterRRectMaskGPU(GrContext* context,
|
| return true;
|
| }
|
|
|
| - sk_sp<GrFragmentProcessor> fp(GrRRectBlurEffect::Make(context, xformedSigma, devRRect));
|
| + sk_sp<GrFragmentProcessor> fp(GrRRectBlurEffect::Make(context, fSigma, xformedSigma,
|
| + srcRRect, devRRect));
|
| if (!fp) {
|
| return false;
|
| }
|
|
|