diff options
author | Daiwei Li <daiweili@suitabletech.com> | 2015-02-27 23:56:12 -0800 |
---|---|---|
committer | Daiwei Li <daiweili@suitabletech.com> | 2015-03-06 18:07:01 +0000 |
commit | 2a28bebdc8548c1171a3f255651edafe685da003 (patch) | |
tree | e4239adb905c33df2f9f31dbe2234c65118ae6f9 | |
parent | 1d2d771f552ff43a508ff301ff0906d8d61b729f (diff) |
Implement cache property for QQuickAnimatedImage
Some longer and larger .gifs can consume a lot of memory
if every decoded frame is cached. Just as the backing QMovie
provides the ability to not cache frame, so should AnimatedImage.
This also allows a workaround for some animated image types
that can contain loops that aren't handled correctly (QTBUG-24869)
to not leak memory.
Change-Id: I0639461d75bb2c758917893e7a6ae5c215fffa9d
Task-number: QTBUG-44447
Task-number: QTBUG-24869
Task-number: QTBUG-28844
Reviewed-by: Alan Alpert <aalpert@blackberry.com>
4 files changed, 72 insertions, 2 deletions
diff --git a/src/quick/items/qquickanimatedimage.cpp b/src/quick/items/qquickanimatedimage.cpp index a989e81176..49fef12467 100644 --- a/src/quick/items/qquickanimatedimage.cpp +++ b/src/quick/items/qquickanimatedimage.cpp @@ -104,7 +104,13 @@ QQuickPixmap* QQuickAnimatedImagePrivate::infoForCurrentFrame(QQmlEngine *engine about its state, such as the current frame and total number of frames. The result is an animated image with a simple progress indicator underneath it. - \b Note: Unlike images, animated images are not cached or shared internally. + \b Note: When animated images are cached, every frame of the animation will be cached. + + Set cache to false if you are playing a long or large animation and you + want to conserve memory. + + If the image data comes from a sequential device (e.g. a socket), + AnimatedImage can only loop if cache is set to true. \clearfloat \snippet qml/animatedimage.qml document @@ -126,6 +132,7 @@ QQuickPixmap* QQuickAnimatedImagePrivate::infoForCurrentFrame(QQmlEngine *engine QQuickAnimatedImage::QQuickAnimatedImage(QQuickItem *parent) : QQuickImage(*(new QQuickAnimatedImagePrivate), parent) { + QObject::connect(this, &QQuickImageBase::cacheChanged, this, &QQuickAnimatedImage::onCacheChanged); } QQuickAnimatedImage::~QQuickAnimatedImage() @@ -372,7 +379,8 @@ void QQuickAnimatedImage::movieRequestFinished() this, SLOT(playingStatusChanged())); connect(d->_movie, SIGNAL(frameChanged(int)), this, SLOT(movieUpdate())); - d->_movie->setCacheMode(QMovie::CacheAll); + if (d->cache) + d->_movie->setCacheMode(QMovie::CacheAll); d->status = Ready; emit statusChanged(d->status); @@ -406,6 +414,11 @@ void QQuickAnimatedImage::movieUpdate() { Q_D(QQuickAnimatedImage); + if (!d->cache) { + qDeleteAll(d->frameMap); + d->frameMap.clear(); + } + if (d->_movie) { d->setPixmap(*d->infoForCurrentFrame(qmlEngine(this))); emit frameChanged(); @@ -426,6 +439,22 @@ void QQuickAnimatedImage::playingStatusChanged() } } +void QQuickAnimatedImage::onCacheChanged() +{ + Q_D(QQuickAnimatedImage); + if (!cache()) { + qDeleteAll(d->frameMap); + d->frameMap.clear(); + if (d->_movie) { + d->_movie->setCacheMode(QMovie::CacheNone); + } + } else { + if (d->_movie) { + d->_movie->setCacheMode(QMovie::CacheAll); + } + } +} + QSize QQuickAnimatedImage::sourceSize() { Q_D(QQuickAnimatedImage); diff --git a/src/quick/items/qquickanimatedimage_p.h b/src/quick/items/qquickanimatedimage_p.h index de37cd8209..409933817f 100644 --- a/src/quick/items/qquickanimatedimage_p.h +++ b/src/quick/items/qquickanimatedimage_p.h @@ -84,6 +84,7 @@ private Q_SLOTS: void movieUpdate(); void movieRequestFinished(); void playingStatusChanged(); + void onCacheChanged(); protected: void load() Q_DECL_OVERRIDE; diff --git a/tests/auto/quick/qquickanimatedimage/data/colors_nocache.qml b/tests/auto/quick/qquickanimatedimage/data/colors_nocache.qml new file mode 100644 index 0000000000..24adca68ec --- /dev/null +++ b/tests/auto/quick/qquickanimatedimage/data/colors_nocache.qml @@ -0,0 +1,6 @@ +import QtQuick 2.0 + +AnimatedImage { + source: "colors.gif" + cache: false +} diff --git a/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp b/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp index ee38a0e8ff..c42000d418 100644 --- a/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp +++ b/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp @@ -70,6 +70,7 @@ private slots: void qtbug_16520(); void progressAndStatusChanges(); void playingAndPausedChanges(); + void noCaching(); }; void tst_qquickanimatedimage::cleanup() @@ -528,6 +529,39 @@ void tst_qquickanimatedimage::playingAndPausedChanges() delete obj; } + +void tst_qquickanimatedimage::noCaching() +{ + QQuickView window, window_nocache; + window.setSource(testFileUrl("colors.qml")); + window_nocache.setSource(testFileUrl("colors_nocache.qml")); + window.show(); + window_nocache.show(); + QTest::qWaitForWindowExposed(&window); + QTest::qWaitForWindowExposed(&window_nocache); + + QQuickAnimatedImage *anim = qobject_cast<QQuickAnimatedImage *>(window.rootObject()); + QVERIFY(anim); + + QQuickAnimatedImage *anim_nocache = qobject_cast<QQuickAnimatedImage *>(window_nocache.rootObject()); + QVERIFY(anim_nocache); + + QCOMPARE(anim->frameCount(), anim_nocache->frameCount()); + + // colors.gif only has 3 frames so this should be fast + for (int loops = 0; loops <= 2; ++loops) { + for (int frame = 0; frame < anim->frameCount(); ++frame) { + anim->setCurrentFrame(frame); + anim_nocache->setCurrentFrame(frame); + + QImage image_cache = window.grabWindow(); + QImage image_nocache = window_nocache.grabWindow(); + + QCOMPARE(image_cache, image_nocache); + } + } +} + QTEST_MAIN(tst_qquickanimatedimage) #include "tst_qquickanimatedimage.moc" |