File indexing completed on 2024-05-19 04:29:17
0001 /* This file is part of the KDE project 0002 SPDX-FileCopyrightText: 2022 Emmet O'Neill <emmetoneill.pdx@gmail.com> 0003 SPDX-FileCopyrightText: 2022 Eoin O'Neill <eoinoneill1991@gmail.com> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "KisPlaybackEngine.h" 0009 0010 #include "kis_canvas2.h" 0011 #include "KisCanvasAnimationState.h" 0012 #include "kis_image_animation_interface.h" 0013 #include "kis_raster_keyframe_channel.h" 0014 #include "animation/KisFrameDisplayProxy.h" 0015 #include "KisViewManager.h" 0016 #include "kis_config.h" 0017 0018 #include "kis_onion_skin_compositor.h" 0019 0020 struct KisPlaybackEngine::Private { 0021 public: 0022 Private() { 0023 } 0024 0025 ~Private() { 0026 } 0027 0028 KisCanvas2* activeCanvas {nullptr}; 0029 bool dropFramesMode {true}; 0030 }; 0031 0032 //===== 0033 0034 KisPlaybackEngine::KisPlaybackEngine(QObject *parent) 0035 : QObject(parent) 0036 , m_d(new Private) 0037 { 0038 KisConfig cfg(true); 0039 m_d->dropFramesMode = cfg.animationDropFrames(); 0040 } 0041 0042 KisPlaybackEngine::~KisPlaybackEngine() 0043 { 0044 } 0045 0046 void KisPlaybackEngine::play() 0047 { 0048 KIS_SAFE_ASSERT_RECOVER_RETURN(activeCanvas() && activeCanvas()->animationState()); 0049 activeCanvas()->animationState()->setPlaybackState(PLAYING); 0050 } 0051 0052 void KisPlaybackEngine::pause() 0053 { 0054 KIS_SAFE_ASSERT_RECOVER_RETURN(activeCanvas() && activeCanvas()->animationState()); 0055 activeCanvas()->animationState()->setPlaybackState(PAUSED); 0056 } 0057 0058 void KisPlaybackEngine::playPause() 0059 { 0060 KIS_SAFE_ASSERT_RECOVER_RETURN(activeCanvas() && activeCanvas()->animationState()); 0061 KisCanvasAnimationState* animationState = activeCanvas()->animationState(); 0062 0063 if (animationState->playbackState() == PLAYING) { 0064 pause(); 0065 seek(animationState->displayProxy()->activeFrame(), SEEK_FINALIZE); 0066 } else { 0067 play(); 0068 } 0069 } 0070 0071 void KisPlaybackEngine::stop() 0072 { 0073 KIS_SAFE_ASSERT_RECOVER_RETURN(activeCanvas() && activeCanvas()->animationState()); 0074 KisCanvasAnimationState* animationState = activeCanvas()->animationState(); 0075 0076 if (animationState->playbackState() != STOPPED) { 0077 const boost::optional<int> origin = animationState->playbackOrigin(); 0078 animationState->setPlaybackState(STOPPED); 0079 if (origin.has_value()) { 0080 seek(origin.value(), SEEK_FINALIZE); 0081 } 0082 } else if (animationState->displayProxy()->activeFrame() != 0) { 0083 KisImageAnimationInterface* ai = activeCanvas()->image()->animationInterface(); 0084 KIS_SAFE_ASSERT_RECOVER_RETURN(ai); 0085 const int firstFrame = ai->documentPlaybackRange().start(); 0086 seek(firstFrame, SEEK_FINALIZE | SEEK_PUSH_AUDIO); 0087 } 0088 } 0089 0090 void KisPlaybackEngine::previousFrame() 0091 { 0092 moveActiveFrameBy(-1); 0093 } 0094 0095 void KisPlaybackEngine::nextFrame() 0096 { 0097 moveActiveFrameBy(1); 0098 } 0099 0100 void KisPlaybackEngine::previousKeyframe() 0101 { 0102 if (!m_d->activeCanvas) return; 0103 KisCanvasAnimationState *animationState = m_d->activeCanvas->animationState(); 0104 KIS_SAFE_ASSERT_RECOVER_RETURN(animationState); 0105 0106 KisNodeSP node = m_d->activeCanvas->viewManager()->activeNode(); 0107 if (!node) return; 0108 0109 KisKeyframeChannel *keyframes = 0110 node->getKeyframeChannel(KisKeyframeChannel::Raster.id()); 0111 if (!keyframes) return; 0112 0113 int currentFrame = animationState->displayProxy()->activeFrame(); 0114 0115 int destinationTime = -1; 0116 if (!keyframes->keyframeAt(currentFrame)) { 0117 destinationTime = keyframes->activeKeyframeTime(currentFrame); 0118 } else { 0119 destinationTime = keyframes->previousKeyframeTime(currentFrame); 0120 } 0121 0122 if (keyframes->keyframeAt(destinationTime)) { 0123 if (animationState->playbackState() != STOPPED) { 0124 stop(); 0125 } 0126 0127 seek(destinationTime, SEEK_FINALIZE | SEEK_PUSH_AUDIO); 0128 } 0129 } 0130 0131 void KisPlaybackEngine::nextKeyframe() 0132 { 0133 if (!m_d->activeCanvas) return; 0134 KisCanvasAnimationState *animationState = m_d->activeCanvas->animationState(); 0135 KIS_SAFE_ASSERT_RECOVER_RETURN(animationState); 0136 0137 KisNodeSP node = m_d->activeCanvas->viewManager()->activeNode(); 0138 if (!node) return; 0139 0140 KisKeyframeChannel *keyframes = 0141 node->getKeyframeChannel(KisKeyframeChannel::Raster.id()); 0142 if (!keyframes) return; 0143 0144 int currentTime = animationState->displayProxy()->activeFrame(); 0145 0146 int destinationTime = -1; 0147 if (keyframes->activeKeyframeAt(currentTime)) { 0148 destinationTime = keyframes->nextKeyframeTime(currentTime); 0149 } 0150 0151 if (keyframes->keyframeAt(destinationTime)) { 0152 // Jump to next key... 0153 if (animationState->playbackState() != STOPPED) { 0154 stop(); 0155 } 0156 0157 seek(destinationTime, SEEK_FINALIZE | SEEK_PUSH_AUDIO); 0158 } else { 0159 // Jump ahead by estimated timing... 0160 const int activeKeyTime = keyframes->activeKeyframeTime(currentTime); 0161 const int previousKeyTime = keyframes->previousKeyframeTime(activeKeyTime); 0162 0163 if (previousKeyTime != -1) { 0164 if (animationState->playbackState() != STOPPED) { 0165 stop(); 0166 } 0167 0168 const int timing = activeKeyTime - previousKeyTime; 0169 seek(currentTime + timing, SEEK_FINALIZE | SEEK_PUSH_AUDIO); 0170 } 0171 } 0172 } 0173 0174 void KisPlaybackEngine::firstFrame() 0175 { 0176 if (!m_d->activeCanvas) return; 0177 KisCanvasAnimationState *animationState = m_d->activeCanvas->animationState(); 0178 KIS_SAFE_ASSERT_RECOVER_RETURN(animationState); 0179 KisImageAnimationInterface *animInterface = m_d->activeCanvas->image()->animationInterface(); 0180 0181 const int startFrame = animInterface->activePlaybackRange().start(); 0182 0183 if (animationState->playbackState() != STOPPED) { 0184 stop(); 0185 } 0186 0187 seek(startFrame, SEEK_FINALIZE | SEEK_PUSH_AUDIO); 0188 } 0189 0190 void KisPlaybackEngine::lastFrame() 0191 { 0192 if (!m_d->activeCanvas) return; 0193 KisCanvasAnimationState *animationState = m_d->activeCanvas->animationState(); 0194 KIS_SAFE_ASSERT_RECOVER_RETURN(animationState); 0195 KisImageAnimationInterface *animInterface = m_d->activeCanvas->image()->animationInterface(); 0196 0197 const int endFrame = animInterface->activePlaybackRange().end(); 0198 0199 if (animationState->playbackState() != STOPPED) { 0200 stop(); 0201 } 0202 0203 seek(endFrame, SEEK_FINALIZE | SEEK_PUSH_AUDIO); 0204 } 0205 0206 void KisPlaybackEngine::previousMatchingKeyframe() 0207 { 0208 if (!m_d->activeCanvas) return; 0209 KisCanvasAnimationState *animationState = m_d->activeCanvas->animationState(); 0210 KIS_SAFE_ASSERT_RECOVER_RETURN(animationState); 0211 0212 KisNodeSP node = m_d->activeCanvas->viewManager()->activeNode(); 0213 if (!node) return; 0214 0215 KisKeyframeChannel *keyframes = 0216 node->getKeyframeChannel(KisKeyframeChannel::Raster.id()); 0217 if (!keyframes) return; 0218 0219 int time = animationState->displayProxy()->activeFrame(); 0220 0221 KisKeyframeSP currentKeyframe = keyframes->keyframeAt(time); 0222 int destinationTime = keyframes->activeKeyframeTime(time); 0223 const int desiredColor = currentKeyframe ? currentKeyframe->colorLabel() : keyframes->keyframeAt(destinationTime)->colorLabel(); 0224 previousKeyframeWithColor(desiredColor); 0225 } 0226 0227 void KisPlaybackEngine::nextMatchingKeyframe() 0228 { 0229 if (!m_d->activeCanvas) return; 0230 KisCanvasAnimationState *animationState = m_d->activeCanvas->animationState(); 0231 KIS_SAFE_ASSERT_RECOVER_RETURN(animationState); 0232 0233 KisNodeSP node = m_d->activeCanvas->viewManager()->activeNode(); 0234 if (!node) return; 0235 0236 KisKeyframeChannel *keyframes = 0237 node->getKeyframeChannel(KisKeyframeChannel::Raster.id()); 0238 if (!keyframes) return; 0239 0240 int time = animationState->displayProxy()->activeFrame(); 0241 0242 if (!keyframes->activeKeyframeAt(time)) { 0243 return; 0244 } 0245 0246 int destinationTime = keyframes->activeKeyframeTime(time); 0247 nextKeyframeWithColor(keyframes->keyframeAt(destinationTime)->colorLabel()); 0248 } 0249 0250 void KisPlaybackEngine::previousUnfilteredKeyframe() 0251 { 0252 previousKeyframeWithColor(KisOnionSkinCompositor::instance()->colorLabelFilter()); 0253 } 0254 0255 void KisPlaybackEngine::nextUnfilteredKeyframe() 0256 { 0257 nextKeyframeWithColor(KisOnionSkinCompositor::instance()->colorLabelFilter()); 0258 } 0259 0260 void KisPlaybackEngine::setDropFramesMode(bool value) 0261 { 0262 if (value != m_d->dropFramesMode) { 0263 m_d->dropFramesMode = value; 0264 Q_EMIT sigDropFramesModeChanged(value); 0265 } 0266 } 0267 0268 bool KisPlaybackEngine::dropFrames() const 0269 { 0270 return m_d->dropFramesMode; 0271 } 0272 0273 int KisPlaybackEngine::frameWrap(int frame, int startFrame, int endFrame) 0274 { 0275 // Since Krita has always considered the end frame as inclusive, we need 0276 // to make sure our wrap method respects that as well. 0277 const int inclusiveEndFrame = endFrame + 1; 0278 frame = ((frame - startFrame) % (inclusiveEndFrame - startFrame)) + startFrame; 0279 0280 if (frame - startFrame < 0) { 0281 frame += (inclusiveEndFrame - startFrame); 0282 } 0283 0284 return frame; 0285 } 0286 0287 KisCanvas2 *KisPlaybackEngine::activeCanvas() const 0288 { 0289 return m_d->activeCanvas; 0290 } 0291 0292 void KisPlaybackEngine::setCanvas(KoCanvasBase *p_canvas) 0293 { 0294 if (m_d->activeCanvas) { 0295 KisCanvasAnimationState* animState = m_d->activeCanvas->animationState(); 0296 KIS_SAFE_ASSERT_RECOVER_RETURN(animState); 0297 0298 animState->disconnect(this); 0299 } 0300 0301 KisCanvas2* canvas = dynamic_cast<KisCanvas2*>(p_canvas); 0302 m_d->activeCanvas = canvas; 0303 0304 if (m_d->activeCanvas) { 0305 KisCanvasAnimationState* animState = m_d->activeCanvas->animationState(); 0306 KIS_SAFE_ASSERT_RECOVER_RETURN(animState); 0307 0308 connect(animState, &KisCanvasAnimationState::sigCancelPlayback, 0309 this, &KisPlaybackEngine::stop); 0310 0311 /** 0312 * TODO: This forced updates causes image recalculation on every document 0313 * switch, which is weird and even causes some crashes on closing 0314 * many documents at once (which is a separate bug it seems). Why 0315 * document switch should forcefully regeneare the canvas? 0316 */ 0317 // KisImageAnimationInterface* animInterface = m_d->activeCanvas->image()->animationInterface(); 0318 // KisFrameDisplayProxy* displayProxy = animState->displayProxy(); 0319 // if (animState->playbackState() != PLAYING) { 0320 // displayProxy->displayFrame(animInterface->currentTime(), false); 0321 // } 0322 } 0323 } 0324 0325 void KisPlaybackEngine::unsetCanvas() 0326 { 0327 m_d->activeCanvas = nullptr; 0328 } 0329 0330 void KisPlaybackEngine::moveActiveFrameBy(int frames) 0331 { 0332 if (!m_d->activeCanvas) return; 0333 KisCanvasAnimationState *animationState = m_d->activeCanvas->animationState(); 0334 KIS_SAFE_ASSERT_RECOVER_RETURN(animationState); 0335 KisImageAnimationInterface *animInterface = m_d->activeCanvas->image()->animationInterface(); 0336 0337 int frame = animationState->displayProxy()->activeFrame() + frames; 0338 0339 const int startFrame = animInterface->activePlaybackRange().start(); 0340 const int endFrame = animInterface->activePlaybackRange().end(); 0341 0342 frame = frameWrap(frame, startFrame, endFrame); 0343 0344 KIS_SAFE_ASSERT_RECOVER_RETURN(frame >= 0); 0345 0346 if (animationState->playbackState() != STOPPED) { 0347 stop(); 0348 } 0349 0350 seek(frame, SEEK_FINALIZE | SEEK_PUSH_AUDIO); 0351 } 0352 0353 void KisPlaybackEngine::nextKeyframeWithColor(int color) 0354 { 0355 QSet<int> validColors; 0356 validColors.insert(color); 0357 nextKeyframeWithColor(validColors); 0358 } 0359 0360 void KisPlaybackEngine::nextKeyframeWithColor(const QSet<int> &validColors) 0361 { 0362 if (!m_d->activeCanvas) return; 0363 KisCanvasAnimationState *animationState = m_d->activeCanvas->animationState(); 0364 KIS_SAFE_ASSERT_RECOVER_RETURN(animationState); 0365 0366 KisNodeSP node = m_d->activeCanvas->viewManager()->activeNode(); 0367 if (!node) return; 0368 0369 KisKeyframeChannel *keyframes = 0370 node->getKeyframeChannel(KisKeyframeChannel::Raster.id()); 0371 if (!keyframes) return; 0372 0373 int time = animationState->displayProxy()->activeFrame(); 0374 0375 if (!keyframes->activeKeyframeAt(time)) { 0376 return; 0377 } 0378 0379 int destinationTime = keyframes->activeKeyframeTime(time); 0380 while ( keyframes->keyframeAt(destinationTime) && 0381 ((destinationTime == time) || 0382 !validColors.contains(keyframes->keyframeAt(destinationTime)->colorLabel()))) { 0383 0384 destinationTime = keyframes->nextKeyframeTime(destinationTime); 0385 } 0386 0387 if (keyframes->keyframeAt(destinationTime)) { 0388 if (animationState->playbackState() != STOPPED) { 0389 stop(); 0390 } 0391 0392 seek(destinationTime, SEEK_FINALIZE | SEEK_PUSH_AUDIO); 0393 } 0394 } 0395 0396 void KisPlaybackEngine::previousKeyframeWithColor(int color) 0397 { 0398 QSet<int> validColors; 0399 validColors.insert(color); 0400 previousKeyframeWithColor(validColors); 0401 } 0402 0403 void KisPlaybackEngine::previousKeyframeWithColor(const QSet<int> &validColors) 0404 { 0405 if (!m_d->activeCanvas) return; 0406 KisCanvasAnimationState *animationState = m_d->activeCanvas->animationState(); 0407 KIS_SAFE_ASSERT_RECOVER_RETURN(animationState); 0408 0409 KisNodeSP node = m_d->activeCanvas->viewManager()->activeNode(); 0410 if (!node) return; 0411 0412 KisKeyframeChannel *keyframes = 0413 node->getKeyframeChannel(KisKeyframeChannel::Raster.id()); 0414 if (!keyframes) return; 0415 0416 int time = animationState->displayProxy()->activeFrame(); 0417 0418 int destinationTime = keyframes->activeKeyframeTime(time); 0419 while (keyframes->keyframeAt(destinationTime) && 0420 ((destinationTime == time) || 0421 !validColors.contains(keyframes->keyframeAt(destinationTime)->colorLabel()))) { 0422 0423 destinationTime = keyframes->previousKeyframeTime(destinationTime); 0424 } 0425 0426 if (keyframes->keyframeAt(destinationTime)) { 0427 if (animationState->playbackState() != STOPPED) { 0428 stop(); 0429 } 0430 0431 seek(destinationTime, SEEK_FINALIZE | SEEK_PUSH_AUDIO); 0432 } 0433 }