1 /*
2  *  Copyright (c) 2017 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 "KisAsyncAnimationCacheRenderDialog.h"
20 
21 #include "KisAsyncAnimationCacheRenderer.h"
22 #include "kis_animation_frame_cache.h"
23 #include <kis_time_range.h>
24 #include <kis_image.h>
25 #include <kis_image_animation_interface.h>
26 
27 namespace {
28 
calcDirtyFramesList(KisAnimationFrameCacheSP cache,const KisTimeRange & playbackRange)29 QList<int> calcDirtyFramesList(KisAnimationFrameCacheSP cache, const KisTimeRange &playbackRange)
30 {
31     QList<int> result;
32 
33     KisImageSP image = cache->image();
34     if (!image) return result;
35 
36     KisImageAnimationInterface *animation = image->animationInterface();
37     if (!animation->hasAnimation()) return result;
38 
39     if (playbackRange.isValid()) {
40         KIS_ASSERT_RECOVER_RETURN_VALUE(!playbackRange.isInfinite(), result);
41 
42         // TODO: optimize check for fully-cached case
43         for (int frame = playbackRange.start(); frame <= playbackRange.end(); frame++) {
44             const KisTimeRange stillFrameRange =
45                 KisTimeRange::calculateIdenticalFramesRecursive(image->root(), frame);
46 
47             KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(stillFrameRange.isValid(), result);
48 
49             if (cache->frameStatus(stillFrameRange.start()) == KisAnimationFrameCache::Uncached) {
50                 result.append(stillFrameRange.start());
51             }
52 
53             if (stillFrameRange.isInfinite()) {
54                 break;
55             } else {
56                 frame = stillFrameRange.end();
57             }
58         }
59     }
60 
61     return result;
62 }
63 
64 }
65 
calcFirstDirtyFrame(KisAnimationFrameCacheSP cache,const KisTimeRange & playbackRange,const KisTimeRange & skipRange)66 int KisAsyncAnimationCacheRenderDialog::calcFirstDirtyFrame(KisAnimationFrameCacheSP cache, const KisTimeRange &playbackRange, const KisTimeRange &skipRange)
67 {
68     int result = -1;
69 
70     KisImageSP image = cache->image();
71     if (!image) return result;
72 
73     KisImageAnimationInterface *animation = image->animationInterface();
74     if (!animation->hasAnimation()) return result;
75 
76     if (playbackRange.isValid()) {
77         KIS_ASSERT_RECOVER_RETURN_VALUE(!playbackRange.isInfinite(), result);
78 
79         // TODO: optimize check for fully-cached case
80         for (int frame = playbackRange.start(); frame <= playbackRange.end(); frame++) {
81             if (skipRange.contains(frame)) {
82                 if (skipRange.isInfinite()) {
83                     break;
84                 } else {
85                     frame = skipRange.end();
86                     continue;
87                 }
88             }
89 
90             if (cache->frameStatus(frame) != KisAnimationFrameCache::Cached) {
91                 result = frame;
92                 break;
93             }
94         }
95     }
96 
97     return result;
98 }
99 
100 
101 struct KisAsyncAnimationCacheRenderDialog::Private
102 {
PrivateKisAsyncAnimationCacheRenderDialog::Private103     Private(KisAnimationFrameCacheSP _cache, const KisTimeRange &_range)
104         : cache(_cache),
105           range(_range)
106     {
107     }
108 
109     KisAnimationFrameCacheSP cache;
110     KisTimeRange range;
111 };
112 
KisAsyncAnimationCacheRenderDialog(KisAnimationFrameCacheSP cache,const KisTimeRange & range,int busyWait)113 KisAsyncAnimationCacheRenderDialog::KisAsyncAnimationCacheRenderDialog(KisAnimationFrameCacheSP cache, const KisTimeRange &range, int busyWait)
114     : KisAsyncAnimationRenderDialogBase(i18n("Regenerating cache..."), cache->image(), busyWait),
115       m_d(new Private(cache, range))
116 {
117 }
118 
~KisAsyncAnimationCacheRenderDialog()119 KisAsyncAnimationCacheRenderDialog::~KisAsyncAnimationCacheRenderDialog()
120 {
121 
122 }
123 
calcDirtyFrames() const124 QList<int> KisAsyncAnimationCacheRenderDialog::calcDirtyFrames() const
125 {
126     return calcDirtyFramesList(m_d->cache, m_d->range);
127 }
128 
createRenderer(KisImageSP image)129 KisAsyncAnimationRendererBase *KisAsyncAnimationCacheRenderDialog::createRenderer(KisImageSP image)
130 {
131     Q_UNUSED(image);
132     return new KisAsyncAnimationCacheRenderer();
133 }
134 
initializeRendererForFrame(KisAsyncAnimationRendererBase * renderer,KisImageSP image,int frame)135 void KisAsyncAnimationCacheRenderDialog::initializeRendererForFrame(KisAsyncAnimationRendererBase *renderer, KisImageSP image, int frame)
136 {
137     Q_UNUSED(image);
138     Q_UNUSED(frame);
139 
140     KisAsyncAnimationCacheRenderer *cacheRenderer =
141         dynamic_cast<KisAsyncAnimationCacheRenderer*>(renderer);
142 
143     KIS_SAFE_ASSERT_RECOVER_RETURN(cacheRenderer);
144 
145     cacheRenderer->setFrameCache(m_d->cache);
146 }
147 
148