Multiple same-source Images are exhausting RAM
-
Hello.
I have a simple QML file containing anImage
. This file is resued several times (20-30) in my application (both directly and in aRepeater
). The idea of the file is to crop the image using the different parts of it. Editing the image is not possible.
The problem is that the applications starts to use more and more RAM for the exact sameImage
(from 100mb -> 1GB). Is there a way to solve this?Samples:
MyImage.qml:import QtQuick 2.12 Item { id: root property int nowX: 0 property int nowY: 0 property int imageSource: 0 implicitWidth: 50 implicitHeight: 50 Item { clip: true Image { x: root.nowX y: root.nowY source: root.imageSource fillMode: Image.PreserveAspectFit } } }
Main file:
import QtQuick 2.12 Flow { Repeater { model: [[0,0], [100,100], [210, 240]] // Sample values MyImage { imageSource: "somePathToA1024x1024Image" nowX: modelData[0] nowY: modelData[1] } } }
-
Hi,
Do you really need such a big image ?
What about using an image cache through for example a custom image provider. -
The image actually is from the user (this is a tool and that
MyImage.qml
is the editor).
It's an image from gaming textures (216x216, 512x512, 1024, 2048), so I can't change it.
I triedQQuickImageProvider
but I misunderstood it's idea.PyQt5:
class CachedImageProvider(QQuickImageProvider): def __init__(self): QQuickImageProvider.__init__(self, QQuickImageProvider.Image) self._cache = {} def requestImage(self, filePath, requestedSize): # filePath = id fileUrl = QUrl(filePath).toLocalFile() # remove file:// if not fileUrl: return QImage(1, 1, QImage.Format_ARGB32), QSize(1, 1) if fileUrl in self._cache: return self._cache[fileUrl], self._cache[fileUrl].size() image = None with open(fileUrl, 'rb') as handle: image = QImage.fromData(handle.read()) if not image: return QImage(1, 1, QImage.Format_ARGB32), QSize(1, 1) self._cache[fileUrl] = image return image, image.size() if __name__ == "__main__": QGuiApplication.setAttribute(Qt.AA_EnableHighDpiScaling) app = QGuiApplication(sys.argv) qmlRegisterType(CommonFontFile, 'Backend', 1, 0, 'BackendFile') engine = QQmlApplicationEngine() engine.addImageProvider('CachedImageProvider', CachedImageProvider()) engine.load('qrc:/qml/main.qml') if not engine.rootObjects(): sys.exit(-1) res = app.exec_() # Deleting the view before it goes out of scope is required to make sure all child QML instances # are destroyed in the correct order. del engine sys.exit(res)
It works correctly (with the proper scheme and id from QML side), and shows correct. But RAM usage still increase with each instance. Also, the provider is called once, as the documentation says. Disabling
cache
does nothing, as the provider's cache isn't saved.
I am missing something for sure, and sorry about that.
Thanks for your time! :) -
I was thinking about QPixmapCache.
-
So something like this? (Still, same)
class CachedImageProvider(QQuickImageProvider): def __init__(self): QQuickImageProvider.__init__(self, QQuickImageProvider.Pixmap) def requestPixmap(self, filePath, requestedSize): # filePath = id fileUrl = QUrl(filePath).toLocalFile() if not fileUrl: return QPixmap(1, 1), QSize(1, 1) pm = QPixmap(1, 1) if not QPixmapCache.find(fileUrl): pm.load(fileUrl) QPixmapCache.insert(fileUrl, pm) return pm, pm.size()
-
It's rather:
pm = QPixmapCache.find(fileUrl) if pm is None: pm = QPixmap(fileUrl) QPixmapCache.insert(fileUrl, pm) return pm, pm.size()
As it is, your code sample fills the QPixmapCache or returns a 1, 1 QPixmap, however, it does re-use the content of the cache.