Add bounds to GrShape
authorbsalomon <bsalomon@google.com>
Fri, 13 May 2016 16:23:38 +0000 (09:23 -0700)
committerCommit bot <commit-bot@chromium.org>
Fri, 13 May 2016 16:23:38 +0000 (09:23 -0700)
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1970003003

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

include/core/SkRect.h
src/gpu/GrShape.cpp
src/gpu/GrShape.h
src/gpu/GrStyle.h
tests/GrShapeTest.cpp

index 3ebe099..39cbb33 100644 (file)
@@ -390,10 +390,8 @@ struct SK_API SkIRect {
 struct SK_API SkRect {
     SkScalar    fLeft, fTop, fRight, fBottom;
 
-    static SkRect SK_WARN_UNUSED_RESULT MakeEmpty() {
-        SkRect r;
-        r.setEmpty();
-        return r;
+    static constexpr SkRect SK_WARN_UNUSED_RESULT MakeEmpty() {
+        return SkRect{0, 0, 0, 0};
     }
 
     static SkRect SK_WARN_UNUSED_RESULT MakeLargest() {
@@ -506,7 +504,7 @@ struct SK_API SkRect {
 
     /** Set this rectangle to the empty rectangle (0,0,0,0)
     */
-    void setEmpty() { memset(this, 0, sizeof(*this)); }
+    void setEmpty() { *this = MakeEmpty(); }
 
     void set(const SkIRect& src) {
         fLeft   = SkIntToScalar(src.fLeft);
index 8462d4d..e0ddc55 100644 (file)
@@ -37,6 +37,28 @@ GrShape& GrShape::operator=(const GrShape& that) {
     return *this;
 }
 
+const SkRect& GrShape::bounds() const {
+    static constexpr SkRect kEmpty = SkRect::MakeEmpty();
+    switch (fType) {
+        case Type::kEmpty:
+            return kEmpty;
+        case Type::kRRect:
+            return fRRect.getBounds();
+        case Type::kPath:
+            return fPath.get()->getBounds();
+    }
+    SkFAIL("Unknown shape type");
+    return kEmpty;
+}
+
+void GrShape::styledBounds(SkRect* bounds) const {
+    if (Type::kEmpty == fType && !fStyle.hasNonDashPathEffect()) {
+        *bounds = SkRect::MakeEmpty();
+    } else {
+        fStyle.adjustBounds(bounds, this->bounds());
+    }
+}
+
 int GrShape::unstyledKeySize() const {
     if (fInheritedKey.count()) {
         return fInheritedKey.count();
index 81684e8..12093f4 100644 (file)
@@ -148,6 +148,12 @@ public:
      */
     bool isEmpty() const { return Type::kEmpty == fType; }
 
+    /** Gets the bounds of the geometry without reflecting the shape's styling. */
+    const SkRect& bounds() const;
+
+    /** Gets the bounds of the geometry reflecting the shape's styling. */
+    void styledBounds(SkRect* bounds) const;
+
     /**
      * Is it known that the unstyled geometry has no unclosed contours. This means that it will
      * not have any caps if stroked (modulo the effect of any path effect).
index 07efb40..cbaa50d 100644 (file)
@@ -167,6 +167,10 @@ public:
     void adjustBounds(SkRect* dst, const SkRect& src) const {
         if (this->pathEffect()) {
             this->pathEffect()->computeFastBounds(dst, src);
+            // This may not be the correct SkStrokeRec to use. skbug.com/5299
+            // It happens to work for dashing.
+            SkScalar radius = fStrokeRec.getInflationRadius();
+            dst->outset(radius, radius);
         } else {
             SkScalar radius = fStrokeRec.getInflationRadius();
             *dst = src.makeOutset(radius, radius);
index 138028d..0e83b69 100644 (file)
 #include "Test.h"
 #if SK_SUPPORT_GPU
 #include "GrShape.h"
-#include "SkPath.h"
+#include "SkCanvas.h"
 #include "SkDashPathEffect.h"
+#include "SkPath.h"
+#include "SkSurface.h"
 
 using Key = SkTArray<uint32_t>;
 
@@ -27,8 +29,44 @@ static bool make_key(Key* key, const GrShape& shape) {
     return true;
 }
 
-namespace {
+static bool test_bounds_by_rasterizing(const SkPath& path, const SkRect& bounds) {
+    static constexpr int kRes = 2000;
+    // This tolerance is in units of 1/kRes fractions of the bounds width/height.
+    static constexpr int kTol = 0;
+    GR_STATIC_ASSERT(kRes % 4 == 0);
+    SkImageInfo info = SkImageInfo::MakeA8(kRes, kRes);
+    sk_sp<SkSurface> surface = SkSurface::MakeRaster(info);
+    surface->getCanvas()->clear(0x0);
+    SkRect clip = SkRect::MakeXYWH(kRes/4, kRes/4, kRes/2, kRes/2);
+    SkMatrix matrix;
+    matrix.setRectToRect(bounds, clip, SkMatrix::kFill_ScaleToFit);
+    clip.outset(SkIntToScalar(kTol), SkIntToScalar(kTol));
+    surface->getCanvas()->clipRect(clip, SkRegion::kDifference_Op);
+    surface->getCanvas()->concat(matrix);
+    SkPaint whitePaint;
+    whitePaint.setColor(SK_ColorWHITE);
+    surface->getCanvas()->drawPath(path, whitePaint);
+    SkPixmap pixmap;
+    surface->getCanvas()->peekPixels(&pixmap);
+#if defined(SK_BUILD_FOR_WIN)
+    // The static constexpr version in #else causes cl.exe to crash.
+    const uint8_t* kZeros = reinterpret_cast<uint8_t*>(calloc(kRes, 1));
+#else
+    static constexpr uint8_t kZeros[kRes] = {0};
+#endif
+    for (int y = 0; y < kRes/4; ++y) {
+        const uint8_t* row = pixmap.addr8(0, y);
+        if (0 != memcmp(kZeros, row, kRes)) {
+            return false;
+        }
+    }
+#ifdef SK_BUILD_FOR_WIN
+    free(const_cast<uint8_t*>(kZeros));
+#endif
+    return true;
+}
 
+namespace {
 class TestCase {
 public:
     template <typename GEO>
@@ -65,6 +103,20 @@ public:
     const Key& appliedPathEffectThenStrokeKey() const { return fAppliedPEThenStrokeKey; }
 
 private:
+    static void CheckBounds(skiatest::Reporter* r, const GrShape& shape, const SkRect& bounds) {
+        SkPath path;
+        shape.asPath(&path);
+        // If the bounds are empty, the path ought to be as well.
+        if (bounds.isEmpty()) {
+            REPORTER_ASSERT(r, path.isEmpty());
+            return;
+        }
+        if (path.isEmpty()) {
+            return;
+        }
+        REPORTER_ASSERT(r, test_bounds_by_rasterizing(path, bounds));
+    }
+
     void init(skiatest::Reporter* r, SkScalar scale) {
         fAppliedPE           = fBase.applyStyle(GrStyle::Apply::kPathEffectOnly, scale);
         fAppliedPEThenStroke = fAppliedPE.applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec,
@@ -93,6 +145,16 @@ private:
         fAppliedFull.asPath(&path);
         REPORTER_ASSERT(r, path.isEmpty() == fAppliedFull.isEmpty());
 
+        CheckBounds(r, fBase, fBase.bounds());
+        CheckBounds(r, fAppliedPE, fAppliedPE.bounds());
+        CheckBounds(r, fAppliedPEThenStroke, fAppliedPEThenStroke.bounds());
+        CheckBounds(r, fAppliedFull, fAppliedFull.bounds());
+        SkRect styledBounds;
+        fBase.styledBounds(&styledBounds);
+        CheckBounds(r, fAppliedFull, styledBounds);
+        fAppliedPE.styledBounds(&styledBounds);
+        CheckBounds(r, fAppliedFull, styledBounds);
+
         // Check that the same path is produced when style is applied by GrShape and GrStyle.
         SkPath preStyle;
         SkPath postPathEffect;