Index: src/gpu/effects/GrRRectEffect.cpp |
diff --git a/src/gpu/effects/GrRRectEffect.cpp b/src/gpu/effects/GrRRectEffect.cpp |
index 571d2b0a7c92292dec5f6eef166d5f4f4c8a3d3e..e0d10514766f1d5727298b9d20f20b8c1d5238b4 100644 |
--- a/src/gpu/effects/GrRRectEffect.cpp |
+++ b/src/gpu/effects/GrRRectEffect.cpp |
@@ -377,16 +377,23 @@ class GLEllipticalRRectEffect; |
class EllipticalRRectEffect : public GrEffect { |
public: |
- // This effect only supports circular corner rrects where the radius is >= kRadiusMin. |
+ enum RRectType { |
+ kSimple_RRectType, |
+ kNinePatch_RRectType, |
jvanverth1
2014/03/12 13:37:55
This might be worth adding to SkRRect.
bsalomon
2014/03/12 14:34:55
Will do in follow-up CL
|
+ }; |
+ |
+ // 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; |
@@ -396,11 +403,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; |
@@ -411,9 +419,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)))); |
+ return CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(EllipticalRRectEffect, (edgeType, rrType, |
+ rrect)))); |
} |
void EllipticalRRectEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const { |
@@ -424,14 +434,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; |
} |
@@ -445,14 +458,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 = (GrEffectEdgeType)random->nextULessThan(kGrEffectEdgeTypeCnt); |
- effect = EllipticalRRectEffect::Create(et, rrect); |
+ effect = GrRRectEffect::Create(et, rrect); |
} while (NULL == effect); |
return effect; |
} |
@@ -477,7 +510,7 @@ public: |
private: |
GrGLUniformManager::UniformHandle fInnerRectUniform; |
- GrGLUniformManager::UniformHandle fInvRadiusXYSqdUniform; |
+ GrGLUniformManager::UniformHandle fInvRadiiSqdUniform; |
SkRRect fPrevRRect; |
typedef GrGLEffect INHERITED; |
}; |
@@ -497,16 +530,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 |
@@ -521,16 +549,40 @@ 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. |
builder->fsCodeAppendf("\t\tfloat grad_dot = 4.0 * dot(Z, Z);\n", fragmentPos); |
builder->fsCodeAppend("\t\tgrad_dot = max(grad_dot, 1.0e-4);\n"); |
builder->fsCodeAppendf("\t\tfloat approx_dist = implicit * inversesqrt(grad_dot);\n", |
- fragmentPos); |
+ fragmentPos); |
if (kFillAA_GrEffectEdgeType == erre.getEdgeType()) { |
builder->fsCodeAppend("\t\tfloat alpha = clamp(0.5 - approx_dist, 0.0, 1.0);\n"); |
@@ -545,7 +597,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, |
@@ -554,14 +607,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), |
jvanverth1
2014/03/12 13:37:55
Use SkScalarInvert or SkScalarFastInvert?
bsalomon
2014/03/12 14:34:55
I checked with Mike and he sez using SkScalarInver
|
+ 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; |
} |
} |
@@ -584,7 +654,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. |
@@ -597,17 +668,20 @@ GrEffectRef* GrRRectEffect::Create(GrEffectEdgeType edgeType, const SkRRect& rre |
continue; |
} |
if (r.fX != r.fY) { |
- return NULL; |
+ cornerFlags = -1; |
jvanverth1
2014/03/12 13:37:55
This seems a little hacky, but I don't have a bett
bsalomon
2014/03/12 14:34:55
yup :)
|
+ 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; |
} |
@@ -625,6 +699,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 { |