diff --git a/libs/image/kis_keyframe_channel.h b/libs/image/kis_keyframe_channel.h --- a/libs/image/kis_keyframe_channel.h +++ b/libs/image/kis_keyframe_channel.h @@ -147,6 +147,7 @@ virtual KisKeyframeSP loadKeyframe(const QDomElement &keyframeNode) = 0; virtual void saveKeyframe(KisKeyframeSP keyframe, QDomElement keyframeElement, const QString &layerFilename) = 0; + void workaroundBrokenFrameTimeBug(int *time); private: KisKeyframeSP replaceKeyframeAt(int time, KisKeyframeSP newKeyframe); diff --git a/libs/image/kis_keyframe_channel.cpp b/libs/image/kis_keyframe_channel.cpp --- a/libs/image/kis_keyframe_channel.cpp +++ b/libs/image/kis_keyframe_channel.cpp @@ -46,12 +46,14 @@ node = newParentNode; id = rhs.id; defaultBounds = rhs.defaultBounds; + haveBrokenFrameTimeBug = rhs.haveBrokenFrameTimeBug; } KeyframesMap keys; KisNodeWSP node; KoID id; KisDefaultBoundsBaseSP defaultBounds; + bool haveBrokenFrameTimeBug = false; }; KisKeyframeChannel::KisKeyframeChannel(const KoID &id, KisDefaultBoundsBaseSP defaultBounds) @@ -516,6 +518,7 @@ if (keyframeNode.nodeName().toUpper() != "KEYFRAME") continue; KisKeyframeSP keyframe = loadKeyframe(keyframeNode); + KIS_SAFE_ASSERT_RECOVER(keyframe) { continue; } if (keyframeNode.hasAttribute("color-label")) { keyframe->setColorLabel(keyframeNode.attribute("color-label").toUInt()); @@ -608,6 +611,33 @@ } } +void KisKeyframeChannel::workaroundBrokenFrameTimeBug(int *time) +{ + /** + * Between Krita 4.1 and 4.4 Krita had a bug which resulted in creating frames + * with negative time stamp. The bug has been fixed, but there might be some files + * still in the wild. + * + * TODO: remove this workaround in Krita 5.0, when no such file are left :) + */ + + if (*time < 0) { + qWarning() << "WARNING: Loading a file with negative animation frames!"; + qWarning() << " The file has been saved with a buggy version of Krita."; + qWarning() << " All the frames with negative ids will be dropped!"; + qWarning() << " " << ppVar(this->id()) << ppVar(*time); + + m_d->haveBrokenFrameTimeBug = true; + *time = 0; + } + + if (m_d->haveBrokenFrameTimeBug) { + while (keyframeAt(*time)) { + (*time)++; + } + } +} + int KisKeyframeChannel::currentTime() const { return m_d->defaultBounds->currentTime(); diff --git a/libs/image/kis_raster_keyframe_channel.cpp b/libs/image/kis_raster_keyframe_channel.cpp --- a/libs/image/kis_raster_keyframe_channel.cpp +++ b/libs/image/kis_raster_keyframe_channel.cpp @@ -260,7 +260,8 @@ KisKeyframeSP KisRasterKeyframeChannel::loadKeyframe(const QDomElement &keyframeNode) { - int time = keyframeNode.attribute("time").toUInt(); + int time = keyframeNode.attribute("time").toInt(); + workaroundBrokenFrameTimeBug(&time); QPoint offset; KisDomUtils::loadValue(keyframeNode, "offset", &offset); @@ -271,7 +272,7 @@ if (m_d->frameFilenames.isEmpty()) { // First keyframe loaded: use the existing frame - Q_ASSERT(keyframeCount() == 1); + KIS_SAFE_ASSERT_RECOVER_NOOP(keyframeCount() == 1); keyframe = constKeys().begin().value(); // Remove from keys. It will get reinserted with new time once we return diff --git a/libs/image/kis_scalar_keyframe_channel.cpp b/libs/image/kis_scalar_keyframe_channel.cpp --- a/libs/image/kis_scalar_keyframe_channel.cpp +++ b/libs/image/kis_scalar_keyframe_channel.cpp @@ -442,7 +442,9 @@ KisKeyframeSP KisScalarKeyframeChannel::loadKeyframe(const QDomElement &keyframeNode) { - int time = keyframeNode.toElement().attribute("time").toUInt(); + int time = keyframeNode.toElement().attribute("time").toInt(); + workaroundBrokenFrameTimeBug(&time); + qreal value = KisDomUtils::toDouble(keyframeNode.toElement().attribute("value")); KUndo2Command tempParentCommand; diff --git a/plugins/tools/tool_transform2/kis_transform_args_keyframe_channel.cpp b/plugins/tools/tool_transform2/kis_transform_args_keyframe_channel.cpp --- a/plugins/tools/tool_transform2/kis_transform_args_keyframe_channel.cpp +++ b/plugins/tools/tool_transform2/kis_transform_args_keyframe_channel.cpp @@ -106,7 +106,9 @@ ToolTransformArgs args; args.fromXML(keyframeNode); - int time = keyframeNode.attribute("time").toUInt(); + int time = keyframeNode.attribute("time").toInt(); + workaroundBrokenFrameTimeBug(&time); + KisTransformArgsKeyframe *keyframe = new KisTransformArgsKeyframe(this, time, args); return toQShared(keyframe);