Revert "Consolidate quad optimizations into single internal function."

This reverts commit 646616a78fe0a003c94a27e79c7e55dcc9ae2d66.

Reason for revert: Suspected as cause of layout test changes.

Original change's description:
> Consolidate quad optimizations into single internal function.
> 
> Routes all non-textured quad draws through single internal function
> 
> Change-Id: Ief66864a0ad2d598982c5bf500c8a84ecbf84387
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/215455
> Commit-Queue: Michael Ludwig <michaelludwig@google.com>
> Reviewed-by: Robert Phillips <robertphillips@google.com>

TBR=robertphillips@google.com,michaelludwig@google.com

Change-Id: I0dc6a0d948c0f5e9221ff6c9fbbbbbb9bc3d9bc0
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/221737
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/gm/samplelocations.cpp b/gm/samplelocations.cpp
index 273d627..baec473 100644
--- a/gm/samplelocations.cpp
+++ b/gm/samplelocations.cpp
@@ -290,8 +290,9 @@
         GrPaint coverPaint;
         coverPaint.setColor4f({1,0,0,1});
         coverPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrcOver));
-        rtc->priv().stencilRect(GrNoClip(), &kStencilCover, std::move(coverPaint), GrAA::kNo,
-                                SkMatrix::I(), SkRect::MakeWH(200, 200));
+        rtc->priv().drawFilledRect(
+                GrNoClip(), std::move(coverPaint), GrAA::kNo, SkMatrix::I(),
+                SkRect::MakeWH(200, 200), &kStencilCover);
 
         // Copy offscreen texture to canvas.
         rtc->drawTexture(
diff --git a/gn/tests.gni b/gn/tests.gni
index 734c1e4..e16a550 100644
--- a/gn/tests.gni
+++ b/gn/tests.gni
@@ -106,7 +106,6 @@
   "$_tests/GrOpListFlushTest.cpp",
   "$_tests/GrPipelineDynamicStateTest.cpp",
   "$_tests/GrPorterDuffTest.cpp",
-  "$_tests/GrQuadCropTest.cpp",
   "$_tests/GrQuadListTest.cpp",
   "$_tests/GrShapeTest.cpp",
   "$_tests/GrSurfaceTest.cpp",
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index c579c1c..f10bda9 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -44,7 +44,6 @@
 #include "src/gpu/effects/GrTextureDomain.h"
 #include "src/gpu/effects/generated/GrColorMatrixFragmentProcessor.h"
 #include "src/gpu/geometry/GrQuad.h"
-#include "src/gpu/geometry/GrQuadUtils.h"
 #include "src/gpu/geometry/GrShape.h"
 #include "src/gpu/ops/GrAtlasTextOp.h"
 #include "src/gpu/ops/GrClearOp.h"
@@ -451,27 +450,67 @@
 void GrRenderTargetContext::drawPaint(const GrClip& clip,
                                       GrPaint&& paint,
                                       const SkMatrix& viewMatrix) {
-    // Start with the render target, since that is the maximum content we could possibly fill.
-    // drawFilledQuad() will automatically restrict it to clip bounds for us if possible.
+    ASSERT_SINGLE_OWNER
+    RETURN_IF_ABANDONED
+    SkDEBUGCODE(this->validate();)
+    GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext", "drawPaint", fContext);
+
+    // set rect to be big enough to fill the space, but not super-huge, so we
+    // don't overflow fixed-point implementations
+
     SkRect r = fRenderTargetProxy->getBoundsRect();
+
+    // Check if we can optimize a clipped drawPaint(). We only do the transformation when there are
+    // no fragment processors because they may depend on having correct local coords and this path
+    // draws in device space without a local matrix. It currently handles converting clipRRect()
+    // to drawRRect() and solid colors to screen-filling drawRects() (which are then converted into
+    // clears if possible in drawRect).
     if (!paint.numTotalFragmentProcessors()) {
-        // The paint is trivial so we won't need to use local coordinates, so skip calculating the
-        // inverse view matrix.
-        this->fillRectToRect(clip, std::move(paint), GrAA::kNo, SkMatrix::I(), r, r);
-    } else {
-        // Use the inverse view matrix to arrive at appropriate local coordinates for the paint.
-        SkMatrix localMatrix;
-        if (!viewMatrix.invert(&localMatrix)) {
-            return;
+        SkRRect rrect;
+        GrAA aa = GrAA::kNo;
+        if (clip.isRRect(r, &rrect, &aa)) {
+            if (rrect.isRect()) {
+                // Use drawFilledRect() with no clip and the reduced rectangle
+                this->drawFilledRect(GrNoClip(), std::move(paint), aa, SkMatrix::I(), rrect.rect());
+            } else {
+                // Use drawRRect() with no clip
+                this->drawRRect(GrNoClip(), std::move(paint), aa, SkMatrix::I(), rrect,
+                                GrStyle::SimpleFill());
+            }
+        } else {
+            // Use drawFilledRect() with no view matrix to draw a fullscreen quad, but preserve
+            // the clip. Since the paint has no FPs we can drop the view matrix without worrying
+            // about local coordinates. If the clip is simple, drawFilledRect() will turn this into
+            // a clear or a scissored clear.
+            this->drawFilledRect(clip, std::move(paint), aa, SkMatrix::I(), r);
         }
-        this->fillRectWithLocalMatrix(clip, std::move(paint), GrAA::kNo, SkMatrix::I(), r,
-                                      localMatrix);
+        return;
     }
+
+    // Since the paint is not trivial, there's no way at this point drawRect() could have converted
+    // this drawPaint() into an optimized clear. drawRect() would then use GrFillRectOp without
+    // a local matrix, so we can simplify things and use the local matrix variant to draw a screen
+    // filling rect with the inverse view matrix for local coords, which works for all matrix
+    // conditions.
+    SkMatrix localMatrix;
+    if (!viewMatrix.invert(&localMatrix)) {
+        return;
+    }
+
+    AutoCheckFlush acf(this->drawingManager());
+    std::unique_ptr<GrDrawOp> op = GrFillRectOp::Make(
+            fContext, std::move(paint), GrAAType::kNone, GrQuadAAFlags::kNone,
+            GrQuad(r), GrQuad::MakeFromRect(r, localMatrix));
+    this->addDrawOp(clip, std::move(op));
+}
+
+static inline bool rect_contains_inclusive(const SkRect& rect, const SkPoint& point) {
+    return point.fX >= rect.fLeft && point.fX <= rect.fRight &&
+           point.fY >= rect.fTop && point.fY <= rect.fBottom;
 }
 
 // Attempts to crop a rect and optional local rect to the clip boundaries.
 // Returns false if the draw can be skipped entirely.
-// FIXME to be removed once drawTexture et al are updated to use attemptQuadOptimization instead
 static bool crop_filled_rect(int width, int height, const GrClip& clip,
                              const SkMatrix& viewMatrix, SkRect* rect,
                              SkRect* localRect = nullptr) {
@@ -521,186 +560,173 @@
     return rect->intersect(clipBounds);
 }
 
-enum class GrRenderTargetContext::QuadOptimization {
-    // The rect to draw doesn't intersect clip or render target, so no draw op should be added
-    kDiscarded,
-    // The rect to draw was converted to some other op and appended to the oplist, so no additional
-    // op is necessary. Currently this can convert it to a clear op or a rrect op. Only valid if
-    // a constColor is provided.
-    kSubmitted,
-    // The clip was folded into the device quad, with updated edge flags and local coords, and
-    // caller is responsible for adding an appropriate op.
-    kClipApplied,
-    // No change to clip, but quad updated to better fit clip/render target, and caller is
-    // responsible for adding an appropriate op.
-    kCropped
-};
-
-GrRenderTargetContext::QuadOptimization GrRenderTargetContext::attemptQuadOptimization(
-        const GrClip& clip, const SkPMColor4f* constColor, bool allowAAChange,
-        GrAA* aa, GrQuadAAFlags* edgeFlags, GrQuad* deviceQuad, GrQuad* localQuad) {
-    // Optimization requirements:
-    // 1. kDiscard applies when clip bounds and quad bounds do not intersect
-    // 2. kClear applies when constColor and final geom is pixel aligned rect;
-    //       pixel aligned rect requires rect clip and (rect quad or quad covers clip)
-    // 3. kRRect applies when constColor and rrect clip and quad covers clip
-    // 4. kExplicitClip applies when rect clip and (rect quad or quad covers clip)
-    // 5. kCropped applies when rect quad (currently)
-    // 6. kNone always applies
-    GrQuadAAFlags newFlags = *edgeFlags;
-
-    // Must use worst case bounds so that stencil buffer updates on approximately sized
-    // render targets don't get corrupted.
-    const SkRect rtRect = SkRect::MakeWH(fRenderTargetProxy->worstCaseWidth(),
-                                         fRenderTargetProxy->worstCaseHeight());
-    SkRect drawBounds = deviceQuad->bounds();
-    if (constColor) {
-        // Don't bother updating local coordinates when the paint will ignore them anyways
-        localQuad = nullptr;
+GrQuadAAFlags set_edge_flag(GrQuadAAFlags currentFlags, GrQuadAAFlags edge, GrAA edgeState) {
+    if (edgeState == GrAA::kNo) {
+        // Turn off 'edge' in currentFlags
+        return currentFlags & (~edge);
+    } else {
+        // Turn on 'edge' in currentFlags
+        return currentFlags | edge;
     }
-
-    // If the quad is entirely off screen, it doesn't matter what the clip does
-    if (!rtRect.intersects(drawBounds)) {
-        return QuadOptimization::kDiscarded;
-    }
-
-    // Check if clip can be represented as a rounded rect (initialize as if clip fully contained
-    // the render target).
-    SkRRect clipRRect = SkRRect::MakeRect(rtRect);
-    GrAA clipAA = allowAAChange ? GrAA::kNo : *aa;
-    bool axisAlignedClip = true;
-    if (!clip.quickContains(rtRect)) {
-        if (!clip.isRRect(rtRect, &clipRRect, &clipAA)) {
-            axisAlignedClip = false;
-        }
-    }
-
-    // If the clip rrect is valid (i.e. axis-aligned), we can potentially combine it with the
-    // draw geometry so that no clip is needed when drawing.
-    if (axisAlignedClip && (allowAAChange || clipAA == *aa)) {
-        // Tighten clip bounds (if clipRRect.isRect() is true, clipBounds now holds the intersection
-        // of the render target and the clip rect)
-        SkRect clipBounds = rtRect;
-        if (!clipBounds.intersect(clipRRect.rect()) || !clipBounds.intersects(drawBounds)) {
-            return QuadOptimization::kDiscarded;
-        }
-
-        if (clipRRect.isRect()) {
-            // No rounded corners, so the kClear and kExplicitClip optimizations are possible
-            if (GrQuadUtils::CropToRect(clipBounds, clipAA, &newFlags, deviceQuad, localQuad)) {
-                if (constColor && deviceQuad->quadType() == GrQuad::Type::kAxisAligned) {
-                    // Clear optimization is possible
-                    drawBounds = deviceQuad->bounds();
-                    if (drawBounds.contains(rtRect)) {
-                        // Fullscreen clear
-                        this->clear(nullptr, *constColor, CanClearFullscreen::kYes);
-                        return QuadOptimization::kSubmitted;
-                    } else if (GrClip::IsPixelAligned(drawBounds) &&
-                               drawBounds.width() > 256 && drawBounds.height() > 256) {
-                        // Scissor + clear (round shouldn't do anything since we are pixel aligned)
-                        SkIRect scissorRect;
-                        drawBounds.round(&scissorRect);
-                        this->clear(&scissorRect, *constColor, CanClearFullscreen::kNo);
-                        return QuadOptimization::kSubmitted;
-                    }
-                }
-
-                // Update overall AA setting.
-                *edgeFlags = newFlags;
-                if (*aa == GrAA::kNo && clipAA == GrAA::kYes &&
-                    newFlags != GrQuadAAFlags::kNone) {
-                    // The clip was anti-aliased and now the draw needs to be upgraded to AA to
-                    // properly reflect the smooth edge of the clip.
-                    *aa = GrAA::kYes;
-                }
-                // We intentionally do not downgrade AA here because we don't know if we need to
-                // preserve MSAA (see GrQuadAAFlags docs). But later in the pipeline, the ops can
-                // use GrResolveAATypeForQuad() to turn off coverage AA when all flags are off.
-
-                // deviceQuad is exactly the intersection of original quad and clip, so it can be
-                // drawn with no clip (submitted by caller)
-                return QuadOptimization::kClipApplied;
-            } else {
-                // The quads have been updated to better fit the clip bounds, but can't get rid of
-                // the clip entirely
-                return QuadOptimization::kCropped;
-            }
-        } else if (constColor) {
-            // Rounded corners and constant filled color (limit ourselves to solid colors because
-            // there is no way to use custom local coordinates with drawRRect).
-            if (GrQuadUtils::CropToRect(clipBounds, clipAA, &newFlags, deviceQuad, localQuad) &&
-                deviceQuad->quadType() == GrQuad::Type::kAxisAligned &&
-                deviceQuad->bounds().contains(clipBounds)) {
-                // Since the cropped quad became a rectangle which covered the bounds of the rrect,
-                // we can draw the rrect directly and ignore the edge flags
-                GrPaint paint;
-                clear_to_grpaint(*constColor, &paint);
-                this->drawRRect(GrFixedClip::Disabled(), std::move(paint), clipAA, SkMatrix::I(),
-                                clipRRect, GrStyle::SimpleFill());
-                return QuadOptimization::kSubmitted;
-            } else {
-                // The quad has been updated to better fit clip bounds, but can't remove the clip
-                return QuadOptimization::kCropped;
-            }
-        }
-    }
-
-    // Crop the quad to the conservative bounds of the clip.
-    SkIRect clipDevBounds;
-    clip.getConservativeBounds(rtRect.width(), rtRect.height(), &clipDevBounds);
-    SkRect clipBounds = SkRect::Make(clipDevBounds);
-
-    // One final check for discarding, since we may have gone here directly due to a complex clip
-    if (!clipBounds.intersects(drawBounds)) {
-        return QuadOptimization::kDiscarded;
-    }
-
-    // Even if this were to return true, the crop rect does not exactly match the clip, so can not
-    // report explicit-clip. Since these edges aren't visible, don't update the final edge flags.
-    GrQuadUtils::CropToRect(clipBounds, clipAA, &newFlags, deviceQuad, localQuad);
-
-    return QuadOptimization::kCropped;
 }
 
-void GrRenderTargetContext::drawFilledQuad(const GrClip& clip,
+bool GrRenderTargetContext::drawFilledRectAsClear(const GrClip& clip, GrPaint&& paint, GrAA aa,
+                                                  const SkMatrix& viewMatrix, const SkRect& rect) {
+    // Rules for a filled rect to become a clear [+scissor]:
+    // 1. The paint is a constant blend color with no other FPs
+    // 2. The view matrix maps rectangles to rectangles, or the transformed quad fully covers
+    //    the render target (or clear region in #3).
+    // 3. The clip is an intersection of rectangles, so the clear region will be the
+    //    intersection of the clip and the provided rect.
+    // 4. The clear region aligns with pixel bounds
+    // 5. There are no user stencil settings (and since the clip was IOR, the clip won't need
+    //    to use the stencil either).
+    // If all conditions pass, the filled rect can either be a fullscreen clear (if it's big
+    // enough), or the rectangle geometry will be used as the scissor clip on the clear.
+    // If everything passes but rule #4, this submits a simplified fill rect op instead so that the
+    // rounding differences between clip and draws don't fight each other.
+    // NOTE: we route draws into clear() regardless of performColorClearsAsDraws() since the
+    // clear call is allowed to reset the oplist even when it also happens to use a GrFillRectOp.
+
+    SkPMColor4f clearColor;
+    if (paint.numCoverageFragmentProcessors() > 0 || !paint.isConstantBlendedColor(&clearColor)) {
+        return false;
+    }
+
+    const SkRect rtRect = fRenderTargetProxy->getBoundsRect();
+    // Will be the intersection of render target, clip, and quad
+    SkRect combinedRect = rtRect;
+
+    SkRRect clipRRect;
+    GrAA clipAA;
+    if (!clip.quickContains(rtRect)) {
+        // If the clip is an rrect with no rounding, then it can replace the full RT bounds as the
+        // limiting region, although we will have to worry about AA. If the clip is anything
+        // more complicated, just punt to the regular fill rect op.
+        if (!clip.isRRect(rtRect, &clipRRect, &clipAA) || !clipRRect.isRect()) {
+            return false;
+        }
+
+        combinedRect = clipRRect.rect();
+    } else {
+        // The clip is outside the render target, so the clip can be ignored
+        clipAA = GrAA::kNo;
+    }
+
+    GrQuadAAFlags edgeFlags; // To account for clip and draw mixing AA modes
+    if (viewMatrix.rectStaysRect()) {
+        // Skip the extra overhead of inverting the view matrix to see if rtRect is contained in the
+        // drawn rectangle, and instead just intersect rtRect with the transformed rect. It will be
+        // the new clear region.
+        SkRect drawRect = viewMatrix.mapRect(rect);
+        if (!combinedRect.intersect(drawRect)) {
+            // No intersection means nothing should be drawn, so return true but don't add an op
+            return true;
+        }
+
+        // In this case, edge flags start based on draw's AA and then switch per-edge to the clip's
+        // AA setting if that edge was inset.
+        edgeFlags = aa == GrAA::kNo ? GrQuadAAFlags::kNone : GrQuadAAFlags::kAll;
+        if (combinedRect.fLeft > drawRect.fLeft) {
+            edgeFlags = set_edge_flag(edgeFlags, GrQuadAAFlags::kLeft, clipAA);
+        }
+        if (combinedRect.fTop > drawRect.fTop) {
+            edgeFlags = set_edge_flag(edgeFlags, GrQuadAAFlags::kTop, clipAA);
+        }
+        if (combinedRect.fRight < drawRect.fRight) {
+            edgeFlags = set_edge_flag(edgeFlags, GrQuadAAFlags::kRight, clipAA);
+        }
+        if (combinedRect.fBottom < drawRect.fBottom) {
+            edgeFlags = set_edge_flag(edgeFlags, GrQuadAAFlags::kBottom, clipAA);
+        }
+    } else {
+        // If the transformed rectangle does not contain the combined rt and clip, the draw is too
+        // complex to be implemented as a clear
+        SkMatrix invM;
+        if (!viewMatrix.invert(&invM)) {
+            return false;
+        }
+        // The clip region in the rect's local space, so the test becomes the local rect containing
+        // the quad's points. If clip is non-AA, test rounded out region to avoid the scenario where
+        // the draw contains the unrounded non-aa clip, but does not contain the rounded version. Be
+        // conservative since we don't know how the GPU would round.
+        SkRect conservative;
+        if (clipAA == GrAA::kNo) {
+            conservative = SkRect::Make(combinedRect.roundOut());
+        } else {
+            conservative = combinedRect;
+        }
+        GrQuad quad = GrQuad::MakeFromRect(conservative, invM);
+        if (!rect_contains_inclusive(rect, quad.point(0)) ||
+            !rect_contains_inclusive(rect, quad.point(1)) ||
+            !rect_contains_inclusive(rect, quad.point(2)) ||
+            !rect_contains_inclusive(rect, quad.point(3))) {
+            // No containment, so combinedRect can't be filled by a solid color
+            return false;
+        }
+        // combinedRect can be filled by a solid color but doesn't need to be modified since it's
+        // inside the quad to be drawn, which also means the edge AA flags respect the clip AA
+        edgeFlags = clipAA == GrAA::kNo ? GrQuadAAFlags::kNone : GrQuadAAFlags::kAll;
+    }
+
+    // Almost every condition is met; now it requires that the combined rect align with pixel
+    // boundaries in order for it to become a scissor-clear. Ignore the AA status in this case
+    // since non-AA with partial-pixel coordinates can be rounded differently on the GPU,
+    // leading to unexpected differences between a scissor test and a rasterized quad.
+    // Also skip very small rectangles since the scissor+clear doesn't by us much then.
+    if (combinedRect.contains(rtRect)) {
+        // Full screen clear
+        this->clear(nullptr, clearColor, CanClearFullscreen::kYes);
+        return true;
+    } else if (GrClip::IsPixelAligned(combinedRect) &&
+               combinedRect.width() > 256 && combinedRect.height() > 256) {
+        // Scissor + clear (round shouldn't do anything since we are pixel aligned)
+        SkIRect scissorRect;
+        combinedRect.round(&scissorRect);
+        this->clear(&scissorRect, clearColor, CanClearFullscreen::kNo);
+        return true;
+    }
+
+    // If we got here, we can't use a scissor + clear, but combinedRect represents the correct
+    // geometry combination of quad + clip so we can perform a simplified fill rect op. We do this
+    // mostly to avoid mismatches in rounding logic on the CPU vs. the GPU, which frequently appears
+    // when drawing and clipping something to the same non-AA rect that never-the-less has
+    // non-integer coordinates.
+    aa = edgeFlags == GrQuadAAFlags::kNone ? GrAA::kNo : GrAA::kYes;
+    GrAAType aaType = this->chooseAAType(aa);
+    this->addDrawOp(GrFixedClip::Disabled(),
+                    GrFillRectOp::Make(fContext, std::move(paint), aaType, edgeFlags,
+                                       GrQuad(combinedRect), GrQuad(combinedRect)));
+    return true;
+}
+
+void GrRenderTargetContext::drawFilledRect(const GrClip& clip,
                                            GrPaint&& paint,
                                            GrAA aa,
-                                           GrQuadAAFlags edgeFlags,
-                                           const GrQuad& deviceQuad,
-                                           const GrQuad& localQuad,
+                                           const SkMatrix& viewMatrix,
+                                           const SkRect& rect,
                                            const GrUserStencilSettings* ss) {
-    ASSERT_SINGLE_OWNER
-    RETURN_IF_ABANDONED
-    SkDEBUGCODE(this->validate();)
-    GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext", "drawFilledQuad", fContext);
 
-    AutoCheckFlush acf(this->drawingManager());
-
-    SkPMColor4f* constColor = nullptr;
-    SkPMColor4f paintColor;
-    if (!ss && !paint.numCoverageFragmentProcessors() &&
-        paint.isConstantBlendedColor(&paintColor)) {
-        // Only consider clears/rrects when it's easy to guarantee 100% fill with single color
-        constColor = &paintColor;
+    if (!ss) {
+        if (this->drawFilledRectAsClear(clip, std::move(paint), aa, viewMatrix, rect)) {
+            return;
+        }
+        // Fall through to fill rect op
+        assert_alive(paint);
     }
 
-    GrQuad croppedDeviceQuad = deviceQuad;
-    GrQuad croppedLocalQuad = localQuad;
-    // Only allow AA settings to change if there are no stencil settings. If they were allowed to
-    // change at this stage, it would bypass higher-level decisions about stencil MSAA.
-    QuadOptimization opt = this->attemptQuadOptimization(clip, constColor, /* allowAAChange */ !ss,
-                                                         &aa, &edgeFlags, &croppedDeviceQuad,
-                                                         &croppedLocalQuad);
-    if (opt >= QuadOptimization::kClipApplied) {
-        // These optimizations require caller to add an op themselves
-        const GrClip& finalClip = opt == QuadOptimization::kClipApplied ? GrFixedClip::Disabled()
-                                                                        : clip;
-        GrAAType aaType = ss ? (aa == GrAA::kYes ? GrAAType::kMSAA : GrAAType::kNone)
-                             : this->chooseAAType(aa);
-        this->addDrawOp(finalClip, GrFillRectOp::Make(fContext, std::move(paint), aaType, edgeFlags,
-                                                      croppedDeviceQuad, croppedLocalQuad, ss));
+    SkRect croppedRect = rect;
+    if (!crop_filled_rect(this->width(), this->height(), clip, viewMatrix, &croppedRect)) {
+        // The rectangle would not be drawn, so no need to add a draw op to the list
+        return;
     }
-    // All other optimization levels were completely handled inside attempt(), so no extra op needed
+
+    GrAAType aaType = this->chooseAAType(aa);
+    GrQuadAAFlags edgeFlags = aa == GrAA::kYes ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone;
+    this->addDrawOp(clip,
+                    GrFillRectOp::Make(fContext, std::move(paint), aaType, edgeFlags,
+                                       GrQuad::MakeFromRect(croppedRect, viewMatrix),
+                                       GrQuad(croppedRect), ss));
 }
 
 void GrRenderTargetContext::drawRect(const GrClip& clip,
@@ -724,8 +750,7 @@
 
     const SkStrokeRec& stroke = style->strokeRec();
     if (stroke.getStyle() == SkStrokeRec::kFill_Style) {
-        // Fills the rect, using rect as its own local coordinates
-        this->fillRectToRect(clip, std::move(paint), aa, viewMatrix, rect, rect);
+        this->drawFilledRect(clip, std::move(paint), aa, viewMatrix, rect);
         return;
     } else if (stroke.getStyle() == SkStrokeRec::kStroke_Style ||
                stroke.getStyle() == SkStrokeRec::kHairline_Style) {
@@ -867,6 +892,98 @@
     fRenderTargetContext->getRTOpList()->addOp(std::move(op), *fRenderTargetContext->caps());
 }
 
+void GrRenderTargetContextPriv::stencilRect(const GrClip& clip,
+                                            const GrUserStencilSettings* ss,
+                                            GrPaint&& paint,
+                                            GrAA doStencilMSAA,
+                                            const SkMatrix& viewMatrix,
+                                            const SkRect& rect,
+                                            const SkMatrix* localMatrix) {
+    ASSERT_SINGLE_OWNER_PRIV
+    RETURN_IF_ABANDONED_PRIV
+    SkDEBUGCODE(fRenderTargetContext->validate();)
+    GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContextPriv", "stencilRect",
+                                   fRenderTargetContext->fContext);
+
+    AutoCheckFlush acf(fRenderTargetContext->drawingManager());
+
+    auto aaType = (GrAA::kYes == doStencilMSAA) ? GrAAType::kMSAA : GrAAType::kNone;
+
+    GrQuad localQuad = localMatrix ? GrQuad::MakeFromRect(rect, *localMatrix)
+                                   : GrQuad(rect);
+    std::unique_ptr<GrDrawOp> op = GrFillRectOp::Make(
+            fRenderTargetContext->fContext, std::move(paint), aaType, GrQuadAAFlags::kNone,
+            GrQuad::MakeFromRect(rect, viewMatrix), localQuad, ss);
+    fRenderTargetContext->addDrawOp(clip, std::move(op));
+}
+
+void GrRenderTargetContext::fillRectWithEdgeAA(const GrClip& clip, GrPaint&& paint, GrAA aa,
+                                               GrQuadAAFlags edgeAA, const SkMatrix& viewMatrix,
+                                               const SkRect& rect, const SkRect* localRect) {
+    ASSERT_SINGLE_OWNER
+    RETURN_IF_ABANDONED
+    SkDEBUGCODE(this->validate();)
+    GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext", "fillRectWithEdgeAA", fContext);
+
+    GrAAType aaType = this->chooseAAType(aa);
+    std::unique_ptr<GrDrawOp> op;
+
+    if (localRect) {
+        // If local coordinates are provided, skip the optimization check to go through
+        // drawFilledRect, and also calculate clipped local coordinates
+        SkRect croppedRect = rect;
+        SkRect croppedLocalRect = *localRect;
+        if (!crop_filled_rect(this->width(), this->height(), clip, viewMatrix, &croppedRect,
+                              &croppedLocalRect)) {
+            return;
+        }
+        op = GrFillRectOp::Make(fContext, std::move(paint), aaType, edgeAA,
+                                GrQuad::MakeFromRect(croppedRect, viewMatrix),
+                                GrQuad(croppedLocalRect));
+    } else {
+        // If aaType turns into MSAA, make sure to keep quads with no AA edges as MSAA. Sending
+        // those to drawFilledRect() would have it turn off MSAA in that case, which breaks seaming
+        // with any partial AA edges that kept MSAA.
+        if (aaType != GrAAType::kMSAA &&
+            (edgeAA == GrQuadAAFlags::kNone || edgeAA == GrQuadAAFlags::kAll)) {
+            // This is equivalent to a regular filled rect draw, so route through there to take
+            // advantage of draw->clear optimizations
+            this->drawFilledRect(clip, std::move(paint), GrAA(edgeAA == GrQuadAAFlags::kAll),
+                                 viewMatrix, rect);
+            return;
+        }
+
+        SkRect croppedRect = rect;
+        if (!crop_filled_rect(this->width(), this->height(), clip, viewMatrix, &croppedRect)) {
+            return;
+        }
+        op = GrFillRectOp::Make(fContext, std::move(paint), aaType, edgeAA,
+                                GrQuad::MakeFromRect(croppedRect, viewMatrix),
+                                GrQuad(croppedRect));
+    }
+
+    AutoCheckFlush acf(this->drawingManager());
+    this->addDrawOp(clip, std::move(op));
+}
+
+void GrRenderTargetContext::fillQuadWithEdgeAA(const GrClip& clip, GrPaint&& paint, GrAA aa,
+                                               GrQuadAAFlags edgeAA, const SkMatrix& viewMatrix,
+                                               const SkPoint quad[4], const SkPoint localQuad[4]) {
+    ASSERT_SINGLE_OWNER
+    RETURN_IF_ABANDONED
+    SkDEBUGCODE(this->validate();)
+    GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext", "fillQuadWithEdgeAA", fContext);
+
+    GrAAType aaType = this->chooseAAType(aa);
+
+    AutoCheckFlush acf(this->drawingManager());
+    const SkPoint* localPoints = localQuad ? localQuad : quad;
+    this->addDrawOp(clip,
+                    GrFillRectOp::Make(fContext, std::move(paint), aaType, edgeAA,
+                                       GrQuad::MakeFromSkQuad(quad, viewMatrix),
+                                       GrQuad::MakeFromSkQuad(localPoints, SkMatrix::I())));
+}
+
 // Creates a paint for GrFillRectOp that matches behavior of GrTextureOp
 static void draw_texture_to_grpaint(sk_sp<GrTextureProxy> proxy, const SkRect* domain,
                                     GrSamplerState::Filter filter, SkBlendMode mode,
@@ -1030,6 +1147,32 @@
     }
 }
 
+void GrRenderTargetContext::fillRectWithLocalMatrix(const GrClip& clip,
+                                                    GrPaint&& paint,
+                                                    GrAA aa,
+                                                    const SkMatrix& viewMatrix,
+                                                    const SkRect& rectToDraw,
+                                                    const SkMatrix& localMatrix) {
+    ASSERT_SINGLE_OWNER
+    RETURN_IF_ABANDONED
+    SkDEBUGCODE(this->validate();)
+    GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext", "fillRectWithLocalMatrix", fContext);
+
+    SkRect croppedRect = rectToDraw;
+    if (!crop_filled_rect(this->width(), this->height(), clip, viewMatrix, &croppedRect)) {
+        return;
+    }
+
+    AutoCheckFlush acf(this->drawingManager());
+
+    GrAAType aaType = this->chooseAAType(aa);
+    GrQuadAAFlags edgeFlags = aa == GrAA::kYes ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone;
+    this->addDrawOp(clip,
+                    GrFillRectOp::Make(fContext, std::move(paint), aaType, edgeFlags,
+                                       GrQuad::MakeFromRect(croppedRect, viewMatrix),
+                                       GrQuad::MakeFromRect(croppedRect, localMatrix)));
+}
+
 void GrRenderTargetContext::drawVertices(const GrClip& clip,
                                          GrPaint&& paint,
                                          const SkMatrix& viewMatrix,
@@ -1775,8 +1918,8 @@
             GrPaint paint;
             paint.addColorFragmentProcessor(std::move(fp));
             paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
-            currRTC->fillRectToRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(),
-                                    dstRect, dstRect);
+            currRTC->drawFilledRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(),
+                                    dstRect);
         } else {
             auto filter = rescaleQuality == kNone_SkFilterQuality ? GrSamplerState::Filter::kNearest
                                                                   : GrSamplerState::Filter::kBilerp;
@@ -2131,9 +2274,6 @@
 
     auto texMatrix = SkMatrix::MakeTrans(x, y);
 
-    SkRect dstRectY = SkRect::MakeWH(dstW, dstH);
-    SkRect dstRectUV = SkRect::MakeWH(dstW / 2, dstH / 2);
-
     // This matrix generates (r,g,b,a) = (0, 0, 0, y)
     float yM[20];
     std::fill_n(yM, 15, 0.f);
@@ -2143,8 +2283,8 @@
     auto yFP = GrColorMatrixFragmentProcessor::Make(yM, false, true, false);
     yPaint.addColorFragmentProcessor(std::move(yFP));
     yPaint.setPorterDuffXPFactory(SkBlendMode::kSrc);
-    yRTC->fillRectToRect(GrNoClip(), std::move(yPaint), GrAA::kNo, SkMatrix::I(),
-                         dstRectY, dstRectY);
+    yRTC->drawFilledRect(GrNoClip(), std::move(yPaint), GrAA::kNo, SkMatrix::I(),
+                         SkRect::MakeWH(dstW, dstH));
     auto yTransfer = yRTC->transferPixels(GrColorType::kAlpha_8,
                                           SkIRect::MakeWH(yRTC->width(), yRTC->height()));
     if (!yTransfer.fTransferBuffer) {
@@ -2162,8 +2302,8 @@
     auto uFP = GrColorMatrixFragmentProcessor::Make(uM, false, true, false);
     uPaint.addColorFragmentProcessor(std::move(uFP));
     uPaint.setPorterDuffXPFactory(SkBlendMode::kSrc);
-    uRTC->fillRectToRect(GrNoClip(), std::move(uPaint), GrAA::kNo, SkMatrix::I(),
-                         dstRectUV, dstRectUV);
+    uRTC->drawFilledRect(GrNoClip(), std::move(uPaint), GrAA::kNo, SkMatrix::I(),
+                         SkRect::MakeWH(dstW / 2, dstH / 2));
     auto uTransfer = uRTC->transferPixels(GrColorType::kAlpha_8,
                                           SkIRect::MakeWH(uRTC->width(), uRTC->height()));
     if (!uTransfer.fTransferBuffer) {
@@ -2180,8 +2320,8 @@
     auto vFP = GrColorMatrixFragmentProcessor::Make(vM, false, true, false);
     vPaint.addColorFragmentProcessor(std::move(vFP));
     vPaint.setPorterDuffXPFactory(SkBlendMode::kSrc);
-    vRTC->fillRectToRect(GrNoClip(), std::move(vPaint), GrAA::kNo, SkMatrix::I(),
-                         dstRectUV, dstRectUV);
+    vRTC->drawFilledRect(GrNoClip(), std::move(vPaint), GrAA::kNo, SkMatrix::I(),
+                         SkRect::MakeWH(dstW / 2, dstH / 2));
     auto vTransfer = vRTC->transferPixels(GrColorType::kAlpha_8,
                                           SkIRect::MakeWH(vRTC->width(), vRTC->height()));
     if (!vTransfer.fTransferBuffer) {
diff --git a/src/gpu/GrRenderTargetContext.h b/src/gpu/GrRenderTargetContext.h
index cc46660..fd72e52 100644
--- a/src/gpu/GrRenderTargetContext.h
+++ b/src/gpu/GrRenderTargetContext.h
@@ -18,7 +18,6 @@
 #include "src/gpu/GrRenderTargetProxy.h"
 #include "src/gpu/GrSurfaceContext.h"
 #include "src/gpu/GrXferProcessor.h"
-#include "src/gpu/geometry/GrQuad.h"
 #include "src/gpu/text/GrTextTarget.h"
 
 class GrBackendSemaphore;
@@ -117,9 +116,9 @@
                         const SkMatrix& viewMatrix,
                         const SkRect& rectToDraw,
                         const SkRect& localRect) {
-        this->drawFilledQuad(clip, std::move(paint), aa,
-                             aa == GrAA::kYes ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone,
-                             GrQuad::MakeFromRect(rectToDraw, viewMatrix), GrQuad(localRect));
+        this->fillRectWithEdgeAA(clip, std::move(paint), aa,
+                                 aa == GrAA::kYes ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone,
+                                 viewMatrix, rectToDraw, &localRect);
     }
 
     /**
@@ -127,15 +126,10 @@
      */
     void fillRectWithLocalMatrix(const GrClip& clip,
                                  GrPaint&& paint,
-                                 GrAA aa,
+                                 GrAA,
                                  const SkMatrix& viewMatrix,
                                  const SkRect& rect,
-                                 const SkMatrix& localMatrix) {
-        this->drawFilledQuad(clip, std::move(paint), aa,
-                             aa == GrAA::kYes ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone,
-                             GrQuad::MakeFromRect(rect, viewMatrix),
-                             GrQuad::MakeFromRect(rect, localMatrix));
-    }
+                                 const SkMatrix& localMatrix);
 
     /**
      * Creates an op that draws a fill rect with per-edge control over anti-aliasing.
@@ -145,11 +139,7 @@
      */
     void fillRectWithEdgeAA(const GrClip& clip, GrPaint&& paint, GrAA aa, GrQuadAAFlags edgeAA,
                             const SkMatrix& viewMatrix, const SkRect& rect,
-                            const SkRect* optionalLocalRect = nullptr) {
-        const SkRect& localRect = optionalLocalRect ? *optionalLocalRect : rect;
-        this->drawFilledQuad(clip, std::move(paint), aa, edgeAA,
-                             GrQuad::MakeFromRect(rect, viewMatrix), GrQuad(localRect));
-    }
+                            const SkRect* optionalLocalRect = nullptr);
 
     /**
      * Similar to fillRectWithEdgeAA but draws an arbitrary 2D convex quadrilateral transformed
@@ -165,12 +155,7 @@
      */
     void fillQuadWithEdgeAA(const GrClip& clip, GrPaint&& paint, GrAA aa, GrQuadAAFlags edgeAA,
                             const SkMatrix& viewMatrix, const SkPoint quad[4],
-                            const SkPoint optionalLocalQuad[4]) {
-        const SkPoint* localQuad = optionalLocalQuad ? optionalLocalQuad : quad;
-        this->drawFilledQuad(clip, std::move(paint), aa, edgeAA,
-                             GrQuad::MakeFromSkQuad(quad, viewMatrix),
-                             GrQuad::MakeFromSkQuad(localQuad, SkMatrix::I()));
-    }
+                            const SkPoint optionalLocalQuad[4]);
 
     /** Used with drawQuadSet */
     struct QuadSetEntry {
@@ -502,7 +487,6 @@
 
 private:
     class TextTarget;
-    enum class QuadOptimization;
 
     GrAAType chooseAAType(GrAA);
 
@@ -541,35 +525,20 @@
                           const SkRRect& origOuter,
                           const SkRRect& origInner);
 
-    // If the drawn quad's paint is a const blended color, provide it as a non-null pointer to
-    // 'constColor', which enables the draw-as-clear optimization. Otherwise it is assumed the paint
-    // requires some form of shading that invalidates using a clear op.
-    //
-    // The non-const pointers should be the original draw request on input, and will be updated as
-    // appropriate depending on the returned optimization level.
-    //
-    // If 'allowAAChange' is true, 'aa' and 'edgeFlags' may be updated to incorporate the AA of the
-    // clip. When it is false, only optimizations that will not change the AA state will be applied.
-    QuadOptimization attemptQuadOptimization(const GrClip& clip,
-                                             const SkPMColor4f* constColor,
-                                             bool allowAAChange,
-                                             GrAA* aa,
-                                             GrQuadAAFlags* edgeFlags,
-                                             GrQuad* deviceQuad,
-                                             GrQuad* localQuad);
-
-    // If stencil settings, 'ss', are non-null, AA controls MSAA or no AA. If they are null, then AA
-    // can choose between coverage, MSAA as per chooseAAType(). This will always attempt to apply
-    // quad optimizations, so all quad/rect public APIs should rely on this function for consistent
-    // clipping behavior.
-    void drawFilledQuad(const GrClip& clip,
+    void drawFilledRect(const GrClip& clip,
                         GrPaint&& paint,
-                        GrAA aa,
-                        GrQuadAAFlags edgeFlags,
-                        const GrQuad& deviceQuad,
-                        const GrQuad& localQuad,
+                        GrAA,
+                        const SkMatrix& viewMatrix,
+                        const SkRect& rect,
                         const GrUserStencilSettings* ss = nullptr);
 
+    // Only consumes the GrPaint if successful.
+    bool drawFilledRectAsClear(const GrClip& clip,
+                               GrPaint&& paint,
+                               GrAA aa,
+                               const SkMatrix& viewMatrix,
+                               const SkRect& rect);
+
     void drawShapeUsingPathRenderer(const GrClip&, GrPaint&&, GrAA, const SkMatrix&,
                                     const GrShape&);
 
diff --git a/src/gpu/GrRenderTargetContextPriv.h b/src/gpu/GrRenderTargetContextPriv.h
index ef60655..fa1d9fd 100644
--- a/src/gpu/GrRenderTargetContextPriv.h
+++ b/src/gpu/GrRenderTargetContextPriv.h
@@ -66,16 +66,8 @@
     // care to only provide hard clips or we could get stuck in a loop. The general clip is needed
     // so that path renderers can use this function.
     void stencilRect(
-            const GrClip& clip, const GrUserStencilSettings* ss, GrPaint&& paint,
-            GrAA doStencilMSAA, const SkMatrix& viewMatrix, const SkRect& rect,
-            const SkMatrix* localMatrix = nullptr) {
-        // Since this provides stencil settings to drawFilledQuad, it performs a different AA type
-        // resolution compared to regular rect draws, which is the main reason it remains separate.
-        GrQuad localQuad = localMatrix ? GrQuad::MakeFromRect(rect, *localMatrix) : GrQuad(rect);
-        fRenderTargetContext->drawFilledQuad(
-                clip, std::move(paint), doStencilMSAA, GrQuadAAFlags::kNone,
-                GrQuad::MakeFromRect(rect, viewMatrix), localQuad, ss);
-    }
+            const GrClip&, const GrUserStencilSettings* ss, GrPaint&& paint, GrAA doStencilMSAA,
+            const SkMatrix& viewMatrix, const SkRect& rect, const SkMatrix* localMatrix = nullptr);
 
     void stencilPath(
             const GrHardClip&, GrAA doStencilMSAA, const SkMatrix& viewMatrix, const GrPath*);
@@ -92,6 +84,12 @@
                             const SkMatrix& viewMatrix,
                             const SkPath&);
 
+    void drawFilledRect(
+            const GrClip& clip, GrPaint&& paint, GrAA aa, const SkMatrix& m, const SkRect& rect,
+            const GrUserStencilSettings* ss = nullptr) {
+        fRenderTargetContext->drawFilledRect(clip, std::move(paint), aa, m, rect, ss);
+    }
+
     SkBudgeted isBudgeted() const;
 
     int maxWindowRectangles() const;
diff --git a/src/gpu/geometry/GrQuad.h b/src/gpu/geometry/GrQuad.h
index 9a080f3..9f4f974 100644
--- a/src/gpu/geometry/GrQuad.h
+++ b/src/gpu/geometry/GrQuad.h
@@ -117,15 +117,6 @@
     // True if anti-aliasing affects this quad. Only valid when quadType == kAxisAligned
     bool aaHasEffectOnRect() const;
 
-    // The non-const pointers are provided to support modifying a GrQuad in-place, but care must be
-    // taken to keep its quad type aligned with the geometric nature of the new coordinates. This is
-    // no different than using the constructors that accept a quad type.
-
-    float* xs() { return fX; }
-    float* ys() { return fY; }
-    float* ws() { return fW; }
-
-    void setQuadType(Type newType) { fType = newType; }
 private:
     template<typename T>
     friend class GrQuadListBase; // for access to fX, fY, fW
diff --git a/src/gpu/geometry/GrQuadUtils.cpp b/src/gpu/geometry/GrQuadUtils.cpp
index fce38d2..ba109cf 100644
--- a/src/gpu/geometry/GrQuadUtils.cpp
+++ b/src/gpu/geometry/GrQuadUtils.cpp
@@ -7,158 +7,9 @@
 
 #include "src/gpu/geometry/GrQuadUtils.h"
 
-#include "include/core/SkRect.h"
 #include "include/private/GrTypesPriv.h"
-#include "include/private/SkVx.h"
 #include "src/gpu/geometry/GrQuad.h"
 
-using V4f = skvx::Vec<4, float>;
-using M4f = skvx::Vec<4, int32_t>;
-
-// Since the local quad may not be type kRect, this uses the opposites for each vertex when
-// interpolating, and calculates new ws in addition to new xs, ys.
-static void interpolate_local(float alpha, int v0, int v1, int v2, int v3,
-                              float lx[4], float ly[4], float lw[4]) {
-    SkASSERT(v0 >= 0 && v0 < 4);
-    SkASSERT(v1 >= 0 && v1 < 4);
-    SkASSERT(v2 >= 0 && v2 < 4);
-    SkASSERT(v3 >= 0 && v3 < 4);
-
-    float beta = 1.f - alpha;
-    lx[v0] = alpha * lx[v0] + beta * lx[v2];
-    ly[v0] = alpha * ly[v0] + beta * ly[v2];
-    lw[v0] = alpha * lw[v0] + beta * lw[v2];
-
-    lx[v1] = alpha * lx[v1] + beta * lx[v3];
-    ly[v1] = alpha * ly[v1] + beta * ly[v3];
-    lw[v1] = alpha * lw[v1] + beta * lw[v3];
-}
-
-// Crops v0 to v1 based on the clipDevRect. v2 is opposite of v0, v3 is opposite of v1.
-// It is written to not modify coordinates if there's no intersection along the edge.
-// Ideally this would have been detected earlier and the entire draw is skipped.
-static bool crop_rect_edge(const SkRect& clipDevRect, int v0, int v1, int v2, int v3,
-                           float x[4], float y[4], float lx[4], float ly[4], float lw[4]) {
-    SkASSERT(v0 >= 0 && v0 < 4);
-    SkASSERT(v1 >= 0 && v1 < 4);
-    SkASSERT(v2 >= 0 && v2 < 4);
-    SkASSERT(v3 >= 0 && v3 < 4);
-
-    if (SkScalarNearlyEqual(x[v0], x[v1])) {
-        // A vertical edge
-        if (x[v0] < clipDevRect.fLeft && x[v2] >= clipDevRect.fLeft) {
-            // Overlapping with left edge of clipDevRect
-            if (lx) {
-                float alpha = (x[v2] - clipDevRect.fLeft) / (x[v2] - x[v0]);
-                interpolate_local(alpha, v0, v1, v2, v3, lx, ly, lw);
-            }
-            x[v0] = clipDevRect.fLeft;
-            x[v1] = clipDevRect.fLeft;
-            return true;
-        } else if (x[v0] > clipDevRect.fRight && x[v2] <= clipDevRect.fRight) {
-            // Overlapping with right edge of clipDevRect
-            if (lx) {
-                float alpha = (clipDevRect.fRight - x[v2]) / (x[v0] - x[v2]);
-                interpolate_local(alpha, v0, v1, v2, v3, lx, ly, lw);
-            }
-            x[v0] = clipDevRect.fRight;
-            x[v1] = clipDevRect.fRight;
-            return true;
-        }
-    } else {
-        // A horizontal edge
-        SkASSERT(SkScalarNearlyEqual(y[v0], y[v1]));
-        if (y[v0] < clipDevRect.fTop && y[v2] >= clipDevRect.fTop) {
-            // Overlapping with top edge of clipDevRect
-            if (lx) {
-                float alpha = (y[v2] - clipDevRect.fTop) / (y[v2] - y[v0]);
-                interpolate_local(alpha, v0, v1, v2, v3, lx, ly, lw);
-            }
-            y[v0] = clipDevRect.fTop;
-            y[v1] = clipDevRect.fTop;
-            return true;
-        } else if (y[v0] > clipDevRect.fBottom && y[v2] <= clipDevRect.fBottom) {
-            // Overlapping with bottom edge of clipDevRect
-            if (lx) {
-                float alpha = (clipDevRect.fBottom - y[v2]) / (y[v0] - y[v2]);
-                interpolate_local(alpha, v0, v1, v2, v3, lx, ly, lw);
-            }
-            y[v0] = clipDevRect.fBottom;
-            y[v1] = clipDevRect.fBottom;
-            return true;
-        }
-    }
-
-    // No overlap so don't crop it
-    return false;
-}
-
-// Updates x and y to intersect with clipDevRect, and applies clipAA policy to edgeFlags for each
-// intersected edge. lx, ly, and lw are updated appropriately and may be null to skip calculations.
-static void crop_rect(const SkRect& clipDevRect, GrAA clipAA, GrQuadAAFlags* edgeFlags,
-                      float x[4], float y[4], float lx[4], float ly[4], float lw[4]) {
-    // Filled in as if clipAA were true, will be inverted at the end if needed.
-    GrQuadAAFlags clipEdgeFlags = GrQuadAAFlags::kNone;
-
-    // However, the quad's left edge may not align with the SkRect notion of left due to 90 degree
-    // rotations or mirrors. So, this processes the logical edges of the quad and clamps it to the 4
-    // sides of clipDevRect.
-
-    // Quad's left is v0 to v1 (op. v2 and v3)
-    if (crop_rect_edge(clipDevRect, 0, 1, 2, 3, x, y, lx, ly, lw)) {
-        clipEdgeFlags |= GrQuadAAFlags::kLeft;
-    }
-    // Quad's top edge is v0 to v2 (op. v1 and v3)
-    if (crop_rect_edge(clipDevRect, 0, 2, 1, 3, x, y, lx, ly, lw)) {
-        clipEdgeFlags |= GrQuadAAFlags::kTop;
-    }
-    // Quad's right edge is v2 to v3 (op. v0 and v1)
-    if (crop_rect_edge(clipDevRect, 2, 3, 0, 1, x, y, lx, ly, lw)) {
-        clipEdgeFlags |= GrQuadAAFlags::kRight;
-    }
-    // Quad's bottom edge is v1 to v3 (op. v0 and v2)
-    if (crop_rect_edge(clipDevRect, 1, 3, 0, 2, x, y, lx, ly, lw)) {
-        clipEdgeFlags |= GrQuadAAFlags::kBottom;
-    }
-
-    if (clipAA == GrAA::kYes) {
-        // Turn on all edges that were clipped
-        *edgeFlags |= clipEdgeFlags;
-    } else {
-        // Turn off all edges that were clipped
-        *edgeFlags &= ~clipEdgeFlags;
-    }
-}
-
-// Calculates barycentric coordinates for each point in (testX, testY) in the triangle formed by
-// (x0,y0) - (x1,y1) - (x2, y2) and stores them in u, v, w.
-static void barycentric_coords(float x0, float y0, float x1, float y1, float x2, float y2,
-                               const V4f& testX, const V4f& testY,
-                               V4f* u, V4f* v, V4f* w) {
-    // Modeled after SkPathOpsQuad::pointInTriangle() but uses float instead of double, is
-    // vectorized and outputs normalized barycentric coordinates instead of inside/outside test
-    float v0x = x2 - x0;
-    float v0y = y2 - y0;
-    float v1x = x1 - x0;
-    float v1y = y1 - y0;
-    V4f v2x = testX - x0;
-    V4f v2y = testY - y0;
-
-    float dot00 = v0x * v0x + v0y * v0y;
-    float dot01 = v0x * v1x + v0y * v1y;
-    V4f   dot02 = v0x * v2x + v0y * v2y;
-    float dot11 = v1x * v1x + v1y * v1y;
-    V4f   dot12 = v1x * v2x + v1y * v2y;
-    float invDenom = sk_ieee_float_divide(1.f, dot00 * dot11 - dot01 * dot01);
-    *u = (dot11 * dot02 - dot01 * dot12) * invDenom;
-    *v = (dot00 * dot12 - dot01 * dot02) * invDenom;
-    *w = 1.f - *u - *v;
-}
-
-static M4f inside_triangle(const V4f& u, const V4f& v, const V4f& w) {
-    return ((u >= 0.f) & (u <= 1.f)) & ((v >= 0.f) & (v <= 1.f)) & ((w >= 0.f) & (w <= 1.f));
-}
-
 namespace GrQuadUtils {
 
 void ResolveAAType(GrAAType requestedAAType, GrQuadAAFlags requestedEdgeFlags, const GrQuad& quad,
@@ -194,79 +45,6 @@
             SK_ABORT("Should not use mixed sample AA with edge AA flags");
             break;
     }
-}
-
-bool CropToRect(const SkRect& cropRect, GrAA cropAA, GrQuadAAFlags* edgeFlags, GrQuad* quad,
-                GrQuad* local) {
-    if (quad->quadType() == GrQuad::Type::kAxisAligned) {
-        // crop_rect keeps the rectangles as rectangles, so there's no need to modify types
-        if (local) {
-            crop_rect(cropRect, cropAA, edgeFlags, quad->xs(), quad->ys(),
-                      local->xs(), local->ys(), local->ws());
-        } else {
-            crop_rect(cropRect, cropAA, edgeFlags, quad->xs(), quad->ys(),
-                      nullptr, nullptr, nullptr);
-        }
-        return true;
-    }
-
-    if (local) {
-        // FIXME (michaelludwig) Calculate cropped local coordinates when not kAxisAligned
-        return false;
-    }
-
-    V4f devX = quad->x4f();
-    V4f devY = quad->y4f();
-    V4f devIW = quad->iw4f();
-    // Project the 3D coordinates to 2D
-    if (quad->quadType() == GrQuad::Type::kPerspective) {
-        devX *= devIW;
-        devY *= devIW;
-    }
-
-    V4f clipX = {cropRect.fLeft, cropRect.fLeft, cropRect.fRight, cropRect.fRight};
-    V4f clipY = {cropRect.fTop, cropRect.fBottom, cropRect.fTop, cropRect.fBottom};
-
-    // Calculate barycentric coordinates for the 4 rect corners in the 2 triangles that the quad
-    // is tessellated into when drawn.
-    V4f u1, v1, w1;
-    barycentric_coords(devX[0], devY[0], devX[1], devY[1], devX[2], devY[2], clipX, clipY,
-                       &u1, &v1, &w1);
-    V4f u2, v2, w2;
-    barycentric_coords(devX[1], devY[1], devX[3], devY[3], devX[2], devY[2], clipX, clipY,
-                       &u2, &v2, &w2);
-
-    // clipDevRect is completely inside this quad if each corner is in at least one of two triangles
-    M4f inTri1 = inside_triangle(u1, v1, w1);
-    M4f inTri2 = inside_triangle(u2, v2, w2);
-    if (all(inTri1 | inTri2)) {
-        // We can crop to exactly the clipDevRect.
-        // FIXME (michaelludwig) - there are other ways to have determined quad covering the clip
-        // rect, but the barycentric coords will be useful to derive local coordinates in the future
-
-        // Since we are cropped to exactly clipDevRect, we have discarded any perspective and the
-        // type becomes kRect. If updated locals were requested, they will incorporate perspective.
-        // FIXME (michaelludwig) - once we have local coordinates handled, it may be desirable to
-        // keep the draw as perspective so that the hardware does perspective interpolation instead
-        // of pushing it into a local coord w and having the shader do an extra divide.
-        clipX.store(quad->xs());
-        clipY.store(quad->ys());
-        quad->ws()[0] = 1.f;
-        quad->ws()[1] = 1.f;
-        quad->ws()[2] = 1.f;
-        quad->ws()[3] = 1.f;
-        quad->setQuadType(GrQuad::Type::kAxisAligned);
-
-        // Update the edge flags to match the clip setting since all 4 edges have been clipped
-        *edgeFlags = cropAA == GrAA::kYes ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone;
-
-        return true;
-    }
-
-    // FIXME (michaelludwig) - use the GrQuadPerEdgeAA tessellation inset/outset math to move
-    // edges to the closest clip corner they are outside of
-
-    return false;
-}
+};
 
 }; // namespace GrQuadUtils
diff --git a/src/gpu/geometry/GrQuadUtils.h b/src/gpu/geometry/GrQuadUtils.h
index 5f6d283..29feeeb 100644
--- a/src/gpu/geometry/GrQuadUtils.h
+++ b/src/gpu/geometry/GrQuadUtils.h
@@ -9,10 +9,8 @@
 #define GrQuadUtils_DEFINED
 
 enum class GrQuadAAFlags;
-enum class GrAA : bool;
 enum class GrAAType : unsigned;
 class GrQuad;
-struct SkRect;
 
 namespace GrQuadUtils {
 
@@ -21,23 +19,6 @@
     void ResolveAAType(GrAAType requestedAAType, GrQuadAAFlags requestedEdgeFlags,
                        const GrQuad& quad, GrAAType* outAAtype, GrQuadAAFlags* outEdgeFlags);
 
-    /**
-     * Crops quad to the provided device-space axis-aligned rectangle. If the intersection of this
-     * quad (projected) and cropRect results in a quadrilateral, this returns true. If not, this
-     * quad may be updated to be a smaller quad of the same type such that its intersection with
-     * cropRect is visually the same.
-     *
-     * The provided edge flags are updated to reflect edges clipped by cropRect (toggling on or off
-     * based on cropAA policy). If provided, the local coordinates will be updated to reflect the
-     * updated device coordinates of this quad.
-     *
-     * 'local' may be null, in which case the new local coordinates will not be calculated. This is
-     * useful when it's known a paint does not require local coordinates. However, neither
-     * 'edgeFlags' nore 'quad' can be null.
-     */
-    bool CropToRect(const SkRect& cropRect, GrAA cropAA, GrQuadAAFlags* edgeFlags, GrQuad* quad,
-                    GrQuad* local=nullptr);
-
 }; // namespace GrQuadUtils
 
 #endif
diff --git a/tests/GrQuadCropTest.cpp b/tests/GrQuadCropTest.cpp
deleted file mode 100644
index 36fafe4..0000000
--- a/tests/GrQuadCropTest.cpp
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * Copyright 2019 Google LLC
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "include/core/SkScalar.h"
-#include "src/gpu/geometry/GrQuad.h"
-#include "src/gpu/geometry/GrQuadUtils.h"
-#include "tests/Test.h"
-
-#define ASSERT(cond) REPORTER_ASSERT(r, cond)
-#define ASSERTF(cond, ...) REPORTER_ASSERT(r, cond, __VA_ARGS__)
-#define TEST(name) DEF_TEST(GrQuadCrop##name, r)
-#define ASSERT_NEARLY_EQUAL(expected, actual) \
-    ASSERTF(SkScalarNearlyEqual(expected, actual), "expected: %f, actual: %f", \
-            expected, actual)
-
-// Make the base rect contain the origin and have unique edge values so that each transform
-// produces a different axis-aligned rectangle.
-static const SkRect kDrawRect = SkRect::MakeLTRB(-5.f, -6.f, 10.f, 11.f);
-
-static void run_crop_axis_aligned_test(skiatest::Reporter* r, const SkRect& clipRect, GrAA clipAA,
-                                       const SkMatrix& viewMatrix, const SkMatrix* localMatrix) {
-    // Should use run_crop_fully_covers_test for non-rect matrices
-    SkASSERT(viewMatrix.rectStaysRect());
-
-    GrQuad drawQuad = GrQuad::MakeFromRect(kDrawRect, viewMatrix);
-    GrQuad localQuad = GrQuad::MakeFromRect(kDrawRect, localMatrix ? *localMatrix : SkMatrix::I());
-    GrQuad* localQuadPtr = localMatrix ? &localQuad : nullptr;
-    GrQuadAAFlags edgeFlags = clipAA == GrAA::kYes ? GrQuadAAFlags::kNone : GrQuadAAFlags::kAll;
-
-    bool exact = GrQuadUtils::CropToRect(clipRect, clipAA, &edgeFlags, &drawQuad, localQuadPtr);
-    ASSERTF(exact, "Expected exact crop");
-    ASSERTF(drawQuad.quadType() == GrQuad::Type::kAxisAligned,
-            "Expected quad to remain axis-aligned");
-
-    // Since we remained a rectangle, the bounds will exactly match the coordinates
-    SkRect expectedBounds = viewMatrix.mapRect(kDrawRect);
-    SkAssertResult(expectedBounds.intersect(clipRect));
-
-    SkRect actualBounds = drawQuad.bounds();
-    ASSERT_NEARLY_EQUAL(expectedBounds.fLeft, actualBounds.fLeft);
-    ASSERT_NEARLY_EQUAL(expectedBounds.fTop, actualBounds.fTop);
-    ASSERT_NEARLY_EQUAL(expectedBounds.fRight, actualBounds.fRight);
-    ASSERT_NEARLY_EQUAL(expectedBounds.fBottom, actualBounds.fBottom);
-
-    // Confirm that local coordinates match up with clipped edges and the transform
-    SkMatrix invViewMatrix;
-    SkAssertResult(viewMatrix.invert(&invViewMatrix));
-
-    if (localMatrix) {
-        SkMatrix toLocal = SkMatrix::Concat(*localMatrix, invViewMatrix);
-
-        for (int p = 0; p < 4; ++p) {
-            SkPoint expectedPoint = drawQuad.point(p);
-            toLocal.mapPoints(&expectedPoint, 1);
-            SkPoint actualPoint = localQuad.point(p);
-
-            ASSERT_NEARLY_EQUAL(expectedPoint.fX, actualPoint.fX);
-            ASSERT_NEARLY_EQUAL(expectedPoint.fY, actualPoint.fY);
-        }
-    }
-
-    // Confirm that the edge flags match, by mapping clip rect to drawRect space and
-    // comparing to the original draw rect edges
-    SkRect drawClip = invViewMatrix.mapRect(clipRect);
-    if (drawClip.fLeft > kDrawRect.fLeft) {
-        if (clipAA == GrAA::kYes) {
-            ASSERTF(edgeFlags & GrQuadAAFlags::kLeft, "Expected left edge AA set");
-        } else {
-            ASSERTF(!(edgeFlags & GrQuadAAFlags::kLeft), "Expected left edge AA unset");
-        }
-    }
-    if (drawClip.fRight < kDrawRect.fRight) {
-        if (clipAA == GrAA::kYes) {
-            ASSERTF(edgeFlags & GrQuadAAFlags::kRight, "Expected right edge AA set");
-        } else {
-            ASSERTF(!(edgeFlags & GrQuadAAFlags::kRight),  "Expected right edge AA unset");
-        }
-    }
-    if (drawClip.fTop > kDrawRect.fTop) {
-        if (clipAA == GrAA::kYes) {
-            ASSERTF(edgeFlags & GrQuadAAFlags::kTop, "Expected top edge AA set");
-        } else {
-            ASSERTF(!(edgeFlags & GrQuadAAFlags::kTop), "Expected top edge AA unset");
-        }
-    }
-    if (drawClip.fBottom < kDrawRect.fBottom) {
-        if (clipAA == GrAA::kYes) {
-            ASSERTF(edgeFlags & GrQuadAAFlags::kBottom, "Expected bottom edge AA set");
-        } else {
-            ASSERTF(!(edgeFlags & GrQuadAAFlags::kBottom), "Expected bottom edge AA unset");
-        }
-    }
-}
-
-static void run_crop_fully_covered_test(skiatest::Reporter* r, GrAA clipAA,
-                                        const SkMatrix& viewMatrix, const SkMatrix* localMatrix) {
-    // Should use run_crop_axis_aligned for rect transforms since that verifies more behavior
-    SkASSERT(!viewMatrix.rectStaysRect());
-
-    // Test what happens when the geometry fully covers the crop rect. Given a fixed crop,
-    // use the provided view matrix to derive the "input" geometry that we know covers the crop.
-    SkMatrix invViewMatrix;
-    SkAssertResult(viewMatrix.invert(&invViewMatrix));
-
-    SkRect containsCrop = kDrawRect; // Use kDrawRect as the crop rect for this test
-    containsCrop.outset(10.f, 10.f);
-    SkRect drawRect = invViewMatrix.mapRect(containsCrop);
-
-    GrQuad drawQuad = GrQuad::MakeFromRect(drawRect, viewMatrix);
-    GrQuadAAFlags edgeFlags = clipAA == GrAA::kYes ? GrQuadAAFlags::kNone : GrQuadAAFlags::kAll;
-
-    if (localMatrix) {
-        GrQuad localQuad = GrQuad::MakeFromRect(drawRect, *localMatrix);
-
-        GrQuad originalDrawQuad = drawQuad;
-        GrQuad originalLocalQuad = localQuad;
-        GrQuadAAFlags originalEdgeFlags = edgeFlags;
-
-        bool exact = GrQuadUtils::CropToRect(kDrawRect, clipAA, &edgeFlags, &drawQuad, &localQuad);
-        // Currently non-rect matrices don't know how to update local coordinates, so the crop
-        // doesn't know how to restrict itself and should leave the inputs unmodified
-        ASSERTF(!exact, "Expected crop to be not exact");
-        ASSERTF(edgeFlags == originalEdgeFlags, "Expected edge flags not to be modified");
-
-        for (int i = 0; i < 4; ++i) {
-            ASSERT_NEARLY_EQUAL(originalDrawQuad.x(i), drawQuad.x(i));
-            ASSERT_NEARLY_EQUAL(originalDrawQuad.y(i), drawQuad.y(i));
-            ASSERT_NEARLY_EQUAL(originalDrawQuad.w(i), drawQuad.w(i));
-
-            ASSERT_NEARLY_EQUAL(originalLocalQuad.x(i), localQuad.x(i));
-            ASSERT_NEARLY_EQUAL(originalLocalQuad.y(i), localQuad.y(i));
-            ASSERT_NEARLY_EQUAL(originalLocalQuad.w(i), localQuad.w(i));
-        }
-    } else {
-        // Since no local coordinates were provided, and the input draw geometry is known to
-        // fully cover the crop rect, the quad should be updated to match cropRect exactly
-        bool exact = GrQuadUtils::CropToRect(kDrawRect, clipAA, &edgeFlags, &drawQuad, nullptr);
-        ASSERTF(exact, "Expected crop to be exact");
-
-        GrQuadAAFlags expectedFlags = clipAA == GrAA::kYes ? GrQuadAAFlags::kAll
-                                                           : GrQuadAAFlags::kNone;
-        ASSERTF(expectedFlags == edgeFlags, "Expected edge flags do not match clip AA setting");
-        ASSERTF(drawQuad.quadType() == GrQuad::Type::kAxisAligned, "Unexpected quad type");
-
-        ASSERT_NEARLY_EQUAL(kDrawRect.fLeft, drawQuad.x(0));
-        ASSERT_NEARLY_EQUAL(kDrawRect.fTop, drawQuad.y(0));
-        ASSERT_NEARLY_EQUAL(1.f, drawQuad.w(0));
-
-        ASSERT_NEARLY_EQUAL(kDrawRect.fLeft, drawQuad.x(1));
-        ASSERT_NEARLY_EQUAL(kDrawRect.fBottom, drawQuad.y(1));
-        ASSERT_NEARLY_EQUAL(1.f, drawQuad.w(1));
-
-        ASSERT_NEARLY_EQUAL(kDrawRect.fRight, drawQuad.x(2));
-        ASSERT_NEARLY_EQUAL(kDrawRect.fTop, drawQuad.y(2));
-        ASSERT_NEARLY_EQUAL(1.f, drawQuad.w(2));
-
-        ASSERT_NEARLY_EQUAL(kDrawRect.fRight, drawQuad.x(3));
-        ASSERT_NEARLY_EQUAL(kDrawRect.fBottom, drawQuad.y(3));
-        ASSERT_NEARLY_EQUAL(1.f, drawQuad.w(3));
-    }
-}
-
-static void test_axis_aligned_all_clips(skiatest::Reporter* r, const SkMatrix& viewMatrix,
-                                        const SkMatrix* localMatrix) {
-    static const float kInsideEdge = SkScalarAbs(kDrawRect.fLeft) - 1.f;
-    static const float kOutsideEdge = SkScalarAbs(kDrawRect.fBottom) + 1.f;
-    static const float kIntersectEdge = SkScalarAbs(kDrawRect.fTop) + 1.f;
-
-    static const SkRect kInsideClipRect = SkRect::MakeLTRB(-kInsideEdge, -kInsideEdge,
-                                                           kInsideEdge, kInsideEdge);
-    static const SkRect kContainsClipRect = SkRect::MakeLTRB(-kOutsideEdge, -kOutsideEdge,
-                                                             kOutsideEdge, kOutsideEdge);
-    static const SkRect kXYAxesClipRect = SkRect::MakeLTRB(-kIntersectEdge, -kIntersectEdge,
-                                                           kIntersectEdge, kIntersectEdge);
-    static const SkRect kXAxisClipRect = SkRect::MakeLTRB(-kIntersectEdge, -kOutsideEdge,
-                                                          kIntersectEdge, kOutsideEdge);
-    static const SkRect kYAxisClipRect = SkRect::MakeLTRB(-kOutsideEdge, -kIntersectEdge,
-                                                          kOutsideEdge, kIntersectEdge);
-
-    run_crop_axis_aligned_test(r, kInsideClipRect, GrAA::kNo, viewMatrix, localMatrix);
-    run_crop_axis_aligned_test(r, kContainsClipRect, GrAA::kNo, viewMatrix, localMatrix);
-    run_crop_axis_aligned_test(r, kXYAxesClipRect, GrAA::kNo, viewMatrix, localMatrix);
-    run_crop_axis_aligned_test(r, kXAxisClipRect, GrAA::kNo, viewMatrix, localMatrix);
-    run_crop_axis_aligned_test(r, kYAxisClipRect, GrAA::kNo, viewMatrix, localMatrix);
-
-    run_crop_axis_aligned_test(r, kInsideClipRect, GrAA::kYes, viewMatrix, localMatrix);
-    run_crop_axis_aligned_test(r, kContainsClipRect, GrAA::kYes, viewMatrix, localMatrix);
-    run_crop_axis_aligned_test(r, kXYAxesClipRect, GrAA::kYes, viewMatrix, localMatrix);
-    run_crop_axis_aligned_test(r, kXAxisClipRect, GrAA::kYes, viewMatrix, localMatrix);
-    run_crop_axis_aligned_test(r, kYAxisClipRect, GrAA::kYes, viewMatrix, localMatrix);
-}
-
-static void test_axis_aligned(skiatest::Reporter* r, const SkMatrix& viewMatrix) {
-    test_axis_aligned_all_clips(r, viewMatrix, nullptr);
-
-    SkMatrix normalized = SkMatrix::MakeRectToRect(kDrawRect, SkRect::MakeWH(1.f, 1.f),
-                                                   SkMatrix::kFill_ScaleToFit);
-    test_axis_aligned_all_clips(r, viewMatrix, &normalized);
-
-    SkMatrix rotated;
-    rotated.setRotate(45.f);
-    test_axis_aligned_all_clips(r, viewMatrix, &rotated);
-
-    SkMatrix perspective;
-    perspective.setPerspY(0.001f);
-    perspective.setSkewX(8.f / 25.f);
-    test_axis_aligned_all_clips(r, viewMatrix, &perspective);
-}
-
-static void test_crop_fully_covered(skiatest::Reporter* r, const SkMatrix& viewMatrix) {
-    run_crop_fully_covered_test(r, GrAA::kNo, viewMatrix, nullptr);
-    run_crop_fully_covered_test(r, GrAA::kYes, viewMatrix, nullptr);
-
-    SkMatrix normalized = SkMatrix::MakeRectToRect(kDrawRect, SkRect::MakeWH(1.f, 1.f),
-                                                   SkMatrix::kFill_ScaleToFit);
-    run_crop_fully_covered_test(r, GrAA::kNo, viewMatrix, &normalized);
-    run_crop_fully_covered_test(r, GrAA::kYes, viewMatrix, &normalized);
-
-    SkMatrix rotated;
-    rotated.setRotate(45.f);
-    run_crop_fully_covered_test(r, GrAA::kNo, viewMatrix, &rotated);
-    run_crop_fully_covered_test(r, GrAA::kYes, viewMatrix, &rotated);
-
-    SkMatrix perspective;
-    perspective.setPerspY(0.001f);
-    perspective.setSkewX(8.f / 25.f);
-    run_crop_fully_covered_test(r, GrAA::kNo, viewMatrix, &perspective);
-    run_crop_fully_covered_test(r, GrAA::kYes, viewMatrix, &perspective);
-}
-
-TEST(AxisAligned) {
-    test_axis_aligned(r, SkMatrix::I());
-    test_axis_aligned(r, SkMatrix::MakeScale(-1.f, 1.f));
-    test_axis_aligned(r, SkMatrix::MakeScale(1.f, -1.f));
-
-    SkMatrix rotation;
-    rotation.setRotate(90.f);
-    test_axis_aligned(r, rotation);
-    rotation.setRotate(180.f);
-    test_axis_aligned(r, rotation);
-    rotation.setRotate(270.f);
-    test_axis_aligned(r, rotation);
-}
-
-TEST(FullyCovered) {
-    SkMatrix rotation;
-    rotation.setRotate(34.f);
-    test_crop_fully_covered(r, rotation);
-
-    SkMatrix skew;
-    skew.setSkewX(0.3f);
-    skew.setSkewY(0.04f);
-    test_crop_fully_covered(r, skew);
-
-    SkMatrix perspective;
-    perspective.setPerspX(0.001f);
-    perspective.setSkewY(8.f / 25.f);
-    test_crop_fully_covered(r, perspective);
-}