Implement drawDRRect for GPU
authorcommit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 9 Apr 2014 21:26:11 +0000 (21:26 +0000)
committercommit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 9 Apr 2014 21:26:11 +0000 (21:26 +0000)
BUG=skia:2259
R=jvanverth@google.com

Author: bsalomon@google.com

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

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

expectations/gm/ignored-tests.txt
include/gpu/GrContext.h
include/gpu/SkGpuDevice.h
src/gpu/GrContext.cpp
src/gpu/GrOvalRenderer.cpp
src/gpu/GrOvalRenderer.h
src/gpu/SkGpuDevice.cpp
src/gpu/effects/GrOvalEffect.cpp

index 2451f43be17fdcad2f90a0539dbee058268d61bd..52a595819b72da86484456a6d3a2d5a25f77b158 100644 (file)
@@ -59,3 +59,7 @@ canvas-layer-state
 # yunchao: https://codereview.chromium.org/204143004/
 # This change add some cases to bitmapshader to avoid potential drawing error for kA8_Config
 bitmapshaders
+
+# bsalomon: This image will change on the GPU due to an accelerated drawDRRect() implementation.
+# https://codereview.chromium.org/220233011/
+drrect
\ No newline at end of file
index b625389b67a6b543c271532f5153f471bfec25a0..c88f469ca0116782790cc3c08a905ba35f23c378 100644 (file)
@@ -457,9 +457,19 @@ public:
      *  @param rrect        the roundrect to draw
      *  @param stroke       the stroke information (width, join, cap)
      */
-    void drawRRect(const GrPaint& paint,
-                   const SkRRect& rrect,
-                   const SkStrokeRec& stroke);
+    void drawRRect(const GrPaint& paint, const SkRRect& rrect, const SkStrokeRec& stroke);
+
+    /**
+     *  Shortcut for drawing an SkPath consisting of nested rrects using a paint.
+     *  Does not support stroking. The result is undefined if outer does not contain
+     *  inner.
+     *
+     *  @param paint        describes how to color pixels.
+     *  @param outer        the outer roundrect
+     *  @param inner        the inner roundrect
+     */
+    void drawDRRect(const GrPaint& paint, const SkRRect& outer, const SkRRect& inner);
+
 
     /**
      * Draws a path.
index d7d797a1020730ea2b9ea1ab834fbce9decafa11..a8231914e4a40cfba9df3362937bfd8d057961b3 100644 (file)
@@ -91,6 +91,8 @@ public:
                           const SkPaint& paint) SK_OVERRIDE;
     virtual void drawRRect(const SkDraw&, const SkRRect& r,
                            const SkPaint& paint) SK_OVERRIDE;
+    virtual void drawDRRect(const SkDraw& draw, const SkRRect& outer,
+                            const SkRRect& inner, const SkPaint& paint) SK_OVERRIDE;
     virtual void drawOval(const SkDraw&, const SkRect& oval,
                           const SkPaint& paint) SK_OVERRIDE;
     virtual void drawPath(const SkDraw&, const SkPath& path,
index 89548ab5c956ee4fd066cef2774746ef8264de18..90bf8c042e498cc2e1ec3f2547f1ea586a919b51 100644 (file)
@@ -1002,9 +1002,9 @@ void GrContext::drawVertices(const GrPaint& paint,
 ///////////////////////////////////////////////////////////////////////////////
 
 void GrContext::drawRRect(const GrPaint& paint,
-                          const SkRRect& rect,
+                          const SkRRect& rrect,
                           const SkStrokeRec& stroke) {
-    if (rect.isEmpty()) {
+    if (rrect.isEmpty()) {
        return;
     }
 
@@ -1014,15 +1014,41 @@ void GrContext::drawRRect(const GrPaint& paint,
 
     GR_CREATE_TRACE_MARKER("GrContext::drawRRect", target);
 
-    if (!fOvalRenderer->drawSimpleRRect(target, this, paint.isAntiAlias(), rect, stroke)) {
+    if (!fOvalRenderer->drawRRect(target, this, paint.isAntiAlias(), rrect, stroke)) {
         SkPath path;
-        path.addRRect(rect);
+        path.addRRect(rrect);
         this->internalDrawPath(target, paint.isAntiAlias(), path, stroke);
     }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
+void GrContext::drawDRRect(const GrPaint& paint,
+                           const SkRRect& outer,
+                           const SkRRect& inner) {
+    if (outer.isEmpty()) {
+       return;
+    }
+
+    AutoRestoreEffects are;
+    AutoCheckFlush acf(this);
+    GrDrawTarget* target = this->prepareToDraw(&paint, BUFFERED_DRAW, &are, &acf);
+
+    GR_CREATE_TRACE_MARKER("GrContext::drawDRRect", target);
+
+    if (!fOvalRenderer->drawDRRect(target, this, paint.isAntiAlias(), outer, inner)) {
+        SkPath path;
+        path.addRRect(inner);
+        path.addRRect(outer);
+        path.setFillType(SkPath::kEvenOdd_FillType);
+
+        SkStrokeRec fillRec(SkStrokeRec::kFill_InitStyle);
+        this->internalDrawPath(target, paint.isAntiAlias(), path, fillRec);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
 void GrContext::drawOval(const GrPaint& paint,
                          const SkRect& oval,
                          const SkStrokeRec& stroke) {
index 2ade8cde07cad184f44c48bd34d6e27d243249ac..45564bcce049c8c3cbf457d39da9a1efe3cd702f 100644 (file)
 
 #include "SkRRect.h"
 #include "SkStrokeRec.h"
+#include "SkTLazy.h"
 
 #include "effects/GrVertexEffect.h"
+#include "effects/GrRRectEffect.h"
 
 namespace {
 
@@ -519,12 +521,14 @@ void GrOvalRenderer::drawCircle(GrDrawTarget* target,
     CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices());
 
     SkStrokeRec::Style style = stroke.getStyle();
-    bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
+    bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
+                        SkStrokeRec::kHairline_Style == style;
+    bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
 
     SkScalar innerRadius = 0.0f;
     SkScalar outerRadius = radius;
     SkScalar halfWidth = 0;
-    if (style != SkStrokeRec::kFill_Style) {
+    if (hasStroke) {
         if (SkScalarNearlyZero(strokeWidth)) {
             halfWidth = SK_ScalarHalf;
         } else {
@@ -532,12 +536,12 @@ void GrOvalRenderer::drawCircle(GrDrawTarget* target,
         }
 
         outerRadius += halfWidth;
-        if (isStroked) {
+        if (isStrokeOnly) {
             innerRadius = radius - halfWidth;
         }
     }
 
-    GrEffectRef* effect = CircleEdgeEffect::Create(isStroked && innerRadius > 0);
+    GrEffectRef* effect = CircleEdgeEffect::Create(isStrokeOnly && innerRadius > 0);
     static const int kCircleEdgeAttrIndex = 1;
     drawState->addCoverageEffect(effect, kCircleEdgeAttrIndex)->unref();
 
@@ -626,11 +630,13 @@ bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
     scaledStroke.fY = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMSkewX] + vm[SkMatrix::kMScaleY]));
 
     SkStrokeRec::Style style = stroke.getStyle();
-    bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
+    bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
+                        SkStrokeRec::kHairline_Style == style;
+    bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
 
     SkScalar innerXRadius = 0;
     SkScalar innerYRadius = 0;
-    if (SkStrokeRec::kFill_Style != style) {
+    if (hasStroke) {
         if (SkScalarNearlyZero(scaledStroke.length())) {
             scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
         } else {
@@ -650,7 +656,7 @@ bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
         }
 
         // this is legit only if scale & translation (which should be the case at the moment)
-        if (isStroked) {
+        if (isStrokeOnly) {
             innerXRadius = xRadius - scaledStroke.fX;
             innerYRadius = yRadius - scaledStroke.fY;
         }
@@ -675,7 +681,7 @@ bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
 
     EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices());
 
-    GrEffectRef* effect = EllipseEdgeEffect::Create(isStroked &&
+    GrEffectRef* effect = EllipseEdgeEffect::Create(isStrokeOnly &&
                                                     innerXRadius > 0 && innerYRadius > 0);
 
     static const int kEllipseCenterAttrIndex = 1;
@@ -877,9 +883,39 @@ GrIndexBuffer* GrOvalRenderer::rRectIndexBuffer(GrGpu* gpu) {
     return fRRectIndexBuffer;
 }
 
-bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, bool useAA,
-                                     const SkRRect& rrect, const SkStrokeRec& stroke)
-{
+bool GrOvalRenderer::drawDRRect(GrDrawTarget* target, GrContext* context, bool useAA,
+                                const SkRRect& outer, const SkRRect& origInner) {
+    GrDrawState::AutoRestoreEffects are;
+    if (!origInner.isEmpty()) {
+        SkTCopyOnFirstWrite<SkRRect> inner(origInner);
+        if (!context->getMatrix().isIdentity()) {
+            if (!origInner.transform(context->getMatrix(), inner.writable())) {
+                return false;
+            }
+        }
+        bool applyAA = useAA &&
+                       !target->getDrawState().getRenderTarget()->isMultisampled() &&
+                       !target->shouldDisableCoverageAAForBlend();
+        GrEffectEdgeType edgeType = applyAA ? kInverseFillAA_GrEffectEdgeType :
+                                              kInverseFillBW_GrEffectEdgeType;
+        GrEffectRef* effect = GrRRectEffect::Create(edgeType, *inner);
+        if (NULL == effect) {
+            return false;
+        }
+        are.set(target->drawState());
+        target->drawState()->addCoverageEffect(effect)->unref();
+    }
+
+    SkStrokeRec fillRec(SkStrokeRec::kFill_InitStyle);
+    return this->drawRRect(target, context, useAA, outer, fillRec);
+}
+
+bool GrOvalRenderer::drawRRect(GrDrawTarget* target, GrContext* context, bool useAA,
+                               const SkRRect& rrect, const SkStrokeRec& stroke) {
+    if (rrect.isOval()) {
+        return this->drawOval(target, context, useAA, rrect.getBounds(), stroke);
+    }
+
     bool useCoverageAA = useAA &&
         !target->getDrawState().getRenderTarget()->isMultisampled() &&
         !target->shouldDisableCoverageAAForBlend();
@@ -890,12 +926,10 @@ bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, b
     }
 
     const SkMatrix& vm = context->getMatrix();
-#ifdef SK_DEBUG
-    {
-        // we should have checked for this previously
-        SkASSERT(useCoverageAA && vm.rectStaysRect() && rrect.isSimple());
+
+    if (!vm.rectStaysRect() || !rrect.isSimple()) {
+        return false;
     }
-#endif
 
     // do any matrix crunching before we reset the draw state for device coords
     const SkRect& rrectBounds = rrect.getBounds();
@@ -908,21 +942,38 @@ bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, b
     SkScalar yRadius = SkScalarAbs(vm[SkMatrix::kMSkewX]*radii.fX +
                                    vm[SkMatrix::kMScaleY]*radii.fY);
 
-    // if hairline stroke is greater than radius, we don't handle that right now
     SkStrokeRec::Style style = stroke.getStyle();
-    if (SkStrokeRec::kHairline_Style == style &&
-        (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
-        return false;
-    }
 
     // do (potentially) anisotropic mapping of stroke
     SkVector scaledStroke;
     SkScalar strokeWidth = stroke.getWidth();
-    scaledStroke.fX = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMScaleX] + vm[SkMatrix::kMSkewY]));
-    scaledStroke.fY = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMSkewX] + vm[SkMatrix::kMScaleY]));
 
-    // if half of strokewidth is greater than radius, we don't handle that right now
-    if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) {
+    bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
+                        SkStrokeRec::kHairline_Style == style;
+    bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
+
+    if (hasStroke) {
+        if (SkStrokeRec::kHairline_Style == style) {
+            scaledStroke.set(1, 1);
+        } else {
+            scaledStroke.fX = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMScaleX] +
+                                                       vm[SkMatrix::kMSkewY]));
+            scaledStroke.fY = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMSkewX] +
+                                                       vm[SkMatrix::kMScaleY]));
+        }
+
+        // if half of strokewidth is greater than radius, we don't handle that right now
+        if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) {
+            return false;
+        }
+    }
+
+    // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
+    // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
+    // patch will have fractional coverage. This only matters when the interior is actually filled.
+    // We could consider falling back to rect rendering here, since a tiny radius is
+    // indistinguishable from a square corner.
+    if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
         return false;
     }
 
@@ -933,8 +984,6 @@ bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, b
         return false;
     }
 
-    bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
-
     GrIndexBuffer* indexBuffer = this->rRectIndexBuffer(context->getGpu());
     if (NULL == indexBuffer) {
         GrPrintf("Failed to create index buffer!\n");
@@ -942,7 +991,7 @@ bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, b
     }
 
     // if the corners are circles, use the circle renderer
-    if ((!isStroked || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
+    if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
         drawState->setVertexAttribs<gCircleVertexAttribs>(SK_ARRAY_COUNT(gCircleVertexAttribs));
         SkASSERT(sizeof(CircleVertex) == drawState->getVertexSize());
 
@@ -956,23 +1005,23 @@ bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, b
         SkScalar innerRadius = 0.0f;
         SkScalar outerRadius = xRadius;
         SkScalar halfWidth = 0;
-        if (style != SkStrokeRec::kFill_Style) {
+        if (hasStroke) {
             if (SkScalarNearlyZero(scaledStroke.fX)) {
                 halfWidth = SK_ScalarHalf;
             } else {
                 halfWidth = SkScalarHalf(scaledStroke.fX);
             }
 
-            if (isStroked) {
+            if (isStrokeOnly) {
                 innerRadius = xRadius - halfWidth;
             }
             outerRadius += halfWidth;
             bounds.outset(halfWidth, halfWidth);
         }
 
-        isStroked = (isStroked && innerRadius >= 0);
+        isStrokeOnly = (isStrokeOnly && innerRadius >= 0);
 
-        GrEffectRef* effect = CircleEdgeEffect::Create(isStroked);
+        GrEffectRef* effect = CircleEdgeEffect::Create(isStrokeOnly);
         static const int kCircleEdgeAttrIndex = 1;
         drawState->addCoverageEffect(effect, kCircleEdgeAttrIndex)->unref();
 
@@ -1025,7 +1074,8 @@ bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, b
         }
 
         // drop out the middle quad if we're stroked
-        int indexCnt = isStroked ? SK_ARRAY_COUNT(gRRectIndices)-6 : SK_ARRAY_COUNT(gRRectIndices);
+        int indexCnt = isStrokeOnly ? SK_ARRAY_COUNT(gRRectIndices) - 6 :
+                                      SK_ARRAY_COUNT(gRRectIndices);
         target->setIndexSourceToBuffer(indexBuffer);
         target->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 16, indexCnt, &bounds);
 
@@ -1036,7 +1086,7 @@ bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, b
 
         SkScalar innerXRadius = 0.0f;
         SkScalar innerYRadius = 0.0f;
-        if (SkStrokeRec::kFill_Style != style) {
+        if (hasStroke) {
             if (SkScalarNearlyZero(scaledStroke.length())) {
                 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
             } else {
@@ -1056,7 +1106,7 @@ bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, b
             }
 
             // this is legit only if scale & translation (which should be the case at the moment)
-            if (isStroked) {
+            if (isStrokeOnly) {
                 innerXRadius = xRadius - scaledStroke.fX;
                 innerYRadius = yRadius - scaledStroke.fY;
             }
@@ -1066,7 +1116,7 @@ bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, b
             bounds.outset(scaledStroke.fX, scaledStroke.fY);
         }
 
-        isStroked = (isStroked && innerXRadius >= 0 && innerYRadius >= 0);
+        isStrokeOnly = (isStrokeOnly && innerXRadius >= 0 && innerYRadius >= 0);
 
         GrDrawTarget::AutoReleaseGeometry geo(target, 16, 0);
         if (!geo.succeeded()) {
@@ -1075,7 +1125,7 @@ bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, b
         }
         EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices());
 
-        GrEffectRef* effect = EllipseEdgeEffect::Create(isStroked);
+        GrEffectRef* effect = EllipseEdgeEffect::Create(isStrokeOnly);
         static const int kEllipseOffsetAttrIndex = 1;
         static const int kEllipseRadiiAttrIndex = 2;
         drawState->addCoverageEffect(effect,
@@ -1134,7 +1184,8 @@ bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, b
         }
 
         // drop out the middle quad if we're stroked
-        int indexCnt = isStroked ? SK_ARRAY_COUNT(gRRectIndices)-6 : SK_ARRAY_COUNT(gRRectIndices);
+        int indexCnt = isStrokeOnly ? SK_ARRAY_COUNT(gRRectIndices) - 6 :
+                                      SK_ARRAY_COUNT(gRRectIndices);
         target->setIndexSourceToBuffer(indexBuffer);
         target->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 16, indexCnt, &bounds);
     }
index 9653fccd722e13af45146bd13c00f79c41756601..92f6ff0cdf209a8d72ec9cf7f9b443afe7ce4120 100644 (file)
@@ -33,8 +33,10 @@ public:
 
     bool drawOval(GrDrawTarget* target, const GrContext* context, bool useAA,
                   const SkRect& oval, const SkStrokeRec& stroke);
-    bool drawSimpleRRect(GrDrawTarget* target, GrContext* context, bool useAA,
-                         const SkRRect& rrect, const SkStrokeRec& stroke);
+    bool drawRRect(GrDrawTarget* target, GrContext* context, bool useAA,
+                   const SkRRect& rrect, const SkStrokeRec& stroke);
+    bool drawDRRect(GrDrawTarget* target, GrContext* context, bool useAA,
+                    const SkRRect& outer, const SkRRect& inner);
 
 private:
     bool drawEllipse(GrDrawTarget* target, bool useCoverageAA,
index 3045782895a5b8c35fc7fb0168ca62e1177dba4e..9acc20434233c73d4080349c59773c7e922f1a53 100644 (file)
@@ -656,17 +656,7 @@ void SkGpuDevice::drawRRect(const SkDraw& draw, const SkRRect& rect,
 
     }
 
-    bool usePath = !rect.isSimple();
-    // another two reasons we might need to call drawPath...
     if (paint.getMaskFilter() || paint.getPathEffect()) {
-        usePath = true;
-    }
-    // until we can rotate rrects...
-    if (!usePath && !fContext->getMatrix().rectStaysRect()) {
-        usePath = true;
-    }
-
-    if (usePath) {
         SkPath path;
         path.addRRect(rect);
         this->drawPath(draw, path, paint, NULL, true);
@@ -676,6 +666,34 @@ void SkGpuDevice::drawRRect(const SkDraw& draw, const SkRRect& rect,
     fContext->drawRRect(grPaint, rect, stroke);
 }
 
+void SkGpuDevice::drawDRRect(const SkDraw& draw, const SkRRect& outer,
+                              const SkRRect& inner, const SkPaint& paint) {
+    SkStrokeRec stroke(paint);
+    if (stroke.isFillStyle()) {
+
+        CHECK_FOR_ANNOTATION(paint);
+        CHECK_SHOULD_DRAW(draw, false);
+
+        GrPaint grPaint;
+        if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
+            return;
+        }
+
+        if (NULL == paint.getMaskFilter() && NULL == paint.getPathEffect()) {
+            fContext->drawDRRect(grPaint, outer, inner);
+            return;
+        }
+    }
+
+    SkPath path;
+    path.addRRect(outer);
+    path.addRRect(inner);
+    path.setFillType(SkPath::kEvenOdd_FillType);
+
+    this->drawPath(draw, path, paint, NULL, true);
+}
+
+
 /////////////////////////////////////////////////////////////////////////////
 
 void SkGpuDevice::drawOval(const SkDraw& draw, const SkRect& oval,
index 97b977f7542b8a357e8700d2c406fa0cf37751e8..40870e27d2ff216ea30a852a5efa7e742fe4bdc5 100644 (file)
@@ -137,7 +137,8 @@ void GLCircleEffect::emitCode(GrGLShaderBuilder* builder,
                               const TextureSamplerArray& samplers) {
     const CircleEffect& ce = drawEffect.castEffect<CircleEffect>();
     const char *circleName;
-    // The circle uniform is (center.x, center.y, radius + 0.5)
+    // The circle uniform is (center.x, center.y, radius + 0.5) for regular fills and
+    // (... ,radius - 0.5) for inverse fills.
     fCircleUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
                                          kVec3f_GrSLType,
                                          "circle",
@@ -171,7 +172,13 @@ GrGLEffect::EffectKey GLCircleEffect::GenKey(const GrDrawEffect& drawEffect,
 void GLCircleEffect::setData(const GrGLUniformManager& uman, const GrDrawEffect& drawEffect) {
     const CircleEffect& ce = drawEffect.castEffect<CircleEffect>();
     if (ce.getRadius() != fPrevRadius || ce.getCenter() != fPrevCenter) {
-        uman.set3f(fCircleUniform, ce.getCenter().fX, ce.getCenter().fY, ce.getRadius() + 0.5f);
+        SkScalar radius = ce.getRadius();
+        if (GrEffectEdgeTypeIsInverseFill(ce.getEdgeType())) {
+            radius -= 0.5f;
+        } else {
+            radius += 0.5f;
+        }
+        uman.set3f(fCircleUniform, ce.getCenter().fX, ce.getCenter().fY, radius);
         fPrevCenter = ce.getCenter();
         fPrevRadius = ce.getRadius();
     }