From 18eafd922d911606dfe991efad8ec5eaafbc2704 Mon Sep 17 00:00:00 2001 From: Florin Malita Date: Thu, 4 Jan 2018 21:11:55 -0500 Subject: [skotty, sksg] Add layer transform inheritance support Split the matrix component of sksg::Transform into its own, free-floating, chainable node. Update the composite transform animator to target matrix nodes instead of transform nodes. Update the layer transform attachment logic to follow "parent" references, and build matrix inheritance chains on the fly. TBR= Change-Id: I017e5e462274c2cc210730e057b3ea2e7de5c0cb Reviewed-on: https://skia-review.googlesource.com/90803 Reviewed-by: Florin Malita Commit-Queue: Florin Malita --- experimental/skotty/Skotty.cpp | 124 +++++++++++++++++++++------- experimental/skotty/SkottyProperties.cpp | 6 +- experimental/skotty/SkottyProperties.h | 10 +-- experimental/sksg/SkSGNode.cpp | 4 +- experimental/sksg/SkSGNode.h | 5 +- experimental/sksg/effects/SkSGTransform.cpp | 51 ++++++++++-- experimental/sksg/effects/SkSGTransform.h | 50 +++++++++-- 7 files changed, 197 insertions(+), 53 deletions(-) (limited to 'experimental') diff --git a/experimental/skotty/Skotty.cpp b/experimental/skotty/Skotty.cpp index e84abcd4a8..3635c2c83b 100644 --- a/experimental/skotty/Skotty.cpp +++ b/experimental/skotty/Skotty.cpp @@ -29,6 +29,7 @@ #include "SkTHash.h" #include +#include #include #include "stdlib.h" @@ -81,33 +82,34 @@ bool AttachProperty(const Json::Value& jprop, AttachContext* ctx, const sk_sp AttachTransform(const Json::Value& t, AttachContext* ctx, - sk_sp wrapped_node) { - if (!t.isObject() || !wrapped_node) - return wrapped_node; +sk_sp AttachMatrix(const Json::Value& t, AttachContext* ctx, + sk_sp parentMatrix) { + if (!t.isObject()) + return nullptr; - auto xform = sk_make_sp(wrapped_node); - auto anchor_attached = AttachProperty(t["a"], ctx, xform, + auto matrix = sksg::Matrix::Make(SkMatrix::I(), std::move(parentMatrix)); + auto composite = sk_make_sp(matrix); + auto anchor_attached = AttachProperty(t["a"], ctx, composite, [](const sk_sp& node, const SkPoint& a) { node->setAnchorPoint(a); }); - auto position_attached = AttachProperty(t["p"], ctx, xform, + auto position_attached = AttachProperty(t["p"], ctx, composite, [](const sk_sp& node, const SkPoint& p) { node->setPosition(p); }); - auto scale_attached = AttachProperty(t["s"], ctx, xform, + auto scale_attached = AttachProperty(t["s"], ctx, composite, [](const sk_sp& node, const SkVector& s) { node->setScale(s); }); - auto rotation_attached = AttachProperty(t["r"], ctx, xform, + auto rotation_attached = AttachProperty(t["r"], ctx, composite, [](const sk_sp& node, SkScalar r) { node->setRotation(r); }); - auto skew_attached = AttachProperty(t["sk"], ctx, xform, + auto skew_attached = AttachProperty(t["sk"], ctx, composite, [](const sk_sp& node, SkScalar sk) { node->setSkew(sk); }); - auto skewaxis_attached = AttachProperty(t["sa"], ctx, xform, + auto skewaxis_attached = AttachProperty(t["sa"], ctx, composite, [](const sk_sp& node, SkScalar sa) { node->setSkewAxis(sa); }); @@ -119,10 +121,10 @@ sk_sp AttachTransform(const Json::Value& t, AttachContext* ctx !skew_attached && !skewaxis_attached) { LogFail(t, "Could not parse transform"); - return wrapped_node; + return nullptr; } - return xform->node(); + return matrix; } sk_sp AttachShape(const Json::Value&, AttachContext* ctx); @@ -327,12 +329,6 @@ static constexpr GroupAttacherT gGroupAttachers[] = { AttachShapeGroup, }; -using TransformAttacherT = sk_sp (*)(const Json::Value&, AttachContext*, - sk_sp); -static constexpr TransformAttacherT gTransformAttachers[] = { - AttachTransform, -}; - using GeometryEffectAttacherT = std::vector> (*)(const Json::Value&, AttachContext*, @@ -365,7 +361,7 @@ const ShapeInfo* FindShapeInfo(const Json::Value& shape) { { "sh", ShapeType::kGeometry , 0 }, // shape -> AttachPathGeometry { "sr", ShapeType::kGeometry , 3 }, // polystar -> AttachPolyStarGeometry { "st", ShapeType::kPaint , 1 }, // stroke -> AttachStrokePaint - { "tr", ShapeType::kTransform , 0 }, // transform -> AttachTransform + { "tr", ShapeType::kTransform , 0 }, // transform -> In-place handler }; if (!shape.isObject()) @@ -449,8 +445,10 @@ sk_sp AttachShape(const Json::Value& shapeArray, AttachContext } break; case ShapeType::kTransform: { // TODO: BM appears to transform the geometry, not the draw op itself. - SkASSERT(info->fAttacherIndex < SK_ARRAY_COUNT(gTransformAttachers)); - xformed_group = gTransformAttachers[info->fAttacherIndex](s, ctx, xformed_group); + if (auto matrix = AttachMatrix(s, ctx, nullptr)) { + xformed_group = sksg::Transform::Make(std::move(xformed_group), + std::move(matrix)); + } } break; } } @@ -503,7 +501,8 @@ sk_sp AttachImageLayer(const Json::Value& layer, AttachContext sk_sp AttachNullLayer(const Json::Value& layer, AttachContext*) { SkASSERT(layer.isObject()); - LOG("?? Null layer stub\n"); + // Null layers are used solely to drive dependent transforms, + // but we use free-floating sksg::Matrices for that purpose. return nullptr; } @@ -522,8 +521,65 @@ sk_sp AttachTextLayer(const Json::Value& layer, AttachContext* return nullptr; } -sk_sp AttachLayer(const Json::Value& layer, AttachContext* ctx) { - if (!layer.isObject()) +struct AttachLayerContext { + AttachLayerContext(const Json::Value& jlayers, AttachContext* ctx) + : fLayerList(jlayers), fCtx(ctx) {} + + const Json::Value& fLayerList; + AttachContext* fCtx; + std::unordered_map> fLayerMatrixCache; + std::unordered_map fLayerIndexCache; + + const Json::Value* findLayer(int index) { + SkASSERT(fLayerList.isArray()); + + if (index < 0) { + return nullptr; + } + + const auto cached = fLayerIndexCache.find(index); + if (cached != fLayerIndexCache.end()) { + return cached->second; + } + + for (const auto& l : fLayerList) { + if (!l.isObject()) { + continue; + } + + if (ParseInt(l["ind"], -1) == index) { + fLayerIndexCache.insert(std::make_pair(index, &l)); + return &l; + } + } + + return nullptr; + } + + sk_sp AttachLayerMatrix(const Json::Value& jlayer) { + SkASSERT(jlayer.isObject()); + + const auto cached = fLayerMatrixCache.find(&jlayer); + if (cached != fLayerMatrixCache.end()) { + return cached->second; + } + + const auto* parentLayer = this->findLayer(ParseInt(jlayer["parent"], -1)); + + // TODO: cycle detection? + auto parentMatrix = (parentLayer && parentLayer != &jlayer) + ? this->AttachLayerMatrix(*parentLayer) : nullptr; + + auto layerMatrix = AttachMatrix(jlayer["ks"], fCtx, std::move(parentMatrix)); + fLayerMatrixCache.insert(std::make_pair(&jlayer, layerMatrix)); + + return layerMatrix; + } +}; + +sk_sp AttachLayer(const Json::Value& jlayer, + AttachLayerContext* layerCtx) { + if (!jlayer.isObject()) return nullptr; using LayerAttacher = sk_sp (*)(const Json::Value&, AttachContext*); @@ -536,22 +592,32 @@ sk_sp AttachLayer(const Json::Value& layer, AttachContext* ctx AttachTextLayer, // 'ty': 5 }; - int type = ParseInt(layer["ty"], -1); + int type = ParseInt(jlayer["ty"], -1); if (type < 0 || type >= SkTo(SK_ARRAY_COUNT(gLayerAttachers))) { return nullptr; } - return AttachTransform(layer["ks"], ctx, gLayerAttachers[type](layer, ctx)); + auto layer = gLayerAttachers[type](jlayer, layerCtx->fCtx); + auto layerMatrix = layerCtx->AttachLayerMatrix(jlayer); + + return layerMatrix + ? sksg::Transform::Make(std::move(layer), std::move(layerMatrix)) + : layer; } sk_sp AttachComposition(const Json::Value& comp, AttachContext* ctx) { if (!comp.isObject()) return nullptr; + const auto& jlayers = comp["layers"]; + if (!jlayers.isArray()) + return nullptr; + SkSTArray<16, sk_sp, true> layers; + AttachLayerContext layerCtx(jlayers, ctx); - for (const auto& l : comp["layers"]) { - if (auto layer_fragment = AttachLayer(l, ctx)) { + for (const auto& l : jlayers) { + if (auto layer_fragment = AttachLayer(l, &layerCtx)) { layers.push_back(std::move(layer_fragment)); } } diff --git a/experimental/skotty/SkottyProperties.cpp b/experimental/skotty/SkottyProperties.cpp index 3029409f6c..0783ce8aaa 100644 --- a/experimental/skotty/SkottyProperties.cpp +++ b/experimental/skotty/SkottyProperties.cpp @@ -174,8 +174,8 @@ void CompositeRRect::apply() { fRRectNode->setRRect(rr); } -CompositeTransform::CompositeTransform(sk_sp wrapped_node) - : fTransformNode(sksg::Transform::Make(std::move(wrapped_node), SkMatrix::I())) {} +CompositeTransform::CompositeTransform(sk_sp matrix) + : fMatrixNode(std::move(matrix)) {} void CompositeTransform::apply() { SkMatrix t = SkMatrix::MakeTrans(-fAnchorPoint.x(), -fAnchorPoint.y()); @@ -185,7 +185,7 @@ void CompositeTransform::apply() { t.postTranslate(fPosition.x(), fPosition.y()); // TODO: skew - fTransformNode->setMatrix(t); + fMatrixNode->setMatrix(t); } CompositePolyStar::CompositePolyStar(sk_sp wrapped_node, Type t) diff --git a/experimental/skotty/SkottyProperties.h b/experimental/skotty/SkottyProperties.h index 8730f61352..164f78093c 100644 --- a/experimental/skotty/SkottyProperties.h +++ b/experimental/skotty/SkottyProperties.h @@ -21,10 +21,10 @@ class SkPath; namespace sksg { +class Matrix; class Path; class RRect; -class RenderNode; -class Transform; +class RenderNode;; } namespace skotty { @@ -139,9 +139,7 @@ private: class CompositeTransform final : public SkRefCnt { public: - explicit CompositeTransform(sk_sp); - - const sk_sp& node() const { return fTransformNode; } + explicit CompositeTransform(sk_sp); COMPOSITE_PROPERTY(AnchorPoint, SkPoint , SkPoint::Make(0, 0)) COMPOSITE_PROPERTY(Position , SkPoint , SkPoint::Make(0, 0)) @@ -153,7 +151,7 @@ public: private: void apply(); - sk_sp fTransformNode; + sk_sp fMatrixNode; using INHERITED = SkRefCnt; }; diff --git a/experimental/sksg/SkSGNode.cpp b/experimental/sksg/SkSGNode.cpp index 13e9864147..fc4d278580 100644 --- a/experimental/sksg/SkSGNode.cpp +++ b/experimental/sksg/SkSGNode.cpp @@ -128,8 +128,8 @@ const SkRect& Node::revalidate(InvalidationController* ic, const SkMatrix& ctm) } const auto result = this->onRevalidate(ic, ctm); - const auto selfDamage = result.fReval == Damage::kForceSelf || - (this->hasSelfInval() && result.fReval != Damage::kBlockSelf); + const auto selfDamage = result.fDamage == Damage::kForceSelf || + (this->hasSelfInval() && result.fDamage != Damage::kBlockSelf); if (selfDamage) { // old bounds diff --git a/experimental/sksg/SkSGNode.h b/experimental/sksg/SkSGNode.h index 58456cf387..1a7560684c 100644 --- a/experimental/sksg/SkSGNode.h +++ b/experimental/sksg/SkSGNode.h @@ -54,18 +54,21 @@ protected: }; struct RevalidationResult { SkRect fBounds; - Damage fReval; + Damage fDamage; }; virtual RevalidationResult onRevalidate(InvalidationController*, const SkMatrix& ctm) = 0; private: void addInvalReceiver(Node*); void removeInvalReceiver(Node*); + // TODO: too friendly, find another way. friend class Draw; friend class EffectNode; friend class Group; + friend class Matrix; friend class Merge; friend class Stroke; + friend class Transform; template void forEachInvalReceiver(Func&&) const; diff --git a/experimental/sksg/effects/SkSGTransform.cpp b/experimental/sksg/effects/SkSGTransform.cpp index dc31623db2..1ea1e619a8 100644 --- a/experimental/sksg/effects/SkSGTransform.cpp +++ b/experimental/sksg/effects/SkSGTransform.cpp @@ -11,21 +11,60 @@ namespace sksg { -Transform::Transform(sk_sp child, const SkMatrix& matrix) +Matrix::Matrix(const SkMatrix& m, sk_sp parent) + : fParent(std::move(parent)) + , fLocalMatrix(m) { + if (fParent) { + fParent->addInvalReceiver(this); + } +} + +Matrix::~Matrix() { + if (fParent) { + fParent->removeInvalReceiver(this); + } +} + +Node::RevalidationResult Matrix::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) { + fTotalMatrix = fLocalMatrix; + + if (fParent) { + fParent->revalidate(ic, ctm); + fTotalMatrix.postConcat(fParent->getTotalMatrix()); + } + + // A free-floating matrix contributes no damage. + return { SkRect::MakeEmpty(), Damage::kBlockSelf }; +} + +Transform::Transform(sk_sp child, sk_sp matrix) : INHERITED(std::move(child)) - , fMatrix(matrix) {} + , fMatrix(std::move(matrix)) { + fMatrix->addInvalReceiver(this); +} + +Transform::~Transform() { + fMatrix->removeInvalReceiver(this); +} void Transform::onRender(SkCanvas* canvas) const { - SkAutoCanvasRestore acr(canvas, !fMatrix.isIdentity()); - canvas->concat(fMatrix); + const auto& m = fMatrix->getTotalMatrix(); + SkAutoCanvasRestore acr(canvas, !m.isIdentity()); + canvas->concat(m); this->INHERITED::onRender(canvas); } Node::RevalidationResult Transform::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) { SkASSERT(this->hasInval()); - auto result = this->INHERITED::onRevalidate(ic, SkMatrix::Concat(ctm, fMatrix)); - fMatrix.mapRect(&result.fBounds); + // We don't care about matrix reval results, but we do care whether it was invalidated. + const auto localDamage = fMatrix->hasInval() ? Damage::kForceSelf : Damage::kDefault; + fMatrix->revalidate(ic, ctm); + + const auto& m = fMatrix->getTotalMatrix(); + auto result = this->INHERITED::onRevalidate(ic, SkMatrix::Concat(ctm, m)); + m.mapRect(&result.fBounds); + result.fDamage = localDamage; return result; } diff --git a/experimental/sksg/effects/SkSGTransform.h b/experimental/sksg/effects/SkSGTransform.h index 8a97a679ed..0d11739f30 100644 --- a/experimental/sksg/effects/SkSGTransform.h +++ b/experimental/sksg/effects/SkSGTransform.h @@ -15,25 +15,63 @@ namespace sksg { /** - * Concrete Effect node, wrapping an SkMatrix. + * Concrete node, wrapping an SkMatrix, with an optional parent Matrix (to allow chaining): + * + * M' = parent x M + */ +class Matrix : public Node { +public: + static sk_sp Make(const SkMatrix& m, sk_sp parent = nullptr) { + return sk_sp(new Matrix(m, std::move(parent))); + } + + ~Matrix() override; + + SG_ATTRIBUTE(Matrix, SkMatrix, fLocalMatrix) + + const SkMatrix& getTotalMatrix() const { return fTotalMatrix; } + +protected: + explicit Matrix(const SkMatrix&, sk_sp); + + RevalidationResult onRevalidate(InvalidationController*, const SkMatrix&) override; + +private: + sk_sp fParent; + SkMatrix fLocalMatrix, + fTotalMatrix; // cached during revalidation + + typedef Node INHERITED; +}; + +/** + * Concrete Effect node, binding a Matrix to a RenderNode. */ class Transform : public EffectNode { public: - static sk_sp Make(sk_sp child, const SkMatrix& matrix) { - return sk_sp(new Transform(std::move(child), matrix)); + static sk_sp Make(sk_sp child, sk_sp matrix) { + return child && matrix + ? sk_sp(new Transform(std::move(child), std::move(matrix))) + : nullptr; } - SG_ATTRIBUTE(Matrix, SkMatrix, fMatrix) + static sk_sp Make(sk_sp child, const SkMatrix& m) { + return Make(std::move(child), Matrix::Make(m)); + } + + ~Transform() override; + + const sk_sp& getMatrix() const { return fMatrix; } protected: - Transform(sk_sp, const SkMatrix&); + Transform(sk_sp, sk_sp); void onRender(SkCanvas*) const override; RevalidationResult onRevalidate(InvalidationController*, const SkMatrix&) override; private: - SkMatrix fMatrix; + sk_sp fMatrix; typedef EffectNode INHERITED; }; -- cgit v1.2.3