[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 <reed@google.com>
Commit-Queue: Florin Malita <fmalita@google.com>
Commit-Queue: Florin Malita <fmalita@chromium.org>
This commit is contained in:
Florin Malita 2020-07-08 07:52:20 -04:00 committed by Skia Commit-Bot
parent 8bf01945da
commit cfbb56c6e1
2 changed files with 36 additions and 10 deletions

View File

@ -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<SkV2>(jkf["ti"], {0,0});
fTo = ParseDefault<SkV2>(jkf["to"], {0,0});
fTi = ParseDefault<SkV2>(jkf["ti"], {0,0});
fTo = ParseDefault<SkV2>(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) {

View File

@ -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}