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 }