From e6345d90f8f9d975128de9890d26b1af1fedc524 Mon Sep 17 00:00:00 2001 From: Florin Malita Date: Wed, 3 Jan 2018 23:37:54 -0500 Subject: [skotty,sksg] Add support for geometry merge TBR= Change-Id: Ia5edbfeae61779ead6031f6dd4e33794b3eefdc0 Reviewed-on: https://skia-review.googlesource.com/90382 Reviewed-by: Florin Malita Commit-Queue: Florin Malita --- experimental/skotty/Skotty.cpp | 59 ++++++++++++++++++----- experimental/sksg/SkSGGeometryNode.cpp | 7 +++ experimental/sksg/SkSGGeometryNode.h | 5 +- experimental/sksg/SkSGNode.h | 1 + experimental/sksg/geometry/SkSGMerge.cpp | 81 ++++++++++++++++++++++++++++++++ experimental/sksg/geometry/SkSGMerge.h | 61 ++++++++++++++++++++++++ experimental/sksg/geometry/SkSGPath.cpp | 4 ++ experimental/sksg/geometry/SkSGPath.h | 1 + experimental/sksg/geometry/SkSGRect.cpp | 13 +++++ experimental/sksg/geometry/SkSGRect.h | 2 + 10 files changed, 221 insertions(+), 13 deletions(-) create mode 100644 experimental/sksg/geometry/SkSGMerge.cpp create mode 100644 experimental/sksg/geometry/SkSGMerge.h (limited to 'experimental') diff --git a/experimental/skotty/Skotty.cpp b/experimental/skotty/Skotty.cpp index f2006bac0b..47ff828183 100644 --- a/experimental/skotty/Skotty.cpp +++ b/experimental/skotty/Skotty.cpp @@ -20,6 +20,7 @@ #include "SkSGDraw.h" #include "SkSGInvalidationController.h" #include "SkSGGroup.h" +#include "SkSGMerge.h" #include "SkSGPath.h" #include "SkSGRect.h" #include "SkSGTransform.h" @@ -28,6 +29,8 @@ #include "SkTHash.h" #include +#include + #include "stdlib.h" namespace skotty { @@ -223,6 +226,26 @@ sk_sp AttachStrokePaint(const Json::Value& jstroke, AttachConte return stroke_node; } +std::vector> AttachMergeGeometryEffect( + const Json::Value& jmerge, AttachContext* ctx, std::vector>&& geos) { + std::vector> merged; + + static constexpr sksg::Merge::Mode gModes[] = { + sksg::Merge::Mode::kMerge, // "mm": 1 + sksg::Merge::Mode::kUnion, // "mm": 2 + sksg::Merge::Mode::kDifference, // "mm": 3 + sksg::Merge::Mode::kIntersect, // "mm": 4 + sksg::Merge::Mode::kXOR , // "mm": 5 + }; + + const auto mode = gModes[SkTPin(ParseInt(jmerge["mm"], 1) - 1, 0, SK_ARRAY_COUNT(gModes))]; + merged.push_back(sksg::Merge::Make(std::move(geos), mode)); + + LOG("** Attached merge path effect, mode: %d\n", mode); + + return merged; +} + using GeometryAttacherT = sk_sp (*)(const Json::Value&, AttachContext*); static constexpr GeometryAttacherT gGeometryAttachers[] = { AttachPathGeometry, @@ -246,8 +269,17 @@ static constexpr TransformAttacherT gTransformAttachers[] = { AttachTransform, }; +using GeometryEffectAttacherT = + std::vector> (*)(const Json::Value&, + AttachContext*, + std::vector>&&); +static constexpr GeometryEffectAttacherT gGeometryEffectAttachers[] = { + AttachMergeGeometryEffect, +}; + enum class ShapeType { kGeometry, + kGeometryEffect, kPaint, kGroup, kTransform, @@ -261,12 +293,13 @@ struct ShapeInfo { const ShapeInfo* FindShapeInfo(const Json::Value& shape) { 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 + { "fl", ShapeType::kPaint , 0 }, // fill -> AttachFillPaint + { "gr", ShapeType::kGroup , 0 }, // group -> AttachShapeGroup + { "mm", ShapeType::kGeometryEffect, 0 }, // merge -> AttachMergeGeometryEffect + { "rc", ShapeType::kGeometry , 1 }, // shape -> AttachRRectGeometry + { "sh", ShapeType::kGeometry , 0 }, // shape -> AttachPathGeometry + { "st", ShapeType::kPaint , 1 }, // stroke -> AttachStrokePaint + { "tr", ShapeType::kTransform , 0 }, // transform -> AttachTransform }; if (!shape.isObject()) @@ -313,8 +346,8 @@ sk_sp AttachShape(const Json::Value& shapeArray, AttachContext sk_sp shape_group = sksg::Group::Make(); sk_sp xformed_group = shape_group; - SkSTArray<16, sk_sp, true> geos; - SkSTArray<16, sk_sp , true> draws; + std::vector> geos; + std::vector> draws; for (const auto& s : shapeArray) { const auto* info = FindShapeInfo(s); @@ -330,6 +363,10 @@ sk_sp AttachShape(const Json::Value& shapeArray, AttachContext geos.push_back(std::move(geo)); } } break; + case ShapeType::kGeometryEffect: { + SkASSERT(info->fAttacherIndex < SK_ARRAY_COUNT(gGeometryEffectAttachers)); + geos = gGeometryEffectAttachers[info->fAttacherIndex](s, ctx, std::move(geos)); + } break; case ShapeType::kPaint: { SkASSERT(info->fAttacherIndex < SK_ARRAY_COUNT(gPaintAttachers)); if (auto paint = gPaintAttachers[info->fAttacherIndex](s, ctx)) { @@ -356,11 +393,11 @@ sk_sp AttachShape(const Json::Value& shapeArray, AttachContext return nullptr; } - for (int i = draws.count() - 1; i >= 0; --i) { - shape_group->addChild(std::move(draws[i])); + for (auto draw = draws.rbegin(); draw != draws.rend(); ++draw) { + shape_group->addChild(std::move(*draw)); } - LOG("** Attached shape: %d draws.\n", draws.count()); + LOG("** Attached shape: %zd draws.\n", draws.size()); return xformed_group; } diff --git a/experimental/sksg/SkSGGeometryNode.cpp b/experimental/sksg/SkSGGeometryNode.cpp index 6ac7eda6a4..cbc0d558e2 100644 --- a/experimental/sksg/SkSGGeometryNode.cpp +++ b/experimental/sksg/SkSGGeometryNode.cpp @@ -7,6 +7,8 @@ #include "SkSGGeometryNode.h" +#include "SkPath.h" + namespace sksg { void GeometryNode::draw(SkCanvas* canvas, const SkPaint& paint) const { @@ -14,4 +16,9 @@ void GeometryNode::draw(SkCanvas* canvas, const SkPaint& paint) const { this->onDraw(canvas, paint); } +SkPath GeometryNode::asPath() const { + SkASSERT(!this->hasInval()); + return this->onAsPath(); +} + } // namespace sksg diff --git a/experimental/sksg/SkSGGeometryNode.h b/experimental/sksg/SkSGGeometryNode.h index a43937510f..a898b06519 100644 --- a/experimental/sksg/SkSGGeometryNode.h +++ b/experimental/sksg/SkSGGeometryNode.h @@ -12,6 +12,7 @@ class SkCanvas; class SkPaint; +class SkPath; namespace sksg { @@ -25,14 +26,14 @@ class GeometryNode : public Node { public: void draw(SkCanvas*, const SkPaint&) const; - // SkPath asPath() const; // unused for now + SkPath asPath() const; protected: GeometryNode() = default; virtual void onDraw(SkCanvas*, const SkPaint&) const = 0; - // virtual SkPath onAsPath() const = 0; // unused for now + virtual SkPath onAsPath() const = 0; private: friend class Draw; // wants to know the cached bounds. diff --git a/experimental/sksg/SkSGNode.h b/experimental/sksg/SkSGNode.h index bc5970a3d9..7758eca4c5 100644 --- a/experimental/sksg/SkSGNode.h +++ b/experimental/sksg/SkSGNode.h @@ -55,6 +55,7 @@ private: friend class Draw; friend class EffectNode; friend class Group; + friend class Merge; friend class Stroke; template diff --git a/experimental/sksg/geometry/SkSGMerge.cpp b/experimental/sksg/geometry/SkSGMerge.cpp new file mode 100644 index 0000000000..a9f06d464f --- /dev/null +++ b/experimental/sksg/geometry/SkSGMerge.cpp @@ -0,0 +1,81 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkSGMerge.h" + +#include "SkCanvas.h" +#include "SkPathOps.h" + +namespace sksg { + +Merge::Merge(std::vector>&& geos, Mode mode) + : fGeos(std::move(geos)) + , fMode(mode) { + for (const auto& geo : fGeos) { + geo->addInvalReceiver(this); + } +} + +Merge::~Merge() { + for (const auto& geo : fGeos) { + geo->removeInvalReceiver(this); + } +} + +void Merge::onDraw(SkCanvas* canvas, const SkPaint& paint) const { + SkASSERT(!this->hasInval()); + canvas->drawPath(fMerged, paint); +} + +SkPath Merge::onAsPath() const { + return fMerged; +} + +static SkPathOp mode_to_op(Merge::Mode mode) { + switch (mode) { + case Merge::Mode::kUnion: + return kUnion_SkPathOp; + case Merge::Mode::kIntersect: + return kIntersect_SkPathOp; + case Merge::Mode::kDifference: + return kDifference_SkPathOp; + case Merge::Mode::kReverseDifference: + return kReverseDifference_SkPathOp; + case Merge::Mode::kXOR: + return kXOR_SkPathOp; + default: + break; + } + + return kUnion_SkPathOp; +} + +SkRect Merge::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) { + SkASSERT(this->hasInval()); + + const auto op = mode_to_op(fMode); + SkOpBuilder builder; + + fMerged.reset(); + + for (const auto& geo : fGeos) { + geo->revalidate(ic, ctm); + if (fMode == Mode::kMerge) { + fMerged.addPath(geo->asPath()); + } else { + builder.add(geo->asPath(), geo == fGeos.front() ? kUnion_SkPathOp : op); + } + } + + if (fMode != Mode::kMerge) { + builder.resolve(&fMerged); + } + + return fMerged.computeTightBounds(); +} + +} // namespace skotty diff --git a/experimental/sksg/geometry/SkSGMerge.h b/experimental/sksg/geometry/SkSGMerge.h new file mode 100644 index 0000000000..b0cb40de9c --- /dev/null +++ b/experimental/sksg/geometry/SkSGMerge.h @@ -0,0 +1,61 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkSGMerge_DEFINED +#define SkSGMerge_DEFINED + +#include "SkSGGeometryNode.h" + +#include "SkPath.h" + +#include + +class SkCanvas; +class SkPaint; + +namespace sksg { + +/** + * Concrete Geometry node, combining other geometries based on Mode. + */ +class Merge final : public GeometryNode { +public: + enum class Mode { + // Append path mode. + kMerge, + + // SkPathOp ops. + kUnion, + kIntersect, + kDifference, + kReverseDifference, + kXOR, + }; + + static sk_sp Make(std::vector>&& geos, Mode mode) { + return sk_sp(new Merge(std::move(geos), mode)); + } + + ~Merge() override; + +protected: + void onDraw(SkCanvas*, const SkPaint&) const override; + + SkRect onRevalidate(InvalidationController*, const SkMatrix&) override; + SkPath onAsPath() const override; + +private: + Merge(std::vector>&& geos, Mode); + + std::vector> fGeos; + SkPath fMerged; + Mode fMode; +}; + +} // namespace sksg + +#endif // SkSGMerge_DEFINED diff --git a/experimental/sksg/geometry/SkSGPath.cpp b/experimental/sksg/geometry/SkSGPath.cpp index 9b7b0427ea..a04dcf60cc 100644 --- a/experimental/sksg/geometry/SkSGPath.cpp +++ b/experimental/sksg/geometry/SkSGPath.cpp @@ -24,4 +24,8 @@ SkRect Path::onRevalidate(InvalidationController*, const SkMatrix&) { return fPath.computeTightBounds(); } +SkPath Path::onAsPath() const { + return fPath; +} + } // namespace sksg diff --git a/experimental/sksg/geometry/SkSGPath.h b/experimental/sksg/geometry/SkSGPath.h index 52212cfc82..18caa10f2a 100644 --- a/experimental/sksg/geometry/SkSGPath.h +++ b/experimental/sksg/geometry/SkSGPath.h @@ -31,6 +31,7 @@ protected: void onDraw(SkCanvas*, const SkPaint&) const override; SkRect onRevalidate(InvalidationController*, const SkMatrix&) override; + SkPath onAsPath() const override; private: explicit Path(const SkPath&); diff --git a/experimental/sksg/geometry/SkSGRect.cpp b/experimental/sksg/geometry/SkSGRect.cpp index a49fcf1315..c2ac384bef 100644 --- a/experimental/sksg/geometry/SkSGRect.cpp +++ b/experimental/sksg/geometry/SkSGRect.cpp @@ -9,6 +9,7 @@ #include "SkCanvas.h" #include "SkPaint.h" +#include "SkPath.h" namespace sksg { @@ -24,6 +25,12 @@ SkRect Rect::onRevalidate(InvalidationController*, const SkMatrix&) { return fRect; } +SkPath Rect::onAsPath() const { + SkPath path; + path.addRect(fRect); + return path; +} + RRect::RRect(const SkRRect& rr) : fRRect(rr) {} void RRect::onDraw(SkCanvas* canvas, const SkPaint& paint) const { @@ -36,4 +43,10 @@ SkRect RRect::onRevalidate(InvalidationController*, const SkMatrix&) { return fRRect.getBounds(); } +SkPath RRect::onAsPath() const { + SkPath path; + path.addRRect(fRRect); + return path; +} + } // namespace sksg diff --git a/experimental/sksg/geometry/SkSGRect.h b/experimental/sksg/geometry/SkSGRect.h index 108b1b2186..ad27910da9 100644 --- a/experimental/sksg/geometry/SkSGRect.h +++ b/experimental/sksg/geometry/SkSGRect.h @@ -35,6 +35,7 @@ protected: void onDraw(SkCanvas*, const SkPaint&) const override; SkRect onRevalidate(InvalidationController*, const SkMatrix&) override; + SkPath onAsPath() const override; private: explicit Rect(const SkRect&); @@ -56,6 +57,7 @@ protected: void onDraw(SkCanvas*, const SkPaint&) const override; SkRect onRevalidate(InvalidationController*, const SkMatrix&) override; + SkPath onAsPath() const override; private: explicit RRect(const SkRRect&); -- cgit v1.2.3