[vlc-devel] [PATCH 8/8] qt, qml: provide RoundImage implementation from cpp

Prince Gupta guptaprince8832 at gmail.com
Wed Apr 21 10:12:04 UTC 2021


previously RoundImage was implemented in QML using Opacitymask which was
slow, new implementation done in cpp using QQuickPaintedItem is around ~5x faster
---
 modules/gui/qt/Makefile.am                    |   3 +-
 modules/gui/qt/maininterface/mainui.cpp       |   5 +
 .../qt/medialibrary/qml/ArtistTopBanner.qml   |   4 +-
 .../qml/MusicAlbumsGridExpandDelegate.qml     |   5 +-
 .../medialibrary/qml/MusicArtistsAlbums.qml   |   4 +-
 .../medialibrary/qml/VideoInfoExpandPanel.qml |   5 +-
 modules/gui/qt/vlc.qrc                        |   1 -
 modules/gui/qt/widgets/native/roundimage.cpp  | 324 ++++++++++++++++++
 modules/gui/qt/widgets/native/roundimage.hpp  | 139 ++++++++
 modules/gui/qt/widgets/qml/GridItem.qml       |   1 -
 modules/gui/qt/widgets/qml/MediaCover.qml     |   5 +-
 modules/gui/qt/widgets/qml/RoundImage.qml     |  56 ---
 modules/gui/qt/widgets/qml/TableColumns.qml   |   1 -
 13 files changed, 483 insertions(+), 70 deletions(-)
 create mode 100644 modules/gui/qt/widgets/native/roundimage.cpp
 create mode 100644 modules/gui/qt/widgets/native/roundimage.hpp
 delete mode 100644 modules/gui/qt/widgets/qml/RoundImage.qml

diff --git a/modules/gui/qt/Makefile.am b/modules/gui/qt/Makefile.am
index 49d3ae6aa8..1b9b513b38 100644
--- a/modules/gui/qt/Makefile.am
+++ b/modules/gui/qt/Makefile.am
@@ -254,6 +254,7 @@ libqt_plugin_la_SOURCES = \
 	gui/qt/widgets/native/interface_widgets.hpp \
 	gui/qt/widgets/native/qvlcframe.cpp \
 	gui/qt/widgets/native/qvlcframe.hpp \
+	gui/qt/widgets/native/roundimage.cpp gui/qt/widgets/native/roundimage.hpp \
 	gui/qt/widgets/native/searchlineedit.cpp gui/qt/widgets/native/searchlineedit.hpp
 if HAVE_WIN32
 libqt_plugin_la_SOURCES += \
@@ -392,6 +393,7 @@ nodist_libqt_plugin_la_SOURCES = \
 	gui/qt/widgets/native/animators.moc.cpp \
 	gui/qt/widgets/native/customwidgets.moc.cpp \
 	gui/qt/widgets/native/interface_widgets.moc.cpp \
+	gui/qt/widgets/native/roundimage.moc.cpp \
 	gui/qt/widgets/native/searchlineedit.moc.cpp
 
 if HAVE_WIN32
@@ -787,7 +789,6 @@ libqt_plugin_la_QML = \
 	gui/qt/widgets/qml/NavigableRow.qml \
 	gui/qt/widgets/qml/PlayCover.qml \
 	gui/qt/widgets/qml/RoundButton.qml \
-	gui/qt/widgets/qml/RoundImage.qml \
 	gui/qt/widgets/qml/ScanProgressBar.qml \
 	gui/qt/widgets/qml/ScrollingText.qml \
 	gui/qt/widgets/qml/SearchBox.qml \
diff --git a/modules/gui/qt/maininterface/mainui.cpp b/modules/gui/qt/maininterface/mainui.cpp
index 0d792d4e59..6aabf3ab90 100644
--- a/modules/gui/qt/maininterface/mainui.cpp
+++ b/modules/gui/qt/maininterface/mainui.cpp
@@ -50,6 +50,8 @@
 
 #include "menus/qml_menu_wrapper.hpp"
 
+#include "widgets/native/roundimage.hpp"
+
 #include "videosurface.hpp"
 
 #include <QQuickWindow>
@@ -257,6 +259,9 @@ void MainUI::registerQMLTypes()
     qmlRegisterType<PlaylistContextMenu>( "org.videolan.vlc", 0, 1, "PlaylistContextMenu" );
     qmlRegisterType<SortFilterProxyModel>( "org.videolan.vlc", 0, 1, "SortFilterProxyModel" );
 
+    // Custom controls
+    qmlRegisterType<RoundImage>( "org.videolan.controls", 0, 1, "RoundImage" );
+
     qRegisterMetaType<QList<QQmlError>>("QList<QQmlError>");
 }
 
diff --git a/modules/gui/qt/medialibrary/qml/ArtistTopBanner.qml b/modules/gui/qt/medialibrary/qml/ArtistTopBanner.qml
index 367c8f2323..9e45675721 100644
--- a/modules/gui/qt/medialibrary/qml/ArtistTopBanner.qml
+++ b/modules/gui/qt/medialibrary/qml/ArtistTopBanner.qml
@@ -22,6 +22,7 @@ import QtQml.Models 2.11
 import QtGraphicalEffects 1.0
 
 import org.videolan.medialib 0.1
+import org.videolan.controls 0.1
 
 import "qrc:///widgets/" as Widgets
 import "qrc:///style/"
@@ -73,12 +74,11 @@ Widgets.NavigableFocusScope {
             Layout.preferredHeight: VLCStyle.cover_normal
             Layout.preferredWidth: VLCStyle.cover_normal
 
-            Widgets.RoundImage {
+            RoundImage {
                 source: artist.cover || VLCStyle.noArtArtist
                 height: VLCStyle.cover_normal
                 width: VLCStyle.cover_normal
                 radius: VLCStyle.cover_normal
-
             }
 
             Rectangle {
diff --git a/modules/gui/qt/medialibrary/qml/MusicAlbumsGridExpandDelegate.qml b/modules/gui/qt/medialibrary/qml/MusicAlbumsGridExpandDelegate.qml
index f974b740f3..7fa67ab541 100644
--- a/modules/gui/qt/medialibrary/qml/MusicAlbumsGridExpandDelegate.qml
+++ b/modules/gui/qt/medialibrary/qml/MusicAlbumsGridExpandDelegate.qml
@@ -21,6 +21,7 @@ import QtQuick.Layouts 1.3
 import QtQml.Models 2.11
 
 import org.videolan.medialib 0.1
+import org.videolan.controls 0.1
 
 import "qrc:///widgets/" as Widgets
 import "qrc:///util/Helpers.js" as Helpers
@@ -89,9 +90,9 @@ Widgets.NavigableFocusScope {
                     height: VLCStyle.expandCover_music_height
                     width: VLCStyle.expandCover_music_width
 
-                    Widgets.RoundImage {
+                    RoundImage {
                         id: expand_cover_id
-                        asynchronous: true
+
                         height: VLCStyle.expandCover_music_height
                         width: VLCStyle.expandCover_music_width
                         radius: VLCStyle.expandCover_music_radius
diff --git a/modules/gui/qt/medialibrary/qml/MusicArtistsAlbums.qml b/modules/gui/qt/medialibrary/qml/MusicArtistsAlbums.qml
index 33b3b141d4..a6237e04af 100644
--- a/modules/gui/qt/medialibrary/qml/MusicArtistsAlbums.qml
+++ b/modules/gui/qt/medialibrary/qml/MusicArtistsAlbums.qml
@@ -21,6 +21,7 @@ import QtQml.Models 2.2
 import QtQuick.Layouts 1.3
 
 import org.videolan.medialib 0.1
+import org.videolan.controls 0.1
 
 import "qrc:///util/" as Util
 import "qrc:///util/Helpers.js" as Helpers
@@ -161,12 +162,11 @@ Widgets.NavigableFocusScope {
                         bottomMargin: VLCStyle.margin_xsmall
                     }
 
-                    Widgets.RoundImage {
+                    RoundImage {
                         source: model.cover || VLCStyle.noArtArtistSmall
                         height: VLCStyle.play_cover_small
                         width: VLCStyle.play_cover_small
                         radius: VLCStyle.play_cover_small
-                        mipmap: true
 
                         Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
 
diff --git a/modules/gui/qt/medialibrary/qml/VideoInfoExpandPanel.qml b/modules/gui/qt/medialibrary/qml/VideoInfoExpandPanel.qml
index 59314c6b3a..c8afb409cc 100644
--- a/modules/gui/qt/medialibrary/qml/VideoInfoExpandPanel.qml
+++ b/modules/gui/qt/medialibrary/qml/VideoInfoExpandPanel.qml
@@ -21,6 +21,7 @@ import QtQml.Models 2.11
 import QtQuick.Layouts 1.3
 
 import org.videolan.medialib 0.1
+import org.videolan.controls 0.1
 
 import "qrc:///widgets/" as Widgets
 import "qrc:///util/KeyHelper.js" as KeyHelper
@@ -94,14 +95,12 @@ Widgets.NavigableFocusScope {
                         width: VLCStyle.gridCover_video_width
 
                         /* A bigger cover for the album */
-                        Widgets.RoundImage {
+                        RoundImage {
                             id: expand_cover_id
 
                             anchors.fill: parent
-                            asynchronous: true
                             source: model.thumbnail || VLCStyle.noArtCover
                             sourceSize: Qt.size(width, height)
-                            fillMode: Image.PreserveAspectFit
                             radius: VLCStyle.gridCover_radius
                         }
 
diff --git a/modules/gui/qt/vlc.qrc b/modules/gui/qt/vlc.qrc
index 93f129a807..f86c76a8ba 100644
--- a/modules/gui/qt/vlc.qrc
+++ b/modules/gui/qt/vlc.qrc
@@ -223,7 +223,6 @@
         <file alias="RoundButton.qml">widgets/qml/RoundButton.qml</file>
         <file alias="LabelSeparator.qml">widgets/qml/LabelSeparator.qml</file>
         <file alias="ContextButton.qml">widgets/qml/ContextButton.qml</file>
-        <file alias="RoundImage.qml">widgets/qml/RoundImage.qml</file>
         <file alias="VideoQualityLabels.qml">widgets/qml/VideoQualityLabels.qml</file>
         <file alias="VideoProgressBar.qml">widgets/qml/VideoProgressBar.qml</file>
         <file alias="NavigableCol.qml">widgets/qml/NavigableCol.qml</file>
diff --git a/modules/gui/qt/widgets/native/roundimage.cpp b/modules/gui/qt/widgets/native/roundimage.cpp
new file mode 100644
index 0000000000..49fa1946d2
--- /dev/null
+++ b/modules/gui/qt/widgets/native/roundimage.cpp
@@ -0,0 +1,324 @@
+/*****************************************************************************
+ * roundimage.cpp: Custom widgets
+ ****************************************************************************
+ * Copyright (C) 2021 the VideoLAN team
+ *
+ * Authors: Prince Gupta <guptaprince8832 at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "roundimage.hpp"
+
+#include <QBrush>
+#include <QImage>
+#include <QPainterPath>
+#include <QPen>
+#include <QQuickWindow>
+#include <QSvgRenderer>
+#include <QGuiApplication>
+
+#ifdef QT_NETWORK_LIB
+#include <QNetworkAccessManager>
+#include <QNetworkReply>
+#include <QNetworkRequest>
+#endif
+
+namespace
+{
+    QString getPath(const QUrl &url)
+    {
+        QString path = url.isLocalFile() ? url.toLocalFile() : url.toString();
+        if (path.startsWith("qrc:///"))
+            path.replace(0, strlen("qrc:///"), ":/");
+        return path;
+    }
+
+    QByteArray readFile(const QUrl &url)
+    {
+#ifdef QT_NETWORK_LIB
+        if (url.scheme() == "http" || url.scheme() == "https")
+        {
+            QNetworkAccessManager networkMgr;
+            networkMgr.setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
+            auto reply = networkMgr.get(QNetworkRequest(url));
+            QEventLoop loop;
+            QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
+            loop.exec();
+            return reply->readAll();
+        }
+#endif
+
+        QByteArray data;
+        QString path = getPath(url);
+        QFile file(path);
+        if (file.open(QIODevice::ReadOnly))
+            data = file.readAll();
+        return data;
+    }
+}
+
+RoundImage::RoundImage(QQuickItem *parent) : QQuickPaintedItem {parent}
+{
+    if (window() || qGuiApp)
+        setDPR(window() ? window()->devicePixelRatio() : qGuiApp->devicePixelRatio());
+
+    connect(this, &QQuickItem::heightChanged, this, &RoundImage::regenerateRoundImage);
+    connect(this, &QQuickItem::widthChanged, this, &RoundImage::regenerateRoundImage);
+}
+
+void RoundImage::paint(QPainter *painter)
+{
+    painter->drawImage(QPointF {0., 0.}, m_roundImage, m_roundImage.rect());
+}
+
+void RoundImage::classBegin()
+{
+    QQuickPaintedItem::classBegin();
+
+    m_isComponentComplete = false;
+}
+
+void RoundImage::componentComplete()
+{
+    QQuickPaintedItem::componentComplete();
+
+    Q_ASSERT(!m_isComponentComplete); // classBegin is not called?
+    m_isComponentComplete = true;
+    if (!m_source.isEmpty())
+        updateSource();
+}
+
+QUrl RoundImage::source() const
+{
+    return m_source;
+}
+
+qreal RoundImage::radius() const
+{
+    return m_radius;
+}
+
+QSizeF RoundImage::sourceSize() const
+{
+    return m_sourceSize;
+}
+
+void RoundImage::setSource(QUrl source)
+{
+    if (m_source == source)
+        return;
+
+    m_source = source;
+    emit sourceChanged(m_source);
+    updateSource();
+}
+
+void RoundImage::setRadius(qreal radius)
+{
+    if (m_radius == radius)
+        return;
+
+    m_radius = radius;
+    emit radiusChanged(m_radius);
+    regenerateRoundImage();
+}
+
+void RoundImage::setSourceSize(QSizeF sourceSize)
+{
+    if (m_sourceSize == sourceSize)
+        return;
+
+    m_sourceSize = sourceSize;
+    emit sourceSizeChanged(m_sourceSize);
+    updateSource();
+}
+
+void RoundImage::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
+{
+    if (change == QQuickItem::ItemDevicePixelRatioHasChanged)
+        setDPR(value.realValue);
+
+    QQuickPaintedItem::itemChange(change, value);
+}
+
+void RoundImage::setDPR(const qreal value)
+{
+    if (m_dpr == value)
+        return;
+
+    m_dpr = value;
+    if (m_sourceSize.isValid())
+        updateSource(); // "effectiveSourceSize" is changed
+    else
+        regenerateRoundImage();
+}
+
+void RoundImage::updateSource()
+{
+    if (!m_isComponentComplete)
+        return;
+
+    const QSizeF effectiveSourceSize = m_sourceSize.isValid() ? m_sourceSize * m_dpr : QSize {};
+    m_loader.reset(new Loader({m_source, effectiveSourceSize}));
+    connect(m_loader.get(), &BaseAsyncTask::result, this, [this]()
+    {
+        m_sourceImage = m_loader->takeResult();
+        m_loader.reset();
+
+        regenerateRoundImage();
+    });
+
+    m_loader->start(*QThreadPool::globalInstance());
+}
+
+void RoundImage::regenerateRoundImage()
+{
+    if (!m_isComponentComplete
+            || m_enqueuedGeneration
+            || m_loader /* when loader ends it will call regenerateRoundImage */)
+        return;
+
+    // use Qt::QueuedConnection to delay generation, so that dependent properties
+    // subsequent updates can be merged, f.e when VLCStyle.scale changes
+    m_enqueuedGeneration = true;
+
+    QMetaObject::invokeMethod(this, [this] ()
+    {
+        m_enqueuedGeneration = false;
+
+        // Image is generated in size factor of `m_dpr` to avoid scaling artefacts when
+        // generated image is set with device pixel ratio
+        m_roundImageGenerator.reset(new RoundImageGenerator({width() * m_dpr, height() * m_dpr, radius() * m_dpr, m_sourceImage}));
+        connect(m_roundImageGenerator.get(), &BaseAsyncTask::result, this, [this]()
+        {
+            m_roundImage = m_roundImageGenerator->takeResult();
+            m_roundImage.setDevicePixelRatio(m_dpr);
+            m_roundImageGenerator.reset();
+
+            update();
+        });
+
+        m_roundImageGenerator->start(*QThreadPool::globalInstance());
+    }, Qt::QueuedConnection);
+}
+
+RoundImage::Loader::Loader(const Params &params) : params {params} {}
+
+RoundImage::ImagePtr RoundImage::Loader::execute()
+{
+    return Image::getImage(params.source, params.sourceSize);
+}
+
+RoundImage::RoundImageGenerator::RoundImageGenerator(const RoundImage::RoundImageGenerator::Params &params) : params {params} {}
+
+QImage RoundImage::RoundImageGenerator::execute()
+{
+    if (params.width <= 0 || params.height <= 0)
+        return {};
+
+    QImage target(params.width, params.height, QImage::Format_ARGB32);
+    if (target.isNull())
+        return target;
+
+    target.fill(Qt::transparent);
+    if (Q_UNLIKELY(!params.image))
+        return target;
+
+    QPainter painter;
+    painter.begin(&target);
+    painter.setRenderHint(QPainter::Antialiasing, true);
+    painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
+
+    QPainterPath path;
+    path.addRoundedRect(0, 0, params.width, params.height, params.radius, params.radius);
+    painter.setClipPath(path);
+
+    params.image->paint(&painter, {params.width, params.height});
+    painter.end();
+
+    return target;
+}
+
+std::shared_ptr<RoundImage::Image> RoundImage::Image::getImage(const QUrl &source, const QSizeF &sourceSize)
+{
+    class QtImage : public Image
+    {
+    public:
+        QtImage(const QByteArray &data, const QSizeF &sourceSize)
+        {
+            m_image.loadFromData(data);
+
+            if (sourceSize.isValid())
+                m_image = m_image.scaled(sourceSize.toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
+        }
+
+        void paint(QPainter *painter, const QSizeF &size) override
+        {
+            if (m_image.isNull() || !painter || !size.isValid())
+                return;
+
+            auto image = m_image;
+
+            // do PreserveAspectCrop
+            const qreal ratio = std::max(qreal(size.width()) / image.width(), qreal(size.height()) / image.height());
+            if (ratio != 0.0)
+                image = image.scaled(qRound(image.width() * ratio), qRound(image.height() * ratio),
+                                     Qt::IgnoreAspectRatio, // aspect ratio handled manually by using `ratio`
+                                     Qt::SmoothTransformation);
+
+            const QPointF alignedCenteredTopLeft {(size.width() - image.width()) / 2, (size.height() - image.height()) / 2};
+            painter->drawImage(alignedCenteredTopLeft, image);
+        }
+
+    private:
+        QImage m_image;
+    };
+
+    class SVGImage : public Image
+    {
+    public:
+        SVGImage(const QByteArray &data)
+        {
+            m_svg.load(data);
+        }
+
+        void paint(QPainter *painter, const QSizeF &size) override
+        {
+            if (!m_svg.isValid() || !painter || !size.isValid())
+                return;
+
+            // do PreserveAspectCrop
+            const QSizeF defaultSize = m_svg.defaultSize();
+            const qreal ratio = std::max(size.width() / defaultSize.width(), size.height() / defaultSize.height());
+            const QSizeF targetSize = defaultSize * ratio;
+            const QPointF alignedCenteredTopLeft {(size.width() - targetSize.width()) / 2., (size.height() - targetSize.height()) / 2.};
+            m_svg.render(painter, QRectF {alignedCenteredTopLeft, targetSize});
+        }
+
+    private:
+        QSvgRenderer m_svg;
+    };
+
+    const QByteArray data = readFile(source);
+    if (source.toString().endsWith(".svg"))
+        return std::make_shared<SVGImage>(data);
+    return std::make_shared<QtImage>(data, sourceSize);
+}
diff --git a/modules/gui/qt/widgets/native/roundimage.hpp b/modules/gui/qt/widgets/native/roundimage.hpp
new file mode 100644
index 0000000000..db97dd4996
--- /dev/null
+++ b/modules/gui/qt/widgets/native/roundimage.hpp
@@ -0,0 +1,139 @@
+/*****************************************************************************
+ * roundimage.hpp: Custom widgets
+ ****************************************************************************
+ * Copyright (C) 2021 the VideoLAN team
+ *
+ * Authors: Prince Gupta <guptaprince8832 at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifndef VLC_QT_ROUNDIMAGE_HPP
+#define VLC_QT_ROUNDIMAGE_HPP
+
+#include "qt.hpp"
+
+#include "util/asynctask.hpp"
+
+#include <QPixmap>
+#include <QPainter>
+#include <QQuickPaintedItem>
+#include <QUrl>
+
+class RoundImage : public QQuickPaintedItem
+{
+    Q_OBJECT
+
+    // url of the image
+    Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
+
+    // sets the maximum number of pixels stored for the loaded image so that large images do not use more memory than necessary
+    Q_PROPERTY(QSizeF sourceSize READ sourceSize WRITE setSourceSize NOTIFY sourceSizeChanged)
+
+    Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged)
+
+public:
+    RoundImage(QQuickItem *parent = nullptr);
+
+    void paint(QPainter *painter) override;
+
+    void classBegin() override;
+    void componentComplete() override;
+
+    QUrl source() const;
+    qreal radius() const;
+    QSizeF sourceSize() const;
+
+public slots:
+    void setSource(QUrl source);
+    void setRadius(qreal radius);
+    void setSourceSize(QSizeF sourceSize);
+
+signals:
+    void sourceChanged(QUrl source);
+    void radiusChanged(int radius);
+    void sourceSizeChanged(QSizeF sourceSize);
+
+protected:
+    void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value) override;
+
+private:
+    class Image
+    {
+    public:
+        static std::shared_ptr<Image> getImage(const QUrl &source, const QSizeF &sourceSize);
+        virtual ~Image() = default;
+
+        // must be reentrant
+        virtual void paint(QPainter *painter, const QSizeF &size) = 0;
+    };
+
+    using ImagePtr = std::shared_ptr<Image>;
+
+    class Loader : public AsyncTask<ImagePtr>
+    {
+    public:
+        struct Params
+        {
+            QUrl source;
+            QSizeF sourceSize;
+        };
+
+        Loader(const Params &params);
+
+        ImagePtr execute();
+
+    private:
+        const Params params;
+    };
+
+    class RoundImageGenerator : public AsyncTask<QImage>
+    {
+    public:
+        struct Params
+        {
+            qreal width;
+            qreal height;
+            qreal radius;
+            ImagePtr image;
+        };
+
+        RoundImageGenerator(const Params &params);
+
+        QImage execute();
+
+    private:
+        const Params params;
+    };
+
+    void setDPR(qreal value);
+    void updateSource();
+    void regenerateRoundImage();
+
+    QUrl m_source;
+    ImagePtr m_sourceImage;
+    qreal m_radius = 0.0;
+    QSizeF m_sourceSize;
+    qreal m_dpr = 1.0; // device pixel ratio
+    QImage m_roundImage;
+    TaskHandle<Loader> m_loader {};
+    TaskHandle<RoundImageGenerator> m_roundImageGenerator {};
+
+    bool m_enqueuedGeneration = false;
+    bool m_isComponentComplete = true;
+};
+
+#endif
+
diff --git a/modules/gui/qt/widgets/qml/GridItem.qml b/modules/gui/qt/widgets/qml/GridItem.qml
index ec8d3972d8..48d2f1a822 100644
--- a/modules/gui/qt/widgets/qml/GridItem.qml
+++ b/modules/gui/qt/widgets/qml/GridItem.qml
@@ -249,7 +249,6 @@ FocusScope {
                 height: pictureHeight
                 playCoverVisible: root.highlighted
                 onPlayIconClicked: root.playClicked()
-                clip: true
                 radius: VLCStyle.gridCover_radius
             }
 
diff --git a/modules/gui/qt/widgets/qml/MediaCover.qml b/modules/gui/qt/widgets/qml/MediaCover.qml
index 1a9d9517e3..7b92d900c5 100644
--- a/modules/gui/qt/widgets/qml/MediaCover.qml
+++ b/modules/gui/qt/widgets/qml/MediaCover.qml
@@ -22,7 +22,9 @@ import QtQuick.Controls 2.4
 import "qrc:///widgets/" as Widgets
 import "qrc:///style/"
 
-Widgets.RoundImage {
+import org.videolan.controls 0.1
+
+RoundImage {
     id: root
 
     property alias playCoverOpacity: playCoverLoader.opacity
@@ -35,6 +37,7 @@ Widgets.RoundImage {
 
     height: VLCStyle.listAlbumCover_height
     width: VLCStyle.listAlbumCover_width
+    sourceSize: Qt.size(width, height)
 
     Loader {
         id: overlay
diff --git a/modules/gui/qt/widgets/qml/RoundImage.qml b/modules/gui/qt/widgets/qml/RoundImage.qml
deleted file mode 100644
index 4d9567cfc3..0000000000
--- a/modules/gui/qt/widgets/qml/RoundImage.qml
+++ /dev/null
@@ -1,56 +0,0 @@
-/*****************************************************************************
- * Copyright (C) 2019 VLC authors and VideoLAN
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * ( at your option ) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
- *****************************************************************************/
-import QtQuick 2.11
-import QtQuick.Controls 2.4
-import QtGraphicalEffects 1.0
-
-import "qrc:///style/"
-
-Item {
-    id: root
-
-    property real radius: 3
-    property alias asynchronous: cover.asynchronous
-    property alias fillMode: cover.fillMode
-    property alias mipmap: cover.mipmap
-    property alias paintedHeight: cover.paintedHeight
-    property alias paintedWidth: cover.paintedWidth
-    property alias source: cover.source
-    property alias sourceSize: cover.sourceSize
-    property alias status: cover.status
-    property alias horizontalAlignment: cover.horizontalAlignment
-    property alias verticalAlignment: cover.verticalAlignment
-
-    Image {
-        id: cover
-
-        anchors.fill: parent
-        asynchronous: true
-        fillMode: Image.PreserveAspectCrop
-        sourceSize: Qt.size(width, height)
-        layer.enabled: true
-        layer.effect: OpacityMask {
-            maskSource: Rectangle {
-                radius: root.radius
-                width: root.width
-                height: root.height
-                visible: false
-            }
-        }
-    }
-}
diff --git a/modules/gui/qt/widgets/qml/TableColumns.qml b/modules/gui/qt/widgets/qml/TableColumns.qml
index cf08caa897..71d46b188e 100644
--- a/modules/gui/qt/widgets/qml/TableColumns.qml
+++ b/modules/gui/qt/widgets/qml/TableColumns.qml
@@ -59,7 +59,6 @@ Item {
 
                 anchors.fill: parent
                 source: (rowModel ? (root.showTitleText ? rowModel.cover : rowModel[model.criteria]) : VLCStyle.noArtCover) || VLCStyle.noArtCover
-                mipmap: true // this widget can down scale the source a lot, so for better visuals we use mipmap
                 playCoverVisible: currentlyFocused || containsMouse
                 playIconSize: VLCStyle.play_cover_small
                 onPlayIconClicked: medialib.addAndPlay( rowModel.id )
-- 
2.27.0



More information about the vlc-devel mailing list