File indexing completed on 2024-05-19 04:26:17
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_image_animation_interface.h" 0008 0009 #include <QFileInfo> 0010 0011 #include "kis_global.h" 0012 #include "kis_image.h" 0013 #include "kis_regenerate_frame_stroke_strategy.h" 0014 #include "kis_switch_time_stroke_strategy.h" 0015 #include "KoProperties.h" 0016 #include "kis_keyframe_channel.h" 0017 #include "kis_raster_keyframe_channel.h" 0018 #include "kis_time_span.h" 0019 0020 #include <KisLockFrameGenerationLock.h> 0021 #include "kis_post_execution_undo_adapter.h" 0022 #include "commands_new/kis_switch_current_time_command.h" 0023 #include "kis_layer_utils.h" 0024 0025 0026 struct KisImageAnimationInterface::Private 0027 { 0028 Private() 0029 : image(0), 0030 externalFrameActive(false), 0031 frameInvalidationBlocked(false), 0032 cachedLastFrameValue(-1), 0033 exportInitialFrameNumber(-1), 0034 m_currentTime(0), 0035 m_currentUITime(0) 0036 { 0037 } 0038 0039 Private(const Private &rhs, KisImage *newImage) 0040 : image(newImage), 0041 externalFrameActive(false), 0042 frameInvalidationBlocked(false), 0043 documentRange(rhs.documentRange), 0044 activePlaybackRange(rhs.activePlaybackRange), 0045 framerate(rhs.framerate), 0046 cachedLastFrameValue(-1), 0047 exportSequenceFilePath(rhs.exportSequenceFilePath), 0048 exportSequenceBaseName(rhs.exportSequenceBaseName), 0049 exportInitialFrameNumber(rhs.exportInitialFrameNumber), 0050 m_currentTime(rhs.m_currentTime), 0051 m_currentUITime(rhs.m_currentUITime) 0052 { 0053 } 0054 0055 KisImage *image; 0056 bool externalFrameActive; 0057 bool frameInvalidationBlocked; 0058 0059 KisTimeSpan documentRange; 0060 KisTimeSpan activePlaybackRange; 0061 int framerate; 0062 int cachedLastFrameValue; 0063 0064 QSet<int> activeLayerSelectedTimes; 0065 0066 QString exportSequenceFilePath; 0067 QString exportSequenceBaseName; 0068 int exportInitialFrameNumber; 0069 0070 KisSwitchTimeStrokeStrategy::SharedTokenWSP switchToken; 0071 0072 QAtomicInt backgroundFrameGenerationBlocked; 0073 QMutex frameGenerationLock; 0074 0075 inline int currentTime() const { 0076 return m_currentTime; 0077 } 0078 0079 inline int currentUITime() const { 0080 return m_currentUITime; 0081 } 0082 0083 inline void setCurrentTime(int value) { 0084 m_currentTime = value; 0085 } 0086 0087 inline void setCurrentUITime(int value) { 0088 m_currentUITime = value; 0089 } 0090 private: 0091 int m_currentTime; 0092 int m_currentUITime; 0093 }; 0094 0095 0096 KisImageAnimationInterface::KisImageAnimationInterface(KisImage *image) 0097 : QObject(image), 0098 m_d(new Private) 0099 { 0100 m_d->image = image; 0101 0102 m_d->framerate = 24; 0103 m_d->documentRange = KisTimeSpan::fromTimeToTime(0, 100); 0104 0105 connect(this, &KisImageAnimationInterface::sigInternalRequestTimeSwitch, this, [this](int frame, bool useUndo) { 0106 this->switchCurrentTimeAsync(frame, useUndo ? STAO_USE_UNDO : STAO_NONE); 0107 }); 0108 } 0109 0110 KisImageAnimationInterface::KisImageAnimationInterface(const KisImageAnimationInterface &rhs, KisImage *newImage) 0111 : m_d(new Private(*rhs.m_d, newImage)) 0112 { 0113 connect(this, &KisImageAnimationInterface::sigInternalRequestTimeSwitch, this, [this](int frame, bool useUndo) { 0114 this->switchCurrentTimeAsync(frame, useUndo ? STAO_USE_UNDO : STAO_NONE); 0115 }); 0116 } 0117 0118 KisImageAnimationInterface::~KisImageAnimationInterface() 0119 { 0120 } 0121 0122 bool KisImageAnimationInterface::hasAnimation() const 0123 { 0124 bool hasAnimation = false; 0125 0126 KisLayerUtils::recursiveApplyNodes( 0127 m_d->image->root(), 0128 [&hasAnimation](KisNodeSP node) { 0129 hasAnimation |= node->isAnimated(); 0130 }); 0131 0132 return hasAnimation; 0133 } 0134 0135 int KisImageAnimationInterface::currentTime() const 0136 { 0137 return m_d->currentTime(); 0138 } 0139 0140 int KisImageAnimationInterface::currentUITime() const 0141 { 0142 return m_d->currentUITime(); 0143 } 0144 0145 const KisTimeSpan& KisImageAnimationInterface::documentPlaybackRange() const 0146 { 0147 return m_d->documentRange; 0148 } 0149 0150 void KisImageAnimationInterface::setDocumentRange(const KisTimeSpan range) 0151 { 0152 KIS_SAFE_ASSERT_RECOVER_RETURN(!range.isInfinite()); 0153 m_d->documentRange = range; 0154 emit sigPlaybackRangeChanged(); 0155 } 0156 0157 void KisImageAnimationInterface::setDocumentRangeStartFrame(int column) 0158 { 0159 KisTimeSpan newRange = KisTimeSpan::fromTimeToTime(column, m_d->documentRange.end()); 0160 setDocumentRange(newRange); 0161 } 0162 0163 void KisImageAnimationInterface::setDocumentRangeEndFrame(int column) 0164 { 0165 KisTimeSpan newRange = KisTimeSpan::fromTimeToTime(m_d->documentRange.start(), column); 0166 setDocumentRange(newRange); 0167 } 0168 0169 const KisTimeSpan& KisImageAnimationInterface::activePlaybackRange() const 0170 { 0171 return m_d->activePlaybackRange.isValid() ? m_d->activePlaybackRange : m_d->documentRange; 0172 } 0173 0174 void KisImageAnimationInterface::setActivePlaybackRange(const KisTimeSpan range) 0175 { 0176 KIS_SAFE_ASSERT_RECOVER_RETURN(!range.isInfinite()); 0177 m_d->activePlaybackRange = range; 0178 emit sigPlaybackRangeChanged(); 0179 } 0180 0181 int KisImageAnimationInterface::framerate() const 0182 { 0183 return m_d->framerate; 0184 } 0185 0186 QString KisImageAnimationInterface::exportSequenceFilePath() 0187 { 0188 return m_d->exportSequenceFilePath; 0189 } 0190 0191 void KisImageAnimationInterface::setExportSequenceFilePath(const QString &filePath) 0192 { 0193 m_d->exportSequenceFilePath = filePath; 0194 } 0195 0196 QString KisImageAnimationInterface::exportSequenceBaseName() 0197 { 0198 return m_d->exportSequenceBaseName; 0199 } 0200 0201 void KisImageAnimationInterface::setExportSequenceBaseName(const QString &baseName) 0202 { 0203 m_d->exportSequenceBaseName = baseName; 0204 } 0205 0206 int KisImageAnimationInterface::exportInitialFrameNumber() 0207 { 0208 return m_d->exportInitialFrameNumber; 0209 } 0210 0211 void KisImageAnimationInterface::setExportInitialFrameNumber(const int frameNum) 0212 { 0213 m_d->exportInitialFrameNumber = frameNum; 0214 } 0215 0216 QSet<int> KisImageAnimationInterface::activeLayerSelectedTimes() 0217 { 0218 return m_d->activeLayerSelectedTimes; 0219 } 0220 0221 void KisImageAnimationInterface::setActiveLayerSelectedTimes(const QSet<int>& times) 0222 { 0223 m_d->activeLayerSelectedTimes = times; 0224 } 0225 0226 void KisImageAnimationInterface::setFramerate(int fps) 0227 { 0228 if (fps > 0) { 0229 m_d->framerate = fps; 0230 emit sigFramerateChanged(); 0231 } 0232 } 0233 0234 KisImageWSP KisImageAnimationInterface::image() const 0235 { 0236 return m_d->image; 0237 } 0238 0239 bool KisImageAnimationInterface::externalFrameActive() const 0240 { 0241 return m_d->externalFrameActive; 0242 } 0243 0244 void KisImageAnimationInterface::requestTimeSwitchWithUndo(int time) 0245 { 0246 requestTimeSwitchNonGUI(time, true); 0247 } 0248 0249 void KisImageAnimationInterface::setDefaultProjectionColor(const KoColor &color) 0250 { 0251 int savedTime = 0; 0252 saveAndResetCurrentTime(currentTime(), &savedTime); 0253 0254 m_d->image->setDefaultProjectionColor(color); 0255 0256 restoreCurrentTime(&savedTime); 0257 } 0258 0259 void KisImageAnimationInterface::requestTimeSwitchNonGUI(int time, bool useUndo) 0260 { 0261 emit sigInternalRequestTimeSwitch(time, useUndo); 0262 } 0263 0264 void KisImageAnimationInterface::explicitlySetCurrentTime(int frameId) 0265 { 0266 m_d->setCurrentTime(frameId); 0267 } 0268 0269 void KisImageAnimationInterface::switchCurrentTimeAsync(int frameId, SwitchTimeAsyncFlags options) 0270 { 0271 const bool useUndo = options & STAO_USE_UNDO; 0272 const bool sameFrame = currentUITime() == frameId; 0273 const bool needsCompositingUpdate = requiresOnionSkinRendering(); 0274 const KisTimeSpan range = KisTimeSpan::calculateIdenticalFramesRecursive(m_d->image->root(), currentUITime()); 0275 const bool needsRegeneration = !range.contains(frameId) || needsCompositingUpdate || (options & STAO_FORCE_REGENERATION); 0276 0277 KisSwitchTimeStrokeStrategy::SharedTokenSP token = m_d->switchToken.toStrongRef(); 0278 0279 // Handle switching frame to new time.. 0280 if (!token || !token->tryResetDestinationTime(frameId, needsRegeneration)) { 0281 0282 if (!sameFrame) { 0283 KisPostExecutionUndoAdapter *undoAdapter = useUndo ? 0284 m_d->image->postExecutionUndoAdapter() : 0; 0285 0286 KisSwitchTimeStrokeStrategy *strategy = 0287 new KisSwitchTimeStrokeStrategy(frameId, needsRegeneration, 0288 this, undoAdapter); 0289 0290 m_d->switchToken = strategy->token(); 0291 0292 KisStrokeId stroke = m_d->image->startStroke(strategy); 0293 m_d->image->endStroke(stroke); 0294 } 0295 0296 if (needsRegeneration) { 0297 KisStrokeStrategy *strategy = 0298 new KisRegenerateFrameStrokeStrategy(this); 0299 0300 KisStrokeId strokeId = m_d->image->startStroke(strategy); 0301 m_d->image->endStroke(strokeId); 0302 } 0303 } 0304 0305 if (!needsRegeneration) { 0306 sigFrameRegenerationSkipped(frameId); 0307 } 0308 0309 0310 0311 m_d->setCurrentUITime(frameId); 0312 emit sigUiTimeChanged(frameId); 0313 } 0314 0315 void KisImageAnimationInterface::requestFrameRegeneration(int frameId, const KisRegion &dirtyRegion, bool isCancellable, KisLockFrameGenerationLock &&lock) 0316 { 0317 KisStrokeStrategy *strategy = 0318 new KisRegenerateFrameStrokeStrategy(frameId, 0319 dirtyRegion, 0320 isCancellable, 0321 this, 0322 std::move(lock)); 0323 0324 QList<KisStrokeJobData*> jobs = KisRegenerateFrameStrokeStrategy::createJobsData(m_d->image); 0325 0326 KisStrokeId stroke = m_d->image->startStroke(strategy); 0327 Q_FOREACH (KisStrokeJobData* job, jobs) { 0328 m_d->image->addJob(stroke, job); 0329 } 0330 m_d->image->endStroke(stroke); 0331 } 0332 0333 void KisImageAnimationInterface::saveAndResetCurrentTime(int frameId, int *savedValue) 0334 { 0335 m_d->externalFrameActive = true; 0336 *savedValue = m_d->currentTime(); 0337 m_d->setCurrentTime(frameId); 0338 } 0339 0340 void KisImageAnimationInterface::restoreCurrentTime(int *savedValue) 0341 { 0342 m_d->setCurrentTime(*savedValue); 0343 m_d->externalFrameActive = false; 0344 } 0345 0346 void KisImageAnimationInterface::notifyFrameReady() 0347 { 0348 emit sigFrameReady(m_d->currentTime()); 0349 } 0350 0351 void KisImageAnimationInterface::notifyFrameCancelled() 0352 { 0353 emit sigFrameCancelled(); 0354 } 0355 0356 void KisImageAnimationInterface::notifyFrameRegenerated() 0357 { 0358 emit sigFrameRegenerated(m_d->currentTime()); 0359 } 0360 0361 bool KisImageAnimationInterface::requiresOnionSkinRendering() { 0362 0363 KisNodeSP onionskinned = KisLayerUtils::recursiveFindNode(m_d->image->root(), [](KisNodeSP p) { 0364 bool onionSkinProp = p->nodeProperties().boolProperty("onionskin", false); 0365 return onionSkinProp; 0366 }); 0367 0368 return onionskinned != nullptr; 0369 } 0370 0371 KisUpdatesFacade* KisImageAnimationInterface::updatesFacade() const 0372 { 0373 return m_d->image; 0374 } 0375 0376 void KisImageAnimationInterface::notifyNodeChanged(const KisNode *node, 0377 const QRect &rect, 0378 bool recursive) 0379 { 0380 notifyNodeChanged(node, QVector<QRect>({rect}), recursive); 0381 } 0382 0383 void KisImageAnimationInterface::notifyNodeChanged(const KisNode *node, 0384 const QVector<QRect> &rects, 0385 bool recursive) 0386 { 0387 if (externalFrameActive() || m_d->frameInvalidationBlocked) return; 0388 0389 // even overlay selection masks are not rendered in the cache 0390 if (node->inherits("KisSelectionMask")) return; 0391 0392 QSet<int> affectedTimes; 0393 affectedTimes << m_d->currentTime(); 0394 0395 // We need to also invalidate ranges that contain cloned keyframe data. 0396 if (recursive) { 0397 QSet<int> clonedTimes; 0398 const int time = m_d->currentTime(); 0399 KisLayerUtils::recursiveApplyNodes(node, [&clonedTimes, time](const KisNode* node){ 0400 clonedTimes += KisRasterKeyframeChannel::clonesOf(node, time); 0401 }); 0402 0403 affectedTimes += clonedTimes; 0404 } else { 0405 affectedTimes += KisRasterKeyframeChannel::clonesOf(node, m_d->currentTime()); 0406 } 0407 0408 foreach (const int& time, affectedTimes ){ 0409 KisTimeSpan invalidateRange; 0410 0411 if (recursive) { 0412 invalidateRange = KisTimeSpan::calculateAffectedFramesRecursive(node, time); 0413 } else { 0414 invalidateRange = KisTimeSpan::calculateNodeAffectedFrames(node, time); 0415 } 0416 0417 // we compress the updated rect (atm, no one uses it anyway) 0418 QRect unitedRect; 0419 Q_FOREACH (const QRect &rc, rects) { 0420 unitedRect |= rc; 0421 } 0422 0423 invalidateFrames(invalidateRange, unitedRect); 0424 } 0425 } 0426 0427 void KisImageAnimationInterface::invalidateFrames(const KisTimeSpan &range, const QRect &rect) 0428 { 0429 m_d->cachedLastFrameValue = -1; 0430 emit sigFramesChanged(range, rect); 0431 } 0432 0433 void KisImageAnimationInterface::invalidateFrame(const int time, KisNodeSP target) 0434 { 0435 m_d->cachedLastFrameValue = -1; 0436 0437 emit sigFramesChanged(KisLayerUtils::fetchLayerActiveRasterFrameSpan(target, time), m_d->image->bounds()); 0438 0439 QSet<int> identicalFrames = KisLayerUtils::fetchLayerIdenticalRasterFrameTimes(target, time); 0440 Q_FOREACH(const int& identicalTime, identicalFrames) { 0441 emit sigFramesChanged(KisLayerUtils::fetchLayerActiveRasterFrameSpan(target, identicalTime), m_d->image->bounds()); 0442 } 0443 } 0444 0445 void KisImageAnimationInterface::blockFrameInvalidation(bool value) 0446 { 0447 m_d->frameInvalidationBlocked = value; 0448 } 0449 0450 int findLastKeyframeTimeRecursive(KisNodeSP node) 0451 { 0452 int time = 0; 0453 0454 KisKeyframeChannel *channel; 0455 Q_FOREACH (channel, node->keyframeChannels()) { 0456 time = std::max(time, channel->lastKeyframeTime()); 0457 } 0458 0459 KisNodeSP child = node->firstChild(); 0460 while (child) { 0461 time = std::max(time, findLastKeyframeTimeRecursive(child)); 0462 child = child->nextSibling(); 0463 } 0464 0465 return time; 0466 } 0467 0468 int KisImageAnimationInterface::totalLength() 0469 { 0470 if (m_d->cachedLastFrameValue < 0) { 0471 m_d->cachedLastFrameValue = findLastKeyframeTimeRecursive(m_d->image->root()); 0472 } 0473 0474 int lastKey = m_d->cachedLastFrameValue; 0475 0476 lastKey = std::max(lastKey, m_d->documentRange.end()); 0477 lastKey = std::max(lastKey, m_d->currentUITime()); 0478 0479 return lastKey + 1; 0480 } 0481 0482 void KisImageAnimationInterface::blockBackgroundFrameGeneration() 0483 { 0484 m_d->backgroundFrameGenerationBlocked.ref(); 0485 } 0486 0487 void KisImageAnimationInterface::unblockBackgroundFrameGeneration() 0488 { 0489 m_d->backgroundFrameGenerationBlocked.deref(); 0490 } 0491 0492 bool KisImageAnimationInterface::backgroundFrameGenerationBlocked() const 0493 { 0494 return m_d->backgroundFrameGenerationBlocked.loadAcquire(); 0495 } 0496 0497 bool KisImageAnimationInterface::tryLockFrameGeneration() 0498 { 0499 return m_d->frameGenerationLock.tryLock(); 0500 } 0501 0502 void KisImageAnimationInterface::lockFrameGeneration() 0503 { 0504 m_d->frameGenerationLock.lock(); 0505 } 0506 0507 void KisImageAnimationInterface::unlockFrameGeneration() 0508 { 0509 m_d->frameGenerationLock.unlock(); 0510 }