From a0499e5140fda1fd29442a1fa818e3e11c40a6be Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Sat, 19 Aug 2023 13:08:12 -0400 Subject: [PATCH] Set sourceSize in for images, improving memory usage and smoothing In some cases (where we don't get a thumbnail) we load the entire image, which might be large. Even when we get a thumbnail, the smoothing we apply doesn't get the best results and the only other option is mipmapping which is more memory intensive - it stores progressively smaller images but keeps all of them in memory. Now the ImageDelegate sets the sourceSize, which helps reduce the memory footprint (since Qt will throw away whatever parts of the image we don't need) and makes the images look smoother when scaled. However, AnimatedImages do not support this so this change introduces a new property to tell if the image could possibly be animated. It will erroneously pick up formats that could support animation (e.g. WebP) but other image formats will still benefit so it's a net positive. The ImageDelegate will load the correct image component depending on this property. --- src/models/messageeventmodel.cpp | 4 ++ src/qml/Component/Timeline/ImageDelegate.qml | 56 ++++++++++++++------ 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/src/models/messageeventmodel.cpp b/src/models/messageeventmodel.cpp index 7dfebc64b..b16c1d79e 100644 --- a/src/models/messageeventmodel.cpp +++ b/src/models/messageeventmodel.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -969,6 +970,9 @@ QVariantMap MessageEventModel::getMediaInfoFromFileInfo(const EventContent::File mediaInfo["width"] = castInfo->imageSize.width(); mediaInfo["height"] = castInfo->imageSize.height(); + // TODO: Images in certain formats (e.g. WebP) will be erroneously marked as animated, even if they are static. + mediaInfo["animated"] = QMovie::supportedFormats().contains(mimeType.preferredSuffix().toUtf8()); + if (!isThumbnail) { QVariantMap tempInfo; auto thumbnailInfo = getMediaInfoFromFileInfo(castInfo->thumbnailInfo(), eventId, true); diff --git a/src/qml/Component/Timeline/ImageDelegate.qml b/src/qml/Component/Timeline/ImageDelegate.qml index 6e88fb5f8..5db517166 100644 --- a/src/qml/Component/Timeline/ImageDelegate.qml +++ b/src/qml/Component/Timeline/ImageDelegate.qml @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-3.0-only import QtQuick 2.15 +import QtQuick.Window 2.15 import QtQuick.Controls 2.15 as QQC2 import QtQuick.Layouts 1.15 import QtQml.Models 2.15 @@ -55,14 +56,12 @@ TimelineContainer { onOpenContextMenu: openFileContext(root) - innerObject: AnimatedImage { - id: img + innerObject: Item { + id: imageContainer property var imageWidth: { if (root.mediaInfo.width > 0) { return root.mediaInfo.width; - } else if (sourceSize.width && sourceSize.width > 0) { - return sourceSize.width; } else { return root.contentMaxWidth; } @@ -70,8 +69,6 @@ TimelineContainer { property var imageHeight: { if (root.mediaInfo.height > 0) { return root.mediaInfo.height; - } else if (sourceSize.height && sourceSize.height > 0) { - return sourceSize.height; } else { // Default to a 16:9 placeholder return root.contentMaxWidth / 16 * 9; @@ -103,22 +100,49 @@ TimelineContainer { Layout.maximumHeight: maxSize.height Layout.preferredWidth: imageWidth Layout.preferredHeight: imageHeight - source: root.mediaInfo.source + + property var imageItem: root.mediaInfo.animated ? animatedImageLoader.item : imageLoader.item + + Loader { + id: imageLoader + + anchors.fill: parent + + active: !root.mediaInfo.animated + sourceComponent: Image { + source: root.mediaInfo.source + sourceSize.width: imageContainer.maxSize.width * Screen.devicePixelRatio + sourceSize.height: imageContainer.maxSize.height * Screen.devicePixelRatio + + fillMode: Image.PreserveAspectFit + } + } + + Loader { + id: animatedImageLoader + + anchors.fill: parent + + active: root.mediaInfo.animated + sourceComponent: AnimatedImage { + source: root.mediaInfo.source + + fillMode: Image.PreserveAspectFit + + paused: !applicationWindow().active + } + } Image { anchors.fill: parent source: root.mediaInfo.tempInfo.source - visible: parent.status !== Image.Ready + visible: imageContainer.imageItem.status !== Image.Ready } - fillMode: Image.PreserveAspectFit - QQC2.ToolTip.text: root.display QQC2.ToolTip.visible: hoverHandler.hovered QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay - paused: !applicationWindow().active - HoverHandler { id: hoverHandler } @@ -126,7 +150,7 @@ TimelineContainer { Rectangle { anchors.fill: parent - visible: root.progressInfo.active && !downloaded + visible: (root.progressInfo.active && !downloaded) || imageContainer.imageItem.status !== Image.Ready color: "#BB000000" @@ -144,8 +168,10 @@ TimelineContainer { TapHandler { acceptedButtons: Qt.LeftButton onTapped: { - img.QQC2.ToolTip.hide() - img.paused = true + imageContainer.QQC2.ToolTip.hide() + if (root.mediaInfo.animated) { + imageContainer.imageItem.paused = true + } root.ListView.view.interactive = false root.ListView.view.showMaximizedMedia(root.index) }