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 }