File indexing completed on 2024-05-19 04:29:09
0001 /* 0002 * SPDX-FileCopyrightText: 2017 Dmitry Kazakov <dimula73@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "KisAsyncAnimationRendererBase.h" 0008 0009 #include <QTimer> 0010 #include <QThread> 0011 0012 #include "KisMpl.h" 0013 #include "kis_image.h" 0014 #include "kis_image_animation_interface.h" 0015 #include "kis_signal_auto_connection.h" 0016 #include "kis_image_config.h" 0017 #include <KisStaticInitializer.h> 0018 0019 KIS_DECLARE_STATIC_INITIALIZER { 0020 qRegisterMetaType<KisAsyncAnimationRendererBase::CancelReason>("KisAsyncAnimationRendererBase::CancelReason"); 0021 } 0022 0023 struct KRITAUI_NO_EXPORT KisAsyncAnimationRendererBase::Private 0024 { 0025 0026 KisSignalAutoConnectionsStore imageRequestConnections; 0027 QTimer regenerationTimeout; 0028 0029 KisImageSP requestedImage; 0030 int requestedFrame = -1; 0031 bool isCancelled = false; 0032 KisRegion requestedRegion; 0033 }; 0034 0035 KisAsyncAnimationRendererBase::KisAsyncAnimationRendererBase(QObject *parent) 0036 : QObject(parent), 0037 m_d(new Private()) 0038 { 0039 connect(&m_d->regenerationTimeout, SIGNAL(timeout()), SLOT(slotFrameRegenerationTimedOut())); 0040 0041 KisImageConfig cfg(true); 0042 0043 m_d->regenerationTimeout.setSingleShot(true); 0044 m_d->regenerationTimeout.setInterval(cfg.frameRenderingTimeout()); 0045 } 0046 0047 KisAsyncAnimationRendererBase::~KisAsyncAnimationRendererBase() 0048 { 0049 0050 } 0051 0052 void KisAsyncAnimationRendererBase::startFrameRegeneration(KisImageSP image, int frame, const KisRegion ®ionOfInterest, Flags flags, KisLockFrameGenerationLock &&frameGenerationLock) 0053 { 0054 KIS_SAFE_ASSERT_RECOVER_NOOP(QThread::currentThread() == this->thread()); 0055 0056 m_d->requestedImage = image; 0057 m_d->requestedFrame = frame; 0058 m_d->isCancelled = false; 0059 m_d->requestedRegion = !regionOfInterest.isEmpty() ? regionOfInterest : image->bounds(); 0060 0061 KisImageAnimationInterface *animation = m_d->requestedImage->animationInterface(); 0062 0063 m_d->imageRequestConnections.clear(); 0064 m_d->imageRequestConnections.addConnection( 0065 animation, SIGNAL(sigFrameReady(int)), 0066 this, SLOT(slotFrameRegenerationFinished(int)), 0067 Qt::DirectConnection); 0068 0069 m_d->imageRequestConnections.addConnection( 0070 animation, SIGNAL(sigFrameCancelled()), 0071 this, SLOT(slotFrameRegenerationCancelled()), 0072 Qt::AutoConnection); 0073 0074 m_d->regenerationTimeout.start(); 0075 animation->requestFrameRegeneration(m_d->requestedFrame, m_d->requestedRegion, flags & Cancellable, std::move(frameGenerationLock)); 0076 } 0077 0078 void KisAsyncAnimationRendererBase::startFrameRegeneration(KisImageSP image, int frame, Flags flags, KisLockFrameGenerationLock &&frameGenerationLock) 0079 { 0080 startFrameRegeneration(image, frame, KisRegion(), flags, std::move(frameGenerationLock)); 0081 } 0082 0083 bool KisAsyncAnimationRendererBase::isActive() const 0084 { 0085 return m_d->requestedImage; 0086 } 0087 0088 void KisAsyncAnimationRendererBase::cancelCurrentFrameRendering(CancelReason cancelReason) 0089 { 0090 KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->requestedImage); 0091 frameCancelledCallback(m_d->requestedFrame, cancelReason); 0092 } 0093 0094 void KisAsyncAnimationRendererBase::slotFrameRegenerationCancelled() 0095 { 0096 // the cancel can arrive in async way 0097 if (!m_d->requestedImage) return; 0098 frameCancelledCallback(m_d->requestedFrame, RenderingFailed); 0099 } 0100 0101 void KisAsyncAnimationRendererBase::slotFrameRegenerationTimedOut() 0102 { 0103 // the timeout can arrive in async way 0104 if (!m_d->requestedImage) return; 0105 frameCancelledCallback(m_d->requestedFrame, RenderingTimedOut); 0106 } 0107 0108 void KisAsyncAnimationRendererBase::slotFrameRegenerationFinished(int frame) 0109 { 0110 // We might have already cancelled the regeneration. We don't check 0111 // isCancelled flag here because this code runs asynchronously. 0112 if (!m_d->requestedImage) return; 0113 0114 // WARNING: executed in the context of image worker thread! 0115 0116 // probably a bit too strict... 0117 KIS_SAFE_ASSERT_RECOVER_NOOP(QThread::currentThread() != this->thread()); 0118 0119 frameCompletedCallback(frame, m_d->requestedRegion); 0120 } 0121 0122 void KisAsyncAnimationRendererBase::notifyFrameCompleted(int frame) 0123 { 0124 KIS_SAFE_ASSERT_RECOVER_NOOP(QThread::currentThread() == this->thread()); 0125 0126 // the image events can come with a delay, even after 0127 // the processing was cancelled 0128 if (m_d->isCancelled) return; 0129 0130 { 0131 // clean state right after the assert checks 0132 auto cleanup = kismpl::finally([&] () { 0133 clearFrameRegenerationState(false); 0134 }); 0135 0136 KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->requestedImage); 0137 KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->requestedFrame == frame); 0138 } 0139 0140 0141 emit sigFrameCompleted(frame); 0142 } 0143 0144 void KisAsyncAnimationRendererBase::notifyFrameCancelled(int frame, CancelReason cancelReason) 0145 { 0146 KIS_SAFE_ASSERT_RECOVER_NOOP(QThread::currentThread() == this->thread()); 0147 0148 // the image events can come with a delay, even after 0149 // the processing was cancelled 0150 if (m_d->isCancelled) return; 0151 0152 { 0153 // clean state right after the assert checks 0154 auto cleanup = kismpl::finally([&] () { 0155 clearFrameRegenerationState(true); 0156 }); 0157 0158 KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->requestedImage); 0159 KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->requestedFrame == frame); 0160 } 0161 0162 emit sigFrameCancelled(frame, cancelReason); 0163 } 0164 0165 void KisAsyncAnimationRendererBase::clearFrameRegenerationState(bool isCancelled) 0166 { 0167 // TODO: for some reason we mark the process as cancelled in any case, and it 0168 // seem to be a correct behavior 0169 Q_UNUSED(isCancelled); 0170 0171 m_d->imageRequestConnections.clear(); 0172 m_d->requestedImage = 0; 0173 m_d->requestedFrame = -1; 0174 m_d->regenerationTimeout.stop(); 0175 m_d->isCancelled = true; 0176 m_d->requestedRegion = KisRegion(); 0177 } 0178 0179 KisImageSP KisAsyncAnimationRendererBase::requestedImage() const 0180 { 0181 return m_d->requestedImage; 0182 } 0183 0184