[skotty,sksg] Initial RRect support

Bug: skia:
Change-Id: I51bf6619e8d857d5d14fcd6651c144bd3c59453f
Reviewed-on: https://skia-review.googlesource.com/90027
Commit-Queue: Florin Malita <fmalita@chromium.org>
Reviewed-by: Mike Reed <reed@google.com>
diff --git a/experimental/skotty/Skotty.cpp b/experimental/skotty/Skotty.cpp
index 7a8e9e3..b94cad2 100644
--- a/experimental/skotty/Skotty.cpp
+++ b/experimental/skotty/Skotty.cpp
@@ -21,6 +21,7 @@
 #include "SkSGInvalidationController.h"
 #include "SkSGGroup.h"
 #include "SkSGPath.h"
+#include "SkSGRect.h"
 #include "SkSGTransform.h"
 #include "SkStream.h"
 #include "SkTArray.h"
@@ -79,7 +80,7 @@
 
 sk_sp<sksg::RenderNode> AttachTransform(const Json::Value& t, AttachContext* ctx,
                                         sk_sp<sksg::RenderNode> wrapped_node) {
-    if (!t.isObject())
+    if (!t.isObject() || !wrapped_node)
         return wrapped_node;
 
     auto xform = sk_make_sp<CompositeTransform>(wrapped_node);
@@ -143,6 +144,26 @@
     return path_attached ? path_node : nullptr;
 }
 
+sk_sp<sksg::GeometryNode> AttachRRectGeometry(const Json::Value& jrect, AttachContext* ctx) {
+    SkASSERT(jrect.isObject());
+
+    auto rect_node = sksg::RRect::Make();
+    auto composite = sk_make_sp<CompositeRRect>(rect_node);
+
+    auto p_attached = AttachProperty<VectorValue, SkPoint>(jrect["p"], ctx, composite,
+            [](const sk_sp<CompositeRRect>& node, const SkPoint& pos) { node->setPosition(pos); });
+    auto s_attached = AttachProperty<VectorValue, SkSize>(jrect["s"], ctx, composite,
+            [](const sk_sp<CompositeRRect>& node, const SkSize& sz) { node->setSize(sz); });
+    auto r_attached = AttachProperty<ScalarValue, SkScalar>(jrect["r"], ctx, composite,
+            [](const sk_sp<CompositeRRect>& node, SkScalar radius) { node->setRadius(radius); });
+
+    if (!p_attached && !s_attached && !r_attached) {
+        return nullptr;
+    }
+
+    return rect_node;
+}
+
 sk_sp<sksg::Color> AttachColorPaint(const Json::Value& obj, AttachContext* ctx) {
     SkASSERT(obj.isObject());
 
@@ -205,6 +226,7 @@
 using GeometryAttacherT = sk_sp<sksg::GeometryNode> (*)(const Json::Value&, AttachContext*);
 static constexpr GeometryAttacherT gGeometryAttachers[] = {
     AttachPathGeometry,
+    AttachRRectGeometry,
 };
 
 using PaintAttacherT = sk_sp<sksg::PaintNode> (*)(const Json::Value&, AttachContext*);
@@ -241,6 +263,7 @@
     static constexpr ShapeInfo gShapeInfo[] = {
         { "fl", ShapeType::kPaint    , 0 }, // fill      -> AttachFillPaint
         { "gr", ShapeType::kGroup    , 0 }, // group     -> AttachShapeGroup
+        { "rc", ShapeType::kGeometry , 1 }, // shape     -> AttachRRectGeometry
         { "sh", ShapeType::kGeometry , 0 }, // shape     -> AttachPathGeometry
         { "st", ShapeType::kPaint    , 1 }, // stroke    -> AttachStrokePaint
         { "tr", ShapeType::kTransform, 0 }, // transform -> AttachTransform
diff --git a/experimental/skotty/SkottyProperties.cpp b/experimental/skotty/SkottyProperties.cpp
index 1a3a145..7ee4008 100644
--- a/experimental/skotty/SkottyProperties.cpp
+++ b/experimental/skotty/SkottyProperties.cpp
@@ -10,6 +10,7 @@
 #include "SkColor.h"
 #include "SkottyPriv.h"
 #include "SkPath.h"
+#include "SkSGRect.h"
 #include "SkSGTransform.h"
 
 namespace  skotty {
@@ -121,6 +122,24 @@
 }
 
 template <>
+SkSize VectorValue::as<SkSize>() const {
+    const auto pt = this->as<SkPoint>();
+    return SkSize::Make(pt.x(), pt.y());
+}
+
+template <>
+std::vector<SkScalar> VectorValue::as<std::vector<SkScalar>>() const {
+    std::vector<SkScalar> vec;
+    vec.reserve(fVals.count());
+
+    for (const auto& val : fVals) {
+        vec.push_back(val);
+    }
+
+    return vec;
+}
+
+template <>
 SkPath ShapeValue::as<SkPath>() const {
     SkPath path;
 
@@ -146,6 +165,19 @@
     return path;
 }
 
+CompositeRRect::CompositeRRect(sk_sp<sksg::RRect> wrapped_node)
+    : fRRectNode(std::move(wrapped_node)) {}
+
+void CompositeRRect::apply() {
+    // BM "position" == "center position"
+    auto rr = SkRRect::MakeRectXY(SkRect::MakeXYWH(fPosition.x() - fSize.width() / 2,
+                                                   fPosition.y() - fSize.height() / 2,
+                                                   fSize.width(), fSize.height()),
+                                  fRadius,
+                                  fRadius);
+   fRRectNode->setRRect(rr);
+}
+
 CompositeTransform::CompositeTransform(sk_sp<sksg::RenderNode> wrapped_node)
     : fTransformNode(sksg::Transform::Make(std::move(wrapped_node), SkMatrix::I())) {}
 
diff --git a/experimental/skotty/SkottyProperties.h b/experimental/skotty/SkottyProperties.h
index 0273aea..e150474 100644
--- a/experimental/skotty/SkottyProperties.h
+++ b/experimental/skotty/SkottyProperties.h
@@ -20,6 +20,7 @@
 class SkPath;
 
 namespace sksg {
+class RRect;
 class RenderNode;
 class Transform;
 }
@@ -100,6 +101,22 @@
     p_type f##p_name = p_default;                     \
   public:
 
+class CompositeRRect final : public SkRefCnt {
+public:
+    explicit CompositeRRect(sk_sp<sksg::RRect>);
+
+    COMPOSITE_PROPERTY(Position, SkPoint , SkPoint::Make(0, 0))
+    COMPOSITE_PROPERTY(Size    , SkSize  , SkSize::Make(0, 0))
+    COMPOSITE_PROPERTY(Radius  , SkScalar, 0)
+
+private:
+    void apply();
+
+    sk_sp<sksg::RRect> fRRectNode;
+
+    using INHERITED = SkRefCnt;
+};
+
 class CompositeTransform final : public SkRefCnt {
 public:
     explicit CompositeTransform(sk_sp<sksg::RenderNode>);
diff --git a/experimental/sksg/geometry/SkSGRect.cpp b/experimental/sksg/geometry/SkSGRect.cpp
index 69bd946..53e6fba 100644
--- a/experimental/sksg/geometry/SkSGRect.cpp
+++ b/experimental/sksg/geometry/SkSGRect.cpp
@@ -22,4 +22,14 @@
     return fRect;
 }
 
+RRect::RRect(const SkRRect& rr) : fRRect(rr) {}
+
+void RRect::onDraw(SkCanvas* canvas, const SkPaint& paint) const {
+    canvas->drawRRect(fRRect, paint);
+}
+
+SkRect RRect::onComputeBounds() const {
+    return fRRect.getBounds();
+}
+
 } // namespace sksg
diff --git a/experimental/sksg/geometry/SkSGRect.h b/experimental/sksg/geometry/SkSGRect.h
index a0e5ec6..cc912f0 100644
--- a/experimental/sksg/geometry/SkSGRect.h
+++ b/experimental/sksg/geometry/SkSGRect.h
@@ -11,6 +11,7 @@
 #include "SkSGGeometryNode.h"
 
 #include "SkRect.h"
+#include "SkRRect.h"
 
 class SkCanvas;
 class SkPaint;
@@ -20,7 +21,7 @@
 /**
  * Concrete Geometry node, wrapping an SkRect.
  */
-class Rect : public GeometryNode {
+class Rect final : public GeometryNode {
 public:
     static sk_sp<Rect> Make()                { return sk_sp<Rect>(new Rect(SkRect::MakeEmpty())); }
     static sk_sp<Rect> Make(const SkRect& r) { return sk_sp<Rect>(new Rect(r)); }
@@ -41,6 +42,27 @@
     SkRect fRect;
 };
 
+/**
+ * Concrete Geometry node, wrapping an SkRRect.
+ */
+class RRect final : public GeometryNode {
+public:
+    static sk_sp<RRect> Make()                  { return sk_sp<RRect>(new RRect(SkRRect())); }
+    static sk_sp<RRect> Make(const SkRRect& rr) { return sk_sp<RRect>(new RRect(rr)); }
+
+    SG_ATTRIBUTE(RRect, SkRRect, fRRect)
+
+protected:
+    void onDraw(SkCanvas*, const SkPaint&) const override;
+
+    SkRect onComputeBounds() const override;
+
+private:
+    explicit RRect(const SkRRect&);
+
+    SkRRect fRRect;
+};
+
 } // namespace sksg
 
 #endif // SkSGRect_DEFINED