File indexing completed on 2024-05-12 15:58:39

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 #include "kis_raster_keyframe_channel.h"
0009 #include "kis_node.h"
0010 #include "kis_dom_utils.h"
0011 
0012 #include "kis_global.h"
0013 #include "kis_paint_device.h"
0014 #include "kis_paint_device_frames_interface.h"
0015 #include "kis_time_span.h"
0016 #include "kundo2command.h"
0017 #include "kis_onion_skin_compositor.h"
0018 #include "kis_layer_utils.h"
0019 
0020 KisRasterKeyframe::KisRasterKeyframe(KisPaintDeviceWSP paintDevice)
0021     : KisKeyframe()
0022 {
0023     m_paintDevice = paintDevice;
0024     KIS_ASSERT(m_paintDevice);
0025 
0026     m_frameID = m_paintDevice->framesInterface()->createFrame(false, 0, QPoint(), nullptr);
0027 }
0028 
0029 KisRasterKeyframe::KisRasterKeyframe(KisPaintDeviceWSP paintDevice, const int &premadeFrameID, const int &colorLabelId)
0030     : KisKeyframe()
0031 {
0032     m_paintDevice = paintDevice;
0033     m_frameID = premadeFrameID;
0034     setColorLabel(colorLabelId);
0035 
0036     KIS_ASSERT(m_paintDevice);
0037 }
0038 
0039 KisRasterKeyframe::~KisRasterKeyframe()
0040 {
0041     // Note: Because keyframe ownership is shared, it's possible for them to outlive
0042     // the paint device.
0043     if (m_paintDevice && m_paintDevice->framesInterface()) {
0044         m_paintDevice->framesInterface()->deleteFrame(m_frameID, nullptr);
0045     }
0046 }
0047 
0048 int KisRasterKeyframe::frameID() const
0049 {
0050     return m_frameID;
0051 }
0052 
0053 QRect KisRasterKeyframe::contentBounds()
0054 {
0055     if (!m_paintDevice) {
0056         return QRect();
0057     }
0058 
0059     return m_paintDevice->framesInterface()->frameBounds(m_frameID);
0060 }
0061 
0062 bool KisRasterKeyframe::hasContent()
0063 {
0064     return !m_paintDevice->framesInterface()->frameBounds(m_frameID).isEmpty();
0065 }
0066 
0067 void KisRasterKeyframe::writeFrameToDevice(KisPaintDeviceSP writeTarget)
0068 {
0069     KIS_SAFE_ASSERT_RECOVER_RETURN(m_paintDevice);
0070 
0071     m_paintDevice->framesInterface()->writeFrameToDevice(m_frameID, writeTarget);
0072 }
0073 
0074 KisKeyframeSP KisRasterKeyframe::duplicate(KisKeyframeChannel *newChannel)
0075 {
0076     if (newChannel) {
0077         KisRasterKeyframeChannel* rasterChannel = dynamic_cast<KisRasterKeyframeChannel*>(newChannel);
0078         KIS_ASSERT(rasterChannel);
0079         KisPaintDeviceWSP targetDevice = rasterChannel->paintDevice();
0080 
0081         if (targetDevice != m_paintDevice) {
0082             int targetFrameID = targetDevice->framesInterface()->createFrame(false, 0, QPoint(), nullptr);
0083             targetDevice->framesInterface()->uploadFrame(m_frameID, targetFrameID, m_paintDevice);
0084             KisKeyframeSP key = toQShared(new KisRasterKeyframe(targetDevice, targetFrameID ));
0085             key->setColorLabel(colorLabel());
0086             return key;
0087         }
0088     }
0089 
0090     int copyFrameID = m_paintDevice->framesInterface()->createFrame(true, m_frameID, QPoint(), nullptr);
0091     KisKeyframeSP key = toQShared(new KisRasterKeyframe(m_paintDevice, copyFrameID));
0092     key->setColorLabel(colorLabel());
0093     return key;
0094 }
0095 
0096 
0097 // ===========================================================================================================
0098 // =======================================KisRasterKeyframeChannel============================================
0099 // ===========================================================================================================
0100 
0101 
0102 struct KisRasterKeyframeChannel::Private
0103 {
0104     Private(KisPaintDeviceWSP paintDevice, const QString filenameSuffix)
0105         : paintDevice(paintDevice),
0106           filenameSuffix(filenameSuffix),
0107           onionSkinsEnabled(false)
0108     {}
0109 
0110     /** @brief Weak pointer to the KisPaintDevice associated with this
0111      * channel and a single layer of a KisImage. While the channel maintains
0112      * "virtual" KisRasterKeyframes, the real "physical" frame images are stored
0113      * within this paint device at the frameID index held in the KisRasterKeyframe. */
0114     KisPaintDeviceWSP paintDevice;
0115 
0116     QMultiHash<int, int> frameIDTimesMap;
0117 
0118     QMap<int, QString> frameFilenames;
0119     QString filenameSuffix;
0120     bool onionSkinsEnabled;
0121 };
0122 
0123 KisRasterKeyframeChannel::KisRasterKeyframeChannel(const KoID &id, const KisPaintDeviceWSP paintDevice, const KisDefaultBoundsBaseSP bounds)
0124     : KisKeyframeChannel(id, bounds),
0125       m_d(new Private(paintDevice, QString()))
0126 {
0127 }
0128 
0129 KisRasterKeyframeChannel::KisRasterKeyframeChannel(const KisRasterKeyframeChannel &rhs, const KisPaintDeviceWSP newPaintDevice)
0130     : KisKeyframeChannel(rhs),
0131       m_d(new Private(newPaintDevice, rhs.m_d->filenameSuffix))
0132 {
0133     KIS_ASSERT_RECOVER_NOOP(&rhs != this);
0134 
0135     m_d->frameFilenames = rhs.m_d->frameFilenames;
0136     m_d->onionSkinsEnabled = rhs.m_d->onionSkinsEnabled;
0137 
0138     // Copy keyframes with attention to clones..
0139     foreach (const int& frame, rhs.constKeys().keys()) {
0140         KisRasterKeyframeSP copySource = rhs.keyframeAt<KisRasterKeyframe>(frame);
0141         if (m_d->frameIDTimesMap.contains(copySource->frameID())){
0142             continue;
0143         }
0144 
0145         KisRasterKeyframeSP transferredKey = toQShared(new KisRasterKeyframe(newPaintDevice, copySource->frameID(), copySource->colorLabel()));
0146         foreach (const int& time, rhs.m_d->frameIDTimesMap.values(transferredKey->frameID())) {
0147             keys().insert(time, transferredKey);
0148             m_d->frameIDTimesMap.insert(transferredKey->frameID(), time);
0149         }
0150     }
0151 }
0152 
0153 KisRasterKeyframeChannel::~KisRasterKeyframeChannel()
0154 {
0155 }
0156 
0157 void KisRasterKeyframeChannel::writeToDevice(int time, KisPaintDeviceSP targetDevice)
0158 {
0159     KisRasterKeyframeSP key = keyframeAt<KisRasterKeyframe>(time);
0160     if (!key) {
0161         key = activeKeyframeAt<KisRasterKeyframe>(time);
0162     }
0163 
0164     key->writeFrameToDevice(targetDevice);
0165 }
0166 
0167 void KisRasterKeyframeChannel::importFrame(int time, KisPaintDeviceSP sourceDevice, KUndo2Command *parentCommand)
0168 {
0169     addKeyframe(time, parentCommand);
0170     KisRasterKeyframeSP keyframe = keyframeAt<KisRasterKeyframe>(time);
0171     m_d->paintDevice->framesInterface()->uploadFrame(keyframe->frameID(), sourceDevice);
0172 }
0173 
0174 QRect KisRasterKeyframeChannel::frameExtents(KisKeyframeSP keyframe)
0175 {
0176     return m_d->paintDevice->framesInterface()->frameBounds(keyframe.dynamicCast<KisRasterKeyframe>()->frameID());
0177 }
0178 
0179 QString KisRasterKeyframeChannel::frameFilename(int frameId) const
0180 {
0181     return m_d->frameFilenames.value(frameId, QString());
0182 }
0183 
0184 void KisRasterKeyframeChannel::setFilenameSuffix(const QString &suffix)
0185 {
0186     m_d->filenameSuffix = suffix;
0187 }
0188 
0189 void KisRasterKeyframeChannel::setFrameFilename(int frameId, const QString &filename)
0190 {
0191     Q_ASSERT(!m_d->frameFilenames.contains(frameId));
0192     m_d->frameFilenames.insert(frameId, filename);
0193 }
0194 
0195 QString KisRasterKeyframeChannel::chooseFrameFilename(int frameId, const QString &layerFilename)
0196 {
0197     QString filename;
0198 
0199     if (m_d->frameFilenames.isEmpty()) {
0200         // Use legacy naming convention for first keyframe
0201         filename = layerFilename + m_d->filenameSuffix;
0202     } else {
0203         filename = layerFilename + m_d->filenameSuffix + ".f" + QString::number(frameId);
0204     }
0205 
0206     setFrameFilename(frameId, filename);
0207 
0208     return filename;
0209 }
0210 
0211 QDomElement KisRasterKeyframeChannel::toXML(QDomDocument doc, const QString &layerFilename)
0212 {
0213     m_d->frameFilenames.clear();
0214 
0215     return KisKeyframeChannel::toXML(doc, layerFilename);
0216 }
0217 
0218 void KisRasterKeyframeChannel::loadXML(const QDomElement &channelNode)
0219 {
0220     m_d->frameFilenames.clear();
0221 
0222     KisKeyframeChannel::loadXML(channelNode);
0223 }
0224 
0225 void KisRasterKeyframeChannel::setOnionSkinsEnabled(bool value)
0226 {
0227     m_d->onionSkinsEnabled = value;
0228 }
0229 
0230 bool KisRasterKeyframeChannel::onionSkinsEnabled() const
0231 {
0232     return m_d->onionSkinsEnabled;
0233 }
0234 
0235 KisPaintDeviceWSP KisRasterKeyframeChannel::paintDevice()
0236 {
0237     return m_d->paintDevice;
0238 }
0239 
0240 void KisRasterKeyframeChannel::insertKeyframe(int time, KisKeyframeSP keyframe, KUndo2Command *parentUndoCmd)
0241 {
0242     KisKeyframeChannel::insertKeyframe(time, keyframe, parentUndoCmd);
0243 
0244     KisRasterKeyframeSP rasterKey = keyframe.dynamicCast<KisRasterKeyframe>();
0245     if (rasterKey) {
0246         m_d->frameIDTimesMap.insert(rasterKey->frameID(), time);
0247     }
0248 }
0249 
0250 void KisRasterKeyframeChannel::removeKeyframe(int time, KUndo2Command *parentUndoCmd)
0251 {
0252     KisRasterKeyframeSP rasterKey = keyframeAt<KisRasterKeyframe>(time);
0253     if (rasterKey) {
0254         m_d->frameIDTimesMap.remove(rasterKey->frameID(), time);
0255     }
0256 
0257     KisKeyframeChannel::removeKeyframe(time, parentUndoCmd);
0258 
0259     if (time == 0) { // There should always be a raster frame on frame 0.
0260         addKeyframe(time, parentUndoCmd);
0261     }
0262 }
0263 
0264 void KisRasterKeyframeChannel::cloneKeyframe(int source, int destination, KUndo2Command *parentUndoCmd)
0265 {
0266     if (!keyframeAt(source)) return;
0267 
0268     insertKeyframe(destination, keyframeAt<KisRasterKeyframe>(source), parentUndoCmd);
0269 }
0270 
0271 bool KisRasterKeyframeChannel::areClones(int timeA, int timeB)
0272 {
0273     /* Edgecase
0274      * If both times are empty, we shouldn't really consider the two "clones".. */
0275     if (keyframeAt(timeA) == nullptr && keyframeAt(timeB) == nullptr) {
0276         return false;
0277     }
0278 
0279     return (keyframeAt(timeA) == keyframeAt(timeB));
0280 }
0281 
0282 QSet<int> KisRasterKeyframeChannel::clonesOf(int time)
0283 {
0284     KisRasterKeyframeSP rasterKey = keyframeAt<KisRasterKeyframe>(time);
0285 
0286     if (!rasterKey) {
0287         return QSet<int>();
0288     }
0289 
0290     QList<int> values = m_d->frameIDTimesMap.values(rasterKey->frameID());
0291 #if QT_VERSION >= QT_VERSION_CHECK(5,14,0)
0292     QSet<int> clones = QSet<int>(values.cbegin(), values.cend());
0293 #else
0294     QSet<int> clones = QSet<int>::fromList(values);
0295 #endif
0296     clones.remove(time); // Clones only! Remove input time from the list.
0297     return clones;
0298 }
0299 
0300 QSet<int> KisRasterKeyframeChannel::timesForFrameID(int frameID) const
0301 {
0302     QSet<int> clones;
0303     if (m_d->frameIDTimesMap.contains(frameID)) {
0304         QList<int> values = m_d->frameIDTimesMap.values(frameID);
0305         #if QT_VERSION >= QT_VERSION_CHECK(5,14,0)
0306         clones = QSet<int>(values.cbegin(), values.cend());
0307         #else
0308         clones = QSet<int>::fromList(values);
0309         #endif
0310     }
0311     return clones;
0312 }
0313 
0314 QSet<int> KisRasterKeyframeChannel::clonesOf(const KisNode *node, int time)
0315 {
0316     QSet<int> clones;
0317 
0318     QMap<QString, KisKeyframeChannel*> chans = node->keyframeChannels();
0319     foreach (KisKeyframeChannel* channel, chans.values()){
0320         KisRasterKeyframeChannel* rasterChan = dynamic_cast<KisRasterKeyframeChannel*>(channel);
0321         if (!rasterChan) {
0322             continue;
0323         }
0324 
0325         QSet<int> chanClones = rasterChan->clonesOf(rasterChan->activeKeyframeTime(time));
0326         clones += chanClones;
0327     }
0328 
0329     return clones;
0330 }
0331 
0332 void KisRasterKeyframeChannel::makeUnique(int time, KUndo2Command* parentUndoCmd)
0333 {
0334     KisRasterKeyframeSP rasterKey = keyframeAt<KisRasterKeyframe>(time);
0335 
0336     if (rasterKey && clonesOf(time).count() > 0) {
0337         insertKeyframe(time, rasterKey->duplicate(), parentUndoCmd);
0338     }
0339 }
0340 
0341 QRect KisRasterKeyframeChannel::affectedRect(int time) const
0342 {
0343     QRect affectedRect;
0344 
0345     QList<KisRasterKeyframeSP> relevantFrames;
0346     relevantFrames.append(keyframeAt<KisRasterKeyframe>(time));
0347     relevantFrames.append(keyframeAt<KisRasterKeyframe>(previousKeyframeTime(time)));
0348 
0349     Q_FOREACH (KisRasterKeyframeSP frame, relevantFrames) {
0350         if (frame) {
0351             affectedRect |= frame->contentBounds();
0352         }
0353     }
0354 
0355     return affectedRect;
0356 }
0357 
0358 void KisRasterKeyframeChannel::saveKeyframe(KisKeyframeSP keyframe, QDomElement keyframeElement, const QString &layerFilename)
0359 {
0360     KisRasterKeyframeSP rasterKeyframe = keyframe.dynamicCast<KisRasterKeyframe>();
0361     KIS_SAFE_ASSERT_RECOVER_RETURN(rasterKeyframe);
0362 
0363     int frame = rasterKeyframe->frameID();
0364 
0365     QString filename = frameFilename(frame);
0366     if (filename.isEmpty()) {
0367         filename = chooseFrameFilename(frame, layerFilename);
0368     }
0369     keyframeElement.setAttribute("frame", filename);
0370 
0371     QPoint offset = m_d->paintDevice->framesInterface()->frameOffset(frame);
0372     KisDomUtils::saveValue(&keyframeElement, "offset", offset);
0373 }
0374 
0375 QPair<int, KisKeyframeSP> KisRasterKeyframeChannel::loadKeyframe(const QDomElement &keyframeNode)
0376 {
0377     int time = keyframeNode.attribute("time").toInt();
0378     workaroundBrokenFrameTimeBug(&time);
0379 
0380     KisRasterKeyframeSP keyframe;
0381 
0382     QPoint offset;
0383     KisDomUtils::loadValue(keyframeNode, "offset", &offset);
0384     QString frameFilename = keyframeNode.attribute("frame");
0385 
0386     if (m_d->frameFilenames.isEmpty()) {
0387 
0388         // First keyframe loaded: use the existing frame
0389         KIS_SAFE_ASSERT_RECOVER_NOOP(keyframeCount() == 1);
0390         int firstKeyframeTime = constKeys().begin().key();
0391         keyframe = keyframeAt<KisRasterKeyframe>(firstKeyframeTime);
0392 
0393         // Remove from keys. It will get reinserted with new time once we return
0394         removeKeyframe(firstKeyframeTime);
0395         m_d->paintDevice->framesInterface()->setFrameOffset(keyframe->frameID(), offset);
0396     } else {
0397 
0398         // If the filename already exists, it's **probably** a clone we can reinstance.
0399         if (m_d->frameFilenames.values().contains(frameFilename)) {
0400 
0401             const int frameId = m_d->frameFilenames.key(frameFilename);
0402             const int cloneOf = m_d->frameIDTimesMap.values(frameId).first();
0403             const KisRasterKeyframeSP instance = keyframeAt<KisRasterKeyframe>(cloneOf);
0404             return QPair<int, KisKeyframeSP>(time, instance);
0405         } else {
0406 
0407             keyframe = toQShared(new KisRasterKeyframe(m_d->paintDevice));
0408             m_d->paintDevice->framesInterface()->setFrameOffset(keyframe->frameID(), offset);
0409         }
0410     }
0411 
0412     setFrameFilename(keyframe->frameID(), frameFilename);
0413 
0414     return QPair<int, KisKeyframeSP>(time, keyframe);
0415 }
0416 
0417 KisKeyframeSP KisRasterKeyframeChannel::createKeyframe()
0418 {
0419     return toQShared(new KisRasterKeyframe(m_d->paintDevice));
0420 }