Chromium Code Reviews| Index: src/effects/SkBlurMaskFilter.cpp |
| diff --git a/src/effects/SkBlurMaskFilter.cpp b/src/effects/SkBlurMaskFilter.cpp |
| index ff98daf048181bec780ebfd6df271fc53b807c0e..ab137d446d74f8201023e40ea1765f464d167442 100644 |
| --- a/src/effects/SkBlurMaskFilter.cpp |
| +++ b/src/effects/SkBlurMaskFilter.cpp |
| @@ -25,6 +25,7 @@ |
| #include "effects/GrSimpleTextureEffect.h" |
| #include "GrTBackendEffectFactory.h" |
| #include "SkGrPixelRef.h" |
| +#include "SkDraw.h" |
| #endif |
| class SkBlurMaskFilterImpl : public SkMaskFilter { |
| @@ -260,9 +261,9 @@ static bool rect_exceeds(const SkRect& r, SkScalar v) { |
| } |
| #ifdef SK_IGNORE_FAST_RRECT_BLUR |
| -SK_CONF_DECLARE( bool, c_analyticBlurRRect, "mask.filter.blur.analyticRRect", false, "Use the faster analytic blur approach for ninepatch rects" ); |
| +SK_CONF_DECLARE( bool, c_analyticBlurRRect, "mask.filter.blur.analytibrrect", false, "Use the faster analytic blur approach for ninepatch rects" ); |
|
bsalomon
2014/04/23 19:50:30
analytibrrect?
|
| #else |
| -SK_CONF_DECLARE( bool, c_analyticBlurRRect, "mask.filter.blur.analyticRRect", true, "Use the faster analytic blur approach for ninepatch round rects" ); |
| +SK_CONF_DECLARE( bool, c_analyticBlurRRect, "mask.filter.blur.analytibrrect", true, "Use the faster analytic blur approach for ninepatch round rects" ); |
| #endif |
| SkMaskFilter::FilterReturn |
| @@ -819,11 +820,287 @@ bool SkBlurMaskFilterImpl::directFilterMaskGPU(GrContext* context, |
| return true; |
| } |
| +class GrGLRRectBlurEffect; |
| + |
| +class GrRRectBlurEffect : public GrEffect { |
| +public: |
| + |
| + // The flags are used to indicate which corners are circluar (unflagged corners are assumed to |
|
bsalomon
2014/04/23 19:50:30
Delete comment?
humper
2014/04/23 20:11:49
Done.
|
| + // be square). |
| + static GrEffectRef* Create(GrContext* context, float sigma, const SkRRect&); |
| + |
| + virtual ~GrRRectBlurEffect() {}; |
| + static const char* Name() { return "GrRRectBlur"; } |
| + |
| + const SkRRect& getRRect() const { return fRRect; } |
| + float getSigma() const { return fSigma; } |
| + |
| + typedef GrGLRRectBlurEffect GLEffect; |
| + |
| + virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE; |
| + |
| + virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; |
| + |
| +private: |
| + GrRRectBlurEffect(float sigma, const SkRRect&, GrTexture* profileTexture); |
| + |
| + virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE; |
| + |
| + SkRRect fRRect; |
| + float fSigma; |
| + GrTextureAccess fNinePatchAccess; |
| + |
| + GR_DECLARE_EFFECT_TEST; |
| + |
| + typedef GrEffect INHERITED; |
| +}; |
| + |
| + |
| +GrEffectRef* GrRRectBlurEffect::Create(GrContext* context, float sigma, const SkRRect& rrect) { |
| + if (!rrect.isSimpleCircular()) { |
| + SkDebugf( "not simple circular\n" ); |
| + return NULL; |
| + } |
| + |
| + // Make sure we can successfully ninepatch this rrect -- the blur sigma has to be |
| + // sufficiently small relative to both the size of the corner radius and the |
| + // width (and height) of the rrect. |
| + |
| + unsigned int blurRadius = 3*SkScalarCeilToInt(sigma-1/6.0f); |
| + unsigned int cornerRadius = SkScalarCeilToInt(rrect.getSimpleRadii().x()); |
| + if (cornerRadius + blurRadius > rrect.width()/2 || |
| + cornerRadius + blurRadius > rrect.height()/2) { |
| + return NULL; |
| + } |
| + |
| + static const GrCacheID::Domain gRRectBlurDomain = GrCacheID::GenerateDomain(); |
| + GrCacheID::Key key; |
| + memset(&key, 0, sizeof(key)); |
| + key.fData32[0] = blurRadius; |
| + key.fData32[1] = cornerRadius; |
| + GrCacheID blurRRectNinePatchID(gRRectBlurDomain, key); |
| + |
| + GrTextureParams params; |
| + params.setFilterMode(GrTextureParams::kBilerp_FilterMode); |
| + |
| + unsigned int smallRectSide = 2*(blurRadius + cornerRadius) + 1; |
| + unsigned int texSide = smallRectSide + 2*blurRadius; |
| + GrTextureDesc texDesc; |
| + texDesc.fWidth = texSide; |
| + texDesc.fHeight = texSide; |
| + texDesc.fConfig = kAlpha_8_GrPixelConfig; |
| + |
| + GrTexture *blurNinePatchTexture = context->findAndRefTexture(texDesc, blurRRectNinePatchID, ¶ms); |
| + |
| + if (NULL == blurNinePatchTexture) { |
| + SkMask mask; |
| + |
| + mask.fBounds = SkIRect::MakeWH(smallRectSide, smallRectSide); |
| + mask.fFormat = SkMask::kA8_Format; |
| + mask.fRowBytes = mask.fBounds.width(); |
| + mask.fImage = SkMask::AllocImage(mask.computeTotalImageSize()); |
| + SkAutoMaskFreeImage amfi(mask.fImage); |
| + |
| + memset(mask.fImage, 0, mask.computeTotalImageSize()); |
| + |
| + SkRect smallRect; |
| + smallRect.setWH( smallRectSide,smallRectSide ); |
|
bsalomon
2014/04/23 19:50:30
smallRect.setWH(smallRectSide, smallRectSide); //
humper
2014/04/23 20:11:49
Done.
|
| + |
| + SkRRect smallRRect; |
| + smallRRect.setRectXY(smallRect, cornerRadius, cornerRadius); |
| + |
| + SkPath path; |
| + path.addRRect( smallRRect ); |
| + |
| + SkDraw::DrawToMask(path, &mask.fBounds, NULL, NULL, &mask, SkMask::kJustRenderImage_CreateMode, SkPaint::kFill_Style); |
| + |
| + SkMask blurred_mask; |
| + SkBlurMask::BoxBlur(&blurred_mask, mask, sigma, SkBlurMask::kNormal_Style, SkBlurMask::kHigh_Quality, NULL, true ); |
| + |
| + blurNinePatchTexture = context->createTexture(¶ms, texDesc, blurRRectNinePatchID, blurred_mask.fImage, 0); |
| + } |
| + |
| + if (NULL == blurNinePatchTexture) { |
| + return NULL; |
| + } |
| + |
| + return CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(GrRRectBlurEffect, |
| + (sigma, rrect, blurNinePatchTexture)))); |
| +} |
| + |
| +void GrRRectBlurEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const { |
| + *validFlags = 0; |
| +} |
| + |
| +const GrBackendEffectFactory& GrRRectBlurEffect::getFactory() const { |
| + return GrTBackendEffectFactory<GrRRectBlurEffect>::getInstance(); |
| +} |
| + |
| +GrRRectBlurEffect::GrRRectBlurEffect(float sigma, const SkRRect& rrect, GrTexture *ninePatchTexture) |
| + : fRRect(rrect), |
| + fSigma(sigma), |
| + fNinePatchAccess(ninePatchTexture) { |
| + this->addTextureAccess(&fNinePatchAccess); |
| + this->setWillReadFragmentPosition(); |
| +} |
| + |
| +bool GrRRectBlurEffect::onIsEqual(const GrEffect& other) const { |
| + const GrRRectBlurEffect& rrbe = CastEffect<GrRRectBlurEffect>(other); |
| + return fRRect == rrbe.fRRect && fSigma == rrbe.fSigma; |
|
bsalomon
2014/04/23 19:50:30
maybe it doesn't really matter much, but it seems
humper
2014/04/23 20:11:49
This was a bug -- changed to just look at the corn
|
| +} |
| + |
| +////////////////////////////////////////////////////////////////////////////// |
| + |
| +GR_DEFINE_EFFECT_TEST(GrRRectBlurEffect); |
| + |
| +GrEffectRef* GrRRectBlurEffect::TestCreate(SkRandom* random, |
| + GrContext* context, |
| + const GrDrawTargetCaps& caps, |
| + GrTexture*[]) { |
| + SkScalar w = random->nextRangeScalar(20.f, 1000.f); |
| + SkScalar h = random->nextRangeScalar(20.f, 1000.f); |
| + SkScalar r = random->nextRangeF(0, 9.f); |
|
bsalomon
2014/04/23 19:50:30
I think if r was actually 0 it would cause ::Creat
humper
2014/04/23 20:11:49
Done.
|
| + SkScalar sigma = random->nextRangeF(1,20); |
| + SkRRect rrect; |
| + rrect.setRectXY(SkRect::MakeWH(w, h), r, r); |
| + return GrRRectBlurEffect::Create(context, sigma, rrect); |
| +} |
| + |
| +////////////////////////////////////////////////////////////////////////////// |
| + |
| +class GrGLRRectBlurEffect : public GrGLEffect { |
| +public: |
| + GrGLRRectBlurEffect(const GrBackendEffectFactory&, const GrDrawEffect&); |
| + |
| + virtual void emitCode(GrGLShaderBuilder* builder, |
| + const GrDrawEffect& drawEffect, |
| + EffectKey key, |
| + const char* outputColor, |
| + const char* inputColor, |
| + const TransformedCoordsArray&, |
| + const TextureSamplerArray&) SK_OVERRIDE; |
| + |
| + virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE; |
| + |
| +private: |
| + GrGLUniformManager::UniformHandle fProxyRectUniform; |
| + GrGLUniformManager::UniformHandle fCornerRadiusUniform; |
| + GrGLUniformManager::UniformHandle fBlurRadiusUniform; |
| + typedef GrGLEffect INHERITED; |
| +}; |
| + |
| +GrGLRRectBlurEffect::GrGLRRectBlurEffect(const GrBackendEffectFactory& factory, |
| + const GrDrawEffect& drawEffect) |
| + : INHERITED (factory) { |
| +} |
| + |
| +void GrGLRRectBlurEffect::emitCode(GrGLShaderBuilder* builder, |
| + const GrDrawEffect& drawEffect, |
| + EffectKey key, |
| + const char* outputColor, |
| + const char* inputColor, |
| + const TransformedCoordsArray&, |
| + const TextureSamplerArray& samplers) { |
| + const char *rectName; |
| + const char *cornerRadiusName; |
| + const char *blurRadiusName; |
| + |
| + // The proxy rect has left, top, right, and bottom edges correspond to |
| + // components x, y, z, and w, respectively. |
| + |
| + fProxyRectUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, |
| + kVec4f_GrSLType, |
| + "proxyRect", |
| + &rectName); |
| + fCornerRadiusUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, |
| + kFloat_GrSLType, |
| + "cornerRadius", |
| + &cornerRadiusName); |
| + fBlurRadiusUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, |
| + kFloat_GrSLType, |
| + "blurRadius", |
| + &blurRadiusName); |
| + const char* fragmentPos = builder->fragmentPosition(); |
| + |
| + // warp the fragment position to the appropriate part of the 9patch blur texture |
| + |
| + builder->fsCodeAppendf("\t\tvec2 rectCenter = (%s.xy + %s.zw)/2.0;\n", rectName, rectName); |
| + builder->fsCodeAppendf("\t\tvec2 translatedFragPos = %s.xy - %s.xy;\n", fragmentPos, rectName); |
| + builder->fsCodeAppendf("\t\tfloat threshold = %s + 2.0*%s;\n", cornerRadiusName, blurRadiusName ); |
| + builder->fsCodeAppendf("\t\tvec2 middle = %s.zw - %s.xy - 2.0*threshold;\n", rectName, rectName ); |
| + |
| + builder->fsCodeAppendf("\t\tif (translatedFragPos.x >= threshold && translatedFragPos.x < (middle.x+threshold)) {\n" ); |
| + builder->fsCodeAppendf("\t\t\ttranslatedFragPos.x = threshold;\n"); |
| + builder->fsCodeAppendf("\t\t} else if (translatedFragPos.x >= (middle.x + threshold)) {\n"); |
| + builder->fsCodeAppendf("\t\t\ttranslatedFragPos.x -= middle.x;\n"); |
| + builder->fsCodeAppendf("\t\t}\n"); |
| + |
| + builder->fsCodeAppendf("\t\tif (translatedFragPos.y > threshold && translatedFragPos.y < (middle.y+threshold)) {\n" ); |
| + builder->fsCodeAppendf("\t\t\ttranslatedFragPos.y = threshold+1;\n"); |
| + builder->fsCodeAppendf("\t\t} else if (translatedFragPos.y >= (middle.y + threshold)) {\n"); |
| + builder->fsCodeAppendf("\t\t\ttranslatedFragPos.y -= middle.y;\n"); |
| + builder->fsCodeAppendf("\t\t}\n"); |
| + |
| + builder->fsCodeAppendf("\t\tvec2 proxyDims = vec2(2.0*threshold+1.0);\n"); |
| + builder->fsCodeAppendf("\t\tvec2 texCoord = translatedFragPos / proxyDims;\n"); |
| + |
| + builder->fsCodeAppendf("\t%s = ", outputColor); |
| + builder->fsAppendTextureLookupAndModulate(inputColor, samplers[0], "texCoord"); |
| + builder->fsCodeAppend(";\n"); |
| +} |
| + |
| +void GrGLRRectBlurEffect::setData(const GrGLUniformManager& uman, |
| + const GrDrawEffect& drawEffect) { |
| + const GrRRectBlurEffect& brre = drawEffect.castEffect<GrRRectBlurEffect>(); |
| + SkRRect rrect = brre.getRRect(); |
| + |
| + unsigned int blurRadius = 3*SkScalarCeilToInt(brre.getSigma()-1/6.0f); |
| + uman.set1f(fBlurRadiusUniform, blurRadius); |
| + |
| + SkRect rect = rrect.getBounds(); |
| + rect.outset(blurRadius, blurRadius); |
| + uman.set4f(fProxyRectUniform, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom); |
| + |
| + SkScalar radius = 0; |
| + SkASSERT(rrect.isSimpleCircular() || rrect.isRect()); |
| + radius = rrect.getSimpleRadii().fX; |
| + uman.set1f(fCornerRadiusUniform, radius); |
| +} |
| + |
| + |
| bool SkBlurMaskFilterImpl::directFilterRRectMaskGPU(GrContext* context, |
| GrPaint* grp, |
| const SkStrokeRec& strokeRec, |
| const SkRRect& rrect) const { |
| - return false; |
| + if (fBlurStyle != SkBlurMaskFilter::kNormal_BlurStyle) { |
| + return false; |
| + } |
| + |
| + if (!strokeRec.isFillStyle()) { |
| + return false; |
| + } |
| + |
| + SkRect proxy_rect = rrect.rect(); |
| + SkMatrix ctm = context->getMatrix(); |
| + SkScalar xformedSigma = this->computeXformedSigma(ctm); |
| + unsigned int extra=3*SkScalarCeilToInt(xformedSigma-1/6.0f); |
| + proxy_rect.outset(extra,extra); |
| + |
| + SkAutoTUnref<GrEffectRef> effect(GrRRectBlurEffect::Create( |
| + context, xformedSigma, rrect)); |
| + if (!effect) { |
| + return false; |
| + } |
| + |
| + GrContext::AutoMatrix am; |
| + if (!am.setIdentity(context, grp)) { |
| + return false; |
| + } |
| + |
| + grp->addCoverageEffect(effect); |
| + |
| + context->drawRect(*grp, proxy_rect); |
| + return true; |
| } |
| bool SkBlurMaskFilterImpl::canFilterMaskGPU(const SkRect& srcBounds, |