Optimizations for analytic shadows.
authorJim Van Verth <jvanverth@google.com>
Fri, 28 Apr 2017 21:30:30 +0000 (17:30 -0400)
committerSkia Commit-Bot <skia-commit-bot@chromium.org>
Mon, 1 May 2017 14:03:10 +0000 (14:03 +0000)
Lots of changes here:
* Batch circle shadows with rrect shadows
* Avoid checking matrix and path conditions twice
* Remove lots of checks for 1/2 pixel radii
  (needed before to force the rrect through the
   regular GPU path)
* Fix scaling effect on ambient blur width
* Remove unused flags

Bug: skia:6119
Change-Id: If0eb78ec4d19d9f978b19bdbc3a7e558a4db2ed9
Reviewed-on: https://skia-review.googlesource.com/14654
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Jim Van Verth <jvanverth@google.com>

include/private/SkShadowFlags.h
src/effects/shadows/SkAmbientShadowMaskFilter.cpp
src/effects/shadows/SkSpotShadowMaskFilter.cpp
src/gpu/GrRenderTargetContext.cpp
src/gpu/ops/GrShadowRRectOp.cpp
src/gpu/ops/GrShadowRRectOp.h

index 0caa010..23b0f54 100644 (file)
@@ -14,12 +14,8 @@ enum SkShadowFlags {
     /** The occluding object is not opaque. Knowing that the occluder is opaque allows
     * us to cull shadow geometry behind it and improve performance. */
     kTransparentOccluder_ShadowFlag = 0x01,
-    /** Use a larger umbra for a darker shadow */
-    kLargerUmbra_ShadowFlag = 0x02,
-    /** Use a Gaussian for the edge function rather than smoothstep */
-    kGaussianEdge_ShadowFlag = 0x04,
     /** mask for all shadow flags */
-    kAll_ShadowFlag = 0x07
+    kAll_ShadowFlag = 0x01
 };
 
 #endif
index 4f4bba2..4d5bc7c 100644 (file)
@@ -187,7 +187,6 @@ bool SkAmbientShadowMaskFilterImpl::directFilterRRectMaskGPU(GrContext*,
                                                              const SkRRect& rrect,
                                                              const SkRRect& devRRect) const {
     // It's likely the caller has already done these checks, but we have to be sure.
-    // TODO: support analytic blurring of general rrect
 
     // Fast path only supports filled rrects for now.
     // TODO: fill and stroke as well.
@@ -195,52 +194,43 @@ bool SkAmbientShadowMaskFilterImpl::directFilterRRectMaskGPU(GrContext*,
         return false;
     }
     // Fast path only supports simple rrects with circular corners.
-    SkASSERT(devRRect.allCornersCircular());
-    if (!rrect.isRect() && !rrect.isOval() && !rrect.isSimple()) {
+    if (!devRRect.isRect() && !devRRect.isCircle() &&
+        (!devRRect.isSimple() || !devRRect.allCornersCircular())) {
         return false;
     }
     // Fast path only supports uniform scale.
-    SkScalar scaleFactors[2];
-    if (!viewMatrix.getMinMaxScales(scaleFactors)) {
-        // matrix is degenerate
+    if (!viewMatrix.isSimilarity()) {
         return false;
     }
-    if (scaleFactors[0] != scaleFactors[1]) {
-        return false;
-    }
-    SkScalar scaleFactor = scaleFactors[0];
-
-    // For all of these, we need to ensure we have a rrect with radius >= 0.5f in device space
-    const SkScalar minRadius = 0.5f / scaleFactor;
-    bool isRect = rrect.getSimpleRadii().fX <= minRadius;
-
-    // TODO: take flags into account when generating shadow data
+    // 1/scale
+    SkScalar scaleFactor = viewMatrix.isScaleTranslate() ?
+        SkScalarInvert(viewMatrix[SkMatrix::kMScaleX]) :
+        sk_float_rsqrt(viewMatrix[SkMatrix::kMScaleX] * viewMatrix[SkMatrix::kMScaleX] +
+                       viewMatrix[SkMatrix::kMSkewX] * viewMatrix[SkMatrix::kMSkewX]);
 
     if (fAmbientAlpha > 0.0f) {
-        SkScalar srcSpaceStrokeWidth = fOccluderHeight * kHeightFactor * kGeomFactor;
+        SkScalar devSpaceStrokeWidth = fOccluderHeight * kHeightFactor * kGeomFactor;
         const float umbraAlpha = (1.0f + SkTMax(fOccluderHeight * kHeightFactor, 0.0f));
-        const SkScalar blurWidth = srcSpaceStrokeWidth * umbraAlpha;
+        const SkScalar devSpaceAmbientBlur = devSpaceStrokeWidth * umbraAlpha;
 
-        // For the ambient rrect, we outset the offset rect by srcSpaceAmbientRadius
-        // minus half the strokeWidth to get our stroke shape.
-        SkScalar ambientPathOutset = SkTMax(srcSpaceStrokeWidth * 0.5f,
-                                            minRadius);
+        // For the ambient rrect, we outset the offset rect
+        // by half the strokeWidth to get our stroke shape.
+        SkScalar srcSpaceStrokeWidth = devSpaceStrokeWidth * scaleFactor;
+        SkScalar ambientPathOutset = 0.5f*srcSpaceStrokeWidth;
 
         SkRRect ambientRRect;
-        if (isRect) {
+        if (rrect.isRect()) {
             const SkRect temp = rrect.rect().makeOutset(ambientPathOutset, ambientPathOutset);
             ambientRRect = SkRRect::MakeRectXY(temp, ambientPathOutset, ambientPathOutset);
         } else {
              rrect.outset(ambientPathOutset, ambientPathOutset, &ambientRRect);
         }
 
-        const SkScalar devSpaceAmbientBlur = blurWidth * scaleFactor;
-
         GrPaint newPaint(paint);
         GrColor4f color = newPaint.getColor4f();
         color.fRGBA[3] *= fAmbientAlpha;
         newPaint.setColor4f(color);
-        SkStrokeRec ambientStrokeRec(SkStrokeRec::kHairline_InitStyle);
+        SkStrokeRec ambientStrokeRec(SkStrokeRec::kFill_InitStyle);
         bool transparent = SkToBool(fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag);
         ambientStrokeRec.setStrokeStyle(srcSpaceStrokeWidth, transparent);
 
@@ -280,12 +270,6 @@ void SkAmbientShadowMaskFilterImpl::toString(SkString* str) const {
         SkAddFlagToString(str,
                           SkToBool(fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag),
                           "TransparentOccluder", &needSeparator);
-        SkAddFlagToString(str,
-                          SkToBool(fFlags & SkShadowFlags::kGaussianEdge_ShadowFlag),
-                          "GaussianEdge", &needSeparator);
-        SkAddFlagToString(str,
-                          SkToBool(fFlags & SkShadowFlags::kLargerUmbra_ShadowFlag),
-                          "LargerUmbra", &needSeparator);
     } else {
         str->append("None");
     }
index 93c7e9c..8471508 100644 (file)
@@ -203,53 +203,38 @@ bool SkSpotShadowMaskFilterImpl::directFilterRRectMaskGPU(GrContext*,
                                                           const SkRRect& rrect,
                                                           const SkRRect& devRRect) const {
     // It's likely the caller has already done these checks, but we have to be sure.
-    // TODO: support analytic blurring of general rrect
 
     // Fast path only supports filled rrects for now.
     // TODO: fill and stroke as well.
     if (SkStrokeRec::kFill_Style != strokeRec.getStyle()) {
         return false;
     }
-    // Fast path only supports simple rrects with near-circular corners.
-    SkASSERT(devRRect.allCornersCircular());
-    if (!rrect.isRect() && !rrect.isOval() && !rrect.isSimple()) {
+    // Fast path only supports simple rrects with circular corners.
+    if (!devRRect.isRect() && !devRRect.isCircle() &&
+        (!devRRect.isSimple() || !devRRect.allCornersCircular())) {
         return false;
     }
     // Fast path only supports uniform scale.
-    SkScalar scaleFactors[2];
-    if (!viewMatrix.getMinMaxScales(scaleFactors)) {
-        // matrix is degenerate
+    if (!viewMatrix.isSimilarity()) {
         return false;
     }
-    if (scaleFactors[0] != scaleFactors[1]) {
-        return false;
-    }
-    SkScalar scaleFactor = scaleFactors[0];
-
-    // For all of these, we need to ensure we have a rrect with radius >= 0.5f in device space
-    const SkScalar minRadius = 0.5f / scaleFactor;
-    bool isRect = rrect.getSimpleRadii().fX <= minRadius;
-
-    // TODO: take flags into account when generating shadow data
+    // 1/scale
+    SkScalar scaleFactor = viewMatrix.isScaleTranslate() ?
+        SkScalarInvert(viewMatrix[SkMatrix::kMScaleX]) :
+        sk_float_rsqrt(viewMatrix[SkMatrix::kMScaleX] * viewMatrix[SkMatrix::kMScaleX] +
+                       viewMatrix[SkMatrix::kMSkewX] * viewMatrix[SkMatrix::kMSkewX]);
 
     if (fSpotAlpha > 0.0f) {
         float zRatio = SkTPin(fOccluderHeight / (fLightPos.fZ - fOccluderHeight), 0.0f, 0.95f);
 
         SkScalar devSpaceSpotRadius = 2.0f * fLightRadius * zRatio;
         // handle scale of radius and pad due to CTM
-        const SkScalar srcSpaceSpotRadius = devSpaceSpotRadius / scaleFactor;
-
-        SkRRect spotRRect;
-        if (isRect) {
-            spotRRect = SkRRect::MakeRectXY(rrect.rect(), minRadius, minRadius);
-        } else {
-            spotRRect = rrect;
-        }
+        const SkScalar srcSpaceSpotRadius = devSpaceSpotRadius * scaleFactor;
 
         SkRRect spotShadowRRect;
         // Compute the scale and translation for the spot shadow.
         const SkScalar scale = fLightPos.fZ / (fLightPos.fZ - fOccluderHeight);
-        spotRRect.transform(SkMatrix::MakeScale(scale, scale), &spotShadowRRect);
+        rrect.transform(SkMatrix::MakeScale(scale, scale), &spotShadowRRect);
 
         SkPoint spotOffset = SkPoint::Make(zRatio*(-fLightPos.fX), zRatio*(-fLightPos.fY));
         // Adjust for the effect of the scale.
@@ -258,8 +243,8 @@ bool SkSpotShadowMaskFilterImpl::directFilterRRectMaskGPU(GrContext*,
         // This offset is in dev space, need to transform it into source space.
         SkMatrix ctmInverse;
         if (!viewMatrix.invert(&ctmInverse)) {
+            // Since the matrix is a similarity, this should never happen, but just in case...
             SkDebugf("Matrix is degenerate. Will not render spot shadow!\n");
-            //**** TODO: this is not good
             return true;
         }
         ctmInverse.mapPoints(&spotOffset, 1);
@@ -300,8 +285,7 @@ bool SkSpotShadowMaskFilterImpl::directFilterRRectMaskGPU(GrContext*,
             if (spotShadowRRect.isOval()) {
                 spotShadowRRect = SkRRect::MakeOval(insetRect);
             } else {
-                SkScalar insetRad = SkTMax(spotShadowRRect.getSimpleRadii().fX - insetAmount,
-                                           minRadius);
+                SkScalar insetRad = spotShadowRRect.getSimpleRadii().fX - insetAmount;
                 spotShadowRRect = SkRRect::MakeRectXY(insetRect, insetRad, insetRad);
             }
             spotStrokeRec.setStrokeStyle(strokeWidth, false);
@@ -356,12 +340,6 @@ void SkSpotShadowMaskFilterImpl::toString(SkString* str) const {
         SkAddFlagToString(str,
                           SkToBool(fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag),
                           "TransparentOccluder", &needSeparator);
-        SkAddFlagToString(str,
-                          SkToBool(fFlags & SkShadowFlags::kGaussianEdge_ShadowFlag),
-                          "GaussianEdge", &needSeparator);
-        SkAddFlagToString(str,
-                          SkToBool(fFlags & SkShadowFlags::kLargerUmbra_ShadowFlag),
-                          "LargerUmbra", &needSeparator);
     } else {
         str->append("None");
     }
index 41fa6dc..6e72441 100644 (file)
@@ -1000,9 +1000,8 @@ void GrRenderTargetContext::drawShadowRRect(const GrClip& clip,
     const SkStrokeRec stroke = style.strokeRec();
     // TODO: add instancing support?
 
-    const GrShaderCaps* shaderCaps = fContext->caps()->shaderCaps();
-    std::unique_ptr<GrLegacyMeshDrawOp> op = GrShadowRRectOp::Make(
-            paint.getColor(), viewMatrix, rrect, blurRadius, stroke, shaderCaps);
+    std::unique_ptr<GrLegacyMeshDrawOp> op = GrShadowRRectOp::Make(paint.getColor(), viewMatrix,
+                                                                   rrect, blurRadius, stroke);
     if (op) {
         GrPipelineBuilder pipelineBuilder(std::move(paint), GrAAType::kNone);
         this->addLegacyMeshDrawOp(std::move(pipelineBuilder), clip, std::move(op));
index 5b172f7..4c2c322 100644 (file)
@@ -15,7 +15,8 @@
 #include "effects/GrShadowGeoProc.h"
 
 ///////////////////////////////////////////////////////////////////////////////
-
+// Circle Data
+//
 // We have two possible cases for geometry for a circle:
 
 // In the case of a normal fill, we draw geometry for the circle as an octagon.
@@ -62,319 +63,8 @@ static const uint16_t* circle_type_to_indices(bool stroked) {
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-
-class ShadowCircleOp final : public GrLegacyMeshDrawOp {
-public:
-    DEFINE_OP_CLASS_ID
-
-    static std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
-                                                    SkPoint center, SkScalar radius,
-                                                    SkScalar blurRadius, const GrStyle& style) {
-        SkASSERT(viewMatrix.isSimilarity());
-        const SkStrokeRec& stroke = style.strokeRec();
-        if (style.hasPathEffect()) {
-            return nullptr;
-        }
-        SkStrokeRec::Style recStyle = stroke.getStyle();
-
-        viewMatrix.mapPoints(&center, 1);
-        radius = viewMatrix.mapRadius(radius);
-        SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
-
-        bool isStrokeOnly =
-                SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
-        bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
-
-        SkScalar innerRadius = -SK_ScalarHalf;
-        SkScalar outerRadius = radius;
-        SkScalar halfWidth = 0;
-        if (hasStroke) {
-            if (SkScalarNearlyZero(strokeWidth)) {
-                halfWidth = SK_ScalarHalf;
-            } else {
-                halfWidth = SkScalarHalf(strokeWidth);
-            }
-
-            outerRadius += halfWidth;
-            if (isStrokeOnly) {
-                innerRadius = radius - halfWidth;
-            }
-        }
-
-        bool stroked = isStrokeOnly && innerRadius > 0.0f;
-        std::unique_ptr<ShadowCircleOp> op(new ShadowCircleOp());
-        op->fViewMatrixIfUsingLocalCoords = viewMatrix;
-
-        SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
-                                            center.fX + outerRadius, center.fY + outerRadius);
-
-        op->fCircles.emplace_back(
-                Circle{color, outerRadius, innerRadius, blurRadius, devBounds, stroked});
-
-        // Use the original radius and stroke radius for the bounds so that it does not include the
-        // AA bloat.
-        radius += halfWidth;
-        op->setBounds(
-                {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
-                HasAABloat::kNo, IsZeroArea::kNo);
-        op->fVertCount = circle_type_to_vert_count(stroked);
-        op->fIndexCount = circle_type_to_index_count(stroked);
-        return std::move(op);
-    }
-
-    const char* name() const override { return "ShadowCircleOp"; }
-
-    SkString dumpInfo() const override {
-        SkString string;
-        for (int i = 0; i < fCircles.count(); ++i) {
-            string.appendf(
-                    "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
-                    "OuterRad: %.2f, InnerRad: %.2f, BlurRad: %.2f\n",
-                    fCircles[i].fColor, fCircles[i].fDevBounds.fLeft, fCircles[i].fDevBounds.fTop,
-                    fCircles[i].fDevBounds.fRight, fCircles[i].fDevBounds.fBottom,
-                    fCircles[i].fOuterRadius, fCircles[i].fInnerRadius, fCircles[i].fBlurRadius);
-        }
-        string.append(DumpPipelineInfo(*this->pipeline()));
-        string.append(INHERITED::dumpInfo());
-        return string;
-    }
-
-private:
-    ShadowCircleOp() : INHERITED(ClassID()) {}
-
-    void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
-                                    GrProcessorAnalysisCoverage* coverage) const override {
-        color->setToConstant(fCircles[0].fColor);
-        *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
-    }
-
-    void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
-        optimizations.getOverrideColorIfSet(&fCircles[0].fColor);
-        if (!optimizations.readsLocalCoords()) {
-            fViewMatrixIfUsingLocalCoords.reset();
-        }
-    }
-
-    void onPrepareDraws(Target* target) const override {
-        SkMatrix localMatrix;
-        if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
-            return;
-        }
-
-        // Setup geometry processor
-        sk_sp<GrGeometryProcessor> gp(GrRRectShadowGeoProc::Make(localMatrix));
-
-        struct CircleVertex {
-            SkPoint fPos;
-            GrColor fColor;
-            SkPoint fOffset;
-            SkScalar fDistanceCorrection;
-        };
-
-        int instanceCount = fCircles.count();
-        size_t vertexStride = gp->getVertexStride();
-        SkASSERT(vertexStride == sizeof(CircleVertex));
-
-        const GrBuffer* vertexBuffer;
-        int firstVertex;
-        char* vertices = (char*)target->makeVertexSpace(vertexStride, fVertCount, &vertexBuffer,
-                                                        &firstVertex);
-        if (!vertices) {
-            SkDebugf("Could not allocate vertices\n");
-            return;
-        }
-
-        const GrBuffer* indexBuffer = nullptr;
-        int firstIndex = 0;
-        uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
-        if (!indices) {
-            SkDebugf("Could not allocate indices\n");
-            return;
-        }
-
-        int currStartVertex = 0;
-        for (int i = 0; i < instanceCount; i++) {
-            const Circle& circle = fCircles[i];
-
-            GrColor color = circle.fColor;
-            SkScalar outerRadius = circle.fOuterRadius;
-            SkScalar innerRadius = circle.fInnerRadius;
-            SkScalar blurRadius = circle.fBlurRadius;
-            SkScalar distanceCorrection = outerRadius / blurRadius;
-
-            const SkRect& bounds = circle.fDevBounds;
-            CircleVertex* ov0 = reinterpret_cast<CircleVertex*>(vertices + 0 * vertexStride);
-            CircleVertex* ov1 = reinterpret_cast<CircleVertex*>(vertices + 1 * vertexStride);
-            CircleVertex* ov2 = reinterpret_cast<CircleVertex*>(vertices + 2 * vertexStride);
-            CircleVertex* ov3 = reinterpret_cast<CircleVertex*>(vertices + 3 * vertexStride);
-            CircleVertex* ov4 = reinterpret_cast<CircleVertex*>(vertices + 4 * vertexStride);
-            CircleVertex* ov5 = reinterpret_cast<CircleVertex*>(vertices + 5 * vertexStride);
-            CircleVertex* ov6 = reinterpret_cast<CircleVertex*>(vertices + 6 * vertexStride);
-            CircleVertex* ov7 = reinterpret_cast<CircleVertex*>(vertices + 7 * vertexStride);
-
-            // The inner radius in the vertex data must be specified in normalized space.
-            innerRadius = innerRadius / outerRadius;
-
-            SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
-            SkScalar halfWidth = 0.5f * bounds.width();
-            SkScalar octOffset = 0.41421356237f;  // sqrt(2) - 1
-
-            ov0->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
-            ov0->fColor = color;
-            ov0->fOffset = SkPoint::Make(-octOffset, -1);
-            ov0->fDistanceCorrection = distanceCorrection;
-
-            ov1->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
-            ov1->fColor = color;
-            ov1->fOffset = SkPoint::Make(octOffset, -1);
-            ov1->fDistanceCorrection = distanceCorrection;
-
-            ov2->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
-            ov2->fColor = color;
-            ov2->fOffset = SkPoint::Make(1, -octOffset);
-            ov2->fDistanceCorrection = distanceCorrection;
-
-            ov3->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
-            ov3->fColor = color;
-            ov3->fOffset = SkPoint::Make(1, octOffset);
-            ov3->fDistanceCorrection = distanceCorrection;
-
-            ov4->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
-            ov4->fColor = color;
-            ov4->fOffset = SkPoint::Make(octOffset, 1);
-            ov4->fDistanceCorrection = distanceCorrection;
-
-            ov5->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
-            ov5->fColor = color;
-            ov5->fOffset = SkPoint::Make(-octOffset, 1);
-            ov5->fDistanceCorrection = distanceCorrection;
-
-            ov6->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
-            ov6->fColor = color;
-            ov6->fOffset = SkPoint::Make(-1, octOffset);
-            ov6->fDistanceCorrection = distanceCorrection;
-
-            ov7->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
-            ov7->fColor = color;
-            ov7->fOffset = SkPoint::Make(-1, -octOffset);
-            ov7->fDistanceCorrection = distanceCorrection;
-
-            if (circle.fStroked) {
-                // compute the inner ring
-                CircleVertex* iv0 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
-                CircleVertex* iv1 = reinterpret_cast<CircleVertex*>(vertices + 9 * vertexStride);
-                CircleVertex* iv2 = reinterpret_cast<CircleVertex*>(vertices + 10 * vertexStride);
-                CircleVertex* iv3 = reinterpret_cast<CircleVertex*>(vertices + 11 * vertexStride);
-                CircleVertex* iv4 = reinterpret_cast<CircleVertex*>(vertices + 12 * vertexStride);
-                CircleVertex* iv5 = reinterpret_cast<CircleVertex*>(vertices + 13 * vertexStride);
-                CircleVertex* iv6 = reinterpret_cast<CircleVertex*>(vertices + 14 * vertexStride);
-                CircleVertex* iv7 = reinterpret_cast<CircleVertex*>(vertices + 15 * vertexStride);
-
-                // cosine and sine of pi/8
-                SkScalar c = 0.923579533f;
-                SkScalar s = 0.382683432f;
-                SkScalar r = circle.fInnerRadius;
-
-                iv0->fPos = center + SkPoint::Make(-s * r, -c * r);
-                iv0->fColor = color;
-                iv0->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
-                iv0->fDistanceCorrection = distanceCorrection;
-
-                iv1->fPos = center + SkPoint::Make(s * r, -c * r);
-                iv1->fColor = color;
-                iv1->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
-                iv1->fDistanceCorrection = distanceCorrection;
-
-                iv2->fPos = center + SkPoint::Make(c * r, -s * r);
-                iv2->fColor = color;
-                iv2->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
-                iv2->fDistanceCorrection = distanceCorrection;
-
-                iv3->fPos = center + SkPoint::Make(c * r, s * r);
-                iv3->fColor = color;
-                iv3->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
-                iv3->fDistanceCorrection = distanceCorrection;
-
-                iv4->fPos = center + SkPoint::Make(s * r, c * r);
-                iv4->fColor = color;
-                iv4->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
-                iv4->fDistanceCorrection = distanceCorrection;
-
-                iv5->fPos = center + SkPoint::Make(-s * r, c * r);
-                iv5->fColor = color;
-                iv5->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
-                iv5->fDistanceCorrection = distanceCorrection;
-
-                iv6->fPos = center + SkPoint::Make(-c * r, s * r);
-                iv6->fColor = color;
-                iv6->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
-                iv6->fDistanceCorrection = distanceCorrection;
-
-                iv7->fPos = center + SkPoint::Make(-c * r, -s * r);
-                iv7->fColor = color;
-                iv7->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
-                iv7->fDistanceCorrection = distanceCorrection;
-            } else {
-                // filled
-                CircleVertex* iv = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
-                iv->fPos = center;
-                iv->fColor = color;
-                iv->fOffset = SkPoint::Make(0, 0);
-                iv->fDistanceCorrection = distanceCorrection;
-            }
-
-            const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
-            const int primIndexCount = circle_type_to_index_count(circle.fStroked);
-            for (int i = 0; i < primIndexCount; ++i) {
-                *indices++ = primIndices[i] + currStartVertex;
-            }
-
-            currStartVertex += circle_type_to_vert_count(circle.fStroked);
-            vertices += circle_type_to_vert_count(circle.fStroked) * vertexStride;
-        }
-
-        GrMesh mesh;
-        mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
-                         firstIndex, fVertCount, fIndexCount);
-        target->draw(gp.get(), this->pipeline(), mesh);
-    }
-
-    bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
-        ShadowCircleOp* that = t->cast<ShadowCircleOp>();
-        if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
-                                    that->bounds(), caps)) {
-            return false;
-        }
-
-        if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
-            return false;
-        }
-
-        fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
-        this->joinBounds(*that);
-        fVertCount += that->fVertCount;
-        fIndexCount += that->fIndexCount;
-        return true;
-    }
-
-    struct Circle {
-        GrColor fColor;
-        SkScalar fOuterRadius;
-        SkScalar fInnerRadius;
-        SkScalar fBlurRadius;
-        SkRect fDevBounds;
-        bool fStroked;
-    };
-
-    SkSTArray<1, Circle, true> fCircles;
-    SkMatrix fViewMatrixIfUsingLocalCoords;
-    int fVertCount;
-    int fIndexCount;
-
-    typedef GrLegacyMeshDrawOp INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
+// RoundRect Data
+//
 // The geometry for a shadow roundrect is similar to a 9-patch:
 //    ____________
 //   |_|________|_|
@@ -492,6 +182,8 @@ static const uint16_t* rrect_type_to_indices(RRectType type) {
     return nullptr;
 }
 
+///////////////////////////////////////////////////////////////////////////////
+
 class ShadowCircularRRectOp final : public GrLegacyMeshDrawOp {
 public:
     DEFINE_OP_CLASS_ID
@@ -499,21 +191,23 @@ public:
     // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
     // whether the rrect is only stroked or stroked and filled.
     ShadowCircularRRectOp(GrColor color, const SkMatrix& viewMatrix, const SkRect& devRect,
-                          float devRadius, float blurRadius, float devStrokeWidth, bool strokeOnly)
+                          float devRadius, bool isCircle, float blurRadius,
+                          float devStrokeWidth, bool strokeOnly)
             : INHERITED(ClassID()), fViewMatrixIfUsingLocalCoords(viewMatrix) {
         SkRect bounds = devRect;
-        SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
-        SkScalar overStrokeRadius = 0.0f;
+        SkASSERT(devStrokeWidth > 0 || !strokeOnly);
+        SkScalar innerRadius = 0.0f;
         SkScalar outerRadius = devRadius;
-        SkScalar umbraInset = SkTMax(outerRadius, blurRadius);
-        SkScalar halfWidth = 0;
+        SkScalar umbraInset;
+        if (isCircle) {
+            umbraInset = 0;
+        } else {
+            umbraInset = SkTMax(outerRadius, blurRadius);
+        }
+
         RRectType type = kFill_RRectType;
         if (devStrokeWidth > 0) {
-            if (SkScalarNearlyZero(devStrokeWidth)) {
-                halfWidth = SK_ScalarHalf;
-            } else {
-                halfWidth = SkScalarHalf(devStrokeWidth);
-            }
+            SkScalar halfWidth = SkScalarHalf(devStrokeWidth);
             outerRadius += halfWidth;
             bounds.outset(halfWidth, halfWidth);
 
@@ -526,22 +220,31 @@ public:
             if (strokeOnly) {
                 // If stroke is greater than width or height, this is still a fill,
                 // otherwise we compute stroke params.
-                if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
-                    // We don't worry about an inner radius, we just need to know if we
-                    // need to create overstroke vertices.
-                    overStrokeRadius = SkTMax(devStrokeWidth - umbraInset,
-                                              0.0f);
-                    type = overStrokeRadius > 0 ? kOverstroke_RRectType : kStroke_RRectType;
+                if (isCircle) {
+                    innerRadius = devRadius - halfWidth;
+                    type = innerRadius > 0 ? kStroke_RRectType : kFill_RRectType;
+                } else {
+                    if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
+                        // We don't worry about a real inner radius, we just need to know if we
+                        // need to create overstroke vertices.
+                        innerRadius = SkTMax(devStrokeWidth - umbraInset, 0.0f);
+                        type = innerRadius > 0 ? kOverstroke_RRectType : kStroke_RRectType;
+                    }
                 }
             }
         }
 
         this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
 
-        fGeoData.emplace_back(Geometry{color, outerRadius, umbraInset, overStrokeRadius,
-                                       blurRadius, bounds, type});
-        fVertCount = rrect_type_to_vert_count(type);
-        fIndexCount = rrect_type_to_index_count(type);
+        fGeoData.emplace_back(Geometry{color, outerRadius, umbraInset, innerRadius,
+                                       blurRadius, bounds, type, isCircle});
+        if (isCircle) {
+            fVertCount = circle_type_to_vert_count(kStroke_RRectType == type);
+            fIndexCount = circle_type_to_index_count(kStroke_RRectType == type);
+        } else {
+            fVertCount = rrect_type_to_vert_count(type);
+            fIndexCount = rrect_type_to_index_count(type);
+        }
     }
 
     const char* name() const override { return "ShadowCircularRRectOp"; }
@@ -551,11 +254,11 @@ public:
         for (int i = 0; i < fGeoData.count(); ++i) {
             string.appendf(
                     "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
-                    "OuterRad: %.2f, Umbra: %.2f, Overstroke: %.2f, BlurRad: %.2f\n",
+                    "OuterRad: %.2f, Umbra: %.2f, InnerRad: %.2f, BlurRad: %.2f\n",
                     fGeoData[i].fColor, fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
                     fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
                     fGeoData[i].fOuterRadius, fGeoData[i].fUmbraInset,
-                    fGeoData[i].fOverstroke, fGeoData[i].fBlurRadius);
+                    fGeoData[i].fInnerRadius, fGeoData[i].fBlurRadius);
         }
         string.append(DumpPipelineInfo(*this->pipeline()));
         string.append(INHERITED::dumpInfo());
@@ -576,6 +279,17 @@ private:
         }
     }
 
+    struct Geometry {
+        GrColor   fColor;
+        SkScalar  fOuterRadius;
+        SkScalar  fUmbraInset;
+        SkScalar  fInnerRadius;
+        SkScalar  fBlurRadius;
+        SkRect    fDevBounds;
+        RRectType fType;
+        bool      fIsCircle;
+    };
+
     struct CircleVertex {
         SkPoint fPos;
         GrColor fColor;
@@ -583,35 +297,254 @@ private:
         SkScalar fDistanceCorrection;
     };
 
-    static void FillInOverstrokeVerts(CircleVertex** verts, const SkRect& bounds, SkScalar inset,
-                                      GrColor color, SkScalar distanceCorrection) {
-        // TL
-        (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fTop + inset);
+    void fillInCircleVerts(const Geometry& args, bool isStroked, CircleVertex** verts) const {
+
+        GrColor color = args.fColor;
+        SkScalar outerRadius = args.fOuterRadius;
+        SkScalar innerRadius = args.fInnerRadius;
+        SkScalar blurRadius = args.fBlurRadius;
+        SkScalar distanceCorrection = outerRadius / blurRadius;
+
+        const SkRect& bounds = args.fDevBounds;
+
+        // The inner radius in the vertex data must be specified in normalized space.
+        innerRadius = innerRadius / outerRadius;
+
+        SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
+        SkScalar halfWidth = 0.5f * bounds.width();
+        SkScalar octOffset = 0.41421356237f;  // sqrt(2) - 1
+
+        (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
         (*verts)->fColor = color;
-        (*verts)->fOffset = SkPoint::Make(0, 0);
+        (*verts)->fOffset = SkPoint::Make(-octOffset, -1);
         (*verts)->fDistanceCorrection = distanceCorrection;
         (*verts)++;
 
-        // TR
-        (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fTop + inset);
+        (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
         (*verts)->fColor = color;
-        (*verts)->fOffset = SkPoint::Make(0, 0);
+        (*verts)->fOffset = SkPoint::Make(octOffset, -1);
         (*verts)->fDistanceCorrection = distanceCorrection;
         (*verts)++;
 
-        // BL
-        (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fBottom - inset);
+        (*verts)->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
         (*verts)->fColor = color;
-        (*verts)->fOffset = SkPoint::Make(0, 0);
+        (*verts)->fOffset = SkPoint::Make(1, -octOffset);
         (*verts)->fDistanceCorrection = distanceCorrection;
         (*verts)++;
 
-        // BR
-        (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fBottom - inset);
+        (*verts)->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
         (*verts)->fColor = color;
-        (*verts)->fOffset = SkPoint::Make(0, 0);
+        (*verts)->fOffset = SkPoint::Make(1, octOffset);
         (*verts)->fDistanceCorrection = distanceCorrection;
         (*verts)++;
+
+        (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
+        (*verts)->fColor = color;
+        (*verts)->fOffset = SkPoint::Make(octOffset, 1);
+        (*verts)->fDistanceCorrection = distanceCorrection;
+        (*verts)++;
+
+        (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
+        (*verts)->fColor = color;
+        (*verts)->fOffset = SkPoint::Make(-octOffset, 1);
+        (*verts)->fDistanceCorrection = distanceCorrection;
+        (*verts)++;
+
+        (*verts)->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
+        (*verts)->fColor = color;
+        (*verts)->fOffset = SkPoint::Make(-1, octOffset);
+        (*verts)->fDistanceCorrection = distanceCorrection;
+        (*verts)++;
+
+        (*verts)->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
+        (*verts)->fColor = color;
+        (*verts)->fOffset = SkPoint::Make(-1, -octOffset);
+        (*verts)->fDistanceCorrection = distanceCorrection;
+        (*verts)++;
+
+        if (isStroked) {
+            // compute the inner ring
+
+            // cosine and sine of pi/8
+            SkScalar c = 0.923579533f;
+            SkScalar s = 0.382683432f;
+            SkScalar r = args.fInnerRadius;
+
+            (*verts)->fPos = center + SkPoint::Make(-s * r, -c * r);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)++;
+
+            (*verts)->fPos = center + SkPoint::Make(s * r, -c * r);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)++;
+
+            (*verts)->fPos = center + SkPoint::Make(c * r, -s * r);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)++;
+
+            (*verts)->fPos = center + SkPoint::Make(c * r, s * r);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)++;
+
+            (*verts)->fPos = center + SkPoint::Make(s * r, c * r);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)++;
+
+            (*verts)->fPos = center + SkPoint::Make(-s * r, c * r);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)++;
+
+            (*verts)->fPos = center + SkPoint::Make(-c * r, s * r);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)++;
+
+            (*verts)->fPos = center + SkPoint::Make(-c * r, -s * r);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)++;
+        } else {
+            // filled
+            (*verts)->fPos = center;
+            (*verts)->fColor = color;
+            (*verts)->fOffset = SkPoint::Make(0, 0);
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)++;
+        }
+    }
+
+    void fillInRRectVerts(const Geometry& args, CircleVertex** verts) const {
+        GrColor color = args.fColor;
+        SkScalar outerRadius = args.fOuterRadius;
+
+        const SkRect& bounds = args.fDevBounds;
+
+        SkScalar umbraInset = args.fUmbraInset;
+        SkScalar minDim = 0.5f*SkTMin(bounds.width(), bounds.height());
+        if (umbraInset > minDim) {
+            umbraInset = minDim;
+        }
+
+        SkScalar xInner[4] = { bounds.fLeft + umbraInset, bounds.fRight - umbraInset,
+            bounds.fLeft + umbraInset, bounds.fRight - umbraInset };
+        SkScalar xMid[4] = { bounds.fLeft + outerRadius, bounds.fRight - outerRadius,
+            bounds.fLeft + outerRadius, bounds.fRight - outerRadius };
+        SkScalar xOuter[4] = { bounds.fLeft, bounds.fRight,
+            bounds.fLeft, bounds.fRight };
+        SkScalar yInner[4] = { bounds.fTop + umbraInset, bounds.fTop + umbraInset,
+            bounds.fBottom - umbraInset, bounds.fBottom - umbraInset };
+        SkScalar yMid[4] = { bounds.fTop + outerRadius, bounds.fTop + outerRadius,
+            bounds.fBottom - outerRadius, bounds.fBottom - outerRadius };
+        SkScalar yOuter[4] = { bounds.fTop, bounds.fTop,
+            bounds.fBottom, bounds.fBottom };
+
+        SkScalar blurRadius = args.fBlurRadius;
+
+        // In the case where we have to inset more for the umbra, our two triangles in the
+        // corner get skewed to a diamond rather than a square. To correct for that,
+        // we also skew the vectors we send to the shader that help define the circle.
+        // By doing so, we end up with a quarter circle in the corner rather than the
+        // elliptical curve.
+        SkVector outerVec = SkVector::Make(0.5f*(outerRadius - umbraInset), -umbraInset);
+        outerVec.normalize();
+        SkVector diagVec = SkVector::Make(outerVec.fX + outerVec.fY,
+                                          outerVec.fX + outerVec.fY);
+        diagVec *= umbraInset / (2 * umbraInset - outerRadius);
+        SkScalar distanceCorrection = umbraInset / blurRadius;
+
+        // build corner by corner
+        for (int i = 0; i < 4; ++i) {
+            // inner point
+            (*verts)->fPos = SkPoint::Make(xInner[i], yInner[i]);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = SkVector::Make(0, 0);
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)++;
+
+            // outer points
+            (*verts)->fPos = SkPoint::Make(xOuter[i], yInner[i]);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = SkVector::Make(0, -1);
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)++;
+
+            (*verts)->fPos = SkPoint::Make(xOuter[i], yMid[i]);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = outerVec;
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)++;
+
+            (*verts)->fPos = SkPoint::Make(xOuter[i], yOuter[i]);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = diagVec;
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)++;
+
+            (*verts)->fPos = SkPoint::Make(xMid[i], yOuter[i]);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = outerVec;
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)++;
+
+            (*verts)->fPos = SkPoint::Make(xInner[i], yOuter[i]);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = SkVector::Make(0, -1);
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)++;
+        }
+
+        // Add the additional vertices for overstroked rrects.
+        // Effectively this is an additional stroked rrect, with its
+        // parameters equal to those in the center of the 9-patch. This will
+        // give constant values across this inner ring.
+        if (kOverstroke_RRectType == args.fType) {
+            SkASSERT(args.fInnerRadius > 0.0f);
+
+            SkScalar inset =  umbraInset + args.fInnerRadius;
+
+            // TL
+            (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fTop + inset);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = SkPoint::Make(0, 0);
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)++;
+
+            // TR
+            (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fTop + inset);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = SkPoint::Make(0, 0);
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)++;
+
+            // BL
+            (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fBottom - inset);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = SkPoint::Make(0, 0);
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)++;
+
+            // BR
+            (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fBottom - inset);
+            (*verts)->fColor = color;
+            (*verts)->fOffset = SkPoint::Make(0, 0);
+            (*verts)->fDistanceCorrection = distanceCorrection;
+            (*verts)++;
+        }
+
     }
 
     void onPrepareDraws(Target* target) const override {
@@ -630,7 +563,6 @@ private:
 
         const GrBuffer* vertexBuffer;
         int firstVertex;
-
         CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(vertexStride, fVertCount,
                                                                      &vertexBuffer, &firstVertex);
         if (!verts) {
@@ -650,104 +582,29 @@ private:
         for (int i = 0; i < instanceCount; i++) {
             const Geometry& args = fGeoData[i];
 
-            GrColor color = args.fColor;
-            SkScalar outerRadius = args.fOuterRadius;
-
-            const SkRect& bounds = args.fDevBounds;
-
-            SkScalar umbraInset = args.fUmbraInset;
+            if (args.fIsCircle) {
+                bool isStroked = SkToBool(kStroke_RRectType == args.fType);
+                this->fillInCircleVerts(args, isStroked, &verts);
 
-            SkScalar minDim = 0.5f*SkTMin(bounds.width(), bounds.height());
-            if (umbraInset > minDim) {
-                umbraInset = minDim;
-            }
+                const uint16_t* primIndices = circle_type_to_indices(isStroked);
+                const int primIndexCount = circle_type_to_index_count(isStroked);
+                for (int i = 0; i < primIndexCount; ++i) {
+                    *indices++ = primIndices[i] + currStartVertex;
+                }
 
-            SkScalar xInner[4] = { bounds.fLeft + umbraInset, bounds.fRight - umbraInset,
-                                   bounds.fLeft + umbraInset, bounds.fRight - umbraInset };
-            SkScalar xMid[4]   = { bounds.fLeft + outerRadius, bounds.fRight - outerRadius,
-                                   bounds.fLeft + outerRadius, bounds.fRight - outerRadius};
-            SkScalar xOuter[4] = { bounds.fLeft, bounds.fRight,
-                                   bounds.fLeft, bounds.fRight };
-            SkScalar yInner[4] = { bounds.fTop + umbraInset, bounds.fTop + umbraInset,
-                                   bounds.fBottom - umbraInset, bounds.fBottom - umbraInset };
-            SkScalar yMid[4]   = { bounds.fTop + outerRadius, bounds.fTop + outerRadius,
-                                   bounds.fBottom - outerRadius, bounds.fBottom - outerRadius };
-            SkScalar yOuter[4] = { bounds.fTop, bounds.fTop,
-                                   bounds.fBottom, bounds.fBottom };
-
-            SkScalar blurRadius = args.fBlurRadius;
-
-            // In the case where we have to inset more for the umbra, our two triangles in the
-            // corner get skewed to a diamond rather than a square. To correct for that,
-            // we also skew the vectors we send to the shader that help define the circle.
-            // By doing so, we end up with a quarter circle in the corner rather than the
-            // elliptical curve.
-            SkVector outerVec = SkVector::Make(0.5f*(outerRadius - umbraInset), -umbraInset);
-            outerVec.normalize();
-            SkVector diagVec = SkVector::Make(outerVec.fX + outerVec.fY,
-                                              outerVec.fX + outerVec.fY);
-            diagVec *= umbraInset / (2 * umbraInset - outerRadius);
-            SkScalar distanceCorrection = umbraInset / blurRadius;
-
-            // build corner by corner
-            for (int i = 0; i < 4; ++i) {
-                // inner point
-                verts->fPos = SkPoint::Make(xInner[i], yInner[i]);
-                verts->fColor = color;
-                verts->fOffset = SkVector::Make(0, 0);
-                verts->fDistanceCorrection = distanceCorrection;
-                verts++;
-
-                // outer points
-                verts->fPos = SkPoint::Make(xOuter[i], yInner[i]);
-                verts->fColor = color;
-                verts->fOffset = SkVector::Make(0, -1);
-                verts->fDistanceCorrection = distanceCorrection;
-                verts++;
-
-                verts->fPos = SkPoint::Make(xOuter[i], yMid[i]);
-                verts->fColor = color;
-                verts->fOffset = outerVec;
-                verts->fDistanceCorrection = distanceCorrection;
-                verts++;
-
-                verts->fPos = SkPoint::Make(xOuter[i], yOuter[i]);
-                verts->fColor = color;
-                verts->fOffset = diagVec;
-                verts->fDistanceCorrection = distanceCorrection;
-                verts++;
-
-                verts->fPos = SkPoint::Make(xMid[i], yOuter[i]);
-                verts->fColor = color;
-                verts->fOffset = outerVec;
-                verts->fDistanceCorrection = distanceCorrection;
-                verts++;
-
-                verts->fPos = SkPoint::Make(xInner[i], yOuter[i]);
-                verts->fColor = color;
-                verts->fOffset = SkVector::Make(0, -1);
-                verts->fDistanceCorrection = distanceCorrection;
-                verts++;
-            }
+                currStartVertex += circle_type_to_vert_count(isStroked);
 
-            // Add the additional vertices for overstroked rrects.
-            // Effectively this is an additional stroked rrect, with its
-            // parameters equal to those in the center of the 9-patch. This will
-            // give constant values across this inner ring.
-            if (kOverstroke_RRectType == args.fType) {
-                SkASSERT(args.fOverstroke > 0.0f);
+            } else {
+                this->fillInRRectVerts(args, &verts);
 
-                FillInOverstrokeVerts(&verts, bounds, umbraInset + args.fOverstroke,
-                                      color, distanceCorrection);
-            }
+                const uint16_t* primIndices = rrect_type_to_indices(args.fType);
+                const int primIndexCount = rrect_type_to_index_count(args.fType);
+                for (int i = 0; i < primIndexCount; ++i) {
+                    *indices++ = primIndices[i] + currStartVertex;
+                }
 
-            const uint16_t* primIndices = rrect_type_to_indices(args.fType);
-            const int primIndexCount = rrect_type_to_index_count(args.fType);
-            for (int i = 0; i < primIndexCount; ++i) {
-                *indices++ = primIndices[i] + currStartVertex;
+                currStartVertex += rrect_type_to_vert_count(args.fType);
             }
-
-            currStartVertex += rrect_type_to_vert_count(args.fType);
         }
 
         GrMesh mesh;
@@ -774,16 +631,6 @@ private:
         return true;
     }
 
-    struct Geometry {
-        GrColor fColor;
-        SkScalar fOuterRadius;
-        SkScalar fUmbraInset;
-        SkScalar fOverstroke;
-        SkScalar fBlurRadius;
-        SkRect fDevBounds;
-        RRectType fType;
-    };
-
     SkSTArray<1, Geometry, true> fGeoData;
     SkMatrix fViewMatrixIfUsingLocalCoords;
     int fVertCount;
@@ -794,30 +641,16 @@ private:
 
 ///////////////////////////////////////////////////////////////////////////////
 
-std::unique_ptr<GrLegacyMeshDrawOp> make_shadow_circle_op(GrColor color,
-                                                          const SkMatrix& viewMatrix,
-                                                          const SkRect& oval,
-                                                          SkScalar blurRadius,
-                                                          const SkStrokeRec& stroke,
-                                                          const GrShaderCaps* shaderCaps) {
-    // we can only draw circles
-    SkScalar width = oval.width();
-    SkASSERT(SkScalarNearlyEqual(width, oval.height()) && viewMatrix.isSimilarity());
-    SkPoint center = {oval.centerX(), oval.centerY()};
-    return ShadowCircleOp::Make(color, viewMatrix, center, width / 2.f, blurRadius,
-                                GrStyle(stroke, nullptr));
-}
-
-static std::unique_ptr<GrLegacyMeshDrawOp> make_shadow_rrect_op(GrColor color,
-                                                                const SkMatrix& viewMatrix,
-                                                                const SkRRect& rrect,
-                                                                SkScalar blurRadius,
-                                                                const SkStrokeRec& stroke) {
-    SkASSERT(viewMatrix.rectStaysRect());
-    SkASSERT(rrect.isSimple());
-    SkASSERT(!rrect.isOval());
-
+namespace GrShadowRRectOp {
+std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color,
+                                         const SkMatrix& viewMatrix,
+                                         const SkRRect& rrect,
+                                         const SkScalar blurRadius,
+                                         const SkStrokeRec& stroke) {
     // Shadow rrect ops only handle simple circular rrects.
+    SkASSERT(viewMatrix.isSimilarity() &&
+             (rrect.isSimple() || rrect.isRect() || rrect.isOval()));
+
     // Do any matrix crunching before we reset the draw state for device coords.
     const SkRect& rrectBounds = rrect.getBounds();
     SkRect bounds;
@@ -830,99 +663,56 @@ static std::unique_ptr<GrLegacyMeshDrawOp> make_shadow_rrect_op(GrColor color,
                                                viewMatrix[SkMatrix::kMScaleY] * radii.fY));
     SkASSERT(SkScalarNearlyEqual(xRadius, yRadius));
 
+    // Hairline style is unexpected and meaningless with this DrawOp.
+    // It will be treated as a fill, which will make it visibly obvious that something's wrong.
     SkStrokeRec::Style style = stroke.getStyle();
+    SkASSERT(SkStrokeRec::kHairline_Style != style);
 
-    // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
-    SkVector scaledStroke = {-1, -1};
-    SkScalar strokeWidth = stroke.getWidth();
-
-    bool isStrokeOnly =
-            SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
+    // Do mapping of stroke. Use -1 to indicate fill-only draws.
+    SkScalar scaledStrokeWidth = -1;
+    bool isStrokeOnly = SkStrokeRec::kStroke_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 * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
-            scaledStroke.fY = SkScalarAbs(
-                   strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
-        }
-
-        // we don't handle anisotropic strokes
-        if (!SkScalarNearlyEqual(scaledStroke.fX, scaledStroke.fY)) {
-            return nullptr;
-        }
-    }
-
-    // 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) {
-        return nullptr;
+        SkScalar strokeWidth = stroke.getWidth();
+        // As the matrix is a similarity matrix, this should be isotropic.
+        scaledStrokeWidth = SkScalarAbs(
+                   strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewX]));
     }
 
-    return std::unique_ptr<GrLegacyMeshDrawOp>(new ShadowCircularRRectOp(
-            color, viewMatrix, bounds, xRadius, blurRadius, scaledStroke.fX, isStrokeOnly));
-}
-
-namespace GrShadowRRectOp {
-std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color,
-                                         const SkMatrix& viewMatrix,
-                                         const SkRRect& rrect,
-                                         const SkScalar blurRadius,
-                                         const SkStrokeRec& stroke,
-                                         const GrShaderCaps* shaderCaps) {
-    if (rrect.isOval()) {
-        return make_shadow_circle_op(color, viewMatrix, rrect.getBounds(), blurRadius, stroke,
-                                     shaderCaps);
-    }
-
-    if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
-        return nullptr;
-    }
-
-    return make_shadow_rrect_op(color, viewMatrix, rrect, blurRadius, stroke);
+    return std::unique_ptr<GrLegacyMeshDrawOp>(new ShadowCircularRRectOp(color, viewMatrix, bounds,
+                                                                         xRadius, rrect.isOval(),
+                                                                         blurRadius,
+                                                                         scaledStrokeWidth,
+                                                                         isStrokeOnly));
 }
 }
+
 ///////////////////////////////////////////////////////////////////////////////
 
 #if GR_TEST_UTILS
 
-DRAW_OP_TEST_DEFINE(ShadowCircleOp) {
-    do {
-        SkScalar rotate = random->nextSScalar1() * 360.f;
-        SkScalar translateX = random->nextSScalar1() * 1000.f;
-        SkScalar translateY = random->nextSScalar1() * 1000.f;
-        SkScalar scale = random->nextSScalar1() * 100.f;
-        SkMatrix viewMatrix;
-        viewMatrix.setRotate(rotate);
-        viewMatrix.postTranslate(translateX, translateY);
-        viewMatrix.postScale(scale, scale);
-        GrColor color = GrRandomColor(random);
-        SkRect circle = GrTest::TestSquare(random);
-        SkPoint center = {circle.centerX(), circle.centerY()};
-        SkScalar radius = circle.width() / 2.f;
-        SkStrokeRec stroke = GrTest::TestStrokeRec(random);
-        SkScalar blurRadius = random->nextSScalar1() * 72.f;
-        std::unique_ptr<GrLegacyMeshDrawOp> op = ShadowCircleOp::Make(
-                color, viewMatrix, center, radius, blurRadius, GrStyle(stroke, nullptr));
-        if (op) {
-            return op;
-        }
-    } while (true);
-}
-
 DRAW_OP_TEST_DEFINE(ShadowRRectOp) {
-    SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
+    // create a similarity matrix
+    SkScalar rotate = random->nextSScalar1() * 360.f;
+    SkScalar translateX = random->nextSScalar1() * 1000.f;
+    SkScalar translateY = random->nextSScalar1() * 1000.f;
+    SkScalar scale = random->nextSScalar1() * 100.f;
+    SkMatrix viewMatrix;
+    viewMatrix.setRotate(rotate);
+    viewMatrix.postTranslate(translateX, translateY);
+    viewMatrix.postScale(scale, scale);
     GrColor color = GrRandomColor(random);
-    const SkRRect& rrect = GrTest::TestRRectSimple(random);
+    SkStrokeRec stroke = GrTest::TestStrokeRec(random);
     SkScalar blurRadius = random->nextSScalar1() * 72.f;
-    return make_shadow_rrect_op(color, viewMatrix, rrect, blurRadius,
-                                GrTest::TestStrokeRec(random));
+    bool isCircle = random->nextBool();
+    if (isCircle) {
+        SkRect circle = GrTest::TestSquare(random);
+        SkRRect rrect = SkRRect::MakeOval(circle);
+        return GrShadowRRectOp::Make(color, viewMatrix, rrect, blurRadius, stroke);
+    } else {
+        const SkRRect& rrect = GrTest::TestRRectSimple(random);
+        return GrShadowRRectOp::Make(color, viewMatrix, rrect, blurRadius, stroke);
+    }
 }
 
 #endif
index 9b9993e..dec63ca 100644 (file)
@@ -20,8 +20,7 @@ class SkStrokeRec;
 namespace GrShadowRRectOp {
 
 std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor, const SkMatrix& viewMatrix, const SkRRect& rrect,
-                                         const SkScalar blurRadius, const SkStrokeRec& stroke,
-                                         const GrShaderCaps* shaderCaps);
+                                         const SkScalar blurRadius, const SkStrokeRec& stroke);
 }
 
 #endif