Details
-
Bug
-
Resolution: Done
-
P3: Somewhat important
-
4.8.x, 5.0.1
-
None
-
263d733ac70403c488d67b1aab120167f156c3f4 313d60c107d9659efd5e93ba5a036054f6b1ebba
Description
QMngHandler returns an extra frame due to an apparent bug in libmng:
If an animation has, for example, actual frames 0 through 7, libmng delivers frames 0 through 6 as expected. But then it delivers frame 7 with the correct pixel data, but returns 1ms for the delay time. (This 1ms is hardcoded in libmng, but I was not able to ascertain exactly why it was there.) Then libmng delivers a frame 8 (which DOES NOT exist in the MNG file), but renders nothing onto QMngHandlerPrivate::image which just contains the previous state (frame 7) and returns the correct delay time before the next frame, frame 0. Stranger still, it only does this on the first animation iteration loop. Subsequent loops only deliver frames 0 through 7.
This extra frame confuses QMovie (when cacheMode is QMovie::CacheAll):
QMovie is not relying on QMngHandler::currentImageNumber() nor QMngHandler::imageCount() because it is not possible for QMngHandler to implement them properly (due to inconsistent behavior from libmng). Instead, QMovie repeatly calls QMngHandler::canRead() and QMngHandler::read() to know when the last frame has been reached. It then assumes that's the correct number of frames for subsequent loops when cacheMode is QMovie::CacheAll. The problem is that QMngHandler::canRead() returns false after the last real frame, but the delay it returned was 1ms. QMovie, then caches that the last frame should be displayed for 1ms. Hence, it appears, on screen, as though the last frame is skipped.
This bug exists for non-looped MNG files as well, but it is not visible. It also exists when QMovie::cacheMode is QMovie::CacheNone, but it's also not visible to the user. (Unless one considers the extra millisecond before the loop) In these two cases, it's only visible when qDebug() statements are added to see what QMovie is really receiving from QMngHandler.
The work-around is to detect when the last real frame has been delivered by libmng (on the first loop iteration) and that it has also returned a delaytime of 1ms. And when this happens, to call libmng's mng_display_resume() once more which gets it to return the true delay time for the last frame (but doesn't actually render anything different onto QMngHandlerPrivate::image).
Note that if an animation frame truly had a 1ms delay time on the last frame (which is unlikely in the first place), then it would not be visible to the user, and so skipping it would not really affect the visualization.
I've attached a imple test_ani.cpp application to demonstrate the problem:
ball.mng is also attached. It is just an example. I obtained it from: tests/benchmarks/gui/image/qimagereader/images/ball.mng
If this is run, you will notice a "skip" in the animation each time loops.
Attachments
Issue Links
- resulted in
-
QTBUG-41752 imageformats: qmnghandler.cpp:278: bad if test ?
- Closed