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

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