From a1e0177f1e0fea8724413886a9c8c621088a19f6 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 3 Dec 2024 07:42:10 +1000 Subject: [PATCH] Move responsibility for drawing label metrics, rects to labeling engine Fixes a TODO --- src/core/labeling/qgslabelingengine.cpp | 119 ++++++++++++++---- .../labeling/qgsvectorlayerlabelprovider.cpp | 42 ------- 2 files changed, 95 insertions(+), 66 deletions(-) diff --git a/src/core/labeling/qgslabelingengine.cpp b/src/core/labeling/qgslabelingengine.cpp index 2d8045ad897c..cef0062771c5 100644 --- a/src/core/labeling/qgslabelingengine.cpp +++ b/src/core/labeling/qgslabelingengine.cpp @@ -517,41 +517,82 @@ void QgsLabelingEngine::drawLabels( QgsRenderContext &context, const QString &la lf->provider()->drawLabelBackground( context, label ); } - // draw the labels - for ( pal::LabelPosition *label : std::as_const( mLabels ) ) + if ( engineSettings().testFlag( Qgis::LabelingFlag::DrawLabelRectOnly ) ) { - if ( context.renderingStopped() ) - break; - - QgsLabelFeature *lf = label->getFeaturePart()->feature(); - if ( !lf ) + // features are pre-rotated but not scaled/translated, + // so we only disable rotation here. Ideally, they'd be + // also pre-scaled/translated, as suggested here: + // https://github.com/qgis/QGIS/issues/20071 + QgsMapToPixel xform = context.mapToPixel(); + xform.setMapRotation( 0, 0, 0 ); + + std::function drawLabelRect; + drawLabelRect = [&xform, painter, &drawLabelRect]( pal::LabelPosition * label ) { - continue; - } + QPointF outPt = xform.transform( label->getX(), label->getY() ).toQPointF(); - if ( !layerId.isEmpty() && lf->provider()->layerId() != layerId ) - continue; + QgsPointXY outPt2 = xform.transform( label->getX() + label->getWidth(), label->getY() + label->getHeight() ); + QRectF rect( 0, 0, outPt2.x() - outPt.x(), outPt2.y() - outPt.y() ); + painter->save(); + painter->setRenderHint( QPainter::Antialiasing, false ); + painter->translate( QPointF( outPt.x(), outPt.y() ) ); + painter->rotate( -label->getAlpha() * 180 / M_PI ); - context.expressionContext().setFeature( lf->feature() ); - context.expressionContext().setFields( lf->feature().fields() ); + if ( label->conflictsWithObstacle() ) + { + painter->setBrush( QColor( 255, 0, 0, 100 ) ); + painter->setPen( QColor( 255, 0, 0, 150 ) ); + } + else + { + painter->setBrush( QColor( 0, 255, 0, 100 ) ); + painter->setPen( QColor( 0, 255, 0, 150 ) ); + } - QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, lf->provider()->layerReferenceScale() ); - if ( lf->symbol() ) + painter->drawRect( rect ); + painter->restore(); + + if ( pal::LabelPosition *nextPart = label->nextPart() ) + drawLabelRect( nextPart ); + }; + + for ( pal::LabelPosition *label : std::as_const( mLabels ) ) { - symbolScope = QgsExpressionContextUtils::updateSymbolScope( lf->symbol(), symbolScope ); + drawLabelRect( label ); } - lf->provider()->drawLabel( context, label ); - // finished with symbol -- we can't keep it around after this, it may be deleted - lf->setSymbol( nullptr ); } - - // draw unplaced labels. These are always rendered on top - if ( settings.testFlag( Qgis::LabelingFlag::DrawUnplacedLabels ) || settings.testFlag( Qgis::LabelingFlag::CollectUnplacedLabels ) ) + else { - for ( pal::LabelPosition *label : std::as_const( mUnlabeled ) ) + if ( engineSettings().testFlag( Qgis::LabelingFlag::DrawLabelMetrics ) ) + { + // features are pre-rotated but not scaled/translated, + // so we only disable rotation here. Ideally, they'd be + // also pre-scaled/translated, as suggested here: + // https://github.com/qgis/QGIS/issues/20071 + QgsMapToPixel xform = context.mapToPixel(); + xform.setMapRotation( 0, 0, 0 ); + + std::function drawLabelMetricsRecursive; + drawLabelMetricsRecursive = [&xform, &context, &drawLabelMetricsRecursive]( pal::LabelPosition * label ) + { + QPointF outPt = xform.transform( label->getX(), label->getY() ).toQPointF(); + QgsLabelingEngine::drawLabelMetrics( label, xform, context, outPt ); + if ( pal::LabelPosition *nextPart = label->nextPart() ) + drawLabelMetricsRecursive( nextPart ); + }; + + for ( pal::LabelPosition *label : std::as_const( mLabels ) ) + { + drawLabelMetricsRecursive( label ); + } + } + + // draw the labels + for ( pal::LabelPosition *label : std::as_const( mLabels ) ) { if ( context.renderingStopped() ) break; + QgsLabelFeature *lf = label->getFeaturePart()->feature(); if ( !lf ) { @@ -569,10 +610,40 @@ void QgsLabelingEngine::drawLabels( QgsRenderContext &context, const QString &la { symbolScope = QgsExpressionContextUtils::updateSymbolScope( lf->symbol(), symbolScope ); } - lf->provider()->drawUnplacedLabel( context, label ); + lf->provider()->drawLabel( context, label ); // finished with symbol -- we can't keep it around after this, it may be deleted lf->setSymbol( nullptr ); } + + // draw unplaced labels. These are always rendered on top + if ( settings.testFlag( Qgis::LabelingFlag::DrawUnplacedLabels ) || settings.testFlag( Qgis::LabelingFlag::CollectUnplacedLabels ) ) + { + for ( pal::LabelPosition *label : std::as_const( mUnlabeled ) ) + { + if ( context.renderingStopped() ) + break; + QgsLabelFeature *lf = label->getFeaturePart()->feature(); + if ( !lf ) + { + continue; + } + + if ( !layerId.isEmpty() && lf->provider()->layerId() != layerId ) + continue; + + context.expressionContext().setFeature( lf->feature() ); + context.expressionContext().setFields( lf->feature().fields() ); + + QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, lf->provider()->layerReferenceScale() ); + if ( lf->symbol() ) + { + symbolScope = QgsExpressionContextUtils::updateSymbolScope( lf->symbol(), symbolScope ); + } + lf->provider()->drawUnplacedLabel( context, label ); + // finished with symbol -- we can't keep it around after this, it may be deleted + lf->setSymbol( nullptr ); + } + } } symbolScopePopper.reset(); diff --git a/src/core/labeling/qgsvectorlayerlabelprovider.cpp b/src/core/labeling/qgsvectorlayerlabelprovider.cpp index d47df8cbbe40..393b7a7b0603 100644 --- a/src/core/labeling/qgsvectorlayerlabelprovider.cpp +++ b/src/core/labeling/qgsvectorlayerlabelprovider.cpp @@ -495,8 +495,6 @@ void QgsVectorLayerLabelProvider::drawUnplacedLabel( QgsRenderContext &context, void QgsVectorLayerLabelProvider::drawLabelPrivate( pal::LabelPosition *label, QgsRenderContext &context, QgsPalLayerSettings &tmpLyr, Qgis::TextComponent drawType, double dpiRatio ) const { // NOTE: this is repeatedly called for multi-part labels - QPainter *painter = context.painter(); - Qgis::TextComponents components; switch ( drawType ) { @@ -523,46 +521,6 @@ void QgsVectorLayerLabelProvider::drawLabelPrivate( pal::LabelPosition *label, Q QPointF outPt = xform.transform( label->getX(), label->getY() ).toQPointF(); - if ( mEngine->engineSettings().testFlag( Qgis::LabelingFlag::DrawLabelRectOnly ) ) // TODO: this should get directly to labeling engine - { - //debugging rect - if ( drawType != Qgis::TextComponent::Text ) - return; - - QgsPointXY outPt2 = xform.transform( label->getX() + label->getWidth(), label->getY() + label->getHeight() ); - QRectF rect( 0, 0, outPt2.x() - outPt.x(), outPt2.y() - outPt.y() ); - painter->save(); - painter->setRenderHint( QPainter::Antialiasing, false ); - painter->translate( QPointF( outPt.x(), outPt.y() ) ); - painter->rotate( -label->getAlpha() * 180 / M_PI ); - - if ( label->conflictsWithObstacle() ) - { - painter->setBrush( QColor( 255, 0, 0, 100 ) ); - painter->setPen( QColor( 255, 0, 0, 150 ) ); - } - else - { - painter->setBrush( QColor( 0, 255, 0, 100 ) ); - painter->setPen( QColor( 0, 255, 0, 150 ) ); - } - - painter->drawRect( rect ); - painter->restore(); - - if ( label->nextPart() ) - drawLabelPrivate( label->nextPart(), context, tmpLyr, drawType, dpiRatio ); - - return; - } - if ( mEngine->engineSettings().testFlag( Qgis::LabelingFlag::DrawLabelMetrics ) ) - { - if ( drawType != Qgis::TextComponent::Text ) - return; - - QgsLabelingEngine::drawLabelMetrics( label, xform, context, outPt ); - } - QgsTextRenderer::Component component; component.dpiRatio = dpiRatio; component.origin = outPt;