From b0f9480d3c728ea32e11cb4f41c2e83b2826aa06 Mon Sep 17 00:00:00 2001 From: Andrew Wiltshire <62200778+AW1534@users.noreply.github.com> Date: Fri, 14 Mar 2025 19:05:45 +0000 Subject: [PATCH] Fix SVG Scaling; Allow SVGs to be rendered at sizes other than their natural size. (#7769) Allow embed.cpp to load SVG assets at screen DPI by leveraging QPixmap::setDevicePixelRatio --------- Co-authored-by: Tres Finocchiaro Co-authored-by: Fawn --- src/gui/embed.cpp | 54 +++++++++++++++++++++++- src/gui/tracks/TrackOperationsWidget.cpp | 5 +-- src/gui/widgets/PixmapButton.cpp | 6 ++- 3 files changed, 59 insertions(+), 6 deletions(-) diff --git a/src/gui/embed.cpp b/src/gui/embed.cpp index e912fd6d4..b47e2d75f 100644 --- a/src/gui/embed.cpp +++ b/src/gui/embed.cpp @@ -24,22 +24,74 @@ #include "embed.h" -#include #include +#include #include +#include #include #include +#include +#include namespace lmms::embed { namespace { +// QPixmapCache and HiDPI compatible SVG-->QPixmap wrapper +auto loadSvgPixmap(const QString& resourceName, int width, int height) -> QPixmap +{ + // QFile requires the file extension to be present, unlike QImageReader + QString fileName = resourceName; + if (!fileName.endsWith(".svg", Qt::CaseInsensitive)) { fileName += ".svg"; } + + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly)) + { + qWarning() << "Failed to open resource for SVG: " << resourceName; + return QPixmap{1, 1}; + } + + QSvgRenderer renderer(file.readAll()); + if (!renderer.isValid()) + { + qWarning() << "Error loading SVG file: " << resourceName; + return QPixmap{1, 1}; + } + + // Get the default size of the SVG (without scaling) + QSize svgSize = renderer.defaultSize(); + + // If width/height are provided, use them + if (width > 0 && height > 0) { svgSize.scale(width, height, Qt::IgnoreAspectRatio); } + + // Scale the svg + qreal devicePixelRatio = QGuiApplication::primaryScreen()->devicePixelRatio(); + svgSize *= devicePixelRatio; + + QImage image(svgSize, QImage::Format_ARGB32); + image.fill(Qt::transparent); + QPainter painter(&image); + renderer.render(&painter); + painter.end(); + + auto pixmap = QPixmap::fromImage(image); + pixmap.setDevicePixelRatio(devicePixelRatio); + + return pixmap; +} + auto loadPixmap(const QString& name, int width, int height, const char* const* xpm) -> QPixmap { if (xpm) { return QPixmap{xpm}; } const auto resourceName = QDir::isAbsolutePath(name) ? name : "artwork:" + name; auto reader = QImageReader{resourceName}; + + if (reader.format().toLower() == "svg") + { + return loadSvgPixmap(resourceName, width, height); + } + if (width > 0 && height > 0) { reader.setScaledSize(QSize{width, height}); } const auto pixmap = QPixmap::fromImageReader(&reader); diff --git a/src/gui/tracks/TrackOperationsWidget.cpp b/src/gui/tracks/TrackOperationsWidget.cpp index 559796b87..c43cd022c 100644 --- a/src/gui/tracks/TrackOperationsWidget.cpp +++ b/src/gui/tracks/TrackOperationsWidget.cpp @@ -108,10 +108,7 @@ TrackOperationsWidget::TrackOperationsWidget( TrackView * parent ) : const auto activePixmap = embed::getIconPixmap(activeGraphic); const auto inactivePixmap = embed::getIconPixmap(inactiveGraphic); - auto necessarySize = activePixmap.size().expandedTo(inactivePixmap.size()); - auto wrapperWidget = new QWidget(parent); - wrapperWidget->setFixedSize(necessarySize); auto button = new PixmapButton(wrapperWidget, toolTip); button->setCheckable(true); @@ -119,6 +116,8 @@ TrackOperationsWidget::TrackOperationsWidget( TrackView * parent ) : button->setInactiveGraphic(inactivePixmap); button->setToolTip(toolTip); + wrapperWidget->setFixedSize(button->minimumSizeHint()); + pixmapButton = button; return wrapperWidget; diff --git a/src/gui/widgets/PixmapButton.cpp b/src/gui/widgets/PixmapButton.cpp index 5f2e01b3d..bd683ed71 100644 --- a/src/gui/widgets/PixmapButton.cpp +++ b/src/gui/widgets/PixmapButton.cpp @@ -107,7 +107,7 @@ void PixmapButton::mouseDoubleClickEvent( QMouseEvent * _me ) void PixmapButton::setActiveGraphic( const QPixmap & _pm ) { m_activePixmap = _pm; - resize( m_activePixmap.width(), m_activePixmap.height() ); + resize(m_activePixmap.size() / m_activePixmap.devicePixelRatio()); } @@ -129,7 +129,9 @@ QSize PixmapButton::sizeHint() const QSize PixmapButton::minimumSizeHint() const { - return m_activePixmap.size().expandedTo(m_inactivePixmap.size()); + const auto activeSize = m_activePixmap.size() / m_activePixmap.devicePixelRatio(); + const auto inactiveSize = m_inactivePixmap.size() / m_inactivePixmap.devicePixelRatio(); + return activeSize.expandedTo(inactiveSize); } bool PixmapButton::isActive() const