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