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

0001 /*
0002  *  SPDX-FileCopyrightText: 2015 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_image_animation_interface.h"
0008 
0009 #include <QFileInfo>
0010 
0011 #include "kis_global.h"
0012 #include "kis_image.h"
0013 #include "kis_regenerate_frame_stroke_strategy.h"
0014 #include "kis_switch_time_stroke_strategy.h"
0015 #include "KoProperties.h"
0016 #include "kis_keyframe_channel.h"
0017 #include "kis_raster_keyframe_channel.h"
0018 #include "kis_time_span.h"
0019 
0020 #include "kis_post_execution_undo_adapter.h"
0021 #include "commands_new/kis_switch_current_time_command.h"
0022 #include "kis_layer_utils.h"
0023 
0024 
0025 struct KisImageAnimationInterface::Private
0026 {
0027     Private()
0028         : image(0)
0029         , externalFrameActive(false)
0030         , frameInvalidationBlocked(false)
0031         , framerate(24)
0032         , cachedLastFrameValue(-1)
0033         , audioChannelMuted(false)
0034         , audioChannelVolume(0.5)
0035         , exportInitialFrameNumber(-1)
0036         , m_currentTime(0)
0037         , m_currentUITime(0)
0038     {
0039     }
0040 
0041     Private(const Private &rhs, KisImage *newImage)
0042         : image(newImage),
0043           externalFrameActive(false),
0044           frameInvalidationBlocked(false),
0045           fullClipRange(rhs.fullClipRange),
0046           playbackRange(rhs.playbackRange),
0047           framerate(rhs.framerate),
0048           cachedLastFrameValue(-1),
0049           audioChannelFileName(rhs.audioChannelFileName),
0050           audioChannelMuted(rhs.audioChannelMuted),
0051           audioChannelVolume(rhs.audioChannelVolume),
0052           exportSequenceFilePath(rhs.exportSequenceFilePath),
0053           exportSequenceBaseName(rhs.exportSequenceBaseName),
0054           exportInitialFrameNumber(rhs.exportInitialFrameNumber),
0055           m_currentTime(rhs.m_currentTime),
0056           m_currentUITime(rhs.m_currentUITime)
0057     {
0058     }
0059 
0060     KisImage *image;
0061     bool externalFrameActive;
0062     bool frameInvalidationBlocked;
0063 
0064     KisTimeSpan fullClipRange;
0065     KisTimeSpan playbackRange;
0066     int framerate;
0067     int cachedLastFrameValue;
0068     QString audioChannelFileName;
0069     bool audioChannelMuted;
0070     qreal audioChannelVolume;
0071 
0072     QSet<int> activeLayerSelectedTimes;
0073 
0074     QString exportSequenceFilePath;
0075     QString exportSequenceBaseName;
0076     int exportInitialFrameNumber;
0077 
0078     KisSwitchTimeStrokeStrategy::SharedTokenWSP switchToken;
0079 
0080     inline int currentTime() const {
0081         return m_currentTime;
0082     }
0083 
0084     inline int currentUITime() const {
0085         return m_currentUITime;
0086     }
0087 
0088     inline void setCurrentTime(int value) {
0089         m_currentTime = value;
0090     }
0091 
0092     inline void setCurrentUITime(int value) {
0093         m_currentUITime = value;
0094     }
0095 private:
0096     int m_currentTime;
0097     int m_currentUITime;
0098 };
0099 
0100 
0101 KisImageAnimationInterface::KisImageAnimationInterface(KisImage *image)
0102     : QObject(image),
0103       m_d(new Private)
0104 {
0105     m_d->image = image;
0106 
0107     m_d->framerate = 24;
0108     m_d->fullClipRange = KisTimeSpan::fromTimeToTime(0, 100);
0109 
0110     connect(this, SIGNAL(sigInternalRequestTimeSwitch(int,bool)), SLOT(switchCurrentTimeAsync(int,bool)));
0111 }
0112 
0113 KisImageAnimationInterface::KisImageAnimationInterface(const KisImageAnimationInterface &rhs, KisImage *newImage)
0114     : m_d(new Private(*rhs.m_d, newImage))
0115 {
0116     connect(this, SIGNAL(sigInternalRequestTimeSwitch(int,bool)), SLOT(switchCurrentTimeAsync(int,bool)));
0117 }
0118 
0119 KisImageAnimationInterface::~KisImageAnimationInterface()
0120 {
0121 }
0122 
0123 bool KisImageAnimationInterface::hasAnimation() const
0124 {
0125     bool hasAnimation = false;
0126 
0127     KisLayerUtils::recursiveApplyNodes(
0128         m_d->image->root(),
0129         [&hasAnimation](KisNodeSP node) {
0130             hasAnimation |= node->isAnimated();
0131         });
0132 
0133     return hasAnimation;
0134 }
0135 
0136 int KisImageAnimationInterface::currentTime() const
0137 {
0138     return m_d->currentTime();
0139 }
0140 
0141 int KisImageAnimationInterface::currentUITime() const
0142 {
0143     return m_d->currentUITime();
0144 }
0145 
0146 const KisTimeSpan& KisImageAnimationInterface::fullClipRange() const
0147 {
0148     return m_d->fullClipRange;
0149 }
0150 
0151 void KisImageAnimationInterface::setFullClipRange(const KisTimeSpan range)
0152 {
0153     KIS_SAFE_ASSERT_RECOVER_RETURN(!range.isInfinite());
0154     m_d->fullClipRange = range;
0155     emit sigFullClipRangeChanged();
0156 }
0157 
0158 void KisImageAnimationInterface::setFullClipRangeStartTime(int column)
0159 {
0160     KisTimeSpan newRange = KisTimeSpan::fromTimeToTime(column,  m_d->fullClipRange.end());
0161     setFullClipRange(newRange);
0162 }
0163 
0164 void KisImageAnimationInterface::setFullClipRangeEndTime(int column)
0165 {
0166     KisTimeSpan newRange = KisTimeSpan::fromTimeToTime(m_d->fullClipRange.start(), column);
0167     setFullClipRange(newRange);
0168 }
0169 
0170 const KisTimeSpan& KisImageAnimationInterface::playbackRange() const
0171 {
0172     return m_d->playbackRange.isValid() ? m_d->playbackRange : m_d->fullClipRange;
0173 }
0174 
0175 void KisImageAnimationInterface::setPlaybackRange(const KisTimeSpan range)
0176 {
0177     KIS_SAFE_ASSERT_RECOVER_RETURN(!range.isInfinite());
0178     m_d->playbackRange = range;
0179     emit sigPlaybackRangeChanged();
0180 }
0181 
0182 int KisImageAnimationInterface::framerate() const
0183 {
0184     return m_d->framerate;
0185 }
0186 
0187 QString KisImageAnimationInterface::audioChannelFileName() const
0188 {
0189     return m_d->audioChannelFileName;
0190 }
0191 
0192 void KisImageAnimationInterface::setAudioChannelFileName(const QString &fileName)
0193 {
0194     QFileInfo info(fileName);
0195 
0196     KIS_SAFE_ASSERT_RECOVER_NOOP(fileName.isEmpty() || info.isAbsolute());
0197     m_d->audioChannelFileName = fileName.isEmpty() ? fileName : info.absoluteFilePath();
0198 
0199     emit sigAudioChannelChanged();
0200 }
0201 
0202 QString KisImageAnimationInterface::exportSequenceFilePath()
0203 {
0204     return m_d->exportSequenceFilePath;
0205 }
0206 
0207 void KisImageAnimationInterface::setExportSequenceFilePath(const QString &filePath)
0208 {
0209     m_d->exportSequenceFilePath = filePath;
0210 }
0211 
0212 QString KisImageAnimationInterface::exportSequenceBaseName()
0213 {
0214     return m_d->exportSequenceBaseName;
0215 }
0216 
0217 void KisImageAnimationInterface::setExportSequenceBaseName(const QString &baseName)
0218 {
0219     m_d->exportSequenceBaseName = baseName;
0220 }
0221 
0222 int KisImageAnimationInterface::exportInitialFrameNumber()
0223 {
0224     return m_d->exportInitialFrameNumber;
0225 }
0226 
0227 void KisImageAnimationInterface::setExportInitialFrameNumber(const int frameNum)
0228 {
0229     m_d->exportInitialFrameNumber = frameNum;
0230 }
0231 
0232 bool KisImageAnimationInterface::isAudioMuted() const
0233 {
0234     return m_d->audioChannelMuted;
0235 }
0236 
0237 void KisImageAnimationInterface::setAudioMuted(bool value)
0238 {
0239     m_d->audioChannelMuted = value;
0240     emit sigAudioChannelChanged();
0241 }
0242 
0243 qreal KisImageAnimationInterface::audioVolume() const
0244 {
0245     return m_d->audioChannelVolume;
0246 }
0247 
0248 void KisImageAnimationInterface::setAudioVolume(qreal value)
0249 {
0250     m_d->audioChannelVolume = value;
0251     emit sigAudioVolumeChanged();
0252 }
0253 
0254 QSet<int> KisImageAnimationInterface::activeLayerSelectedTimes()
0255 {
0256     return m_d->activeLayerSelectedTimes;
0257 }
0258 
0259 void KisImageAnimationInterface::setActiveLayerSelectedTimes(const QSet<int>& times)
0260 {
0261     m_d->activeLayerSelectedTimes = times;
0262 }
0263 
0264 void KisImageAnimationInterface::setFramerate(int fps)
0265 {
0266     if (fps > 0) {
0267         m_d->framerate = fps;
0268         emit sigFramerateChanged();
0269     }
0270 }
0271 
0272 KisImageWSP KisImageAnimationInterface::image() const
0273 {
0274     return m_d->image;
0275 }
0276 
0277 bool KisImageAnimationInterface::externalFrameActive() const
0278 {
0279     return m_d->externalFrameActive;
0280 }
0281 
0282 void KisImageAnimationInterface::requestTimeSwitchWithUndo(int time)
0283 {
0284     requestTimeSwitchNonGUI(time, true);
0285 }
0286 
0287 void KisImageAnimationInterface::setDefaultProjectionColor(const KoColor &color)
0288 {
0289     int savedTime = 0;
0290     saveAndResetCurrentTime(currentTime(), &savedTime);
0291 
0292     m_d->image->setDefaultProjectionColor(color);
0293 
0294     restoreCurrentTime(&savedTime);
0295 }
0296 
0297 void KisImageAnimationInterface::requestTimeSwitchNonGUI(int time, bool useUndo)
0298 {
0299     emit sigInternalRequestTimeSwitch(time, useUndo);
0300 }
0301 
0302 void KisImageAnimationInterface::explicitlySetCurrentTime(int frameId)
0303 {
0304     m_d->setCurrentTime(frameId);
0305 }
0306 
0307 void KisImageAnimationInterface::switchCurrentTimeAsync(int frameId, bool useUndo)
0308 {
0309     const bool sameFrame = currentUITime() == frameId;
0310     const bool needsCompositingUpdate = requiresOnionSkinRendering();
0311     const KisTimeSpan range = KisTimeSpan::calculateIdenticalFramesRecursive(m_d->image->root(), currentUITime());
0312     
0313     const bool needsRegeneration = !range.contains(frameId) || needsCompositingUpdate;
0314 
0315     KisSwitchTimeStrokeStrategy::SharedTokenSP token = m_d->switchToken.toStrongRef();
0316 
0317     // Handle switching frame to new time..    
0318     if (!token || !token->tryResetDestinationTime(frameId, needsRegeneration)) {
0319 
0320         if (!sameFrame) {
0321             KisPostExecutionUndoAdapter *undoAdapter = useUndo ?
0322                 m_d->image->postExecutionUndoAdapter() : 0;
0323 
0324             KisSwitchTimeStrokeStrategy *strategy =
0325                 new KisSwitchTimeStrokeStrategy(frameId, needsRegeneration,
0326                                                 this, undoAdapter);
0327 
0328             m_d->switchToken = strategy->token();
0329 
0330             KisStrokeId stroke = m_d->image->startStroke(strategy);
0331             m_d->image->endStroke(stroke);
0332         }
0333         
0334         if (needsRegeneration) {
0335             KisStrokeStrategy *strategy =
0336                 new KisRegenerateFrameStrokeStrategy(this);
0337 
0338             KisStrokeId strokeId = m_d->image->startStroke(strategy);
0339             m_d->image->endStroke(strokeId);
0340         }
0341     }
0342     
0343     
0344 
0345     m_d->setCurrentUITime(frameId);
0346     emit sigUiTimeChanged(frameId);
0347 }
0348 
0349 void KisImageAnimationInterface::requestFrameRegeneration(int frameId, const KisRegion &dirtyRegion, bool isCancellable)
0350 {
0351     KisStrokeStrategy *strategy =
0352         new KisRegenerateFrameStrokeStrategy(frameId,
0353                                              dirtyRegion,
0354                                              isCancellable,
0355                                              this);
0356 
0357     QList<KisStrokeJobData*> jobs = KisRegenerateFrameStrokeStrategy::createJobsData(m_d->image);
0358 
0359     KisStrokeId stroke = m_d->image->startStroke(strategy);
0360     Q_FOREACH (KisStrokeJobData* job, jobs) {
0361         m_d->image->addJob(stroke, job);
0362     }
0363     m_d->image->endStroke(stroke);
0364 }
0365 
0366 void KisImageAnimationInterface::saveAndResetCurrentTime(int frameId, int *savedValue)
0367 {
0368     m_d->externalFrameActive = true;
0369     *savedValue = m_d->currentTime();
0370     m_d->setCurrentTime(frameId);
0371 }
0372 
0373 void KisImageAnimationInterface::restoreCurrentTime(int *savedValue)
0374 {
0375     m_d->setCurrentTime(*savedValue);
0376     m_d->externalFrameActive = false;
0377 }
0378 
0379 void KisImageAnimationInterface::notifyFrameReady()
0380 {
0381     emit sigFrameReady(m_d->currentTime());
0382 }
0383 
0384 void KisImageAnimationInterface::notifyFrameCancelled()
0385 {
0386     emit sigFrameCancelled();
0387 }
0388 
0389 bool KisImageAnimationInterface::requiresOnionSkinRendering() {
0390     
0391     KisNodeSP onionskinned = KisLayerUtils::recursiveFindNode(m_d->image->root(), [](KisNodeSP p) {
0392         bool onionSkinProp = p->nodeProperties().boolProperty("onionskin", false);
0393         return onionSkinProp;
0394     });
0395     
0396     return onionskinned != nullptr;
0397 }
0398 
0399 KisUpdatesFacade* KisImageAnimationInterface::updatesFacade() const
0400 {
0401     return m_d->image;
0402 }
0403 
0404 void KisImageAnimationInterface::notifyNodeChanged(const KisNode *node,
0405                                                    const QRect &rect,
0406                                                    bool recursive)
0407 {
0408     notifyNodeChanged(node, QVector<QRect>({rect}), recursive);
0409 }
0410 
0411 void KisImageAnimationInterface::notifyNodeChanged(const KisNode *node,
0412                                                    const QVector<QRect> &rects,
0413                                                    bool recursive)
0414 {
0415     if (externalFrameActive() || m_d->frameInvalidationBlocked) return;
0416 
0417     // even overlay selection masks are not rendered in the cache
0418     if (node->inherits("KisSelectionMask")) return;
0419 
0420     QSet<int> affectedTimes;
0421     affectedTimes << m_d->currentTime();
0422 
0423     // We need to also invalidate ranges that contain cloned keyframe data.
0424     if (recursive) {
0425         QSet<int> clonedTimes;
0426         const int time = m_d->currentTime();
0427         KisLayerUtils::recursiveApplyNodes(node, [&clonedTimes, time](const KisNode* node){
0428             clonedTimes += KisRasterKeyframeChannel::clonesOf(node, time);
0429         });
0430 
0431         affectedTimes += clonedTimes;
0432     } else {
0433         affectedTimes += KisRasterKeyframeChannel::clonesOf(node, m_d->currentTime());
0434     }
0435 
0436     foreach (const int& time, affectedTimes ){
0437         KisTimeSpan invalidateRange;
0438 
0439         if (recursive) {
0440             invalidateRange = KisTimeSpan::calculateAffectedFramesRecursive(node, time);
0441         } else {
0442             invalidateRange = KisTimeSpan::calculateNodeAffectedFrames(node, time);
0443         }
0444 
0445         // we compress the updated rect (atm, no one uses it anyway)
0446         QRect unitedRect;
0447         Q_FOREACH (const QRect &rc, rects) {
0448             unitedRect |= rc;
0449         }
0450 
0451         invalidateFrames(invalidateRange, unitedRect);
0452     }
0453 }
0454 
0455 void KisImageAnimationInterface::invalidateFrames(const KisTimeSpan &range, const QRect &rect)
0456 {
0457     m_d->cachedLastFrameValue = -1;
0458     emit sigFramesChanged(range, rect);
0459 }
0460 
0461 void KisImageAnimationInterface::invalidateFrame(const int time, KisNodeSP target)
0462 {
0463     m_d->cachedLastFrameValue = -1;
0464 
0465     emit sigFramesChanged(KisLayerUtils::fetchLayerActiveRasterFrameSpan(target, time), m_d->image->bounds());
0466 
0467     QSet<int> identicalFrames = KisLayerUtils::fetchLayerIdenticalRasterFrameTimes(target, time);
0468     Q_FOREACH(const int& identicalTime, identicalFrames) {
0469         emit sigFramesChanged(KisLayerUtils::fetchLayerActiveRasterFrameSpan(target, identicalTime), m_d->image->bounds());
0470     }
0471 }
0472 
0473 void KisImageAnimationInterface::blockFrameInvalidation(bool value)
0474 {
0475     m_d->frameInvalidationBlocked = value;
0476 }
0477 
0478 int findLastKeyframeTimeRecursive(KisNodeSP node)
0479 {
0480     int time = 0;
0481 
0482     KisKeyframeChannel *channel;
0483     Q_FOREACH (channel, node->keyframeChannels()) {
0484         time = std::max(time, channel->lastKeyframeTime());
0485     }
0486 
0487     KisNodeSP child = node->firstChild();
0488     while (child) {
0489         time = std::max(time, findLastKeyframeTimeRecursive(child));
0490         child = child->nextSibling();
0491     }
0492 
0493     return time;
0494 }
0495 
0496 int KisImageAnimationInterface::totalLength()
0497 {
0498     if (m_d->cachedLastFrameValue < 0) {
0499         m_d->cachedLastFrameValue = findLastKeyframeTimeRecursive(m_d->image->root());
0500     }
0501 
0502     int lastKey = m_d->cachedLastFrameValue;
0503 
0504     lastKey  = std::max(lastKey, m_d->fullClipRange.end());
0505     lastKey  = std::max(lastKey, m_d->currentUITime());
0506 
0507     return lastKey + 1;
0508 }