From cfbb56c6e180f6d49c9ec664d823e01bd438df9f Mon Sep 17 00:00:00 2001 From: Florin Malita Date: Wed, 8 Jul 2020 07:52:20 -0400 Subject: [PATCH] [skottie] Auto-orient fixes Two issues: 1) For static keyframes (start_value == end_value) AE yields horizontal orientation (0 tangent). We technically have the same logic in Skottie, but our value deduplication logic interferes: the two consecutive equal values are consolidated, and the result ends up holding the spatial lerp info for the next frame => our hold frames auto-orient for the beginning of the next keyframe. Fix: skip value deduplication when spatial lerp is present. 2) The very last keyframe is always static and holds no spatial info. AE retains the orientation of the previous frame, but Skottie yields 0 tangent. Fix: the easiest way to accomplish AE semantics is to detect when we're dealing with the last keyframe, and swap with the previous keyframe with an adjust weight of 1 (to select the end value). This produces the same lerp result (because keyframed values are always contiguous) and also respects the orientation of the prev frame. TBR= Change-Id: Id661f7804533e95b747722457489a7ef759572a4 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/301176 Reviewed-by: Mike Reed Commit-Queue: Florin Malita Commit-Queue: Florin Malita --- .../src/animator/Vec2KeyframeAnimator.cpp | 45 ++++++++++++++----- resources/skottie/skottie-auto-orient-2.json | 1 + 2 files changed, 36 insertions(+), 10 deletions(-) create mode 100644 resources/skottie/skottie-auto-orient-2.json diff --git a/modules/skottie/src/animator/Vec2KeyframeAnimator.cpp b/modules/skottie/src/animator/Vec2KeyframeAnimator.cpp index 7907203156..6b90fc2d76 100644 --- a/modules/skottie/src/animator/Vec2KeyframeAnimator.cpp +++ b/modules/skottie/src/animator/Vec2KeyframeAnimator.cpp @@ -55,11 +55,6 @@ public: private: void backfill_spatial(const SpatialValue& val) { - if (fTi == SkV2{0,0} && fTo == SkV2{0,0}) { - // no tangents => linear - return; - } - SkASSERT(!fValues.empty()); auto& prev_val = fValues.back(); SkASSERT(!prev_val.cmeasure); @@ -110,13 +105,16 @@ public: return false; } - this->backfill_spatial(val); + if (fPendingSpatial) { + this->backfill_spatial(val); + } // Track the last keyframe spatial tangents (checked on next parseValue). - fTi = ParseDefault(jkf["ti"], {0,0}); - fTo = ParseDefault(jkf["to"], {0,0}); + fTi = ParseDefault(jkf["ti"], {0,0}); + fTo = ParseDefault(jkf["to"], {0,0}); + fPendingSpatial = fTi != SkV2{0,0} || fTo != SkV2{0,0}; - if (fValues.empty() || val.v2 != fValues.back().v2) { + if (fValues.empty() || val.v2 != fValues.back().v2 || fPendingSpatial) { fValues.push_back(std::move(val)); } @@ -130,6 +128,7 @@ public: float* fRotTarget; // optional SkV2 fTi{0,0}, fTo{0,0}; + bool fPendingSpatial = false; }; private: @@ -155,7 +154,33 @@ private: } StateChanged onSeek(float t) override { - const auto& lerp_info = this->getLERPInfo(t); + auto get_lerp_info = [this](float t) { + auto lerp_info = this->getLERPInfo(t); + + // When tracking rotation/orientation, the last keyframe requires special handling: + // it doesn't store any spatial information but it is expected to maintain the + // previous orientation (per AE semantics). + // + // The easiest way to achieve this is to actually swap with the previous keyframe, + // with an adjusted weight of 1. + const auto vidx = lerp_info.vrec0.idx; + if (fRotTarget && vidx == fValues.size() - 1 && vidx > 0) { + SkASSERT(!fValues[vidx].cmeasure); + SkASSERT(lerp_info.vrec1.idx == vidx); + + // Change LERPInfo{0, SIZE - 1, SIZE - 1} + // to LERPInfo{1, SIZE - 2, SIZE - 1} + lerp_info.weight = 1; + lerp_info.vrec0 = {vidx - 1}; + + // This yields equivalent lerp results because keyframed values are contiguous + // i.e frame[n-1].end_val == frame[n].start_val. + } + + return lerp_info; + }; + + const auto lerp_info = get_lerp_info(t); const auto& v0 = fValues[lerp_info.vrec0.idx]; if (v0.cmeasure) { diff --git a/resources/skottie/skottie-auto-orient-2.json b/resources/skottie/skottie-auto-orient-2.json new file mode 100644 index 0000000000..b3f3024f66 --- /dev/null +++ b/resources/skottie/skottie-auto-orient-2.json @@ -0,0 +1 @@ +{"assets":[{"id":"comp_0","layers":[{"ao":0,"bm":0,"ddd":0,"ind":1,"ip":0,"ks":{"a":{"a":0,"ix":1,"k":[0,0,0]},"o":{"a":0,"ix":11,"k":100},"p":{"a":0,"ix":2,"k":[250,250,0]},"r":{"a":0,"ix":10,"k":0},"s":{"a":0,"ix":6,"k":[100,100,100]}},"nm":"Shape Layer 1","op":601,"shapes":[{"bm":0,"cix":2,"hd":false,"it":[{"d":1,"hd":false,"ir":{"a":0,"ix":6,"k":5},"is":{"a":0,"ix":8,"k":0},"ix":1,"mn":"ADBE Vector Shape - Star","nm":"Polystar Path 1","or":{"a":0,"ix":7,"k":10},"os":{"a":0,"ix":9,"k":0},"p":{"a":0,"ix":4,"k":[0,0]},"pt":{"a":0,"ix":3,"k":3},"r":{"a":0,"ix":5,"k":-30},"sy":1,"ty":"sr"},{"bm":0,"c":{"a":0,"ix":4,"k":[1,0,0,1]},"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","o":{"a":0,"ix":5,"k":100},"r":1,"ty":"fl"},{"a":{"a":0,"ix":1,"k":[0,0]},"nm":"Transform","o":{"a":0,"ix":7,"k":100},"p":{"a":0,"ix":2,"k":[0,0]},"r":{"a":0,"ix":6,"k":0},"s":{"a":0,"ix":3,"k":[100,100]},"sa":{"a":0,"ix":5,"k":0},"sk":{"a":0,"ix":4,"k":0},"ty":"tr"}],"ix":1,"mn":"ADBE Vector Group","nm":"Group 1","np":2,"ty":"gr"},{"bm":0,"cix":2,"hd":false,"it":[{"d":1,"hd":false,"ir":{"a":0,"ix":6,"k":10},"is":{"a":0,"ix":8,"k":0},"ix":1,"mn":"ADBE Vector Shape - Star","nm":"Polystar Path 1","or":{"a":0,"ix":7,"k":20},"os":{"a":0,"ix":9,"k":0},"p":{"a":0,"ix":4,"k":[-10,0]},"pt":{"a":0,"ix":3,"k":3},"r":{"a":0,"ix":5,"k":-30},"sy":1,"ty":"sr"},{"bm":0,"c":{"a":0,"ix":4,"k":[0,0.98059129715,0.110340073705,1]},"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","o":{"a":0,"ix":5,"k":100},"r":1,"ty":"fl"},{"a":{"a":0,"ix":1,"k":[0,0]},"nm":"Transform","o":{"a":0,"ix":7,"k":100},"p":{"a":0,"ix":2,"k":[0,0]},"r":{"a":0,"ix":6,"k":0},"s":{"a":0,"ix":3,"k":[100,100]},"sa":{"a":0,"ix":5,"k":0},"sk":{"a":0,"ix":4,"k":0},"ty":"tr"}],"ix":2,"mn":"ADBE Vector Group","nm":"Group 2","np":2,"ty":"gr"}],"sr":1,"st":0,"ty":4}]}],"ddd":0,"fr":60,"h":500,"ip":0,"layers":[{"ao":1,"bm":0,"ddd":0,"h":500,"ind":1,"ip":0,"ks":{"a":{"a":0,"ix":1,"k":[250,250,0]},"o":{"a":0,"ix":11,"k":100},"p":{"a":1,"ix":2,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"s":[50,350,0],"t":0,"ti":[0,0,0],"to":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"s":[50,350,0],"t":60,"ti":[-50,41.667,0],"to":[50,-41.667,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"s":[350,100,0],"t":180,"ti":[0,0,0],"to":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"s":[350,100,0],"t":240,"ti":[389.833,130,0],"to":[295.167,113,0]},{"s":[50,350,0],"t":360}]},"r":{"a":0,"ix":10,"k":0},"s":{"a":0,"ix":6,"k":[100,100,100]}},"nm":"precomp","op":601,"refId":"comp_0","sr":1,"st":0,"ty":0,"w":500},{"ao":0,"bm":0,"ddd":0,"ind":2,"ip":0,"ks":{"a":{"a":0,"ix":1,"k":[0,0,0]},"o":{"a":0,"ix":11,"k":100},"p":{"a":0,"ix":2,"k":[250,250,0]},"r":{"a":0,"ix":10,"k":0},"s":{"a":0,"ix":6,"k":[100,100,100]}},"nm":"Shape Layer 1","op":421,"shapes":[{"hd":false,"ind":0,"ix":1,"ks":{"a":0,"ix":2,"k":{"c":false,"i":[[0,0],[-74.001,62.595],[52.868,-132.17],[175,59]],"o":[[74,-62.468],[0,0],[-40,100],[-116.558,-39.297]],"v":[[-201,101],[98,-148],[231,36],[-201,101]]}},"mn":"ADBE Vector Shape - Group","nm":"Path 1","ty":"sh"},{"bm":0,"c":{"a":0,"ix":3,"k":[0,0,0,1]},"hd":false,"lc":1,"lj":1,"ml":4,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","o":{"a":0,"ix":4,"k":100},"ty":"st","w":{"a":0,"ix":5,"k":2}}],"sr":1,"st":0,"ty":4},{"ao":0,"bm":0,"ddd":0,"ind":3,"ip":0,"ks":{"a":{"a":0,"ix":1,"k":[250,250,0]},"o":{"a":0,"ix":11,"k":100},"p":{"a":0,"ix":2,"k":[250,250,0]},"r":{"a":0,"ix":10,"k":0},"s":{"a":0,"ix":6,"k":[100,100,100]}},"nm":"White Solid 1","op":421,"sc":"#ffffff","sh":500,"sr":1,"st":0,"sw":500,"ty":1}],"markers":[],"nm":"tricky cases","op":421,"v":"5.7.0","w":500} \ No newline at end of file