Add gm that tests shaded stroked rectangles.
authorbsalomon <bsalomon@google.com>
Wed, 6 Jul 2016 18:54:59 +0000 (11:54 -0700)
committerCommit bot <commit-bot@chromium.org>
Wed, 6 Jul 2016 18:54:59 +0000 (11:54 -0700)
Fix GPU handling of previously untested cases.

Move rect->path fallback from SkGpuDevice to GrDrawContext.

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2125663003

Review-Url: https://codereview.chromium.org/2125663003

gm/stroke_rect_shader.cpp [new file with mode: 0644]
include/core/SkPaint.h
src/gpu/GrDrawContext.cpp
src/gpu/SkGpuDevice.cpp
src/gpu/batches/GrAAStrokeRectBatch.cpp
src/gpu/batches/GrAAStrokeRectBatch.h
src/gpu/batches/GrNonAAStrokeRectBatch.cpp
src/gpu/batches/GrNonAAStrokeRectBatch.h
src/gpu/batches/GrRectBatchFactory.h
src/gpu/effects/GrDashingEffect.cpp

diff --git a/gm/stroke_rect_shader.cpp b/gm/stroke_rect_shader.cpp
new file mode 100644 (file)
index 0000000..0eb09e9
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm.h"
+#include "SkGradientShader.h"
+
+namespace skiagm {
+
+// Draw stroked rects (both AA and nonAA) with all the types of joins:
+//    bevel, miter, miter-limited-to-bevel, round
+// and as a hairline.
+DEF_SIMPLE_GM(stroke_rect_shader, canvas, 690, 300) {
+    static constexpr SkRect kRect {0, 0, 100, 100};
+    static constexpr SkPoint kPts[] {{kRect.fLeft, kRect.fTop}, {kRect.fRight, kRect.fBottom}};
+    static constexpr SkColor kColors[] {SK_ColorRED, SK_ColorBLUE};
+    SkPaint paint;
+    sk_sp<SkShader> shader = SkGradientShader::MakeLinear(kPts, kColors, nullptr, 2,
+                                                          SkShader::kClamp_TileMode);
+    paint.setShader(std::move(shader));
+    paint.setStyle(SkPaint::kStroke_Style);
+    // Do a large initial translate so that local coords disagree with device coords significantly
+    // for the first rect drawn.
+    canvas->translate(kRect.centerX(), kRect.centerY());
+    static constexpr SkScalar kPad = 20;
+    for (auto aa : {false, true}) {
+        paint.setAntiAlias(aa);
+        canvas->save();
+
+        static constexpr SkScalar kStrokeWidth = 10;
+        paint.setStrokeWidth(kStrokeWidth);
+
+        paint.setStrokeJoin(SkPaint::kBevel_Join);
+        canvas->drawRect(kRect, paint);
+        canvas->translate(kRect.width() + kPad, 0);
+
+        paint.setStrokeJoin(SkPaint::kMiter_Join);
+        canvas->drawRect(kRect, paint);
+        canvas->translate(kRect.width() + kPad, 0);
+
+        // This miter limit should effectively produce a bevel join.
+        paint.setStrokeMiter(0.01f);
+        canvas->drawRect(kRect, paint);
+        canvas->translate(kRect.width() + kPad, 0);
+
+        paint.setStrokeJoin(SkPaint::kRound_Join);
+        canvas->drawRect(kRect, paint);
+        canvas->translate(kRect.width() + kPad, 0);
+
+        paint.setStrokeWidth(0);
+        canvas->drawRect(kRect, paint);
+
+        canvas->restore();
+        canvas->translate(0, kRect.height() + kPad);
+    }
+}
+
+}
index 293ffed..c0f1f3e 100644 (file)
@@ -409,9 +409,10 @@ public:
         kRound_Cap,     //!< begin/end contours with a semi-circle extension
         kSquare_Cap,    //!< begin/end contours with a half square extension
 
-        kCapCount,
+        kLast_Cap = kSquare_Cap,
         kDefault_Cap = kButt_Cap
     };
+    static constexpr int kCapCount = kLast_Cap + 1;
 
     /** Join enum specifies the settings for the paint's strokejoin. This is
         the treatment that is applied to corners in paths and rectangles.
@@ -421,9 +422,10 @@ public:
         kRound_Join,    //!< connect path segments with a round join
         kBevel_Join,    //!< connect path segments with a flat bevel join
 
-        kJoinCount,
+        kLast_Join = kBevel_Join,
         kDefault_Join = kMiter_Join
     };
+    static constexpr int kJoinCount = kLast_Join + 1;
 
     /** Return the paint's stroke cap type, controlling how the start and end
         of stroked lines and paths are treated.
index d057984..5474707 100644 (file)
@@ -311,11 +311,12 @@ void GrDrawContext::drawRect(const GrClip& clip,
     AutoCheckFlush acf(fDrawingManager);
 
     const SkStrokeRec& stroke = style->strokeRec();
-    SkScalar width = stroke.getWidth();
-
-    // Check if this is a full RT draw and can be replaced with a clear. We don't bother checking
-    // cases where the RT is fully inside a stroke.
-    if (width < 0) {
+    bool useHWAA;
+    bool snapToPixelCenters = false;
+    SkAutoTUnref<GrDrawBatch> batch;
+    if (stroke.getStyle() == SkStrokeRec::kFill_Style) {
+        // Check if this is a full RT draw and can be replaced with a clear. We don't bother
+        // checking cases where the RT is fully inside a stroke.
         SkRect rtRect;
         fRenderTarget->getBoundsRect(&rtRect);
         // Does the clip contain the entire RT?
@@ -339,32 +340,54 @@ void GrDrawContext::drawRect(const GrClip& clip,
                 }
             }
         }
-    }
-
-    bool useHWAA;
-    bool snapToPixelCenters = false;
-    SkAutoTUnref<GrDrawBatch> batch;
-    if (width < 0) {
         batch.reset(this->getFillRectBatch(paint, viewMatrix, rect, &useHWAA));
-    } else {
+    } else if (stroke.getStyle() == SkStrokeRec::kStroke_Style ||
+               stroke.getStyle() == SkStrokeRec::kHairline_Style) {
+        if ((!rect.width() || !rect.height()) &&
+            SkStrokeRec::kHairline_Style != stroke.getStyle()) {
+            SkScalar r = stroke.getWidth() / 2;
+            // TODO: Move these stroke->fill fallbacks to GrShape?
+            switch (stroke.getJoin()) {
+                case SkPaint::kMiter_Join:
+                    this->drawRect(clip, paint, viewMatrix,
+                                   {rect.fLeft - r, rect.fTop - r,
+                                    rect.fRight + r, rect.fBottom + r},
+                                   &GrStyle::SimpleFill());
+                    return;
+                case SkPaint::kRound_Join:
+                    // Raster draws nothing when both dimensions are empty.
+                    if (rect.width() || rect.height()){
+                        SkRRect rrect = SkRRect::MakeRectXY(rect.makeOutset(r, r), r, r);
+                        this->drawRRect(clip, paint, viewMatrix, rrect, GrStyle::SimpleFill());
+                        return;
+                    }
+                case SkPaint::kBevel_Join:
+                    if (!rect.width()) {
+                        this->drawRect(clip, paint, viewMatrix,
+                                       {rect.fLeft - r, rect.fTop, rect.fRight + r, rect.fBottom},
+                                       &GrStyle::SimpleFill());
+                    } else {
+                        this->drawRect(clip, paint, viewMatrix,
+                                       {rect.fLeft, rect.fTop - r, rect.fRight, rect.fBottom + r},
+                                       &GrStyle::SimpleFill());
+                    }
+                    return;
+                }
+        }
         GrColor color = paint.getColor();
-
         if (should_apply_coverage_aa(paint, fRenderTarget.get(), &useHWAA)) {
             // The stroke path needs the rect to remain axis aligned (no rotation or skew).
             if (viewMatrix.rectStaysRect()) {
-                batch.reset(GrRectBatchFactory::CreateAAStroke(color, viewMatrix, rect,
-                                                               stroke));
+                batch.reset(GrRectBatchFactory::CreateAAStroke(color, viewMatrix, rect, stroke));
             }
         } else {
-            // Non-AA hairlines are snapped to pixel centers to make which pixels are hit
-            // deterministic
-            snapToPixelCenters = (0 == width && !fRenderTarget->isUnifiedMultisampled());
-            batch.reset(GrRectBatchFactory::CreateNonAAStroke(color, viewMatrix, rect,
-                                                              width, snapToPixelCenters));
-
             // Depending on sub-pixel coordinates and the particular GPU, we may lose a corner of
             // hairline rects. We jam all the vertices to pixel centers to avoid this, but not
             // when MSAA is enabled because it can cause ugly artifacts.
+            snapToPixelCenters = stroke.getStyle() == SkStrokeRec::kHairline_Style &&
+                                 !fRenderTarget->isUnifiedMultisampled();
+            batch.reset(GrRectBatchFactory::CreateNonAAStroke(color, viewMatrix, rect,
+                                                              stroke, snapToPixelCenters));
         }
     }
 
index 2759a3e..f6851f1 100644 (file)
@@ -516,24 +516,9 @@ void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect, const SkPaint
     GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawRect", fContext);
     CHECK_SHOULD_DRAW(draw);
 
-    bool doStroke = paint.getStyle() != SkPaint::kFill_Style;
-    SkScalar width = paint.getStrokeWidth();
-
-    /*
-        We have special code for hairline strokes, miter-strokes, bevel-stroke
-        and fills. Anything else we just call our path code.
-     */
-    bool usePath = doStroke && width > 0 &&
-                   (paint.getStrokeJoin() == SkPaint::kRound_Join ||
-                    (paint.getStrokeJoin() == SkPaint::kBevel_Join && rect.isEmpty()));
-
-    // a few other reasons we might need to call drawPath...
-    if (paint.getMaskFilter() || paint.getPathEffect() ||
-        paint.getStyle() == SkPaint::kStrokeAndFill_Style) { // we can't both stroke and fill rects
-        usePath = true;
-    }
 
-    if (usePath) {
+    // A couple reasons we might need to call drawPath.
+    if (paint.getMaskFilter() || paint.getPathEffect()) {
         SkPath path;
         path.setIsVolatile(true);
         path.addRect(rect);
index 98b764a..3e08932 100644 (file)
@@ -516,14 +516,25 @@ void AAStrokeRectBatch::generateAAStrokeRectGeometry(void* vertices,
     }
 }
 
-inline static bool is_miter(const SkStrokeRec& stroke) {
+// We support all hairlines, bevels, and miters, but not round joins. Also, check whether the miter
+// limit makes a miter join effectively beveled.
+inline static bool allowed_stroke(const SkStrokeRec& stroke, bool* isMiter) {
+    SkASSERT(stroke.getStyle() == SkStrokeRec::kStroke_Style ||
+             stroke.getStyle() == SkStrokeRec::kHairline_Style);
     // For hairlines, make bevel and round joins appear the same as mitered ones.
-    // small miter limit means right angles show bevel...
-    if ((stroke.getWidth() > 0) && (stroke.getJoin() != SkPaint::kMiter_Join ||
-                                    stroke.getMiter() < SK_ScalarSqrt2)) {
-        return false;
+    if (!stroke.getWidth()) {
+        *isMiter = true;
+        return true;
     }
-    return true;
+    if (stroke.getJoin() == SkPaint::kBevel_Join) {
+        *isMiter = false;
+        return true;
+    }
+    if (stroke.getJoin() == SkPaint::kMiter_Join) {
+        *isMiter = stroke.getMiter() >= SK_ScalarSqrt2;
+        return true;
+    }
+    return false;
 }
 
 static void compute_rects(SkRect* devOutside, SkRect* devOutsideAssist, SkRect* devInside,
@@ -596,7 +607,10 @@ GrDrawBatch* Create(GrColor color,
                     const SkMatrix& viewMatrix,
                     const SkRect& rect,
                     const SkStrokeRec& stroke) {
-    bool isMiterStroke = is_miter(stroke);
+    bool isMiterStroke;
+    if (!allowed_stroke(stroke, &isMiterStroke)) {
+        return nullptr;
+    }
     AAStrokeRectBatch* batch = AAStrokeRectBatch::Create(viewMatrix, isMiterStroke);
 
     SkRect devOutside, devOutsideAssist, devInside;
@@ -609,28 +623,6 @@ GrDrawBatch* Create(GrColor color,
     return batch;
 }
 
-bool Append(GrBatch* origBatch,
-            GrColor color,
-            const SkMatrix& viewMatrix,
-            const SkRect& rect,
-            const SkStrokeRec& stroke) {
-    AAStrokeRectBatch* batch = origBatch->cast<AAStrokeRectBatch>();
-
-    // we can't batch across vm changes
-    bool isMiterStroke = is_miter(stroke);
-    if (!batch->canAppend(viewMatrix, isMiterStroke)) {
-        return false;
-    }
-
-    SkRect devOutside, devOutsideAssist, devInside;
-    bool isDegenerate;
-    compute_rects(&devOutside, &devOutsideAssist, &devInside, &isDegenerate, viewMatrix,
-                  rect, stroke.getWidth(), isMiterStroke);
-
-    batch->appendAndUpdateBounds(color, devOutside, devOutsideAssist, devInside, isDegenerate);
-    return true;
-}
-
 };
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
index e0069a1..964cc5b 100644 (file)
@@ -29,12 +29,6 @@ GrDrawBatch* Create(GrColor color,
                     const SkRect& rect,
                     const SkStrokeRec& stroke);
 
-bool Append(GrBatch*,
-            GrColor color,
-            const SkMatrix& viewMatrix,
-            const SkRect& rect,
-            const SkStrokeRec& stroke);
-
-};
+}
 
 #endif
index a38760c..fb90608 100644 (file)
@@ -211,29 +211,30 @@ private:
     typedef GrVertexBatch INHERITED;
 };
 
+// Allow all hairlines and all miters, so long as the miter limit doesn't produce beveled corners.
+inline static bool allowed_stroke(const SkStrokeRec& stroke) {
+    SkASSERT(stroke.getStyle() == SkStrokeRec::kStroke_Style ||
+             stroke.getStyle() == SkStrokeRec::kHairline_Style);
+    return !stroke.getWidth() ||
+            (stroke.getJoin() == SkPaint::kMiter_Join && stroke.getMiter() > SK_ScalarSqrt2);
+}
+
 namespace GrNonAAStrokeRectBatch {
 
 GrDrawBatch* Create(GrColor color,
                     const SkMatrix& viewMatrix,
                     const SkRect& rect,
-                    SkScalar strokeWidth,
+                    const SkStrokeRec& stroke,
                     bool snapToPixelCenters) {
+    if (!allowed_stroke(stroke)) {
+        return nullptr;
+    }
     NonAAStrokeRectBatch* batch = NonAAStrokeRectBatch::Create();
-    batch->append(color, viewMatrix, rect, strokeWidth);
+    batch->append(color, viewMatrix, rect, stroke.getWidth());
     batch->init(snapToPixelCenters);
     return batch;
 }
 
-void Append(GrBatch* origBatch,
-            GrColor color,
-            const SkMatrix& viewMatrix,
-            const SkRect& rect,
-            SkScalar strokeWidth,
-            bool snapToPixelCenters) {
-    NonAAStrokeRectBatch* batch = origBatch->cast<NonAAStrokeRectBatch>();
-    batch->appendAndUpdateBounds(color, viewMatrix, rect, strokeWidth, snapToPixelCenters);
-}
-
 };
 
 #ifdef GR_TEST_UTILS
@@ -242,9 +243,13 @@ DRAW_BATCH_TEST_DEFINE(NonAAStrokeRectBatch) {
     SkMatrix viewMatrix = GrTest::TestMatrix(random);
     GrColor color = GrRandomColor(random);
     SkRect rect = GrTest::TestRect(random);
-    SkScalar strokeWidth = random->nextBool() ? 0.0f : 1.0f;
-
-    return GrNonAAStrokeRectBatch::Create(color, viewMatrix, rect, strokeWidth, random->nextBool());
+    SkScalar strokeWidth = random->nextBool() ? 0.0f : 2.0f;
+    SkPaint paint;
+    paint.setStrokeWidth(strokeWidth);
+    paint.setStyle(SkPaint::kStroke_Style);
+    paint.setStrokeJoin(SkPaint::kMiter_Join);
+    SkStrokeRec strokeRec(paint);
+    return GrNonAAStrokeRectBatch::Create(color, viewMatrix, rect, strokeRec, random->nextBool());
 }
 
 #endif
index 3cee47d..4d94337 100644 (file)
@@ -14,6 +14,7 @@
 
 class GrDrawBatch;
 struct SkRect;
+class SkStrokeRec;
 class SkMatrix;
 
 namespace GrNonAAStrokeRectBatch {
@@ -21,15 +22,9 @@ namespace GrNonAAStrokeRectBatch {
 GrDrawBatch* Create(GrColor color,
                     const SkMatrix& viewMatrix,
                     const SkRect& rect,
-                    SkScalar strokeWidth,
+                    const SkStrokeRec&,
                     bool snapToPixelCenters);
 
-void Append(GrColor color,
-            const SkMatrix& viewMatrix,
-            const SkRect& rect,
-            SkScalar strokeWidth,
-            bool snapToPixelCenters);
-
-};
+}
 
 #endif
index 16eeaee..4512f6a 100644 (file)
@@ -55,9 +55,9 @@ inline GrDrawBatch* CreateAAFill(GrColor color,
 inline GrDrawBatch* CreateNonAAStroke(GrColor color,
                                       const SkMatrix& viewMatrix,
                                       const SkRect& rect,
-                                      SkScalar strokeWidth,
+                                      const SkStrokeRec& strokeRec,
                                       bool snapToPixelCenters) {
-    return GrNonAAStrokeRectBatch::Create(color, viewMatrix, rect, strokeWidth, snapToPixelCenters);
+    return GrNonAAStrokeRectBatch::Create(color, viewMatrix, rect, strokeRec, snapToPixelCenters);
 }
 
 inline GrDrawBatch* CreateAAStroke(GrColor color,
index ae37540..4e82a62 100644 (file)
@@ -1240,7 +1240,7 @@ DRAW_BATCH_TEST_DEFINE(DashBatch) {
     }
 
     // pick random cap
-    SkPaint::Cap cap = SkPaint::Cap(random->nextULessThan(SkPaint::Cap::kCapCount));
+    SkPaint::Cap cap = SkPaint::Cap(random->nextULessThan(SkPaint::kCapCount));
 
     SkScalar intervals[2];