Skip to content
Permalink
Browse files
Merge pull request #43027 from nirvn/desired
[layouts] Respect XYZ output zoom level when previewing map items in the layout designer
  • Loading branch information
nirvn committed May 4, 2021
2 parents e66e305 + 123aae2 commit a9c678a1e0b3344654430fa46168f4bc6a9b8712
@@ -191,6 +191,26 @@ The default value is 96 dpi.
Sets the ``dpi`` (dots per inch) used for conversion between real world units (e.g. mm) and pixels.

.. seealso:: :py:func:`outputDpi`
%End

double dpiTarget() const;
%Docstring
Returns the target DPI (dots per inch) to be taken into consideration when rendering.

The default value is -1, which states no DPI target is provided.

.. seealso:: :py:func:`setDpiTarget`

.. versionadded:: 3.20
%End

void setDpiTarget( double dpi );
%Docstring
Sets the target ``dpi`` (dots per inch) to be taken into consideration when rendering.

.. seealso:: :py:func:`dpiTarget`

.. versionadded:: 3.20
%End

void setMagnificationFactor( double factor, const QgsPointXY *center = 0 );
@@ -278,6 +278,15 @@ to physical sizes. This is usually equal to the number of pixels
per millimeter.

.. seealso:: :py:func:`setScaleFactor`
%End

double dpiTarget() const;
%Docstring
Returns the targeted DPI for rendering.

.. seealso:: :py:func:`setDpiTarget`

.. versionadded:: 3.20
%End

bool renderingStopped() const;
@@ -425,6 +434,15 @@ to physical sizes. This should usually be equal to the number of pixels
per millimeter.

.. seealso:: :py:func:`scaleFactor`
%End

void setDpiTarget( double dpi );
%Docstring
Sets the targeted ``dpi`` for rendering.

.. seealso:: :py:func:`dpiTarget`

.. versionadded:: 3.20
%End

void setRendererScale( double scale );
@@ -77,7 +77,8 @@ Base class for raster data providers.
WriteLayerMetadata,
ProviderHintBenefitsFromResampling,
ProviderHintCanPerformProviderResampling,
ReloadData
ReloadData,
DpiDependentData,
};

typedef QFlags<QgsRasterDataProvider::ProviderCapability> ProviderCapabilities;
@@ -20,7 +20,14 @@ The drawing pipe for raster layers.
#include "qgsrasterdrawer.h"
%End
public:
QgsRasterDrawer( QgsRasterIterator *iterator );

QgsRasterDrawer( QgsRasterIterator *iterator, double dpiTarget = -1.0 );
%Docstring
The QgsRasterDrawer constructor.

:param iterator: the raster iterator to fetch data from
:param dpiTarget: the target ``dpi`` (dots per inch) to be taken into consideration when rendering
%End

void draw( QPainter *p, QgsRasterViewPort *viewPort, const QgsMapToPixel *qgsMapToPixel, QgsRasterBlockFeedback *feedback = 0 );
%Docstring
@@ -1484,6 +1484,8 @@ QgsMapSettings QgsLayoutItemMap::mapSettings( const QgsRectangle &extent, QSizeF
jobMapSettings.setExtent( extent );
jobMapSettings.setOutputSize( size.toSize() );
jobMapSettings.setOutputDpi( dpi );
if ( layout()->renderContext().isPreviewRender() )
jobMapSettings.setDpiTarget( layout()->renderContext().dpi() );
jobMapSettings.setBackgroundColor( Qt::transparent );
jobMapSettings.setRotation( mEvaluatedMapRotation );
if ( mLayout )
@@ -408,6 +408,7 @@ LayerRenderJobs QgsMapRendererJob::prepareJobs( QPainter *painter, QgsLabelingEn
job.estimatedRenderingTime = mLayerRenderingTimeHints.value( ml->id(), 0 );
job.renderingTime = -1;


job.context = QgsRenderContext::fromMapSettings( mSettings );
job.context.expressionContext().appendScope( QgsExpressionContextUtils::layerScope( ml ) );
job.context.setPainter( painter );
@@ -272,6 +272,15 @@ void QgsMapSettings::setOutputDpi( double dpi )
updateDerived();
}

double QgsMapSettings::dpiTarget() const
{
return mDpiTarget;
}

void QgsMapSettings::setDpiTarget( double dpi )
{
mDpiTarget = dpi;
}

QStringList QgsMapSettings::layerIds() const
{
@@ -204,6 +204,24 @@ class CORE_EXPORT QgsMapSettings : public QgsTemporalRangeObject
*/
void setOutputDpi( double dpi );

/**
* Returns the target DPI (dots per inch) to be taken into consideration when rendering.
*
* The default value is -1, which states no DPI target is provided.
*
* \see setDpiTarget()
* \since QGIS 3.20
*/
double dpiTarget() const;

/**
* Sets the target \a dpi (dots per inch) to be taken into consideration when rendering.
*
* \see dpiTarget()
* \since QGIS 3.20
*/
void setDpiTarget( double dpi );

/**
* Set the magnification factor.
* \param factor the factor of magnification
@@ -773,7 +791,8 @@ class CORE_EXPORT QgsMapSettings : public QgsTemporalRangeObject

protected:

double mDpi;
double mDpi = 96.0;
double mDpiTarget = -1;

QSize mSize;
float mDevicePixelRatio = 1.0;
@@ -51,6 +51,7 @@ QgsRenderContext::QgsRenderContext( const QgsRenderContext &rh )
, mMapToPixel( rh.mMapToPixel )
, mRenderingStopped( rh.mRenderingStopped )
, mScaleFactor( rh.mScaleFactor )
, mDpiTarget( rh.mDpiTarget )
, mRendererScale( rh.mRendererScale )
, mLabelingEngine( rh.mLabelingEngine )
, mSelectionColor( rh.mSelectionColor )
@@ -88,6 +89,7 @@ QgsRenderContext &QgsRenderContext::operator=( const QgsRenderContext &rh )
mMapToPixel = rh.mMapToPixel;
mRenderingStopped = rh.mRenderingStopped;
mScaleFactor = rh.mScaleFactor;
mDpiTarget = rh.mDpiTarget;
mRendererScale = rh.mRendererScale;
mLabelingEngine = rh.mLabelingEngine;
mSelectionColor = rh.mSelectionColor;
@@ -220,6 +222,7 @@ QgsRenderContext QgsRenderContext::fromMapSettings( const QgsMapSettings &mapSet
ctx.setFlag( LosslessImageRendering, mapSettings.testFlag( QgsMapSettings::LosslessImageRendering ) );
ctx.setFlag( Render3DMap, mapSettings.testFlag( QgsMapSettings::Render3DMap ) );
ctx.setScaleFactor( mapSettings.outputDpi() / 25.4 ); // = pixels per mm
ctx.setDpiTarget( mapSettings.dpiTarget() >= 0.0 ? mapSettings.dpiTarget() : -1.0 );
ctx.setRendererScale( mapSettings.scale() );
ctx.setExpressionContext( mapSettings.expressionContext() );
ctx.setSegmentationTolerance( mapSettings.segmentationTolerance() );
@@ -333,6 +333,14 @@ class CORE_EXPORT QgsRenderContext : public QgsTemporalRangeObject
*/
double scaleFactor() const {return mScaleFactor;}

/**
* Returns the targeted DPI for rendering.
*
* \see setDpiTarget()
* \since QGIS 3.20
*/
double dpiTarget() const {return mDpiTarget;}

/**
* Returns TRUE if the rendering operation has been stopped and any ongoing
* rendering should be canceled immediately.
@@ -476,6 +484,14 @@ class CORE_EXPORT QgsRenderContext : public QgsTemporalRangeObject
*/
void setScaleFactor( double factor ) {mScaleFactor = factor;}

/**
* Sets the targeted \a dpi for rendering.
*
* \see dpiTarget()
* \since QGIS 3.20
*/
void setDpiTarget( double dpi ) {mDpiTarget = dpi;}

/**
* Sets the renderer map scale. This should match the desired scale denominator
* for the rendered map, eg 1000.0 for a 1:1000 map render.
@@ -928,6 +944,9 @@ class CORE_EXPORT QgsRenderContext : public QgsTemporalRangeObject
//! Factor to scale line widths and point marker sizes
double mScaleFactor = 1.0;

//! Targeted DPI
double mDpiTarget = -1.0;

//! Map scale
double mRendererScale = 1.0;

@@ -102,7 +102,8 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider, public QgsRast
WriteLayerMetadata = 1 << 2, //!< Provider can write layer metadata to the data store. Since QGIS 3.0. See QgsDataProvider::writeLayerMetadata()
ProviderHintBenefitsFromResampling = 1 << 3, //!< Provider benefits from resampling and should apply user default resampling settings (since QGIS 3.10)
ProviderHintCanPerformProviderResampling = 1 << 4, //!< Provider can perform resampling (to be opposed to post rendering resampling) (since QGIS 3.16)
ReloadData = 1 << 5 //!< Is able to force reload data / clear local caches. Since QGIS 3.18, see QgsDataProvider::reloadProviderData()
ReloadData = 1 << 5, //!< Is able to force reload data / clear local caches. Since QGIS 3.18, see QgsDataProvider::reloadProviderData()
DpiDependentData = 1 << 6, //! Provider's rendering is dependent on requested pixel size of the viewport (since QGIS 3.20)
};

//! Provider capabilities
@@ -29,7 +29,9 @@
#include <QPrinter>
#endif

QgsRasterDrawer::QgsRasterDrawer( QgsRasterIterator *iterator ): mIterator( iterator )
QgsRasterDrawer::QgsRasterDrawer( QgsRasterIterator *iterator, double dpiTarget )
: mIterator( iterator )
, mDpiTarget( dpiTarget )
{
}

@@ -122,22 +124,22 @@ void QgsRasterDrawer::drawImage( QPainter *p, QgsRasterViewPort *viewPort, const
return;
}

const double dpiScaleFactor = mDpiTarget >= 0.0 ? mDpiTarget / p->device()->logicalDpiX() : 1.0;
//top left position in device coords
QPoint tlPoint = QPoint( viewPort->mTopLeftPoint.x() + topLeftCol, viewPort->mTopLeftPoint.y() + topLeftRow );
QPoint tlPoint = QPoint( viewPort->mTopLeftPoint.x() + std::floor( topLeftCol / dpiScaleFactor ), viewPort->mTopLeftPoint.y() + std::floor( topLeftRow / dpiScaleFactor ) );

QgsScopedQPainterState painterState( p );
p->setRenderHint( QPainter::Antialiasing, false );

// Blending problem was reported with PDF output if background color has alpha < 255
// in #7766, it seems to be a bug in Qt, setting a brush with alpha 255 is a workaround
// which should not harm anything
p->setBrush( QBrush( QColor( Qt::white ), Qt::NoBrush ) );

if ( qgsMapToPixel )
{
int w = qgsMapToPixel->mapWidth();
int h = qgsMapToPixel->mapHeight();

double rotation = qgsMapToPixel->mapRotation();
const int w = qgsMapToPixel->mapWidth();
const int h = qgsMapToPixel->mapHeight();
const double rotation = qgsMapToPixel->mapRotation();
if ( rotation )
{
// both viewPort and image sizes are dependent on scale
@@ -149,7 +151,7 @@ void QgsRasterDrawer::drawImage( QPainter *p, QgsRasterViewPort *viewPort, const
}
}

p->drawImage( tlPoint, img );
p->drawImage( tlPoint, dpiScaleFactor != 1.0 ? img.scaledToHeight( std::ceil( img.height() / dpiScaleFactor ) ) : img );

#if 0
// For debugging:
@@ -37,7 +37,13 @@ class QgsRasterIterator;
class CORE_EXPORT QgsRasterDrawer
{
public:
QgsRasterDrawer( QgsRasterIterator *iterator );

/**
* The QgsRasterDrawer constructor.
* \param iterator the raster iterator to fetch data from
* \param dpiTarget the target \a dpi (dots per inch) to be taken into consideration when rendering
*/
QgsRasterDrawer( QgsRasterIterator *iterator, double dpiTarget = -1.0 );

/**
* Draws raster data.
@@ -64,6 +70,7 @@ class CORE_EXPORT QgsRasterDrawer

private:
QgsRasterIterator *mIterator = nullptr;
double mDpiTarget = -1.0;
};

#endif // QGSRASTERDRAWER_H
@@ -58,7 +58,7 @@ void QgsRasterLayerRendererFeedback::onNewData()
feedback.setPreviewOnly( true );
feedback.setRenderPartialOutput( true );
QgsRasterIterator iterator( mR->mPipe->last() );
QgsRasterDrawer drawer( &iterator );
QgsRasterDrawer drawer( &iterator, mR->renderContext()->dpiTarget() );
drawer.draw( mR->renderContext()->painter(), mR->mRasterViewPort, &mR->renderContext()->mapToPixel(), &feedback );
mR->mReadyToCompose = true;
QgsDebugMsgLevel( QStringLiteral( "total raster preview time: %1 ms" ).arg( t.elapsed() ), 3 );
@@ -201,6 +201,19 @@ QgsRasterLayerRenderer::QgsRasterLayerRenderer( QgsRasterLayer *layer, QgsRender
mRasterViewPort->mWidth = static_cast<qgssize>( std::abs( mRasterViewPort->mBottomRightPoint.x() - mRasterViewPort->mTopLeftPoint.x() ) );
mRasterViewPort->mHeight = static_cast<qgssize>( std::abs( mRasterViewPort->mBottomRightPoint.y() - mRasterViewPort->mTopLeftPoint.y() ) );


if ( mProviderCapabilities & QgsRasterDataProvider::DpiDependentData
&& rendererContext.dpiTarget() >= 0.0 )
{
const double dpiScaleFactor = rendererContext.dpiTarget() / rendererContext.painter()->device()->logicalDpiX();
mRasterViewPort->mWidth *= dpiScaleFactor;
mRasterViewPort->mHeight *= dpiScaleFactor;
}
else
{
rendererContext.setDpiTarget( -1.0 );
}

//the drawable area can start to get very very large when you get down displaying 2x2 or smaller, this is because
//mapToPixel.mapUnitsPerPixel() is less then 1,
//so we will just get the pixel data and then render these special cases differently in paintImageToCanvas()
@@ -322,7 +335,7 @@ bool QgsRasterLayerRenderer::render()

// Drawer to pipe?
QgsRasterIterator iterator( mPipe->last() );
QgsRasterDrawer drawer( &iterator );
QgsRasterDrawer drawer( &iterator, renderContext()->dpiTarget() );
drawer.draw( renderContext()->painter(), mRasterViewPort, &renderContext()->mapToPixel(), mFeedback );

if ( restoreOldResamplingStage )
@@ -227,6 +227,8 @@ void QgsLayoutPropertiesWidget::dpiChanged( int value )
mLayout->undoStack()->beginCommand( mLayout, tr( "Set Default DPI" ), QgsLayout::UndoLayoutDpi );
mLayout->renderContext().setDpi( value );
mLayout->undoStack()->endCommand();

mLayout->refresh();
}

void QgsLayoutPropertiesWidget::worldFileToggled()
@@ -2024,6 +2024,11 @@ int QgsWmsProvider::capabilities() const
// capability |= Capability::Prefetch;
}

if ( mSettings.mTiled || mSettings.mXyz )
{
capability |= DpiDependentData;
}

QgsDebugMsgLevel( QStringLiteral( "capability = %1" ).arg( capability ), 2 );
return capability;
}

0 comments on commit a9c678a

Please sign in to comment.