mirror of https://github.com/procxx/kepka.git
Partially support new spec keyframes parsing.
This commit is contained in:
parent
33b3fa68f0
commit
fbfd3ddd68
|
@ -1 +1 @@
|
||||||
Subproject commit 99604e470f4ebebcb817e6ee5374b34d6b0dd8e4
|
Subproject commit 5cf5976b1874cb39aabcedf30b2b26b5dbd407a6
|
|
@ -56,9 +56,15 @@
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
enum class EasingSegmentState : char {
|
||||||
|
Complete,
|
||||||
|
Incomplete,
|
||||||
|
Final,
|
||||||
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct EasingSegment {
|
struct EasingSegment {
|
||||||
bool complete = false;
|
EasingSegmentState state = EasingSegmentState::Incomplete;
|
||||||
double startFrame = 0;
|
double startFrame = 0;
|
||||||
double endFrame = 0;
|
double endFrame = 0;
|
||||||
T startValue;
|
T startValue;
|
||||||
|
@ -83,12 +89,32 @@ public:
|
||||||
if (m_animated) {
|
if (m_animated) {
|
||||||
QJsonArray keyframes = definition.value(QLatin1String("k")).toArray();
|
QJsonArray keyframes = definition.value(QLatin1String("k")).toArray();
|
||||||
QJsonArray::const_iterator it = keyframes.constBegin();
|
QJsonArray::const_iterator it = keyframes.constBegin();
|
||||||
|
QJsonArray::const_iterator previous;
|
||||||
while (it != keyframes.constEnd()) {
|
while (it != keyframes.constEnd()) {
|
||||||
EasingSegment<T> easing = parseKeyframe((*it).toObject(),
|
QJsonObject keyframe = (*it).toObject();
|
||||||
|
EasingSegment<T> easing = parseKeyframe(keyframe,
|
||||||
fromExpression);
|
fromExpression);
|
||||||
addEasing(easing);
|
addEasing(easing);
|
||||||
|
|
||||||
|
if (m_easingCurves.length() > 1) {
|
||||||
|
postprocessEasingCurve(
|
||||||
|
m_easingCurves.at(m_easingCurves.length() - 2),
|
||||||
|
(*previous).toObject(),
|
||||||
|
fromExpression);
|
||||||
|
}
|
||||||
|
previous = it;
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
|
finalizeEasingCurves();
|
||||||
|
if (m_easingCurves.length() > 0) {
|
||||||
|
const EasingSegment<T> &last = m_easingCurves.last();
|
||||||
|
if (last.state == EasingSegmentState::Complete) {
|
||||||
|
postprocessEasingCurve(
|
||||||
|
last,
|
||||||
|
(*previous).toObject(),
|
||||||
|
fromExpression);
|
||||||
|
}
|
||||||
|
}
|
||||||
m_value = T();
|
m_value = T();
|
||||||
} else
|
} else
|
||||||
m_value = getValue(definition.value(QLatin1String("k")));
|
m_value = getValue(definition.value(QLatin1String("k")));
|
||||||
|
@ -129,16 +155,32 @@ protected:
|
||||||
void addEasing(EasingSegment<T>& easing)
|
void addEasing(EasingSegment<T>& easing)
|
||||||
{
|
{
|
||||||
if (m_easingCurves.length()) {
|
if (m_easingCurves.length()) {
|
||||||
EasingSegment<T> prevEase = m_easingCurves.last();
|
EasingSegment<T> &prevEase = m_easingCurves.last();
|
||||||
// The end value has to be hand picked to the
|
// The end value has to be hand picked to the
|
||||||
// previous easing segment, as the json data does
|
// previous easing segment, as the json data does
|
||||||
// not contain end values for segments
|
// not contain end values for segments
|
||||||
prevEase.endFrame = easing.startFrame - 1;
|
prevEase.endFrame = easing.startFrame - 1;
|
||||||
m_easingCurves.replace(m_easingCurves.length() - 1, prevEase);
|
if (prevEase.state == EasingSegmentState::Incomplete) {
|
||||||
|
prevEase.endValue = easing.startValue;
|
||||||
|
prevEase.state = EasingSegmentState::Complete;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
m_easingCurves.push_back(easing);
|
m_easingCurves.push_back(easing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void finalizeEasingCurves()
|
||||||
|
{
|
||||||
|
if (m_easingCurves.length()) {
|
||||||
|
EasingSegment<T> &last = m_easingCurves.last();
|
||||||
|
if (last.state == EasingSegmentState::Incomplete) {
|
||||||
|
last.endValue = last.startValue;
|
||||||
|
last.endFrame = last.startFrame;
|
||||||
|
this->m_endFrame = last.startFrame;
|
||||||
|
last.state = EasingSegmentState::Final;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const EasingSegment<T>* getEasingSegment(int frame)
|
const EasingSegment<T>* getEasingSegment(int frame)
|
||||||
{
|
{
|
||||||
// TODO: Improve with a faster search algorithm
|
// TODO: Improve with a faster search algorithm
|
||||||
|
@ -178,9 +220,16 @@ protected:
|
||||||
this->m_endFrame = startTime;
|
this->m_endFrame = startTime;
|
||||||
easing.startFrame = startTime;
|
easing.startFrame = startTime;
|
||||||
easing.endFrame = startTime;
|
easing.endFrame = startTime;
|
||||||
|
easing.state = EasingSegmentState::Final;
|
||||||
if (m_easingCurves.length()) {
|
if (m_easingCurves.length()) {
|
||||||
easing.startValue = m_easingCurves.last().endValue;
|
const EasingSegment<T> &last = m_easingCurves.last();
|
||||||
easing.endValue = m_easingCurves.last().endValue;
|
if (last.state == EasingSegmentState::Complete) {
|
||||||
|
easing.startValue = last.endValue;
|
||||||
|
easing.endValue = last.endValue;
|
||||||
|
} else {
|
||||||
|
qCWarning(lcLottieQtBodymovinParser())
|
||||||
|
<< "Last keyframe found after an incomplete one";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return easing;
|
return easing;
|
||||||
}
|
}
|
||||||
|
@ -188,9 +237,12 @@ protected:
|
||||||
if (m_startFrame > startTime)
|
if (m_startFrame > startTime)
|
||||||
m_startFrame = startTime;
|
m_startFrame = startTime;
|
||||||
|
|
||||||
easing.startValue = getValue(keyframe.value(QLatin1String("s")).toArray());
|
|
||||||
easing.endValue = getValue(keyframe.value(QLatin1String("e")).toArray());
|
|
||||||
easing.startFrame = startTime;
|
easing.startFrame = startTime;
|
||||||
|
easing.startValue = getValue(keyframe.value(QLatin1String("s")).toArray());
|
||||||
|
if (keyframe.contains(QLatin1String("e"))) {
|
||||||
|
easing.endValue = getValue(keyframe.value(QLatin1String("e")).toArray());
|
||||||
|
easing.state = EasingSegmentState::Complete;
|
||||||
|
}
|
||||||
|
|
||||||
QJsonObject easingIn = keyframe.value(QLatin1String("i")).toObject();
|
QJsonObject easingIn = keyframe.value(QLatin1String("i")).toObject();
|
||||||
QJsonObject easingOut = keyframe.value(QLatin1String("o")).toObject();
|
QJsonObject easingOut = keyframe.value(QLatin1String("o")).toObject();
|
||||||
|
@ -206,11 +258,15 @@ protected:
|
||||||
|
|
||||||
easing.easing.addCubicBezierSegment(c1, c2, QPointF(1.0, 1.0));
|
easing.easing.addCubicBezierSegment(c1, c2, QPointF(1.0, 1.0));
|
||||||
|
|
||||||
easing.complete = true;
|
|
||||||
|
|
||||||
return easing;
|
return easing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void postprocessEasingCurve(
|
||||||
|
const EasingSegment<T> &easing,
|
||||||
|
const QJsonObject keyframe,
|
||||||
|
bool fromExpression) {
|
||||||
|
}
|
||||||
|
|
||||||
virtual T getValue(const QJsonValue &value)
|
virtual T getValue(const QJsonValue &value)
|
||||||
{
|
{
|
||||||
if (value.isArray())
|
if (value.isArray())
|
||||||
|
@ -278,9 +334,16 @@ protected:
|
||||||
this->m_endFrame = startTime;
|
this->m_endFrame = startTime;
|
||||||
easingCurve.startFrame = startTime;
|
easingCurve.startFrame = startTime;
|
||||||
easingCurve.endFrame = startTime;
|
easingCurve.endFrame = startTime;
|
||||||
|
easingCurve.state = EasingSegmentState::Final;
|
||||||
if (this->m_easingCurves.length()) {
|
if (this->m_easingCurves.length()) {
|
||||||
easingCurve.startValue = this->m_easingCurves.last().endValue;
|
const EasingSegment<T> &last = this->m_easingCurves.last();
|
||||||
easingCurve.endValue = this->m_easingCurves.last().endValue;
|
if (last.state == EasingSegmentState::Complete) {
|
||||||
|
easingCurve.startValue = last.endValue;
|
||||||
|
easingCurve.endValue = last.endValue;
|
||||||
|
} else {
|
||||||
|
qCWarning(lcLottieQtBodymovinParser())
|
||||||
|
<< "Last keyframe found after an incomplete one";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return easingCurve;
|
return easingCurve;
|
||||||
}
|
}
|
||||||
|
@ -288,29 +351,38 @@ protected:
|
||||||
if (this->m_startFrame > startTime)
|
if (this->m_startFrame > startTime)
|
||||||
this->m_startFrame = startTime;
|
this->m_startFrame = startTime;
|
||||||
|
|
||||||
qreal xs, ys, xe, ye;
|
qreal xs, ys;
|
||||||
// Keyframes originating from an expression use only scalar values.
|
// Keyframes originating from an expression use only scalar values.
|
||||||
// They must be expanded for both x and y coordinates
|
// They must be expanded for both x and y coordinates
|
||||||
if (fromExpression) {
|
if (fromExpression) {
|
||||||
xs = startValues.at(0).toDouble();
|
xs = startValues.at(0).toDouble();
|
||||||
ys = startValues.at(0).toDouble();
|
ys = startValues.at(0).toDouble();
|
||||||
xe = endValues.at(0).toDouble();
|
|
||||||
ye = endValues.at(0).toDouble();
|
|
||||||
} else {
|
} else {
|
||||||
xs = startValues.at(0).toDouble();
|
xs = startValues.at(0).toDouble();
|
||||||
ys = startValues.at(1).toDouble();
|
ys = startValues.at(1).toDouble();
|
||||||
xe = endValues.at(0).toDouble();
|
|
||||||
ye = endValues.at(1).toDouble();
|
|
||||||
}
|
}
|
||||||
T s(xs, ys);
|
T s(xs, ys);
|
||||||
T e(xe, ye);
|
|
||||||
|
|
||||||
QJsonObject easingIn = keyframe.value(QLatin1String("i")).toObject();
|
QJsonObject easingIn = keyframe.value(QLatin1String("i")).toObject();
|
||||||
QJsonObject easingOut = keyframe.value(QLatin1String("o")).toObject();
|
QJsonObject easingOut = keyframe.value(QLatin1String("o")).toObject();
|
||||||
|
|
||||||
easingCurve.startFrame = startTime;
|
easingCurve.startFrame = startTime;
|
||||||
easingCurve.startValue = s;
|
easingCurve.startValue = s;
|
||||||
easingCurve.endValue = e;
|
if (!endValues.isEmpty()) {
|
||||||
|
qreal xe, ye;
|
||||||
|
// Keyframes originating from an expression use only scalar values.
|
||||||
|
// They must be expanded for both x and y coordinates
|
||||||
|
if (fromExpression) {
|
||||||
|
xe = endValues.at(0).toDouble();
|
||||||
|
ye = endValues.at(0).toDouble();
|
||||||
|
} else {
|
||||||
|
xe = endValues.at(0).toDouble();
|
||||||
|
ye = endValues.at(1).toDouble();
|
||||||
|
}
|
||||||
|
T e(xe, ye);
|
||||||
|
easingCurve.endValue = e;
|
||||||
|
easingCurve.state = EasingSegmentState::Complete;
|
||||||
|
}
|
||||||
|
|
||||||
if (easingIn.value(QLatin1String("x")).isArray()) {
|
if (easingIn.value(QLatin1String("x")).isArray()) {
|
||||||
QJsonArray eixArr = easingIn.value(QLatin1String("x")).toArray();
|
QJsonArray eixArr = easingIn.value(QLatin1String("x")).toArray();
|
||||||
|
@ -323,7 +395,7 @@ protected:
|
||||||
qreal eix = eixArr.takeAt(0).toDouble();
|
qreal eix = eixArr.takeAt(0).toDouble();
|
||||||
qreal eiy = eiyArr.takeAt(0).toDouble();
|
qreal eiy = eiyArr.takeAt(0).toDouble();
|
||||||
|
|
||||||
qreal eox =eoxArr.takeAt(0).toDouble();
|
qreal eox = eoxArr.takeAt(0).toDouble();
|
||||||
qreal eoy = eoyArr.takeAt(0).toDouble();
|
qreal eoy = eoyArr.takeAt(0).toDouble();
|
||||||
|
|
||||||
QPointF c1 = QPointF(eox, eoy);
|
QPointF c1 = QPointF(eox, eoy);
|
||||||
|
@ -345,7 +417,6 @@ protected:
|
||||||
easingCurve.easing.addCubicBezierSegment(c1, c2, QPointF(1.0, 1.0));
|
easingCurve.easing.addCubicBezierSegment(c1, c2, QPointF(1.0, 1.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
easingCurve.complete = true;
|
|
||||||
return easingCurve;
|
return easingCurve;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -57,13 +57,13 @@ public:
|
||||||
BMProperty2D<QPointF>::construct(definition);
|
BMProperty2D<QPointF>::construct(definition);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual EasingSegment<QPointF> parseKeyframe(const QJsonObject keyframe, bool fromExpression) override
|
virtual void postprocessEasingCurve(
|
||||||
{
|
const EasingSegment<QPointF> &easing,
|
||||||
EasingSegment<QPointF> easing = BMProperty2D<QPointF>::parseKeyframe(keyframe, fromExpression);
|
const QJsonObject keyframe,
|
||||||
|
bool fromExpression) override {
|
||||||
// No need to parse further incomplete keyframes (i.e. last keyframes)
|
// No need to parse further incomplete keyframes (i.e. last keyframes)
|
||||||
if (!easing.complete) {
|
if (easing.state != EasingSegmentState::Complete) {
|
||||||
return easing;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
qreal tix = 0, tiy = 0, tox = 0, toy = 0;
|
qreal tix = 0, tiy = 0, tox = 0, toy = 0;
|
||||||
|
@ -101,8 +101,6 @@ public:
|
||||||
|
|
||||||
m_bezierPath.moveTo(s);
|
m_bezierPath.moveTo(s);
|
||||||
m_bezierPath.cubicTo(c1, c2, e);
|
m_bezierPath.cubicTo(c1, c2, e);
|
||||||
|
|
||||||
return easing;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool update(int frame) override
|
virtual bool update(int frame) override
|
||||||
|
@ -112,7 +110,7 @@ public:
|
||||||
|
|
||||||
int adjustedFrame = qBound(m_startFrame, frame, m_endFrame);
|
int adjustedFrame = qBound(m_startFrame, frame, m_endFrame);
|
||||||
if (const EasingSegment<QPointF> *easing = getEasingSegment(adjustedFrame)) {
|
if (const EasingSegment<QPointF> *easing = getEasingSegment(adjustedFrame)) {
|
||||||
if (easing->complete) {
|
if (easing->state == EasingSegmentState::Complete) {
|
||||||
qreal progress = ((adjustedFrame - m_startFrame) * 1.0) / (m_endFrame - m_startFrame);
|
qreal progress = ((adjustedFrame - m_startFrame) * 1.0) / (m_endFrame - m_startFrame);
|
||||||
qreal easedValue = easing->easing.valueForProgress(progress);
|
qreal easedValue = easing->easing.valueForProgress(progress);
|
||||||
m_value = m_bezierPath.pointAtPercent(easedValue);
|
m_value = m_bezierPath.pointAtPercent(easedValue);
|
||||||
|
|
Loading…
Reference in New Issue