Skip to content

Commit

Permalink
[layouts] Respect XYZ output zoom level when previewing map items in …
Browse files Browse the repository at this point in the history
…the layout designer
  • Loading branch information
nirvn committed May 3, 2021
1 parent c09360c commit 8d1371f
Show file tree
Hide file tree
Showing 15 changed files with 125 additions and 14 deletions.
16 changes: 16 additions & 0 deletions python/core/auto_generated/qgsmapsettings.sip.in
Expand Up @@ -191,6 +191,22 @@ 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:`setDpiTarger`
%End

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

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

void setMagnificationFactor( double factor, const QgsPointXY *center = 0 );
Expand Down
19 changes: 19 additions & 0 deletions python/core/auto_generated/qgsrendercontext.sip.in
Expand Up @@ -278,6 +278,16 @@ 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.
scale.

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

.. versionadded:: 3.20
%End

bool renderingStopped() const;
Expand Down Expand Up @@ -425,6 +435,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 );
Expand Down
Expand Up @@ -77,7 +77,8 @@ Base class for raster data providers.
WriteLayerMetadata,
ProviderHintBenefitsFromResampling,
ProviderHintCanPerformProviderResampling,
ReloadData
ReloadData,
PixelDependentRendering,
};

typedef QFlags<QgsRasterDataProvider::ProviderCapability> ProviderCapabilities;
Expand Down
2 changes: 1 addition & 1 deletion python/core/auto_generated/raster/qgsrasterdrawer.sip.in
Expand Up @@ -20,7 +20,7 @@ The drawing pipe for raster layers.
#include "qgsrasterdrawer.h"
%End
public:
QgsRasterDrawer( QgsRasterIterator *iterator );
QgsRasterDrawer( QgsRasterIterator *iterator, double dpiTarget = -1.0 );

void draw( QPainter *p, QgsRasterViewPort *viewPort, const QgsMapToPixel *qgsMapToPixel, QgsRasterBlockFeedback *feedback = 0 );
%Docstring
Expand Down
2 changes: 2 additions & 0 deletions src/core/layout/qgslayoutitemmap.cpp
Expand Up @@ -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 )
Expand Down
1 change: 1 addition & 0 deletions src/core/qgsmaprendererjob.cpp
Expand Up @@ -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 );
Expand Down
9 changes: 9 additions & 0 deletions src/core/qgsmapsettings.cpp
Expand Up @@ -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
{
Expand Down
19 changes: 18 additions & 1 deletion src/core/qgsmapsettings.h
Expand Up @@ -204,6 +204,22 @@ 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 setDpiTarger()
*/
double dpiTarget() const;

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

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

protected:

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

QSize mSize;
float mDevicePixelRatio = 1.0;
Expand Down
3 changes: 3 additions & 0 deletions src/core/qgsrendercontext.cpp
Expand Up @@ -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 )
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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() );
Expand Down
20 changes: 20 additions & 0 deletions src/core/qgsrendercontext.h
Expand Up @@ -333,6 +333,15 @@ class CORE_EXPORT QgsRenderContext : public QgsTemporalRangeObject
*/
double scaleFactor() const {return mScaleFactor;}

/**
* Returns the targeted DPI for rendering.
* scale.
*
* \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.
Expand Down Expand Up @@ -476,6 +485,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.
Expand Down Expand Up @@ -928,6 +945,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;

Expand Down
3 changes: 2 additions & 1 deletion src/core/raster/qgsrasterdataprovider.h
Expand Up @@ -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()
PixelDependentRendering = 1 << 6, //! Provider's rendering is dependent on requested pixel size of the viewport
};

//! Provider capabilities
Expand Down
17 changes: 10 additions & 7 deletions src/core/raster/qgsrasterdrawer.cpp
Expand Up @@ -29,7 +29,9 @@
#include <QPrinter>
#endif

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

Expand Down Expand Up @@ -115,28 +117,29 @@ void QgsRasterDrawer::draw( QPainter *p, QgsRasterViewPort *viewPort, const QgsM
}
}

#include <QDebug>
void QgsRasterDrawer::drawImage( QPainter *p, QgsRasterViewPort *viewPort, const QImage &img, int topLeftCol, int topLeftRow, const QgsMapToPixel *qgsMapToPixel ) const
{
if ( !p || !viewPort )
{
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() + topLeftCol / dpiScaleFactor, viewPort->mTopLeftPoint.y() + 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 ) );

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

double rotation = qgsMapToPixel->mapRotation();
if ( rotation )
{
Expand All @@ -149,7 +152,7 @@ void QgsRasterDrawer::drawImage( QPainter *p, QgsRasterViewPort *viewPort, const
}
}

p->drawImage( tlPoint, img );
p->drawImage( tlPoint, dpiScaleFactor != 1.0 ? img.scaledToWidth( img.width() / dpiScaleFactor ) : img );

#if 0
// For debugging:
Expand Down
3 changes: 2 additions & 1 deletion src/core/raster/qgsrasterdrawer.h
Expand Up @@ -37,7 +37,7 @@ class QgsRasterIterator;
class CORE_EXPORT QgsRasterDrawer
{
public:
QgsRasterDrawer( QgsRasterIterator *iterator );
QgsRasterDrawer( QgsRasterIterator *iterator, double dpiTarget = -1.0 );

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

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

#endif // QGSRASTERDRAWER_H
17 changes: 15 additions & 2 deletions src/core/raster/qgsrasterlayerrenderer.cpp
Expand Up @@ -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 );
Expand Down Expand Up @@ -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::PixelDependentRendering
&& 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()
Expand Down Expand Up @@ -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 )
Expand Down
5 changes: 5 additions & 0 deletions src/providers/wms/qgswmsprovider.cpp
Expand Up @@ -2024,6 +2024,11 @@ int QgsWmsProvider::capabilities() const
// capability |= Capability::Prefetch;
}

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

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

0 comments on commit 8d1371f

Please sign in to comment.