From ecda842bf2b632f75198d6cf0fac1fcb5ef862ad Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Mardelle Date: Tue, 28 Jun 2011 10:37:23 +0000 Subject: [PATCH] Rewrite generation of timeline thumbnails when zooming at frame level, using separate thread so UI is still responsive. svn path=/trunk/kdenlive/; revision=5740 --- src/clipitem.cpp | 55 ++++++++++++++++++++++++++--------------- src/clipitem.h | 3 ++- src/clipmanager.cpp | 10 ++++++++ src/clipmanager.h | 10 ++++++++ src/customtrackview.cpp | 7 ------ src/customtrackview.h | 11 --------- src/docclipbase.cpp | 2 ++ src/kthumb.cpp | 39 ++++++++++++++++++++++++++++- src/kthumb.h | 18 ++++++++++++++ 9 files changed, 115 insertions(+), 40 deletions(-) diff --git a/src/clipitem.cpp b/src/clipitem.cpp index 07878af2..d59a562d 100644 --- a/src/clipitem.cpp +++ b/src/clipitem.cpp @@ -756,11 +756,28 @@ void ClipItem::paint(QPainter *painter, QLineF l2(mapped.left() + m_startPix.width(), mapped.top(), mapped.left() + m_startPix.width(), mapped.bottom()); painter->drawLine(l2); } - if (painter->matrix().m11() == FRAME_SIZE) { + if (painter->matrix().m11() == FRAME_SIZE && m_clip->thumbProducer() && clipType() != COLOR && clipType() != AUDIO && !m_audioOnly) { int offset = (m_info.startPos - m_info.cropStart).frames(m_fps); - int left = qMax((int) m_info.startPos.frames(m_fps) + 1, (int) mapToScene(exposed.left(), 0).x()); - int right = qMin((int)(m_info.startPos + m_info.cropDuration).frames(m_fps) - 1, (int) mapToScene(exposed.right(), 0).x()); - doGetIntraThumbs(painter, mapped.topLeft(), m_info.cropStart.frames(m_fps), left - offset, right - offset); + int left = qMax((int) m_info.startPos.frames(m_fps) + 1, (int) mapToScene(exposed.left(), 0).x()) - offset; + int right = qMin((int)(m_info.startPos + m_info.cropDuration).frames(m_fps) - 1, (int) mapToScene(exposed.right(), 0).x()) - offset; + QPointF startPos = mapped.topLeft(); + int twidth = FRAME_SIZE; + int startOffset = m_info.cropStart.frames(m_fps); + if (clipType() == IMAGE || clipType() == TEXT) { + for (int i = left; i <= right; i++) { + painter->drawPixmap(startPos + QPointF(twidth *(i - startOffset), 0), m_startPix); + } + } + else { +#if KDE_IS_VERSION(4,5,0) + m_clip->thumbProducer()->queryIntraThumbs(left, right); + connect(m_clip->thumbProducer(), SIGNAL(thumbsCached()), this, SLOT(slotGotThumbsCache())); + QString path = m_clip->fileURL().path() + "%"; + for (int i = left; i <= right; i++) { + painter->drawImage(startPos + QPointF(twidth *(i - startOffset), 0), m_clip->thumbProducer()->findCachedThumb(path + QString::number(i))); + } +#endif + } } painter->setPen(Qt::black); } @@ -1707,35 +1724,27 @@ void ClipItem::updateKeyframes(QDomElement effect) if (!m_keyframes.contains(m_selectedKeyframe)) m_selectedKeyframe = -1; } -void ClipItem::doGetIntraThumbs(QPainter *painter, const QPointF startPos, int offset, int start, int end) +/*void ClipItem::slotGetIntraThumbs(CustomTrackView *view, int start, int end) { - if (!m_clip->thumbProducer() || clipType() == COLOR) return; - if (scene() && scene()->views().isEmpty()) return; - CustomTrackView *view = (CustomTrackView *) scene()->views()[0]; - if (view == NULL) return; const int theight = KdenliveSettings::trackheight(); const int twidth = FRAME_SIZE; - - if (clipType() == IMAGE || clipType() == TEXT) { - for (int i = start; i <= end; i++) - painter->drawPixmap(startPos + QPointF(twidth *(i - offset), 0), m_startPix); - } + QString path = m_clip->fileURL().path() + "%"; QPixmap p; for (int i = start; i <= end; i++) { #if KDE_IS_VERSION(4,5,0) - if (!view->m_pixmapCache->findPixmap(m_clip->fileURL().path() + "%" + QString::number(i), &p)) { + if (!view->m_pixmapCache->contains(path + QString::number(i))) { p = m_clip->extractImage(i, twidth, theight); - view->m_pixmapCache->insertPixmap(m_clip->fileURL().path() + "%" + QString::number(i), p); + view->m_pixmapCache->insertPixmap(path + QString::number(i), p); } #else - if (!view->m_pixmapCache->find(m_clip->fileURL().path() + "%" + QString::number(i), p)) { + if (!view->m_pixmapCache->find(path + QString::number(i), p)) { p = m_clip->extractImage(i, twidth, theight); - view->m_pixmapCache->insert(m_clip->fileURL().path() + "%" + QString::number(i), p); + view->m_pixmapCache->insert(path + QString::number(i), p); } #endif - painter->drawPixmap(startPos + QPointF(twidth *(i - offset), 0), p); } -} + update(); +}*/ QList ClipItem::updatePanZoom(int width, int height, int cut) { @@ -1930,4 +1939,10 @@ void ClipItem::updateGeometryKeyframes(QDomElement effect, int paramIndex, int w param.setAttribute("value", geometry.serialise(cropStart().frames(m_fps), (cropStart() + cropDuration()).frames(m_fps) - 1)); } +void ClipItem::slotGotThumbsCache() +{ + disconnect(m_clip->thumbProducer(), SIGNAL(thumbsCached()), this, SLOT(slotGotThumbsCache())); + update(); +} + #include "clipitem.moc" diff --git a/src/clipitem.h b/src/clipitem.h index 87b9baa6..7e49dfdb 100644 --- a/src/clipitem.h +++ b/src/clipitem.h @@ -226,7 +226,6 @@ private: QPixmap m_videoPix; QPixmap m_audioPix; - void doGetIntraThumbs(QPainter *painter, const QPointF startPos, int offset, int start, int end); private slots: void slotGetStartThumb(); @@ -237,6 +236,8 @@ private slots: void slotSetStartThumb(QImage img); void slotSetEndThumb(QImage img); void slotThumbReady(int frame, QImage img); + /** @brief The thumbnailer has finished to cache all required thumbs. */ + void slotGotThumbsCache(); public slots: void slotFetchThumbs(); diff --git a/src/clipmanager.cpp b/src/clipmanager.cpp index a1ebafcb..2a4187d6 100644 --- a/src/clipmanager.cpp +++ b/src/clipmanager.cpp @@ -52,6 +52,10 @@ ClipManager::ClipManager(KdenliveDoc *doc) : connect(&m_fileWatcher, SIGNAL(deleted(const QString &)), this, SLOT(slotClipMissing(const QString &))); connect(&m_fileWatcher, SIGNAL(created(const QString &)), this, SLOT(slotClipAvailable(const QString &))); connect(&m_modifiedTimer, SIGNAL(timeout()), this, SLOT(slotProcessModifiedClips())); + +#if KDE_IS_VERSION(4,5,0) + pixmapCache = new KImageCache("kdenlive-thumbs", 1000000); +#endif } ClipManager::~ClipManager() @@ -60,6 +64,9 @@ ClipManager::~ClipManager() m_generatingAudioId.clear(); qDeleteAll(m_clipList); m_clipList.clear(); +#if KDE_IS_VERSION(4,5,0) + delete pixmapCache; +#endif } void ClipManager::clear() @@ -71,6 +78,9 @@ void ClipManager::clear() m_clipList.clear(); m_clipIdCounter = 1; m_folderIdCounter = 1; +#if KDE_IS_VERSION(4,5,0) + pixmapCache->clear(); +#endif } void ClipManager::checkAudioThumbs() diff --git a/src/clipmanager.h b/src/clipmanager.h index b9374d21..f9ac1722 100644 --- a/src/clipmanager.h +++ b/src/clipmanager.h @@ -35,6 +35,12 @@ #include #include #include +#include + +#if KDE_IS_VERSION(4,5,0) +#include +#endif + #include "gentime.h" #include "definitions.h" @@ -104,6 +110,10 @@ Q_OBJECT public: QDomElement groupsXml() const; int clipsCount() const; +#if KDE_IS_VERSION(4,5,0) + KImageCache* pixmapCache; +#endif + private slots: /** A clip was externally modified, monitor for more changes and prepare for reload */ void slotClipModified(const QString &path); diff --git a/src/customtrackview.cpp b/src/customtrackview.cpp index 1f8cc7aa..766dc247 100644 --- a/src/customtrackview.cpp +++ b/src/customtrackview.cpp @@ -142,12 +142,6 @@ CustomTrackView::CustomTrackView(KdenliveDoc *doc, CustomTrackScene* projectscen m_activeTrackBrush = KStatefulBrush(KColorScheme::View, KColorScheme::ActiveBackground, KSharedConfig::openConfig(KdenliveSettings::colortheme())); -#if KDE_IS_VERSION(4,5,0) - m_pixmapCache = new KImageCache("kdenlive-thumbs", 1000000); -#else - m_pixmapCache = new KPixmapCache("kdenlive-thumbs"); -#endif - m_animationTimer = new QTimeLine(800); m_animationTimer->setFrameRange(0, 5); m_animationTimer->setUpdateInterval(100); @@ -192,7 +186,6 @@ CustomTrackView::~CustomTrackView() qDeleteAll(m_guides); m_guides.clear(); m_waitingThumbs.clear(); - delete m_pixmapCache; delete m_animationTimer; } diff --git a/src/customtrackview.h b/src/customtrackview.h index a88286fe..9312566c 100644 --- a/src/customtrackview.h +++ b/src/customtrackview.h @@ -22,12 +22,6 @@ #define CUSTOMTRACKVIEW_H #include -#if KDE_IS_VERSION(4,5,0) -#include -#else -#include -#endif - #include #include @@ -184,11 +178,6 @@ public: void clearSelection(); void editItemDuration(); void buildGuidesMenu(QMenu *goMenu) const; -#if KDE_IS_VERSION(4,5,0) - KImageCache* m_pixmapCache; -#else - KPixmapCache* m_pixmapCache; -#endif /** update the timeline objects when palette changes */ void updatePalette(); /** @brief Returns true if a track has audio data on it. diff --git a/src/docclipbase.cpp b/src/docclipbase.cpp index 677eee7e..cb63298a 100644 --- a/src/docclipbase.cpp +++ b/src/docclipbase.cpp @@ -1183,3 +1183,5 @@ QPixmap DocClipBase::extractImage(int frame, int width, int height) return p; } + + diff --git a/src/kthumb.cpp b/src/kthumb.cpp index 3208ea4d..29ac0f06 100644 --- a/src/kthumb.cpp +++ b/src/kthumb.cpp @@ -57,18 +57,22 @@ KThumb::KThumb(ClipManager *clipManager, KUrl url, const QString &id, const QStr KThumb::~KThumb() { m_requestedThumbs.clear(); + m_intraFramesQueue.clear(); if (m_audioThumbProducer.isRunning()) { m_stopAudioThumbs = true; m_audioThumbProducer.waitForFinished(); slotAudioThumbOver(); } m_future.waitForFinished(); + m_intra.waitForFinished(); } void KThumb::setProducer(Mlt::Producer *producer) { m_requestedThumbs.clear(); + m_intraFramesQueue.clear(); m_future.waitForFinished(); + m_intra.waitForFinished(); m_producer = producer; // FIXME: the profile() call leaks an object, but trying to free // it leads to a double-free in Profile::~Profile() @@ -148,7 +152,6 @@ QPixmap KThumb::getImage(KUrl url, int frame, int width, int height) return pix; } - //static QImage KThumb::getFrame(Mlt::Producer *producer, int framepos, int width, int height) { @@ -445,6 +448,40 @@ void KThumb::askForAudioThumbs(const QString &id) m_clipManager->askForAudioThumb(id); } +#if KDE_IS_VERSION(4,5,0) +void KThumb::queryIntraThumbs(int start, int end) +{ + for (int i = start; i <= end; i++) { + if (!m_intraFramesQueue.contains(i)) m_intraFramesQueue.append(i); + } + qSort(m_intraFramesQueue); + if (!m_intra.isRunning()) m_intra = QtConcurrent::run(this, &KThumb::slotGetIntraThumbs); +} + +void KThumb::slotGetIntraThumbs() +{ + int theight = KdenliveSettings::trackheight(); + int twidth = FRAME_SIZE; + QString path = m_url.path() + "%"; + QImage img; + + while (!m_intraFramesQueue.isEmpty()) { + int pos = m_intraFramesQueue.takeFirst(); + if (!m_clipManager->pixmapCache->contains(path + QString::number(pos))) { + m_clipManager->pixmapCache->insertImage(path + QString::number(pos), getFrame(m_producer, pos, twidth, theight)); + } + m_intraFramesQueue.removeAll(pos); + } + emit thumbsCached(); +} + +QImage KThumb::findCachedThumb(const QString path) +{ + QImage img; + m_clipManager->pixmapCache->findImage(path, &img); + return img; +} +#endif #include "kthumb.moc" diff --git a/src/kthumb.h b/src/kthumb.h index 48900175..1d71582d 100644 --- a/src/kthumb.h +++ b/src/kthumb.h @@ -24,6 +24,7 @@ #include #include +#include #include @@ -62,6 +63,12 @@ Q_OBJECT public: void updateThumbUrl(const QString &hash); void extractImage(int frame, int frame2); QPixmap extractImage(int frame, int width, int height); +#if KDE_IS_VERSION(4,5,0) + /** @brief Request thumbnails for the frame range. */ + void queryIntraThumbs(int start, int end); + /** @brief Query cached thumbnail. */ + QImage findCachedThumb(const QString path); +#endif public slots: void updateClipUrl(KUrl url, const QString &hash); @@ -82,6 +89,10 @@ public slots: private slots: void slotAudioThumbOver(); void slotCreateAudioThumbs(); +#if KDE_IS_VERSION(4,5,0) + /** @brief Fetch all requested frames. */ + void slotGetIntraThumbs(); +#endif private: QFuture m_audioThumbProducer; @@ -92,7 +103,10 @@ private: ClipManager *m_clipManager; QString m_id; QList m_requestedThumbs; + /** @brief Controls the thumbnails process. */ QFuture m_future; + /** @brief Controls the intra frames thumbnails process (cached thumbnails). */ + QFuture m_intra; QFile m_audioThumbFile; bool m_stopAudioThumbs; double m_frame; @@ -100,12 +114,16 @@ private: int m_frequency; int m_channels; int m_arrayWidth; + /** @brief List of frame numbers from which we want to extract thumbnails. */ + QList m_intraFramesQueue; void doGetThumbs(); signals: void thumbReady(int, QImage); void mainThumbReady(const QString &, QPixmap); void audioThumbReady(QMap >); + /** @brief We have finished caching all requested thumbs. */ + void thumbsCached(); }; #endif -- 2.39.2