88#include < QNetworkReply>
99#include < QImage>
1010#include < QPainter>
11+ #include < QMovie>
12+ #include < QBuffer>
1113
1214#include " view.h"
1315
@@ -28,6 +30,19 @@ View::View(QWidget* parent) : QWidget(parent)
2830 networkManager = new QNetworkAccessManager (this );
2931 currentImage = QImage ();
3032 nextImage = QImage ();
33+ movie = nullptr ;
34+ animationTimer = new QTimer (this );
35+ isAnimatedImage = false ;
36+
37+ connect (animationTimer, &QTimer::timeout, this , &View::updateMovieFrame);
38+ }
39+
40+ View::~View ()
41+ {
42+ if (movie) {
43+ movie->stop ();
44+ delete movie;
45+ }
3146}
3247
3348void View::loadPage (const QString &uri)
@@ -61,6 +76,15 @@ void View::loadImage(const QString &preUri)
6176{
6277 qDebug () << " Type: Image" ;
6378
79+ // Stop any existing animation
80+ if (movie) {
81+ movie->stop ();
82+ delete movie;
83+ movie = nullptr ;
84+ }
85+ animationTimer->stop ();
86+ isAnimatedImage = false ;
87+
6488 QFileInfo fileInfo = QFileInfo (preUri);
6589 QString src;
6690
@@ -99,19 +123,15 @@ void View::loadImage(const QString &preUri)
99123
100124 connect (reply, &QNetworkReply::finished, this , [=]() {
101125 if (reply->error () == QNetworkReply::NoError) {
102- QImage newImage;
103126 QByteArray data = reply->readAll ();
104127 qDebug () << " Received image data size:" << data.size ();
105128
106- if (newImage.loadFromData (data)) {
107- qDebug () << " Successfully loaded image. Size:" << newImage.size ();
108- nextImage = newImage;
109- // Only hide web view and update current image after the new image is loaded
110- webView->setVisible (false );
111- currentImage = nextImage;
112- update ();
129+ if (tryLoadAsAnimatedGif (data)) {
130+ // Successfully loaded as animated GIF
131+ return ;
113132 } else {
114- qDebug () << " Failed to load image from data" ;
133+ // Load as static image
134+ loadAsStaticImage (data);
115135 }
116136 } else {
117137 qDebug () << " Network error:" << reply->errorString ();
@@ -125,13 +145,109 @@ void View::loadImage(const QString &preUri)
125145 });
126146}
127147
148+ bool View::tryLoadAsAnimatedGif (const QByteArray& data)
149+ {
150+ // Try to load as QMovie first to check if it's an animated GIF
151+ QBuffer* testBuffer = new QBuffer ();
152+ testBuffer->setData (data);
153+ testBuffer->open (QIODevice::ReadOnly);
154+
155+ // Create QMovie to test if it's animated
156+ QMovie testMovie (testBuffer, QByteArray (), this );
157+
158+ if (testMovie.isValid () && testMovie.frameCount () > 1 ) {
159+ // This is an animated GIF
160+ qDebug () << " Detected animated GIF with" << testMovie.frameCount () << " frames" ;
161+ delete testBuffer; // Clean up test buffer
162+
163+ // Create a new buffer for the actual movie
164+ QBuffer* buffer = new QBuffer ();
165+ buffer->setData (data);
166+ buffer->open (QIODevice::ReadOnly);
167+
168+ // Create the actual movie for animation
169+ movie = new QMovie (buffer, QByteArray (), this );
170+
171+ if (movie->isValid ()) {
172+ qDebug () << " GIF animation loaded successfully. Frame count:" << movie->frameCount ();
173+ qDebug () << " Animation speed:" << movie->speed () << " ms per frame" ;
174+
175+ setupAnimation ();
176+ return true ;
177+ } else {
178+ qDebug () << " Failed to load GIF as animation, falling back to static image" ;
179+ delete movie;
180+ movie = nullptr ;
181+ delete buffer;
182+
183+ // Fall back to static image loading - this preserves original behavior
184+ loadAsStaticImage (data);
185+ return true ; // Return true to prevent double loading
186+ }
187+ } else {
188+ // This is a static image (including single-frame GIFs)
189+ delete testBuffer;
190+ return false ;
191+ }
192+ }
193+
194+ void View::loadAsStaticImage (const QByteArray& data)
195+ {
196+ QImage newImage;
197+ if (newImage.loadFromData (data)) {
198+ qDebug () << " Successfully loaded static image. Size:" << newImage.size ();
199+ nextImage = newImage;
200+ webView->setVisible (false );
201+ currentImage = nextImage;
202+ update ();
203+ } else {
204+ qDebug () << " Failed to load image from data" ;
205+ }
206+ }
207+
208+ void View::updateMovieFrame ()
209+ {
210+ if (movie && isAnimatedImage && movie->state () == QMovie::Running) {
211+ // Try to advance to the next frame
212+ if (movie->jumpToNextFrame ()) {
213+ QImage newFrame = movie->currentImage ();
214+ if (!newFrame.isNull ()) {
215+ currentImage = newFrame;
216+ update ();
217+ }
218+ }
219+
220+ // Schedule next frame update
221+ scheduleNextFrame ();
222+ }
223+ }
224+
225+ void View::scheduleNextFrame ()
226+ {
227+ int frameDelay = movie->nextFrameDelay ();
228+ if (frameDelay > 0 ) {
229+ animationTimer->start (frameDelay);
230+ } else {
231+ // If no delay specified, try to get it from the movie speed
232+ frameDelay = movie->speed ();
233+ if (frameDelay > 0 ) {
234+ animationTimer->start (frameDelay);
235+ } else {
236+ animationTimer->start (100 ); // Default delay
237+ }
238+ }
239+ }
240+
128241void View::paintEvent (QPaintEvent*)
129242{
130243 QPainter painter (this );
131244 painter.fillRect (rect (), Qt::black);
132245
133246 if (!currentImage.isNull ()) {
134- qDebug () << " Painting image. Size:" << currentImage.size ();
247+ // Only log for static images to avoid spam during animation
248+ if (!isAnimatedImage) {
249+ qDebug () << " Painting image. Size:" << currentImage.size ();
250+ }
135251 QSize scaledSize = currentImage.size ();
136252 scaledSize.scale (size (), Qt::KeepAspectRatio);
137253 QRect targetRect = QRect (QPoint (0 , 0 ), size ());
@@ -157,3 +273,25 @@ void View::handleAuthRequest(QNetworkReply* reply, QAuthenticator* auth)
157273 Q_UNUSED (auth)
158274 webView->load (QUrl::fromLocalFile (QStandardPaths::locate (QStandardPaths::AppDataLocation, " res/access_denied.html" )));
159275}
276+
277+ void View::setupAnimation ()
278+ {
279+ isAnimatedImage = true ;
280+ webView->setVisible (false );
281+
282+ // Start the animation
283+ movie->start ();
284+
285+ // Set up timer for frame updates
286+ int frameDelay = movie->nextFrameDelay ();
287+ if (frameDelay > 0 ) {
288+ animationTimer->start (frameDelay);
289+ } else {
290+ // Default to 100ms if no delay specified
291+ animationTimer->start (100 );
292+ }
293+
294+ // Get the first frame
295+ currentImage = movie->currentImage ();
296+ update ();
297+ }
0 commit comments