[krita/krita/3.1] libs/ui: Regenerate frames cache *before* starting the playback

Boudewijn Rempt null at kde.org
Thu Apr 6 10:03:16 UTC 2017


Git commit 6213b0e10e782056d13c90a7ad0e94f6a506616c by Boudewijn Rempt, on behalf of Dmitry Kazakov.
Committed on 06/04/2017 at 10:02.
Pushed by rempt into branch 'krita/3.1'.

Regenerate frames cache *before* starting the playback

Now when you click "Play" button, the cache is first regenerated,
and only after that, the playback starts.

BUG:373315
Fixes T4878
CC:kimageshop at kde.org

M  +1    -0    libs/ui/CMakeLists.txt
M  +70   -0    libs/ui/KisAnimationCacheRegenerator.cpp
M  +10   -0    libs/ui/KisAnimationCacheRegenerator.h
M  +12   -0    libs/ui/canvas/kis_animation_player.cpp
A  +124  -0    libs/ui/dialogs/KisAnimationCacheUpdateProgressDialog.cpp     [License: GPL (v2+)]
C  +15   -20   libs/ui/dialogs/KisAnimationCacheUpdateProgressDialog.h [from: libs/ui/KisAnimationCacheRegenerator.h - 058% similarity]
M  +3    -20   libs/ui/kis_animation_cache_populator.cpp

https://commits.kde.org/krita/6213b0e10e782056d13c90a7ad0e94f6a506616c

diff --git a/libs/ui/CMakeLists.txt b/libs/ui/CMakeLists.txt
index 84a65ffad2b..8501cf5cb57 100644
--- a/libs/ui/CMakeLists.txt
+++ b/libs/ui/CMakeLists.txt
@@ -398,6 +398,7 @@ endif()
         kis_animation_frame_cache.cpp
         kis_animation_cache_populator.cpp
         KisAnimationCacheRegenerator.cpp
+        dialogs/KisAnimationCacheUpdateProgressDialog.cpp
         canvas/kis_animation_player.cpp
         kis_animation_exporter.cpp
         kis_animation_importer.cpp
diff --git a/libs/ui/KisAnimationCacheRegenerator.cpp b/libs/ui/KisAnimationCacheRegenerator.cpp
index eaa344c0940..c40844d997d 100644
--- a/libs/ui/KisAnimationCacheRegenerator.cpp
+++ b/libs/ui/KisAnimationCacheRegenerator.cpp
@@ -28,6 +28,7 @@
 #include "kis_animation_frame_cache.h"
 #include "kis_update_info.h"
 #include "kis_signal_auto_connection.h"
+#include "kis_time_range.h"
 
 
 struct Q_DECL_HIDDEN KisAnimationCacheRegenerator::Private
@@ -59,6 +60,75 @@ KisAnimationCacheRegenerator::~KisAnimationCacheRegenerator()
 {
 }
 
+int KisAnimationCacheRegenerator::calcFirstDirtyFrame(KisAnimationFrameCacheSP cache, const KisTimeRange &playbackRange, const KisTimeRange &skipRange)
+{
+    int result = -1;
+
+    KisImageSP image = cache->image();
+    if (!image) return result;
+
+    KisImageAnimationInterface *animation = image->animationInterface();
+    if (!animation->hasAnimation()) return result;
+
+    if (playbackRange.isValid()) {
+        KIS_ASSERT_RECOVER_RETURN_VALUE(!playbackRange.isInfinite(), result);
+
+        // TODO: optimize check for fully-cached case
+        for (int frame = playbackRange.start(); frame <= playbackRange.end(); frame++) {
+            if (skipRange.contains(frame)) {
+                if (skipRange.isInfinite()) {
+                    break;
+                } else {
+                    frame = skipRange.end();
+                    continue;
+                }
+            }
+
+            if (cache->frameStatus(frame) != KisAnimationFrameCache::Cached) {
+                result = frame;
+                break;
+            }
+        }
+    }
+
+    return result;
+}
+
+int KisAnimationCacheRegenerator::calcNumberOfDirtyFrame(KisAnimationFrameCacheSP cache, const KisTimeRange &playbackRange)
+{
+    int result = 0;
+
+    KisImageSP image = cache->image();
+    if (!image) return result;
+
+    KisImageAnimationInterface *animation = image->animationInterface();
+    if (!animation->hasAnimation()) return result;
+
+    if (playbackRange.isValid()) {
+        KIS_ASSERT_RECOVER_RETURN_VALUE(!playbackRange.isInfinite(), result);
+
+        // TODO: optimize check for fully-cached case
+        for (int frame = playbackRange.start(); frame <= playbackRange.end(); frame++) {
+            KisTimeRange stillFrameRange = KisTimeRange::infinite(0);
+            KisTimeRange::calculateTimeRangeRecursive(image->root(), frame, stillFrameRange, true);
+
+            KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(stillFrameRange.isValid(), 0);
+
+            if (cache->frameStatus(stillFrameRange.start()) == KisAnimationFrameCache::Uncached) {
+                result++;
+            }
+
+            if (stillFrameRange.isInfinite()) {
+                break;
+            } else {
+                frame = stillFrameRange.end();
+            }
+        }
+    }
+
+    return result;
+}
+
 void KisAnimationCacheRegenerator::startFrameRegeneration(int frame, KisAnimationFrameCacheSP cache)
 {
     KIS_ASSERT_RECOVER_NOOP(QThread::currentThread() == this->thread());
diff --git a/libs/ui/KisAnimationCacheRegenerator.h b/libs/ui/KisAnimationCacheRegenerator.h
index f6f148b13a4..5b7fd7bc3c2 100644
--- a/libs/ui/KisAnimationCacheRegenerator.h
+++ b/libs/ui/KisAnimationCacheRegenerator.h
@@ -24,6 +24,9 @@
 #include "kritaui_export.h"
 #include "kis_types.h"
 
+class KisTimeRange;
+
+
 class KRITAUI_EXPORT KisAnimationCacheRegenerator : public QObject
 {
     Q_OBJECT
@@ -31,6 +34,13 @@ public:
     explicit KisAnimationCacheRegenerator(QObject *parent = 0);
     ~KisAnimationCacheRegenerator();
 
+    static int calcFirstDirtyFrame(KisAnimationFrameCacheSP cache,
+                                   const KisTimeRange &playbackRange,
+                                   const KisTimeRange &skipRange);
+    static int calcNumberOfDirtyFrame(KisAnimationFrameCacheSP cache,
+                                      const KisTimeRange &playbackRange);
+
+
 public Q_SLOTS:
     void startFrameRegeneration(int frame, KisAnimationFrameCacheSP cache);
     void cancelCurrentFrameRegeneration();
diff --git a/libs/ui/canvas/kis_animation_player.cpp b/libs/ui/canvas/kis_animation_player.cpp
index 5641c603deb..a944cf6347f 100644
--- a/libs/ui/canvas/kis_animation_player.cpp
+++ b/libs/ui/canvas/kis_animation_player.cpp
@@ -48,6 +48,9 @@
 #include "KisViewManager.h"
 #include "kis_icon_utils.h"
 
+#include "KisPart.h"
+#include "dialogs/KisAnimationCacheUpdateProgressDialog.h"
+
 
 using namespace boost::accumulators;
 typedef accumulator_set<qreal, stats<tag::rolling_mean> > FpsAccumulator;
@@ -326,6 +329,15 @@ void KisAnimationPlayer::slotUpdatePlaybackTimer()
 
 void KisAnimationPlayer::play()
 {
+    {
+        const KisImageAnimationInterface *animation = m_d->canvas->image()->animationInterface();
+        const KisTimeRange &range = animation->playbackRange();
+        if (!range.isValid()) return;
+
+        KisAnimationCacheUpdateProgressDialog dlg(200, KisPart::instance()->currentMainwindow());
+        dlg.regenerateRange(m_d->canvas->frameCache(), range, m_d->canvas->viewManager());
+    }
+
     m_d->playing = true;
 
     slotUpdatePlaybackTimer();
diff --git a/libs/ui/dialogs/KisAnimationCacheUpdateProgressDialog.cpp b/libs/ui/dialogs/KisAnimationCacheUpdateProgressDialog.cpp
new file mode 100644
index 00000000000..f407736725b
--- /dev/null
+++ b/libs/ui/dialogs/KisAnimationCacheUpdateProgressDialog.cpp
@@ -0,0 +1,124 @@
+/*
+ *  Copyright (c) 2017 Dmitry Kazakov <dimula73 at gmail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "KisAnimationCacheUpdateProgressDialog.h"
+
+#include <QEventLoop>
+#include <QProgressDialog>
+#include <QElapsedTimer>
+#include <QApplication>
+#include <QThread>
+#include <KisAnimationCacheRegenerator.h>
+#include "kis_animation_frame_cache.h"
+#include "kis_time_range.h"
+#include "kis_image.h"
+#include "KisViewManager.h"
+
+struct KisAnimationCacheUpdateProgressDialog::Private
+{
+    Private(int _busyWait, QWidget *parent)
+        : busyWait(_busyWait),
+          progressDialog(i18n("Regenerating cache..."), i18n("Cancel"), 0, 0, parent)
+    {
+        progressDialog.setWindowModality(Qt::ApplicationModal);
+        connect(&progressDialog, SIGNAL(canceled()), &regenerator, SLOT(cancelCurrentFrameRegeneration()));
+    }
+
+    int busyWait;
+    KisAnimationCacheRegenerator regenerator;
+
+    KisAnimationFrameCacheSP cache;
+    KisTimeRange playbackRange;
+    int dirtyFramesCount = 0;
+    int processedFramesCount = 0;
+    bool hasSomethingToDo = true;
+
+    QProgressDialog progressDialog;
+};
+
+KisAnimationCacheUpdateProgressDialog::KisAnimationCacheUpdateProgressDialog(int busyWait, QWidget *parent)
+    : QObject(parent),
+      m_d(new Private(busyWait, parent))
+{
+    connect(&m_d->regenerator, SIGNAL(sigFrameFinished()), SLOT(slotFrameFinished()));
+    connect(&m_d->regenerator, SIGNAL(sigFrameCancelled()), SLOT(slotFrameCancelled()));
+}
+
+KisAnimationCacheUpdateProgressDialog::~KisAnimationCacheUpdateProgressDialog()
+{
+}
+
+void KisAnimationCacheUpdateProgressDialog::regenerateRange(KisAnimationFrameCacheSP cache, const KisTimeRange &playbackRange, KisViewManager *viewManager)
+{
+    m_d->cache = cache;
+    m_d->playbackRange = playbackRange;
+
+    m_d->dirtyFramesCount = m_d->regenerator.calcNumberOfDirtyFrame(m_d->cache, m_d->playbackRange);
+
+    m_d->progressDialog.setMaximum(m_d->dirtyFramesCount);
+
+    // HACK ALERT: since the slot is named 'finished', so it increments
+    //             the preseccedFramesCount field on every call. And since
+    //             this is a cold-start, we should decrement it in advance.
+    m_d->processedFramesCount = -1;
+    slotFrameFinished();
+
+    QElapsedTimer t;
+    t.start();
+
+    while (t.elapsed() < m_d->busyWait) {
+        QApplication::processEvents();
+
+        if (!m_d->hasSomethingToDo) {
+            break;
+        }
+
+        QThread::yieldCurrentThread();
+    }
+
+
+    if (m_d->hasSomethingToDo) {
+        m_d->progressDialog.exec();
+    }
+
+    KisImageSP image = cache->image();
+    viewManager->blockUntillOperationsFinishedForced(image);
+}
+
+void KisAnimationCacheUpdateProgressDialog::slotFrameFinished()
+{
+    m_d->processedFramesCount++;
+    int currentDirtyFrame = m_d->regenerator.calcFirstDirtyFrame(m_d->cache, m_d->playbackRange, KisTimeRange());
+
+    if (currentDirtyFrame >= 0) {
+        m_d->regenerator.startFrameRegeneration(currentDirtyFrame, m_d->cache);
+    } else {
+        m_d->hasSomethingToDo = false;
+        m_d->processedFramesCount = m_d->dirtyFramesCount;
+    }
+
+    m_d->progressDialog.setValue(m_d->processedFramesCount);
+}
+
+void KisAnimationCacheUpdateProgressDialog::slotFrameCancelled()
+{
+    m_d->hasSomethingToDo = false;
+    m_d->processedFramesCount = m_d->dirtyFramesCount;
+    m_d->progressDialog.setValue(m_d->processedFramesCount);
+}
+
diff --git a/libs/ui/KisAnimationCacheRegenerator.h b/libs/ui/dialogs/KisAnimationCacheUpdateProgressDialog.h
similarity index 58%
copy from libs/ui/KisAnimationCacheRegenerator.h
copy to libs/ui/dialogs/KisAnimationCacheUpdateProgressDialog.h
index f6f148b13a4..5505d3953b5 100644
--- a/libs/ui/KisAnimationCacheRegenerator.h
+++ b/libs/ui/dialogs/KisAnimationCacheUpdateProgressDialog.h
@@ -16,41 +16,36 @@
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
-#ifndef KISANIMATIONCACHEREGENERATOR_H
-#define KISANIMATIONCACHEREGENERATOR_H
+#ifndef KISANIMATIONCACHEUPDATEPROGRESSDIALOG_H
+#define KISANIMATIONCACHEUPDATEPROGRESSDIALOG_H
 
 #include <QObject>
 #include <QScopedPointer>
-#include "kritaui_export.h"
+
 #include "kis_types.h"
 
-class KRITAUI_EXPORT KisAnimationCacheRegenerator : public QObject
+class KisTimeRange;
+class KisViewManager;
+
+
+class KisAnimationCacheUpdateProgressDialog : public QObject
 {
     Q_OBJECT
 public:
-    explicit KisAnimationCacheRegenerator(QObject *parent = 0);
-    ~KisAnimationCacheRegenerator();
+    explicit KisAnimationCacheUpdateProgressDialog(int busyWait = 200, QWidget *parent = 0);
+    ~KisAnimationCacheUpdateProgressDialog();
 
-public Q_SLOTS:
-    void startFrameRegeneration(int frame, KisAnimationFrameCacheSP cache);
-    void cancelCurrentFrameRegeneration();
+    void regenerateRange(KisAnimationFrameCacheSP cache, const KisTimeRange &playbackRange, KisViewManager *viewManager);
 
 Q_SIGNALS:
-    void sigFrameCancelled();
-    void sigFrameFinished();
-
-    void sigInternalStartFrameConversion();
-
-private Q_SLOTS:
-    void slotFrameRegenerationCancelled();
-    void slotFrameRegenerationFinished(int frame);
-    void slotFrameStartConversion();
-    void slotFrameConverted();
 
+public Q_SLOTS:
+    void slotFrameFinished();
+    void slotFrameCancelled();
 
 private:
     struct Private;
     const QScopedPointer<Private> m_d;
 };
 
-#endif // KISANIMATIONCACHEREGENERATOR_H
+#endif // KISANIMATIONCACHEUPDATEPROGRESSDIALOG_H
diff --git a/libs/ui/kis_animation_cache_populator.cpp b/libs/ui/kis_animation_cache_populator.cpp
index 280fd1c1347..f2913299da5 100644
--- a/libs/ui/kis_animation_cache_populator.cpp
+++ b/libs/ui/kis_animation_cache_populator.cpp
@@ -178,27 +178,10 @@ struct KisAnimationCachePopulator::Private
         KisImageAnimationInterface *animation = image->animationInterface();
         KisTimeRange currentRange = animation->fullClipRange();
 
-        if (!animation->hasAnimation()) return false;
+        const int frame = KisAnimationCacheRegenerator::calcFirstDirtyFrame(cache, currentRange, skipRange);
 
-        if (currentRange.isValid()) {
-            Q_ASSERT(!currentRange.isInfinite());
-
-            // TODO: optimize check for fully-cached case
-
-            for (int frame = currentRange.start(); frame <= currentRange.end(); frame++) {
-                if (skipRange.contains(frame)) {
-                    if (skipRange.isInfinite()) {
-                        break;
-                    } else {
-                        frame = skipRange.end();
-                        continue;
-                    }
-                }
-
-                if (cache->frameStatus(frame) != KisAnimationFrameCache::Cached) {
-                    return regenerate(cache, frame);
-                }
-            }
+        if (frame >= 0) {
+            return regenerate(cache, frame);
         }
 
         return false;



More information about the kimageshop mailing list