Fix pixelated icons in style lists on hi dpi displays

Use QgsScreenProperties in style model to ensure we
correctly generate preview icons matching the device
pixel ratio and DPI of all attached displays
This commit is contained in:
Nyall Dawson 2023-06-22 11:14:15 +10:00
parent b5e3af7a3a
commit ff7a5c934c
17 changed files with 184 additions and 123 deletions

View File

@ -636,7 +636,7 @@ Sets the label placement ``settings``.
.. versionadded:: 3.26
%End
static QPixmap labelSettingsPreviewPixmap( const QgsPalLayerSettings &settings, QSize size, const QString &previewText = QString(), int padding = 0, double devicePixelRatio = 1 );
static QPixmap labelSettingsPreviewPixmap( const QgsPalLayerSettings &settings, QSize size, const QString &previewText = QString(), int padding = 0, const QgsScreenProperties &screen = QgsScreenProperties() );
%Docstring
Returns a pixmap preview for label ``settings``.
@ -644,7 +644,7 @@ Returns a pixmap preview for label ``settings``.
:param size: target pixmap size
:param previewText: text to render in preview, or empty for default text
:param padding: space between icon edge and color ramp
:param devicePixelRatio: can be used to generate a pixmap using a specific device pixel ratio (since QGIS 3.32)
:param screen: can be used to specify the destination screen properties for the icon. This allows the icon to be generated using the correct DPI and device pixel ratio for the target screen (since QGIS 3.32)
.. versionadded:: 3.10
%End

View File

@ -35,6 +35,10 @@ Constructor for QgsScreenProperties, copying
properties from the specified ``screen``.
%End
bool operator==( const QgsScreenProperties &other ) const;
bool operator!=( const QgsScreenProperties &other ) const;
bool isValid() const;
%Docstring
Returns ``True`` if the properties are valid.
@ -70,6 +74,7 @@ to match the screen settings.
};
/************************************************************************
* This file has been generated automatically from *
* *

View File

@ -9,8 +9,6 @@
class QgsCombinedStyleModel: QConcatenateTablesProxyModel
{
%Docstring(signature="appended")
@ -79,12 +77,12 @@ This allows style icons to be generated at an icon size which
corresponds exactly to the view's icon size in which this model is used.
%End
void addDesiredIconDevicePixelRatio( double ratio );
void addTargetScreenProperties( const QgsScreenProperties &properties );
%Docstring
Adds an additional icon device pixel ``ratio`` to generate for Qt.DecorationRole data.
Adds additional target screen ``properties`` to use when generating icons for Qt.DecorationRole data.
This allows style icons to be generated at an icon device pixel ratio which
corresponds exactly to the view's icon size in which this model is used.
This allows style icons to be generated at an icon device pixel ratio and DPI which
corresponds exactly to the view's screen properties in which this model is used.
.. versionadded:: 3.32
%End

View File

@ -90,12 +90,12 @@ This allows style icons to be generated at an icon size which
corresponds exactly to the view's icon size in which this model is used.
%End
void addDesiredIconDevicePixelRatio( double ratio );
void addTargetScreenProperties( const QgsScreenProperties &properties );
%Docstring
Adds an additional icon device pixel ``ratio`` to generate for Qt.DecorationRole data.
Adds additional target screen ``properties`` to use when generating icons for Qt.DecorationRole data.
This allows style icons to be generated at an icon device pixel ratio which
corresponds exactly to the view's icon size in which this model is used.
This allows style icons to be generated at an icon device pixel ratio and DPI which
corresponds exactly to the view's screen properties in which this model is used.
.. versionadded:: 3.32
%End
@ -364,12 +364,12 @@ This allows style icons to be generated at an icon size which
corresponds exactly to the view's icon size in which this model is used.
%End
void addDesiredIconDevicePixelRatio( double ratio );
void addTargetScreenProperties( const QgsScreenProperties &properties );
%Docstring
Adds an additional icon device pixel ``ratio`` to generate for Qt.DecorationRole data.
Adds additional target screen ``properties`` to use when generating icons for Qt.DecorationRole data.
This allows style icons to be generated at an icon device pixel ratio which
corresponds exactly to the view's icon size in which this model is used.
This allows style icons to be generated at an icon device pixel ratio and DPI which
corresponds exactly to the view's screen properties in which this model is used.
.. versionadded:: 3.32
%End

View File

@ -718,7 +718,7 @@ Updates the format by evaluating current values of data defined properties.
.. versionadded:: 3.10
%End
static QPixmap textFormatPreviewPixmap( const QgsTextFormat &format, QSize size, const QString &previewText = QString(), int padding = 0, double devicePixelRatio = 1 );
static QPixmap textFormatPreviewPixmap( const QgsTextFormat &format, QSize size, const QString &previewText = QString(), int padding = 0, const QgsScreenProperties &screen = QgsScreenProperties() );
%Docstring
Returns a pixmap preview for a text ``format``.
@ -726,7 +726,7 @@ Returns a pixmap preview for a text ``format``.
:param size: target pixmap size
:param previewText: text to render in preview, or empty for default text
:param padding: space between icon edge and color ramp
:param devicePixelRatio: can be used to generate a pixmap using a specific device pixel ratio (since QGIS 3.32)
:param screen: can be used to specify the destination screen properties for the icon. This allows the icon to be generated using the correct DPI and device pixel ratio for the target screen (since QGIS 3.32)
.. versionadded:: 3.10
%End

View File

@ -1357,8 +1357,10 @@ void QgsPalLayerSettings::setCallout( QgsCallout *callout )
mCallout.reset( callout );
}
QPixmap QgsPalLayerSettings::labelSettingsPreviewPixmap( const QgsPalLayerSettings &settings, QSize size, const QString &previewText, int padding, double devicePixelRatio )
QPixmap QgsPalLayerSettings::labelSettingsPreviewPixmap( const QgsPalLayerSettings &settings, QSize size, const QString &previewText, int padding, const QgsScreenProperties &screen )
{
const double devicePixelRatio = screen.isValid() ? screen.devicePixelRatio() : 1;
// for now, just use format
QgsTextFormat tempFormat = settings.format();
QPixmap pixmap( size * devicePixelRatio );
@ -1406,10 +1408,24 @@ QPixmap QgsPalLayerSettings::labelSettingsPreviewPixmap( const QgsPalLayerSettin
context.setMapToPixel( newCoordXForm );
context.setFlag( Qgis::RenderContextFlag::Antialiasing, true );
QWidget *activeWindow = QApplication::activeWindow();
const double physicalDpiX = activeWindow && activeWindow->screen() ? activeWindow->screen()->physicalDotsPerInchX() : 96.0;
context.setScaleFactor( physicalDpiX / 25.4 );
context.setDevicePixelRatio( devicePixelRatio );
if ( screen.isValid() )
{
screen.updateRenderContextForScreen( context );
}
else
{
QWidget *activeWindow = QApplication::activeWindow();
if ( QScreen *screen = activeWindow ? activeWindow->screen() : nullptr )
{
context.setScaleFactor( screen->physicalDotsPerInch() / 25.4 );
context.setDevicePixelRatio( screen->devicePixelRatio() );
}
else
{
context.setScaleFactor( 96.0 / 25.4 );
context.setDevicePixelRatio( 1.0 );
}
}
context.setUseAdvancedEffects( true );
context.setPainter( &painter );

View File

@ -968,10 +968,10 @@ class CORE_EXPORT QgsPalLayerSettings
* \param size target pixmap size
* \param previewText text to render in preview, or empty for default text
* \param padding space between icon edge and color ramp
* \param devicePixelRatio can be used to generate a pixmap using a specific device pixel ratio (since QGIS 3.32)
* \param screen can be used to specify the destination screen properties for the icon. This allows the icon to be generated using the correct DPI and device pixel ratio for the target screen (since QGIS 3.32)
* \since QGIS 3.10
*/
static QPixmap labelSettingsPreviewPixmap( const QgsPalLayerSettings &settings, QSize size, const QString &previewText = QString(), int padding = 0, double devicePixelRatio = 1 );
static QPixmap labelSettingsPreviewPixmap( const QgsPalLayerSettings &settings, QSize size, const QString &previewText = QString(), int padding = 0, const QgsScreenProperties &screen = QgsScreenProperties() );
/**
* Returns the layer's unplaced label visibility.

View File

@ -16,6 +16,8 @@
#define QGSSCREENPROPERTIES_H
#include "qgis_core.h"
#include "qgis_sip.h"
#include "qgis.h"
class QScreen;
class QgsRenderContext;
@ -46,6 +48,18 @@ class CORE_EXPORT QgsScreenProperties
*/
explicit QgsScreenProperties( const QScreen *screen );
bool operator==( const QgsScreenProperties &other ) const
{
return mValid == other.mValid
&& mDevicePixelRatio == other.mDevicePixelRatio
&& mPhysicalDpi == other.mPhysicalDpi;
}
bool operator!=( const QgsScreenProperties &other ) const
{
return !( *this == other );
}
/**
* Returns TRUE if the properties are valid.
*/
@ -84,4 +98,14 @@ class CORE_EXPORT QgsScreenProperties
double mPhysicalDpi = 96;
};
/**
* Returns a hash for a screen \a properties.
*
* \since QGIS 3.32
*/
CORE_EXPORT inline uint qHash( const QgsScreenProperties &properties ) SIP_SKIP
{
return qHash( properties.devicePixelRatio() ) + qHash( properties.physicalDpi() );
}
#endif // QGSSCREENPROPERTIES_H

View File

@ -76,9 +76,9 @@ void QgsCombinedStyleModel::addStyle( QgsStyle *style )
styleModel->addDesiredIconSize( size );
}
for ( double ratio : std::as_const( mDevicePixelRatios ) )
for ( auto it = mTargetScreenProperties.constBegin(); it != mTargetScreenProperties.constEnd(); ++it )
{
styleModel->addDesiredIconDevicePixelRatio( ratio );
styleModel->addTargetScreenProperties( *it );
}
addSourceModel( styleModel );
@ -135,9 +135,9 @@ void QgsCombinedStyleModel::addDefaultStyle()
styleModel->addDesiredIconSize( size );
}
for ( double ratio : std::as_const( mDevicePixelRatios ) )
for ( auto it = mTargetScreenProperties.constBegin(); it != mTargetScreenProperties.constEnd(); ++it )
{
styleModel->addDesiredIconDevicePixelRatio( ratio );
styleModel->addTargetScreenProperties( *it );
}
addSourceModel( styleModel );
@ -164,18 +164,18 @@ void QgsCombinedStyleModel::addDesiredIconSize( QSize size )
}
}
void QgsCombinedStyleModel::addDesiredIconDevicePixelRatio( double ratio )
void QgsCombinedStyleModel::addTargetScreenProperties( const QgsScreenProperties &properties )
{
if ( !mDevicePixelRatios.contains( ratio ) )
mDevicePixelRatios.append( ratio );
if ( !mTargetScreenProperties.contains( properties ) )
mTargetScreenProperties.insert( properties );
for ( auto it = mOwnedStyleModels.constBegin(); it != mOwnedStyleModels.constEnd(); ++it )
{
it.value()->addDesiredIconDevicePixelRatio( ratio );
it.value()->addTargetScreenProperties( properties );
}
if ( mStyles.contains( QgsStyle::defaultStyle() ) )
{
QgsApplication::defaultStyleModel()->addDesiredIconDevicePixelRatio( ratio );
QgsApplication::defaultStyleModel()->addTargetScreenProperties( properties );
}
}

View File

@ -19,13 +19,13 @@
#include "qgis_core.h"
#include "qgis_sip.h"
#include <QtGlobal>
#include <QSet>
#include <QConcatenateTablesProxyModel>
class QgsStyle;
class QgsStyleModel;
class QgsSingleItemModel;
class QgsScreenProperties;
/**
* \ingroup core
@ -92,14 +92,14 @@ class CORE_EXPORT QgsCombinedStyleModel: public QConcatenateTablesProxyModel
void addDesiredIconSize( QSize size );
/**
* Adds an additional icon device pixel \a ratio to generate for Qt::DecorationRole data.
* Adds additional target screen \a properties to use when generating icons for Qt::DecorationRole data.
*
* This allows style icons to be generated at an icon device pixel ratio which
* corresponds exactly to the view's icon size in which this model is used.
* This allows style icons to be generated at an icon device pixel ratio and DPI which
* corresponds exactly to the view's screen properties in which this model is used.
*
* \since QGIS 3.32
*/
void addDesiredIconDevicePixelRatio( double ratio );
void addTargetScreenProperties( const QgsScreenProperties &properties );
private:
@ -108,7 +108,7 @@ class CORE_EXPORT QgsCombinedStyleModel: public QConcatenateTablesProxyModel
QHash< QgsStyle *, QgsSingleItemModel * > mTitleModels;
QList< QSize > mAdditionalSizes;
QList< double > mDevicePixelRatios;
QSet< QgsScreenProperties > mTargetScreenProperties;
};

View File

@ -39,13 +39,6 @@ QgsAbstractStyleEntityIconGenerator *QgsStyleModel::sIconGenerator = nullptr;
QgsAbstractStyleEntityIconGenerator::QgsAbstractStyleEntityIconGenerator( QObject *parent )
: QObject( parent )
{
// Default to generating icons using the application devicePixelRatio.
// Additional ratios may be added by individual views to this model (eg on
// different screens with different pixel ratios)
if ( QGuiApplication *guiApplication = qobject_cast< QGuiApplication * >( QApplication::instance() ) )
mDevicePixelRatios.append( guiApplication->devicePixelRatio() );
else
mDevicePixelRatios.append( 1.0 );
}
void QgsAbstractStyleEntityIconGenerator::setIconSizes( const QList<QSize> &sizes )
@ -58,14 +51,14 @@ QList<QSize> QgsAbstractStyleEntityIconGenerator::iconSizes() const
return mIconSizes;
}
void QgsAbstractStyleEntityIconGenerator::setDevicePixelRatios( const QList<double> &ratios )
void QgsAbstractStyleEntityIconGenerator::setTargetScreenProperties( const QSet<QgsScreenProperties> &properties )
{
mDevicePixelRatios = ratios;
mTargetScreenProperties = properties;
}
QList<double> QgsAbstractStyleEntityIconGenerator::devicePixelRatios() const
QSet<QgsScreenProperties> QgsAbstractStyleEntityIconGenerator::targetScreenProperties() const
{
return mDevicePixelRatios;
return mTargetScreenProperties;
}
@ -79,19 +72,15 @@ QgsStyleModel::QgsStyleModel( QgsStyle *style, QObject *parent )
{
Q_ASSERT( mStyle );
// Default to generating icons using the application devicePixelRatio.
// Additional ratios may be added by individual views to this model (eg on
// different screens with different pixel ratios)
if ( QGuiApplication *guiApplication = qobject_cast< QGuiApplication * >( QApplication::instance() ) )
mDevicePixelRatios.append( guiApplication->devicePixelRatio() );
else
mDevicePixelRatios.append( 1.0 );
for ( QgsStyle::StyleEntity entity : ENTITIES )
{
mEntityNames.insert( entity, mStyle->allNames( entity ) );
}
// ensure we always generate icons using default screen properties
// in addition to actual target screen properties (ie device pixel ratio of 1, 96 dpi)
mTargetScreenProperties.insert( QgsScreenProperties() );
connect( mStyle, &QgsStyle::entityAdded, this, &QgsStyleModel::onEntityAdded );
connect( mStyle, &QgsStyle::entityRemoved, this, &QgsStyleModel::onEntityRemoved );
connect( mStyle, &QgsStyle::entityRenamed, this, &QgsStyleModel::onEntityRename );
@ -149,6 +138,14 @@ QVariant QgsStyleModel::data( const QModelIndex &index, int role ) const
QString tooltip = QStringLiteral( "<h3>%1</h3><p><i>%2</i>" ).arg( name,
tags.count() > 0 ? tags.join( QLatin1String( ", " ) ) : tr( "Not tagged" ) );
// generate tooltips targeting the largest device pixel ratio for all attached screens
QgsScreenProperties maxDevicePixelRatioScreen;
for ( auto it = mTargetScreenProperties.constBegin(); it != mTargetScreenProperties.constEnd(); ++it )
{
if ( !maxDevicePixelRatioScreen.isValid() || it->devicePixelRatio() > maxDevicePixelRatioScreen.devicePixelRatio() )
maxDevicePixelRatioScreen = *it;
}
switch ( entityType )
{
case QgsStyle::SymbolEntity:
@ -159,11 +156,11 @@ QVariant QgsStyleModel::data( const QModelIndex &index, int role ) const
{
int width = static_cast< int >( Qgis::UI_SCALE_FACTOR * QFontMetrics( data( index, Qt::FontRole ).value< QFont >() ).horizontalAdvance( 'X' ) * 23 );
int height = static_cast< int >( width / 1.61803398875 ); // golden ratio
QPixmap pm = QgsSymbolLayerUtils::symbolPreviewPixmap( symbol.get(), QSize( width, height ), height / 20, nullptr, false, mExpressionContext.get() );
QPixmap pm = QgsSymbolLayerUtils::symbolPreviewPixmap( symbol.get(), QSize( width, height ), height / 20, nullptr, false, mExpressionContext.get(), nullptr, maxDevicePixelRatioScreen );
QByteArray data;
QBuffer buffer( &data );
pm.save( &buffer, "PNG", 100 );
tooltip += QStringLiteral( "<p><img src='data:image/png;base64, %3'>" ).arg( QString( data.toBase64() ) );
tooltip += QStringLiteral( "<p><img src='data:image/png;base64, %3' width=\"%4\">" ).arg( QString( data.toBase64() ) ).arg( width );
}
break;
}
@ -173,8 +170,7 @@ QVariant QgsStyleModel::data( const QModelIndex &index, int role ) const
int width = static_cast< int >( Qgis::UI_SCALE_FACTOR * QFontMetrics( data( index, Qt::FontRole ).value< QFont >() ).horizontalAdvance( 'X' ) * 23 );
int height = static_cast< int >( width / 1.61803398875 ); // golden ratio
const QgsTextFormat format = mStyle->textFormat( name );
const double maxDevicePixelRatio = *std::max_element( mDevicePixelRatios.constBegin(), mDevicePixelRatios.constEnd() );
QPixmap pm = QgsTextFormat::textFormatPreviewPixmap( format, QSize( width, height ), QString(), height / 20, maxDevicePixelRatio );
QPixmap pm = QgsTextFormat::textFormatPreviewPixmap( format, QSize( width, height ), QString(), height / 20, maxDevicePixelRatioScreen );
QByteArray data;
QBuffer buffer( &data );
pm.save( &buffer, "PNG", 100 );
@ -186,9 +182,8 @@ QVariant QgsStyleModel::data( const QModelIndex &index, int role ) const
{
int width = static_cast< int >( Qgis::UI_SCALE_FACTOR * QFontMetrics( data( index, Qt::FontRole ).value< QFont >() ).horizontalAdvance( 'X' ) * 23 );
int height = static_cast< int >( width / 1.61803398875 ); // golden ratio
const double maxDevicePixelRatio = *std::max_element( mDevicePixelRatios.constBegin(), mDevicePixelRatios.constEnd() );
const QgsPalLayerSettings settings = mStyle->labelSettings( name );
QPixmap pm = QgsPalLayerSettings::labelSettingsPreviewPixmap( settings, QSize( width, height ), QString(), height / 20, maxDevicePixelRatio );
QPixmap pm = QgsPalLayerSettings::labelSettingsPreviewPixmap( settings, QSize( width, height ), QString(), height / 20, maxDevicePixelRatioScreen );
QByteArray data;
QBuffer buffer( &data );
pm.save( &buffer, "PNG", 100 );
@ -204,11 +199,11 @@ QVariant QgsStyleModel::data( const QModelIndex &index, int role ) const
const QgsLegendPatchShape shape = mStyle->legendPatchShape( name );
if ( const QgsSymbol *symbol = mStyle->previewSymbolForPatchShape( shape ) )
{
QPixmap pm = QgsSymbolLayerUtils::symbolPreviewPixmap( symbol, QSize( width, height ), height / 20, nullptr, false, nullptr, &shape );
QPixmap pm = QgsSymbolLayerUtils::symbolPreviewPixmap( symbol, QSize( width, height ), height / 20, nullptr, false, nullptr, &shape, maxDevicePixelRatioScreen );
QByteArray data;
QBuffer buffer( &data );
pm.save( &buffer, "PNG", 100 );
tooltip += QStringLiteral( "<p><img src='data:image/png;base64, %3'>" ).arg( QString( data.toBase64() ) );
tooltip += QStringLiteral( "<p><img src='data:image/png;base64, %3' width=\"%4\">" ).arg( QString( data.toBase64() ) ).arg( width );
}
break;
}
@ -262,12 +257,15 @@ QVariant QgsStyleModel::data( const QModelIndex &index, int role ) const
std::unique_ptr< QgsSymbol > symbol( mStyle->symbol( name ) );
if ( symbol )
{
if ( mAdditionalSizes.isEmpty() )
icon.addPixmap( QgsSymbolLayerUtils::symbolPreviewPixmap( symbol.get(), QSize( 24, 24 ), 1, nullptr, false, mExpressionContext.get() ) );
for ( const QSize &s : mAdditionalSizes )
for ( auto it = mTargetScreenProperties.constBegin(); it != mTargetScreenProperties.constEnd(); ++it )
{
icon.addPixmap( QgsSymbolLayerUtils::symbolPreviewPixmap( symbol.get(), s, static_cast< int >( s.width() * ICON_PADDING_FACTOR ), nullptr, false, mExpressionContext.get() ) );
if ( mAdditionalSizes.isEmpty() )
icon.addPixmap( QgsSymbolLayerUtils::symbolPreviewPixmap( symbol.get(), QSize( 24, 24 ), 1, nullptr, false, mExpressionContext.get(), nullptr, *it ) );
for ( const QSize &s : mAdditionalSizes )
{
icon.addPixmap( QgsSymbolLayerUtils::symbolPreviewPixmap( symbol.get(), s, static_cast< int >( s.width() * ICON_PADDING_FACTOR ), nullptr, false, mExpressionContext.get(), nullptr, *it ) );
}
}
}
@ -304,13 +302,13 @@ QVariant QgsStyleModel::data( const QModelIndex &index, int role ) const
return icon;
const QgsTextFormat format( mStyle->textFormat( name ) );
for ( const double pixelRatio : std::as_const( mDevicePixelRatios ) )
for ( auto it = mTargetScreenProperties.constBegin(); it != mTargetScreenProperties.constEnd(); ++it )
{
if ( mAdditionalSizes.isEmpty() )
icon.addPixmap( QgsTextFormat::textFormatPreviewPixmap( format, QSize( 24, 24 ), QString(), 1, pixelRatio ) );
icon.addPixmap( QgsTextFormat::textFormatPreviewPixmap( format, QSize( 24, 24 ), QString(), 1, *it ) );
for ( const QSize &s : mAdditionalSizes )
{
icon.addPixmap( QgsTextFormat::textFormatPreviewPixmap( format, s, QString(), static_cast< int >( s.width() * ICON_PADDING_FACTOR ), pixelRatio ) );
icon.addPixmap( QgsTextFormat::textFormatPreviewPixmap( format, s, QString(), static_cast< int >( s.width() * ICON_PADDING_FACTOR ), *it ) );
}
}
mIconCache[ entityType ].insert( name, icon );
@ -325,13 +323,13 @@ QVariant QgsStyleModel::data( const QModelIndex &index, int role ) const
return icon;
const QgsPalLayerSettings settings( mStyle->labelSettings( name ) );
for ( const double pixelRatio : std::as_const( mDevicePixelRatios ) )
for ( auto it = mTargetScreenProperties.constBegin(); it != mTargetScreenProperties.constEnd(); ++it )
{
if ( mAdditionalSizes.isEmpty() )
icon.addPixmap( QgsPalLayerSettings::labelSettingsPreviewPixmap( settings, QSize( 24, 24 ), QString(), 1, pixelRatio ) );
icon.addPixmap( QgsPalLayerSettings::labelSettingsPreviewPixmap( settings, QSize( 24, 24 ), QString(), 1, *it ) );
for ( const QSize &s : mAdditionalSizes )
{
icon.addPixmap( QgsPalLayerSettings::labelSettingsPreviewPixmap( settings, s, QString(), static_cast< int >( s.width() * ICON_PADDING_FACTOR ), pixelRatio ) );
icon.addPixmap( QgsPalLayerSettings::labelSettingsPreviewPixmap( settings, s, QString(), static_cast< int >( s.width() * ICON_PADDING_FACTOR ), *it ) );
}
}
mIconCache[ entityType ].insert( name, icon );
@ -348,14 +346,17 @@ QVariant QgsStyleModel::data( const QModelIndex &index, int role ) const
const QgsLegendPatchShape shape = mStyle->legendPatchShape( name );
if ( !shape.isNull() )
{
if ( const QgsSymbol *symbol = mStyle->previewSymbolForPatchShape( shape ) )
for ( auto it = mTargetScreenProperties.constBegin(); it != mTargetScreenProperties.constEnd(); ++it )
{
if ( mAdditionalSizes.isEmpty() )
icon.addPixmap( QgsSymbolLayerUtils::symbolPreviewPixmap( symbol, QSize( 24, 24 ), 1, nullptr, false, mExpressionContext.get(), &shape ) );
for ( const QSize &s : mAdditionalSizes )
if ( const QgsSymbol *symbol = mStyle->previewSymbolForPatchShape( shape ) )
{
icon.addPixmap( QgsSymbolLayerUtils::symbolPreviewPixmap( symbol, s, static_cast< int >( s.width() * ICON_PADDING_FACTOR ), nullptr, false, mExpressionContext.get(), &shape ) );
if ( mAdditionalSizes.isEmpty() )
icon.addPixmap( QgsSymbolLayerUtils::symbolPreviewPixmap( symbol, QSize( 24, 24 ), 1, nullptr, false, mExpressionContext.get(), &shape, *it ) );
for ( const QSize &s : mAdditionalSizes )
{
icon.addPixmap( QgsSymbolLayerUtils::symbolPreviewPixmap( symbol, s, static_cast< int >( s.width() * ICON_PADDING_FACTOR ), nullptr, false, mExpressionContext.get(), &shape, *it ) );
}
}
}
}
@ -628,15 +629,15 @@ void QgsStyleModel::addDesiredIconSize( QSize size )
mIconCache.clear();
}
void QgsStyleModel::addDesiredIconDevicePixelRatio( double ratio )
void QgsStyleModel::addTargetScreenProperties( const QgsScreenProperties &properties )
{
if ( mDevicePixelRatios.contains( ratio ) )
if ( mTargetScreenProperties.contains( properties ) )
return;
mDevicePixelRatios << ratio;
mTargetScreenProperties.insert( properties );
if ( sIconGenerator )
sIconGenerator->setDevicePixelRatios( mDevicePixelRatios );
sIconGenerator->setTargetScreenProperties( mTargetScreenProperties );
mIconCache.clear();
}
@ -995,12 +996,12 @@ void QgsStyleProxyModel::addDesiredIconSize( QSize size )
mCombinedModel->addDesiredIconSize( size );
}
void QgsStyleProxyModel::addDesiredIconDevicePixelRatio( double ratio )
void QgsStyleProxyModel::addTargetScreenProperties( const QgsScreenProperties &properties )
{
if ( mModel )
mModel->addDesiredIconDevicePixelRatio( ratio );
mModel->addTargetScreenProperties( properties );
if ( mCombinedModel )
mCombinedModel->addDesiredIconDevicePixelRatio( ratio );
mCombinedModel->addTargetScreenProperties( properties );
}
bool QgsStyleProxyModel::symbolTypeFilterEnabled() const

View File

@ -74,20 +74,26 @@ class CORE_EXPORT QgsAbstractStyleEntityIconGenerator : public QObject
QList< QSize > iconSizes() const;
/**
* Sets the list of icon device pixel \a ratios to generate.
* Sets the target screen \a properties to use when generating icons.
*
* \see devicePixelRatios()
* This allows style icons to be generated at an icon device pixel ratio and DPI which
* corresponds exactly to the view's screen properties in which this model is used.
*
* \see targetScreenProperties()
* \since QGIS 3.32
*/
void setDevicePixelRatios( const QList< double > &ratios );
void setTargetScreenProperties( const QSet< QgsScreenProperties > &properties );
/**
* Returns the list of icon device pixel ratios to generate.
* Returns the target screen properties to use when generating icons.
*
* \see setDevicePixelRatios()
* This allows style icons to be generated at an icon device pixel ratio and DPI which
* corresponds exactly to the view's screen properties in which this model is used.
*
* \see setTargetScreenProperties()
* \since QGIS 3.32
*/
QList< double > devicePixelRatios() const;
QSet< QgsScreenProperties > targetScreenProperties() const;
signals:
@ -100,7 +106,7 @@ class CORE_EXPORT QgsAbstractStyleEntityIconGenerator : public QObject
private:
QList< QSize > mIconSizes;
QList< double > mDevicePixelRatios;
QSet< QgsScreenProperties > mTargetScreenProperties;
};
@ -183,14 +189,14 @@ class CORE_EXPORT QgsStyleModel: public QAbstractItemModel
void addDesiredIconSize( QSize size );
/**
* Adds an additional icon device pixel \a ratio to generate for Qt::DecorationRole data.
* Adds additional target screen \a properties to use when generating icons for Qt::DecorationRole data.
*
* This allows style icons to be generated at an icon device pixel ratio which
* corresponds exactly to the view's icon size in which this model is used.
* This allows style icons to be generated at an icon device pixel ratio and DPI which
* corresponds exactly to the view's screen properties in which this model is used.
*
* \since QGIS 3.32
*/
void addDesiredIconDevicePixelRatio( double ratio );
void addTargetScreenProperties( const QgsScreenProperties &properties );
/**
* Sets the icon \a generator to use for deferred style entity icon generation.
@ -219,7 +225,7 @@ class CORE_EXPORT QgsStyleModel: public QAbstractItemModel
QHash< QgsStyle::StyleEntity, QStringList > mEntityNames;
QList< double > mDevicePixelRatios;
QSet< QgsScreenProperties > mTargetScreenProperties;
QList< QSize > mAdditionalSizes;
mutable std::unique_ptr< QgsExpressionContext > mExpressionContext;
@ -483,14 +489,14 @@ class CORE_EXPORT QgsStyleProxyModel: public QSortFilterProxyModel
void addDesiredIconSize( QSize size );
/**
* Adds an additional icon device pixel \a ratio to generate for Qt::DecorationRole data.
* Adds additional target screen \a properties to use when generating icons for Qt::DecorationRole data.
*
* This allows style icons to be generated at an icon device pixel ratio which
* corresponds exactly to the view's icon size in which this model is used.
* This allows style icons to be generated at an icon device pixel ratio and DPI which
* corresponds exactly to the view's screen properties in which this model is used.
*
* \since QGIS 3.32
*/
void addDesiredIconDevicePixelRatio( double ratio );
void addTargetScreenProperties( const QgsScreenProperties &properties );
public slots:

View File

@ -1104,8 +1104,9 @@ void QgsTextFormat::updateDataDefinedProperties( QgsRenderContext &context )
mMaskSettings.updateDataDefinedProperties( context, d->mDataDefinedProperties );
}
QPixmap QgsTextFormat::textFormatPreviewPixmap( const QgsTextFormat &format, QSize size, const QString &previewText, int padding, double devicePixelRatio )
QPixmap QgsTextFormat::textFormatPreviewPixmap( const QgsTextFormat &format, QSize size, const QString &previewText, int padding, const QgsScreenProperties &screen )
{
const double devicePixelRatio = screen.isValid() ? screen.devicePixelRatio() : 1;
QgsTextFormat tempFormat = format;
QPixmap pixmap( size * devicePixelRatio );
pixmap.fill( Qt::transparent );
@ -1152,10 +1153,24 @@ QPixmap QgsTextFormat::textFormatPreviewPixmap( const QgsTextFormat &format, QSi
newCoordXForm.setParameters( 1, 0, 0, 0, 0, 0 );
context.setMapToPixel( newCoordXForm );
QWidget *activeWindow = QApplication::activeWindow();
const double physicalDpiX = ( activeWindow && activeWindow->screen() ? activeWindow->screen()->physicalDotsPerInch() : 96.0 );
context.setScaleFactor( physicalDpiX / 25.4 );
context.setDevicePixelRatio( devicePixelRatio );
if ( screen.isValid() )
{
screen.updateRenderContextForScreen( context );
}
else
{
QWidget *activeWindow = QApplication::activeWindow();
if ( QScreen *screen = activeWindow ? activeWindow->screen() : nullptr )
{
context.setScaleFactor( screen->physicalDotsPerInch() / 25.4 );
context.setDevicePixelRatio( screen->devicePixelRatio() );
}
else
{
context.setScaleFactor( 96.0 / 25.4 );
context.setDevicePixelRatio( 1.0 );
}
}
context.setUseAdvancedEffects( true );
context.setFlag( Qgis::RenderContextFlag::Antialiasing, true );

View File

@ -24,6 +24,7 @@
#include "qgstextshadowsettings.h"
#include "qgstextmasksettings.h"
#include "qgsstringutils.h"
#include "qgsscreenproperties.h"
#include <QSharedDataPointer>
@ -651,10 +652,10 @@ class CORE_EXPORT QgsTextFormat
* \param size target pixmap size
* \param previewText text to render in preview, or empty for default text
* \param padding space between icon edge and color ramp
* \param devicePixelRatio can be used to generate a pixmap using a specific device pixel ratio (since QGIS 3.32)
* \param screen can be used to specify the destination screen properties for the icon. This allows the icon to be generated using the correct DPI and device pixel ratio for the target screen (since QGIS 3.32)
* \since QGIS 3.10
*/
static QPixmap textFormatPreviewPixmap( const QgsTextFormat &format, QSize size, const QString &previewText = QString(), int padding = 0, double devicePixelRatio = 1 );
static QPixmap textFormatPreviewPixmap( const QgsTextFormat &format, QSize size, const QString &previewText = QString(), int padding = 0, const QgsScreenProperties &screen = QgsScreenProperties() );
private:

View File

@ -16,12 +16,10 @@
#include "qgsstyleitemslistwidget.h"
#include "qgsstylemanagerdialog.h"
#include "qgsstylesavedialog.h"
#include "qgspanelwidget.h"
#include "qgssettings.h"
#include "qgsgui.h"
#include "qgswindowmanagerinterface.h"
#include "qgsapplication.h"
#include "qgsproject.h"
#include "qgsprojectstylesettings.h"
#include <QScrollBar>
@ -253,7 +251,7 @@ void QgsStyleItemsListWidget::setStyle( QgsStyle *style )
mModel->addDesiredIconSize( viewSymbols->iconSize() );
mModel->addDesiredIconSize( mSymbolTreeView->iconSize() );
mModel->addDesiredIconDevicePixelRatio( mSymbolTreeView->devicePixelRatioF() );
mModel->addTargetScreenProperties( QgsScreenProperties( screen() ) );
viewSymbols->setTextElideMode( Qt::TextElideMode::ElideRight );

View File

@ -20,9 +20,6 @@
#include "qgsapplication.h"
#include "qgsstyle.h"
#include "qgssymbol.h"
#include "qgssymbollayerutils.h"
#include "qgscolorramp.h"
#include "qgslogger.h"
#include "qgsnetworkcontentfetchertask.h"
#include "qgsstylegroupselectiondialog.h"
#include "qgsguiutils.h"
@ -128,7 +125,7 @@ QgsStyleExportImportDialog::QgsStyleExportImportDialog( QgsStyle *style, QWidget
mModel = new QgsStyleProxyModel( dialogStyle, this );
mModel->addDesiredIconSize( listItems->iconSize() );
mModel->addDesiredIconDevicePixelRatio( listItems->devicePixelRatioF() );
mModel->addTargetScreenProperties( QgsScreenProperties( screen() ) );
listItems->setModel( mModel );

View File

@ -502,7 +502,7 @@ void QgsStyleManagerDialog::setCurrentStyle( QgsStyle *style )
}
mModel->addDesiredIconSize( mSymbolTreeView->iconSize() );
mModel->addDesiredIconSize( listItems->iconSize() );
mModel->addDesiredIconDevicePixelRatio( listItems->devicePixelRatioF() );
mModel->addTargetScreenProperties( QgsScreenProperties( screen() ) );
mModel->setFilterString( searchBox->text() );