Index: src/gpu/effects/GrRRectEffect.cpp |
diff --git a/src/gpu/effects/GrRRectEffect.cpp b/src/gpu/effects/GrRRectEffect.cpp |
index 51404beda72f193e53ebe7deaf82980834fb6175..324d02da02df5c140aec42a9a98c5e0607535cc5 100644 |
--- a/src/gpu/effects/GrRRectEffect.cpp |
+++ b/src/gpu/effects/GrRRectEffect.cpp |
@@ -375,22 +375,27 @@ void GLCircularRRectEffect::setData(const GrGLUniformManager& uman, |
class GLEllipticalRRectEffect; |
-/** |
- * Currently this effect only supports "simple" elliptical round rects (i.e. |
- * the corners all have a common x/y radii pair). |
- */ |
class EllipticalRRectEffect : public GrEffect { |
public: |
- // This effect only supports rrects where the radii are >= kRadiusMin |
+ // This effect currently works for these two classifications of SkRRects |
+ enum RRectType { |
+ kSimple_RRectType, // SkRRect::kSimple_Type |
+ kNinePatch_RRectType, // The two left x radii are the same, the two |
+ // top y radii are the same, etc. |
+ }; |
+ |
+ // This effect only supports rrects where the radii are >= kRadiusMin. |
static const SkScalar kRadiusMin; |
- static GrEffectRef* Create(GrEffectEdgeType, const SkRRect&); |
+ static GrEffectRef* Create(GrEffectEdgeType, RRectType, const SkRRect&); |
virtual ~EllipticalRRectEffect() {}; |
static const char* Name() { return "EllipticalRRect"; } |
const SkRRect& getRRect() const { return fRRect; } |
+ RRectType getRRectType() const { return fRRectType; } |
+ |
GrEffectEdgeType getEdgeType() const { return fEdgeType; } |
typedef GLEllipticalRRectEffect GLEffect; |
@@ -400,11 +405,12 @@ public: |
virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; |
private: |
- EllipticalRRectEffect(GrEffectEdgeType, const SkRRect&); |
+ EllipticalRRectEffect(GrEffectEdgeType, RRectType, const SkRRect&); |
virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE; |
SkRRect fRRect; |
+ RRectType fRRectType; |
GrEffectEdgeType fEdgeType; |
GR_DECLARE_EFFECT_TEST; |
@@ -415,9 +421,11 @@ private: |
const SkScalar EllipticalRRectEffect::kRadiusMin = 0.5f; |
GrEffectRef* EllipticalRRectEffect::Create(GrEffectEdgeType edgeType, |
+ RRectType rrType, |
const SkRRect& rrect) { |
-// SkASSERT(kFillAA_GrEffectEdgeType == edgeType || kInverseFillAA_GrEffectEdgeType == edgeType); |
- return CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(EllipticalRRectEffect, (edgeType, rrect)))); |
+ SkASSERT(kFillAA_GrEffectEdgeType == edgeType || kInverseFillAA_GrEffectEdgeType == edgeType); |
+ return CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(EllipticalRRectEffect, (edgeType, rrType, |
+ rrect)))); |
} |
void EllipticalRRectEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const { |
@@ -428,14 +436,17 @@ const GrBackendEffectFactory& EllipticalRRectEffect::getFactory() const { |
return GrTBackendEffectFactory<EllipticalRRectEffect>::getInstance(); |
} |
-EllipticalRRectEffect::EllipticalRRectEffect(GrEffectEdgeType edgeType, const SkRRect& rrect) |
+EllipticalRRectEffect::EllipticalRRectEffect(GrEffectEdgeType edgeType, RRectType rrType, |
+ const SkRRect& rrect) |
: fRRect(rrect) |
+ , fRRectType(rrType) |
, fEdgeType(edgeType){ |
this->setWillReadFragmentPosition(); |
} |
bool EllipticalRRectEffect::onIsEqual(const GrEffect& other) const { |
const EllipticalRRectEffect& erre = CastEffect<EllipticalRRectEffect>(other); |
+ // No need to check fRRectType as it is derived from fRRect. |
return fEdgeType == erre.fEdgeType && fRRect == erre.fRRect; |
} |
@@ -449,15 +460,34 @@ GrEffectRef* EllipticalRRectEffect::TestCreate(SkRandom* random, |
GrTexture*[]) { |
SkScalar w = random->nextRangeScalar(20.f, 1000.f); |
SkScalar h = random->nextRangeScalar(20.f, 1000.f); |
- SkScalar rx = random->nextRangeF(kRadiusMin, 9.f); |
- SkScalar ry = random->nextRangeF(kRadiusMin, 9.f); |
+ SkVector r[4]; |
+ r[SkRRect::kUpperLeft_Corner].fX = random->nextRangeF(kRadiusMin, 9.f); |
+ // ensure at least one corner really is elliptical |
+ do { |
+ r[SkRRect::kUpperLeft_Corner].fY = random->nextRangeF(kRadiusMin, 9.f); |
+ } while (r[SkRRect::kUpperLeft_Corner].fY == r[SkRRect::kUpperLeft_Corner].fX); |
+ |
SkRRect rrect; |
- rrect.setRectXY(SkRect::MakeWH(w, h), rx, ry); |
+ if (random->nextBool()) { |
+ // half the time create a four-radii rrect. |
+ r[SkRRect::kLowerRight_Corner].fX = random->nextRangeF(kRadiusMin, 9.f); |
+ r[SkRRect::kLowerRight_Corner].fY = random->nextRangeF(kRadiusMin, 9.f); |
+ |
+ r[SkRRect::kUpperRight_Corner].fX = r[SkRRect::kLowerRight_Corner].fX; |
+ r[SkRRect::kUpperRight_Corner].fY = r[SkRRect::kUpperLeft_Corner].fY; |
+ |
+ r[SkRRect::kLowerLeft_Corner].fX = r[SkRRect::kUpperLeft_Corner].fX; |
+ r[SkRRect::kLowerLeft_Corner].fY = r[SkRRect::kLowerRight_Corner].fY; |
+ |
+ rrect.setRectRadii(SkRect::MakeWH(w, h), r); |
+ } else { |
+ rrect.setRectXY(SkRect::MakeWH(w, h), r[SkRRect::kUpperLeft_Corner].fX, |
+ r[SkRRect::kUpperLeft_Corner].fY); |
+ } |
GrEffectRef* effect; |
do { |
- GrEffectEdgeType et = random->nextBool() ? kFillAA_GrEffectEdgeType : |
- kInverseFillAA_GrEffectEdgeType; |
- effect = EllipticalRRectEffect::Create(et, rrect); |
+ GrEffectEdgeType et = (GrEffectEdgeType)random->nextULessThan(kGrEffectEdgeTypeCnt); |
+ effect = GrRRectEffect::Create(et, rrect); |
} while (NULL == effect); |
return effect; |
} |
@@ -482,7 +512,7 @@ public: |
private: |
GrGLUniformManager::UniformHandle fInnerRectUniform; |
- GrGLUniformManager::UniformHandle fInvRadiusXYSqdUniform; |
+ GrGLUniformManager::UniformHandle fInvRadiiSqdUniform; |
SkRRect fPrevRRect; |
typedef GrGLEffect INHERITED; |
}; |
@@ -502,16 +532,11 @@ void GLEllipticalRRectEffect::emitCode(GrGLShaderBuilder* builder, |
const TextureSamplerArray& samplers) { |
const EllipticalRRectEffect& erre = drawEffect.castEffect<EllipticalRRectEffect>(); |
const char *rectName; |
- const char *invRadiusXYSqdName; |
// The inner rect is the rrect bounds inset by the x/y radii |
fInnerRectUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, |
kVec4f_GrSLType, |
"innerRect", |
&rectName); |
- fInvRadiusXYSqdUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, |
- kVec2f_GrSLType, |
- "invRadiusXY", |
- &invRadiusXYSqdName); |
const char* fragmentPos = builder->fragmentPosition(); |
// At each quarter-ellipse corner we compute a vector that is the offset of the fragment pos |
// to the ellipse center. The vector is pinned in x and y to be in the quarter-plane relevant |
@@ -526,9 +551,33 @@ void GLEllipticalRRectEffect::emitCode(GrGLShaderBuilder* builder, |
// need be computed to determine the min alpha. |
builder->fsCodeAppendf("\t\tvec2 dxy0 = %s.xy - %s.xy;\n", rectName, fragmentPos); |
builder->fsCodeAppendf("\t\tvec2 dxy1 = %s.xy - %s.zw;\n", fragmentPos, rectName); |
- builder->fsCodeAppend("\t\tvec2 dxy = max(max(dxy0, dxy1), 0.0);\n"); |
- // Z is the x/y offsets divided by squared radii. |
- builder->fsCodeAppendf("\t\tvec2 Z = dxy * %s;\n", invRadiusXYSqdName); |
+ switch (erre.getRRectType()) { |
+ case EllipticalRRectEffect::kSimple_RRectType: { |
+ const char *invRadiiXYSqdName; |
+ fInvRadiiSqdUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, |
+ kVec2f_GrSLType, |
+ "invRadiiXY", |
+ &invRadiiXYSqdName); |
+ builder->fsCodeAppend("\t\tvec2 dxy = max(max(dxy0, dxy1), 0.0);\n"); |
+ // Z is the x/y offsets divided by squared radii. |
+ builder->fsCodeAppendf("\t\tvec2 Z = dxy * %s;\n", invRadiiXYSqdName); |
+ break; |
+ } |
+ case EllipticalRRectEffect::kNinePatch_RRectType: { |
+ const char *invRadiiLTRBSqdName; |
+ fInvRadiiSqdUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, |
+ kVec4f_GrSLType, |
+ "invRadiiLTRB", |
+ &invRadiiLTRBSqdName); |
+ builder->fsCodeAppend("\t\tvec2 dxy = max(max(dxy0, dxy1), 0.0);\n"); |
+ // Z is the x/y offsets divided by squared radii. We only care about the (at most) one |
+ // corner where both the x and y offsets are positive, hence the maxes. (The inverse |
+ // squared radii will always be positive.) |
+ builder->fsCodeAppendf("\t\tvec2 Z = max(max(dxy0 * %s.xy, dxy1 * %s.zw), 0.0);\n", |
+ invRadiiLTRBSqdName, invRadiiLTRBSqdName); |
+ break; |
+ } |
+ } |
// implicit is the evaluation of (x/a)^2 + (y/b)^2 - 1. |
builder->fsCodeAppend("\t\tfloat implicit = dot(Z, dxy) - 1.0;\n"); |
// grad_dot is the squared length of the gradient of the implicit. |
@@ -549,7 +598,8 @@ void GLEllipticalRRectEffect::emitCode(GrGLShaderBuilder* builder, |
GrGLEffect::EffectKey GLEllipticalRRectEffect::GenKey(const GrDrawEffect& drawEffect, |
const GrGLCaps&) { |
const EllipticalRRectEffect& erre = drawEffect.castEffect<EllipticalRRectEffect>(); |
- return erre.getEdgeType(); |
+ GR_STATIC_ASSERT(kLast_GrEffectEdgeType < (1 << 3)); |
+ return erre.getRRectType() | erre.getEdgeType() << 3; |
} |
void GLEllipticalRRectEffect::setData(const GrGLUniformManager& uman, |
@@ -558,14 +608,31 @@ void GLEllipticalRRectEffect::setData(const GrGLUniformManager& uman, |
const SkRRect& rrect = erre.getRRect(); |
if (rrect != fPrevRRect) { |
SkRect rect = rrect.getBounds(); |
- SkASSERT(rrect.isSimple()); |
- const SkVector& radius = rrect.getSimpleRadii(); |
- SkASSERT(radius.fX >= EllipticalRRectEffect::kRadiusMin); |
- SkASSERT(radius.fY >= EllipticalRRectEffect::kRadiusMin); |
- rect.inset(radius.fX, radius.fY); |
+ const SkVector& r0 = rrect.radii(SkRRect::kUpperLeft_Corner); |
+ SkASSERT(r0.fX >= EllipticalRRectEffect::kRadiusMin); |
+ SkASSERT(r0.fY >= EllipticalRRectEffect::kRadiusMin); |
+ switch (erre.getRRectType()) { |
+ case EllipticalRRectEffect::kSimple_RRectType: |
+ rect.inset(r0.fX, r0.fY); |
+ uman.set2f(fInvRadiiSqdUniform, 1.f / (r0.fX * r0.fX), |
+ 1.f / (r0.fY * r0.fY)); |
+ break; |
+ case EllipticalRRectEffect::kNinePatch_RRectType: { |
+ const SkVector& r1 = rrect.radii(SkRRect::kLowerRight_Corner); |
+ SkASSERT(r1.fX >= EllipticalRRectEffect::kRadiusMin); |
+ SkASSERT(r1.fY >= EllipticalRRectEffect::kRadiusMin); |
+ rect.fLeft += r0.fX; |
+ rect.fTop += r0.fY; |
+ rect.fRight -= r1.fX; |
+ rect.fBottom -= r1.fY; |
+ uman.set4f(fInvRadiiSqdUniform, 1.f / (r0.fX * r0.fX), |
+ 1.f / (r0.fY * r0.fY), |
+ 1.f / (r1.fX * r1.fX), |
+ 1.f / (r1.fY * r1.fY)); |
+ break; |
+ } |
+ } |
uman.set4f(fInnerRectUniform, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom); |
- uman.set2f(fInvRadiusXYSqdUniform, 1.f / (radius.fX * radius.fX), |
- 1.f / (radius.fY * radius.fY)); |
fPrevRRect = rrect; |
} |
} |
@@ -588,7 +655,8 @@ GrEffectRef* GrRRectEffect::Create(GrEffectEdgeType edgeType, const SkRRect& rre |
rrect.getSimpleRadii().fY < EllipticalRRectEffect::kRadiusMin) { |
return NULL; |
} |
- return EllipticalRRectEffect::Create(edgeType, rrect); |
+ return EllipticalRRectEffect::Create(edgeType, |
+ EllipticalRRectEffect::kSimple_RRectType, rrect); |
} |
} else if (rrect.isComplex()) { |
// Check for the "tab" cases - two adjacent circular corners and two square corners. |
@@ -601,17 +669,20 @@ GrEffectRef* GrRRectEffect::Create(GrEffectEdgeType edgeType, const SkRRect& rre |
continue; |
} |
if (r.fX != r.fY) { |
- return NULL; |
+ cornerFlags = -1; |
+ break; |
} |
if (!cornerFlags) { |
radius = r.fX; |
if (radius < CircularRRectEffect::kRadiusMin) { |
- return NULL; |
+ cornerFlags = -1; |
+ break; |
} |
cornerFlags = 1 << c; |
} else { |
if (r.fX != radius) { |
- return NULL; |
+ cornerFlags = -1; |
+ break; |
} |
cornerFlags |= 1 << c; |
} |
@@ -629,6 +700,18 @@ GrEffectRef* GrRRectEffect::Create(GrEffectEdgeType edgeType, const SkRRect& rre |
case CircularRRectEffect::kAll_CornerFlags: |
break; |
default: |
+ if (rrect.isNinePatch()) { |
+ const SkVector& r0 = rrect.radii(SkRRect::kUpperLeft_Corner); |
+ const SkVector& r1 = rrect.radii(SkRRect::kLowerRight_Corner); |
+ if (r0.fX >= EllipticalRRectEffect::kRadiusMin && |
+ r0.fY >= EllipticalRRectEffect::kRadiusMin && |
+ r1.fX >= EllipticalRRectEffect::kRadiusMin && |
+ r1.fY >= EllipticalRRectEffect::kRadiusMin) { |
+ return EllipticalRRectEffect::Create(edgeType, |
+ EllipticalRRectEffect::kNinePatch_RRectType, |
+ rrect); |
+ } |
+ } |
return NULL; |
} |
} else { |