File indexing completed on 2024-05-12 17:18:50

0001 /*
0002  * SPDX-FileCopyrightText: 2006 Peter Penz <peter.penz19@gmail.com>
0003  *
0004  * SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "pixmapviewer.h"
0008 
0009 #include <KIconLoader>
0010 
0011 #include <QImageReader>
0012 #include <QMovie>
0013 #include <QPainter>
0014 #include <QStyle>
0015 
0016 PixmapViewer::PixmapViewer(QWidget *parent, Transition transition)
0017     : QWidget(parent)
0018     , m_animatedImage(nullptr)
0019     , m_transition(transition)
0020     , m_animationStep(0)
0021     , m_sizeHint()
0022     , m_hasAnimatedImage(false)
0023 {
0024     setMinimumWidth(KIconLoader::SizeEnormous);
0025     setMinimumHeight(KIconLoader::SizeEnormous);
0026 
0027     m_animation.setDuration(150);
0028     m_animation.setEasingCurve(QEasingCurve::Linear);
0029 
0030     if (m_transition != NoTransition) {
0031         connect(&m_animation, &QTimeLine::valueChanged, this, QOverload<>::of(&PixmapViewer::update));
0032         connect(&m_animation, &QTimeLine::finished, this, &PixmapViewer::checkPendingPixmaps);
0033     }
0034 }
0035 
0036 PixmapViewer::~PixmapViewer()
0037 {
0038 }
0039 
0040 void PixmapViewer::setPixmap(const QPixmap &pixmap)
0041 {
0042     if (pixmap.isNull()) {
0043         return;
0044     }
0045 
0046     // Avoid flicker with static pixmap if an animated image is running
0047     if (m_animatedImage && m_animatedImage->state() == QMovie::Running) {
0048         return;
0049     }
0050 
0051     if ((m_transition != NoTransition) && (m_animation.state() == QTimeLine::Running)) {
0052         m_pendingPixmaps.enqueue(pixmap);
0053         if (m_pendingPixmaps.count() > 5) {
0054             // don't queue more than 5 pixmaps
0055             m_pendingPixmaps.takeFirst();
0056         }
0057         return;
0058     }
0059 
0060     m_oldPixmap = m_pixmap.isNull() ? pixmap : m_pixmap;
0061     m_pixmap = pixmap;
0062     update();
0063 
0064     const bool animateTransition = (m_transition != NoTransition) && (m_pixmap.size() != m_oldPixmap.size());
0065     if (animateTransition) {
0066         m_animation.start();
0067     } else if (m_hasAnimatedImage) {
0068         // If there is no transition animation but an animatedImage
0069         // and it is not already running, start animating now
0070         if (m_animatedImage->state() != QMovie::Running) {
0071             m_animatedImage->setScaledSize(m_pixmap.size());
0072             m_animatedImage->start();
0073         }
0074     }
0075 }
0076 
0077 void PixmapViewer::setSizeHint(const QSize &size)
0078 {
0079     if (m_animatedImage && size != m_sizeHint) {
0080         m_animatedImage->stop();
0081     }
0082 
0083     m_sizeHint = size;
0084     updateGeometry();
0085 }
0086 
0087 QSize PixmapViewer::sizeHint() const
0088 {
0089     return m_sizeHint;
0090 }
0091 
0092 void PixmapViewer::setAnimatedImageFileName(const QString &fileName)
0093 {
0094     if (!m_animatedImage) {
0095         m_animatedImage = new QMovie(this);
0096         connect(m_animatedImage, &QMovie::frameChanged, this, &PixmapViewer::updateAnimatedImageFrame);
0097     }
0098 
0099     if (m_animatedImage->fileName() != fileName) {
0100         m_animatedImage->stop();
0101         m_animatedImage->setFileName(fileName);
0102     }
0103 
0104     m_hasAnimatedImage = m_animatedImage->isValid() && (m_animatedImage->frameCount() > 1);
0105 }
0106 
0107 QString PixmapViewer::animatedImageFileName() const
0108 {
0109     if (!m_hasAnimatedImage) {
0110         return QString();
0111     }
0112     return m_animatedImage->fileName();
0113 }
0114 
0115 void PixmapViewer::paintEvent(QPaintEvent *event)
0116 {
0117     QWidget::paintEvent(event);
0118 
0119     QPainter painter(this);
0120 
0121     if (m_transition != NoTransition || (m_hasAnimatedImage && m_animatedImage->state() != QMovie::Running)) {
0122         const float value = m_animation.currentValue();
0123         const int scaledWidth = static_cast<int>((m_oldPixmap.width() * (1.0 - value)) + (m_pixmap.width() * value));
0124         const int scaledHeight = static_cast<int>((m_oldPixmap.height() * (1.0 - value)) + (m_pixmap.height() * value));
0125 
0126         const bool useOldPixmap = (m_transition == SizeTransition) && (m_oldPixmap.width() > m_pixmap.width());
0127         const QPixmap &largePixmap = useOldPixmap ? m_oldPixmap : m_pixmap;
0128         if (!largePixmap.isNull()) {
0129             const QPixmap scaledPixmap = largePixmap.scaled(scaledWidth, scaledHeight, Qt::IgnoreAspectRatio, Qt::FastTransformation);
0130 
0131             style()->drawItemPixmap(&painter, rect(), Qt::AlignCenter, scaledPixmap);
0132         }
0133     } else if (!m_pixmap.isNull()) {
0134         style()->drawItemPixmap(&painter, rect(), Qt::AlignCenter, m_pixmap);
0135     }
0136 }
0137 
0138 void PixmapViewer::checkPendingPixmaps()
0139 {
0140     if (!m_pendingPixmaps.isEmpty()) {
0141         QPixmap pixmap = m_pendingPixmaps.dequeue();
0142         m_oldPixmap = m_pixmap.isNull() ? pixmap : m_pixmap;
0143         m_pixmap = pixmap;
0144         update();
0145         m_animation.start();
0146     } else if (m_hasAnimatedImage) {
0147         m_animatedImage->setScaledSize(m_pixmap.size());
0148         m_animatedImage->start();
0149     } else {
0150         m_oldPixmap = m_pixmap;
0151     }
0152 }
0153 
0154 void PixmapViewer::updateAnimatedImageFrame()
0155 {
0156     Q_ASSERT(m_animatedImage);
0157 
0158     m_pixmap = m_animatedImage->currentPixmap();
0159     update();
0160 }
0161 
0162 void PixmapViewer::stopAnimatedImage()
0163 {
0164     if (m_hasAnimatedImage) {
0165         m_animatedImage->stop();
0166         m_hasAnimatedImage = false;
0167     }
0168 }
0169 
0170 bool PixmapViewer::isAnimatedMimeType(const QString &mimeType)
0171 {
0172     const QList<QByteArray> imageFormats = QImageReader::imageFormatsForMimeType(mimeType.toUtf8());
0173     return std::any_of(imageFormats.begin(), imageFormats.end(), [](const QByteArray &format) {
0174         return QMovie::supportedFormats().contains(format);
0175     });
0176 }
0177 
0178 #include "moc_pixmapviewer.cpp"