File indexing completed on 2024-05-12 15:58:40

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     // When keyframe is changed (value, tangents, etc), we should notify that the channel has been updated.
0172     connect(this, &KisScalarKeyframeChannel::sigKeyframeChanged, this, [](const KisKeyframeChannel *channel, int time) {
0173         const KisScalarKeyframeChannel* chan = dynamic_cast<const KisScalarKeyframeChannel*>(channel);
0174         KIS_SAFE_ASSERT_RECOVER_RETURN(chan);
0175         chan->sigChannelUpdated(
0176                     chan->affectedFrames(time),
0177                     chan->affectedRect(time)
0178                     );
0179     });
0180 }
0181 
0182 KisScalarKeyframeChannel::KisScalarKeyframeChannel(const KisScalarKeyframeChannel &rhs)
0183     : KisKeyframeChannel(rhs)
0184 {
0185     m_d.reset(new Private(*rhs.m_d));
0186 
0187     Q_FOREACH (int time, rhs.constKeys().keys()) {
0188         KisKeyframeChannel::copyKeyframe(&rhs, time, this, time);
0189     }
0190 
0191     connect(this, &KisScalarKeyframeChannel::sigKeyframeChanged, this, [](const KisKeyframeChannel *channel, int time) {
0192         KIS_SAFE_ASSERT_RECOVER_RETURN(channel);
0193         const KisScalarKeyframeChannel* scalarChan = dynamic_cast<const KisScalarKeyframeChannel*>(channel);
0194         KIS_SAFE_ASSERT_RECOVER_RETURN(scalarChan);
0195         scalarChan->sigChannelUpdated(
0196                     scalarChan->affectedFrames(time),
0197                     scalarChan->affectedRect(time) );
0198     });
0199 }
0200 
0201 KisScalarKeyframeChannel::~KisScalarKeyframeChannel()
0202 {
0203 }
0204 
0205 void KisScalarKeyframeChannel::addScalarKeyframe(int time, qreal value, KUndo2Command *parentUndoCmd) {
0206     KisScalarKeyframeSP scalarKey = keyframeAt<KisScalarKeyframe>(time);
0207     if (!scalarKey) {
0208         addKeyframe(time, parentUndoCmd);
0209         scalarKey = keyframeAt<KisScalarKeyframe>(time);
0210     }
0211 
0212     if (scalarKey) {
0213         scalarKey->setValue(value, parentUndoCmd);
0214     }
0215 }
0216 
0217 QSharedPointer<ScalarKeyframeLimits> KisScalarKeyframeChannel::limits() const
0218 {
0219     return m_d->limits;
0220 }
0221 
0222 void KisScalarKeyframeChannel::setLimits(qreal low, qreal high)
0223 {
0224     m_d->limits = toQShared(new ScalarKeyframeLimits(low, high));
0225     QSet<int> keyEntries = allKeyframeTimes();
0226     foreach (const int &time, keyEntries) {
0227         KisScalarKeyframeSP scalarKey = keyframeAt<KisScalarKeyframe>(time);
0228         scalarKey->setLimits(m_d->limits);
0229         scalarKey->setValue(scalarKey->value());
0230     }
0231 }
0232 
0233 void KisScalarKeyframeChannel::removeLimits()
0234 {
0235     if (m_d->limits) {
0236         m_d->limits.reset();
0237     }
0238 }
0239 
0240 qreal KisScalarKeyframeChannel::valueAt(int time) const
0241 {
0242     const int activeKeyTime = activeKeyframeTime(time);
0243     KisScalarKeyframeSP activeKey = keyframeAt<KisScalarKeyframe>(activeKeyTime);
0244     KisScalarKeyframeSP nextKeyframe = keyframeAt<KisScalarKeyframe>(nextKeyframeTime(time));
0245     qreal result = qQNaN();
0246 
0247     if (activeKey) {
0248         if (!nextKeyframe) {
0249             result = activeKey->value();
0250         } else {
0251             switch (activeKey->interpolationMode()) {
0252             case KisScalarKeyframe::Constant: {
0253                     result = activeKey->value();
0254                     break;
0255                 }
0256             case KisScalarKeyframe::Linear: {
0257                     const int nextKeyTime = nextKeyframeTime(time);
0258                     const qreal activeKeyValue = activeKey->value();
0259                     const qreal nextKeyValue = keyframeAt<KisScalarKeyframe>(nextKeyTime)->value();
0260                     const int interpolationLength = (nextKeyTime - activeKeyTime);
0261                     if (interpolationLength > 0) {
0262                         result = activeKeyValue + (nextKeyValue - activeKeyValue) * (time - activeKeyTime) / (interpolationLength);
0263                     } else {
0264                         result = activeKeyValue;
0265                     }
0266                     break;
0267                 }
0268             case KisScalarKeyframe::Bezier: {
0269                     const int nextKeyTime = nextKeyframeTime(time);
0270                     const KisScalarKeyframeSP nextKey = keyframeAt<KisScalarKeyframe>(nextKeyTime);
0271                     QPointF point0 = QPointF(activeKeyTime, activeKey->value());
0272                     QPointF point1 = QPointF(nextKeyTime, nextKey->value());
0273 
0274                     QPointF tangent0 = activeKey->rightTangent();
0275                     QPointF tangent1 = nextKey->leftTangent();
0276 
0277                     normalizeTangents(point0, tangent0, tangent1, point1);
0278                     qreal t = KisScalarKeyframeChannel::findCubicCurveParameter(point0.x(), tangent0.x(), tangent1.x(), point1.x(), time);
0279                     result = KisScalarKeyframeChannel::interpolate(point0, tangent0, tangent1, point1, t).y();
0280                     break;
0281                 }
0282             default: {
0283                     KIS_ASSERT_RECOVER_BREAK(false);
0284                     break;
0285                 }
0286             }
0287         }
0288     } else {
0289         if (nextKeyframe) {
0290             result = nextKeyframe->value();
0291         } else {
0292             return qQNaN();
0293         }
0294     }
0295 
0296     // Output value must be also be clamped to account for interpolation.
0297     if (m_d->limits) {
0298         return m_d->limits->clamp(result);
0299     } else {
0300         return result;
0301     }
0302 }
0303 
0304 bool KisScalarKeyframeChannel::isCurrentTimeAffectedBy(int keyTime) {
0305     return affectedFrames(activeKeyframeTime(keyTime)).contains(currentTime());
0306 }
0307 
0308 void KisScalarKeyframeChannel::setDefaultValue(qreal value)
0309 {
0310     m_d->defaultValue = value;
0311 }
0312 
0313 void KisScalarKeyframeChannel::setDefaultInterpolationMode(KisScalarKeyframe::InterpolationMode mode)
0314 {
0315     m_d->defaultInterpolationMode = mode;
0316 }
0317 
0318 QPointF KisScalarKeyframeChannel::interpolate(QPointF point1, QPointF rightTangent, QPointF leftTangent, QPointF point2, qreal t)
0319 {
0320     normalizeTangents(point1, rightTangent, leftTangent, point2);
0321 
0322     qreal x = cubicBezier(point1.x(), rightTangent.x(), leftTangent.x(), point2.x(), t);
0323     qreal y = cubicBezier(point1.y(), rightTangent.y(), leftTangent.y(), point2.y(), t);
0324 
0325     return QPointF(x,y);
0326 }
0327 
0328 void KisScalarKeyframeChannel::insertKeyframe(int time, KisKeyframeSP keyframe, KUndo2Command *parentUndoCmd)
0329 {
0330     KisKeyframeChannel::insertKeyframe(time, keyframe, parentUndoCmd);
0331 
0332     KisScalarKeyframeSP scalarKeyframe = keyframe.dynamicCast<KisScalarKeyframe>();
0333     if (scalarKeyframe) {
0334         scalarKeyframe->valueChangedChannelConnection =
0335                 QObject::connect(scalarKeyframe.data(),
0336                                  &KisScalarKeyframe::sigChanged,
0337                                  [this, time](const KisScalarKeyframe* key){
0338                                      Q_UNUSED(key);
0339                                      emit sigKeyframeChanged(this, time);
0340                                  });
0341     }
0342 }
0343 
0344 void KisScalarKeyframeChannel::removeKeyframe(int time, KUndo2Command *parentUndoCmd)
0345 {
0346     KisKeyframeChannel::removeKeyframe(time, parentUndoCmd);
0347 
0348     KisScalarKeyframeSP keyframe = keyframeAt<KisScalarKeyframe>(time);
0349     if (keyframe) {
0350         disconnect(keyframe->valueChangedChannelConnection);
0351     }
0352 }
0353 
0354 KisTimeSpan KisScalarKeyframeChannel::affectedFrames(int time) const
0355 {
0356     KisTimeSpan normalSpan = KisKeyframeChannel::affectedFrames(time);
0357 
0358     const int activeKeyTime = activeKeyframeTime(time);
0359     const int previousKeyTime = previousKeyframeTime(activeKeyTime);
0360     const KisScalarKeyframeSP prevScalarKey = keyframeAt<KisScalarKeyframe>(previousKeyTime);
0361 
0362     if(prevScalarKey) {
0363         // In the case that a previous keyframe is present with a non-constant interpolation mode,
0364         // the affected frames must include all the frames just after the previous keyframe.
0365         if (prevScalarKey->interpolationMode() == KisScalarKeyframe::Constant) {
0366             return normalSpan;
0367         } else {
0368             return normalSpan | KisTimeSpan::fromTimeToTime(previousKeyTime + 1, activeKeyTime);
0369         }
0370     } else {
0371         const KisScalarKeyframeSP firstScalarKey = keyframeAt<KisScalarKeyframe>(firstKeyframeTime());
0372         if (!firstScalarKey) {
0373             return KisTimeSpan::infinite(0);
0374         }
0375         return normalSpan | KisTimeSpan::fromTimeToTime(0, activeKeyTime);
0376     }
0377 }
0378 
0379 KisTimeSpan KisScalarKeyframeChannel::identicalFrames(int time) const
0380 {
0381     //Failsafe == no keys should mean all frames are identical!
0382     if (allKeyframeTimes().count() == 0) {
0383         return KisTimeSpan::infinite(0);
0384     }
0385 
0386     KisScalarKeyframeSP activeScalarKey = activeKeyframeAt<KisScalarKeyframe>(time);
0387     if ( activeScalarKey &&
0388          activeScalarKey->interpolationMode() != KisScalarKeyframe::Constant &&
0389          activeScalarKey != keyframeAt(lastKeyframeTime()) ) {
0390         //TODO: Two keyframes should be considered identical if linear with same value..
0391         //TODO: Also, if bezier with same value AND tangents lie between points.
0392         //                                          (tangenty == keyframey)
0393         return KisTimeSpan::fromTimeToTime(time, time);
0394     }
0395 
0396     const int nextKeyTime = nextKeyframeTime(time);
0397 
0398     //Before the first frame => there's no active frame but a valid next frame.
0399     if (!activeScalarKey && keyframeAt(nextKeyTime)) {
0400         return KisTimeSpan::fromTimeToTime(0, nextKeyTime);
0401     }
0402 
0403     //No next frame, all frames after are identical.
0404     if (!keyframeAt(nextKeyTime)) {
0405        return KisTimeSpan::infinite(activeKeyframeTime(time));
0406     }
0407 
0408     return KisTimeSpan::fromTimeToTime(activeKeyframeTime(time), nextKeyTime - 1);
0409 }
0410 
0411 qreal KisScalarKeyframeChannel::findCubicCurveParameter(int time0, qreal delta0, qreal delta1, int time1, int time)
0412 {
0413     if (time == time0) return 0.0;
0414     if (time == time1) return 1.0;
0415 
0416     qreal min_t = 0.0;
0417     qreal max_t = 1.0;
0418 
0419     while (true) {
0420         qreal t = (max_t + min_t) / 2;
0421         qreal time_t = cubicBezier(time0, delta0, delta1, time1, t);
0422 
0423         if (time_t < time - 0.05) {
0424             min_t = t;
0425         } else if (time_t > time + 0.05) {
0426             max_t = t;
0427         } else {
0428             // Close enough
0429             return t;
0430         }
0431     }
0432 }
0433 
0434 qreal KisScalarKeyframeChannel::cubicBezier(qreal p0, qreal delta1, qreal delta2, qreal p3, qreal t) {
0435     qreal p1 = p0 + delta1;
0436     qreal p2 = p3 + delta2;
0437 
0438     qreal c = 1-t;
0439     return c*c*c * p0 + 3*c*c*t * p1 + 3*c*t*t * p2 + t*t*t * p3;
0440 }
0441 
0442 void KisScalarKeyframeChannel::normalizeTangents(const QPointF point1, QPointF &rightTangent, QPointF &leftTangent, const QPointF point2)
0443 {
0444     // To ensure that the curve is monotonic wrt time,
0445     // check that control points lie between the endpoints.
0446     // If not, force them into range by scaling down the tangents
0447 
0448     float interval = point2.x() - point1.x();
0449     if (rightTangent.x() < 0) rightTangent *= 0;
0450     if (leftTangent.x() > 0) leftTangent *= 0;
0451 
0452     if (rightTangent.x() > interval) {
0453         rightTangent *= interval / rightTangent.x();
0454     }
0455     if (leftTangent.x() < -interval) {
0456         leftTangent *= interval / -leftTangent.x();
0457     }
0458 }
0459 
0460 KisKeyframeSP KisScalarKeyframeChannel::createKeyframe()
0461 {
0462     KisScalarKeyframe *keyframe = new KisScalarKeyframe(m_d->defaultValue, m_d->limits);
0463     keyframe->setInterpolationMode(m_d->defaultInterpolationMode);
0464     return toQShared(keyframe);
0465 }
0466 
0467 QRect KisScalarKeyframeChannel::affectedRect(int time) const
0468 {
0469     Q_UNUSED(time);
0470 
0471     if (node()) {
0472         return node()->exactBounds();
0473     } else {
0474         return QRect();
0475     }
0476 }
0477 
0478 void KisScalarKeyframeChannel::saveKeyframe(KisKeyframeSP keyframe, QDomElement keyframeElement, const QString &layerFilename)
0479 {
0480     Q_UNUSED(layerFilename);
0481     KisScalarKeyframeSP scalarKey = keyframe.dynamicCast<KisScalarKeyframe>();
0482     KIS_SAFE_ASSERT_RECOVER_RETURN(scalarKey);
0483     const qreal value = scalarKey->value();
0484     keyframeElement.setAttribute("value", KisDomUtils::toString(value));
0485 
0486     QString interpolationMode;
0487     if (scalarKey->interpolationMode() == KisScalarKeyframe::Constant) interpolationMode = "constant";
0488     if (scalarKey->interpolationMode() == KisScalarKeyframe::Linear) interpolationMode = "linear";
0489     if (scalarKey->interpolationMode() == KisScalarKeyframe::Bezier) interpolationMode = "bezier";
0490 
0491     QString tangentsMode;
0492     if (scalarKey->tangentsMode() == KisScalarKeyframe::Smooth) tangentsMode = "smooth";
0493     if (scalarKey->tangentsMode() == KisScalarKeyframe::Sharp) tangentsMode = "sharp";
0494 
0495     keyframeElement.setAttribute("interpolation", interpolationMode);
0496     keyframeElement.setAttribute("tangents", tangentsMode);
0497     KisDomUtils::saveValue(&keyframeElement, "leftTangent", scalarKey->leftTangent());
0498     KisDomUtils::saveValue(&keyframeElement, "rightTangent", scalarKey->rightTangent());
0499 }
0500 
0501 QPair<int, KisKeyframeSP> KisScalarKeyframeChannel::loadKeyframe(const QDomElement &keyframeNode)
0502 {
0503     int time = keyframeNode.toElement().attribute("time").toInt();
0504     workaroundBrokenFrameTimeBug(&time);
0505 
0506     qreal value = KisDomUtils::toDouble(keyframeNode.toElement().attribute("value"));
0507 
0508     KisScalarKeyframeSP keyframe = createKeyframe().dynamicCast<KisScalarKeyframe>();
0509     keyframe->setValue(value);
0510 
0511     KisScalarKeyframeSP scalarKey = keyframe.dynamicCast<KisScalarKeyframe>();
0512 
0513     QString interpolationMode = keyframeNode.toElement().attribute("interpolation");
0514     if (interpolationMode == "constant") {
0515         scalarKey->setInterpolationMode(KisScalarKeyframe::Constant);
0516     } else if (interpolationMode == "linear") {
0517         scalarKey->setInterpolationMode(KisScalarKeyframe::Linear);
0518     } else if (interpolationMode == "bezier") {
0519         scalarKey->setInterpolationMode(KisScalarKeyframe::Bezier);
0520     }
0521 
0522     QString tangentsMode = keyframeNode.toElement().attribute("tangents");
0523     if (tangentsMode == "smooth") {
0524         scalarKey->setTangentsMode(KisScalarKeyframe::Smooth);
0525     } else if (tangentsMode == "sharp") {
0526         scalarKey->setTangentsMode(KisScalarKeyframe::Sharp);
0527     }
0528 
0529     QPointF leftTangent;
0530     QPointF rightTangent;
0531     KisDomUtils::loadValue(keyframeNode, "leftTangent", &leftTangent);
0532     KisDomUtils::loadValue(keyframeNode, "rightTangent", &rightTangent);
0533     scalarKey->setInterpolationTangents(leftTangent, rightTangent);
0534 
0535     return QPair<int, KisKeyframeSP>(time, keyframe);
0536 }