]> cloud.milkyroute.net Git - dolphin.git/blob - src/panels/information/pixmapviewer.cpp
InformationPanel: prevent animated images from glitching
[dolphin.git] / src / panels / information / pixmapviewer.cpp
1 /*
2 * SPDX-FileCopyrightText: 2006 Peter Penz <peter.penz19@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 #include "pixmapviewer.h"
8
9 #include <KIconLoader>
10
11 #include <QImageReader>
12 #include <QMovie>
13 #include <QPainter>
14 #include <QStyle>
15
16 PixmapViewer::PixmapViewer(QWidget *parent, Transition transition)
17 : QWidget(parent)
18 , m_animatedImage(nullptr)
19 , m_transition(transition)
20 , m_animationStep(0)
21 , m_sizeHint()
22 , m_hasAnimatedImage(false)
23 {
24 setMinimumWidth(KIconLoader::SizeEnormous);
25 setMinimumHeight(KIconLoader::SizeEnormous);
26
27 m_animation.setDuration(150);
28 m_animation.setEasingCurve(QEasingCurve::Linear);
29
30 if (m_transition != NoTransition) {
31 connect(&m_animation, &QTimeLine::valueChanged, this, QOverload<>::of(&PixmapViewer::update));
32 connect(&m_animation, &QTimeLine::finished, this, &PixmapViewer::checkPendingPixmaps);
33 }
34 }
35
36 PixmapViewer::~PixmapViewer()
37 {
38 }
39
40 void PixmapViewer::setPixmap(const QPixmap &pixmap)
41 {
42 if (pixmap.isNull()) {
43 return;
44 }
45
46 // Avoid flicker with static pixmap if an animated image is running
47 if (m_animatedImage) {
48 m_animatedImage->setScaledSize(pixmap.size());
49 if (m_animatedImage->state() == QMovie::Running) {
50 return;
51 }
52 }
53
54 if ((m_transition != NoTransition) && (m_animation.state() == QTimeLine::Running)) {
55 m_pendingPixmaps.enqueue(pixmap);
56 if (m_pendingPixmaps.count() > 5) {
57 // don't queue more than 5 pixmaps
58 m_pendingPixmaps.takeFirst();
59 }
60 return;
61 }
62
63 m_oldPixmap = m_pixmap.isNull() ? pixmap : m_pixmap;
64 m_pixmap = pixmap;
65 update();
66
67 const bool animateTransition = (m_transition != NoTransition) && (m_pixmap.size() != m_oldPixmap.size());
68 if (animateTransition) {
69 m_animation.start();
70 } else if (m_hasAnimatedImage) {
71 // If there is no transition animation but an animatedImage
72 // and it is not already running, start animating now
73 if (m_animatedImage->state() != QMovie::Running) {
74 m_animatedImage->start();
75 }
76 }
77 }
78
79 void PixmapViewer::setSizeHint(const QSize &size)
80 {
81 if (m_animatedImage && size != m_sizeHint) {
82 m_animatedImage->stop();
83 }
84
85 m_sizeHint = size;
86 updateGeometry();
87 }
88
89 QSize PixmapViewer::sizeHint() const
90 {
91 return m_sizeHint;
92 }
93
94 void PixmapViewer::setAnimatedImageFileName(const QString &fileName)
95 {
96 if (!m_animatedImage) {
97 m_animatedImage = new QMovie(this);
98 connect(m_animatedImage, &QMovie::frameChanged, this, &PixmapViewer::updateAnimatedImageFrame);
99 }
100
101 if (m_animatedImage->fileName() != fileName) {
102 m_animatedImage->stop();
103 m_animatedImage->setFileName(fileName);
104 }
105
106 m_hasAnimatedImage = m_animatedImage->isValid() && (m_animatedImage->frameCount() > 1);
107 }
108
109 QString PixmapViewer::animatedImageFileName() const
110 {
111 if (!m_hasAnimatedImage) {
112 return QString();
113 }
114 return m_animatedImage->fileName();
115 }
116
117 void PixmapViewer::paintEvent(QPaintEvent *event)
118 {
119 QWidget::paintEvent(event);
120
121 QPainter painter(this);
122
123 if (m_transition != NoTransition || (m_hasAnimatedImage && m_animatedImage->state() != QMovie::Running)) {
124 const float value = m_animation.currentValue();
125 const int scaledWidth = static_cast<int>((m_oldPixmap.width() * (1.0 - value)) + (m_pixmap.width() * value));
126 const int scaledHeight = static_cast<int>((m_oldPixmap.height() * (1.0 - value)) + (m_pixmap.height() * value));
127
128 const bool useOldPixmap = (m_transition == SizeTransition) && (m_oldPixmap.width() > m_pixmap.width());
129 const QPixmap &largePixmap = useOldPixmap ? m_oldPixmap : m_pixmap;
130 if (!largePixmap.isNull()) {
131 QPixmap scaledPixmap = largePixmap.scaled(scaledWidth, scaledHeight, Qt::IgnoreAspectRatio, Qt::FastTransformation);
132 scaledPixmap.setDevicePixelRatio(devicePixelRatioF());
133
134 style()->drawItemPixmap(&painter, rect(), Qt::AlignCenter, scaledPixmap);
135 }
136 } else if (!m_pixmap.isNull()) {
137 style()->drawItemPixmap(&painter, rect(), Qt::AlignCenter, m_pixmap);
138 }
139 }
140
141 void PixmapViewer::checkPendingPixmaps()
142 {
143 if (!m_pendingPixmaps.isEmpty()) {
144 QPixmap pixmap = m_pendingPixmaps.dequeue();
145 m_oldPixmap = m_pixmap.isNull() ? pixmap : m_pixmap;
146 m_pixmap = pixmap;
147 update();
148 m_animation.start();
149 } else if (m_hasAnimatedImage) {
150 m_animatedImage->start();
151 } else {
152 m_oldPixmap = m_pixmap;
153 }
154 }
155
156 void PixmapViewer::updateAnimatedImageFrame()
157 {
158 Q_ASSERT(m_animatedImage);
159
160 m_pixmap = m_animatedImage->currentPixmap();
161 update();
162 }
163
164 void PixmapViewer::stopAnimatedImage()
165 {
166 if (m_hasAnimatedImage) {
167 m_animatedImage->stop();
168 m_hasAnimatedImage = false;
169 }
170 }
171
172 bool PixmapViewer::isAnimatedMimeType(const QString &mimeType)
173 {
174 const QList<QByteArray> imageFormats = QImageReader::imageFormatsForMimeType(mimeType.toUtf8());
175 return std::any_of(imageFormats.begin(), imageFormats.end(), [](const QByteArray &format) {
176 return QMovie::supportedFormats().contains(format);
177 });
178 }
179
180 #include "moc_pixmapviewer.cpp"