1 /*
2  *  Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18 
19 #include "kis_image_animation_interface.h"
20 
21 #include <QFileInfo>
22 
23 #include "kis_global.h"
24 #include "kis_image.h"
25 #include "kis_regenerate_frame_stroke_strategy.h"
26 #include "kis_switch_time_stroke_strategy.h"
27 #include "kis_keyframe_channel.h"
28 #include "kis_time_range.h"
29 
30 #include "kis_post_execution_undo_adapter.h"
31 #include "commands_new/kis_switch_current_time_command.h"
32 #include "kis_layer_utils.h"
33 
34 
35 struct KisImageAnimationInterface::Private
36 {
PrivateKisImageAnimationInterface::Private37     Private()
38         : image(0),
39           externalFrameActive(false),
40           frameInvalidationBlocked(false),
41           cachedLastFrameValue(-1),
42           audioChannelMuted(false),
43           audioChannelVolume(0.5),
44           m_currentTime(0),
45           m_currentUITime(0)
46     {
47     }
48 
PrivateKisImageAnimationInterface::Private49     Private(const Private &rhs, KisImage *newImage)
50         : image(newImage),
51           externalFrameActive(false),
52           frameInvalidationBlocked(false),
53           fullClipRange(rhs.fullClipRange),
54           playbackRange(rhs.playbackRange),
55           framerate(rhs.framerate),
56           cachedLastFrameValue(-1),
57           audioChannelFileName(rhs.audioChannelFileName),
58           audioChannelMuted(rhs.audioChannelMuted),
59           audioChannelVolume(rhs.audioChannelVolume),
60           m_currentTime(rhs.m_currentTime),
61           m_currentUITime(rhs.m_currentUITime)
62     {
63     }
64 
65     KisImage *image;
66     bool externalFrameActive;
67     bool frameInvalidationBlocked;
68 
69     KisTimeRange fullClipRange;
70     KisTimeRange playbackRange;
71     int framerate;
72     int cachedLastFrameValue;
73     QString audioChannelFileName;
74     bool audioChannelMuted;
75     qreal audioChannelVolume;
76 
77     KisSwitchTimeStrokeStrategy::SharedTokenWSP switchToken;
78 
currentTimeKisImageAnimationInterface::Private79     inline int currentTime() const {
80         return m_currentTime;
81     }
82 
currentUITimeKisImageAnimationInterface::Private83     inline int currentUITime() const {
84         return m_currentUITime;
85     }
setCurrentTimeKisImageAnimationInterface::Private86     inline void setCurrentTime(int value) {
87         m_currentTime = value;
88     }
89 
setCurrentUITimeKisImageAnimationInterface::Private90     inline void setCurrentUITime(int value) {
91         m_currentUITime = value;
92     }
93 private:
94     int m_currentTime;
95     int m_currentUITime;
96 };
97 
98 
KisImageAnimationInterface(KisImage * image)99 KisImageAnimationInterface::KisImageAnimationInterface(KisImage *image)
100     : QObject(image),
101       m_d(new Private)
102 {
103     m_d->image = image;
104 
105     m_d->framerate = 24;
106     m_d->fullClipRange = KisTimeRange::fromTime(0, 100);
107 
108     connect(this, SIGNAL(sigInternalRequestTimeSwitch(int,bool)), SLOT(switchCurrentTimeAsync(int,bool)));
109 }
110 
KisImageAnimationInterface(const KisImageAnimationInterface & rhs,KisImage * newImage)111 KisImageAnimationInterface::KisImageAnimationInterface(const KisImageAnimationInterface &rhs, KisImage *newImage)
112     : m_d(new Private(*rhs.m_d, newImage))
113 {
114     connect(this, SIGNAL(sigInternalRequestTimeSwitch(int,bool)), SLOT(switchCurrentTimeAsync(int,bool)));
115 }
116 
~KisImageAnimationInterface()117 KisImageAnimationInterface::~KisImageAnimationInterface()
118 {
119 }
120 
hasAnimation() const121 bool KisImageAnimationInterface::hasAnimation() const
122 {
123     bool hasAnimation = false;
124 
125     KisLayerUtils::recursiveApplyNodes(
126         m_d->image->root(),
127         [&hasAnimation](KisNodeSP node) {
128             hasAnimation |= node->isAnimated();
129         });
130 
131     return hasAnimation;
132 }
133 
currentTime() const134 int KisImageAnimationInterface::currentTime() const
135 {
136     return m_d->currentTime();
137 }
138 
currentUITime() const139 int KisImageAnimationInterface::currentUITime() const
140 {
141     return m_d->currentUITime();
142 }
143 
fullClipRange() const144 const KisTimeRange& KisImageAnimationInterface::fullClipRange() const
145 {
146     return m_d->fullClipRange;
147 }
148 
setFullClipRange(const KisTimeRange range)149 void KisImageAnimationInterface::setFullClipRange(const KisTimeRange range)
150 {
151     KIS_SAFE_ASSERT_RECOVER_RETURN(!range.isInfinite());
152     m_d->fullClipRange = range;
153     emit sigFullClipRangeChanged();
154 }
155 
setFullClipRangeStartTime(int column)156 void KisImageAnimationInterface::setFullClipRangeStartTime(int column)
157 {
158     KisTimeRange newRange(column,  m_d->fullClipRange.end(), false);
159     setFullClipRange(newRange);
160 }
161 
setFullClipRangeEndTime(int column)162 void KisImageAnimationInterface::setFullClipRangeEndTime(int column)
163 {
164     KisTimeRange newRange(m_d->fullClipRange.start(), column, false);
165     setFullClipRange(newRange);
166 }
167 
playbackRange() const168 const KisTimeRange& KisImageAnimationInterface::playbackRange() const
169 {
170     return m_d->playbackRange.isValid() ? m_d->playbackRange : m_d->fullClipRange;
171 }
172 
setPlaybackRange(const KisTimeRange range)173 void KisImageAnimationInterface::setPlaybackRange(const KisTimeRange range)
174 {
175     KIS_SAFE_ASSERT_RECOVER_RETURN(!range.isInfinite());
176     m_d->playbackRange = range;
177     emit sigPlaybackRangeChanged();
178 }
179 
framerate() const180 int KisImageAnimationInterface::framerate() const
181 {
182     return m_d->framerate;
183 }
184 
audioChannelFileName() const185 QString KisImageAnimationInterface::audioChannelFileName() const
186 {
187     return m_d->audioChannelFileName;
188 }
189 
setAudioChannelFileName(const QString & fileName)190 void KisImageAnimationInterface::setAudioChannelFileName(const QString &fileName)
191 {
192     QFileInfo info(fileName);
193 
194     KIS_SAFE_ASSERT_RECOVER_NOOP(fileName.isEmpty() || info.isAbsolute());
195     m_d->audioChannelFileName = fileName.isEmpty() ? fileName : info.absoluteFilePath();
196 
197     emit sigAudioChannelChanged();
198 }
199 
isAudioMuted() const200 bool KisImageAnimationInterface::isAudioMuted() const
201 {
202     return m_d->audioChannelMuted;
203 }
204 
setAudioMuted(bool value)205 void KisImageAnimationInterface::setAudioMuted(bool value)
206 {
207     m_d->audioChannelMuted = value;
208     emit sigAudioChannelChanged();
209 }
210 
audioVolume() const211 qreal KisImageAnimationInterface::audioVolume() const
212 {
213     return m_d->audioChannelVolume;
214 }
215 
setAudioVolume(qreal value)216 void KisImageAnimationInterface::setAudioVolume(qreal value)
217 {
218     m_d->audioChannelVolume = value;
219     emit sigAudioVolumeChanged();
220 }
221 
setFramerate(int fps)222 void KisImageAnimationInterface::setFramerate(int fps)
223 {
224     m_d->framerate = fps;
225     emit sigFramerateChanged();
226 }
227 
image() const228 KisImageWSP KisImageAnimationInterface::image() const
229 {
230     return m_d->image;
231 }
232 
externalFrameActive() const233 bool KisImageAnimationInterface::externalFrameActive() const
234 {
235     return m_d->externalFrameActive;
236 }
237 
requestTimeSwitchWithUndo(int time)238 void KisImageAnimationInterface::requestTimeSwitchWithUndo(int time)
239 {
240     if (currentUITime() == time) return;
241     requestTimeSwitchNonGUI(time, true);
242 }
243 
setDefaultProjectionColor(const KoColor & color)244 void KisImageAnimationInterface::setDefaultProjectionColor(const KoColor &color)
245 {
246     int savedTime = 0;
247     saveAndResetCurrentTime(currentTime(), &savedTime);
248 
249     m_d->image->setDefaultProjectionColor(color);
250 
251     restoreCurrentTime(&savedTime);
252 }
253 
requestTimeSwitchNonGUI(int time,bool useUndo)254 void KisImageAnimationInterface::requestTimeSwitchNonGUI(int time, bool useUndo)
255 {
256     emit sigInternalRequestTimeSwitch(time, useUndo);
257 }
258 
explicitlySetCurrentTime(int frameId)259 void KisImageAnimationInterface::explicitlySetCurrentTime(int frameId)
260 {
261     m_d->setCurrentTime(frameId);
262 }
263 
switchCurrentTimeAsync(int frameId,bool useUndo)264 void KisImageAnimationInterface::switchCurrentTimeAsync(int frameId, bool useUndo)
265 {
266     if (currentUITime() == frameId) return;
267 
268     const KisTimeRange range = KisTimeRange::calculateIdenticalFramesRecursive(m_d->image->root(), currentUITime());
269     const bool needsRegeneration = !range.contains(frameId);
270 
271     KisSwitchTimeStrokeStrategy::SharedTokenSP token =
272         m_d->switchToken.toStrongRef();
273 
274     if (!token || !token->tryResetDestinationTime(frameId, needsRegeneration)) {
275 
276         {
277             KisPostExecutionUndoAdapter *undoAdapter = useUndo ?
278                 m_d->image->postExecutionUndoAdapter() : 0;
279 
280             KisSwitchTimeStrokeStrategy *strategy =
281                 new KisSwitchTimeStrokeStrategy(frameId, needsRegeneration,
282                                                 this, undoAdapter);
283 
284             m_d->switchToken = strategy->token();
285 
286             KisStrokeId stroke = m_d->image->startStroke(strategy);
287             m_d->image->endStroke(stroke);
288         }
289 
290         if (needsRegeneration) {
291             KisStrokeStrategy *strategy =
292                 new KisRegenerateFrameStrokeStrategy(this);
293 
294             KisStrokeId strokeId = m_d->image->startStroke(strategy);
295             m_d->image->endStroke(strokeId);
296         }
297 
298     }
299 
300     m_d->setCurrentUITime(frameId);
301     emit sigUiTimeChanged(frameId);
302 }
303 
requestFrameRegeneration(int frameId,const KisRegion & dirtyRegion)304 void KisImageAnimationInterface::requestFrameRegeneration(int frameId, const KisRegion &dirtyRegion)
305 {
306     KisStrokeStrategy *strategy =
307         new KisRegenerateFrameStrokeStrategy(frameId,
308                                              dirtyRegion,
309                                              this);
310 
311     QList<KisStrokeJobData*> jobs = KisRegenerateFrameStrokeStrategy::createJobsData(m_d->image);
312 
313     KisStrokeId stroke = m_d->image->startStroke(strategy);
314     Q_FOREACH (KisStrokeJobData* job, jobs) {
315         m_d->image->addJob(stroke, job);
316     }
317     m_d->image->endStroke(stroke);
318 }
319 
saveAndResetCurrentTime(int frameId,int * savedValue)320 void KisImageAnimationInterface::saveAndResetCurrentTime(int frameId, int *savedValue)
321 {
322     m_d->externalFrameActive = true;
323     *savedValue = m_d->currentTime();
324     m_d->setCurrentTime(frameId);
325 }
326 
restoreCurrentTime(int * savedValue)327 void KisImageAnimationInterface::restoreCurrentTime(int *savedValue)
328 {
329     m_d->setCurrentTime(*savedValue);
330     m_d->externalFrameActive = false;
331 }
332 
notifyFrameReady()333 void KisImageAnimationInterface::notifyFrameReady()
334 {
335     emit sigFrameReady(m_d->currentTime());
336 }
337 
notifyFrameCancelled()338 void KisImageAnimationInterface::notifyFrameCancelled()
339 {
340     emit sigFrameCancelled();
341 }
342 
updatesFacade() const343 KisUpdatesFacade* KisImageAnimationInterface::updatesFacade() const
344 {
345     return m_d->image;
346 }
347 
notifyNodeChanged(const KisNode * node,const QRect & rect,bool recursive)348 void KisImageAnimationInterface::notifyNodeChanged(const KisNode *node,
349                                                    const QRect &rect,
350                                                    bool recursive)
351 {
352     notifyNodeChanged(node, QVector<QRect>({rect}), recursive);
353 }
354 
notifyNodeChanged(const KisNode * node,const QVector<QRect> & rects,bool recursive)355 void KisImageAnimationInterface::notifyNodeChanged(const KisNode *node,
356                                                    const QVector<QRect> &rects,
357                                                    bool recursive)
358 {
359     if (externalFrameActive() || m_d->frameInvalidationBlocked) return;
360 
361     // even overlay selection masks are not rendered in the cache
362     if (node->inherits("KisSelectionMask")) return;
363 
364     const int currentTime = m_d->currentTime();
365     KisTimeRange invalidateRange;
366 
367     if (recursive) {
368         invalidateRange = KisTimeRange::calculateAffectedFramesRecursive(node, currentTime);
369     } else {
370         invalidateRange = KisTimeRange::calculateNodeAffectedFrames(node, currentTime);
371     }
372 
373     // we compress the updated rect (atm, no one uses it anyway)
374     QRect unitedRect;
375     Q_FOREACH (const QRect &rc, rects) {
376         unitedRect |= rc;
377     }
378 
379     invalidateFrames(invalidateRange, unitedRect);
380 }
381 
invalidateFrames(const KisTimeRange & range,const QRect & rect)382 void KisImageAnimationInterface::invalidateFrames(const KisTimeRange &range, const QRect &rect)
383 {
384     m_d->cachedLastFrameValue = -1;
385     emit sigFramesChanged(range, rect);
386 }
387 
blockFrameInvalidation(bool value)388 void KisImageAnimationInterface::blockFrameInvalidation(bool value)
389 {
390     m_d->frameInvalidationBlocked = value;
391 }
392 
findLastKeyframeTimeRecursive(KisNodeSP node)393 int findLastKeyframeTimeRecursive(KisNodeSP node)
394 {
395     int time = 0;
396 
397     KisKeyframeChannel *channel;
398     Q_FOREACH (channel, node->keyframeChannels()) {
399         KisKeyframeSP keyframe = channel->lastKeyframe();
400         if (keyframe) {
401             time = std::max(time, keyframe->time());
402         }
403     }
404 
405     KisNodeSP child = node->firstChild();
406     while (child) {
407         time = std::max(time, findLastKeyframeTimeRecursive(child));
408         child = child->nextSibling();
409     }
410 
411     return time;
412 }
413 
totalLength()414 int KisImageAnimationInterface::totalLength()
415 {
416     if (m_d->cachedLastFrameValue < 0) {
417         m_d->cachedLastFrameValue = findLastKeyframeTimeRecursive(m_d->image->root());
418     }
419 
420     int lastKey = m_d->cachedLastFrameValue;
421 
422     lastKey  = std::max(lastKey, m_d->fullClipRange.end());
423     lastKey  = std::max(lastKey, m_d->currentUITime());
424 
425     return lastKey + 1;
426 }
427