]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/panels/information/pixmapviewer.cpp
Detect animated format using mimeType instead of file path
[dolphin.git] / src / panels / information / pixmapviewer.cpp
index 24a6295696610a886e56339fee5f43538e5efe10..39fedb1a1f6b8a4c9c209e8f7f364ce11f9ef3c8 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);
@@ -37,7 +41,7 @@ PixmapViewer::PixmapViewer(QWidget* parent, Transition transition) :
     m_animation.setCurveShape(QTimeLine::LinearCurve);
 
     if (m_transition != NoTransition) {
-        connect(&m_animation, &QTimeLine::valueChanged, this, static_cast<void(PixmapViewer::*)()>(&PixmapViewer::update));
+        connect(&m_animation, &QTimeLine::valueChanged, this, QOverload<>::of(&PixmapViewer::update));
         connect(&m_animation, &QTimeLine::finished, this, &PixmapViewer::checkPendingPixmaps);
     }
 }
@@ -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));
@@ -97,14 +141,14 @@ void PixmapViewer::paintEvent(QPaintEvent* event)
         const bool useOldPixmap = (m_transition == SizeTransition) &&
                                   (m_oldPixmap.width() > m_pixmap.width());
         const QPixmap& largePixmap = useOldPixmap ? m_oldPixmap : m_pixmap;
-       if (!largePixmap.isNull()) {
+        if (!largePixmap.isNull()) {
             const QPixmap scaledPixmap = largePixmap.scaled(scaledWidth,
                                                             scaledHeight,
                                                             Qt::IgnoreAspectRatio,
                                                             Qt::FastTransformation);
 
             style()->drawItemPixmap(&painter, rect(), Qt::AlignCenter, scaledPixmap);
-       }
+        }
     } else {
         style()->drawItemPixmap(&painter, rect(), Qt::AlignCenter, m_pixmap);
     }
@@ -112,14 +156,39 @@ void PixmapViewer::paintEvent(QPaintEvent* event)
 
 void PixmapViewer::checkPendingPixmaps()
 {
-    if (m_pendingPixmaps.count() > 0) {
+    if (!m_pendingPixmaps.isEmpty()) {
         QPixmap pixmap = m_pendingPixmaps.dequeue();
         m_oldPixmap = m_pixmap.isNull() ? pixmap : m_pixmap;
         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::isAnimatedMimeType(const QString &mimeType)
+{
+    const QList<QByteArray> imageFormats = QImageReader::imageFormatsForMimeType(mimeType.toUtf8());
+    return std::any_of(imageFormats.begin(), imageFormats.end(),
+                       [](const QByteArray &format){ return QMovie::supportedFormats().contains(format); });
+}