File indexing completed on 2024-05-19 04:26:18

0001 /*
0002  *  SPDX-FileCopyrightText: 2015 Jouni Pentikäinen <joupent@gmail.com>
0003  *  SPDX-FileCopyrightText: 2020 Emmet O 'Neill <emmetoneill.pdx@gmail.com>
0004  *  SPDX-FileCopyrightText: 2020 Eoin O 'Neill <eoinoneill1991@gmail.com>
0005  *
0006  *  SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #ifndef KIS_KEYFRAME_CHANNEL_H
0010 #define KIS_KEYFRAME_CHANNEL_H
0011 
0012 #include <QVariant>
0013 #include <QDomElement>
0014 #include <kundo2command.h>
0015 
0016 #include "kis_types.h"
0017 #include "KoID.h"
0018 #include "kis_keyframe.h"
0019 #include "kis_default_bounds.h"
0020 #include "kis_default_bounds_node_wrapper.h"
0021 
0022 #include "kritaimage_export.h"
0023 
0024 class KisTimeSpan;
0025 
0026 
0027 /** @brief KisKeyframeChannel stores and manages KisKeyframes.
0028  * Maps units of time to virtual keyframe values.
0029  * This class is a key piece of Krita's animation backend.
0030  * Abstract base class of KisRasterKeyframeChannel, KisScalarKeyframeChannel, etc.
0031  */
0032 class KRITAIMAGE_EXPORT KisKeyframeChannel : public QObject
0033 {
0034     Q_OBJECT
0035 
0036 public:
0037     static const KoID Raster;
0038     static const KoID Opacity;
0039     static const KoID TransformArguments;
0040     static const KoID PositionX;
0041     static const KoID PositionY;
0042     static const KoID ScaleX;
0043     static const KoID ScaleY;
0044     static const KoID ShearX;
0045     static const KoID ShearY;
0046     static const KoID RotationX;
0047     static const KoID RotationY;
0048     static const KoID RotationZ;
0049 
0050     KisKeyframeChannel(const KoID &id, KisDefaultBoundsBaseSP bounds);
0051     KisKeyframeChannel(const KisKeyframeChannel &rhs);
0052     ~KisKeyframeChannel() override;
0053 
0054     /** @brief Add a new keyframe to the channel at the specified time. */
0055     void addKeyframe(int time, KUndo2Command *parentUndoCmd = nullptr);
0056 
0057     /** @brief Insert an existing keyframe into the channel at the specified time. */
0058     virtual void insertKeyframe(int time, KisKeyframeSP keyframe, KUndo2Command *parentUndoCmd = nullptr);
0059 
0060     /** @brief Remove a keyframe from the channel at the specified time. */
0061     virtual void removeKeyframe(int time, KUndo2Command *parentUndoCmd = nullptr);
0062 
0063     // Inter-channel operations..
0064     /** @brief Move a keyframe across channel(s) at the specified times. */
0065     static void moveKeyframe(KisKeyframeChannel *sourceChannel, int sourceTime, KisKeyframeChannel *targetChannel, int targetTime, KUndo2Command* parentUndoCmd = nullptr);
0066 
0067     /** @brief Copy a keyframe across channel(s) at the specified times. */
0068     static void copyKeyframe(const KisKeyframeChannel *sourceChannel, int sourceTime, KisKeyframeChannel *targetChannel, int targetTime, KUndo2Command* parentUndoCmd = nullptr);
0069 
0070     /** @brief Swap two keyframes across channel(s) at the specified times. */
0071     static void swapKeyframes(KisKeyframeChannel *channelA, int timeA, KisKeyframeChannel *channelB, int timeB, KUndo2Command* parentUndoCmd = nullptr);
0072 
0073     // Intra-channel convenience methods..
0074     void moveKeyframe(int sourceTime, int targetTime, KUndo2Command* parentUndoCmd = nullptr) { moveKeyframe(this, sourceTime, this, targetTime, parentUndoCmd); }
0075     void copyKeyframe(int sourceTime, int targetTime, KUndo2Command* parentUndoCmd = nullptr) { copyKeyframe(this, sourceTime, this, targetTime, parentUndoCmd); }
0076     void swapKeyframes(int timeA, int timeB, KUndo2Command* parentUndoCmd = nullptr) { swapKeyframes(this, timeA, this, timeB, parentUndoCmd); }
0077 
0078     // Keyframe methods..
0079     /** @brief Get a keyframe at specified time.
0080      * Used primarily when the value of a given keyframe is needed. */
0081     KisKeyframeSP keyframeAt(int time) const;
0082     KisKeyframeSP activeKeyframeAt(int time) const { return keyframeAt(activeKeyframeTime(time)); }
0083 
0084     // Quick casting convenience templates..
0085     template <class KeyframeType>
0086     QSharedPointer<KeyframeType> keyframeAt(int time) const {
0087         return keyframeAt(time).dynamicCast<KeyframeType>();
0088     }
0089 
0090     template <class KeyframeType>
0091     QSharedPointer<KeyframeType> activeKeyframeAt(int time) const {
0092         return activeKeyframeAt(time).dynamicCast<KeyframeType>();
0093     }
0094 
0095     int keyframeCount() const;
0096 
0097     // Time methods..
0098     /** @brief Get the time of the active keyframe.
0099      * Useful for snapping any time to that of the most recent keyframe.
0100      * @param  time  Input time. When not specified, currentTime() will be used.
0101      */
0102     int activeKeyframeTime(int time) const;
0103     int activeKeyframeTime() const { return activeKeyframeTime(currentTime()); }
0104 
0105     int firstKeyframeTime() const;
0106     int previousKeyframeTime(const int time) const;
0107     int nextKeyframeTime(const int time) const;
0108     int lastKeyframeTime() const;
0109 
0110     /** @brief Get a set of all integer times that map to a keyframe. */
0111     QSet<int> allKeyframeTimes() const;
0112 
0113     QString id() const;
0114     QString name() const;
0115 
0116     void setNode(KisNodeWSP node);
0117     KisNodeWSP node() const;
0118     void setDefaultBounds(KisDefaultBoundsBaseSP bounds);
0119 
0120     /** @brief Calculates a pseudo-unique hash based on
0121      * the relevant internal state of the channel. */
0122     int channelHash() const;
0123 
0124     /** @brief Get the set of frames affected by any changes to the value
0125      * or content of the active keyframe at the given time. */
0126     virtual KisTimeSpan affectedFrames(int time) const;
0127 
0128     /** @brief Get a span of times for which the channel gives identical
0129      * results compared to frame at a given time.
0130      * NOTE: This set may be different than the set of affected frames
0131      * due to interpolation.
0132      */
0133     virtual KisTimeSpan identicalFrames(int time) const;
0134 
0135     /**
0136      * The rect that is affected by a frame at the given time
0137      */
0138     virtual QRect affectedRect(int time) const = 0;
0139 
0140     virtual QDomElement toXML(QDomDocument doc, const QString &layerFilename);
0141     virtual void loadXML(const QDomElement &channelNode);
0142 
0143     static KoID channelIdToKoId(const QString &id);
0144 
0145 Q_SIGNALS:
0146     /** @brief This signal is emitted just AFTER a keyframe was added to the channel. */
0147     void sigAddedKeyframe(const KisKeyframeChannel *channel, int time);
0148 
0149     /** @brief This signal is emitted just BEFORE a keyframe is removed from the channel. */
0150     void sigKeyframeAboutToBeRemoved(const KisKeyframeChannel *channel, int time);
0151 
0152     /** @brief This signal is emitted just AFTER a keyframe is removed from the channel. */
0153     void sigKeyframeHasBeenRemoved(const KisKeyframeChannel *channel, int time);
0154 
0155     /** @brief This signal is emitted just AFTER a non-raster keyframe was changed its value */
0156     void sigKeyframeChanged(const KisKeyframeChannel *channel, int time);
0157 
0158     /** This signal is an aggregate of the following signals:
0159      *      - sigAddedKeyframe
0160      *      - sigKeyframeHasBeenRemoved
0161      *      - sigKeyframeChanged
0162      */
0163     void sigAnyKeyframeChange();
0164 
0165 protected:
0166     typedef QMap<int, KisKeyframeSP> TimeKeyframeMap;
0167     TimeKeyframeMap &keys();
0168     const TimeKeyframeMap &constKeys() const;
0169     int currentTime() const;
0170 
0171     /**
0172      * Actual implementation of the frame removal that does everything except of
0173      * the signal emission (that should be emitted **before** the removal)
0174      */
0175     virtual void removeKeyframeImpl(int time, KUndo2Command *parentUndoCmd);
0176 
0177     /**
0178      * @brief Between Krita 4.1 and 4.4 Krita had a bug which resulted in creating frames
0179      * with negative time stamp. The bug has been fixed, but there might be some files
0180      * still in the wild.
0181      *
0182      * TODO: remove this workaround in Krita 5.0, when no such file are left :)
0183      */
0184     Q_DECL_DEPRECATED void workaroundBrokenFrameTimeBug(int *time);
0185 
0186 private:
0187     struct Private;
0188     QScopedPointer<Private> m_d;
0189 
0190     /** @brief Virtual keyframe creation function.
0191      * Derived classes implement this function based on the needs
0192      * of their specific KisKeyframe subclasses.
0193      */
0194     virtual KisKeyframeSP createKeyframe() = 0;
0195     virtual QPair<int, KisKeyframeSP> loadKeyframe(const QDomElement &keyframeNode) = 0;
0196     virtual void saveKeyframe(KisKeyframeSP keyframe, QDomElement keyframeElement, const QString &layerFilename) = 0;
0197 };
0198 
0199 #endif // KIS_KEYFRAME_CHANNEL_H