[vlc-commits] [Git][videolan/vlc][master] 4 commits: qt: rename applyRadius() to prepareImage() in RoundImage

Hugo Beauzée-Luyssen (@chouquette) gitlab at videolan.org
Fri Jul 29 06:39:54 UTC 2022



Hugo Beauzée-Luyssen pushed to branch master at VideoLAN / VLC


Commits:
996deea5 by Fatih Uzunoglu at 2022-07-29T06:19:05+00:00
qt: rename applyRadius() to prepareImage() in RoundImage

applyRadius() does more than its name suggests.

- - - - -
28c55101 by Fatih Uzunoglu at 2022-07-29T06:19:05+00:00
qt: introduce QSGRoundedRectangularImageNode

- - - - -
4436ec75 by Fatih Uzunoglu at 2022-07-29T06:19:05+00:00
qt: use custom geometry instead of clipping the image in RoundImage

This is an optimization for the rendering by making textures opaque when source image is also opaque.

Instead of clipping the image, a custom geometry denoting rounded rectangular shape is defined.

- - - - -
0f9b7e68 by Fatih Uzunoglu at 2022-07-29T06:19:05+00:00
qt: fix dpr adjustment in RoundImage

A window is assigned to a QQuickItem after it gets constructed.

- - - - -


5 changed files:

- modules/gui/qt/Makefile.am
- + modules/gui/qt/util/qsgroundedrectangularimagenode.cpp
- + modules/gui/qt/util/qsgroundedrectangularimagenode.hpp
- modules/gui/qt/widgets/native/roundimage.cpp
- modules/gui/qt/widgets/native/roundimage.hpp


Changes:

=====================================
modules/gui/qt/Makefile.am
=====================================
@@ -283,6 +283,8 @@ libqt_plugin_la_SOURCES = \
 	gui/qt/util/mouse_event_filter.hpp \
 	gui/qt/util/effects_image_provider.cpp \
 	gui/qt/util/effects_image_provider.hpp \
+	gui/qt/util/qsgroundedrectangularimagenode.cpp \
+	gui/qt/util/qsgroundedrectangularimagenode.hpp \
 	gui/qt/widgets/native/animators.cpp \
 	gui/qt/widgets/native/animators.hpp \
 	gui/qt/widgets/native/customwidgets.cpp gui/qt/widgets/native/customwidgets.hpp \
@@ -459,6 +461,7 @@ nodist_libqt_plugin_la_SOURCES = \
 	gui/qt/util/variables.moc.cpp \
 	gui/qt/util/vlctick.moc.cpp \
 	gui/qt/util/qmlinputitem.moc.cpp \
+	gui/qt/util/qsgroundedrectangularimagenode.moc.cpp \
 	gui/qt/widgets/native/animators.moc.cpp \
 	gui/qt/widgets/native/customwidgets.moc.cpp \
 	gui/qt/widgets/native/interface_widgets.moc.cpp \


=====================================
modules/gui/qt/util/qsgroundedrectangularimagenode.cpp
=====================================
@@ -0,0 +1,285 @@
+/*****************************************************************************
+ * Copyright (C) 2022 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.
+ *****************************************************************************/
+
+#include "qsgroundedrectangularimagenode.hpp"
+
+#include <QSGTextureMaterial>
+#include <QSGOpaqueTextureMaterial>
+
+#include <QCache>
+#include <QVector>
+#include <QPainterPath>
+
+template<class T>
+T QSGRoundedRectangularImageNode::material_cast(QSGMaterial* const material)
+{
+#ifdef NDEBUG
+    return static_cast<T>(object);
+#else
+    const auto ret = dynamic_cast<T>(material);
+    assert(ret); // incompatible material type
+    return ret;
+#endif
+}
+
+QSGRoundedRectangularImageNode::QSGRoundedRectangularImageNode()
+{
+    setFlags(QSGGeometryNode::OwnsMaterial |
+             QSGGeometryNode::OwnsOpaqueMaterial |
+             QSGGeometryNode::OwnsGeometry);
+
+    setMaterial(new QSGTextureMaterial);
+    setOpaqueMaterial(new QSGOpaqueTextureMaterial);
+
+    setSmooth(m_smooth);
+
+     // Useful for debugging:
+#ifdef QSG_RUNTIME_DESCRIPTION
+    qsgnode_set_description(this, QStringLiteral("RoundedRectangularImage"));
+#endif
+}
+
+QSGTextureMaterial *QSGRoundedRectangularImageNode::material() const
+{
+    return material_cast<QSGTextureMaterial*>(QSGGeometryNode::material());
+}
+
+QSGOpaqueTextureMaterial *QSGRoundedRectangularImageNode::opaqueMaterial() const
+{
+    return material_cast<QSGOpaqueTextureMaterial*>(QSGGeometryNode::opaqueMaterial());
+}
+
+void QSGRoundedRectangularImageNode::setSmooth(const bool smooth)
+{
+    if (m_smooth == smooth)
+        return;
+
+    {
+        const enum QSGTexture::Filtering filtering = smooth ? QSGTexture::Linear : QSGTexture::Nearest;
+        material()->setFiltering(filtering);
+        opaqueMaterial()->setFiltering(filtering);
+    }
+
+    {
+        const enum QSGTexture::Filtering mipmapFiltering = smooth ? QSGTexture::Linear : QSGTexture::None;
+        material()->setMipmapFiltering(mipmapFiltering);
+        opaqueMaterial()->setMipmapFiltering(mipmapFiltering);
+    }
+
+    markDirty(QSGNode::DirtyMaterial);
+}
+
+void QSGRoundedRectangularImageNode::setTexture(const std::shared_ptr<QSGTexture>& texture)
+{
+    assert(texture);
+
+    {
+        const bool wasAtlas = (!m_texture || m_texture->isAtlasTexture());
+
+        m_texture = texture;
+
+        // Unless we operate on atlas textures, it should be
+        // fine to not rebuild the geometry
+        if (wasAtlas || texture->isAtlasTexture())
+            rebuildGeometry(); // Texture coordinate mismatch
+    }
+
+    material()->setTexture(texture.get());
+    opaqueMaterial()->setTexture(texture.get());
+
+    markDirty(QSGNode::DirtyMaterial);
+}
+
+bool QSGRoundedRectangularImageNode::setShape(const Shape& shape)
+{
+    if (m_shape == shape)
+        return false;
+
+    const bool ret = rebuildGeometry(shape);
+
+    if (ret)
+        m_shape = shape;
+
+    return ret;
+}
+
+bool QSGRoundedRectangularImageNode::rebuildGeometry(const Shape& shape)
+{
+    QSGGeometry* const geometry = this->geometry();
+    QSGGeometry* const rebuiltGeometry = rebuildGeometry(shape,
+                                                         geometry,
+                                                         m_texture->isAtlasTexture() ? m_texture.get()
+                                                                                     : nullptr);
+    if (!rebuiltGeometry)
+    {
+        return false;
+    }
+    else if (rebuiltGeometry == geometry)
+    {
+        // Was able to reconstruct old geometry instance
+        markDirty(QSGNode::DirtyGeometry);
+    }
+    else
+    {
+        // - Dirty bit set implicitly
+        // - No need to remove the old geometry
+        setGeometry(rebuiltGeometry);
+    }
+
+    return true;
+}
+
+QSGGeometry* QSGRoundedRectangularImageNode::rebuildGeometry(const Shape& shape,
+                                                             QSGGeometry* geometry,
+                                                             const QSGTexture* const atlasTexture)
+{
+    if (!shape.isValid())
+        return nullptr;
+
+    int vertexCount;
+
+    QVector<QPointF> *path;
+    std::unique_ptr<QVector<QPointF>> upPath;
+
+    if (qFuzzyIsNull(shape.radius))
+    {
+        // 4 vertices are needed to construct
+        // a rectangle using triangle strip.
+        vertexCount = 4;
+        path = nullptr; // unused
+    }
+    else
+    {
+        using SizePair = QPair<qreal, qreal>;
+        using ShapePair = QPair<SizePair, qreal>;
+
+        // We could cache QSGGeometry itself, but
+        // it would not be really useful for atlas
+        // textures.
+        static QCache<ShapePair, QVector<QPointF>> paths;
+
+        ShapePair key {{shape.rect.width(), shape.rect.height()}, {shape.radius}};
+        if (paths.contains(key))
+        {
+            // There is no cache manipulation after this point,
+            // so path is assumed to be valid within its scope
+            path = paths.object(key);
+        }
+        else
+        {
+            QPainterPath painterPath;
+            painterPath.addRoundedRect(0, 0, key.first.first, key.first.second, key.second, key.second);
+            painterPath = painterPath.simplified();
+
+            path = new QVector<QPointF>(painterPath.elementCount());
+
+            const int elementCount = painterPath.elementCount();
+            for (int i = 0; i < elementCount; ++i)
+            {
+                // QPainterPath is not necessarily compatible with
+                // with GPU primitives. However, simplified painter path
+                // with rounded rectangle shape consists of painter path
+                // elements of types which can be drawn using primitives..
+                assert(painterPath.elementAt(i).type == QPainterPath::ElementType::MoveToElement
+                       || painterPath.elementAt(i).type == QPainterPath::ElementType::LineToElement);
+
+                // Symmetry based triangulation based on ordered painter path.
+                (*path)[i] = (painterPath.elementAt((i % 2) ? (i)
+                                                            : (elementCount - i - 1)));
+            }
+
+            if (!paths.insert(key, path))
+                upPath.reset(path); // Own the path so there is no leak
+        }
+
+        vertexCount = path->count();
+    }
+
+    if (!geometry)
+    {
+        geometry = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), vertexCount);
+        geometry->setIndexDataPattern(QSGGeometry::StaticPattern); // Is this necessary? Indexing is not used
+        geometry->setVertexDataPattern(QSGGeometry::StaticPattern);
+        geometry->setDrawingMode(QSGGeometry::DrawingMode::DrawTriangleStrip);
+    }
+    else
+    {
+        // Size check is implicitly done:
+        geometry->allocate(vertexCount);
+
+        // Assume the passed geometry is not a stray one.
+        // It is possible to check and create a new QSGGeometry
+        // if it is incompatible, but it should not be necessary.
+        // So instead, just pass QSGGeometry to this function that
+        // is either inherently compatible, or that is created by
+        // this function.
+
+        // These two are not required for compatibility,
+        // but lets still assert them for performance reasons.
+        assert(geometry->indexDataPattern() == QSGGeometry::StaticPattern);
+        assert(geometry->vertexDataPattern() == QSGGeometry::StaticPattern);
+
+        assert(geometry->drawingMode() == QSGGeometry::DrawingMode::DrawTriangleStrip);
+        assert(geometry->attributes() == QSGGeometry::defaultAttributes_TexturedPoint2D().attributes);
+        assert(geometry->sizeOfVertex() == QSGGeometry::defaultAttributes_TexturedPoint2D().stride);
+    }
+
+    QRectF texNormalSubRect;
+    if (atlasTexture)
+    {
+        // The texture might not be in the atlas, but it is okay.
+        texNormalSubRect = atlasTexture->normalizedTextureSubRect();
+    }
+    else
+    {
+        // In case no texture is given at all:
+        texNormalSubRect = {0.0, 0.0, 1.0, 1.0};
+    }
+
+    if (qFuzzyIsNull(shape.radius))
+    {
+        // Use the helper function to reconstruct the pure rectangular geometry:
+        QSGGeometry::updateTexturedRectGeometry(geometry, shape.rect, texNormalSubRect);
+    }
+    else
+    {
+        const auto mapToAtlasTexture = [texNormalSubRect] (const QPointF& nPoint) -> QPointF {
+            return {texNormalSubRect.x() + (texNormalSubRect.width() * nPoint.x()),
+                texNormalSubRect.y() + (texNormalSubRect.height() * nPoint.y())};
+        };
+
+        QSGGeometry::TexturedPoint2D* const points = geometry->vertexDataAsTexturedPoint2D();
+
+        const qreal dx = shape.rect.x();
+        const qreal dy = shape.rect.y();
+        for (int i = 0; i < geometry->vertexCount(); ++i)
+        {
+            const QPointF& pos = path->at(i);
+            QPointF tPos = {pos.x() / shape.rect.width(), pos.y() / shape.rect.height()};
+            if (atlasTexture)
+                tPos = mapToAtlasTexture(tPos);
+
+            points[i].set(pos.x() + dx, pos.y() + dy, tPos.x(), tPos.y());
+        }
+    }
+
+    geometry->markIndexDataDirty();
+    geometry->markVertexDataDirty();
+
+    return geometry;
+}


=====================================
modules/gui/qt/util/qsgroundedrectangularimagenode.hpp
=====================================
@@ -0,0 +1,87 @@
+/*****************************************************************************
+ * Copyright (C) 2022 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.
+ *****************************************************************************/
+
+#ifndef QSGROUNDEDRECTANGULARIMAGENODE_HPP
+#define QSGROUNDEDRECTANGULARIMAGENODE_HPP
+
+#include <QSGGeometryNode>
+
+#include <memory>
+#include <cmath>
+
+class QSGTextureMaterial;
+class QSGOpaqueTextureMaterial;
+class QSGTexture;
+
+class QSGRoundedRectangularImageNode : public QSGGeometryNode
+{
+    template<class T>
+    static T material_cast(QSGMaterial* const material);
+
+public:
+    struct Shape
+    {
+        QRectF rect;
+        qreal radius = 0.0;
+
+        constexpr bool operator ==(const Shape& b) const
+        {
+            return (rect == b.rect && qFuzzyCompare(radius, b.radius));
+        }
+
+        constexpr bool isValid() const
+        {
+            return (!rect.isEmpty() && std::isgreaterequal(radius, 0.0));
+        }
+    };
+
+    QSGRoundedRectangularImageNode();
+
+    // For convenience:
+    QSGTextureMaterial* material() const;
+    QSGOpaqueTextureMaterial* opaqueMaterial() const;
+
+    void setSmooth(const bool smooth);
+    void setTexture(const std::shared_ptr<QSGTexture>& texture);
+
+    inline constexpr Shape shape() const
+    {
+        return m_shape;
+    }
+
+    bool setShape(const Shape& shape);
+
+    inline bool rebuildGeometry()
+    {
+        return rebuildGeometry(m_shape);
+    }
+
+    bool rebuildGeometry(const Shape& shape);
+
+    // Constructs a geometry denoting rounded rectangle using QPainterPath
+    static QSGGeometry* rebuildGeometry(const Shape& shape,
+                                        QSGGeometry* geometry,
+                                        const QSGTexture* const atlasTexture = nullptr);
+
+private:
+    std::shared_ptr<QSGTexture> m_texture;
+    Shape m_shape;
+    bool m_smooth = true;
+};
+
+#endif // QSGROUNDEDRECTANGULARIMAGENODE_HPP


=====================================
modules/gui/qt/widgets/native/roundimage.cpp
=====================================
@@ -27,6 +27,7 @@
 
 #include "roundimage.hpp"
 #include "util/asynctask.hpp"
+#include "util/qsgroundedrectangularimagenode.hpp"
 
 #include <qhashfunctions.h>
 
@@ -77,8 +78,13 @@ namespace
     // images are cached (result of RoundImageGenerator) with the cost calculated from QImage::sizeInBytes
     QCache<ImageCacheKey, QImage> imageCache(32 * 1024 * 1024); // 32 MiB
 
-    QImage applyRadius(const QSize &targetSize, const qreal radius, const QImage sourceImage)
+    QImage prepareImage(const QSize &targetSize, const qreal radius, const QImage sourceImage)
     {
+        if (qFuzzyIsNull(radius))
+        {
+            return sourceImage.scaled(targetSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+        }
+
         QImage target(targetSize, QImage::Format_ARGB32_Premultiplied);
         if (target.isNull())
             return target;
@@ -130,7 +136,7 @@ namespace
             errorStr = reader.errorString();
 
             if (!errorStr.isEmpty())
-                img = applyRadius(requestedSize.isValid() ? requestedSize : img.size(), radius, img);
+                img = prepareImage(requestedSize.isValid() ? requestedSize : img.size(), radius, img);
 
             return img;
         }
@@ -272,7 +278,7 @@ namespace
 
             QImage execute()
             {
-                return applyRadius(sourceImg.size(), radius, sourceImg);
+                return prepareImage(sourceImg.size(), radius, sourceImg);
             }
 
         private:
@@ -351,11 +357,20 @@ namespace
 
 RoundImage::RoundImage(QQuickItem *parent) : QQuickItem {parent}
 {
-    if (window() || qGuiApp)
-        setDPR(window() ? window()->devicePixelRatio() : qGuiApp->devicePixelRatio());
+    if (Q_LIKELY(qGuiApp))
+        setDPR(qGuiApp->devicePixelRatio());
 
     connect(this, &QQuickItem::heightChanged, this, &RoundImage::regenerateRoundImage);
     connect(this, &QQuickItem::widthChanged, this, &RoundImage::regenerateRoundImage);
+
+    connect(this, &QQuickItem::windowChanged, this, [this](const QQuickWindow* const window) {
+        if (window)
+            setDPR(window->devicePixelRatio());
+        else if (Q_LIKELY(qGuiApp))
+            setDPR(qGuiApp->devicePixelRatio());
+    });
+
+    connect(this, &QQuickItem::windowChanged, this, &RoundImage::adjustQSGCustomGeometry);
 }
 
 RoundImage::~RoundImage()
@@ -365,7 +380,17 @@ RoundImage::~RoundImage()
 
 QSGNode *RoundImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
 {
-    auto node = static_cast<QSGImageNode *>(oldNode);
+    auto customImageNode = dynamic_cast<QSGRoundedRectangularImageNode*>(oldNode);
+    auto imageNode = dynamic_cast<QSGImageNode*>(oldNode);
+
+    if (Q_UNLIKELY(oldNode && ((m_QSGCustomGeometry && !customImageNode)
+                               || (!m_QSGCustomGeometry && !imageNode))))
+    {
+        // This must be extremely unlikely.
+        // Assigned to different window with different renderer?
+        delete oldNode;
+        oldNode = nullptr;
+    }
 
     if (m_roundImage.isNull())
     {
@@ -374,12 +399,19 @@ QSGNode *RoundImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
         return nullptr;
     }
 
-    if (!node)
+    if (!oldNode)
     {
-        assert(window());
-        node = window()->createImageNode();
-        assert(node);
-        node->setOwnsTexture(true);
+        if (m_QSGCustomGeometry)
+        {
+            customImageNode = new QSGRoundedRectangularImageNode;
+        }
+        else
+        {
+            assert(window());
+            imageNode = window()->createImageNode();
+            assert(imageNode);
+            imageNode->setOwnsTexture(true);
+        }
     }
 
     if (m_dirty)
@@ -388,16 +420,21 @@ QSGNode *RoundImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
         assert(window());
 
         QQuickWindow::CreateTextureOptions flags = QQuickWindow::TextureCanUseAtlas;
-        if (Q_LIKELY(m_roundImage.hasAlphaChannel()))
-            flags |= QQuickWindow::TextureHasAlphaChannel;
 
-        QSGTexture* texture = window()->createTextureFromImage(m_roundImage, flags);
+        if (!m_roundImage.hasAlphaChannel())
+            flags |= QQuickWindow::TextureIsOpaque;
 
-        if (texture)
+        if (std::unique_ptr<QSGTexture> texture { window()->createTextureFromImage(m_roundImage, flags) })
         {
-            // No need to delete the old texture manually as it is owned by the node.
-            node->setTexture(texture);
-            node->markDirty(QSGNode::DirtyMaterial);
+            if (m_QSGCustomGeometry)
+            {
+                customImageNode->setTexture(std::move(texture));
+            }
+            else
+            {
+                // No need to delete the old texture manually as it is owned by the node.
+                imageNode->setTexture(texture.release());
+            }
         }
         else
         {
@@ -405,9 +442,20 @@ QSGNode *RoundImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
         }
     }
 
-    node->setRect(boundingRect());
-
-    return node;
+    // Geometry:
+    if (m_QSGCustomGeometry)
+    {
+        customImageNode->setShape({boundingRect(), radius()});
+        customImageNode->setSmooth(smooth());
+        assert(customImageNode->geometry() && customImageNode->material());
+        return customImageNode;
+    }
+    else
+    {
+        imageNode->setRect(boundingRect());
+        imageNode->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest);
+        return imageNode;
+    }
 }
 
 void RoundImage::componentComplete()
@@ -450,7 +498,6 @@ void RoundImage::setRadius(qreal radius)
 
     m_radius = radius;
     emit radiusChanged(m_radius);
-    regenerateRoundImage();
 }
 
 void RoundImage::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
@@ -493,9 +540,10 @@ void RoundImage::handleImageResponseFinished()
     setRoundImage(image);
     setStatus(Status::Ready);
 
+    // FIXME: These should be gathered from the generator:
     const qreal scaledWidth = this->width() * m_dpr;
     const qreal scaledHeight = this->height() * m_dpr;
-    const qreal scaledRadius = this->radius() * m_dpr;
+    const qreal scaledRadius = m_QSGCustomGeometry ? 0.0 : (this->radius() * m_dpr);
 
     const ImageCacheKey key {source(), QSizeF {scaledWidth, scaledHeight}.toSize(), scaledRadius};
     imageCache.insert(key, new QImage(image), image.sizeInBytes());
@@ -523,7 +571,7 @@ void RoundImage::load()
 
     const qreal scaledWidth = this->width() * m_dpr;
     const qreal scaledHeight = this->height() * m_dpr;
-    const qreal scaledRadius = this->radius() * m_dpr;
+    const qreal scaledRadius = m_QSGCustomGeometry ? 0.0 : (this->radius() * m_dpr);
 
     const ImageCacheKey key {source(), QSizeF {scaledWidth, scaledHeight}.toSize(), scaledRadius};
     if (auto image = imageCache.object(key)) // should only by called in mainthread
@@ -580,3 +628,83 @@ void RoundImage::regenerateRoundImage()
 
     QMetaObject::invokeMethod(this, &RoundImage::load, Qt::QueuedConnection);
 }
+
+void RoundImage::adjustQSGCustomGeometry(const QQuickWindow* const window)
+{
+    if (!window) return;
+
+    // No need to check if the scene graph is initialized according to docs.
+
+    const auto enableCustomGeometry = [this, window]() {
+        if (m_QSGCustomGeometry)
+            return;
+
+        // Favor custom geometry instead of clipping the image.
+        // This allows making the texture opaque, as long as
+        // source image is also opaque, for optimization
+        // purposes.
+        if (window->format().samples() != -1)
+            m_QSGCustomGeometry = true;
+        // No need to regenerate as transparent part will not
+        // matter. However, in order for the material to not
+        // require blending, a regeneration is necessary.
+        // We could force the material to not require blending
+        // for the outer transparent part which is not within the
+        // geometry, but then inherently transparent images would
+        // not be rendered correctly due to the alpha channel.
+
+        QMetaObject::invokeMethod(this,
+                                  &RoundImage::regenerateRoundImage,
+                                  Qt::QueuedConnection);
+
+        // It might be tempting to not regenerate the image on size
+        // change. However;
+        // If upscaled, we don't know if the image can be provided
+        // in a higher resolution.
+        // If downscaled, we would like to free some used memory.
+        // On the other hand, there is no need to regenerate the
+        // image when the radius changes. This behavior does not
+        // mean that the radius can be animated. Although possible,
+        // the custom geometry node is not designed to handle animations..
+        disconnect(this, &RoundImage::radiusChanged, this, &RoundImage::regenerateRoundImage);
+        connect(this, &RoundImage::radiusChanged, this, &QQuickItem::update);
+    };
+
+    const auto disableCustomGeometry = [this]() {
+        if (!m_QSGCustomGeometry)
+            return;
+
+        m_QSGCustomGeometry = false;
+
+        QMetaObject::invokeMethod(this,
+                                  &RoundImage::regenerateRoundImage,
+                                  Qt::QueuedConnection);
+
+        connect(this, &RoundImage::radiusChanged, this, &RoundImage::regenerateRoundImage);
+        disconnect(this, &RoundImage::radiusChanged, this, &QQuickItem::update);
+    };
+
+
+    if (window->rendererInterface()->graphicsApi() == QSGRendererInterface::GraphicsApi::OpenGL)
+    {
+        // Direct OpenGL, enable custom geometry:
+        enableCustomGeometry();
+    }
+    else
+    {
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+        // QSG(Opaque)TextureMaterial supports Qt RHI,
+        // so there is no obstacle using custom geometry
+        // if Qt RHI is in use.
+        if (QSGRendererInterface::isApiRhiBased(window->rendererInterface()->graphicsApi()))
+            enableCustomGeometry();
+        else
+            disableCustomGeometry();
+#else
+        // Qt RHI is introduced in Qt 5.14.
+        // QSG(Opaque)TextureMaterial does not support any graphics API other than OpenGL
+        // without the Qt RHI abstraction layer.
+        disableCustomGeometry();
+#endif
+    }
+}


=====================================
modules/gui/qt/widgets/native/roundimage.hpp
=====================================
@@ -83,12 +83,15 @@ private:
     void setRoundImage(QImage image);
     void setStatus(const Status status);
     void regenerateRoundImage();
+    Q_SLOT void adjustQSGCustomGeometry(const QQuickWindow* const window);
 
     QUrl m_source;
     qreal m_radius = 0.0;
     qreal m_dpr = 1.0; // device pixel ratio
     Status m_status = Status::Null;
 
+    bool m_QSGCustomGeometry = false;
+
     QImage m_roundImage;
     bool m_dirty = false;
 



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/728ab28a0747176ef929fe87b4026996a62fbe8e...0f9b7e6871304a2fad4f6a69a77b8b6404302955

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/728ab28a0747176ef929fe87b4026996a62fbe8e...0f9b7e6871304a2fad4f6a69a77b8b6404302955
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the vlc-commits mailing list