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

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_switch_time_stroke_strategy.h"
0008 
0009 #include <QMutex>
0010 
0011 #include "kis_image_animation_interface.h"
0012 #include "kis_post_execution_undo_adapter.h"
0013 #include "commands_new/kis_switch_current_time_command.h"
0014 
0015 
0016 struct KisSwitchTimeStrokeStrategy::Private
0017 {
0018     Private(int frameId, bool needsRegeneration)
0019         : token(new SharedToken(frameId, needsRegeneration))
0020     {
0021     }
0022 
0023     KisImageAnimationInterface *interface {nullptr};
0024     KisPostExecutionUndoAdapter *undoAdapter {nullptr};
0025     SharedTokenSP token;
0026 };
0027 
0028 KisSwitchTimeStrokeStrategy::KisSwitchTimeStrokeStrategy(int frameId,
0029                                                          bool needsRegeneration,
0030                                                          KisImageAnimationInterface *interface,
0031                                                          KisPostExecutionUndoAdapter *undoAdapter)
0032     : KisSimpleStrokeStrategy(QLatin1String("switch_current_frame_stroke"), kundo2_i18n("Switch Frames")),
0033       m_d(new Private(frameId, needsRegeneration))
0034 {
0035     m_d->interface = interface;
0036     m_d->undoAdapter = undoAdapter;
0037 
0038 
0039     enableJob(JOB_INIT, true, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE);
0040 
0041     // switching frames is a distinct user action, so it should
0042     // cancel the playback or any action easily
0043     setRequestsOtherStrokesToEnd(true);
0044     setClearsRedoOnStart(false);
0045 }
0046 
0047 KisSwitchTimeStrokeStrategy::~KisSwitchTimeStrokeStrategy()
0048 {
0049 }
0050 void KisSwitchTimeStrokeStrategy::initStrokeCallback()
0051 {
0052     const int frameId = m_d->token->fetchTime();
0053 
0054     if (frameId == m_d->interface->currentTime()) return;
0055 
0056     const int oldTime = m_d->interface->currentTime();
0057     m_d->interface->explicitlySetCurrentTime(frameId);
0058 
0059     if (m_d->undoAdapter) {
0060         KUndo2CommandSP cmd(
0061             new KisSwitchCurrentTimeCommand(m_d->interface,
0062                                             oldTime,
0063                                             frameId));
0064         m_d->undoAdapter->addCommand(cmd);
0065     }
0066 }
0067 KisStrokeStrategy* KisSwitchTimeStrokeStrategy::createLodClone(int levelOfDetail)
0068 {
0069     Q_UNUSED(levelOfDetail);
0070 
0071     // This stroke is explicitly made LEGACY, so that it could create a barrier for
0072     // time switch. Theoretically, we can have separate time ids for Lod0 and LodN,
0073     // but currently this idea doesn't wotk for some reason. The consequences of
0074     // making this stroke a barrier:
0075     //
0076     // * LodSync stroke is started after every time switch (not slow, but still...)
0077     // * The frame switches only *after* all the pending strokes are finished
0078     // * LodSync stroke is started even when the image is not changed (not regeneration needed)!
0079 
0080     return 0;
0081 }
0082 
0083 KisSwitchTimeStrokeStrategy::SharedTokenSP KisSwitchTimeStrokeStrategy::token() const
0084 {
0085     return m_d->token;
0086 }
0087 
0088 
0089 /*****************************************************************/
0090 /*        KisSwitchTimeStrokeStrategy::SharedToken               */
0091 /*****************************************************************/
0092 
0093 struct KisSwitchTimeStrokeStrategy::SharedToken::Private {
0094     Private(int _time, bool _needsRegeneration)
0095         : time(_time),
0096           needsRegeneration(_needsRegeneration),
0097           isCompleted(false)
0098     {
0099     }
0100 
0101     QMutex mutex;
0102     int time;
0103     bool needsRegeneration;
0104     bool isCompleted;
0105 };
0106 
0107 KisSwitchTimeStrokeStrategy::SharedToken::SharedToken(int initialTime, bool needsRegeneration)
0108     : m_d(new Private(initialTime, needsRegeneration))
0109 {
0110 }
0111 
0112 KisSwitchTimeStrokeStrategy::SharedToken::~SharedToken()
0113 {
0114 }
0115 
0116 bool KisSwitchTimeStrokeStrategy::SharedToken::tryResetDestinationTime(int time, bool needsRegeneration)
0117 {
0118     QMutexLocker l(&m_d->mutex);
0119 
0120     const bool result =
0121         !m_d->isCompleted &&
0122         (m_d->needsRegeneration || !needsRegeneration);
0123 
0124     if (result) {
0125         m_d->time = time;
0126     }
0127 
0128     return result;
0129 }
0130 
0131 int KisSwitchTimeStrokeStrategy::SharedToken::fetchTime() const
0132 {
0133     QMutexLocker l(&m_d->mutex);
0134     KIS_SAFE_ASSERT_RECOVER_NOOP(!m_d->isCompleted);
0135 
0136     m_d->isCompleted = true;
0137     return m_d->time;
0138 }