diff options
Diffstat (limited to 'experimental/skottie/SkottieValue.cpp')
-rw-r--r-- | experimental/skottie/SkottieValue.cpp | 93 |
1 files changed, 90 insertions, 3 deletions
diff --git a/experimental/skottie/SkottieValue.cpp b/experimental/skottie/SkottieValue.cpp index 386c8ab623..edfa891aa1 100644 --- a/experimental/skottie/SkottieValue.cpp +++ b/experimental/skottie/SkottieValue.cpp @@ -8,6 +8,7 @@ #include "SkottieValue.h" #include "SkColor.h" +#include "SkNx.h" #include "SkPoint.h" #include "SkSize.h" @@ -19,6 +20,12 @@ size_t ValueTraits<ScalarValue>::Cardinality(const ScalarValue&) { } template <> +ScalarValue ValueTraits<ScalarValue>::Lerp(const ScalarValue& v0, const ScalarValue& v1, float t) { + SkASSERT(t >= 0 && t <= 1); + return v0 + (v1 - v0) * t; +} + +template <> template <> SkScalar ValueTraits<ScalarValue>::As<SkScalar>(const ScalarValue& v) { return v; @@ -30,6 +37,20 @@ size_t ValueTraits<VectorValue>::Cardinality(const VectorValue& vec) { } template <> +VectorValue ValueTraits<VectorValue>::Lerp(const VectorValue& v0, const VectorValue& v1, float t) { + SkASSERT(v0.size() == v1.size()); + + VectorValue v; + v.reserve(v0.size()); + + for (size_t i = 0; i < v0.size(); ++i) { + v.push_back(ValueTraits<ScalarValue>::Lerp(v0[i], v1[i], t)); + } + + return v; +} + +template <> template <> SkColor ValueTraits<VectorValue>::As<SkColor>(const VectorValue& v) { // best effort to turn this into a color @@ -61,13 +82,79 @@ SkSize ValueTraits<VectorValue>::As<SkSize>(const VectorValue& vec) { } template <> -size_t ValueTraits<ShapeValue>::Cardinality(const ShapeValue& path) { - return SkTo<size_t>(path.countVerbs()); +size_t ValueTraits<ShapeValue>::Cardinality(const ShapeValue& shape) { + return shape.fVertices.size(); +} + +static SkPoint lerp_point(const SkPoint& v0, const SkPoint& v1, const Sk2f& t) { + const auto v2f0 = Sk2f::Load(&v0), + v2f1 = Sk2f::Load(&v1); + + SkPoint v; + (v2f0 + (v2f1 - v2f0) * t).store(&v); + + return v; +} + +template <> +ShapeValue ValueTraits<ShapeValue>::Lerp(const ShapeValue& v0, const ShapeValue& v1, float t) { + SkASSERT(t >= 0 && t <= 1); + SkASSERT(v0.fVertices.size() == v1.fVertices.size()); + SkASSERT(v0.fClosed == v1.fClosed); + + ShapeValue v; + v.fClosed = v0.fClosed; + v.fVolatile = true; // interpolated values are volatile + + const auto t2f = Sk2f(t); + v.fVertices.reserve(v0.fVertices.size()); + + for (size_t i = 0; i < v0.fVertices.size(); ++i) { + v.fVertices.emplace_back(BezierVertex({ + lerp_point(v0.fVertices[i].fInPoint , v1.fVertices[i].fInPoint , t2f), + lerp_point(v0.fVertices[i].fOutPoint, v1.fVertices[i].fOutPoint, t2f), + lerp_point(v0.fVertices[i].fVertex , v1.fVertices[i].fVertex , t2f) + })); + } + + return v; } template <> template <> -SkPath ValueTraits<ShapeValue>::As<SkPath>(const ShapeValue& path) { +SkPath ValueTraits<ShapeValue>::As<SkPath>(const ShapeValue& shape) { + SkPath path; + + if (!shape.fVertices.empty()) { + path.moveTo(shape.fVertices.front().fVertex); + } + + const auto& addCubic = [&](size_t from, size_t to) { + const auto c0 = shape.fVertices[from].fVertex + shape.fVertices[from].fOutPoint, + c1 = shape.fVertices[to].fVertex + shape.fVertices[to].fInPoint; + + if (c0 == shape.fVertices[from].fVertex && + c1 == shape.fVertices[to].fVertex) { + // If the control points are coincident, we can power-reduce to a straight line. + // TODO: we could also do that when the controls are on the same line as the + // vertices, but it's unclear how common that case is. + path.lineTo(shape.fVertices[to].fVertex); + } else { + path.cubicTo(c0, c1, shape.fVertices[to].fVertex); + } + }; + + for (size_t i = 1; i < shape.fVertices.size(); ++i) { + addCubic(i - 1, i); + } + + if (!shape.fVertices.empty() && shape.fClosed) { + addCubic(shape.fVertices.size() - 1, 0); + path.close(); + } + + path.setIsVolatile(shape.fVolatile); + return path; } |