File indexing completed on 2024-05-19 04:26:33

0001 /*
0002  *  SPDX-FileCopyrightText: 2015 Jouni Pentikäinen <joupent@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_scalar_keyframe_channel.h"
0008 #include "kis_node.h"
0009 #include "kundo2command.h"
0010 #include "kis_keyframe_commands.h"
0011 #include "kis_time_span.h"
0012 #include <kis_global.h>
0013 #include <kis_dom_utils.h>
0014 
0015 
0016 KisScalarKeyframe::KisScalarKeyframe(qreal value, QSharedPointer<ScalarKeyframeLimits> limits)
0017     : KisKeyframe(),
0018       m_value(value),
0019       m_interpolationMode(Constant),
0020       m_tangentsMode(Smooth),
0021       m_channelLimits(limits)
0022 {
0023 }
0024 
0025 KisScalarKeyframe::KisScalarKeyframe(qreal value, InterpolationMode interpMode, TangentsMode tangentMode,
0026                                      QPointF leftTangent, QPointF rightTangent,
0027                                      QSharedPointer<ScalarKeyframeLimits> limits)
0028     : m_value(value),
0029       m_interpolationMode(interpMode),
0030       m_tangentsMode(tangentMode),
0031       m_leftTangent(leftTangent),
0032       m_rightTangent(rightTangent),
0033       m_channelLimits(limits)
0034 {
0035 }
0036 
0037 KisKeyframeSP KisScalarKeyframe::duplicate(KisKeyframeChannel *newChannel)
0038 {
0039     if (newChannel) {
0040         KisScalarKeyframeChannel *scalarChannel = dynamic_cast<KisScalarKeyframeChannel*>(newChannel);
0041         KIS_ASSERT(scalarChannel);
0042         // When transitioning between channels, set limits to those of the new channel.
0043         KisScalarKeyframeSP scalarKey = toQShared(new KisScalarKeyframe(m_value, scalarChannel->limits()));
0044         scalarKey->setInterpolationMode(m_interpolationMode);
0045         scalarKey->setTangentsMode(m_tangentsMode);
0046         scalarKey->setInterpolationTangents(leftTangent(), rightTangent());
0047         return scalarKey;
0048     } else {
0049         return toQShared(new KisScalarKeyframe(value(), interpolationMode(), tangentsMode(),
0050                                                leftTangent(), rightTangent(),
0051                                                m_channelLimits.toStrongRef()));
0052     }
0053 }
0054 
0055 qreal KisScalarKeyframe::value() const
0056 {
0057     return m_value;
0058 }
0059 
0060 void KisScalarKeyframe::setValue(qreal value, KUndo2Command *parentUndoCmd)
0061 {
0062     if (parentUndoCmd) {
0063         KUndo2Command* cmd = new KisScalarKeyframeUpdateCommand(this, value, parentUndoCmd);
0064         cmd->redo();
0065     } else {
0066         m_value = value;
0067 
0068         QSharedPointer<ScalarKeyframeLimits> limits = m_channelLimits.toStrongRef();
0069         if (limits) {
0070             m_value = limits->clamp(m_value);
0071         }
0072 
0073         emit sigChanged(this);
0074     }
0075 }
0076 
0077 void KisScalarKeyframe::setInterpolationMode(InterpolationMode mode, KUndo2Command *parentUndoCmd)
0078 {
0079     if (parentUndoCmd) {
0080         KUndo2Command* cmd = new KisScalarKeyframeUpdateCommand(this, mode, parentUndoCmd);
0081         cmd->redo();
0082     } else {
0083         m_interpolationMode = mode;
0084         emit sigChanged(this);
0085     }
0086 }
0087 
0088 KisScalarKeyframe::InterpolationMode KisScalarKeyframe::interpolationMode() const
0089 {
0090     return m_interpolationMode;
0091 }
0092 
0093 void KisScalarKeyframe::setTangentsMode(TangentsMode mode, KUndo2Command *parentUndoCmd)
0094 {
0095     if (parentUndoCmd) {
0096         KUndo2Command* cmd = new KisScalarKeyframeUpdateCommand(this, mode, parentUndoCmd);
0097         cmd->redo();
0098     } else {
0099         m_tangentsMode = mode;
0100         emit sigChanged(this);
0101     }
0102 }
0103 
0104 KisScalarKeyframe::TangentsMode KisScalarKeyframe::tangentsMode() const
0105 {
0106     return m_tangentsMode;
0107 }
0108 
0109 void KisScalarKeyframe::setInterpolationTangents(QPointF leftTangent, QPointF rightTangent, KUndo2Command *parentUndoCmd)
0110 {
0111     if (parentUndoCmd) {
0112         KUndo2Command* cmd = new KisScalarKeyframeUpdateCommand(this, leftTangent, rightTangent, parentUndoCmd);
0113         cmd->redo();
0114     } else {
0115         m_leftTangent = leftTangent;
0116         m_rightTangent = rightTangent;
0117         emit sigChanged(this);
0118     }
0119 }
0120 
0121 QPointF KisScalarKeyframe::leftTangent() const
0122 {
0123     return m_leftTangent;
0124 }
0125 
0126 QPointF KisScalarKeyframe::rightTangent() const
0127 {
0128     return m_rightTangent;
0129 }
0130 
0131 void KisScalarKeyframe::setLimits(QSharedPointer<ScalarKeyframeLimits> limits)
0132 {
0133     m_channelLimits = limits;
0134 }
0135 
0136 
0137 // ========================================================================================================
0138 // ==================================== KisScalarKeyframeChannel ==========================================
0139 // ========================================================================================================
0140 
0141 
0142 struct KisScalarKeyframeChannel::Private
0143 {
0144 public:
0145     Private()
0146         : defaultValue(0),
0147           defaultInterpolationMode(KisScalarKeyframe::Constant)
0148     {}
0149 
0150     Private(const Private &rhs)
0151         : defaultValue(rhs.defaultValue),
0152           defaultInterpolationMode(rhs.defaultInterpolationMode)
0153     {
0154         if (rhs.limits) {
0155             limits = toQShared(new ScalarKeyframeLimits(*rhs.limits));
0156         }
0157     }
0158 
0159     qreal defaultValue;
0160     KisScalarKeyframe::InterpolationMode defaultInterpolationMode;
0161 
0162     /** Optional structure that can be added to a channel in order to
0163      * limit its scalar values within a certain range. */
0164     QSharedPointer<ScalarKeyframeLimits> limits;
0165 };
0166 
0167 KisScalarKeyframeChannel::KisScalarKeyframeChannel(const KoID &id, KisDefaultBoundsBaseSP bounds)
0168     : KisKeyframeChannel(id, bounds)
0169     , m_d(new Private)
0170 {
0171 }
0172 
0173 KisScalarKeyframeChannel::KisScalarKeyframeChannel(const KisScalarKeyframeChannel &rhs)
0174     : KisKeyframeChannel(rhs)
0175 {
0176     m_d.reset(new Private(*rhs.m_d));
0177 
0178     Q_FOREACH (int time, rhs.constKeys().keys()) {
0179         KisKeyframeChannel::copyKeyframe(&rhs, time, this, time);
0180     }
0181 }
0182 
0183 KisScalarKeyframeChannel::~KisScalarKeyframeChannel()
0184 {
0185 }
0186 
0187 void KisScalarKeyframeChannel::addScalarKeyframe(int time, qreal value, KUndo2Command *parentUndoCmd) {
0188     KisScalarKeyframeSP scalarKey = keyframeAt<KisScalarKeyframe>(time);
0189     if (!scalarKey) {
0190         addKeyframe(time, parentUndoCmd);
0191         scalarKey = keyframeAt<KisScalarKeyframe>(time);
0192     }
0193 
0194     if (scalarKey) {
0195         scalarKey->setValue(value, parentUndoCmd);
0196     }
0197 }
0198 
0199 QSharedPointer<ScalarKeyframeLimits> KisScalarKeyframeChannel::limits() const
0200 {
0201     return m_d->limits;
0202 }
0203 
0204 void KisScalarKeyframeChannel::setLimits(qreal low, qreal high)
0205 {
0206     m_d->limits = toQShared(new ScalarKeyframeLimits(low, high));
0207     QSet<int> keyEntries = allKeyframeTimes();
0208     foreach (const int &time, keyEntries) {
0209         KisScalarKeyframeSP scalarKey = keyframeAt<KisScalarKeyframe>(time);
0210         scalarKey->setLimits(m_d->limits);
0211         scalarKey->setValue(scalarKey->value());
0212     }
0213 }
0214 
0215 void KisScalarKeyframeChannel::removeLimits()
0216 {
0217     if (m_d->limits) {
0218         m_d->limits.reset();
0219     }
0220 }
0221 
0222 qreal KisScalarKeyframeChannel::valueAt(int time) const
0223 {
0224     const int activeKeyTime = activeKeyframeTime(time);
0225     KisScalarKeyframeSP activeKey = keyframeAt<KisScalarKeyframe>(activeKeyTime);
0226     KisScalarKeyframeSP nextKeyframe = keyframeAt<KisScalarKeyframe>(nextKeyframeTime(time));
0227     qreal result = qQNaN();
0228 
0229     if (activeKey) {
0230         if (!nextKeyframe) {
0231             result = activeKey->value();
0232         } else {
0233             switch (activeKey->interpolationMode()) {
0234             case KisScalarKeyframe::Constant: {
0235                     result = activeKey->value();
0236                     break;
0237                 }
0238             case KisScalarKeyframe::Linear: {
0239                     const int nextKeyTime = nextKeyframeTime(time);
0240                     const qreal activeKeyValue = activeKey->value();
0241                     const qreal nextKeyValue = keyframeAt<KisScalarKeyframe>(nextKeyTime)->value();
0242                     const int interpolationLength = (nextKeyTime - activeKeyTime);
0243                     if (interpolationLength > 0) {
0244                         result = activeKeyValue + (nextKeyValue - activeKeyValue) * (time - activeKeyTime) / (interpolationLength);
0245                     } else {
0246                         result = activeKeyValue;
0247                     }
0248                     break;
0249                 }
0250             case KisScalarKeyframe::Bezier: {
0251                     const int nextKeyTime = nextKeyframeTime(time);
0252                     const KisScalarKeyframeSP nextKey = keyframeAt<KisScalarKeyframe>(nextKeyTime);
0253                     QPointF point0 = QPointF(activeKeyTime, activeKey->value());
0254                     QPointF point1 = QPointF(nextKeyTime, nextKey->value());
0255 
0256                     QPointF tangent0 = activeKey->rightTangent();
0257                     QPointF tangent1 = nextKey->leftTangent();
0258 
0259                     normalizeTangents(point0, tangent0, tangent1, point1);
0260                     qreal t = KisScalarKeyframeChannel::findCubicCurveParameter(point0.x(), tangent0.x(), tangent1.x(), point1.x(), time);
0261                     result = KisScalarKeyframeChannel::interpolate(point0, tangent0, tangent1, point1, t).y();
0262                     break;
0263                 }
0264             default: {
0265                     KIS_ASSERT_RECOVER_BREAK(false);
0266                     break;
0267                 }
0268             }
0269         }
0270     } else {
0271         if (nextKeyframe) {
0272             result = nextKeyframe->value();
0273         } else {
0274             return qQNaN();
0275         }
0276     }
0277 
0278     // Output value must be also be clamped to account for interpolation.
0279     if (m_d->limits) {
0280         return m_d->limits->clamp(result);
0281     } else {
0282         return result;
0283     }
0284 }
0285 
0286 bool KisScalarKeyframeChannel::isCurrentTimeAffectedBy(int keyTime) {
0287     return affectedFrames(activeKeyframeTime(keyTime)).contains(currentTime());
0288 }
0289 
0290 void KisScalarKeyframeChannel::setDefaultValue(qreal value)
0291 {
0292     m_d->defaultValue = value;
0293 }
0294 
0295 void KisScalarKeyframeChannel::setDefaultInterpolationMode(KisScalarKeyframe::InterpolationMode mode)
0296 {
0297     m_d->defaultInterpolationMode = mode;
0298 }
0299 
0300 QPointF KisScalarKeyframeChannel::interpolate(QPointF point1, QPointF rightTangent, QPointF leftTangent, QPointF point2, qreal t)
0301 {
0302     normalizeTangents(point1, rightTangent, leftTangent, point2);
0303 
0304     qreal x = cubicBezier(point1.x(), rightTangent.x(), leftTangent.x(), point2.x(), t);
0305     qreal y = cubicBezier(point1.y(), rightTangent.y(), leftTangent.y(), point2.y(), t);
0306 
0307     return QPointF(x,y);
0308 }
0309 
0310 void KisScalarKeyframeChannel::insertKeyframe(int time, KisKeyframeSP keyframe, KUndo2Command *parentUndoCmd)
0311 {
0312     KisScalarKeyframeSP scalarKeyframe = keyframe.dynamicCast<KisScalarKeyframe>();
0313     if (scalarKeyframe) {
0314         scalarKeyframe->valueChangedChannelConnection =
0315             QObject::connect(scalarKeyframe.data(),
0316                              &KisScalarKeyframe::sigChanged,
0317                              [this, time](const KisScalarKeyframe* key){
0318                                  Q_UNUSED(key);
0319                                  emit sigKeyframeChanged(this, time);
0320                              });
0321     }
0322 
0323     KisKeyframeChannel::insertKeyframe(time, keyframe, parentUndoCmd);
0324 }
0325 
0326 void KisScalarKeyframeChannel::removeKeyframe(int time, KUndo2Command *parentUndoCmd)
0327 {
0328     KisScalarKeyframeSP keyframe = keyframeAt<KisScalarKeyframe>(time);
0329     if (keyframe) {
0330         disconnect(keyframe->valueChangedChannelConnection);
0331     }
0332 
0333     KisKeyframeChannel::removeKeyframe(time, parentUndoCmd);
0334 }
0335 
0336 KisTimeSpan KisScalarKeyframeChannel::affectedFrames(int time) const
0337 {
0338     KisTimeSpan normalSpan = KisKeyframeChannel::affectedFrames(time);
0339 
0340     const int activeKeyTime = activeKeyframeTime(time);
0341     const int previousKeyTime = previousKeyframeTime(activeKeyTime);
0342     const KisScalarKeyframeSP prevScalarKey = keyframeAt<KisScalarKeyframe>(previousKeyTime);
0343 
0344     if(prevScalarKey) {
0345         // In the case that a previous keyframe is present with a non-constant interpolation mode,
0346         // the affected frames must include all the frames just after the previous keyframe.
0347         if (prevScalarKey->interpolationMode() == KisScalarKeyframe::Constant) {
0348             return normalSpan;
0349         } else {
0350             return normalSpan | KisTimeSpan::fromTimeToTime(previousKeyTime + 1, activeKeyTime);
0351         }
0352     } else {
0353         const KisScalarKeyframeSP firstScalarKey = keyframeAt<KisScalarKeyframe>(firstKeyframeTime());
0354         if (!firstScalarKey) {
0355             return KisTimeSpan::infinite(0);
0356         }
0357         return normalSpan | KisTimeSpan::fromTimeToTime(0, activeKeyTime);
0358     }
0359 }
0360 
0361 KisTimeSpan KisScalarKeyframeChannel::identicalFrames(int time) const
0362 {
0363     //Failsafe == no keys should mean all frames are identical!
0364     if (allKeyframeTimes().count() == 0) {
0365         return KisTimeSpan::infinite(0);
0366     }
0367 
0368     KisScalarKeyframeSP activeScalarKey = activeKeyframeAt<KisScalarKeyframe>(time);
0369     if ( activeScalarKey &&
0370          activeScalarKey->interpolationMode() != KisScalarKeyframe::Constant &&
0371          activeScalarKey != keyframeAt(lastKeyframeTime()) ) {
0372         //TODO: Two keyframes should be considered identical if linear with same value..
0373         //TODO: Also, if bezier with same value AND tangents lie between points.
0374         //                                          (tangenty == keyframey)
0375         return KisTimeSpan::fromTimeToTime(time, time);
0376     }
0377 
0378     const int nextKeyTime = nextKeyframeTime(time);
0379 
0380     //Before the first frame => there's no active frame but a valid next frame.
0381     if (!activeScalarKey && keyframeAt(nextKeyTime)) {
0382         return KisTimeSpan::fromTimeToTime(0, nextKeyTime);
0383     }
0384 
0385     //No next frame, all frames after are identical.
0386     if (!keyframeAt(nextKeyTime)) {
0387        return KisTimeSpan::infinite(activeKeyframeTime(time));
0388     }
0389 
0390     return KisTimeSpan::fromTimeToTime(activeKeyframeTime(time), nextKeyTime - 1);
0391 }
0392 
0393 qreal KisScalarKeyframeChannel::findCubicCurveParameter(int time0, qreal delta0, qreal delta1, int time1, int time)
0394 {
0395     if (time == time0) return 0.0;
0396     if (time == time1) return 1.0;
0397 
0398     qreal min_t = 0.0;
0399     qreal max_t = 1.0;
0400 
0401     while (true) {
0402         qreal t = (max_t + min_t) / 2;
0403         qreal time_t = cubicBezier(time0, delta0, delta1, time1, t);
0404 
0405         if (time_t < time - 0.05) {
0406             min_t = t;
0407         } else if (time_t > time + 0.05) {
0408             max_t = t;
0409         } else {
0410             // Close enough
0411             return t;
0412         }
0413     }
0414 }
0415 
0416 qreal KisScalarKeyframeChannel::cubicBezier(qreal p0, qreal delta1, qreal delta2, qreal p3, qreal t) {
0417     qreal p1 = p0 + delta1;
0418     qreal p2 = p3 + delta2;
0419 
0420     qreal c = 1-t;
0421     return c*c*c * p0 + 3*c*c*t * p1 + 3*c*t*t * p2 + t*t*t * p3;
0422 }
0423 
0424 void KisScalarKeyframeChannel::normalizeTangents(const QPointF point1, QPointF &rightTangent, QPointF &leftTangent, const QPointF point2)
0425 {
0426     // To ensure that the curve is monotonic wrt time,
0427     // check that control points lie between the endpoints.
0428     // If not, force them into range by scaling down the tangents
0429 
0430     float interval = point2.x() - point1.x();
0431     if (rightTangent.x() < 0) rightTangent *= 0;
0432     if (leftTangent.x() > 0) leftTangent *= 0;
0433 
0434     if (rightTangent.x() > interval) {
0435         rightTangent *= interval / rightTangent.x();
0436     }
0437     if (leftTangent.x() < -interval) {
0438         leftTangent *= interval / -leftTangent.x();
0439     }
0440 }
0441 
0442 KisKeyframeSP KisScalarKeyframeChannel::createKeyframe()
0443 {
0444     KisScalarKeyframe *keyframe = new KisScalarKeyframe(m_d->defaultValue, m_d->limits);
0445     keyframe->setInterpolationMode(m_d->defaultInterpolationMode);
0446     return toQShared(keyframe);
0447 }
0448 
0449 QRect KisScalarKeyframeChannel::affectedRect(int time) const
0450 {
0451     Q_UNUSED(time);
0452 
0453     if (node()) {
0454         return node()->exactBounds();
0455     } else {
0456         return QRect();
0457     }
0458 }
0459 
0460 void KisScalarKeyframeChannel::saveKeyframe(KisKeyframeSP keyframe, QDomElement keyframeElement, const QString &layerFilename)
0461 {
0462     Q_UNUSED(layerFilename);
0463     KisScalarKeyframeSP scalarKey = keyframe.dynamicCast<KisScalarKeyframe>();
0464     KIS_SAFE_ASSERT_RECOVER_RETURN(scalarKey);
0465     const qreal value = scalarKey->value();
0466     keyframeElement.setAttribute("value", KisDomUtils::toString(value));
0467 
0468     QString interpolationMode;
0469     if (scalarKey->interpolationMode() == KisScalarKeyframe::Constant) interpolationMode = "constant";
0470     if (scalarKey->interpolationMode() == KisScalarKeyframe::Linear) interpolationMode = "linear";
0471     if (scalarKey->interpolationMode() == KisScalarKeyframe::Bezier) interpolationMode = "bezier";
0472 
0473     QString tangentsMode;
0474     if (scalarKey->tangentsMode() == KisScalarKeyframe::Smooth) tangentsMode = "smooth";
0475     if (scalarKey->tangentsMode() == KisScalarKeyframe::Sharp) tangentsMode = "sharp";
0476 
0477     keyframeElement.setAttribute("interpolation", interpolationMode);
0478     keyframeElement.setAttribute("tangents", tangentsMode);
0479     KisDomUtils::saveValue(&keyframeElement, "leftTangent", scalarKey->leftTangent());
0480     KisDomUtils::saveValue(&keyframeElement, "rightTangent", scalarKey->rightTangent());
0481 }
0482 
0483 QPair<int, KisKeyframeSP> KisScalarKeyframeChannel::loadKeyframe(const QDomElement &keyframeNode)
0484 {
0485     int time = keyframeNode.toElement().attribute("time").toInt();
0486     workaroundBrokenFrameTimeBug(&time);
0487 
0488     qreal value = KisDomUtils::toDouble(keyframeNode.toElement().attribute("value"));
0489 
0490     KisScalarKeyframeSP keyframe = createKeyframe().dynamicCast<KisScalarKeyframe>();
0491     keyframe->setValue(value);
0492 
0493     KisScalarKeyframeSP scalarKey = keyframe.dynamicCast<KisScalarKeyframe>();
0494 
0495     QString interpolationMode = keyframeNode.toElement().attribute("interpolation");
0496     if (interpolationMode == "constant") {
0497         scalarKey->setInterpolationMode(KisScalarKeyframe::Constant);
0498     } else if (interpolationMode == "linear") {
0499         scalarKey->setInterpolationMode(KisScalarKeyframe::Linear);
0500     } else if (interpolationMode == "bezier") {
0501         scalarKey->setInterpolationMode(KisScalarKeyframe::Bezier);
0502     }
0503 
0504     QString tangentsMode = keyframeNode.toElement().attribute("tangents");
0505     if (tangentsMode == "smooth") {
0506         scalarKey->setTangentsMode(KisScalarKeyframe::Smooth);
0507     } else if (tangentsMode == "sharp") {
0508         scalarKey->setTangentsMode(KisScalarKeyframe::Sharp);
0509     }
0510 
0511     QPointF leftTangent;
0512     QPointF rightTangent;
0513     KisDomUtils::loadValue(keyframeNode, "leftTangent", &leftTangent);
0514     KisDomUtils::loadValue(keyframeNode, "rightTangent", &rightTangent);
0515     scalarKey->setInterpolationTangents(leftTangent, rightTangent);
0516 
0517     return QPair<int, KisKeyframeSP>(time, keyframe);
0518 }