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 }