Skip to content

Commit

Permalink
Correctly handle rendered item results stored when map canvas redraw
Browse files Browse the repository at this point in the history
partially uses cached layer results
  • Loading branch information
nyalldawson committed Sep 6, 2021
1 parent 8f2eb92 commit 40bef07
Show file tree
Hide file tree
Showing 17 changed files with 325 additions and 32 deletions.
Expand Up @@ -33,14 +33,6 @@ Constructor for QgsRenderedAnnotationItemDetails.
sipRes = PyUnicode_FromString( str.toUtf8().constData() ); sipRes = PyUnicode_FromString( str.toUtf8().constData() );
%End %End


virtual QgsRenderedAnnotationItemDetails *clone() const /Factory/;


QString layerId() const;
%Docstring
Returns the layer ID of the associated map layer.
%End

QString itemId() const; QString itemId() const;
%Docstring %Docstring
Returns the item ID of the associated annotation item. Returns the item ID of the associated annotation item.
Expand Down
11 changes: 11 additions & 0 deletions python/core/auto_generated/maprenderer/qgsmaprendererjob.sip.in
Expand Up @@ -89,6 +89,16 @@ should be retained.
.. seealso:: :py:func:`takeLabelingResults` .. seealso:: :py:func:`takeLabelingResults`


.. versionadded:: 3.0 .. versionadded:: 3.0
%End

QStringList layersRedrawnFromCache() const;
%Docstring
Returns a list of the layer IDs for all layers which were redrawn from cached
images.

This method should only be called after the render job is completed.

.. versionadded:: 3.22
%End %End


virtual QgsLabelingResults *takeLabelingResults() = 0 /Transfer/; virtual QgsLabelingResults *takeLabelingResults() = 0 /Transfer/;
Expand Down Expand Up @@ -205,6 +215,7 @@ emitted when asynchronous rendering is finished (or canceled).











}; };
Expand Down
Expand Up @@ -39,6 +39,38 @@ Returns a list with details of the rendered annotation items within the specifie
.. versionadded:: 3.22 .. versionadded:: 3.22
%End %End


void appendResults( const QList< QgsRenderedItemDetails * > &results /Transfer/, const QgsRenderContext &context );
%Docstring
Appends rendered item details to the results object.

Ownership of ``results`` is transferred to the this object.

The render ``context`` argument is used to specify the render context used to render the items. It will be used
to transform the details to the destination map CRS.
%End

void transferResults( QgsRenderedItemResults *other, const QStringList &layerIds );
%Docstring
Transfers all results from an ``other`` QgsRenderedItemResults object where the items
have layer IDs matching the specified list.

Items are removed from ``other`` and transferred to this object.

.. warning::

After calling this method the ``other`` results will be left in an undefined state.
%End

void transferResults( QgsRenderedItemResults *other );
%Docstring
Transfers all results from an ``other`` QgsRenderedItemResults object to this one.

Items are removed from ``other`` and transferred to this object.

.. warning::

After calling this method the ``other`` results will be left in an undefined state.
%End


private: private:
QgsRenderedItemResults( const QgsRenderedItemResults & ); QgsRenderedItemResults( const QgsRenderedItemResults & );
Expand Down
14 changes: 12 additions & 2 deletions python/core/auto_generated/qgsrendereditemdetails.sip.in
Expand Up @@ -9,6 +9,11 @@






// this is needed for the "convert to subclass" code below to compile
%ModuleHeaderCode
#include "qgsrenderedannotationitemdetails.h"
%End

class QgsRenderedItemDetails class QgsRenderedItemDetails
{ {
%Docstring(signature="appended") %Docstring(signature="appended")
Expand All @@ -29,11 +34,16 @@ Base class for detailed information about a rendered item.
sipType = 0; sipType = 0;
%End %End


QgsRenderedItemDetails( const QString &layerId );
%Docstring
Constructor for QgsRenderedItemDetails.
%End

virtual ~QgsRenderedItemDetails(); virtual ~QgsRenderedItemDetails();


virtual QgsRenderedItemDetails *clone() const = 0 /Factory/; QString layerId() const;
%Docstring %Docstring
Clones the details. Returns the layer ID of the associated map layer.
%End %End


QgsRectangle boundingBox() const; QgsRectangle boundingBox() const;
Expand Down
7 changes: 1 addition & 6 deletions src/core/annotations/qgsrenderedannotationitemdetails.cpp
Expand Up @@ -17,13 +17,8 @@
#include "qgsrenderedannotationitemdetails.h" #include "qgsrenderedannotationitemdetails.h"


QgsRenderedAnnotationItemDetails::QgsRenderedAnnotationItemDetails( const QString &layerId, const QString &itemId ) QgsRenderedAnnotationItemDetails::QgsRenderedAnnotationItemDetails( const QString &layerId, const QString &itemId )
: mLayerId( layerId ) : QgsRenderedItemDetails( layerId )
, mItemId( itemId ) , mItemId( itemId )
{ {


} }

QgsRenderedAnnotationItemDetails *QgsRenderedAnnotationItemDetails::clone() const
{
return new QgsRenderedAnnotationItemDetails( *this );
}
8 changes: 0 additions & 8 deletions src/core/annotations/qgsrenderedannotationitemdetails.h
Expand Up @@ -43,21 +43,13 @@ class CORE_EXPORT QgsRenderedAnnotationItemDetails : public QgsRenderedItemDetai
% End % End
#endif #endif


QgsRenderedAnnotationItemDetails *clone() const override SIP_FACTORY;

/**
* Returns the layer ID of the associated map layer.
*/
QString layerId() const { return mLayerId; }

/** /**
* Returns the item ID of the associated annotation item. * Returns the item ID of the associated annotation item.
*/ */
QString itemId() const { return mItemId; } QString itemId() const { return mItemId; }


private: private:


QString mLayerId;
QString mItemId; QString mItemId;


}; };
Expand Down
6 changes: 6 additions & 0 deletions src/core/maprenderer/qgsmaprendererjob.cpp
Expand Up @@ -147,6 +147,11 @@ void QgsMapRendererJob::start()
} }
} }


QStringList QgsMapRendererJob::layersRedrawnFromCache() const
{
return mLayersRedrawnFromCache;
}

QgsRenderedItemResults *QgsMapRendererJob::takeRenderedItemResults() QgsRenderedItemResults *QgsMapRendererJob::takeRenderedItemResults()
{ {
return mRenderedItemResults.release(); return mRenderedItemResults.release();
Expand Down Expand Up @@ -513,6 +518,7 @@ std::vector<LayerRenderJob> QgsMapRendererJob::prepareJobs( QPainter *painter, Q
job.img->setDevicePixelRatio( static_cast<qreal>( mSettings.devicePixelRatio() ) ); job.img->setDevicePixelRatio( static_cast<qreal>( mSettings.devicePixelRatio() ) );
job.renderer = nullptr; job.renderer = nullptr;
job.context()->setPainter( nullptr ); job.context()->setPainter( nullptr );
mLayersRedrawnFromCache.append( ml->id() );
continue; continue;
} }


Expand Down
12 changes: 12 additions & 0 deletions src/core/maprenderer/qgsmaprendererjob.h
Expand Up @@ -287,6 +287,16 @@ class CORE_EXPORT QgsMapRendererJob : public QObject SIP_ABSTRACT
*/ */
virtual bool usedCachedLabels() const = 0; virtual bool usedCachedLabels() const = 0;


/**
* Returns a list of the layer IDs for all layers which were redrawn from cached
* images.
*
* This method should only be called after the render job is completed.
*
* \since QGIS 3.22
*/
QStringList layersRedrawnFromCache() const;

/** /**
* Gets pointer to internal labeling engine (in order to get access to the results). * Gets pointer to internal labeling engine (in order to get access to the results).
* This should not be used if cached labeling was redrawn - see usedCachedLabels(). * This should not be used if cached labeling was redrawn - see usedCachedLabels().
Expand Down Expand Up @@ -437,6 +447,8 @@ class CORE_EXPORT QgsMapRendererJob : public QObject SIP_ABSTRACT
std::unique_ptr< QgsRenderedItemResults > mRenderedItemResults; std::unique_ptr< QgsRenderedItemResults > mRenderedItemResults;
#endif #endif


QStringList mLayersRedrawnFromCache;

/** /**
* Prepares the cache for storing the result of labeling. Returns FALSE if * Prepares the cache for storing the result of labeling. Returns FALSE if
* the render cannot use cached labels and should not cache the result. * the render cannot use cached labels and should not cache the result.
Expand Down
1 change: 1 addition & 0 deletions src/core/maprenderer/qgsmaprenderersequentialjob.cpp
Expand Up @@ -138,6 +138,7 @@ void QgsMapRendererSequentialJob::internalFinished()


mLabelingResults.reset( mInternalJob->takeLabelingResults() ); mLabelingResults.reset( mInternalJob->takeLabelingResults() );
mUsedCachedLabels = mInternalJob->usedCachedLabels(); mUsedCachedLabels = mInternalJob->usedCachedLabels();
mLayersRedrawnFromCache = mInternalJob->layersRedrawnFromCache();


mRenderedItemResults.reset( mInternalJob->takeRenderedItemResults() ); mRenderedItemResults.reset( mInternalJob->takeRenderedItemResults() );


Expand Down
31 changes: 31 additions & 0 deletions src/core/maprenderer/qgsrendereditemresults.cpp
Expand Up @@ -126,4 +126,35 @@ void QgsRenderedItemResults::appendResults( const QList<QgsRenderedItemDetails *
} }
} }


void QgsRenderedItemResults::transferResults( QgsRenderedItemResults *other, const QStringList &layerIds )
{
for ( auto it = other->mDetails.begin(); it != other->mDetails.end(); )
{
if ( layerIds.contains( ( *it )->layerId() ) )
{
if ( QgsRenderedAnnotationItemDetails *annotationDetails = dynamic_cast< QgsRenderedAnnotationItemDetails * >( ( *it ).get() ) )
mAnnotationItemsIndex->insert( annotationDetails, annotationDetails->boundingBox() );

mDetails.emplace_back( std::move( *it ) );
other->mDetails.erase( it );
}
else
{
it++;
}
}
}

void QgsRenderedItemResults::transferResults( QgsRenderedItemResults *other )
{
for ( auto it = other->mDetails.begin(); it != other->mDetails.end(); ++it )
{
if ( QgsRenderedAnnotationItemDetails *annotationDetails = dynamic_cast< QgsRenderedAnnotationItemDetails * >( ( *it ).get() ) )
mAnnotationItemsIndex->insert( annotationDetails, annotationDetails->boundingBox() );

mDetails.emplace_back( std::move( *it ) );
}
other->mDetails.clear();
}



24 changes: 19 additions & 5 deletions src/core/maprenderer/qgsrendereditemresults.h
Expand Up @@ -61,20 +61,34 @@ class CORE_EXPORT QgsRenderedItemResults
*/ */
QList<const QgsRenderedAnnotationItemDetails *> renderedAnnotationItemsInBounds( const QgsRectangle &bounds ) const; QList<const QgsRenderedAnnotationItemDetails *> renderedAnnotationItemsInBounds( const QgsRectangle &bounds ) const;


#ifndef SIP_RUN

/** /**
* Appends rendered item details to the results object. * Appends rendered item details to the results object.
* *
* Ownership of \a results is transferred to the this object. * Ownership of \a results is transferred to the this object.
* *
* The render \a context argument is used to specify the render context used to render the items. It will be used * The render \a context argument is used to specify the render context used to render the items. It will be used
* to transform the details to the destination map CRS. * to transform the details to the destination map CRS.
*/
void appendResults( const QList< QgsRenderedItemDetails * > &results SIP_TRANSFER, const QgsRenderContext &context );

/**
* Transfers all results from an \a other QgsRenderedItemResults object where the items
* have layer IDs matching the specified list.
*
* Items are removed from \a other and transferred to this object.
* *
* \note Not available in Python bindings. * \warning After calling this method the \a other results will be left in an undefined state.
*/ */
void appendResults( const QList< QgsRenderedItemDetails * > &results, const QgsRenderContext &context ); void transferResults( QgsRenderedItemResults *other, const QStringList &layerIds );
#endif
/**
* Transfers all results from an \a other QgsRenderedItemResults object to this one.
*
* Items are removed from \a other and transferred to this object.
*
* \warning After calling this method the \a other results will be left in an undefined state.
*/
void transferResults( QgsRenderedItemResults *other );


private: private:
#ifdef SIP_RUN #ifdef SIP_RUN
Expand Down
6 changes: 6 additions & 0 deletions src/core/qgsrendereditemdetails.cpp
Expand Up @@ -16,4 +16,10 @@


#include "qgsrendereditemdetails.h" #include "qgsrendereditemdetails.h"


QgsRenderedItemDetails::QgsRenderedItemDetails( const QString &layerId )
: mLayerId( layerId )
{

}

QgsRenderedItemDetails::~QgsRenderedItemDetails() = default; QgsRenderedItemDetails::~QgsRenderedItemDetails() = default;
17 changes: 15 additions & 2 deletions src/core/qgsrendereditemdetails.h
Expand Up @@ -21,6 +21,13 @@
#include "qgis_sip.h" #include "qgis_sip.h"
#include "qgsrectangle.h" #include "qgsrectangle.h"


#ifdef SIP_RUN
// this is needed for the "convert to subclass" code below to compile
% ModuleHeaderCode
#include "qgsrenderedannotationitemdetails.h"
% End
#endif

/** /**
* \ingroup core * \ingroup core
* \brief Base class for detailed information about a rendered item. * \brief Base class for detailed information about a rendered item.
Expand All @@ -39,12 +46,17 @@ class CORE_EXPORT QgsRenderedItemDetails
SIP_END SIP_END
#endif #endif


/**
* Constructor for QgsRenderedItemDetails.
*/
QgsRenderedItemDetails( const QString &layerId );

virtual ~QgsRenderedItemDetails(); virtual ~QgsRenderedItemDetails();


/** /**
* Clones the details. * Returns the layer ID of the associated map layer.
*/ */
virtual QgsRenderedItemDetails *clone() const = 0 SIP_FACTORY; QString layerId() const { return mLayerId; }


/** /**
* Returns the bounding box of the item (in map units). * Returns the bounding box of the item (in map units).
Expand All @@ -62,6 +74,7 @@ class CORE_EXPORT QgsRenderedItemDetails


private: private:


QString mLayerId;
QgsRectangle mBounds; QgsRectangle mBounds;
}; };


Expand Down
26 changes: 25 additions & 1 deletion src/gui/qgsmapcanvas.cpp
Expand Up @@ -495,11 +495,13 @@ void QgsMapCanvas::setCachingEnabled( bool enabled )
if ( enabled ) if ( enabled )
{ {
mCache = new QgsMapRendererCache; mCache = new QgsMapRendererCache;
mPreviousRenderedItemResults = std::make_unique< QgsRenderedItemResults >();
} }
else else
{ {
delete mCache; delete mCache;
mCache = nullptr; mCache = nullptr;
mPreviousRenderedItemResults.reset();
} }
} }


Expand All @@ -512,6 +514,9 @@ void QgsMapCanvas::clearCache()
{ {
if ( mCache ) if ( mCache )
mCache->clear(); mCache->clear();

if ( mPreviousRenderedItemResults )
mPreviousRenderedItemResults = std::make_unique< QgsRenderedItemResults >();
} }


void QgsMapCanvas::setParallelRenderingEnabled( bool enabled ) void QgsMapCanvas::setParallelRenderingEnabled( bool enabled )
Expand Down Expand Up @@ -707,7 +712,26 @@ void QgsMapCanvas::rendererJobFinished()
} }
mLabelingResultsOutdated = false; mLabelingResultsOutdated = false;


mRenderedItemResults.reset( mJob->takeRenderedItemResults() ); std::unique_ptr< QgsRenderedItemResults > renderedItemResults( mJob->takeRenderedItemResults() );
// if a layer was redrawn from the cached version, we should copy any existing rendered item results from that layer
if ( mRenderedItemResults )
{
renderedItemResults->transferResults( mRenderedItemResults.get(), mJob->layersRedrawnFromCache() );
}
if ( mPreviousRenderedItemResults )
{
// also transfer any results from previous renders which happened before this
renderedItemResults->transferResults( mPreviousRenderedItemResults.get(), mJob->layersRedrawnFromCache() );
}
if ( mRenderedItemResults && mPreviousRenderedItemResults )
{
// for other layers which ARE present in the most recent rendered item results BUT were not part of this render, we
// store the results in a temporary store in case they are later switched back on and the layer's image is taken
// from the cache
mPreviousRenderedItemResults->transferResults( mRenderedItemResults.get() );
}

mRenderedItemResults = std::move( renderedItemResults );
mRenderedItemResultsOutdated = false; mRenderedItemResultsOutdated = false;


QImage img = mJob->renderedImage(); QImage img = mJob->renderedImage();
Expand Down
6 changes: 6 additions & 0 deletions src/gui/qgsmapcanvas.h
Expand Up @@ -1319,6 +1319,12 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView, public QgsExpressionContex
*/ */
std::unique_ptr< QgsRenderedItemResults > mRenderedItemResults; std::unique_ptr< QgsRenderedItemResults > mRenderedItemResults;


/**
* Rendered results stored from previously rendered maps
* \since QGIS 3.22
*/
std::unique_ptr< QgsRenderedItemResults > mPreviousRenderedItemResults;

/** /**
* TRUE if the rendered item results stored in mRenderedItemResults are outdated (e.g. as a result of an ongoing canvas render) * TRUE if the rendered item results stored in mRenderedItemResults are outdated (e.g. as a result of an ongoing canvas render)
* *
Expand Down

0 comments on commit 40bef07

Please sign in to comment.