Revision control

Copy as Markdown

// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// Qt-Security score:significant reason:default
#ifndef QLOTTIESPATIALPROPERTY_P_H
#define QLOTTIESPATIALPROPERTY_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <QPointF>
#include <QPainterPath>
#include <QtLottie/private/qlottieproperty_p.h>
QT_BEGIN_NAMESPACE
class QLottieSpatialProperty : public QLottieProperty2D<QPointF>
{
public:
virtual void construct(const QJsonObject &definition) override
{
qCDebug(lcLottieQtLottieParser) << "QLottieSpatialProperty::parse()";
QLottieProperty2D<QPointF>::construct(definition);
}
virtual EasingSegment<QPointF> parseKeyframe(const QJsonObject keyframe,
bool fromExpression) override
{
m_subPaths.append(QPainterPath{});
EasingSegment<QPointF> easing =
QLottieProperty2D<QPointF>::parseKeyframe(keyframe, fromExpression);
// No need to parse further incomplete keyframes (i.e. last keyframes)
if (!easing.complete) {
return easing;
}
qreal tix = 0, tiy = 0, tox = 0, toy = 0;
if (fromExpression) {
// If spatial property definition originates from
// an expression (specifically Slider), it contains scalar
// property. It must be expanded to both x and y coordinates
QJsonArray iArr = keyframe.value("i"_L1).toArray();
QJsonArray oArr = keyframe.value("o"_L1).toArray();
if (iArr.count() && oArr.count()) {
tix = iArr.at(0).toDouble();
tiy = tix;
tox = oArr.at(0).toDouble();
toy = tox;
}
} else {
QJsonArray tiArr = keyframe.value("ti"_L1).toArray();
QJsonArray toArr = keyframe.value("to"_L1).toArray();
if (tiArr.count() && toArr.count()) {
tix = tiArr.at(0).toDouble();
tiy = tiArr.at(1).toDouble();
tox = toArr.at(0).toDouble();
toy = toArr.at(1).toDouble();
}
}
QPointF s(easing.startValue);
QPointF e(easing.endValue);
QPointF c1(tox, toy);
QPointF c2(tix, tiy);
moveTo(s);
if (c1.isNull() && c2.isNull()) {
lineTo(e);
} else {
c1 += s;
c2 += e;
cubicTo(c1, c2, e);
}
return easing;
}
virtual EasingSegment<QPointF> parseKeyframe(const QJsonObject keyframe,
const QJsonObject nextKeyframe,
bool fromExpression) override
{
m_subPaths.append(QPainterPath{});
EasingSegment<QPointF> easing =
QLottieProperty2D<QPointF>::parseKeyframe(keyframe, nextKeyframe, fromExpression);
// No need to parse further incomplete keyframes (i.e. last keyframes)
if (!easing.complete) {
return easing;
}
qreal tix = 0, tiy = 0, tox = 0, toy = 0;
if (fromExpression) {
// If spatial property definition originates from
// an expression (specifically Slider), it contains scalar
// property. It must be expanded to both x and y coordinates
QJsonArray iArr = keyframe.value("i"_L1).toArray();
QJsonArray oArr = keyframe.value("o"_L1).toArray();
if (iArr.count() && oArr.count()) {
tix = iArr.at(0).toDouble();
tiy = tix;
tox = oArr.at(0).toDouble();
toy = tox;
}
} else {
QJsonArray tiArr = keyframe.value("ti"_L1).toArray();
QJsonArray toArr = keyframe.value("to"_L1).toArray();
if (tiArr.count() && toArr.count()) {
tix = tiArr.at(0).toDouble();
tiy = tiArr.at(1).toDouble();
tox = toArr.at(0).toDouble();
toy = toArr.at(1).toDouble();
}
}
QPointF s(easing.startValue);
QPointF e(easing.endValue);
QPointF c1(tox, toy);
QPointF c2(tix, tiy);
moveTo(s);
if (c1.isNull() && c2.isNull()) {
lineTo(e);
} else {
c1 += s;
c2 += e;
cubicTo(c1, c2, e);
}
return easing;
}
virtual bool update(int frame) override
{
if (!m_animated)
return false;
int adjustedFrame = qBound(m_startFrame, frame, m_endFrame);
int index = -1;
if (const EasingSegment<QPointF> *easing = getEasingSegment(adjustedFrame, &index)) {
if (easing->endFrame == easing->startFrame) { // Last, special keyframe
m_value = easing->startValue;
} else if (adjustedFrame == easing->startFrame) {
m_value = easing->startValue;
} else if (adjustedFrame == easing->endFrame) {
m_value = easing->endValue;
} else {
const qreal progress = qreal(adjustedFrame - easing->startFrame)
/ (easing->endFrame - easing->startFrame);
const qreal easedProgress = easing->valueForProgress(progress);
const QPainterPath subPath = m_subPaths.value(index);
if (subPath.elementCount() == 2) // I.e. a lineto; do simple linear calculation
m_value = easing->startValue + easedProgress * (easing->endValue - easing->startValue);
else
m_value = subPath.pointAtPercent(easedProgress);
}
}
return true;
}
bool hasCurves() const
{
for (qsizetype i = 0; i < m_subPaths.size(); ++i) {
const QPainterPath path = m_subPaths.at(i);
for (int j = 0; j < path.elementCount(); ++j)
if (path.elementAt(j).isCurveTo())
return true;
}
return false;
}
const QList<QPainterPath> &subPaths() const
{
return m_subPaths;
}
private:
void moveTo(const QPointF &e)
{
m_subPaths.last().setCachingEnabled(true);
m_subPaths.last().moveTo(e);
}
void lineTo(const QPointF &e)
{
m_subPaths.last().lineTo(e);
}
void cubicTo(const QPointF &c1, const QPointF &c2, const QPointF &e)
{
m_subPaths.last().cubicTo(c1, c2, e);
}
QList<QPainterPath> m_subPaths;
};
QT_END_NAMESPACE
#endif // QLOTTIESPATIALPROPERTY_P_H