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"