Index: src/effects/SkBlurMaskFilter.cpp |
diff --git a/src/effects/SkBlurMaskFilter.cpp b/src/effects/SkBlurMaskFilter.cpp |
index f5c80f23055364d62a4b52b01e72e381a36890a3..4d96332e2c5f7d038096d245485a7f6a5565674e 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 { |
@@ -251,9 +252,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.analyticblurrrect", false, "Use the faster analytic blur approach for ninepatch rects" ); |
#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.analyticblurrrect", true, "Use the faster analytic blur approach for ninepatch round rects" ); |
#endif |
SkMaskFilter::FilterReturn |
@@ -809,11 +810,285 @@ bool SkBlurMaskFilterImpl::directFilterMaskGPU(GrContext* context, |
return true; |
} |
+class GrGLRRectBlurEffect; |
+ |
+class GrRRectBlurEffect : public GrEffect { |
+public: |
+ |
+ 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(SkIntToScalar(smallRectSide), SkIntToScalar(smallRectSide)); |
+ |
+ SkRRect smallRRect; |
+ smallRRect.setRectXY(smallRect, SkIntToScalar(cornerRadius), SkIntToScalar(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, kNormal_SkBlurStyle, kHigh_SkBlurQuality, 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.getSimpleRadii().fX == rrbe.fRRect.getSimpleRadii().fX && fSigma == rrbe.fSigma; |
+} |
+ |
+////////////////////////////////////////////////////////////////////////////// |
+ |
+GR_DEFINE_EFFECT_TEST(GrRRectBlurEffect); |
+ |
+GrEffectRef* GrRRectBlurEffect::TestCreate(SkRandom* random, |
+ GrContext* context, |
+ const GrDrawTargetCaps& caps, |
+ GrTexture*[]) { |
+ SkScalar w = random->nextRangeScalar(100.f, 1000.f); |
+ SkScalar h = random->nextRangeScalar(100.f, 1000.f); |
+ SkScalar r = random->nextRangeF(1.f, 9.f); |
+ SkScalar sigma = random->nextRangeF(1.f,10.f); |
+ 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;\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(); |
+ |
+ float blurRadius = 3.f*SkScalarCeilToScalar(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 != kNormal_SkBlurStyle) { |
+ return false; |
+ } |
+ |
+ if (!strokeRec.isFillStyle()) { |
+ return false; |
+ } |
+ |
+ SkRect proxy_rect = rrect.rect(); |
+ SkMatrix ctm = context->getMatrix(); |
+ SkScalar xformedSigma = this->computeXformedSigma(ctm); |
+ float extra=3.f*SkScalarCeilToScalar(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, |