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