...
 
Commits (24)
......@@ -52,7 +52,7 @@ void runRenderingTest(KisImageSP image, int numCores, int numClones)
cfg.setFrameRenderingClones(numClones);
}
const KisTimeRange range = image->animationInterface()->fullClipRange();
const KisTimeSpan range = image->animationInterface()->fullClipRange();
KisAsyncAnimationFramesSaveDialog dlg(image, range, "temp_frames.png", 0, 0);
dlg.setBatchMode(true);
......
......@@ -2552,6 +2552,44 @@
<statusTip></statusTip>
</Action>
<Action name="define_animation_cycle">
<icon></icon>
<text>Create cycle</text>
<whatsThis></whatsThis>
<toolTip></toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="delete_animation_cycle">
<icon></icon>
<text>Remove cycle</text>
<whatsThis></whatsThis>
<toolTip></toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
<Action name="add_animation_repeat">
<icon></icon>
<text>Add repeat</text>
<whatsThis></whatsThis>
<toolTip></toolTip>
<iconText></iconText>
<activationFlags>100000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut></shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
</Actions>
......
/*
* Copyright (c) 2019 Jouni Pentikäinen <joupent@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KRITA_KISCOLLECTIONUTILS_H
#define KRITA_KISCOLLECTIONUTILS_H
namespace KisCollectionUtils {
/**
* Finds the last item the in the map with a key less than or equal to the given one.
* Returns map.constEnd() if no such key exists.
*/
template<class T>
typename QMap<int, T>::const_iterator lastBeforeOrAt(const QMap<int, T> &map, int maximumKey)
{
typename QMap<int, T>::const_iterator i = map.upperBound(maximumKey);
if (i == map.constBegin()) return map.constEnd();
return i - 1;
}
/**
* Finds the last item the in the map with a key strictly less than the given key.
* Returns map.constEnd() if no such key exists.
*/
template<class T>
typename QMap<int, T>::const_iterator lastBefore(const QMap<int, T> &map, int currentKey)
{
typename QMap<int, T>::const_iterator active = lastBeforeOrAt(map, currentKey);
if (active == map.constEnd()) return map.constEnd();
if (currentKey > active.key()) return active;
if (active == map.constBegin()) return map.constEnd();
return active - 1;
}
/**
* Finds the first item the in the map with a key greater than the given one.
* Returns map.constEnd() if no such key exists.
*/
template<class T>
typename QMap<int, T>::const_iterator firstAfter(const QMap<int, T> &map, int currentKey)
{
return map.upperBound(currentKey);
}
}
#endif //KRITA_KISCOLLECTIONUTILS_H
......@@ -285,6 +285,7 @@ set(kritaimage_LIB_SRCS
kis_onion_skin_compositor.cpp
kis_onion_skin_cache.cpp
kis_idle_watcher.cpp
kis_animation_cycle.cpp
kis_psd_layer_style.cpp
kis_layer_properties_icons.cpp
......
/*
* Copyright (c) 2018 Jouni Pentikäinen <joupent@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_animation_cycle.h"
#include <kis_pointer_utils.h>
#include "kis_time_range.h"
#include "kis_keyframe_channel.h"
KisAnimationCycle::KisAnimationCycle(KisKeyframeChannel *channel, KisKeyframeSP firstKeyframe, KisKeyframeSP lastKeyframe)
: KisKeyframeBase(channel, firstKeyframe->time())
, m_firstSourceKeyframe(firstKeyframe)
, m_lastSourceKeyframe(lastKeyframe)
{}
KisAnimationCycle::KisAnimationCycle(const KisAnimationCycle &cycle, KisKeyframeSP firstKeyframe, KisKeyframeSP lastKeyframe)
: KisKeyframeBase(cycle.channel(), firstKeyframe->time())
, m_firstSourceKeyframe(firstKeyframe)
, m_lastSourceKeyframe(lastKeyframe)
, m_repeats(cycle.m_repeats)
{}
KisKeyframeSP KisAnimationCycle::firstSourceKeyframe() const
{
return m_firstSourceKeyframe;
}
KisKeyframeSP KisAnimationCycle::lastSourceKeyframe() const
{
return m_lastSourceKeyframe;
}
KisTimeSpan KisAnimationCycle::originalRange() const
{
const KisKeyframeBaseSP firstAfterCycle = m_lastSourceKeyframe->channel()->nextItem(*m_lastSourceKeyframe);
KisTimeSpan range;
if (firstAfterCycle.isNull()) {
// TODO: semantics of repeat definition without a terminating keyframe?
range = KisTimeSpan(m_firstSourceKeyframe->time(), m_lastSourceKeyframe->time());
} else {
range = KisTimeSpan(m_firstSourceKeyframe->time(), firstAfterCycle->time() - 1);
}
return range;
}
int KisAnimationCycle::duration() const
{
return originalRange().duration();
}
void KisAnimationCycle::addRepeat(QSharedPointer<KisRepeatFrame> repeat)
{
if (m_repeats.contains(repeat)) return;
m_repeats.append(repeat);
}
void KisAnimationCycle::removeRepeat(QSharedPointer<KisRepeatFrame> repeat)
{
m_repeats.removeAll(repeat);
}
const QVector<QWeakPointer<KisRepeatFrame>>& KisAnimationCycle::repeats() const
{
return m_repeats;
}
KisFrameSet KisAnimationCycle::instancesWithin(KisKeyframeSP original, KisTimeSpan range) const
{
KisFrameSet frames;
const int originalTime = original->time();
const KisKeyframeBaseSP next = original->channel()->nextItem(*original);
const int frameDuration = (next.isNull()) ? -1 : next->time() - originalTime;
const int interval = duration();
int infiniteFrom = frameDuration == -1 ? originalTime : -1;
QVector<KisTimeSpan> spans;
Q_FOREACH(const QWeakPointer<KisRepeatFrame> repeatFrame, m_repeats) {
auto repeat = repeatFrame.toStrongRef();
if (!repeat) continue;
const int endTime = repeat->end();
if (range.isEmpty()) {
if (endTime != -1) {
range = KisTimeSpan(0, endTime - 1);
} else {
infiniteFrom = repeat->firstInstanceOf(originalTime);
continue;
}
}
KisTimeSpan repeatRange = (endTime != -1) ? range & KisTimeSpan(repeat->time(), endTime) : range.truncateRight(repeat->time());
int firstInstance = repeat->firstInstanceOf(originalTime);
if (firstInstance == -1) continue;
if (firstInstance < repeatRange.start()) {
firstInstance += interval * ((range.start() - firstInstance) / interval);
}
for (int repeatTime = firstInstance; repeatTime <= repeatRange.end(); repeatTime += interval) {
bool endsWithinRange = frameDuration != -1 && repeatTime + frameDuration - 1 <= repeatRange.end();
const int repeatEndTime = endsWithinRange ? (repeatTime + frameDuration - 1) : repeatRange.end();
spans.append(KisTimeSpan(repeatTime, repeatEndTime));
}
}
frames |= KisFrameSet(spans);
if (infiniteFrom != -1) {
frames |= KisFrameSet::infiniteFrom(infiniteFrom);
} else {
frames |= KisFrameSet::between(originalTime, originalTime + frameDuration - 1);
}
return frames;
}
QRect KisAnimationCycle::affectedRect() const
{
QRect rect;
KisKeyframeSP keyframe = m_firstSourceKeyframe;
do {
rect |= keyframe->affectedRect();
keyframe = channel()->nextKeyframe(keyframe);
} while (keyframe && keyframe != m_lastSourceKeyframe);
return rect;
}
KisKeyframeSP KisAnimationCycle::getOriginalKeyframeFor(int time) const
{
return channel()->activeKeyframeAt(time);
}
KisRepeatFrame::KisRepeatFrame(KisKeyframeChannel *channel, int time, QSharedPointer<KisAnimationCycle> cycle)
: KisKeyframeBase(channel, time)
, m_cycle(cycle)
{}
QSharedPointer<KisAnimationCycle> KisRepeatFrame::cycle() const
{
return m_cycle;
}
QRect KisRepeatFrame::affectedRect() const
{
KisKeyframeSP keyframe = m_cycle->firstSourceKeyframe();
QRect rect;
while (!keyframe.isNull() && keyframe->time() <= m_cycle->lastSourceKeyframe()->time()) {
rect |= keyframe->affectedRect();
keyframe = channel()->nextKeyframe(keyframe);
}
return rect;
}
int KisRepeatFrame::getOriginalTimeFor(int time) const
{
KisTimeSpan originalRange = m_cycle->originalRange();
int timeWithinCycle = (time - this->time()) % originalRange.duration();
return m_cycle->firstSourceKeyframe()->time() + timeWithinCycle;
}
KisKeyframeSP KisRepeatFrame::getOriginalKeyframeFor(int time) const
{
return channel()->activeKeyframeAt(getOriginalTimeFor(time));
}
int KisRepeatFrame::firstInstanceOf(int originalTime) const
{
KisTimeSpan originalRange = m_cycle->originalRange();
const int timeWithinCycle = originalTime - originalRange.start();
const int first = this->time() + timeWithinCycle;
const KisKeyframeSP next = channel()->nextKeyframe(time());
if (next && next->time() < first) return -1;
return first;
}
int KisRepeatFrame::previousVisibleFrame(int time) const
{
if (time <= this->time()) return -1;
const int earlierOriginalTime = getOriginalTimeFor(time - 1);
int originalStart, originalEnd;
channel()->activeKeyframeRange(earlierOriginalTime, &originalStart, &originalEnd);
if (originalEnd == -1) return -1;
const int durationOfOriginalKeyframe = originalEnd + 1 - originalStart;
return time - durationOfOriginalKeyframe;
}
int KisRepeatFrame::nextVisibleFrame(int time) const
{
const int originalTime = getOriginalTimeFor(time);
int originalStart, originalEnd;
channel()->activeKeyframeRange(originalTime, &originalStart, &originalEnd);
if (originalEnd == -1) return -1;
const int durationOfOriginalKeyframe = originalEnd + 1 - originalStart;
const int nextFrameTime = time + durationOfOriginalKeyframe;
const int endTime = end();
return (endTime == -1 || nextFrameTime < endTime) ? nextFrameTime : -1;
}
int KisRepeatFrame::end() const
{
const KisKeyframeBaseSP next = channel()->nextItem(*this);
return next ? next->time() - 1 : -1;
}
/*
* Copyright (c) 2018 Jouni Pentikäinen <joupent@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_ANIMATION_CYCLE_H
#define KIS_ANIMATION_CYCLE_H
#include "kis_keyframe.h"
class KisTimeSpan;
class KisFrameSet;
class KisRepeatFrame;
class KRITAIMAGE_EXPORT KisAnimationCycle : public KisKeyframeBase {
public:
KisAnimationCycle(KisKeyframeChannel *channel, KisKeyframeSP firstKeyframe, KisKeyframeSP lastKeyframe);
KisAnimationCycle(const KisAnimationCycle &cycle, KisKeyframeSP firstKeyframe, KisKeyframeSP lastKeyframe);
KisKeyframeSP firstSourceKeyframe() const;
KisKeyframeSP lastSourceKeyframe() const;
KisTimeSpan originalRange() const;
int duration() const;
void addRepeat(QSharedPointer<KisRepeatFrame> repeat);
void removeRepeat(QSharedPointer<KisRepeatFrame> repeat);
const QVector<QWeakPointer<KisRepeatFrame>>& repeats() const;
QRect affectedRect() const override;
KisKeyframeSP getOriginalKeyframeFor(int time) const override;
KisFrameSet instancesWithin(KisKeyframeSP original, KisTimeSpan range) const;
private:
friend class KisKeyframeChannel;
KisKeyframeSP m_firstSourceKeyframe, m_lastSourceKeyframe;
QVector<QWeakPointer<KisRepeatFrame>> m_repeats;
};
class KRITAIMAGE_EXPORT KisRepeatFrame : public KisKeyframeBase
{
public:
KisRepeatFrame(KisKeyframeChannel *channel, int time, QSharedPointer<KisAnimationCycle> cycle);
QSharedPointer<KisAnimationCycle> cycle() const;
QRect affectedRect() const override;
int getOriginalTimeFor(int time) const;
KisKeyframeSP getOriginalKeyframeFor(int time) const override;
/// Returns the earliest time the original frame appears in this repeat, or -1 if it never does.
int firstInstanceOf(int originalTime) const;
/** Returns the time at which the previous frame within the repeat appears,
* or -1 if time is at the first repeated frame.
* NB: time should be at the start of a repeated frame
*/
int previousVisibleFrame(int time) const;
/** Returns the time at which the next frame within the repeat appears,
* or -1 if time is at the last repeated frame.
* NB: time should be at the start of a repeated frame
*/
int nextVisibleFrame(int time) const;
/**
* Finds the time of the next keyframe if any.
* Returns -1 if the cycle continues indefinitely.
*/
int end() const;
private:
QSharedPointer<KisAnimationCycle> m_cycle;
};
#endif
......@@ -140,7 +140,7 @@ bool KisColorSpaceConvertVisitor::convertPaintDevice(KisLayer* layer)
paintLayer->setAlphaLocked(alphaLock);
}
layer->setDirty();
layer->invalidateFrames(KisTimeRange::infinite(0), layer->extent());
layer->invalidateFrames(KisFrameSet::infiniteFrom(0), layer->extent());
return true;
......
......@@ -393,7 +393,7 @@ void KisImage::nodeChanged(KisNode* node)
void KisImage::invalidateAllFrames()
{
invalidateFrames(KisTimeRange::infinite(0), QRect());
invalidateFrames(KisFrameSet::infiniteFrom(0), QRect());
}
void KisImage::setOverlaySelectionMask(KisSelectionMaskSP mask)
......@@ -1740,9 +1740,9 @@ void KisImage::requestProjectionUpdate(KisNode *node, const QVector<QRect> &rect
KisNodeGraphListener::requestProjectionUpdate(node, rects, resetAnimationCache);
}
void KisImage::invalidateFrames(const KisTimeRange &range, const QRect &rect)
void KisImage::invalidateFrames(const KisFrameSet &frames, const QRect &rect)
{
m_d->animationInterface->invalidateFrames(range, rect);
m_d->animationInterface->invalidateFrames(frames, rect);
}
void KisImage::requestTimeSwitch(int time)
......
......@@ -54,6 +54,7 @@ class KisSpontaneousJob;
class KisImageAnimationInterface;
class KUndo2MagicString;
class KisProofingConfiguration;
class KisFrameSet;
namespace KisMetaData
{
......@@ -92,7 +93,7 @@ public: // KisNodeGraphListener implementation
void invalidateAllFrames() override;
void notifySelectionChanged() override;
void requestProjectionUpdate(KisNode *node, const QVector<QRect> &rects, bool resetAnimationCache) override;
void invalidateFrames(const KisTimeRange &range, const QRect &rect) override;
void invalidateFrames(const KisFrameSet &range, const QRect &rect) override;
void requestTimeSwitch(int time) override;
KisNode* graphOverlayNode() const override;
......
......@@ -66,8 +66,8 @@ struct KisImageAnimationInterface::Private
bool externalFrameActive;
bool frameInvalidationBlocked;
KisTimeRange fullClipRange;
KisTimeRange playbackRange;
KisTimeSpan fullClipRange;
KisTimeSpan playbackRange;
int framerate;
int cachedLastFrameValue;
QString audioChannelFileName;
......@@ -103,7 +103,7 @@ KisImageAnimationInterface::KisImageAnimationInterface(KisImage *image)
m_d->image = image;
m_d->framerate = 24;
m_d->fullClipRange = KisTimeRange::fromTime(0, 100);
m_d->fullClipRange = KisTimeSpan(0, 100);
connect(this, SIGNAL(sigInternalRequestTimeSwitch(int,bool)), SLOT(switchCurrentTimeAsync(int,bool)));
}
......@@ -141,38 +141,36 @@ int KisImageAnimationInterface::currentUITime() const
return m_d->currentUITime();
}
const KisTimeRange& KisImageAnimationInterface::fullClipRange() const
const KisTimeSpan& KisImageAnimationInterface::fullClipRange() const
{
return m_d->fullClipRange;
}
void KisImageAnimationInterface::setFullClipRange(const KisTimeRange range)
void KisImageAnimationInterface::setFullClipRange(const KisTimeSpan range)
{
KIS_SAFE_ASSERT_RECOVER_RETURN(!range.isInfinite());
m_d->fullClipRange = range;
emit sigFullClipRangeChanged();
}
void KisImageAnimationInterface::setFullClipRangeStartTime(int column)
{
KisTimeRange newRange(column, m_d->fullClipRange.end(), false);
KisTimeSpan newRange(column, m_d->fullClipRange.end());
setFullClipRange(newRange);
}
void KisImageAnimationInterface::setFullClipRangeEndTime(int column)
{
KisTimeRange newRange(m_d->fullClipRange.start(), column, false);
KisTimeSpan newRange(m_d->fullClipRange.start(), column);
setFullClipRange(newRange);
}
const KisTimeRange& KisImageAnimationInterface::playbackRange() const
const KisTimeSpan& KisImageAnimationInterface::playbackRange() const
{
return m_d->playbackRange.isValid() ? m_d->playbackRange : m_d->fullClipRange;
return !m_d->playbackRange.isEmpty() ? m_d->playbackRange : m_d->fullClipRange;
}
void KisImageAnimationInterface::setPlaybackRange(const KisTimeRange range)
void KisImageAnimationInterface::setPlaybackRange(const KisTimeSpan range)
{
KIS_SAFE_ASSERT_RECOVER_RETURN(!range.isInfinite());
m_d->playbackRange = range;
emit sigPlaybackRangeChanged();
}
......@@ -265,8 +263,7 @@ void KisImageAnimationInterface::switchCurrentTimeAsync(int frameId, bool useUnd
{
if (currentUITime() == frameId) return;
const KisTimeRange range = KisTimeRange::calculateIdenticalFramesRecursive(m_d->image->root(), currentUITime());
const bool needsRegeneration = !range.contains(frameId);
const bool needsRegeneration = !areFramesIdentical(m_d->image->root(), currentUITime(), frameId);
KisSwitchTimeStrokeStrategy::SharedTokenSP token =
m_d->switchToken.toStrongRef();
......@@ -361,12 +358,12 @@ void KisImageAnimationInterface::notifyNodeChanged(const KisNode *node,
if (node->inherits("KisSelectionMask")) return;
const int currentTime = m_d->currentTime();
KisTimeRange invalidateRange;
KisFrameSet invalidateRange;
if (recursive) {
invalidateRange = KisTimeRange::calculateAffectedFramesRecursive(node, currentTime);
invalidateRange = calculateAffectedFramesRecursive(node, currentTime);
} else {
invalidateRange = KisTimeRange::calculateNodeAffectedFrames(node, currentTime);
invalidateRange = calculateNodeAffectedFrames(node, currentTime);
}
// we compress the updated rect (atm, no one uses it anyway)
......@@ -378,7 +375,7 @@ void KisImageAnimationInterface::notifyNodeChanged(const KisNode *node,
invalidateFrames(invalidateRange, unitedRect);
}
void KisImageAnimationInterface::invalidateFrames(const KisTimeRange &range, const QRect &rect)
void KisImageAnimationInterface::invalidateFrames(const KisFrameSet &range, const QRect &rect)
{
m_d->cachedLastFrameValue = -1;
emit sigFramesChanged(range, rect);
......
......@@ -26,7 +26,8 @@
#include "kritaimage_export.h"
class KisUpdatesFacade;
class KisTimeRange;
class KisFrameSet;
class KisTimeSpan;
class KoColor;
namespace KisLayerUtils {
......@@ -103,7 +104,7 @@ public:
void notifyNodeChanged(const KisNode *node, const QRect &rect, bool recursive);
void notifyNodeChanged(const KisNode *node, const QVector<QRect> &rects, bool recursive);
void invalidateFrames(const KisTimeRange &range, const QRect &rect);
void invalidateFrames(const KisFrameSet &range, const QRect &rect);
/**
* Changes the default color of the "external frame" projection of
......@@ -116,15 +117,15 @@ public:
* The current time range selected by user.
* @return current time range
*/
const KisTimeRange& fullClipRange() const;
void setFullClipRange(const KisTimeRange range);
const KisTimeSpan& fullClipRange() const;
void setFullClipRange(const KisTimeSpan range);
void setFullClipRangeStartTime(int column);
void setFullClipRangeEndTime(int column);
const KisTimeRange &playbackRange() const;
void setPlaybackRange(const KisTimeRange range);
const KisTimeSpan &playbackRange() const;
void setPlaybackRange(const KisTimeSpan range);
int framerate() const;
......@@ -187,7 +188,7 @@ Q_SIGNALS:
void sigFrameReady(int time);
void sigFrameCancelled();
void sigUiTimeChanged(int newTime);
void sigFramesChanged(const KisTimeRange &range, const QRect &rect);
void sigFramesChanged(const KisFrameSet &range, const QRect &rect);
void sigInternalRequestTimeSwitch(int frameId, bool useUndo);
......
......@@ -16,8 +16,8 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_image_config.h"
#include "kis_keyframe.h"
#include "kis_image_config.h"
#include "kis_keyframe_channel.h"
#include "kis_types.h"
......@@ -25,36 +25,62 @@
struct KisKeyframeSPStaticRegistrar {
KisKeyframeSPStaticRegistrar() {
qRegisterMetaType<KisKeyframeBaseSP>("KisKeyframeBaseSP");
qRegisterMetaType<KisKeyframeSP>("KisKeyframeSP");
}
};
static KisKeyframeSPStaticRegistrar __registrar;
struct KisKeyframe::Private
struct KisKeyframeBase::Private
{
QPointer<KisKeyframeChannel> channel;
int time;
InterpolationMode interpolationMode;
InterpolationTangentsMode tangentsMode;
Private(KisKeyframeChannel *channel, int time)
: channel(channel), time(time)
{}
};
KisKeyframeBase::KisKeyframeBase(KisKeyframeChannel *channel, int time)
: m_d(new Private(channel, time))
{}
KisKeyframeBase::~KisKeyframeBase() = default;
KisKeyframeChannel *KisKeyframeBase::channel() const
{
return m_d->channel;
}
int KisKeyframeBase::time() const
{
return m_d->time;
}
void KisKeyframeBase::setTime(int time)
{
m_d->time = time;
}
struct KisKeyframe::Private
{
InterpolationMode interpolationMode{Constant};
InterpolationTangentsMode tangentsMode{Smooth};
QPointF leftTangent;
QPointF rightTangent;
int colorLabel{0};
Private(KisKeyframeChannel *channel, int time)
: channel(channel), time(time), interpolationMode(Constant)
{}
};
KisKeyframe::KisKeyframe(KisKeyframeChannel *channel, int time)
: m_d(new Private(channel, time))
: KisKeyframeBase(channel, time)
, m_d(new Private())
{
m_d->colorLabel = KisImageConfig(true).defaultFrameColorLabel();
}
KisKeyframe::KisKeyframe(const KisKeyframe *rhs, KisKeyframeChannel *channel)
: m_d(new Private(channel, rhs->time()))
: KisKeyframeBase(channel, rhs->time())
, m_d(new Private())
{
m_d->interpolationMode = rhs->m_d->interpolationMode;
m_d->tangentsMode = rhs->m_d->tangentsMode;
......@@ -66,16 +92,6 @@ KisKeyframe::KisKeyframe(const KisKeyframe *rhs, KisKeyframeChannel *channel)
KisKeyframe::~KisKeyframe()
{}
int KisKeyframe::time() const
{
return m_d->time;
}
void KisKeyframe::setTime(int time)
{
m_d->time = time;
}
void KisKeyframe::setInterpolationMode(KisKeyframe::InterpolationMode mode)
{
m_d->interpolationMode = mode;
......@@ -126,7 +142,7 @@ bool KisKeyframe::hasContent() const {
return true;
}
KisKeyframeChannel *KisKeyframe::channel() const
KisKeyframeSP KisKeyframe::getOriginalKeyframeFor(int) const
{
return m_d->channel;
return channel()->keyframeAt(this->time());
}
......@@ -28,7 +28,26 @@
class KisKeyframeChannel;
class KRITAIMAGE_EXPORT KisKeyframe
class KRITAIMAGE_EXPORT KisKeyframeBase
{
public:
KisKeyframeBase(KisKeyframeChannel *channel, int time);
virtual ~KisKeyframeBase();
KisKeyframeChannel *channel() const;
int time() const;
void setTime(int time);
virtual QRect affectedRect() const = 0;
virtual KisKeyframeSP getOriginalKeyframeFor(int time) const = 0;
private:
struct Private;
QScopedPointer<Private> m_d;
};
class KRITAIMAGE_EXPORT KisKeyframe : public KisKeyframeBase
{
public:
enum InterpolationMode {
......@@ -43,7 +62,7 @@ public:
};
KisKeyframe(KisKeyframeChannel *channel, int time);
virtual ~KisKeyframe();
~KisKeyframe() override;
/**
* Create a copy of the keyframe for insertion into given channel.
......@@ -51,9 +70,6 @@ public:
*/
virtual KisKeyframeSP cloneFor(KisKeyframeChannel *channel) const = 0;
int time() const;
void setTime(int time);
void setInterpolationMode(InterpolationMode mode);
InterpolationMode interpolationMode() const;
void setTangentsMode(InterpolationTangentsMode mode);
......@@ -64,9 +80,10 @@ public:
int colorLabel() const;
void setColorLabel(int label);
virtual bool hasContent() const; // does any content exist in keyframe, or is it empty?
KisKeyframeChannel *channel() const;
KisKeyframeSP getOriginalKeyframeFor(int time) const override;
protected:
KisKeyframe(const KisKeyframe *rhs, KisKeyframeChannel *channel);
......@@ -76,6 +93,8 @@ private:
QScopedPointer<Private> m_d;
};
Q_DECLARE_METATYPE(KisKeyframeBase*)
Q_DECLARE_METATYPE(KisKeyframeBaseSP)
Q_DECLARE_METATYPE(KisKeyframe*)
Q_DECLARE_METATYPE(KisKeyframeSP)
#endif
This diff is collapsed.
......@@ -29,8 +29,12 @@
#include "kis_keyframe.h"
#include "kis_default_bounds.h"
class KisTimeRange;
class KisFrameSet;
class KisTimeSpan;
class KisAnimationCycle;
class KisRepeatFrame;
class KisVisibleKeyframeIterator;
class KisDefineCycleCommand;
class KRITAIMAGE_EXPORT KisKeyframeChannel : public QObject
{
......@@ -64,23 +68,65 @@ public:
KisNodeWSP node() const;
KisKeyframeSP addKeyframe(int time, KUndo2Command *parentCommand = 0);
bool deleteKeyframe(KisKeyframeSP keyframe, KUndo2Command *parentCommand = 0);
bool moveKeyframe(KisKeyframeSP keyframe, int newTime, KUndo2Command *parentCommand = 0);
bool deleteKeyframe(KisKeyframeBaseSP keyframe, KUndo2Command *parentCommand = 0);
bool moveKeyframe(KisKeyframeBaseSP keyframe, int newTime, KUndo2Command *parentCommand = 0);
bool swapFrames(int lhsTime, int rhsTime, KUndo2Command *parentCommand = 0);
KisKeyframeSP copyKeyframe(const KisKeyframeSP keyframe, int newTime, KUndo2Command *parentCommand = 0);
KisKeyframeBaseSP copyItem(const KisKeyframeBaseSP item, int newTime, KUndo2Command *parentCommand = 0);
KisKeyframeSP copyAsKeyframe(const KisKeyframeBaseSP item, int originalTime, int newTime, KUndo2Command *parentCommand = 0);
virtual KisKeyframeSP linkKeyframe(const KisKeyframeBaseSP keyframe, int newTime, KUndo2Command *parentCommand = 0);
KisKeyframeSP copyExternalKeyframe(KisKeyframeChannel *srcChannel, int srcTime, int dstTime, KUndo2Command *parentCommand = 0);
KisDefineCycleCommand * createCycle(KisKeyframeSP firstKeyframe, KisKeyframeSP lastKeyframe, KUndo2Command *parentCommand = 0);
KUndo2Command * deleteCycle(QSharedPointer<KisAnimationCycle> cycle, KUndo2Command *parentCommand = 0);
QSharedPointer<KisRepeatFrame> addRepeat(QSharedPointer<KisAnimationCycle> cycle, int time, KUndo2Command *parentCommand);
bool swapExternalKeyframe(KisKeyframeChannel *srcChannel, int srcTime, int dstTime, KUndo2Command *parentCommand = 0);
KisKeyframeSP keyframeAt(int time) const;
KisKeyframeSP activeKeyframeAt(int time) const;
KisKeyframeSP visibleKeyframeAt(int time) const;
KisKeyframeSP currentlyActiveKeyframe() const;
KisKeyframeSP firstKeyframe() const;
KisKeyframeSP nextKeyframe(KisKeyframeSP keyframe) const;
KisKeyframeSP previousKeyframe(KisKeyframeSP keyframe) const;
KisKeyframeSP nextKeyframe(int time) const;
KisKeyframeSP previousKeyframe(int time) const;
KisKeyframeSP lastKeyframe() const;
KisKeyframeBaseSP itemAt(int time) const;
KisKeyframeBaseSP activeItemAt(int time) const;
KisKeyframeBaseSP nextItem(const KisKeyframeBase &item) const;
KisKeyframeBaseSP previousItem(const KisKeyframeBase &item) const;
KisVisibleKeyframeIterator visibleKeyframesFrom(int time) const;
QList<QSharedPointer<KisAnimationCycle>> cycles() const;
/**
* Finds the original range of the cycle defined or repeated at the given time.
* @arg time a time at any frame within the original cycle or any repeat of it.
*/
KisTimeSpan cycledRangeAt(int time) const;
/**
* Finds the cycle defined at time, if any.
* @arg time a time within the original range of the cycle.
*/
QSharedPointer<KisAnimationCycle> cycleAt(int time) const;
/**
* Finds the repeat of a cycle at the time, if any.
*/
QSharedPointer<KisRepeatFrame> activeRepeatAt(int time) const;
/**
* Finds the span of time of the keyframe active at given time.
* If there is no active keyframe, first will be -1.
* If the keyframe continues indefinitely, last will be -1.
*/
void activeKeyframeRange(int time, int *first, int *last) const;
/**
* Calculates a pseudo-unique keyframes hash. The hash changes
* every time any frame is added/removed/moved
......@@ -92,7 +138,7 @@ public:
* Get the set of frames affected by any changes to the value
* of the active keyframe at the given time.
*/
KisTimeRange affectedFrames(int time) const;
virtual KisFrameSet affectedFrames(int time) const;
/**
* Get a set of frames for which the channel gives identical
......@@ -101,15 +147,12 @@ public:
* Note: this set may be different than the set of affected frames
* due to interpolation.
*/
KisTimeRange identicalFrames(int time) const;
virtual KisFrameSet identicalFrames(int time, const KisTimeSpan range) const;
virtual bool areFramesIdentical(int time1, int time2) const;
virtual bool isFrameAffectedBy(int targetFrame, int changedFrame) const;
int keyframeCount() const;
int keyframeRowIndexOf(KisKeyframeSP keyframe) const;
KisKeyframeSP keyframeAtRow(int row) const;
int keyframeInsertionRow(int time) const;
virtual bool hasScalarValue() const = 0;
virtual qreal minScalarValue() const;
virtual qreal maxScalarValue() const;
......@@ -122,27 +165,25 @@ public:
int currentTime() const;
Q_SIGNALS:
void sigKeyframeAboutToBeAdded(KisKeyframeSP keyframe);
void sigKeyframeAdded(KisKeyframeSP keyframe);
void sigKeyframeAboutToBeRemoved(KisKeyframeSP keyframe);
void sigKeyframeRemoved(KisKeyframeSP keyframe);
void sigKeyframeAboutToBeMoved(KisKeyframeSP keyframe, int toTime);
void sigKeyframeMoved(KisKeyframeSP keyframe, int fromTime);
void sigKeyframeChanged(KisKeyframeSP keyframe);
void sigKeyframeAboutToBeAdded(KisKeyframeBaseSP keyframe);
void sigKeyframeAdded(KisKeyframeBaseSP keyframe);
void sigKeyframeAboutToBeRemoved(KisKeyframeBaseSP keyframe);
void sigKeyframeRemoved(KisKeyframeBaseSP keyframe);
void sigKeyframeAboutToBeMoved(KisKeyframeBaseSP keyframe, int toTime);
void sigKeyframeMoved(KisKeyframeBaseSP keyframe, int fromTime);
void sigKeyframeChanged(KisKeyframeBaseSP keyframe);
protected:
typedef QMap<int, KisKeyframeSP> KeyframesMap;
KeyframesMap &keys();
const KeyframesMap &constKeys() const;
KeyframesMap::const_iterator activeKeyIterator(int time) const;
virtual KisKeyframeSP createKeyframe(int time, const KisKeyframeSP copySrc, KUndo2Command *parentCommand) = 0;
virtual void destroyKeyframe(KisKeyframeSP key, KUndo2Command *parentCommand) = 0;
virtual void uploadExternalKeyframe(KisKeyframeChannel *srcChannel, int srcTime, KisKeyframeSP dstFrame) = 0;
virtual QRect affectedRect(KisKeyframeSP key) = 0;
virtual void requestUpdate(const KisTimeRange &range, const QRect &rect);
virtual void requestUpdate(const KisFrameSet &range, const QRect &rect);
virtual KisKeyframeSP loadKeyframe(const QDomElement &keyframeNode) = 0;
virtual void saveKeyframe(KisKeyframeSP keyframe, QDomElement keyframeElement, const QString &layerFilename) = 0;
......@@ -150,22 +191,49 @@ protected:
void workaroundBrokenFrameTimeBug(int *time);
private:
KisKeyframeSP replaceKeyframeAt(int time, KisKeyframeSP newKeyframe);
void insertKeyframeLogical(KisKeyframeSP keyframe);
void removeKeyframeLogical(KisKeyframeSP keyframe);
bool deleteKeyframeImpl(KisKeyframeSP keyframe, KUndo2Command *parentCommand, bool recreate);
void moveKeyframeImpl(KisKeyframeSP keyframe, int newTime);
void swapKeyframesImpl(KisKeyframeSP lhsKeyframe, KisKeyframeSP rhsKeyframe);
KisKeyframeBaseSP replaceKeyframeAt(int time, KisKeyframeBaseSP newKeyframe);
void insertKeyframeLogical(KisKeyframeBaseSP keyframe);
void removeKeyframeLogical(KisKeyframeBaseSP keyframe);
bool deleteKeyframeImpl(KisKeyframeBaseSP keyframe, KUndo2Command *parentCommand, bool recreate);
void moveKeyframeImpl(KisKeyframeBaseSP keyframe, int newTime);
void swapKeyframesImpl(KisKeyframeBaseSP lhsKeyframe, KisKeyframeBaseSP rhsKeyframe);
void addCycle(QSharedPointer<KisAnimationCycle> cycle);
void removeCycle(QSharedPointer<KisAnimationCycle> cycle);
friend class KisMoveKeyframesCommand;
friend class KisMoveFrameCommand;
friend class KisReplaceKeyframeCommand;
friend class KisSwapFramesCommand;
friend class KisDefineCycleCommand;
private:
KisKeyframeSP insertKeyframe(int time, const KisKeyframeSP copySrc, KUndo2Command *parentCommand);
KisKeyframeSP insertKeyframe(int time, const KisKeyframeBaseSP copySrc, KUndo2Command *parentCommand);
QSharedPointer<KisAnimationCycle> loadCycle(const QDomElement &cycleElement);
struct Private;
QScopedPointer<Private> m_d;
};
class KisVisibleKeyframeIterator
{
public:
KisVisibleKeyframeIterator();
explicit KisVisibleKeyframeIterator(KisKeyframeSP keyframe);
KisVisibleKeyframeIterator& operator--();
KisVisibleKeyframeIterator& operator++();
bool isValid() const;
KisKeyframeSP operator->() const;
KisKeyframeSP operator*() const;
private:
KisVisibleKeyframeIterator &invalidate();
KisKeyframeChannel *m_channel{nullptr};
KisKeyframeSP m_keyframe;
int m_time{-1};
};
#endif // KIS_KEYFRAME_CHANNEL_H
This diff is collapsed.
......@@ -21,35 +21,49 @@
#include "kundo2command.h"
#include "kritaimage_export.h"
namespace KisKeyframeCommands
{
struct KRITAIMAGE_EXPORT KeyframeMove
{
KisKeyframeBaseSP keyframe;
int oldTime, newTime;
KeyframeMove() = default;
KeyframeMove(KisKeyframeBaseSP keyframe, int newTime);
};
/**
* Returns either a new command for operations needed to move the keyframes or null if the operation is invalid against the current state of the channel
*/
KRITAIMAGE_EXPORT KUndo2CommandSP tryMoveKeyframes(KisKeyframeChannel *channel, QVector<KeyframeMove> moves, KUndo2Command *parentCommand);
}
class KRITAIMAGE_EXPORT KisReplaceKeyframeCommand : public KUndo2Command
{
public:
KisReplaceKeyframeCommand(KisKeyframeChannel *channel, int time, KisKeyframeSP keyframe, KUndo2Command *parentCommand);
KisReplaceKeyframeCommand(KisKeyframeChannel *channel, int time, KisKeyframeBaseSP keyframe, KUndo2Command *parentCommand);
void redo() override;
void undo() override;
private:
void doSwap(bool insert);
private:
KisKeyframeChannel *m_channel;
int m_time;
KisKeyframeSP m_keyframe;
KisKeyframeSP m_existingKeyframe;
KisKeyframeBaseSP m_keyframe;
KisKeyframeBaseSP m_existingKeyframe;
};
class KRITAIMAGE_EXPORT KisMoveFrameCommand : public KUndo2Command
{
public:
KisMoveFrameCommand(KisKeyframeChannel *channel, KisKeyframeSP keyframe, int oldTime, int newTime, KUndo2Command *parentCommand);
KisMoveFrameCommand(KisKeyframeChannel *channel, KisKeyframeBaseSP keyframe, int oldTime, int newTime, KUndo2Command *parentCommand);
void redo() override;
void undo() override;
private:
KisKeyframeChannel *m_channel;
KisKeyframeSP m_keyframe;
KisKeyframeBaseSP m_keyframe;
int m_oldTime;
int m_newTime;
};
......@@ -57,15 +71,31 @@ private:
class KRITAIMAGE_EXPORT KisSwapFramesCommand : public KUndo2Command
{
public:
KisSwapFramesCommand(KisKeyframeChannel *channel, KisKeyframeSP lhsFrame, KisKeyframeSP rhsFrame, KUndo2Command *parentCommand);
KisSwapFramesCommand(KisKeyframeChannel *channel, KisKeyframeBaseSP lhsFrame, KisKeyframeBaseSP rhsFrame, KUndo2Command *parentCommand);
void redo() override;
void undo() override;
private:
KisKeyframeChannel *m_channel;
KisKeyframeBaseSP m_lhsFrame;
KisKeyframeBaseSP m_rhsFrame;
};
class KRITAIMAGE_EXPORT KisDefineCycleCommand : public KUndo2Command
{
public:
KisDefineCycleCommand(QSharedPointer<KisAnimationCycle> oldCycle, QSharedPointer<KisAnimationCycle> newCycle, KUndo2Command *parentCommand);
QSharedPointer<KisAnimationCycle> cycle() const;
void redo() override;
void undo() override;
private:
KisKeyframeChannel *m_channel;
KisKeyframeSP m_lhsFrame;
KisKeyframeSP m_rhsFrame;
QSharedPointer<KisAnimationCycle> m_oldCycle;
QSharedPointer<KisAnimationCycle> m_newCycle;
};
#endif
......@@ -943,12 +943,12 @@ QImage KisLayer::createThumbnailForFrame(qint32 w, qint32 h, int time)
}
KisPaintDeviceSP originalDevice = original();
if (originalDevice ) {
if (originalDevice && originalDevice->framesInterface()) {
KisRasterKeyframeChannel *channel = originalDevice->keyframeChannel();
if (channel) {
KisPaintDeviceSP targetDevice = new KisPaintDevice(colorSpace());
KisKeyframeSP keyframe = channel->activeKeyframeAt(time);
KisKeyframeSP keyframe = channel->visibleKeyframeAt(time);
channel->fetchFrame(keyframe, targetDevice);
return targetDevice->createThumbnail(w, h, 1,
KoColorConversionTransformation::internalRenderingIntent(),
......
......@@ -858,7 +858,7 @@ namespace KisLayerUtils {
void applyKeyframeColorLabel(KisKeyframeSP dstKeyframe) {
Q_FOREACH(KisNodeSP srcNode, m_info->allSrcNodes()) {
Q_FOREACH(KisKeyframeChannel *channel, srcNode->keyframeChannels().values()) {
KisKeyframeSP keyframe = channel->keyframeAt(m_frame);
KisKeyframeSP keyframe = channel->visibleKeyframeAt(m_frame);
if (!keyframe.isNull() && keyframe->colorLabel() != 0) {
dstKeyframe->setColorLabel(keyframe->colorLabel());
return;
......
......@@ -618,7 +618,7 @@ void KisNode::setDirtyDontResetAnimationCache(const QVector<QRect> &rects)
}
}
void KisNode::invalidateFrames(const KisTimeRange &range, const QRect &rect)
void KisNode::invalidateFrames(const KisFrameSet &range, const QRect &rect)
{
if(m_d->graphListener) {
m_d->graphListener->invalidateFrames(range, rect);
......
......@@ -38,7 +38,7 @@ class KisBusyProgressIndicator;
class KisAbstractProjectionPlane;
class KisProjectionLeaf;
class KisKeyframeChannel;
class KisTimeRange;
class KisFrameSet;
class KisUndoAdapter;
......@@ -156,7 +156,7 @@ public:
* and need to be recached.
* @param range frames to invalidate
*/
void invalidateFrames(const KisTimeRange &range, const QRect &rect);
void invalidateFrames(const KisFrameSet &range, const QRect &rect);
/**
* Informs that the current world time should be changed.
......
......@@ -90,7 +90,7 @@ void KisNodeGraphListener::requestProjectionUpdate(KisNode * /*node*/, const QVe
{
}
void KisNodeGraphListener::invalidateFrames(const KisTimeRange &range, const QRect &rect)
void KisNodeGraphListener::invalidateFrames(const KisFrameSet &range, const QRect &rect)
{
Q_UNUSED(range);
Q_UNUSED(rect);
......
......@@ -22,7 +22,7 @@
#include <QScopedPointer>
class KisTimeRange;
class KisFrameSet;
class KisNode;
class QRect;
......@@ -97,7 +97,7 @@ public:
*/
virtual void requestProjectionUpdate(KisNode * node, const QVector<QRect> &rects, bool resetAnimationCache);
virtual void invalidateFrames(const KisTimeRange &range, const QRect &rect);
virtual void invalidateFrames(const KisFrameSet &range, const QRect &rect);
virtual void requestTimeSwitch(int time);
......
......@@ -58,13 +58,13 @@ struct KisOnionSkinCompositor::Private
}
KisKeyframeSP getNextFrameToComposite(KisKeyframeChannel *channel, KisKeyframeSP keyframe, bool backwards)
KisVisibleKeyframeIterator getNextFrameToComposite(KisVisibleKeyframeIterator keyframe, bool backwards)
{
while (!keyframe.isNull()) {
keyframe = backwards ? channel->previousKeyframe(keyframe) : channel->nextKeyframe(keyframe);
while (keyframe.isValid()) {
keyframe = backwards ? --keyframe : ++keyframe;
if (colorLabelFilter.isEmpty()) {
return keyframe;
} else if (!keyframe.isNull()) {
} else if (keyframe.isValid()) {
if (colorLabelFilter.contains(keyframe->colorLabel())) {
return keyframe;
}
......@@ -152,22 +152,20 @@ void KisOnionSkinCompositor::composite(const KisPaintDeviceSP sourceDevice, KisP
KisPainter gcDest(targetDevice);
gcDest.setCompositeOp(sourceDevice->colorSpace()->compositeOp(COMPOSITE_BEHIND));
KisKeyframeSP keyframeBck;
KisKeyframeSP keyframeFwd;
int time = sourceDevice->defaultBounds()->currentTime();
keyframeBck = keyframeFwd = keyframes->activeKeyframeAt(time);
const int time = sourceDevice->defaultBounds()->currentTime();
KisVisibleKeyframeIterator backward = keyframes->visibleKeyframesFrom(time);
KisVisibleKeyframeIterator forward = backward;
for (int offset = 1; offset <= m_d->numberOfSkins; offset++) {
keyframeBck = m_d->getNextFrameToComposite(keyframes, keyframeBck, true);
keyframeFwd = m_d->getNextFrameToComposite(keyframes, keyframeFwd, false);
backward = m_d->getNextFrameToComposite(backward, true);
forward = m_d->getNextFrameToComposite(forward, false);
if (!keyframeBck.isNull()) {
m_d->tryCompositeFrame(keyframes, keyframeBck, gcFrame, gcDest, backwardTintDevice, m_d->skinOpacity(-offset), rect);
if (backward.isValid()) {
m_d->tryCompositeFrame(keyframes, *backward, gcFrame, gcDest, backwardTintDevice, m_d->skinOpacity(-offset), rect);
}
if (!keyframeFwd.isNull()) {
m_d->tryCompositeFrame(keyframes, keyframeFwd, gcFrame, gcDest, forwardTintDevice, m_d->skinOpacity(offset), rect);
if (forward.isValid()) {
m_d->tryCompositeFrame(keyframes, *forward, gcFrame, gcDest, forwardTintDevice, m_d->skinOpacity(offset), rect);
}
}
......@@ -197,22 +195,23 @@ QRect KisOnionSkinCompositor::calculateExtent(const KisPaintDeviceSP device)
KisKeyframeSP keyframeFwd;
KisRasterKeyframeChannel *channel = device->keyframeChannel();
keyframeBck = keyframeFwd = channel->activeKeyframeAt(device->defaultBounds()->currentTime());
KisVisibleKeyframeIterator forward = channel->visibleKeyframesFrom(device->defaultBounds()->currentTime());
KisVisibleKeyframeIterator backward = forward;
for (int offset = 1; offset <= m_d->numberOfSkins; offset++) {
if (!keyframeBck.isNull()) {
keyframeBck = channel->previousKeyframe(keyframeBck);
if (backward.isValid()) {
--backward;
if (!keyframeBck.isNull()) {
rect |= channel->frameExtents(keyframeBck);
rect |= channel->frameExtents(*backward);
}
}
if (!keyframeFwd.isNull()) {
keyframeFwd = channel->nextKeyframe(keyframeFwd);
if (forward.isValid()) {
++forward;
if (!keyframeFwd.isNull()) {
rect |= channel->frameExtents(keyframeFwd);
if (forward.isValid()) {
rect |= channel->frameExtents(*forward);
}
}
}
......
......@@ -25,6 +25,23 @@
#include "kis_time_range.h"
#include "kundo2command.h"
#include "kis_onion_skin_compositor.h"
#include "kis_keyframe_commands.h"
struct KisRasterKeyframeChannel::Private
{
Private(KisPaintDeviceWSP paintDevice, const QString filenameSuffix)
: paintDevice(paintDevice),
filenameSuffix(filenameSuffix),
onionSkinsEnabled(false)
{}
KisPaintDeviceWSP paintDevice;
QMap<QString, int> framesByFilename;
QMap<int, QString> frameFilenames;
QMap<int, QVector<KisKeyframeSP>> frameInstances;
QString filenameSuffix;
bool onionSkinsEnabled;
};
class KisRasterKeyframe : public KisKeyframe
{
......@@ -52,20 +69,38 @@ public:
return channel->keyframeHasContent(this);
}
};
struct KisRasterKeyframeChannel::Private
{
Private(KisPaintDeviceWSP paintDevice, const QString filenameSuffix)
: paintDevice(paintDevice),
filenameSuffix(filenameSuffix),
onionSkinsEnabled(false)
{}
KisPaintDeviceWSP paintDevice;
QMap<int, QString> frameFilenames;
QString filenameSuffix;
bool onionSkinsEnabled;
QRect affectedRect() const override
{
KisRasterKeyframeChannel *ch = dynamic_cast<KisRasterKeyframeChannel *>(channel());
KisRasterKeyframeChannel::Private *ch_d = ch->m_d.data();
// Calculate changed area as the union of the current and previous keyframe.
// This makes sure there are no artifacts left over from the previous frame
// where the new one doesn't cover the area.
KisKeyframeSP neighbor = ch->previousKeyframe(time());
// Using the *next* keyframe at the start of the timeline avoids artifacts
// when deleting or moving the first key
if (neighbor.isNull()) neighbor = ch->nextKeyframe(time());
QRect rect = ch_d->paintDevice->framesInterface()->frameBounds(frameId);
if (!neighbor.isNull()) {
// Note: querying through frameIdAt makes sure cycle repeats resolve to their original frames
const int neighborFrameId = ch->frameIdAt(neighbor->time());
rect |= ch_d->paintDevice->framesInterface()->frameBounds(neighborFrameId);
}
if (ch_d->onionSkinsEnabled) {
const QRect dirtyOnionSkinsRect =
KisOnionSkinCompositor::instance()->calculateFullExtent(ch_d->paintDevice);
rect |= dirtyOnionSkinsRect;
}
return rect;
}
};
KisRasterKeyframeChannel::KisRasterKeyframeChannel(const KoID &id, const KisPaintDeviceWSP paintDevice, KisDefaultBoundsBaseSP defaultBounds)
......@@ -88,6 +123,21 @@ KisRasterKeyframeChannel::~KisRasterKeyframeChannel()
{
}
KisKeyframeSP KisRasterKeyframeChannel::linkKeyframe(const KisKeyframeBaseSP source, int newTime, KUndo2Command *parentCommand)
{
KisKeyframeSP sourceKeyframe = source.dynamicCast<KisKeyframe>();
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(sourceKeyframe, KisKeyframeSP());
const int frame = frameId(sourceKeyframe);
KisKeyframeSP newKeyframe = toQShared(new KisRasterKeyframe(this, newTime, frame));
m_d->frameInstances[frame].append(newKeyframe);
KUndo2Command *cmd = new KisReplaceKeyframeCommand(this, newTime, newKeyframe, parentCommand);
cmd->redo();
return newKeyframe;
}
int KisRasterKeyframeChannel::frameId(KisKeyframeSP keyframe) const
{
return frameId(keyframe.data());
......@@ -102,7 +152,7 @@ int KisRasterKeyframeChannel::frameId(const KisKeyframe *keyframe) const