diff options
author | Florin Malita <fmalita@chromium.org> | 2017-12-30 12:27:00 -0500 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2017-12-30 22:55:39 +0000 |
commit | 094ccde2380bfbb615e25d0d80208148fcd47f17 (patch) | |
tree | d6233c61996a3f268938953608b0b3a2d4590e07 /experimental/skotty/SkottyProperties.cpp | |
parent | 83b2b08afcf903a455cd0ea999d0c2936088fffd (diff) |
Initial Lottie loader impl (Skotty)
Coarse workflow:
* Construction
1) build a Json tree
2) collect asset IDs (for preComp/image layer resolution)
3) "attach" pass
- traverse the Json tree
- build an SkSG dom, one fragment at a time
- attach "animator" objects to the dom, for each animated prop
4) done, we can throw away the Json tree
* For each animation tick
1) iterate over active animators and poke their respective dom nodes/attributes
2) revalidate the SkSG dom
3) draw the SkSG dom
Note: post construction, things are super-simple - we just poke SkSG DOM attributes
with interpolated values, and everything else is handled by SkSG (invalidation,
revalidation, render).
Change-Id: I96a02be7eb4fb4cb3831f59bf2b3908ea190c0dd
Reviewed-on: https://skia-review.googlesource.com/89420
Reviewed-by: Mike Reed <reed@google.com>
Commit-Queue: Florin Malita <fmalita@chromium.org>
Diffstat (limited to 'experimental/skotty/SkottyProperties.cpp')
-rw-r--r-- | experimental/skotty/SkottyProperties.cpp | 157 |
1 files changed, 157 insertions, 0 deletions
diff --git a/experimental/skotty/SkottyProperties.cpp b/experimental/skotty/SkottyProperties.cpp new file mode 100644 index 0000000000..f65a106735 --- /dev/null +++ b/experimental/skotty/SkottyProperties.cpp @@ -0,0 +1,157 @@ +/* + * 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 "SkottyProperties.h" + +#include "SkColor.h" +#include "SkottyPriv.h" +#include "SkPath.h" +#include "SkSGTransform.h" + +namespace skotty { + +namespace { + +using PointArray = SkSTArray<64, SkPoint, true>; + +bool ParsePoints(const Json::Value& v, PointArray* pts) { + if (!v.isArray()) { + return false; + } + + for (Json::ArrayIndex i = 0; i < v.size(); ++i) { + const auto& pt = v[i]; + if (!pt.isArray() || pt.size() != 2 || + !pt[0].isConvertibleTo(Json::realValue) || + !pt[1].isConvertibleTo(Json::realValue)) { + return false; + } + + pts->push_back(SkPoint::Make(ParseScalar(pt[0], 0), ParseScalar(pt[1], 0))); + } + return true; +} + +} // namespace + +bool ScalarValue::Parse(const Json::Value& v, ScalarValue* scalar) { + if (v.isNull() || !v.isConvertibleTo(Json::realValue)) + return false; + + scalar->fVal = ParseScalar(v, 0); + return true; +} + +bool VectorValue::Parse(const Json::Value& v, VectorValue* vec) { + SkASSERT(vec->fVals.empty()); + + if (!v.isArray()) + return false; + + for (Json::ArrayIndex i = 0; i < v.size(); ++i) { + const auto& el = v[i]; + if (el.isNull() || !el.isConvertibleTo(Json::realValue)) + return false; + + vec->fVals.emplace_back(ParseScalar(el, 0)); + } + + return true; +} + +bool ShapeValue::Parse(const Json::Value& v, ShapeValue* shape) { + PointArray inPts, + outPts, + verts; + + // Some files appear to wrap these in arrays for no reason. + if (v.isArray()) { + return Parse(v[0], shape); + } + + if (!v.isObject() || + !ParsePoints(v["i"], &inPts) || + !ParsePoints(v["o"], &outPts) || + !ParsePoints(v["v"], &verts) || + inPts.count() != outPts.count() || + inPts.count() != verts.count()) { + + return false; + } + + SkASSERT(shape->fVertices.empty()); + for (int i = 0; i < inPts.count(); ++i) { + shape->fVertices.emplace_back(BezierVertex({inPts[i], outPts[i], verts[i]})); + } + + shape->fClose = ParseBool(v["c"], false); + + return true; +} + +template <> +SkColor VectorValue::as<SkColor>() const { + // best effort to turn this into a color + const auto r = fVals.count() > 0 ? fVals[0].as<SkScalar>() : 0, + g = fVals.count() > 1 ? fVals[1].as<SkScalar>() : 0, + b = fVals.count() > 2 ? fVals[2].as<SkScalar>() : 0, + a = fVals.count() > 3 ? fVals[3].as<SkScalar>() : 1; + + return SkColorSetARGB(SkTPin<SkScalar>(a, 0, 1) * 255, + SkTPin<SkScalar>(r, 0, 1) * 255, + SkTPin<SkScalar>(g, 0, 1) * 255, + SkTPin<SkScalar>(b, 0, 1) * 255); +} + +template <> +SkPoint VectorValue::as<SkPoint>() const { + // best effort to turn this into a point + const auto x = fVals.count() > 0 ? fVals[0].as<SkScalar>() : 0, + y = fVals.count() > 1 ? fVals[1].as<SkScalar>() : 0; + return SkPoint::Make(x, y); +} + +template <> +SkPath ShapeValue::as<SkPath>() const { + SkPath path; + + if (!fVertices.empty()) { + path.moveTo(fVertices.front().fVertex); + } + + const auto& addCubic = [](const BezierVertex& from, const BezierVertex& to, SkPath* path) { + path->cubicTo(from.fVertex + from.fOutPoint, + to.fVertex + to.fInPoint, + to.fVertex); + }; + + for (int i = 1; i < fVertices.count(); ++i) { + addCubic(fVertices[i - 1], fVertices[i], &path); + } + + if (fClose) { + addCubic(fVertices.back(), fVertices.front(), &path); + } + + return path; +} + +CompositeTransform::CompositeTransform(sk_sp<sksg::RenderNode> wrapped_node) + : fTransformNode(sksg::Transform::Make(std::move(wrapped_node), SkMatrix::I())) {} + +void CompositeTransform::apply() { + SkMatrix t = SkMatrix::MakeTrans(-fAnchorPoint.x(), -fAnchorPoint.y()); + + t.postScale(fScale.x() / 100, fScale.y() / 100); // 100% based + t.postRotate(fRotation); + t.postTranslate(fPosition.x(), fPosition.y()); + // TODO: skew + + fTransformNode->setMatrix(t); +} + +} // namespace skotty |