aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2014-03-13 18:01:05 +0000
committerGravatar commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2014-03-13 18:01:05 +0000
commitfa5edbe5750632b4590ed951c594dd2766ccc4cf (patch)
treeea35a8f03a39defbaf637828d87bd52cfbc4950f
parent9c28fa5579734129f1bad026b8b6aea3bf76da5f (diff)
Add effect-based clip for nine-patch SkRRects.
BUG=skia:2181 R=jvanverth@google.com, robertphillips@google.com, rmistry@google.com Author: bsalomon@google.com Review URL: https://codereview.chromium.org/194703011 git-svn-id: http://skia.googlecode.com/svn/trunk@13794 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r--gm/rrects.cpp6
-rw-r--r--include/core/SkRRect.h11
-rw-r--r--src/gpu/effects/GrRRectEffect.cpp157
3 files changed, 136 insertions, 38 deletions
diff --git a/gm/rrects.cpp b/gm/rrects.cpp
index 6c63af9b30..b89c79ff6b 100644
--- a/gm/rrects.cpp
+++ b/gm/rrects.cpp
@@ -185,7 +185,7 @@ private:
static const int kTileY = 40;
static const int kNumSimpleCases = 7;
- static const int kNumComplexCases = 27;
+ static const int kNumComplexCases = 29;
static const SkVector gRadii[kNumComplexCases][4];
static const int kNumRRects = kNumSimpleCases + kNumComplexCases;
@@ -240,6 +240,10 @@ const SkVector RRectGM::gRadii[kNumComplexCases][4] = {
{ { 0, 0 }, { 0, 0 }, { 15, 15 }, { 0, 0 } },
{ { 0, 0 }, { 15, 15 }, { 0, 0 }, { 0, 0 } },
{ { 15, 15 }, { 0, 0 }, { 0, 0 }, { 0, 0 } },
+
+ // nine patch elliptical
+ { { 5, 7 }, { 8, 7 }, { 8, 12 }, { 5, 12 } },
+ { { 0, 7 }, { 8, 7 }, { 8, 12 }, { 0, 12 } },
};
///////////////////////////////////////////////////////////////////////////////
diff --git a/include/core/SkRRect.h b/include/core/SkRRect.h
index 9878b54fdc..c09d2d46fe 100644
--- a/include/core/SkRRect.h
+++ b/include/core/SkRRect.h
@@ -103,6 +103,17 @@ public:
bool allCornersCircular() const;
+ /**
+ * Are both x-radii the same on the two left corners, and similar for the top, right, and
+ * bottom. When this is the case the four ellipse centers form a rectangle.
+ */
+ bool isNinePatch() const {
+ return fRadii[kUpperLeft_Corner].fX == fRadii[kLowerLeft_Corner].fX &&
+ fRadii[kUpperRight_Corner].fX == fRadii[kLowerRight_Corner].fX &&
+ fRadii[kUpperLeft_Corner].fY == fRadii[kUpperRight_Corner].fY &&
+ fRadii[kLowerLeft_Corner].fY == fRadii[kLowerRight_Corner].fY;
+ }
+
SkScalar width() const { return fRect.width(); }
SkScalar height() const { return fRect.height(); }
diff --git a/src/gpu/effects/GrRRectEffect.cpp b/src/gpu/effects/GrRRectEffect.cpp
index 51404beda7..324d02da02 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 {