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@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 [fro= m: 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/KisAnimatio= nCacheRegenerator.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::~KisAnimationCacheRegenera= tor() { } = +int KisAnimationCacheRegenerator::calcFirstDirtyFrame(KisAnimationFrameCac= heSP cache, const KisTimeRange &playbackRange, const KisTimeRange &skipRang= e) +{ + int result =3D -1; + + KisImageSP image =3D cache->image(); + if (!image) return result; + + KisImageAnimationInterface *animation =3D image->animationInterface(); + if (!animation->hasAnimation()) return result; + + if (playbackRange.isValid()) { + KIS_ASSERT_RECOVER_RETURN_VALUE(!playbackRange.isInfinite(), resul= t); + + // TODO: optimize check for fully-cached case + for (int frame =3D playbackRange.start(); frame <=3D playbackRange= .end(); frame++) { + if (skipRange.contains(frame)) { + if (skipRange.isInfinite()) { + break; + } else { + frame =3D skipRange.end(); + continue; + } + } + + if (cache->frameStatus(frame) !=3D KisAnimationFrameCache::Cac= hed) { + result =3D frame; + break; + } + } + } + + return result; +} + +int KisAnimationCacheRegenerator::calcNumberOfDirtyFrame(KisAnimationFrame= CacheSP cache, const KisTimeRange &playbackRange) +{ + int result =3D 0; + + KisImageSP image =3D cache->image(); + if (!image) return result; + + KisImageAnimationInterface *animation =3D image->animationInterface(); + if (!animation->hasAnimation()) return result; + + if (playbackRange.isValid()) { + KIS_ASSERT_RECOVER_RETURN_VALUE(!playbackRange.isInfinite(), resul= t); + + // TODO: optimize check for fully-cached case + for (int frame =3D playbackRange.start(); frame <=3D playbackRange= .end(); frame++) { + KisTimeRange stillFrameRange =3D KisTimeRange::infinite(0); + KisTimeRange::calculateTimeRangeRecursive(image->root(), frame= , stillFrameRange, true); + + KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(stillFrameRange.isValid()= , 0); + + if (cache->frameStatus(stillFrameRange.start()) =3D=3D KisAnim= ationFrameCache::Uncached) { + result++; + } + + if (stillFrameRange.isInfinite()) { + break; + } else { + frame =3D stillFrameRange.end(); + } + } + } + + return result; +} + void KisAnimationCacheRegenerator::startFrameRegeneration(int frame, KisAn= imationFrameCacheSP cache) { KIS_ASSERT_RECOVER_NOOP(QThread::currentThread() =3D=3D this->thread()= ); diff --git a/libs/ui/KisAnimationCacheRegenerator.h b/libs/ui/KisAnimationC= acheRegenerator.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 =3D 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_a= nimation_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 > FpsAccumulator; @@ -326,6 +329,15 @@ void KisAnimationPlayer::slotUpdatePlaybackTimer() = void KisAnimationPlayer::play() { + { + const KisImageAnimationInterface *animation =3D m_d->canvas->image= ()->animationInterface(); + const KisTimeRange &range =3D 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 =3D true; = slotUpdatePlaybackTimer(); diff --git a/libs/ui/dialogs/KisAnimationCacheUpdateProgressDialog.cpp b/li= bs/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 + * + * 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-13= 01, USA. + */ + +#include "KisAnimationCacheUpdateProgressDialog.h" + +#include +#include +#include +#include +#include +#include +#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(ca= ncelCurrentFrameRegeneration())); + } + + int busyWait; + KisAnimationCacheRegenerator regenerator; + + KisAnimationFrameCacheSP cache; + KisTimeRange playbackRange; + int dirtyFramesCount =3D 0; + int processedFramesCount =3D 0; + bool hasSomethingToDo =3D true; + + QProgressDialog progressDialog; +}; + +KisAnimationCacheUpdateProgressDialog::KisAnimationCacheUpdateProgressDial= og(int busyWait, QWidget *parent) + : QObject(parent), + m_d(new Private(busyWait, parent)) +{ + connect(&m_d->regenerator, SIGNAL(sigFrameFinished()), SLOT(slotFrameF= inished())); + connect(&m_d->regenerator, SIGNAL(sigFrameCancelled()), SLOT(slotFrame= Cancelled())); +} + +KisAnimationCacheUpdateProgressDialog::~KisAnimationCacheUpdateProgressDia= log() +{ +} + +void KisAnimationCacheUpdateProgressDialog::regenerateRange(KisAnimationFr= ameCacheSP cache, const KisTimeRange &playbackRange, KisViewManager *viewMa= nager) +{ + m_d->cache =3D cache; + m_d->playbackRange =3D playbackRange; + + m_d->dirtyFramesCount =3D 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 =3D -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 =3D cache->image(); + viewManager->blockUntillOperationsFinishedForced(image); +} + +void KisAnimationCacheUpdateProgressDialog::slotFrameFinished() +{ + m_d->processedFramesCount++; + int currentDirtyFrame =3D m_d->regenerator.calcFirstDirtyFrame(m_d->ca= che, m_d->playbackRange, KisTimeRange()); + + if (currentDirtyFrame >=3D 0) { + m_d->regenerator.startFrameRegeneration(currentDirtyFrame, m_d->ca= che); + } else { + m_d->hasSomethingToDo =3D false; + m_d->processedFramesCount =3D m_d->dirtyFramesCount; + } + + m_d->progressDialog.setValue(m_d->processedFramesCount); +} + +void KisAnimationCacheUpdateProgressDialog::slotFrameCancelled() +{ + m_d->hasSomethingToDo =3D false; + m_d->processedFramesCount =3D m_d->dirtyFramesCount; + m_d->progressDialog.setValue(m_d->processedFramesCount); +} + diff --git a/libs/ui/KisAnimationCacheRegenerator.h b/libs/ui/dialogs/KisAn= imationCacheUpdateProgressDialog.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-13= 01, USA. */ = -#ifndef KISANIMATIONCACHEREGENERATOR_H -#define KISANIMATIONCACHEREGENERATOR_H +#ifndef KISANIMATIONCACHEUPDATEPROGRESSDIALOG_H +#define KISANIMATIONCACHEUPDATEPROGRESSDIALOG_H = #include #include -#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 =3D 0); - ~KisAnimationCacheRegenerator(); + explicit KisAnimationCacheUpdateProgressDialog(int busyWait =3D 200, Q= Widget *parent =3D 0); + ~KisAnimationCacheUpdateProgressDialog(); = -public Q_SLOTS: - void startFrameRegeneration(int frame, KisAnimationFrameCacheSP cache); - void cancelCurrentFrameRegeneration(); + void regenerateRange(KisAnimationFrameCacheSP cache, const KisTimeRang= e &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 m_d; }; = -#endif // KISANIMATIONCACHEREGENERATOR_H +#endif // KISANIMATIONCACHEUPDATEPROGRESSDIALOG_H diff --git a/libs/ui/kis_animation_cache_populator.cpp b/libs/ui/kis_animat= ion_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 =3D image->animationInterfac= e(); KisTimeRange currentRange =3D animation->fullClipRange(); = - if (!animation->hasAnimation()) return false; + const int frame =3D KisAnimationCacheRegenerator::calcFirstDirtyFr= ame(cache, currentRange, skipRange); = - if (currentRange.isValid()) { - Q_ASSERT(!currentRange.isInfinite()); - - // TODO: optimize check for fully-cached case - - for (int frame =3D currentRange.start(); frame <=3D currentRan= ge.end(); frame++) { - if (skipRange.contains(frame)) { - if (skipRange.isInfinite()) { - break; - } else { - frame =3D skipRange.end(); - continue; - } - } - - if (cache->frameStatus(frame) !=3D KisAnimationFrameCache:= :Cached) { - return regenerate(cache, frame); - } - } + if (frame >=3D 0) { + return regenerate(cache, frame); } = return false;