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 &regionOfInterest, 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