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

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_regenerate_frame_stroke_strategy.h"
0008 
0009 #include <KisRegion.h>
0010 #include "kis_image_interfaces.h"
0011 #include "kis_image_animation_interface.h"
0012 #include "kis_node.h"
0013 #include "kis_image.h"
0014 #include "krita_utils.h"
0015 
0016 #include "kis_full_refresh_walker.h"
0017 #include "kis_async_merger.h"
0018 #include "kis_projection_updates_filter.h"
0019 
0020 
0021 struct KisRegenerateFrameStrokeStrategy::Private
0022 {
0023     Type type;
0024     int frameId;
0025     int previousFrameId;
0026     KisRegion dirtyRegion;
0027     KisImageAnimationInterface *interface;
0028     QStack<KisProjectionUpdatesFilterSP> prevUpdatesFilters;
0029 
0030     class Data : public KisStrokeJobData {
0031     public:
0032         Data(KisNodeSP _root, const QRect &_rect, const QRect &_cropRect)
0033             : KisStrokeJobData(CONCURRENT),
0034               root(_root), rect(_rect), cropRect(_cropRect)
0035             {}
0036 
0037         KisStrokeJobData* createLodClone(int levelOfDetail) override {
0038             Q_UNUSED(levelOfDetail);
0039             return new KisStrokeJobData(CONCURRENT);
0040         }
0041 
0042         KisNodeSP root;
0043         QRect rect;
0044         QRect cropRect;
0045     };
0046 
0047     void saveAndResetUpdatesFilter() {
0048         KisImageSP image = interface->image().toStrongRef();
0049         if (!image) {
0050             return;
0051         }
0052 
0053         while (KisProjectionUpdatesFilterCookie cookie = image->currentProjectionUpdatesFilter()) {
0054             prevUpdatesFilters.push(image->removeProjectionUpdatesFilter(cookie));
0055         }
0056     }
0057 
0058     void restoreUpdatesFilter() {
0059         KisImageSP image = interface->image().toStrongRef();
0060         if (!image) {
0061             return;
0062         }
0063 
0064         while (!prevUpdatesFilters.isEmpty()) {
0065             image->addProjectionUpdatesFilter(prevUpdatesFilters.pop());
0066         }
0067     }
0068 };
0069 
0070 KisRegenerateFrameStrokeStrategy::KisRegenerateFrameStrokeStrategy(int frameId,
0071                                                                    const KisRegion &dirtyRegion,
0072                                                                    bool isCancellable,
0073                                                                    KisImageAnimationInterface *interface)
0074     : KisSimpleStrokeStrategy(QLatin1String("regenerate_external_frame_stroke")),
0075       m_d(new Private)
0076 {
0077     m_d->type = EXTERNAL_FRAME;
0078 
0079     m_d->frameId = frameId;
0080     m_d->dirtyRegion = dirtyRegion;
0081     m_d->interface = interface;
0082 
0083     enableJob(JOB_INIT);
0084     enableJob(JOB_FINISH, true, KisStrokeJobData::BARRIER);
0085     enableJob(JOB_CANCEL, true, KisStrokeJobData::BARRIER);
0086 
0087     enableJob(JOB_DOSTROKE);
0088 
0089     enableJob(JOB_SUSPEND);
0090     enableJob(JOB_RESUME);
0091 
0092     setRequestsOtherStrokesToEnd(false);
0093     setClearsRedoOnStart(false);
0094     setCanForgetAboutMe(isCancellable);
0095 }
0096 
0097 KisRegenerateFrameStrokeStrategy::KisRegenerateFrameStrokeStrategy(KisImageAnimationInterface *interface)
0098     : KisSimpleStrokeStrategy(QLatin1String("regenerate_current_frame_stroke"), kundo2_i18n("Render Animation")),
0099       m_d(new Private)
0100 {
0101     m_d->type = CURRENT_FRAME;
0102 
0103     m_d->frameId = 0;
0104     m_d->dirtyRegion = KisRegion();
0105     m_d->interface = interface;
0106 
0107     enableJob(JOB_INIT);
0108     enableJob(JOB_FINISH, true, KisStrokeJobData::BARRIER);
0109     enableJob(JOB_CANCEL, true, KisStrokeJobData::BARRIER);
0110 
0111     enableJob(JOB_SUSPEND);
0112     enableJob(JOB_RESUME);
0113 
0114     // switching frames is a distinct user action, so it should
0115     // cancel the playback or any action easily
0116     setRequestsOtherStrokesToEnd(true);
0117     setClearsRedoOnStart(false);
0118 }
0119 
0120 KisRegenerateFrameStrokeStrategy::~KisRegenerateFrameStrokeStrategy()
0121 {
0122 }
0123 
0124 void KisRegenerateFrameStrokeStrategy::initStrokeCallback()
0125 {
0126     KisImageSP image = m_d->interface->image().toStrongRef();
0127     if (!image) {
0128         return;
0129     }
0130     if (m_d->type == EXTERNAL_FRAME) {
0131         m_d->saveAndResetUpdatesFilter();
0132         image->disableUIUpdates();
0133         m_d->interface->saveAndResetCurrentTime(m_d->frameId, &m_d->previousFrameId);
0134     } else if (m_d->type == CURRENT_FRAME) {
0135         m_d->interface->blockFrameInvalidation(true);
0136         m_d->interface->updatesFacade()->refreshGraphAsync(KisNodeSP());
0137     }
0138 }
0139 
0140 void KisRegenerateFrameStrokeStrategy::doStrokeCallback(KisStrokeJobData *data)
0141 {
0142     Private::Data *d = dynamic_cast<Private::Data*>(data);
0143     KIS_ASSERT(d);
0144     KIS_ASSERT(!m_d->dirtyRegion.isEmpty());
0145     KIS_ASSERT(m_d->type == EXTERNAL_FRAME);
0146 
0147     const bool skipNonRenderableNodes = m_d->type == EXTERNAL_FRAME;
0148     KisBaseRectsWalkerSP walker = new KisFullRefreshWalker(d->cropRect,
0149                                                            skipNonRenderableNodes ? KisFullRefreshWalker::SkipNonRenderableNodes : KisFullRefreshWalker::None);
0150     walker->collectRects(d->root, d->rect);
0151 
0152     KisAsyncMerger merger;
0153     merger.startMerge(*walker);
0154 }
0155 
0156 void KisRegenerateFrameStrokeStrategy::finishStrokeCallback()
0157 {
0158     KisImageSP image = m_d->interface->image().toStrongRef();
0159     if (!image) {
0160         return;
0161     }
0162     if (m_d->type == EXTERNAL_FRAME) {
0163         m_d->interface->notifyFrameReady();
0164         m_d->interface->restoreCurrentTime(&m_d->previousFrameId);
0165         image->enableUIUpdates();
0166         m_d->restoreUpdatesFilter();
0167     } else if (m_d->type == CURRENT_FRAME) {
0168         m_d->interface->blockFrameInvalidation(false);
0169     }
0170 }
0171 
0172 void KisRegenerateFrameStrokeStrategy::cancelStrokeCallback()
0173 {
0174     KisImageSP image = m_d->interface->image().toStrongRef();
0175     if (!image) {
0176         return;
0177     }
0178     if (m_d->type == EXTERNAL_FRAME) {
0179         m_d->interface->notifyFrameCancelled();
0180         m_d->interface->restoreCurrentTime(&m_d->previousFrameId);
0181         image->enableUIUpdates();
0182         m_d->restoreUpdatesFilter();
0183     } else if (m_d->type == CURRENT_FRAME) {
0184         m_d->interface->blockFrameInvalidation(false);
0185     }
0186 }
0187 
0188 KisStrokeStrategy* KisRegenerateFrameStrokeStrategy::createLodClone(int levelOfDetail)
0189 {
0190     Q_UNUSED(levelOfDetail);
0191 
0192     /**
0193      * We need to regenerate animation frames on LodN level only if
0194      * we are processing current frame. Return dummy stroke otherwise
0195      */
0196     return m_d->type == CURRENT_FRAME ?
0197         new KisRegenerateFrameStrokeStrategy(m_d->interface) :
0198         new KisSimpleStrokeStrategy(QLatin1String("dumb-lodn-KisRegenerateFrameStrokeStrategy"));
0199 }
0200 
0201 void KisRegenerateFrameStrokeStrategy::suspendStrokeCallback()
0202 {
0203     KisImageSP image = m_d->interface->image().toStrongRef();
0204     if (!image) {
0205         return;
0206     }
0207     if (m_d->type == EXTERNAL_FRAME) {
0208         m_d->interface->restoreCurrentTime(&m_d->previousFrameId);
0209         image->enableUIUpdates();
0210         m_d->restoreUpdatesFilter();
0211     } else if (m_d->type == CURRENT_FRAME) {
0212         m_d->interface->blockFrameInvalidation(false);
0213     }
0214 }
0215 
0216 void KisRegenerateFrameStrokeStrategy::resumeStrokeCallback()
0217 {
0218     KisImageSP image = m_d->interface->image().toStrongRef();
0219     if (!image) {
0220         return;
0221     }
0222     if (m_d->type == EXTERNAL_FRAME) {
0223         m_d->saveAndResetUpdatesFilter();
0224         image->disableUIUpdates();
0225         m_d->interface->saveAndResetCurrentTime(m_d->frameId, &m_d->previousFrameId);
0226     } else if (m_d->type == CURRENT_FRAME) {
0227         m_d->interface->blockFrameInvalidation(true);
0228     }
0229 }
0230 
0231 QList<KisStrokeJobData*> KisRegenerateFrameStrokeStrategy::createJobsData(KisImageWSP _image)
0232 {
0233     using KritaUtils::splitRectIntoPatches;
0234     using KritaUtils::optimalPatchSize;
0235     KisImageSP image = _image;
0236 
0237     const QRect cropRect = image->bounds();
0238     QVector<QRect> rects = splitRectIntoPatches(image->bounds(), optimalPatchSize());
0239     QList<KisStrokeJobData*> jobsData;
0240 
0241     Q_FOREACH (const QRect &rc, rects) {
0242         jobsData << new Private::Data(image->root(), rc, cropRect);
0243     }
0244 
0245     return jobsData;
0246 }