When clipping treat rrect corners where either the x or y radius is < 0.5 as square.
authorcommit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 24 Mar 2014 19:24:59 +0000 (19:24 +0000)
committercommit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 24 Mar 2014 19:24:59 +0000 (19:24 +0000)
BUG=skia:2181
R=robertphillips@google.com, jvanverth@google.com

Author: bsalomon@google.com

Review URL: https://codereview.chromium.org/200723011

git-svn-id: http://skia.googlecode.com/svn/trunk@13918 2bbb7eff-a529-9590-31e7-b0007b416f81

src/gpu/effects/GrRRectEffect.cpp

index e4120c9..1e8613b 100644 (file)
@@ -9,16 +9,20 @@
 
 #include "gl/GrGLEffect.h"
 #include "gl/GrGLSL.h"
+#include "GrConvexPolyEffect.h"
 #include "GrTBackendEffectFactory.h"
 
 #include "SkRRect.h"
 
+// The effects defined here only handle rrect radii >= kRadiusMin.
+static const SkScalar kRadiusMin = SK_ScalarHalf;
+
+//////////////////////////////////////////////////////////////////////////////
+
 class GLCircularRRectEffect;
 
 class CircularRRectEffect : public GrEffect {
 public:
-    // This effect only supports circular corner rrects where the radius is >= kRadiusMin.
-    static const SkScalar kRadiusMin;
 
     enum CornerFlags {
         kTopLeft_CornerFlag     = (1 << SkRRect::kUpperLeft_Corner),
@@ -34,6 +38,7 @@ public:
         kAll_CornerFlags = kTopLeft_CornerFlag    | kTopRight_CornerFlag |
                            kBottomLeft_CornerFlag | kBottomRight_CornerFlag,
 
+        kNone_CornerFlags = 0
     };
 
     // The flags are used to indicate which corners are circluar (unflagged corners are assumed to
@@ -69,8 +74,6 @@ private:
     typedef GrEffect INHERITED;
 };
 
-const SkScalar CircularRRectEffect::kRadiusMin = 0.5f;
-
 GrEffectRef* CircularRRectEffect::Create(GrEffectEdgeType edgeType,
                                  uint32_t circularCornerFlags,
                                  const SkRRect& rrect) {
@@ -303,7 +306,7 @@ void GLCircularRRectEffect::setData(const GrGLUniformManager& uman,
             case CircularRRectEffect::kAll_CornerFlags:
                 SkASSERT(rrect.isSimpleCircular());
                 radius = rrect.getSimpleRadii().fX;
-                SkASSERT(radius >= CircularRRectEffect::kRadiusMin);
+                SkASSERT(radius >= kRadiusMin);
                 rect.inset(radius, radius);
                 break;
             case CircularRRectEffect::kTopLeft_CornerFlag:
@@ -377,9 +380,6 @@ class GLEllipticalRRectEffect;
 
 class EllipticalRRectEffect : public GrEffect {
 public:
-    // This effect only supports rrects where the radii are >= kRadiusMin.
-    static const SkScalar kRadiusMin;
-
     static GrEffectRef* Create(GrEffectEdgeType, const SkRRect&);
 
     virtual ~EllipticalRRectEffect() {};
@@ -409,8 +409,6 @@ private:
     typedef GrEffect INHERITED;
 };
 
-const SkScalar EllipticalRRectEffect::kRadiusMin = 0.5f;
-
 GrEffectRef* EllipticalRRectEffect::Create(GrEffectEdgeType edgeType, const SkRRect& rrect) {
     SkASSERT(kFillAA_GrEffectEdgeType == edgeType || kInverseFillAA_GrEffectEdgeType == edgeType);
     return CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(EllipticalRRectEffect, (edgeType, rrect))));
@@ -596,8 +594,8 @@ void GLEllipticalRRectEffect::setData(const GrGLUniformManager& uman,
     if (rrect != fPrevRRect) {
         SkRect rect = rrect.getBounds();
         const SkVector& r0 = rrect.radii(SkRRect::kUpperLeft_Corner);
-        SkASSERT(r0.fX >= EllipticalRRectEffect::kRadiusMin);
-        SkASSERT(r0.fY >= EllipticalRRectEffect::kRadiusMin);
+        SkASSERT(r0.fX >= kRadiusMin);
+        SkASSERT(r0.fY >= kRadiusMin);
         switch (erre.getRRect().getType()) {
             case SkRRect::kSimple_Type:
                 rect.inset(r0.fX, r0.fY);
@@ -606,8 +604,8 @@ void GLEllipticalRRectEffect::setData(const GrGLUniformManager& uman,
                 break;
             case SkRRect::kNinePatch_Type: {
                 const SkVector& r1 = rrect.radii(SkRRect::kLowerRight_Corner);
-                SkASSERT(r1.fX >= EllipticalRRectEffect::kRadiusMin);
-                SkASSERT(r1.fY >= EllipticalRRectEffect::kRadiusMin);
+                SkASSERT(r1.fX >= kRadiusMin);
+                SkASSERT(r1.fY >= kRadiusMin);
                 rect.fLeft += r0.fX;
                 rect.fTop += r0.fY;
                 rect.fRight -= r1.fX;
@@ -632,43 +630,53 @@ GrEffectRef* GrRRectEffect::Create(GrEffectEdgeType edgeType, const SkRRect& rre
     if (kFillAA_GrEffectEdgeType != edgeType && kInverseFillAA_GrEffectEdgeType != edgeType) {
         return NULL;
     }
-    uint32_t cornerFlags;
+
+    if (rrect.isRect()) {
+        return GrConvexPolyEffect::Create(edgeType, rrect.getBounds());
+    }
+
     if (rrect.isSimple()) {
+        if (rrect.getSimpleRadii().fX < kRadiusMin || rrect.getSimpleRadii().fY < kRadiusMin) {
+            // In this case the corners are extremely close to rectangular and we collapse the
+            // clip to a rectangular clip.
+            return GrConvexPolyEffect::Create(edgeType, rrect.getBounds());
+        }
         if (rrect.getSimpleRadii().fX == rrect.getSimpleRadii().fY) {
-            if (rrect.getSimpleRadii().fX < CircularRRectEffect::kRadiusMin) {
-                return NULL;
-            }
-            cornerFlags = CircularRRectEffect::kAll_CornerFlags;
+            return CircularRRectEffect::Create(edgeType, CircularRRectEffect::kAll_CornerFlags, 
+                                               rrect);
         } else {
-            if (rrect.getSimpleRadii().fX < EllipticalRRectEffect::kRadiusMin ||
-                rrect.getSimpleRadii().fY < EllipticalRRectEffect::kRadiusMin) {
-                return NULL;
-            }
             return EllipticalRRectEffect::Create(edgeType, rrect);
         }
-    } else if (rrect.isComplex() || rrect.isNinePatch()) {
+    }
+
+    if (rrect.isComplex() || rrect.isNinePatch()) {
         // Check for the "tab" cases - two adjacent circular corners and two square corners.
-        SkScalar radius = 0;
-        cornerFlags = 0;
+        SkScalar circularRadius = 0;
+        uint32_t cornerFlags  = 0;
+
+        SkVector radii[4];
+        bool squashedRadii = false;
         for (int c = 0; c < 4; ++c) {
-            const SkVector& r = rrect.radii((SkRRect::Corner)c);
-            SkASSERT((0 == r.fX) == (0 == r.fY));
-            if (0 == r.fX) {
+            radii[c] = rrect.radii((SkRRect::Corner)c);
+            SkASSERT((0 == radii[c].fX) == (0 == radii[c].fY));
+            if (0 == radii[c].fX) {
+                // The corner is square, so no need to squash or flag as circular.
+                continue;
+            }
+            if (radii[c].fX < kRadiusMin || radii[c].fY < kRadiusMin) {
+                radii[c].set(0, 0);
+                squashedRadii = true;
                 continue;
             }
-            if (r.fX != r.fY) {
+            if (radii[c].fX != radii[c].fY) {
                 cornerFlags = ~0U;
                 break;
             }
             if (!cornerFlags) {
-                radius = r.fX;
-                if (radius < CircularRRectEffect::kRadiusMin) {
-                    cornerFlags = ~0U;
-                    break;
-                }
+                circularRadius = radii[c].fX;
                 cornerFlags = 1 << c;
             } else {
-                if (r.fX != radius) {
+                if (radii[c].fX != circularRadius) {
                    cornerFlags = ~0U;
                    break;
                 }
@@ -677,6 +685,10 @@ GrEffectRef* GrRRectEffect::Create(GrEffectEdgeType edgeType, const SkRRect& rre
         }
 
         switch (cornerFlags) {
+            case CircularRRectEffect::kAll_CornerFlags:
+                // This rrect should have been caught in the simple case above. Though, it would
+                // be correctly handled in the fallthrough code.
+                SkASSERT(false);
             case CircularRRectEffect::kTopLeft_CornerFlag:
             case CircularRRectEffect::kTopRight_CornerFlag:
             case CircularRRectEffect::kBottomRight_CornerFlag:
@@ -684,24 +696,29 @@ GrEffectRef* GrRRectEffect::Create(GrEffectEdgeType edgeType, const SkRRect& rre
             case CircularRRectEffect::kLeft_CornerFlags:
             case CircularRRectEffect::kTop_CornerFlags:
             case CircularRRectEffect::kRight_CornerFlags:
-            case CircularRRectEffect::kBottom_CornerFlags:
-            case CircularRRectEffect::kAll_CornerFlags:
-                break;
-            default:
+            case CircularRRectEffect::kBottom_CornerFlags: {
+                SkTCopyOnFirstWrite<SkRRect> rr(rrect);
+                if (squashedRadii) {
+                    rr.writable()->setRectRadii(rrect.getBounds(), radii);
+                }
+                return CircularRRectEffect::Create(edgeType, cornerFlags, *rr);
+            }
+            case CircularRRectEffect::kNone_CornerFlags:
+                return GrConvexPolyEffect::Create(edgeType, rrect.getBounds());
+            default: {
+                if (squashedRadii) {
+                    // If we got here then we squashed some but not all the radii to zero. (If all
+                    // had been squashed cornerFlags would be 0.) The elliptical effect doesn't
+                    // support some rounded and some square corners.
+                    return NULL;
+                }
                 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, rrect);
-                    }
+                    return EllipticalRRectEffect::Create(edgeType, rrect);
                 }
                 return NULL;
+            }
         }
-    } else {
-        return NULL;
     }
-    return CircularRRectEffect::Create(edgeType, cornerFlags, rrect);
+
+    return NULL;
 }