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