Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_image_animation_interface.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2015 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
8
9#include <QMutex>
10
11#include "kis_global.h"
12#include "kis_image.h"
15#include "KoProperties.h"
18#include "kis_time_span.h"
19
23#include "kis_layer_utils.h"
24
25
27{
38
54
58
63
65
69
71
74
75 inline int currentTime() const {
76 return m_currentTime;
77 }
78
79 inline int currentUITime() const {
80 return m_currentUITime;
81 }
82
83 inline void setCurrentTime(int value) {
85 }
86
87 inline void setCurrentUITime(int value) {
89 }
90private:
93};
94
95
97 : QObject(image),
98 m_d(new Private)
99{
100 m_d->image = image;
101
102 m_d->framerate = 24;
103 m_d->documentRange = KisTimeSpan::fromTimeToTime(0, 100);
104
105 connect(this, &KisImageAnimationInterface::sigInternalRequestTimeSwitch, this, [this](int frame, bool useUndo) {
106 this->switchCurrentTimeAsync(frame, useUndo ? STAO_USE_UNDO : STAO_NONE);
107 });
108}
109
111 : m_d(new Private(*rhs.m_d, newImage))
112{
113 connect(this, &KisImageAnimationInterface::sigInternalRequestTimeSwitch, this, [this](int frame, bool useUndo) {
114 this->switchCurrentTimeAsync(frame, useUndo ? STAO_USE_UNDO : STAO_NONE);
115 });
116}
117
121
123{
124 bool hasAnimation = false;
125
127 m_d->image->root(),
128 [&hasAnimation](KisNodeSP node) {
129 hasAnimation |= node->isAnimated();
130 });
131
132 return hasAnimation;
133}
134
136{
137 return m_d->currentTime();
138}
139
141{
142 return m_d->currentUITime();
143}
144
146{
147 return m_d->documentRange;
148}
149
151{
153 m_d->documentRange = range;
155}
156
158{
159 KisTimeSpan newRange = KisTimeSpan::fromTimeToTime(column, m_d->documentRange.end());
160 setDocumentRange(newRange);
161}
162
164{
165 KisTimeSpan newRange = KisTimeSpan::fromTimeToTime(m_d->documentRange.start(), column);
166 setDocumentRange(newRange);
167}
168
170{
171 return m_d->activePlaybackRange.isValid() ? m_d->activePlaybackRange : m_d->documentRange;
172}
173
175{
177 m_d->activePlaybackRange = range;
179}
180
182{
183 return m_d->framerate;
184}
185
187{
188 return m_d->exportSequenceFilePath;
189}
190
192{
193 m_d->exportSequenceFilePath = filePath;
194}
195
197{
198 return m_d->exportSequenceBaseName;
199}
200
202{
203 m_d->exportSequenceBaseName = baseName;
204}
205
207{
208 return m_d->exportInitialFrameNumber;
209}
210
212{
213 m_d->exportInitialFrameNumber = frameNum;
214}
215
217{
218 return m_d->activeLayerSelectedTimes;
219}
220
222{
223 m_d->activeLayerSelectedTimes = times;
224}
225
227{
228 if (fps > 0) {
229 m_d->framerate = fps;
230 Q_EMIT sigFramerateChanged();
231 }
232}
233
235{
236 return m_d->image;
237}
238
240{
241 return m_d->externalFrameActive;
242}
243
248
250{
251 int savedTime = 0;
253
254 m_d->image->setDefaultProjectionColor(color);
255
256 restoreCurrentTime(&savedTime);
257}
258
260{
261 Q_EMIT sigInternalRequestTimeSwitch(time, useUndo);
262}
263
265{
266 m_d->setCurrentTime(frameId);
267}
268
269void KisImageAnimationInterface::switchCurrentTimeAsync(int frameId, SwitchTimeAsyncFlags options)
270{
271 const bool useUndo = options & STAO_USE_UNDO;
272 const bool sameFrame = currentUITime() == frameId;
273 const bool needsCompositingUpdate = requiresOnionSkinRendering();
275 const bool needsRegeneration = !range.contains(frameId) || needsCompositingUpdate || (options & STAO_FORCE_REGENERATION);
276
277 KisSwitchTimeStrokeStrategy::SharedTokenSP token = m_d->switchToken.toStrongRef();
278
279 // Handle switching frame to new time..
280 if (!token || !token->tryResetDestinationTime(frameId, needsRegeneration)) {
281
282 if (!sameFrame) {
283 KisPostExecutionUndoAdapter *undoAdapter = useUndo ?
284 m_d->image->postExecutionUndoAdapter() : 0;
285
287 new KisSwitchTimeStrokeStrategy(frameId, needsRegeneration,
288 this, undoAdapter);
289
290 m_d->switchToken = strategy->token();
291
292 KisStrokeId stroke = m_d->image->startStroke(strategy);
293 m_d->image->endStroke(stroke);
294 }
295
296 if (needsRegeneration) {
297 KisStrokeStrategy *strategy =
299
300 KisStrokeId strokeId = m_d->image->startStroke(strategy);
301 m_d->image->endStroke(strokeId);
302 }
303 }
304
305 if (!needsRegeneration) {
307 }
308
309
310
311 m_d->setCurrentUITime(frameId);
312 Q_EMIT sigUiTimeChanged(frameId);
313}
314
315void KisImageAnimationInterface::requestFrameRegeneration(int frameId, const KisRegion &dirtyRegion, bool isCancellable, KisLockFrameGenerationLock &&lock)
316{
317 KisStrokeStrategy *strategy =
319 dirtyRegion,
320 isCancellable,
321 this,
322 std::move(lock));
323
325
326 KisStrokeId stroke = m_d->image->startStroke(strategy);
327 Q_FOREACH (KisStrokeJobData* job, jobs) {
328 m_d->image->addJob(stroke, job);
329 }
330 m_d->image->endStroke(stroke);
331}
332
334{
335 m_d->externalFrameActive = true;
336 *savedValue = m_d->currentTime();
337 m_d->setCurrentTime(frameId);
338}
339
341{
342 m_d->setCurrentTime(*savedValue);
343 m_d->externalFrameActive = false;
344}
345
347{
348 Q_EMIT sigFrameReady(m_d->currentTime());
349}
350
355
360
362
363 KisNodeSP onionskinned = KisLayerUtils::recursiveFindNode(m_d->image->root(), [](KisNodeSP p) {
364 bool onionSkinProp = p->nodeProperties().boolProperty("onionskin", false);
365 return onionSkinProp;
366 });
367
368 return onionskinned != nullptr;
369}
370
375
377 const QRect &rect,
378 bool recursive)
379{
380 notifyNodeChanged(node, QVector<QRect>({rect}), recursive);
381}
382
384 const QVector<QRect> &rects,
385 bool recursive)
386{
387 if (externalFrameActive() || m_d->frameInvalidationBlocked) return;
388
389 // even overlay selection masks are not rendered in the cache
390 if (node->inherits("KisSelectionMask")) return;
391
392 QSet<int> affectedTimes;
393 affectedTimes << m_d->currentTime();
394
395 // We need to also invalidate ranges that contain cloned keyframe data.
396 if (recursive) {
397 QSet<int> clonedTimes;
398 const int time = m_d->currentTime();
399 KisLayerUtils::recursiveApplyNodes(node, [&clonedTimes, time](const KisNode* node){
400 clonedTimes += KisRasterKeyframeChannel::clonesOf(node, time);
401 });
402
403 affectedTimes += clonedTimes;
404 } else {
405 affectedTimes += KisRasterKeyframeChannel::clonesOf(node, m_d->currentTime());
406 }
407
408 foreach (const int& time, affectedTimes ){
409 KisTimeSpan invalidateRange;
410
411 if (recursive) {
412 invalidateRange = KisTimeSpan::calculateAffectedFramesRecursive(node, time);
413 } else {
414 invalidateRange = KisTimeSpan::calculateNodeAffectedFrames(node, time);
415 }
416
417 // we compress the updated rect (atm, no one uses it anyway)
418 QRect unitedRect;
419 Q_FOREACH (const QRect &rc, rects) {
420 unitedRect |= rc;
421 }
422
423 invalidateFrames(invalidateRange, unitedRect);
424 }
425}
426
428{
429 m_d->cachedLastFrameValue = -1;
430 Q_EMIT sigFramesChanged(range, rect);
431}
432
434{
435 m_d->cachedLastFrameValue = -1;
436
438
439 QSet<int> identicalFrames = KisLayerUtils::fetchLayerIdenticalRasterFrameTimes(target, time);
440 Q_FOREACH(const int& identicalTime, identicalFrames) {
441 Q_EMIT sigFramesChanged(KisLayerUtils::fetchLayerActiveRasterFrameSpan(target, identicalTime), m_d->image->bounds());
442 }
443}
444
446{
447 m_d->frameInvalidationBlocked = value;
448}
449
451{
452 int time = 0;
453
454 KisKeyframeChannel *channel;
455 Q_FOREACH (channel, node->keyframeChannels()) {
456 time = std::max(time, channel->lastKeyframeTime());
457 }
458
459 KisNodeSP child = node->firstChild();
460 while (child) {
461 time = std::max(time, findLastKeyframeTimeRecursive(child));
462 child = child->nextSibling();
463 }
464
465 return time;
466}
467
469{
470 if (m_d->cachedLastFrameValue < 0) {
471 m_d->cachedLastFrameValue = findLastKeyframeTimeRecursive(m_d->image->root());
472 }
473
474 int lastKey = m_d->cachedLastFrameValue;
475
476 lastKey = std::max(lastKey, m_d->documentRange.end());
477 lastKey = std::max(lastKey, m_d->currentUITime());
478
479 return lastKey + 1;
480}
481
483{
484 m_d->backgroundFrameGenerationBlocked.ref();
485}
486
488{
489 m_d->backgroundFrameGenerationBlocked.deref();
490}
491
493{
494 return m_d->backgroundFrameGenerationBlocked.loadAcquire();
495}
496
498{
499 return m_d->frameGenerationLock.tryLock();
500}
501
503{
504 m_d->frameGenerationLock.lock();
505}
506
508{
509 m_d->frameGenerationLock.unlock();
510}
float value(const T *src, size_t ch)
const Params2D p
KisMagneticGraph::vertex_descriptor target(typename KisMagneticGraph::edge_descriptor e, KisMagneticGraph g)
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
void setExportSequenceBaseName(const QString &baseName)
void sigFrameReady(int time)
sigFrameReady notifies when an External frame has been regenerated and is available.
void sigUiTimeChanged(int newTime)
void switchCurrentTimeAsync(int frameId, SwitchTimeAsyncFlags options=STAO_NONE)
void setActiveLayerSelectedTimes(const QSet< int > &times)
void setDocumentRange(const KisTimeSpan range)
const KisTimeSpan & activePlaybackRange() const
activePlaybackRange
void sigFrameRegenerationSkipped(int time)
sigFrameRegenerationSkipped notified when async frame changes are skipped.
void sigInternalRequestTimeSwitch(int frameId, bool useUndo)
void invalidateFrame(const int time, KisNodeSP target)
void setActivePlaybackRange(const KisTimeSpan range)
const KisTimeSpan & documentPlaybackRange() const
documentPlaybackRange
void setDefaultProjectionColor(const KoColor &color)
void invalidateFrames(const KisTimeSpan &range, const QRect &rect)
void requestFrameRegeneration(int frameId, const KisRegion &dirtyRegion, bool isCancellable, KisLockFrameGenerationLock &&lock)
void notifyNodeChanged(const KisNode *node, const QRect &rect, bool recursive)
void setExportInitialFrameNumber(const int frameNum)
void sigFrameRegenerated(int time)
sigFrameRegenerated notifies when internal frame has been fully regenerated.
void saveAndResetCurrentTime(int frameId, int *savedValue)
void requestTimeSwitchNonGUI(int time, bool useUndo=false)
void sigFramesChanged(const KisTimeSpan &range, const QRect &rect)
void setExportSequenceFilePath(const QString &filePath)
const QScopedPointer< Private > m_d
KisKeyframeChannel stores and manages KisKeyframes. Maps units of time to virtual keyframe values....
static QList< KisStrokeJobData * > createJobsData(KisImageWSP image)
bool contains(int time) const
bool isInfinite() const
static KisTimeSpan calculateNodeAffectedFrames(const KisNode *node, int time)
static KisTimeSpan calculateIdenticalFramesRecursive(const KisNode *node, int time)
static KisTimeSpan calculateAffectedFramesRecursive(const KisNode *node, int time)
static KisTimeSpan fromTimeToTime(int start, int end)
bool isValid() const
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
int findLastKeyframeTimeRecursive(KisNodeSP node)
KisNodeSP recursiveFindNode(KisNodeSP node, std::function< bool(KisNodeSP)> func)
KisTimeSpan fetchLayerActiveRasterFrameSpan(KisNodeSP node, const int time)
void recursiveApplyNodes(NodePointer node, Functor func)
QSet< int > fetchLayerIdenticalRasterFrameTimes(KisNodeSP node, const int &frameTime)
QMap< QString, KisKeyframeChannel * > keyframeChannels
Private(const Private &rhs, KisImage *newImage)
KisSwitchTimeStrokeStrategy::SharedTokenWSP switchToken
KisNodeSP firstChild() const
Definition kis_node.cpp:361
KisNodeSP nextSibling() const
Definition kis_node.cpp:408