~ruther/guix-local

95a74533f12a2636caafc93f24a8058adc8d27c9 — Maxim Cournoyer 4 years ago 649a4e9
gnu: jami: Reduce memory consumption in conversation view.

This fixes <https://git.jami.net/savoirfairelinux/jami-client-qt/-/issues/649>.

* gnu/packages/patches/jami-images-loading.patch: New patch.
* gnu/packages/patches/jami-memory-usage.patch: Likewise.
* gnu/local.mk (dist_patch_DATA): Register them.
* gnu/packages/jami.scm (jami)[source]: Apply them.
M gnu/local.mk => gnu/local.mk +2 -0
@@ 1299,6 1299,8 @@ dist_patch_DATA =						\
  %D%/packages/patches/json-c-0.12-CVE-2020-12762.patch	\
  %D%/packages/patches/jsoncpp-pkg-config-version.patch		\
  %D%/packages/patches/jami-fix-crash-on-quit.patch		\
  %D%/packages/patches/jami-images-loading.patch		\
  %D%/packages/patches/jami-memory-usage.patch			\
  %D%/packages/patches/jamvm-1.5.1-aarch64-support.patch	\
  %D%/packages/patches/jamvm-1.5.1-armv7-support.patch	\
  %D%/packages/patches/jamvm-2.0.0-aarch64-support.patch	\

M gnu/packages/jami.scm => gnu/packages/jami.scm +3 -1
@@ 568,7 568,9 @@ decentralized calling using P2P-DHT.")
    (version %jami-version)
    (source (origin
              (inherit %jami-sources)
              (patches (search-patches "jami-fix-crash-on-quit.patch"))))
              (patches (search-patches "jami-fix-crash-on-quit.patch"
                                       "jami-images-loading.patch"
                                       "jami-memory-usage.patch"))))
    (build-system qt-build-system)
    (outputs '("out" "debug"))
    (arguments

A gnu/packages/patches/jami-images-loading.patch => gnu/packages/patches/jami-images-loading.patch +152 -0
@@ 0,0 1,152 @@
From be9dd0d0d8cb4556cd930edd783c0a1699565ac0 Mon Sep 17 00:00:00 2001
From: kkostiuk <kateryna.kostiuk@savoirfairelinux.com>
Date: Mon, 1 Nov 2021 17:39:23 -0400
Subject: [PATCH] conversation: fix long loading time for images

Change-Id: Id88cfbd571f4b504f258758bd13b4e4a91bf1b49
---
 .../DataTransferMessageDelegate.qml           | 52 +++++++++++++++++--
 src/messagesadapter.cpp                       | 20 +++++--
 src/messagesadapter.h                         |  2 +-
 3 files changed, 66 insertions(+), 8 deletions(-)

diff --git a/client-qt/src/commoncomponents/DataTransferMessageDelegate.qml b/client-qt/src/commoncomponents/DataTransferMessageDelegate.qml
index 7875e01..2e7dcc0 100644
--- a/client-qt/src/commoncomponents/DataTransferMessageDelegate.qml
+++ b/client-qt/src/commoncomponents/DataTransferMessageDelegate.qml
@@ -252,10 +252,11 @@ Loader {
                 Loader {
                     id: localMediaCompLoader
                     anchors.right: isOutgoing ? parent.right : undefined
+                    asynchronous: true
                     width: sourceComponent.width
                     height: sourceComponent.height
                     sourceComponent: mediaInfo.isImage !== undefined ?
-                                         imageComp :
+                                         imageComp : mediaInfo.isAnimatedImage !== undefined ? animatedImageComp :
                                          avComp
                     Component {
                         id: avComp
@@ -302,9 +303,9 @@ Loader {
                         }
                     }
                     Component {
-                        id: imageComp
+                        id: animatedImageComp
                         AnimatedImage {
-                            id: img
+                            id: animatedImg
                             anchors.right: isOutgoing ? parent.right : undefined
                             property real minSize: 192
                             property real maxSize: 256
@@ -327,6 +328,51 @@ Loader {
                                 anchors.fill: parent
                             }
                             layer.enabled: true
+                            layer.effect: OpacityMask {
+                                maskSource: MessageBubble {
+                                    out: isOutgoing
+                                    type: seq
+                                    width: animatedImg.width
+                                    height: animatedImg.height
+                                    radius: msgRadius
+                                }
+                            }
+                            HoverHandler {
+                                target : parent
+                                onHoveredChanged: {
+                                    localMediaMsgItem.hoveredLink = hovered ? animatedImg.source : ""
+                                }
+                                cursorShape: Qt.PointingHandCursor
+                            }
+                        }
+                    }
+
+                    Component {
+                        id: imageComp
+                        Image {
+                            id: img
+                            anchors.right: isOutgoing ? parent.right : undefined
+                            property real minSize: 192
+                            property real maxSize: 256
+                            cache: true
+                            fillMode: Image.PreserveAspectCrop
+                            mipmap: true
+                            antialiasing: true
+                            autoTransform: false
+                            asynchronous: true
+                            source: "file:///" + Body
+                            property real aspectRatio: implicitWidth / implicitHeight
+                            property real adjustedWidth: Math.min(maxSize,
+                                                                  Math.max(minSize,
+                                                                           innerContent.width - senderMargin))
+                            width: adjustedWidth
+                            height: Math.ceil(adjustedWidth / aspectRatio)
+                            Rectangle {
+                                color: JamiTheme.previewImageBackgroundColor
+                                z: -1
+                                anchors.fill: parent
+                            }
+                            layer.enabled: true
                             layer.effect: OpacityMask {
                                 maskSource: MessageBubble {
                                     out: isOutgoing
diff --git a/client-qt/src/messagesadapter.cpp b/client-qt/src/messagesadapter.cpp
index 91f8eed..ba38e53 100644
--- a/client-qt/src/messagesadapter.cpp
+++ b/client-qt/src/messagesadapter.cpp
@@ -458,13 +458,24 @@ MessagesAdapter::conversationTypersUrlToName(const QSet<QString>& typersSet)
     return nameList;
 }
 
-bool
+QVariantMap
 MessagesAdapter::isLocalImage(const QString& msg)
 {
     QImageReader reader;
     reader.setDecideFormatFromContent(true);
     reader.setFileName(msg);
-    return !reader.read().isNull();
+    QByteArray fileFormat = reader.format();
+    if (fileFormat == "gif") {
+        return {{"isAnimatedImage", true}};
+    }
+    QList<QByteArray> supportedFormats = reader.supportedImageFormats();
+    auto iterator = std::find_if(supportedFormats.begin(),
+                                 supportedFormats.end(),
+                                 [fileFormat](QByteArray format) { return format == fileFormat; });
+    if (iterator != supportedFormats.end()) {
+        return {{"isImage", true}};
+    }
+    return {{"isImage", false}};
 }
 
 QVariantMap
@@ -476,8 +487,9 @@ MessagesAdapter::getMediaInfo(const QString& msg)
           "<%1 style='width:100%;height:%2;outline:none;background-color:#f1f3f4;"
           "object-fit:cover;' "
           "controls controlsList='nodownload' src='file://%3' type='%4'/></body>";
-    if (isLocalImage(msg)) {
-        return {{"isImage", true}};
+    QVariantMap fileInfo = isLocalImage(msg);
+    if (fileInfo["isImage"].toBool() || fileInfo["isAnimatedImage"].toBool()) {
+        return fileInfo;
     }
     QRegularExpression vPattern("[^\\s]+(.*?)\\.(avi|mov|webm|webp|rmvb)$",
                                 QRegularExpression::CaseInsensitiveOption);
diff --git a/client-qt/src/messagesadapter.h b/client-qt/src/messagesadapter.h
index bfa4e62..1965c5e 100644
--- a/client-qt/src/messagesadapter.h
+++ b/client-qt/src/messagesadapter.h
@@ -101,7 +101,7 @@ protected:
     Q_INVOKABLE void deleteInteraction(const QString& interactionId);
     Q_INVOKABLE void copyToDownloads(const QString& interactionId, const QString& displayName);
     Q_INVOKABLE void userIsComposing(bool isComposing);
-    Q_INVOKABLE bool isLocalImage(const QString& msg);
+    Q_INVOKABLE QVariantMap isLocalImage(const QString& msg);
     Q_INVOKABLE QVariantMap getMediaInfo(const QString& msg);
     Q_INVOKABLE bool isRemoteImage(const QString& msg);
     Q_INVOKABLE QString getFormattedTime(const quint64 timestamp);
-- 
GitLab


A gnu/packages/patches/jami-memory-usage.patch => gnu/packages/patches/jami-memory-usage.patch +70 -0
@@ 0,0 1,70 @@
From e796b3325d95b5ddd6162b5513c8325210f41fc5 Mon Sep 17 00:00:00 2001
From: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
Date: Wed, 26 Jan 2022 11:37:07 -0500
Subject: [PATCH] datatransferimage: improve memory usage

+ Reduce listview caching' size by 50%
+ use sourceSize to compress images and speedup loading
+ use autoTransform: true to rotate images when needed

Change-Id: Idf1babdc73f43aa6a79b89428c25c5d06856c0ef
GitLab: #649
---

diff --git a/client-qt/src/commoncomponents/DataTransferMessageDelegate.qml b/client-qt/src/commoncomponents/DataTransferMessageDelegate.qml
index d017c03..ca5913e 100644
--- a/client-qt/src/commoncomponents/DataTransferMessageDelegate.qml
+++ b/client-qt/src/commoncomponents/DataTransferMessageDelegate.qml
@@ -255,9 +255,13 @@
                     asynchronous: true
                     width: sourceComponent.width
                     height: sourceComponent.height
-                    sourceComponent: mediaInfo.isImage !== undefined ?
-                                         imageComp : mediaInfo.isAnimatedImage !== undefined ? animatedImageComp :
-                                         avComp
+                    sourceComponent: {
+                        if (mediaInfo.isImage)
+                            return imageComp
+                        if (mediaInfo.isAnimatedImage)
+                            return animatedImageComp
+                        return avComp
+                    }
                     Component {
                         id: avComp
                         WebEngineView {
@@ -316,7 +320,7 @@
                             fillMode: Image.PreserveAspectCrop
                             mipmap: true
                             antialiasing: true
-                            autoTransform: false
+                            autoTransform: true
                             asynchronous: true
                             source: "file:///" + Body
                             property real aspectRatio: implicitWidth / implicitHeight
@@ -361,8 +365,10 @@
                             fillMode: Image.PreserveAspectCrop
                             mipmap: true
                             antialiasing: true
-                            autoTransform: false
+                            autoTransform: true
                             asynchronous: true
+                            sourceSize.width: width
+                            sourceSize.height: height
                             source: "file:///" + Body
                             property real aspectRatio: implicitWidth / implicitHeight
                             property real adjustedWidth: Math.min(maxSize,
diff --git a/client-qt/src/mainview/components/MessageListView.qml b/client-qt/src/mainview/components/MessageListView.qml
index 2b7c326..f65e67b 100644
--- a/client-qt/src/mainview/components/MessageListView.qml
+++ b/client-qt/src/mainview/components/MessageListView.qml
@@ -174,8 +174,8 @@
     width: parent.width
     // this offscreen caching is pretty huge
     // displayMarginEnd may be removed
-    displayMarginBeginning: 4096
-    displayMarginEnd: 4096
+    displayMarginBeginning: 2048
+    displayMarginEnd: 2048
     maximumFlickVelocity: 2048
     verticalLayoutDirection: ListView.BottomToTop
     boundsBehavior: Flickable.StopAtBounds