[plasma/union] src/output/qtquick/plugin: output/quick: Align icon to device pixels

Arjen Hiemstra <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit 13f0908d1c1133efdfd00291330194e16aaa05b6 by Arjen Hiemstra.
Committed on 03/08/2026 at 15:42.
Pushed by ahiemstra into branch 'master'.

output/quick: Align icon to device pixels

This aligns the image node of Icon to device pixels based on scene
coordinates. This ensures we never render on fractional pixels, which
causes rendering artifacts.

M  +17   -12   src/output/qtquick/plugin/Icon.cpp

https://invent.kde.org/plasma/union/-/commit/13f0908d1c1133efdfd00291330194e16aaa05b6

diff --git a/src/output/qtquick/plugin/Icon.cpp b/src/output/qtquick/plugin/Icon.cpp
index df43ae7f..9195f568 100644
--- a/src/output/qtquick/plugin/Icon.cpp
+++ b/src/output/qtquick/plugin/Icon.cpp
@@ -46,6 +46,7 @@ Icon::Icon(QQuickItem *parent)
     : QQuickItem(parent)
 {
     setFlag(QQuickItem::ItemHasContents, true);
+    setFlag(QQuickItem::ItemObservesViewport, true);
     setSmooth(false);
 }
 
@@ -173,12 +174,18 @@ QSGNode *Icon::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *)
 
     auto imageNode = static_cast<QSGImageNode *>(node);
 
-    auto bounds = boundingRect();
+    auto renderWindow = QQuickRenderControl::renderWindowFor(window());
+    if (!renderWindow) {
+        renderWindow = window();
+    }
 
-    imageNode->setRect(QRectF{std::round(bounds.x() + (bounds.width() - m_iconSize.width()) / 2.0),
-                              std::round(bounds.y() + (bounds.height() - m_iconSize.height()) / 2.0),
-                              qreal(m_iconSize.width()),
-                              qreal(m_iconSize.height())});
+    auto dpr = renderWindow->devicePixelRatio();
+
+    auto bounds = boundingRect();
+    auto centerPos = mapToScene(QPointF{std::round(bounds.x() + (bounds.width() - m_iconSize.width()) / 2.0), //
+                                        std::round(bounds.y() + (bounds.height() - m_iconSize.height()) / 2.0)});
+    auto correctedPos = mapFromScene(QPointF{std::round(centerPos.x() * dpr) / dpr, std::round(centerPos.y() * dpr) / dpr});
+    imageNode->setRect(QRectF{correctedPos, m_iconSize.toSizeF()});
     imageNode->setOwnsTexture(true);
 
     if (smooth()) {
@@ -187,18 +194,12 @@ QSGNode *Icon::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *)
         imageNode->setFiltering(QSGTexture::Nearest);
     }
 
-    auto renderWindow = QQuickRenderControl::renderWindowFor(window());
-    if (!renderWindow) {
-        renderWindow = window();
-    }
-
-    auto dpr = renderWindow->devicePixelRatio();
-
     if (m_iconChanged || !imageNode->texture() || !qFuzzyCompare(m_iconDpr, dpr)) {
         const auto mode = isEnabled() ? QIcon::Mode::Normal : QIcon::Mode::Disabled;
         auto image = m_icon.pixmap(m_iconSize, dpr, mode).toImage();
         imageNode->setTexture(window()->createTextureFromImage(image, QQuickWindow::TextureCanUseAtlas));
         m_iconChanged = false;
+        m_iconDpr = dpr;
     }
 
     return node;
@@ -221,6 +222,10 @@ void Icon::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChang
         update();
     }
 
+    if (change == QQuickItem::ItemChange::ItemTransformHasChanged) {
+        update();
+    }
+
     QQuickItem::itemChange(change, value);
 }
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.