Krita Source Code Documentation
Loading...
Searching...
No Matches
KisPlaybackEngine.cpp
Go to the documentation of this file.
1/* This file is part of the KDE project
2 SPDX-FileCopyrightText: 2022 Emmet O'Neill <emmetoneill.pdx@gmail.com>
3 SPDX-FileCopyrightText: 2022 Eoin O'Neill <eoinoneill1991@gmail.com>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include "KisPlaybackEngine.h"
9
10#include "kis_canvas2.h"
15#include "KisViewManager.h"
16#include "kis_config.h"
17
19
21public:
23 }
24
26 }
27
29 bool dropFramesMode {true};
30};
31
32//=====
33
35 : QObject(parent)
36 , m_d(new Private)
37{
38 KisConfig cfg(true);
39 m_d->dropFramesMode = cfg.animationDropFrames();
40}
41
45
51
53{
56
57 animationState->setPlaybackState(PAUSED);
58
59 seek(animationState->displayProxy()->activeFrame(), SEEK_FINALIZE);
60}
61
63{
66
67 if (animationState->playbackState() == PLAYING) {
68 pause();
69 } else {
70 play();
71 }
72}
73
75{
78
79 if (animationState->playbackState() != STOPPED) {
80 const boost::optional<int> origin = animationState->playbackOrigin();
81 animationState->setPlaybackState(STOPPED);
82 if (origin.has_value()) {
83 seek(origin.value(), SEEK_FINALIZE);
84 }
85 } else if (animationState->displayProxy()->activeFrame() != 0) {
88 const int firstFrame = ai->documentPlaybackRange().start();
90 }
91}
92
97
102
104{
105 if (!m_d->activeCanvas) return;
106 KisCanvasAnimationState *animationState = m_d->activeCanvas->animationState();
107 KIS_SAFE_ASSERT_RECOVER_RETURN(animationState);
108
109 KisNodeSP node = m_d->activeCanvas->viewManager()->activeNode();
110 if (!node) return;
111
112 KisKeyframeChannel *keyframes =
114 if (!keyframes) return;
115
116 int currentFrame = animationState->displayProxy()->activeFrame();
117
118 int destinationTime = -1;
119 if (!keyframes->keyframeAt(currentFrame)) {
120 destinationTime = keyframes->activeKeyframeTime(currentFrame);
121 } else {
122 destinationTime = keyframes->previousKeyframeTime(currentFrame);
123 }
124
125 if (keyframes->keyframeAt(destinationTime)) {
126 if (animationState->playbackState() != STOPPED) {
127 stop();
128 }
129
130 seek(destinationTime, SEEK_FINALIZE | SEEK_PUSH_AUDIO);
131 }
132}
133
135{
136 if (!m_d->activeCanvas) return;
137 KisCanvasAnimationState *animationState = m_d->activeCanvas->animationState();
138 KIS_SAFE_ASSERT_RECOVER_RETURN(animationState);
139
140 KisNodeSP node = m_d->activeCanvas->viewManager()->activeNode();
141 if (!node) return;
142
143 KisKeyframeChannel *keyframes =
145 if (!keyframes) return;
146
147 int currentTime = animationState->displayProxy()->activeFrame();
148
149 int destinationTime = -1;
150 if (keyframes->activeKeyframeAt(currentTime)) {
151 destinationTime = keyframes->nextKeyframeTime(currentTime);
152 }
153
154 if (keyframes->keyframeAt(destinationTime)) {
155 // Jump to next key...
156 if (animationState->playbackState() != STOPPED) {
157 stop();
158 }
159
160 seek(destinationTime, SEEK_FINALIZE | SEEK_PUSH_AUDIO);
161 } else {
162 // Jump ahead by estimated timing...
163 const int activeKeyTime = keyframes->activeKeyframeTime(currentTime);
164 const int previousKeyTime = keyframes->previousKeyframeTime(activeKeyTime);
165
166 if (previousKeyTime != -1) {
167 if (animationState->playbackState() != STOPPED) {
168 stop();
169 }
170
171 const int timing = activeKeyTime - previousKeyTime;
172 seek(currentTime + timing, SEEK_FINALIZE | SEEK_PUSH_AUDIO);
173 }
174 }
175}
176
178{
179 if (!m_d->activeCanvas) return;
180 KisCanvasAnimationState *animationState = m_d->activeCanvas->animationState();
181 KIS_SAFE_ASSERT_RECOVER_RETURN(animationState);
182 KisImageAnimationInterface *animInterface = m_d->activeCanvas->image()->animationInterface();
183
184 const int startFrame = animInterface->activePlaybackRange().start();
185
186 if (animationState->playbackState() != STOPPED) {
187 stop();
188 }
189
190 seek(startFrame, SEEK_FINALIZE | SEEK_PUSH_AUDIO);
191}
192
194{
195 if (!m_d->activeCanvas) return;
196 KisCanvasAnimationState *animationState = m_d->activeCanvas->animationState();
197 KIS_SAFE_ASSERT_RECOVER_RETURN(animationState);
198 KisImageAnimationInterface *animInterface = m_d->activeCanvas->image()->animationInterface();
199
200 const int endFrame = animInterface->activePlaybackRange().end();
201
202 if (animationState->playbackState() != STOPPED) {
203 stop();
204 }
205
207}
208
210{
211 if (!m_d->activeCanvas) return;
212 KisCanvasAnimationState *animationState = m_d->activeCanvas->animationState();
213 KIS_SAFE_ASSERT_RECOVER_RETURN(animationState);
214
215 KisNodeSP node = m_d->activeCanvas->viewManager()->activeNode();
216 if (!node) return;
217
218 KisKeyframeChannel *keyframes =
220 if (!keyframes) return;
221
222 int time = animationState->displayProxy()->activeFrame();
223
224 KisKeyframeSP currentKeyframe = keyframes->keyframeAt(time);
225 int destinationTime = keyframes->activeKeyframeTime(time);
226 const int desiredColor = currentKeyframe ? currentKeyframe->colorLabel() : keyframes->keyframeAt(destinationTime)->colorLabel();
227 previousKeyframeWithColor(desiredColor);
228}
229
231{
232 if (!m_d->activeCanvas) return;
233 KisCanvasAnimationState *animationState = m_d->activeCanvas->animationState();
234 KIS_SAFE_ASSERT_RECOVER_RETURN(animationState);
235
236 KisNodeSP node = m_d->activeCanvas->viewManager()->activeNode();
237 if (!node) return;
238
239 KisKeyframeChannel *keyframes =
241 if (!keyframes) return;
242
243 int time = animationState->displayProxy()->activeFrame();
244
245 if (!keyframes->activeKeyframeAt(time)) {
246 return;
247 }
248
249 int destinationTime = keyframes->activeKeyframeTime(time);
250 nextKeyframeWithColor(keyframes->keyframeAt(destinationTime)->colorLabel());
251}
252
257
262
264{
265 if (value != m_d->dropFramesMode) {
266 m_d->dropFramesMode = value;
268 }
269}
270
272{
273 return m_d->dropFramesMode;
274}
275
276int KisPlaybackEngine::frameWrap(int frame, int startFrame, int endFrame)
277{
278 // Since Krita has always considered the end frame as inclusive, we need
279 // to make sure our wrap method respects that as well.
280 const int inclusiveEndFrame = endFrame + 1;
281 frame = ((frame - startFrame) % (inclusiveEndFrame - startFrame)) + startFrame;
282
283 if (frame - startFrame < 0) {
284 frame += (inclusiveEndFrame - startFrame);
285 }
286
287 return frame;
288}
289
291{
292 return m_d->activeCanvas;
293}
294
296{
297 if (m_d->activeCanvas) {
298 KisCanvasAnimationState* animState = m_d->activeCanvas->animationState();
300
301 animState->disconnect(this);
302 }
303
304 KisCanvas2* canvas = dynamic_cast<KisCanvas2*>(p_canvas);
305 m_d->activeCanvas = canvas;
306
307 if (m_d->activeCanvas) {
308 KisCanvasAnimationState* animState = m_d->activeCanvas->animationState();
310
313
320// KisImageAnimationInterface* animInterface = m_d->activeCanvas->image()->animationInterface();
321// KisFrameDisplayProxy* displayProxy = animState->displayProxy();
322// if (animState->playbackState() != PLAYING) {
323// displayProxy->displayFrame(animInterface->currentTime(), false);
324// }
325 }
326}
327
329{
330 m_d->activeCanvas = nullptr;
331}
332
334{
335 if (!m_d->activeCanvas) return;
336 KisCanvasAnimationState *animationState = m_d->activeCanvas->animationState();
337 KIS_SAFE_ASSERT_RECOVER_RETURN(animationState);
338 KisImageAnimationInterface *animInterface = m_d->activeCanvas->image()->animationInterface();
339
340 int frame = animationState->displayProxy()->activeFrame() + frames;
341
342 const int startFrame = animInterface->activePlaybackRange().start();
343 const int endFrame = animInterface->activePlaybackRange().end();
344
345 frame = frameWrap(frame, startFrame, endFrame);
346
348
349 if (animationState->playbackState() != STOPPED) {
350 stop();
351 }
352
354}
355
357{
358 QSet<int> validColors;
359 validColors.insert(color);
360 nextKeyframeWithColor(validColors);
361}
362
363void KisPlaybackEngine::nextKeyframeWithColor(const QSet<int> &validColors)
364{
365 if (!m_d->activeCanvas) return;
366 KisCanvasAnimationState *animationState = m_d->activeCanvas->animationState();
367 KIS_SAFE_ASSERT_RECOVER_RETURN(animationState);
368
369 KisNodeSP node = m_d->activeCanvas->viewManager()->activeNode();
370 if (!node) return;
371
372 KisKeyframeChannel *keyframes =
374 if (!keyframes) return;
375
376 int time = animationState->displayProxy()->activeFrame();
377
378 if (!keyframes->activeKeyframeAt(time)) {
379 return;
380 }
381
382 int destinationTime = keyframes->activeKeyframeTime(time);
383 while ( keyframes->keyframeAt(destinationTime) &&
384 ((destinationTime == time) ||
385 !validColors.contains(keyframes->keyframeAt(destinationTime)->colorLabel()))) {
386
387 destinationTime = keyframes->nextKeyframeTime(destinationTime);
388 }
389
390 if (keyframes->keyframeAt(destinationTime)) {
391 if (animationState->playbackState() != STOPPED) {
392 stop();
393 }
394
395 seek(destinationTime, SEEK_FINALIZE | SEEK_PUSH_AUDIO);
396 }
397}
398
400{
401 QSet<int> validColors;
402 validColors.insert(color);
403 previousKeyframeWithColor(validColors);
404}
405
406void KisPlaybackEngine::previousKeyframeWithColor(const QSet<int> &validColors)
407{
408 if (!m_d->activeCanvas) return;
409 KisCanvasAnimationState *animationState = m_d->activeCanvas->animationState();
410 KIS_SAFE_ASSERT_RECOVER_RETURN(animationState);
411
412 KisNodeSP node = m_d->activeCanvas->viewManager()->activeNode();
413 if (!node) return;
414
415 KisKeyframeChannel *keyframes =
417 if (!keyframes) return;
418
419 int time = animationState->displayProxy()->activeFrame();
420
421 int destinationTime = keyframes->activeKeyframeTime(time);
422 while (keyframes->keyframeAt(destinationTime) &&
423 ((destinationTime == time) ||
424 !validColors.contains(keyframes->keyframeAt(destinationTime)->colorLabel()))) {
425
426 destinationTime = keyframes->previousKeyframeTime(destinationTime);
427 }
428
429 if (keyframes->keyframeAt(destinationTime)) {
430 if (animationState->playbackState() != STOPPED) {
431 stop();
432 }
433
434 seek(destinationTime, SEEK_FINALIZE | SEEK_PUSH_AUDIO);
435 }
436}
float value(const T *src, size_t ch)
@ SEEK_PUSH_AUDIO
@ SEEK_FINALIZE
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
KisCanvasAnimationState * animationState() const
KisImageWSP image() const
The KisCanvasAnimationState class stores all of the canvas-specific animation state.
class KisFrameDisplayProxy * displayProxy()
void setPlaybackState(PlaybackState state)
setPlaybackState changes the animation playback state for this canvas. KisPlaybackEngine should respo...
boost::optional< int > playbackOrigin()
Get the animation frame to return to (for this canvas) when playback is stopped, if available.
bool animationDropFrames(bool defaultValue=false) const
int activeFrame() const
Gets the active frame, the frame that is intended to be shown. This should always reflect the actual ...
const KisTimeSpan & activePlaybackRange() const
activePlaybackRange
const KisTimeSpan & documentPlaybackRange() const
documentPlaybackRange
KisImageAnimationInterface * animationInterface() const
KisKeyframeChannel stores and manages KisKeyframes. Maps units of time to virtual keyframe values....
static const KoID Raster
int previousKeyframeTime(const int time) const
KisKeyframeSP keyframeAt(int time) const
Get a keyframe at specified time. Used primarily when the value of a given keyframe is needed.
KisKeyframeSP activeKeyframeAt(int time) const
int activeKeyframeTime(int time) const
Get the time of the active keyframe. Useful for snapping any time to that of the most recent keyframe...
int nextKeyframeTime(const int time) const
static KisOnionSkinCompositor * instance()
virtual void previousUnfilteredKeyframe()
previousUnfilteredKeyframe && nextUnfilteredKeyframe Navigate to keyframes based on the current onion...
virtual void nextMatchingKeyframe()
virtual void setDropFramesMode(bool value)
virtual void playPause()
virtual void nextUnfilteredKeyframe()
void sigDropFramesModeChanged(bool value)
virtual void unsetCanvas() override
void moveActiveFrameBy(int frames)
Move the active frame of the animation player forwards or backwards by some number of frames.
virtual void previousMatchingKeyframe()
previousMatchingKeyframe && nextMatchingKeyframe Navigate to the next keyframe that has the same colo...
virtual void previousKeyframe()
QScopedPointer< Private > m_d
int frameWrap(int frame, int startFrame, int endFrame)
KisPlaybackEngine(QObject *parent=nullptr)
virtual void seek(int frameIndex, SeekOptionFlags options=SEEK_FINALIZE|SEEK_PUSH_AUDIO)=0
virtual void firstFrame()
virtual void nextKeyframe()
class KisCanvas2 * activeCanvas() const
virtual void nextFrame()
virtual void previousFrame()
void nextKeyframeWithColor(int color)
virtual void setCanvas(KoCanvasBase *p_canvas) override
void previousKeyframeWithColor(int color)
int start() const
int end() const
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
KisKeyframeChannel * getKeyframeChannel(const QString &id, bool create)