Skip to content

Commit

Permalink
[fix #17773] fix HiDPI in map canvas on mac
Browse files Browse the repository at this point in the history
For system introducing pixel device ratio, the image rendered by the canvas is actually scaled up to match the physical size of the canvas"
  • Loading branch information
3nids committed Oct 19, 2018
1 parent 7ad0dcf commit ea982fe
Show file tree
Hide file tree
Showing 8 changed files with 86 additions and 14 deletions.
23 changes: 23 additions & 0 deletions python/core/auto_generated/qgsmapsettings.sip.in
Expand Up @@ -58,6 +58,29 @@ Returns the size of the resulting map image
void setOutputSize( QSize size );
%Docstring
Sets the size of the resulting map image
%End

int devicePixelRatio() const;
%Docstring
Returns device pixel ratio

.. versionadded:: 3.4
%End

void setDevicePixelRatio( int dpr );
%Docstring
Sets the device pixel ratio

.. versionadded:: 3.4
%End

QSize physicalSize() const;
%Docstring
Returns the physical size of the map canvas
This is equivalent to the output size multiplicated
by the device pixel ratio

.. versionadded:: 3.4
%End

double rotation() const;
Expand Down
9 changes: 6 additions & 3 deletions src/core/qgsmaprenderercustompainterjob.cpp
Expand Up @@ -60,14 +60,17 @@ void QgsMapRendererCustomPainterJob::start()
prepareTime.start();

// clear the background
mPainter->fillRect( 0, 0, mSettings.outputSize().width(), mSettings.outputSize().height(), mSettings.backgroundColor() );
mPainter->fillRect( 0, 0, mSettings.physicalSize().width(), mSettings.physicalSize().height(), mSettings.backgroundColor() );

mPainter->setRenderHint( QPainter::Antialiasing, mSettings.testFlag( QgsMapSettings::Antialiasing ) );

#ifndef QT_NO_DEBUG
QPaintDevice *paintDevice = mPainter->device();
QString errMsg = QStringLiteral( "pre-set DPI not equal to painter's DPI (%1 vs %2)" ).arg( paintDevice->logicalDpiX() ).arg( mSettings.outputDpi() );
Q_ASSERT_X( qgsDoubleNear( paintDevice->logicalDpiX(), mSettings.outputDpi() ), "Job::startRender()", errMsg.toLatin1().data() );
QString errMsg = QStringLiteral( "pre-set DPI not equal to painter's DPI (%1 vs %2)" )
.arg( paintDevice->logicalDpiX() )
.arg( mSettings.outputDpi() );
Q_ASSERT_X( qgsDoubleNear( paintDevice->logicalDpiX(), mSettings.outputDpi() ),
"Job::startRender()", errMsg.toLatin1().data() );
#endif

mLabelingEngineV2.reset();
Expand Down
16 changes: 9 additions & 7 deletions src/core/qgsmaprendererjob.cpp
Expand Up @@ -313,6 +313,7 @@ LayerRenderJobs QgsMapRendererJob::prepareJobs( QPainter *painter, QgsLabelingEn
job.cached = true;
job.imageInitialized = true;
job.img = new QImage( mCache->cacheImage( ml->id() ) );
job.img->setDevicePixelRatio( mSettings.devicePixelRatio() );
job.renderer = nullptr;
job.context.setPainter( nullptr );
continue;
Expand All @@ -324,10 +325,9 @@ LayerRenderJobs QgsMapRendererJob::prepareJobs( QPainter *painter, QgsLabelingEn
if ( mCache || !painter || needTemporaryImage( ml ) )
{
// Flattened image for drawing when a blending mode is set
QImage *mypFlattenedImage = nullptr;
mypFlattenedImage = new QImage( mSettings.outputSize().width(),
mSettings.outputSize().height(),
mSettings.outputImageFormat() );
QImage *mypFlattenedImage = new QImage( mSettings.physicalSize(),
mSettings.outputImageFormat() );
mypFlattenedImage->setDevicePixelRatio( mSettings.devicePixelRatio() );
if ( mypFlattenedImage->isNull() )
{
mErrors.append( Error( ml->id(), tr( "Insufficient memory for image %1x%2" ).arg( mSettings.outputSize().width() ).arg( mSettings.outputSize().height() ) ) );
Expand Down Expand Up @@ -366,6 +366,7 @@ LabelRenderJob QgsMapRendererJob::prepareLabelingJob( QPainter *painter, QgsLabe
job.cached = true;
job.complete = true;
job.img = new QImage( mCache->cacheImage( LABEL_CACHE_ID ) );
Q_ASSERT( job.img->devicePixelRatio() == 2 );
job.context.setPainter( nullptr );
}
else
Expand All @@ -374,9 +375,9 @@ LabelRenderJob QgsMapRendererJob::prepareLabelingJob( QPainter *painter, QgsLabe
{
// Flattened image for drawing labels
QImage *mypFlattenedImage = nullptr;
mypFlattenedImage = new QImage( mSettings.outputSize().width(),
mSettings.outputSize().height(),
mypFlattenedImage = new QImage( mSettings.physicalSize(),
mSettings.outputImageFormat() );
mypFlattenedImage->setDevicePixelRatio( mSettings.devicePixelRatio() );
if ( mypFlattenedImage->isNull() )
{
mErrors.append( Error( QStringLiteral( "labels" ), tr( "Insufficient memory for label image %1x%2" ).arg( mSettings.outputSize().width() ).arg( mSettings.outputSize().height() ) ) );
Expand Down Expand Up @@ -447,7 +448,8 @@ void QgsMapRendererJob::cleanupLabelJob( LabelRenderJob &job )

QImage QgsMapRendererJob::composeImage( const QgsMapSettings &settings, const LayerRenderJobs &jobs, const LabelRenderJob &labelJob )
{
QImage image( settings.outputSize(), settings.outputImageFormat() );
QImage image( settings.physicalSize(), settings.outputImageFormat() );
image.setDevicePixelRatio( settings.devicePixelRatio() );
image.fill( settings.backgroundColor().rgba() );

QPainter painter( &image );
Expand Down
3 changes: 2 additions & 1 deletion src/core/qgsmaprenderersequentialjob.cpp
Expand Up @@ -25,7 +25,8 @@ QgsMapRendererSequentialJob::QgsMapRendererSequentialJob( const QgsMapSettings &
{
QgsDebugMsgLevel( QStringLiteral( "SEQUENTIAL construct" ), 5 );

mImage = QImage( mSettings.outputSize(), mSettings.outputImageFormat() );
mImage = QImage( mSettings.physicalSize(), mSettings.outputImageFormat() );
mImage.setDevicePixelRatio( mSettings.devicePixelRatio() );
mImage.setDotsPerMeterX( 1000 * settings.outputDpi() / 25.4 );
mImage.setDotsPerMeterY( 1000 * settings.outputDpi() / 25.4 );
mImage.fill( Qt::transparent );
Expand Down
19 changes: 17 additions & 2 deletions src/core/qgsmapsettings.cpp
Expand Up @@ -146,8 +146,8 @@ void QgsMapSettings::updateDerived()
}
}

double myHeight = mSize.height();
double myWidth = mSize.width();
double myHeight = mSize.height() * mDevicePixelRatio;
double myWidth = mSize.width() * mDevicePixelRatio;

if ( !myWidth || !myHeight )
{
Expand Down Expand Up @@ -230,6 +230,21 @@ void QgsMapSettings::setOutputSize( QSize size )
updateDerived();
}

int QgsMapSettings::devicePixelRatio() const
{
return mDevicePixelRatio;
}

void QgsMapSettings::setDevicePixelRatio( int dpr )
{
mDevicePixelRatio = dpr;
}

QSize QgsMapSettings::physicalSize() const
{
return outputSize() * mDevicePixelRatio;
}

double QgsMapSettings::outputDpi() const
{
return mDpi;
Expand Down
21 changes: 21 additions & 0 deletions src/core/qgsmapsettings.h
Expand Up @@ -81,6 +81,26 @@ class CORE_EXPORT QgsMapSettings
//! Sets the size of the resulting map image
void setOutputSize( QSize size );

/**
* Returns device pixel ratio
* \since QGIS 3.4
*/
int devicePixelRatio() const;

/**
* Sets the device pixel ratio
* \since QGIS 3.4
*/
void setDevicePixelRatio( int dpr );

/**
* Returns the physical size of the map canvas
* This is equivalent to the output size multiplicated
* by the device pixel ratio
* \since QGIS 3.4
*/
QSize physicalSize() const;

/**
* Returns the rotation of the resulting map image, in degrees clockwise.
* \see setRotation()
Expand Down Expand Up @@ -403,6 +423,7 @@ class CORE_EXPORT QgsMapSettings
double mDpi;

QSize mSize;
int mDevicePixelRatio = 1;

QgsRectangle mExtent;

Expand Down
4 changes: 3 additions & 1 deletion src/gui/qgsmapcanvas.cpp
Expand Up @@ -163,6 +163,7 @@ QgsMapCanvas::QgsMapCanvas( QWidget *parent )

QSize s = viewport()->size();
mSettings.setOutputSize( s );
mSettings.setDevicePixelRatio( devicePixelRatio() );
setSceneRect( 0, 0, s.width(), s.height() );
mScene->setSceneRect( QRectF( 0, 0, s.width(), s.height() ) );

Expand Down Expand Up @@ -682,7 +683,8 @@ QgsRectangle QgsMapCanvas::imageRect( const QImage &img, const QgsMapSettings &m
// expects (encoding of position and size of the item)
const QgsMapToPixel &m2p = mapSettings.mapToPixel();
QgsPointXY topLeft = m2p.toMapCoordinates( 0, 0 );
double res = m2p.mapUnitsPerPixel();
Q_ASSERT( img.devicePixelRatio() == mapSettings.devicePixelRatio() );
double res = m2p.mapUnitsPerPixel() / img.devicePixelRatioF();
QgsRectangle rect( topLeft.x(), topLeft.y(), topLeft.x() + img.width()*res, topLeft.y() - img.height()*res );
return rect;
}
Expand Down
5 changes: 5 additions & 0 deletions src/gui/qgsmapcanvasmap.cpp
Expand Up @@ -83,7 +83,12 @@ void QgsMapCanvasMap::paint( QPainter *painter )
painter->drawImage( QRectF( ul.x(), ul.y(), lr.x() - ul.x(), lr.y() - ul.y() ), imIt->first, QRect( 0, 0, imIt->first.width(), imIt->first.height() ) );
}

qDebug() << "map painter: " << w << h << mImage.size() << mImage.devicePixelRatioF();
painter->drawImage( QRect( 0, 0, w, h ), mImage );
//Q_ASSERT(mImage.isNull() || mImage.devicePixelRatio()==2);
//QImage img = mImage;
//img.setDevicePixelRatio(2);
//painter->drawImage( 0,0, img );

// For debugging:
#if 0
Expand Down

0 comments on commit ea982fe

Please sign in to comment.