Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Don't cache labeling if blend modes are used
The flattening of the label results to an image blocks
blend modes applying correctly to underlying layers
  • Loading branch information
nyalldawson committed Feb 7, 2017
1 parent 2b3805e commit d0d9bab
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 34 deletions.
3 changes: 2 additions & 1 deletion src/core/qgsmaprenderercustompainterjob.cpp
Expand Up @@ -83,8 +83,9 @@ void QgsMapRendererCustomPainterJob::start()
mLabelingEngineV2->setMapSettings( mSettings );
}

bool canUseLabelCache = prepareLabelCache();
mLayerJobs = prepareJobs( mPainter, mLabelingEngineV2 );
mLabelJob = prepareLabelingJob( mPainter, mLabelingEngineV2 );
mLabelJob = prepareLabelingJob( mPainter, mLabelingEngineV2, canUseLabelCache );

QgsDebugMsg( "Rendering prepared in (seconds): " + QString( "%1" ).arg( prepareTime.elapsed() / 1000.0 ) );

Expand Down
73 changes: 42 additions & 31 deletions src/core/qgsmaprendererjob.cpp
Expand Up @@ -35,6 +35,7 @@
#include "qgscsexception.h"
#include "qgslabelingengine.h"
#include "qgsmaplayerlistutils.h"
#include "qgsvectorlayerlabeling.h"

///@cond PRIVATE

Expand Down Expand Up @@ -70,6 +71,39 @@ const QgsMapSettings& QgsMapRendererJob::mapSettings() const
return mSettings;
}

bool QgsMapRendererJob::prepareLabelCache() const
{
bool canCache = mCache;

// calculate which layers will be labeled
QSet< QgsMapLayer* > labeledLayers;
Q_FOREACH ( const QgsMapLayer* ml, mSettings.layers() )
{
QgsVectorLayer* vl = const_cast< QgsVectorLayer* >( qobject_cast<const QgsVectorLayer *>( ml ) );
if ( vl && QgsPalLabeling::staticWillUseLayer( vl ) )
labeledLayers << vl;
if ( vl->labeling()->requiresAdvancedEffects( vl ) )
{
canCache = false;
break;
}
}

if ( mCache && mCache->hasCacheImage( LABEL_CACHE_ID ) )
{
// we may need to clear label cache and re-register labeled features - check for that here

// can we reuse the cached label solution?
bool canUseCache = canCache && mCache->dependentLayers( LABEL_CACHE_ID ).toSet() == labeledLayers;
if ( !canUseCache )
{
// no - participating layers have changed
mCache->clearCacheImage( LABEL_CACHE_ID );
}
}
return canCache;
}


bool QgsMapRendererJob::reprojectToLayerExtent( const QgsMapLayer *ml, const QgsCoordinateTransform& ct, QgsRectangle &extent, QgsRectangle &r2 )
{
Expand Down Expand Up @@ -190,30 +224,7 @@ LayerRenderJobs QgsMapRendererJob::prepareJobs( QPainter* painter, QgsLabelingEn
QgsDebugMsg( QString( "CACHE VALID: %1" ).arg( cacheValid ) );
}

bool hasCachedLabels = false;
if ( cacheValid && mCache->hasCacheImage( LABEL_CACHE_ID ) )
{
// we may need to clear label cache and re-register labeled features - check for that here

// calculate which layers will be labeled
QSet< QgsMapLayer* > labeledLayers;
Q_FOREACH ( const QgsMapLayer* ml, mSettings.layers() )
{
QgsVectorLayer* vl = const_cast< QgsVectorLayer* >( qobject_cast<const QgsVectorLayer *>( ml ) );
if ( vl && QgsPalLabeling::staticWillUseLayer( vl ) )
labeledLayers << vl;
}

// can we reuse the cached label solution?
bool canUseCache = mCache->dependentLayers( LABEL_CACHE_ID ).toSet() == labeledLayers;
if ( !canUseCache )
{
// no - participating layers have changed
mCache->clearCacheImage( LABEL_CACHE_ID );
}

hasCachedLabels = canUseCache;
}
bool requiresLabelRedraw = !( mCache && mCache->hasCacheImage( LABEL_CACHE_ID ) );

mGeometryCaches.clear();

Expand Down Expand Up @@ -258,9 +269,9 @@ LayerRenderJobs QgsMapRendererJob::prepareJobs( QPainter* painter, QgsLabelingEn
if ( mCache && ml->type() == QgsMapLayer::VectorLayer )
{
QgsVectorLayer* vl = qobject_cast<QgsVectorLayer *>( ml );
bool requiresLabelRedraw = false;
requiresLabelRedraw = ( labelingEngine2 && QgsPalLabeling::staticWillUseLayer( vl ) ) && !hasCachedLabels;
if ( vl->isEditable() || requiresLabelRedraw )
bool requiresLabeling = false;
requiresLabeling = ( labelingEngine2 && QgsPalLabeling::staticWillUseLayer( vl ) ) && requiresLabelRedraw;
if ( vl->isEditable() || requiresLabeling )
{
mCache->clearCacheImage( ml->id() );
}
Expand Down Expand Up @@ -345,7 +356,7 @@ LayerRenderJobs QgsMapRendererJob::prepareJobs( QPainter* painter, QgsLabelingEn
return layerJobs;
}

LabelRenderJob QgsMapRendererJob::prepareLabelingJob( QPainter* painter, QgsLabelingEngine* labelingEngine2 )
LabelRenderJob QgsMapRendererJob::prepareLabelingJob( QPainter* painter, QgsLabelingEngine* labelingEngine2, bool canUseLabelCache )
{
LabelRenderJob job;
job.context = QgsRenderContext::fromMapSettings( mSettings );
Expand All @@ -354,8 +365,8 @@ LabelRenderJob QgsMapRendererJob::prepareLabelingJob( QPainter* painter, QgsLabe
job.context.setExtent( mSettings.visibleExtent() );

// if we can use the cache, let's do it and avoid rendering!
bool canUseCache = mCache && mCache->hasCacheImage( LABEL_CACHE_ID );
if ( canUseCache )
bool hasCache = canUseLabelCache && mCache && mCache->hasCacheImage( LABEL_CACHE_ID );
if ( hasCache )
{
job.cached = true;
job.complete = true;
Expand All @@ -364,7 +375,7 @@ LabelRenderJob QgsMapRendererJob::prepareLabelingJob( QPainter* painter, QgsLabe
}
else
{
if ( mCache || !painter )
if ( canUseLabelCache && ( mCache || !painter ) )
{
// Flattened image for drawing labels
QImage * mypFlattenedImage = nullptr;
Expand Down
11 changes: 10 additions & 1 deletion src/core/qgsmaprendererjob.h
Expand Up @@ -70,6 +70,8 @@ struct LabelRenderJob
QImage* img = nullptr;
//! If true, img already contains cached image from previous rendering
bool cached = false;
//! Will be true if labeling is eligible for caching
bool canUseCache = false;
//! If true then label render is complete
bool complete = false;
//! Time it took to render the labels in ms (it is -1 if not rendered or still rendering)
Expand Down Expand Up @@ -218,6 +220,13 @@ class CORE_EXPORT QgsMapRendererJob : public QObject

int mRenderingTime = 0;

/**
* Prepares the cache for storing the result of labeling. Returns false if
* the render cannot use cached labels and should not cache the result.
* @note not available in Python bindings
*/
bool prepareLabelCache() const;

//! @note not available in python bindings
LayerRenderJobs prepareJobs( QPainter* painter, QgsLabelingEngine* labelingEngine2 );

Expand All @@ -226,7 +235,7 @@ class CORE_EXPORT QgsMapRendererJob : public QObject
* @note not available in python bindings
* @note added in QGIS 3.0
*/
LabelRenderJob prepareLabelingJob( QPainter* painter, QgsLabelingEngine* labelingEngine2 );
LabelRenderJob prepareLabelingJob( QPainter* painter, QgsLabelingEngine* labelingEngine2, bool canUseLabelCache = true );

//! @note not available in python bindings
static QImage composeImage( const QgsMapSettings& settings, const LayerRenderJobs& jobs, const LabelRenderJob& labelJob );
Expand Down
3 changes: 2 additions & 1 deletion src/core/qgsmaprendererparalleljob.cpp
Expand Up @@ -63,8 +63,9 @@ void QgsMapRendererParallelJob::start()
mLabelingEngineV2->setMapSettings( mSettings );
}

bool canUseLabelCache = prepareLabelCache();
mLayerJobs = prepareJobs( nullptr, mLabelingEngineV2 );
mLabelJob = prepareLabelingJob( nullptr, mLabelingEngineV2 );
mLabelJob = prepareLabelingJob( nullptr, mLabelingEngineV2, canUseLabelCache );

QgsDebugMsg( QString( "QThreadPool max thread count is %1" ).arg( QThreadPool::globalInstance()->maxThreadCount() ) );

Expand Down
19 changes: 19 additions & 0 deletions src/core/qgsrulebasedlabeling.cpp
Expand Up @@ -119,6 +119,20 @@ void QgsRuleBasedLabeling::Rule::updateElseRules()
}
}

bool QgsRuleBasedLabeling::Rule::requiresAdvancedEffects() const
{
if ( mSettings && mSettings->format().containsAdvancedEffects() )
return true;

Q_FOREACH ( Rule* rule, mChildren )
{
if ( rule->requiresAdvancedEffects() )
return true;
}

return false;
}

void QgsRuleBasedLabeling::Rule::subProviderIds( QStringList& list ) const
{
Q_FOREACH ( const Rule* rule, mChildren )
Expand Down Expand Up @@ -437,3 +451,8 @@ QgsPalLayerSettings QgsRuleBasedLabeling::settings( QgsVectorLayer* layer, const

return QgsPalLayerSettings();
}

bool QgsRuleBasedLabeling::requiresAdvancedEffects( QgsVectorLayer* ) const
{
return mRootRule->requiresAdvancedEffects();
}
7 changes: 7 additions & 0 deletions src/core/qgsrulebasedlabeling.h
Expand Up @@ -260,6 +260,12 @@ class CORE_EXPORT QgsRuleBasedLabeling : public QgsAbstractVectorLayerLabeling
//! register individual features
RegisterResult registerFeature( QgsFeature& feature, QgsRenderContext& context, RuleToProviderMap& subProviders, QgsGeometry* obstacleGeometry = nullptr );

/**
* Returns true if this rule or any of its children requires advanced composition effects
* to render.
*/
bool requiresAdvancedEffects() const;

protected:

/**
Expand Down Expand Up @@ -326,6 +332,7 @@ class CORE_EXPORT QgsRuleBasedLabeling : public QgsAbstractVectorLayerLabeling
virtual QgsVectorLayerLabelProvider *provider( QgsVectorLayer* layer ) const override;
virtual QStringList subProviders() const override;
virtual QgsPalLayerSettings settings( QgsVectorLayer* layer, const QString& providerId = QString() ) const override;
bool requiresAdvancedEffects( QgsVectorLayer* layer ) const override;

protected:
Rule* mRootRule;
Expand Down
5 changes: 5 additions & 0 deletions src/core/qgsvectorlayerlabeling.cpp
Expand Up @@ -60,3 +60,8 @@ QgsPalLayerSettings QgsVectorLayerSimpleLabeling::settings( QgsVectorLayer* laye
else
return QgsPalLayerSettings();
}

bool QgsVectorLayerSimpleLabeling::requiresAdvancedEffects( QgsVectorLayer* layer ) const
{
return settings( layer ).format().containsAdvancedEffects();
}
9 changes: 9 additions & 0 deletions src/core/qgsvectorlayerlabeling.h
Expand Up @@ -56,6 +56,14 @@ class CORE_EXPORT QgsAbstractVectorLayerLabeling
//! they are identified by their ID (e.g. in case of rule-based labeling, provider ID == rule key)
virtual QgsPalLayerSettings settings( QgsVectorLayer* layer, const QString& providerId = QString() ) const = 0;

/**
* Returns true if drawing labels requires advanced effects like composition
* modes, which could prevent it being used as an isolated cached image
* or exported to a vector format.
* @note added in QGIS 3.0
*/
virtual bool requiresAdvancedEffects( QgsVectorLayer* layer ) const = 0;

// static stuff

//! Try to create instance of an implementation based on the XML data
Expand All @@ -79,6 +87,7 @@ class CORE_EXPORT QgsVectorLayerSimpleLabeling : public QgsAbstractVectorLayerLa
virtual QgsVectorLayerLabelProvider* provider( QgsVectorLayer* layer ) const override;
virtual QDomElement save( QDomDocument& doc ) const override;
virtual QgsPalLayerSettings settings( QgsVectorLayer* layer, const QString& providerId = QString() ) const override;
bool requiresAdvancedEffects( QgsVectorLayer* layer ) const override;
};

#endif // QGSVECTORLAYERLABELING_H

0 comments on commit d0d9bab

Please sign in to comment.