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 <tres.finocchiaro@gmail.com>
Co-authored-by: Fawn <rubiefawn@gmail.com>
This commit is contained in:
Andrew Wiltshire
2025-03-14 19:05:45 +00:00
committed by GitHub
parent 2cd4810755
commit b0f9480d3c
3 changed files with 59 additions and 6 deletions

View File

@@ -24,22 +24,74 @@
#include "embed.h"
#include <QDebug>
#include <QDir>
#include <QGuiApplication>
#include <QImageReader>
#include <QPainter>
#include <QPixmapCache>
#include <QResource>
#include <QScreen>
#include <QSvgRenderer>
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);

View File

@@ -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;

View File

@@ -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