[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:
parent
8bf01945da
commit
cfbb56c6e1
@ -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) {
|
||||
|
1
resources/skottie/skottie-auto-orient-2.json
Normal file
1
resources/skottie/skottie-auto-orient-2.json
Normal 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}
|
Loading…
Reference in New Issue
Block a user