Skip to content
Permalink
Browse files
Correctly handle rendered item results stored when map canvas redraw
partially uses cached layer results
  • Loading branch information
nyalldawson committed Sep 6, 2021
1 parent 8f2eb92 commit 40bef072649adb0c0992b4b98d82b6b9d8642dd2
@@ -33,14 +33,6 @@ Constructor for QgsRenderedAnnotationItemDetails.
sipRes = PyUnicode_FromString( str.toUtf8().constData() );
%End

virtual QgsRenderedAnnotationItemDetails *clone() const /Factory/;


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

QString itemId() const;
%Docstring
Returns the item ID of the associated annotation item.
@@ -89,6 +89,16 @@ should be retained.
.. seealso:: :py:func:`takeLabelingResults`

.. 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

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






};
@@ -39,6 +39,38 @@ Returns a list with details of the rendered annotation items within the specifie
.. versionadded:: 3.22
%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:
QgsRenderedItemResults( const QgsRenderedItemResults & );
@@ -9,6 +9,11 @@



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

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

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

virtual ~QgsRenderedItemDetails();

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

QgsRectangle boundingBox() const;
@@ -17,13 +17,8 @@
#include "qgsrenderedannotationitemdetails.h"

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

}

QgsRenderedAnnotationItemDetails *QgsRenderedAnnotationItemDetails::clone() const
{
return new QgsRenderedAnnotationItemDetails( *this );
}
@@ -43,21 +43,13 @@ class CORE_EXPORT QgsRenderedAnnotationItemDetails : public QgsRenderedItemDetai
% End
#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.
*/
QString itemId() const { return mItemId; }

private:

QString mLayerId;
QString mItemId;

};
@@ -147,6 +147,11 @@ void QgsMapRendererJob::start()
}
}

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

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

@@ -287,6 +287,16 @@ class CORE_EXPORT QgsMapRendererJob : public QObject SIP_ABSTRACT
*/
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).
* This should not be used if cached labeling was redrawn - see usedCachedLabels().
@@ -437,6 +447,8 @@ class CORE_EXPORT QgsMapRendererJob : public QObject SIP_ABSTRACT
std::unique_ptr< QgsRenderedItemResults > mRenderedItemResults;
#endif

QStringList mLayersRedrawnFromCache;

/**
* Prepares the cache for storing the result of labeling. Returns FALSE if
* the render cannot use cached labels and should not cache the result.
@@ -138,6 +138,7 @@ void QgsMapRendererSequentialJob::internalFinished()

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

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

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


@@ -61,20 +61,34 @@ class CORE_EXPORT QgsRenderedItemResults
*/
QList<const QgsRenderedAnnotationItemDetails *> renderedAnnotationItemsInBounds( const QgsRectangle &bounds ) const;

#ifndef SIP_RUN

/**
* Appends rendered item details to the results 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
* 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 );
#endif
void transferResults( QgsRenderedItemResults *other, const QStringList &layerIds );

/**
* 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:
#ifdef SIP_RUN
@@ -16,4 +16,10 @@

#include "qgsrendereditemdetails.h"

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

}

QgsRenderedItemDetails::~QgsRenderedItemDetails() = default;
@@ -21,6 +21,13 @@
#include "qgis_sip.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
* \brief Base class for detailed information about a rendered item.
@@ -39,12 +46,17 @@ class CORE_EXPORT QgsRenderedItemDetails
SIP_END
#endif

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

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).
@@ -62,6 +74,7 @@ class CORE_EXPORT QgsRenderedItemDetails

private:

QString mLayerId;
QgsRectangle mBounds;
};

@@ -495,11 +495,13 @@ void QgsMapCanvas::setCachingEnabled( bool enabled )
if ( enabled )
{
mCache = new QgsMapRendererCache;
mPreviousRenderedItemResults = std::make_unique< QgsRenderedItemResults >();
}
else
{
delete mCache;
mCache = nullptr;
mPreviousRenderedItemResults.reset();
}
}

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

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

void QgsMapCanvas::setParallelRenderingEnabled( bool enabled )
@@ -707,7 +712,26 @@ void QgsMapCanvas::rendererJobFinished()
}
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;

QImage img = mJob->renderedImage();
@@ -1319,6 +1319,12 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView, public QgsExpressionContex
*/
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)
*

0 comments on commit 40bef07

Please sign in to comment.