Skip to content
Permalink
Browse files

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 d0d9bab0a69717aee087fad149e62cce36df82f6
@@ -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 ) );

@@ -35,6 +35,7 @@
#include "qgscsexception.h"
#include "qgslabelingengine.h"
#include "qgsmaplayerlistutils.h"
#include "qgsvectorlayerlabeling.h"

///@cond PRIVATE

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

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

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

@@ -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 )
@@ -437,3 +451,8 @@ QgsPalLayerSettings QgsRuleBasedLabeling::settings( QgsVectorLayer* layer, const

return QgsPalLayerSettings();
}

bool QgsRuleBasedLabeling::requiresAdvancedEffects( QgsVectorLayer* ) const
{
return mRootRule->requiresAdvancedEffects();
}
@@ -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:

/**
@@ -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;
@@ -60,3 +60,8 @@ QgsPalLayerSettings QgsVectorLayerSimpleLabeling::settings( QgsVectorLayer* laye
else
return QgsPalLayerSettings();
}

bool QgsVectorLayerSimpleLabeling::requiresAdvancedEffects( QgsVectorLayer* layer ) const
{
return settings( layer ).format().containsAdvancedEffects();
}
@@ -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
@@ -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.
You can’t perform that action at this time.