aboutsummaryrefslogtreecommitdiffhomepage
path: root/experimental
diff options
context:
space:
mode:
Diffstat (limited to 'experimental')
-rw-r--r--experimental/skotty/Skotty.cpp124
-rw-r--r--experimental/skotty/SkottyProperties.cpp6
-rw-r--r--experimental/skotty/SkottyProperties.h10
-rw-r--r--experimental/sksg/SkSGNode.cpp4
-rw-r--r--experimental/sksg/SkSGNode.h5
-rw-r--r--experimental/sksg/effects/SkSGTransform.cpp51
-rw-r--r--experimental/sksg/effects/SkSGTransform.h50
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;
};