Analytic rrect clip for cicular corners, radius >= 0.5

BUG=skia:2181
R=robertphillips@google.com, jvanverth@google.com

Author: bsalomon@google.com

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

git-svn-id: http://skia.googlecode.com/svn/trunk@13498 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/expectations/gm/ignored-tests.txt b/expectations/gm/ignored-tests.txt
index a165a84..776cf93 100644
--- a/expectations/gm/ignored-tests.txt
+++ b/expectations/gm/ignored-tests.txt
@@ -42,3 +42,12 @@
 # https://codereview.chromium.org/163663002/
 bitmapsource
 imagefiltersscaled
+
+# Need to rebaseline GPU versions of these tests due to rrect clipping changes and
+# changes to rrect_* tests.
+# http://skbug.com/2181 https://codereview.chromium.org/171413004/
+complexclip2_rrect_aa
+rrect_clip_bw
+rrect_clip_aa
+rrect_bw
+rrect_aa
\ No newline at end of file
diff --git a/gm/rrects.cpp b/gm/rrects.cpp
index 299e8d8..9e07703 100644
--- a/gm/rrects.cpp
+++ b/gm/rrects.cpp
@@ -74,6 +74,11 @@
         fRRects[0].setRect(SkRect::MakeWH(kTileX-2, kTileY-2));
         fRRects[1].setOval(SkRect::MakeWH(kTileX-2, kTileY-2));
         fRRects[2].setRectXY(SkRect::MakeWH(kTileX-2, kTileY-2), 10, 10);
+        fRRects[3].setRectXY(SkRect::MakeWH(kTileX-2, kTileY-2), 10, 5);
+        // small circular corners are an interesting test case for gpu clipping
+        fRRects[4].setRectXY(SkRect::MakeWH(kTileX-2, kTileY-2), 1, 1);
+        fRRects[5].setRectXY(SkRect::MakeWH(kTileX-2, kTileY-2), 0.5f, 0.5f);
+        fRRects[6].setRectXY(SkRect::MakeWH(kTileX-2, kTileY-2), 0.2f, 0.2f);
 
         // The first complex case needs special handling since it is a square
         fRRects[kNumSimpleCases].setRectRadii(SkRect::MakeWH(kTileY-2, kTileY-2), gRadii[0]);
@@ -92,7 +97,7 @@
     static const int kTileX = 80;
     static const int kTileY = 40;
 
-    static const int kNumSimpleCases = 3;
+    static const int kNumSimpleCases = 7;
     static const int kNumComplexCases = 19;
     static const SkVector gRadii[kNumComplexCases][4];
 
diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi
index b1dd564..a40ff66 100644
--- a/gyp/gpu.gypi
+++ b/gyp/gpu.gypi
@@ -139,6 +139,8 @@
       '<(skia_src_path)/gpu/effects/GrBicubicEffect.h',
       '<(skia_src_path)/gpu/effects/GrCustomCoordsTextureEffect.cpp',
       '<(skia_src_path)/gpu/effects/GrCustomCoordsTextureEffect.h',
+      '<(skia_src_path)/gpu/effects/GrRRectEffect.cpp',
+      '<(skia_src_path)/gpu/effects/GrRRectEffect.h',
       '<(skia_src_path)/gpu/effects/GrSimpleTextureEffect.cpp',
       '<(skia_src_path)/gpu/effects/GrSimpleTextureEffect.h',
       '<(skia_src_path)/gpu/effects/GrSingleTextureEffect.cpp',
diff --git a/include/core/SkRRect.h b/include/core/SkRRect.h
index e70cff6..141a3d2 100644
--- a/include/core/SkRRect.h
+++ b/include/core/SkRRect.h
@@ -96,6 +96,9 @@
     inline bool isRect() const { return kRect_Type == this->getType(); }
     inline bool isOval() const { return kOval_Type == this->getType(); }
     inline bool isSimple() const { return kSimple_Type == this->getType(); }
+    inline bool isSimpleCircular() const {
+        return this->isSimple() && fRadii[0].fX == fRadii[0].fY;
+    }
     inline bool isComplex() const { return kComplex_Type == this->getType(); }
 
     SkScalar width() const { return fRect.width(); }
diff --git a/include/gpu/GrTBackendEffectFactory.h b/include/gpu/GrTBackendEffectFactory.h
index 3711c7e..fd14b4f 100644
--- a/include/gpu/GrTBackendEffectFactory.h
+++ b/include/gpu/GrTBackendEffectFactory.h
@@ -64,7 +64,7 @@
     /** Returns a new instance of the appropriate *GL* implementation class
         for the given GrEffect; caller is responsible for deleting
         the object. */
-    virtual GLEffect* createGLInstance(const GrDrawEffect& drawEffect) const SK_OVERRIDE {
+    virtual GrGLEffect* createGLInstance(const GrDrawEffect& drawEffect) const SK_OVERRIDE {
         return SkNEW_ARGS(GLEffect, (*this, drawEffect));
     }
 
diff --git a/src/gpu/GrClipMaskManager.cpp b/src/gpu/GrClipMaskManager.cpp
index 7ccf30f..b637bd6 100644
--- a/src/gpu/GrClipMaskManager.cpp
+++ b/src/gpu/GrClipMaskManager.cpp
@@ -19,6 +19,7 @@
 #include "GrSWMaskHelper.h"
 #include "effects/GrTextureDomain.h"
 #include "effects/GrConvexPolyEffect.h"
+#include "effects/GrRRectEffect.h"
 #include "SkRasterClip.h"
 #include "SkStrokeRec.h"
 #include "SkTLazy.h"
@@ -189,6 +190,9 @@
                                                            GrConvexPolyEffect::kFillNoAA_EdgeType;
                 effect.reset(GrConvexPolyEffect::Create(type, path, &offset));
             }
+        } else if (isAA && SkClipStack::Element::kRRect_Type == type && !rt->isMultisampled()) {
+            const SkRRect& rrect = elements.tail()->getRRect();
+            effect.reset(GrRRectEffect::Create(rrect));
         } else if (isAA && SkClipStack::Element::kRect_Type == type && !rt->isMultisampled()) {
             // We only handle AA/non-MSAA rects here. Coverage effect AA isn't MSAA friendly and
             // non-AA rect clips are handled by the scissor.
diff --git a/src/gpu/effects/GrConvexPolyEffect.cpp b/src/gpu/effects/GrConvexPolyEffect.cpp
index eedc9c9..bbfa3bb 100644
--- a/src/gpu/effects/GrConvexPolyEffect.cpp
+++ b/src/gpu/effects/GrConvexPolyEffect.cpp
@@ -9,7 +9,6 @@
 
 #include "gl/GrGLEffect.h"
 #include "gl/GrGLSL.h"
-#include "gl/GrGLVertexEffect.h"
 #include "GrTBackendEffectFactory.h"
 
 #include "SkPath.h"
diff --git a/src/gpu/effects/GrConvexPolyEffect.h b/src/gpu/effects/GrConvexPolyEffect.h
index d7d17ab..c712eb3 100644
--- a/src/gpu/effects/GrConvexPolyEffect.h
+++ b/src/gpu/effects/GrConvexPolyEffect.h
@@ -10,7 +10,6 @@
 
 #include "GrDrawTargetCaps.h"
 #include "GrEffect.h"
-#include "GrVertexEffect.h"
 
 class GrGLConvexPolyEffect;
 class SkPath;
diff --git a/src/gpu/effects/GrRRectEffect.cpp b/src/gpu/effects/GrRRectEffect.cpp
new file mode 100644
index 0000000..a2a6af7
--- /dev/null
+++ b/src/gpu/effects/GrRRectEffect.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrRRectEffect.h"
+
+#include "gl/GrGLEffect.h"
+#include "gl/GrGLSL.h"
+#include "GrTBackendEffectFactory.h"
+
+#include "SkPath.h"
+
+// This effect only supports circular corner rrects where all corners have the same radius
+// which must be <= kRadiusMin.
+static const SkScalar kRadiusMin = 0.5;
+
+//////////////////////////////////////////////////////////////////////////////
+class GrGLRRectEffect : public GrGLEffect {
+public:
+    GrGLRRectEffect(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;
+
+    static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&) { return 0; }
+
+    virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
+
+private:
+    GrGLUniformManager::UniformHandle   fInnerRectUniform;
+    GrGLUniformManager::UniformHandle   fRadiusPlusHalfUniform;
+    SkRRect                             fPrevRRect;
+    typedef GrGLEffect INHERITED;
+};
+
+GrGLRRectEffect::GrGLRRectEffect(const GrBackendEffectFactory& factory,
+                                 const GrDrawEffect& drawEffect)
+    : INHERITED (factory) {
+    fPrevRRect.setEmpty();
+}
+
+void GrGLRRectEffect::emitCode(GrGLShaderBuilder* builder,
+                               const GrDrawEffect& drawEffect,
+                               EffectKey key,
+                               const char* outputColor,
+                               const char* inputColor,
+                               const TransformedCoordsArray&,
+                               const TextureSamplerArray& samplers) {
+    const char *rectName;
+    const char *radiusPlusHalfName;
+    // The inner rect is the rrect bounds inset by the radius. Its top, left, right, and bottom
+    // edges correspond to components x, y, z, and w, respectively.
+    fInnerRectUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
+                                            kVec4f_GrSLType,
+                                            "innerRect",
+                                            &rectName);
+    fRadiusPlusHalfUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
+                                                 kFloat_GrSLType,
+                                                 "radiusPlusHalf",
+                                                 &radiusPlusHalfName);
+    const char* fragmentPos = builder->fragmentPosition();
+    // At each quarter-circle corner we compute a vector that is the offset of the fragment position
+    // from the circle center. The vector is pinned in x and y to be in the quarter-plane relevant
+    // to that corner. This means that points near the interior near the rrect top edge will have
+    // a vector that points straight up for both the TL left and TR corners. Computing an
+    // alpha from this vector at either the TR or TL corner will give the correct result. Similarly,
+    // fragments near the other three edges will get the correct AA. Fragments in the interior of
+    // the rrect will have a (0,0) vector at all four corners. So long as the radius > 0.5 they will
+    // correctly produce an alpha value of 1 at all four corners. We take the min of all the alphas.
+    // The code below is a simplified version of the above that performs maxs on the vector
+    // components before computing distances and alpha values so that only one distance computation
+    // 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");
+    builder->fsCodeAppendf("\t\tfloat alpha = clamp(%s - length(dxy), 0.0, 1.0);\n",
+                           radiusPlusHalfName);
+
+    builder->fsCodeAppendf("\t\t%s = %s;\n", outputColor,
+                           (GrGLSLExpr4(inputColor) * GrGLSLExpr1("alpha")).c_str());
+}
+
+void GrGLRRectEffect::setData(const GrGLUniformManager& uman, const GrDrawEffect& drawEffect) {
+    const GrRRectEffect& rre = drawEffect.castEffect<GrRRectEffect>();
+    const SkRRect& rrect = rre.getRRect();
+    if (rrect != fPrevRRect) {
+        SkASSERT(rrect.isSimpleCircular());
+        SkRect rect = rrect.getBounds();
+        SkScalar radius = rrect.getSimpleRadii().fX;
+        SkASSERT(radius >= kRadiusMin);
+        rect.inset(radius, radius);
+        uman.set4f(fInnerRectUniform, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
+        uman.set1f(fRadiusPlusHalfUniform, radius + 0.5f);
+        fPrevRRect = rrect;
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+GrEffectRef* GrRRectEffect::Create(const SkRRect& rrect) {
+    if (!rrect.isSimpleCircular()) {
+        return NULL;
+    }
+    if (rrect.getSimpleRadii().fX < kRadiusMin) {
+        return NULL;
+    }
+    return CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(GrRRectEffect, (rrect))));
+}
+
+GrRRectEffect::~GrRRectEffect() {}
+
+void GrRRectEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+    *validFlags = 0;
+}
+
+const GrBackendEffectFactory& GrRRectEffect::getFactory() const {
+    return GrTBackendEffectFactory<GrRRectEffect>::getInstance();
+}
+
+GrRRectEffect::GrRRectEffect(const SkRRect& rrect)
+    : fRRect(rrect) {
+    SkASSERT(rrect.isSimpleCircular());
+    SkASSERT(rrect.getSimpleRadii().fX >= kRadiusMin);
+    this->setWillReadFragmentPosition();
+}
+
+bool GrRRectEffect::onIsEqual(const GrEffect& other) const {
+    const GrRRectEffect& rre = CastEffect<GrRRectEffect>(other);
+    return fRRect == rre.fRRect;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_EFFECT_TEST(GrRRectEffect);
+
+GrEffectRef* GrRRectEffect::TestCreate(SkRandom* random,
+                                       GrContext*,
+                                       const GrDrawTargetCaps& caps,
+                                       GrTexture*[]) {
+    SkScalar w = random->nextRangeScalar(20.f, 1000.f);
+    SkScalar h = random->nextRangeScalar(20.f, 1000.f);
+    SkScalar r = random->nextRangeF(kRadiusMin, 9.f);
+    SkRRect rrect;
+    rrect.setRectXY(SkRect::MakeWH(w, h), r, r);
+
+    return GrRRectEffect::Create(rrect);
+}
diff --git a/src/gpu/effects/GrRRectEffect.h b/src/gpu/effects/GrRRectEffect.h
new file mode 100644
index 0000000..3d899e2
--- /dev/null
+++ b/src/gpu/effects/GrRRectEffect.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrRRectEffect_DEFINED
+#define GrRRectEffect_DEFINED
+
+#include "GrEffect.h"
+
+#include "SkRRect.h"
+
+class GrGLRRectEffect;
+
+/**
+ * An effect that performs anti-aliasing for an SkRRect. It doesn't support all varieties of SkRRect
+ * so the caller must check for a NULL return from the Create() method.
+ */
+class GrRRectEffect : public GrEffect {
+public:
+    static GrEffectRef* Create(const SkRRect&);
+
+    virtual ~GrRRectEffect();
+    static const char* Name() { return "RRect"; }
+
+    const SkRRect& getRRect() const { return fRRect; }
+
+    typedef GrGLRRectEffect GLEffect;
+
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
+
+    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
+
+private:
+    GrRRectEffect(const SkRRect&);
+
+    virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE;
+
+    SkRRect fRRect;
+
+    GR_DECLARE_EFFECT_TEST;
+
+    typedef GrEffect INHERITED;
+};
+
+
+#endif