[plasma/union/Plasma/6.7] 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 1e562866a6734c68160fae1e23250aae95b264e0 by Arjen Hiemstra.
Committed on 04/08/2026 at 07:49.
Pushed by ahiemstra into branch 'Plasma/6.7'.

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.


(cherry picked from commit 13f0908d1c1133efdfd00291330194e16aaa05b6)

Co-authored-by: Arjen Hiemstra <[email protected]>

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

https://invent.kde.org/plasma/union/-/commit/1e562866a6734c68160fae1e23250aae95b264e0

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.