Skip to content
Permalink
Browse files

[fix #17773] fix HiDPI in map canvas on mac

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 ea982fe3d268fd3d56833b17a2310607a00e29b9
@@ -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;
@@ -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();
@@ -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;
@@ -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() ) ) );
@@ -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
@@ -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() ) ) );
@@ -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 );
@@ -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 );
@@ -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 )
{
@@ -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;
@@ -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()
@@ -403,6 +423,7 @@ class CORE_EXPORT QgsMapSettings
double mDpi;

QSize mSize;
int mDevicePixelRatio = 1;

QgsRectangle mExtent;

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

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

0 comments on commit ea982fe

Please sign in to comment.
You can’t perform that action at this time.