[krita/kazakov/svg-loading] libs/ui: Regenerate frames cache *before* starting the playback
Dmitry Kazakov
null at kde.org
Tue Apr 4 07:37:42 UTC 2017
Git commit e964c2951f1e3bbf5becf48016d2bb209c599c17 by Dmitry Kazakov.
Committed on 04/04/2017 at 07:36.
Pushed by dkazakov into branch 'kazakov/svg-loading'.
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/e964c2951f1e3bbf5becf48016d2bb209c599c17
diff --git a/libs/ui/CMakeLists.txt b/libs/ui/CMakeLists.txt
index 7f80df9e246..1e81c5ff2a6 100644
--- a/libs/ui/CMakeLists.txt
+++ b/libs/ui/CMakeLists.txt
@@ -386,6 +386,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()), ®enerator, 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