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