]> cloud.milkyroute.net Git - dolphin.git/commitdiff
[dolphin] Animate gifs on preview
authorIsmael Asensio <isma.af@mgmail.com>
Sat, 21 Sep 2019 14:15:04 +0000 (16:15 +0200)
committerElvis Angelaccio <elvis.angelaccio@kde.org>
Sat, 21 Sep 2019 14:25:50 +0000 (16:25 +0200)
Summary:
Adds the capability to view animated images on the preview in the information panel.
This was a request from a user back in 2009 (https://bugs.kde.org/show_bug.cgi?id=182257), but I think nowadays with stickers/memes and what not, it's even more useful.
It keeps the size default transition of the preview viewer before starting the animation, so that the visual integration is smoother.

{F7289110}

FEATURE: 182257
FIXED-IN: 19.11.80

Test Plan: Open the information panel and hover over some animated images (gif/webp/mng)

Reviewers: #dolphin, #vdg, ngraham, elvisangelaccio

Reviewed By: #vdg, ngraham

Subscribers: pino, fuksitter, meven, broulik, kfm-devel

Tags: #dolphin

Differential Revision: https://phabricator.kde.org/D23538

src/panels/information/informationpanelcontent.cpp
src/panels/information/pixmapviewer.cpp
src/panels/information/pixmapviewer.h

index b051603fd807242d198c574b0968af1394d04958..2a8682a127e9eb5818e86fa0c518bf14b7b529fb 100644 (file)
@@ -169,6 +169,7 @@ void InformationPanelContent::showItem(const KFileItem& item)
     if (item != m_item) {
         m_item = item;
 
+        m_preview->stopAnimatedImage();
         refreshMetaData();
     }
     refreshPreview();
@@ -237,7 +238,8 @@ void InformationPanelContent::refreshPreview()
             refreshPixmapView();
 
             const QString mimeType = m_item.mimetype();
-            m_isVideo = mimeType.startsWith(QLatin1String("video/"));
+            const bool isAnimatedImage = m_preview->isAnimatedImage(itemUrl.toLocalFile());
+            m_isVideo = !isAnimatedImage && mimeType.startsWith(QLatin1String("video/"));
             usePhonon = m_isVideo || mimeType.startsWith(QLatin1String("audio/"));
 
             if (usePhonon) {
@@ -268,6 +270,9 @@ void InformationPanelContent::refreshPreview()
                     adjustWidgetSizes(parentWidget()->width());
                 }
             } else {
+                if (isAnimatedImage) {
+                    m_preview->setAnimatedImageFileName(itemUrl.toLocalFile());
+                }
                 // When we don't need it, hide the phonon widget first to avoid flickering
                 m_phononWidget->hide();
                 m_preview->show();
@@ -276,6 +281,7 @@ void InformationPanelContent::refreshPreview()
             }
         }
     } else {
+        m_preview->stopAnimatedImage();
         m_preview->hide();
         m_phononWidget->hide();
     }
@@ -303,6 +309,8 @@ void InformationPanelContent::showItems(const KFileItemList& items)
         m_previewJob->kill();
     }
 
+    m_preview->stopAnimatedImage();
+
     m_preview->setPixmap(
         QIcon::fromTheme(QStringLiteral("dialog-information")).pixmap(KIconLoader::SizeEnormous, KIconLoader::SizeEnormous)
     );
index 311995ec2f81250e2b99f7947cb3b06fceda1b85..2601e82ae50aba1915ada813f6f4453d3adbbc4f 100644 (file)
 
 #include <KIconLoader>
 
+#include <QImageReader>
+#include <QMovie>
 #include <QPainter>
 #include <QStyle>
 
 PixmapViewer::PixmapViewer(QWidget* parent, Transition transition) :
     QWidget(parent),
+    m_animatedImage(nullptr),
     m_transition(transition),
     m_animationStep(0),
-    m_sizeHint()
+    m_sizeHint(),
+    m_hasAnimatedImage(false)
 {
     setMinimumWidth(KIconLoader::SizeEnormous);
     setMinimumHeight(KIconLoader::SizeEnormous);
@@ -52,6 +56,11 @@ void PixmapViewer::setPixmap(const QPixmap& pixmap)
         return;
     }
 
+    // Avoid flicker with static pixmap if an animated image is running
+    if (m_animatedImage && m_animatedImage->state() == QMovie::Running) {
+        return;
+    }
+
     if ((m_transition != NoTransition) && (m_animation.state() == QTimeLine::Running)) {
         m_pendingPixmaps.enqueue(pixmap);
         if (m_pendingPixmaps.count() > 5) {
@@ -65,15 +74,26 @@ void PixmapViewer::setPixmap(const QPixmap& pixmap)
     m_pixmap = pixmap;
     update();
 
-    const bool animate = (m_transition != NoTransition) &&
-                         (m_pixmap.size() != m_oldPixmap.size());
-    if (animate) {
+    const bool animateTransition = (m_transition != NoTransition) &&
+                                   (m_pixmap.size() != m_oldPixmap.size());
+    if (animateTransition) {
         m_animation.start();
+    } else if (m_hasAnimatedImage) {
+        // If there is no transition animation but an animatedImage
+        // and it is not already running, start animating now
+        if (m_animatedImage->state() != QMovie::Running) {
+            m_animatedImage->setScaledSize(m_pixmap.size());
+            m_animatedImage->start();
+        }
     }
 }
 
 void PixmapViewer::setSizeHint(const QSize& size)
 {
+    if (m_animatedImage && size != m_sizeHint) {
+        m_animatedImage->stop();
+    }
+
     m_sizeHint = size;
     updateGeometry();
 }
@@ -83,13 +103,37 @@ QSize PixmapViewer::sizeHint() const
     return m_sizeHint;
 }
 
+void PixmapViewer::setAnimatedImageFileName(const QString &fileName)
+{
+    if (!m_animatedImage) {
+        m_animatedImage = new QMovie(this);
+        connect(m_animatedImage, &QMovie::frameChanged, this, &PixmapViewer::updateAnimatedImageFrame);
+    }
+
+    if (m_animatedImage->fileName() != fileName) {
+        m_animatedImage->stop();
+        m_animatedImage->setFileName(fileName);
+    }
+
+    m_hasAnimatedImage = m_animatedImage->isValid() && (m_animatedImage->frameCount() > 1);
+}
+
+
+QString PixmapViewer::animatedImageFileName() const
+{
+    if (!m_hasAnimatedImage) {
+        return QString();
+    }
+    return m_animatedImage->fileName();
+}
+
 void PixmapViewer::paintEvent(QPaintEvent* event)
 {
     QWidget::paintEvent(event);
 
     QPainter painter(this);
 
-    if (m_transition != NoTransition) {
+    if (m_transition != NoTransition || (m_hasAnimatedImage && m_animatedImage->state() != QMovie::Running)) {
         const float value = m_animation.currentValue();
         const int scaledWidth  = static_cast<int>((m_oldPixmap.width()  * (1.0 - value)) + (m_pixmap.width()  * value));
         const int scaledHeight = static_cast<int>((m_oldPixmap.height() * (1.0 - value)) + (m_pixmap.height() * value));
@@ -118,8 +162,32 @@ void PixmapViewer::checkPendingPixmaps()
         m_pixmap = pixmap;
         update();
         m_animation.start();
+    } else if (m_hasAnimatedImage) {
+        m_animatedImage->setScaledSize(m_pixmap.size());
+        m_animatedImage->start();
     } else {
         m_oldPixmap = m_pixmap;
     }
 }
 
+void PixmapViewer::updateAnimatedImageFrame()
+{
+    Q_ASSERT (m_animatedImage);
+
+    m_pixmap = m_animatedImage->currentPixmap();
+    update();
+}
+
+void PixmapViewer::stopAnimatedImage()
+{
+    if (m_hasAnimatedImage) {
+        m_animatedImage->stop();
+        m_hasAnimatedImage = false;
+    }
+}
+
+bool PixmapViewer::isAnimatedImage(const QString &fileName)
+{
+    const QByteArray imageFormat = QImageReader::imageFormat(fileName);
+    return !imageFormat.isEmpty() && QMovie::supportedFormats().contains(imageFormat);
+}
index 46e5cf5fc7e5d3a08b574e2014e9e0bd6316ac79..37071045fb167359e734357cb82f1f9f384190eb 100644 (file)
@@ -26,6 +26,7 @@
 #include <QWidget>
 
 class QPaintEvent;
+class QMovie;
 
 /**
  * @brief Widget which shows a pixmap centered inside the boundaries.
@@ -73,20 +74,33 @@ public:
     void setSizeHint(const QSize& size);
     QSize sizeHint() const override;
 
+    void setAnimatedImageFileName(const QString& fileName);
+    QString animatedImageFileName() const;
+
+    void stopAnimatedImage();
+
+    /**
+     * Checks if \a fileName contains an animated image supported by QMovie.
+     */
+    static bool isAnimatedImage(const QString &fileName);
+
 protected:
     void paintEvent(QPaintEvent* event) override;
 
 private Q_SLOTS:
     void checkPendingPixmaps();
+    void updateAnimatedImageFrame();
 
 private:
     QPixmap m_pixmap;
     QPixmap m_oldPixmap;
+    QMovie* m_animatedImage;
     QQueue<QPixmap> m_pendingPixmaps;
     QTimeLine m_animation;
     Transition m_transition;
     int m_animationStep;
     QSize m_sizeHint;
+    bool m_hasAnimatedImage;
 };
 
 inline QPixmap PixmapViewer::pixmap() const