]> cloud.milkyroute.net Git - dolphin.git/blob - src/panels/information/pixmapviewer.cpp
InformationPanel: fix potential glitches with gifs
[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 if ((m_transition != NoTransition) && (m_animation.state() == QTimeLine::Running)) {
47 m_pendingPixmaps.enqueue(pixmap);
48 if (m_pendingPixmaps.count() > 5) {
49 // don't queue more than 5 pixmaps
50 m_pendingPixmaps.takeFirst();
51 }
52 return;
53 }
54
55 m_oldPixmap = m_pixmap.isNull() ? pixmap : m_pixmap;
56 m_pixmap = pixmap;
57
58 // Avoid flicker with static pixmap if an animated image is running
59 if (m_animatedImage && m_animatedImage->state() == QMovie::Running) {
60 return;
61 }
62
63 update();
64
65 const bool animateTransition = (m_transition != NoTransition) && (m_pixmap.size() != m_oldPixmap.size());
66 if (animateTransition) {
67 m_animation.start();
68 } else if (m_hasAnimatedImage) {
69 // If there is no transition animation but an animatedImage
70 // and it is not already running, start animating now
71 if (m_animatedImage->state() != QMovie::Running) {
72 m_animatedImage->start();
73 }
74 }
75 }
76
77 void PixmapViewer::setSizeHint(const QSize &size)
78 {
79 if (m_animatedImage && size != m_sizeHint) {
80 m_animatedImage->stop();
81 }
82
83 m_sizeHint = size;
84 updateGeometry();
85 }
86
87 QSize PixmapViewer::sizeHint() const
88 {
89 return m_sizeHint;
90 }
91
92 void PixmapViewer::setAnimatedImageFileName(const QString &fileName)
93 {
94 if (!m_animatedImage) {
95 m_animatedImage = new QMovie(this);
96 connect(m_animatedImage, &QMovie::frameChanged, this, &PixmapViewer::updateAnimatedImageFrame);
97 }
98
99 if (m_animatedImage->fileName() != fileName) {
100 m_animatedImage->setFileName(fileName);
101 }
102
103 m_hasAnimatedImage = m_animatedImage->isValid() && (m_animatedImage->frameCount() > 1);
104 }
105
106 QString PixmapViewer::animatedImageFileName() const
107 {
108 if (!m_hasAnimatedImage) {
109 return QString();
110 }
111 return m_animatedImage->fileName();
112 }
113
114 void PixmapViewer::paintEvent(QPaintEvent *event)
115 {
116 QWidget::paintEvent(event);
117
118 QPainter painter(this);
119
120 if (m_transition != NoTransition || (m_hasAnimatedImage && m_animatedImage->state() != QMovie::Running)) {
121 const float value = m_animation.currentValue();
122 const int scaledWidth = static_cast<int>((m_oldPixmap.width() * (1.0 - value)) + (m_pixmap.width() * value));
123 const int scaledHeight = static_cast<int>((m_oldPixmap.height() * (1.0 - value)) + (m_pixmap.height() * value));
124
125 const bool useOldPixmap = (m_transition == SizeTransition) && (m_oldPixmap.width() > m_pixmap.width());
126 const QPixmap &largePixmap = useOldPixmap ? m_oldPixmap : m_pixmap;
127 if (!largePixmap.isNull()) {
128 QPixmap scaledPixmap = largePixmap.scaled(scaledWidth, scaledHeight, Qt::IgnoreAspectRatio, Qt::FastTransformation);
129 scaledPixmap.setDevicePixelRatio(devicePixelRatioF());
130
131 style()->drawItemPixmap(&painter, rect(), Qt::AlignCenter, scaledPixmap);
132 }
133 } else if (!m_pixmap.isNull()) {
134 style()->drawItemPixmap(&painter, rect(), Qt::AlignCenter, m_pixmap);
135 }
136 }
137
138 void PixmapViewer::checkPendingPixmaps()
139 {
140 if (!m_pendingPixmaps.isEmpty()) {
141 QPixmap pixmap = m_pendingPixmaps.dequeue();
142 m_oldPixmap = m_pixmap.isNull() ? pixmap : m_pixmap;
143 m_pixmap = pixmap;
144 update();
145 m_animation.start();
146 } else if (m_hasAnimatedImage) {
147 m_animatedImage->start();
148 } else {
149 m_oldPixmap = m_pixmap;
150 }
151 }
152
153 void PixmapViewer::updateAnimatedImageFrame()
154 {
155 Q_ASSERT(m_animatedImage);
156
157 m_pixmap = m_animatedImage->currentPixmap();
158 update();
159 }
160
161 void PixmapViewer::stopAnimatedImage()
162 {
163 if (m_hasAnimatedImage) {
164 m_animatedImage->stop();
165 m_hasAnimatedImage = false;
166 delete m_animatedImage;
167 m_animatedImage = nullptr;
168 }
169 }
170
171 bool PixmapViewer::isAnimatedMimeType(const QString &mimeType)
172 {
173 const QList<QByteArray> imageFormats = QImageReader::imageFormatsForMimeType(mimeType.toUtf8());
174 return std::any_of(imageFormats.begin(), imageFormats.end(), [](const QByteArray &format) {
175 return QMovie::supportedFormats().contains(format);
176 });
177 }
178
179 #include "moc_pixmapviewer.cpp"