diff options
author | Florin Malita <fmalita@chromium.org> | 2018-01-04 21:11:55 -0500 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2018-01-05 03:29:04 +0000 |
commit | 18eafd922d911606dfe991efad8ec5eaafbc2704 (patch) | |
tree | ba4bfefd307c84a0045986b3d08d930f0d9380e5 /experimental | |
parent | 98992ae3b7e06aa8de48d91b5b12dd3a5e8ea54f (diff) |
[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 <fmalita@chromium.org>
Commit-Queue: Florin Malita <fmalita@chromium.org>
Diffstat (limited to 'experimental')
-rw-r--r-- | experimental/skotty/Skotty.cpp | 124 | ||||
-rw-r--r-- | experimental/skotty/SkottyProperties.cpp | 6 | ||||
-rw-r--r-- | experimental/skotty/SkottyProperties.h | 10 | ||||
-rw-r--r-- | experimental/sksg/SkSGNode.cpp | 4 | ||||
-rw-r--r-- | experimental/sksg/SkSGNode.h | 5 | ||||
-rw-r--r-- | experimental/sksg/effects/SkSGTransform.cpp | 51 | ||||
-rw-r--r-- | experimental/sksg/effects/SkSGTransform.h | 50 |
7 files changed, 197 insertions, 53 deletions
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 <cmath> +#include <unordered_map> #include <vector> #include "stdlib.h" @@ -81,33 +82,34 @@ bool AttachProperty(const Json::Value& jprop, AttachContext* ctx, const sk_sp<No return true; } -sk_sp<sksg::RenderNode> AttachTransform(const Json::Value& t, AttachContext* ctx, - sk_sp<sksg::RenderNode> wrapped_node) { - if (!t.isObject() || !wrapped_node) - return wrapped_node; +sk_sp<sksg::Matrix> AttachMatrix(const Json::Value& t, AttachContext* ctx, + sk_sp<sksg::Matrix> parentMatrix) { + if (!t.isObject()) + return nullptr; - auto xform = sk_make_sp<CompositeTransform>(wrapped_node); - auto anchor_attached = AttachProperty<VectorValue, SkPoint>(t["a"], ctx, xform, + auto matrix = sksg::Matrix::Make(SkMatrix::I(), std::move(parentMatrix)); + auto composite = sk_make_sp<CompositeTransform>(matrix); + auto anchor_attached = AttachProperty<VectorValue, SkPoint>(t["a"], ctx, composite, [](const sk_sp<CompositeTransform>& node, const SkPoint& a) { node->setAnchorPoint(a); }); - auto position_attached = AttachProperty<VectorValue, SkPoint>(t["p"], ctx, xform, + auto position_attached = AttachProperty<VectorValue, SkPoint>(t["p"], ctx, composite, [](const sk_sp<CompositeTransform>& node, const SkPoint& p) { node->setPosition(p); }); - auto scale_attached = AttachProperty<VectorValue, SkVector>(t["s"], ctx, xform, + auto scale_attached = AttachProperty<VectorValue, SkVector>(t["s"], ctx, composite, [](const sk_sp<CompositeTransform>& node, const SkVector& s) { node->setScale(s); }); - auto rotation_attached = AttachProperty<ScalarValue, SkScalar>(t["r"], ctx, xform, + auto rotation_attached = AttachProperty<ScalarValue, SkScalar>(t["r"], ctx, composite, [](const sk_sp<CompositeTransform>& node, SkScalar r) { node->setRotation(r); }); - auto skew_attached = AttachProperty<ScalarValue, SkScalar>(t["sk"], ctx, xform, + auto skew_attached = AttachProperty<ScalarValue, SkScalar>(t["sk"], ctx, composite, [](const sk_sp<CompositeTransform>& node, SkScalar sk) { node->setSkew(sk); }); - auto skewaxis_attached = AttachProperty<ScalarValue, SkScalar>(t["sa"], ctx, xform, + auto skewaxis_attached = AttachProperty<ScalarValue, SkScalar>(t["sa"], ctx, composite, [](const sk_sp<CompositeTransform>& node, SkScalar sa) { node->setSkewAxis(sa); }); @@ -119,10 +121,10 @@ sk_sp<sksg::RenderNode> 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<sksg::RenderNode> AttachShape(const Json::Value&, AttachContext* ctx); @@ -327,12 +329,6 @@ static constexpr GroupAttacherT gGroupAttachers[] = { AttachShapeGroup, }; -using TransformAttacherT = sk_sp<sksg::RenderNode> (*)(const Json::Value&, AttachContext*, - sk_sp<sksg::RenderNode>); -static constexpr TransformAttacherT gTransformAttachers[] = { - AttachTransform, -}; - using GeometryEffectAttacherT = std::vector<sk_sp<sksg::GeometryNode>> (*)(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<sksg::RenderNode> 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<sksg::RenderNode> AttachImageLayer(const Json::Value& layer, AttachContext sk_sp<sksg::RenderNode> 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<sksg::RenderNode> AttachTextLayer(const Json::Value& layer, AttachContext* return nullptr; } -sk_sp<sksg::RenderNode> 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<const Json::Value*, sk_sp<sksg::Matrix>> fLayerMatrixCache; + std::unordered_map<int, const Json::Value*> 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<sksg::Matrix> 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<sksg::RenderNode> AttachLayer(const Json::Value& jlayer, + AttachLayerContext* layerCtx) { + if (!jlayer.isObject()) return nullptr; using LayerAttacher = sk_sp<sksg::RenderNode> (*)(const Json::Value&, AttachContext*); @@ -536,22 +592,32 @@ sk_sp<sksg::RenderNode> 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<int>(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<sksg::RenderNode> 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<sksg::RenderNode>, 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<sksg::RenderNode> wrapped_node) - : fTransformNode(sksg::Transform::Make(std::move(wrapped_node), SkMatrix::I())) {} +CompositeTransform::CompositeTransform(sk_sp<sksg::Matrix> 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<sksg::Path> 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<sksg::RenderNode>); - - const sk_sp<sksg::Transform>& node() const { return fTransformNode; } + explicit CompositeTransform(sk_sp<sksg::Matrix>); 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<sksg::Transform> fTransformNode; + sk_sp<sksg::Matrix> 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 <typename Func> 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<RenderNode> child, const SkMatrix& matrix) +Matrix::Matrix(const SkMatrix& m, sk_sp<Matrix> 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<RenderNode> child, sk_sp<Matrix> 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<Matrix> Make(const SkMatrix& m, sk_sp<Matrix> parent = nullptr) { + return sk_sp<Matrix>(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<Matrix>); + + RevalidationResult onRevalidate(InvalidationController*, const SkMatrix&) override; + +private: + sk_sp<Matrix> 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<Transform> Make(sk_sp<RenderNode> child, const SkMatrix& matrix) { - return sk_sp<Transform>(new Transform(std::move(child), matrix)); + static sk_sp<Transform> Make(sk_sp<RenderNode> child, sk_sp<Matrix> matrix) { + return child && matrix + ? sk_sp<Transform>(new Transform(std::move(child), std::move(matrix))) + : nullptr; } - SG_ATTRIBUTE(Matrix, SkMatrix, fMatrix) + static sk_sp<Transform> Make(sk_sp<RenderNode> child, const SkMatrix& m) { + return Make(std::move(child), Matrix::Make(m)); + } + + ~Transform() override; + + const sk_sp<Matrix>& getMatrix() const { return fMatrix; } protected: - Transform(sk_sp<RenderNode>, const SkMatrix&); + Transform(sk_sp<RenderNode>, sk_sp<Matrix>); void onRender(SkCanvas*) const override; RevalidationResult onRevalidate(InvalidationController*, const SkMatrix&) override; private: - SkMatrix fMatrix; + sk_sp<Matrix> fMatrix; typedef EffectNode INHERITED; }; |