QGIS API Documentation  3.27.0-Master (0e23467727)
qgsgrouplayerrenderer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsgrouplayerrenderer.cpp
3  ----------------
4  Date : September 2021
5  Copyright : (C) 2021 by Nyall Dawson
6  Email : nyall dot dawson at gmail dot com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgsgrouplayerrenderer.h"
19 #include "qgsgrouplayer.h"
20 #include "qgsfeedback.h"
21 #include "qgspainteffect.h"
22 #include "qgsrendercontext.h"
23 #include "qgslogger.h"
24 #include <optional>
25 
27  : QgsMapLayerRenderer( layer->id(), &context )
28  , mFeedback( std::make_unique< QgsFeedback >() )
29  , mLayerOpacity( layer->opacity() )
30 {
31  const QList< QgsMapLayer * > layers = layer->childLayers();
32  const QgsCoordinateReferenceSystem destinationCrs = context.coordinateTransform().destinationCrs();
33  for ( QgsMapLayer *childLayer : layers )
34  {
35  // we have to temporarily set the context's crs and extent to the correct one for the child layer, BEFORE creating the
36  // child layer's renderer
37  QgsCoordinateTransform layerToDestTransform( childLayer->crs(), destinationCrs, context.transformContext() );
38  layerToDestTransform.setBallparkTransformsAreAppropriate( true );
39  context.setCoordinateTransform( layerToDestTransform );
40  try
41  {
42  const QgsRectangle extentInChildLayerCrs = layerToDestTransform.transformBoundingBox( context.mapExtent(), Qgis::TransformDirection::Reverse );
43  context.setExtent( extentInChildLayerCrs );
44  }
45  catch ( QgsCsException & )
46  {
47  QgsDebugMsg( QStringLiteral( "Error transforming extent of %1 to destination CRS" ).arg( childLayer->id() ) );
48  continue;
49  }
50 
51  mChildRenderers.emplace_back( childLayer->createMapRenderer( context ) );
52  mRendererCompositionModes.emplace_back( childLayer->blendMode() );
53  mRendererOpacity.emplace_back( childLayer->type() != QgsMapLayerType::RasterLayer ? childLayer->opacity() : 1.0 );
54  mTransforms.emplace_back( layerToDestTransform );
55  }
56 
57  mPaintEffect.reset( layer->paintEffect() && layer->paintEffect()->enabled() ? layer->paintEffect()->clone() : nullptr );
58 
59  mForceRasterRender = layer->blendMode() != QPainter::CompositionMode_SourceOver;
60 }
61 
63 
65 {
66  return mFeedback.get();
67 }
68 
70 {
71  QgsRenderContext &context = *renderContext();
72 
73  context.painter()->save();
74  if ( mPaintEffect )
75  {
76  mPaintEffect->begin( context );
77  }
78 
79  const QgsCoordinateReferenceSystem destinationCrs = context.coordinateTransform().destinationCrs();
80  bool canceled = false;
81  int i = 0;
82  for ( const std::unique_ptr< QgsMapLayerRenderer > &renderer : std::as_const( mChildRenderers ) )
83  {
84  if ( mFeedback->isCanceled() )
85  {
86  canceled = true;
87  break;
88  }
89 
90  context.setCoordinateTransform( mTransforms[i] );
91 
92  // don't need to catch exceptions here -- it would have already been caught in the QgsGroupLayerRenderer constructor!
93  const QgsRectangle extentInChildLayerCrs = mTransforms[i].transformBoundingBox( context.mapExtent(), Qgis::TransformDirection::Reverse );
94  context.setExtent( extentInChildLayerCrs );
95 
96  QImage image;
97  if ( context.useAdvancedEffects() )
98  context.painter()->setCompositionMode( mRendererCompositionModes[i] );
99 
100  QPainter *prevPainter = context.painter();
101  std::unique_ptr< QPainter > imagePainter;
102  if ( renderer->forceRasterRender() )
103  {
104  image = QImage( context.deviceOutputSize(), context.imageFormat() );
105  image.setDevicePixelRatio( static_cast<qreal>( context.devicePixelRatio() ) );
106  image.fill( 0 );
107  imagePainter = std::make_unique< QPainter >( &image );
108 
109  context.setPainterFlagsUsingContext( imagePainter.get() );
110  context.setPainter( imagePainter.get() );
111  }
112  renderer->render();
113 
114  if ( imagePainter )
115  {
116  imagePainter->end();
117  context.setPainter( prevPainter );
118 
119  context.painter()->setOpacity( mRendererOpacity[i] );
120  context.painter()->drawImage( 0, 0, image );
121  context.painter()->setOpacity( 1.0 );
122  }
123  context.painter()->setCompositionMode( QPainter::CompositionMode_SourceOver );
124  i++;
125  }
126 
127  if ( mPaintEffect )
128  {
129  mPaintEffect->end( context );
130  }
131 
132  context.painter()->restore();
133 
134  return !canceled;
135 }
136 
138 {
140  return false;
141 
142  if ( mForceRasterRender || !qgsDoubleNear( mLayerOpacity, 1.0 ) )
143  return true;
144 
145  for ( QPainter::CompositionMode mode : mRendererCompositionModes )
146  {
147  if ( mode != QPainter::CompositionMode_SourceOver )
148  return true;
149  }
150 
151  return false;
152 }
@ UseAdvancedEffects
Enable layer opacity and blending effects.
This class represents a coordinate reference system (CRS).
Class for doing transforms between two map coordinate systems.
void setBallparkTransformsAreAppropriate(bool appropriate)
Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system, which the transform will transform coordinates t...
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool handle180Crossover=false) const SIP_THROW(QgsCsException)
Transforms a rectangle from the source CRS to the destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition: qgsfeedback.h:45
~QgsGroupLayerRenderer() override
QgsGroupLayerRenderer(QgsGroupLayer *layer, QgsRenderContext &context)
Constructor for a QgsGroupLayerRenderer, for the specified layer.
bool forceRasterRender() const override
Returns true if the renderer must be rendered to a raster paint device (e.g.
QgsFeedback * feedback() const override
Access to feedback object of the layer renderer (may be nullptr)
bool render() override
Do the rendering (based on data stored in the class).
A map layer which consists of a set of child layers, where all component layers are rendered as a sin...
Definition: qgsgrouplayer.h:42
QgsPaintEffect * paintEffect() const
Returns the current paint effect for the group layer.
QList< QgsMapLayer * > childLayers() const
Returns the child layers contained by the group.
Base class for utility classes that encapsulate information necessary for rendering of map layers.
QgsRenderContext * renderContext()
Returns the render context associated with the renderer.
Base class for all map layer types.
Definition: qgsmaplayer.h:73
QPainter::CompositionMode blendMode() const
Returns the current blending mode for a layer.
virtual QgsPaintEffect * clone() const =0
Duplicates an effect by creating a deep copy of the effect.
bool enabled() const
Returns whether the effect is enabled.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
Contains information about the context of a rendering operation.
void setCoordinateTransform(const QgsCoordinateTransform &t)
Sets the current coordinate transform for the context.
bool useAdvancedEffects() const
Returns true if advanced effects such as blend modes such be used.
QPainter * painter()
Returns the destination QPainter for the render operation.
void setPainterFlagsUsingContext(QPainter *painter=nullptr) const
Sets relevant flags on a destination painter, using the flags and settings currently defined for the ...
QgsCoordinateTransformContext transformContext() const
Returns the context's coordinate transform context, which stores various information regarding which ...
float devicePixelRatio() const
Returns the device pixel ratio.
QgsRectangle mapExtent() const
Returns the original extent of the map being rendered.
void setExtent(const QgsRectangle &extent)
When rendering a map layer, calling this method sets the "clipping" extent for the layer (in the laye...
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
QSize deviceOutputSize() const
Returns the device output size of the render.
QgsCoordinateTransform coordinateTransform() const
Returns the current coordinate transform for the context.
QImage::Format imageFormat() const
Returns the QImage format which should be used for QImages created during rendering.
@ RasterLayer
Raster layer.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:2260
#define QgsDebugMsg(str)
Definition: qgslogger.h:38