1 /***************************************************************************
2   qgsmaprendererstagedrenderjob.cpp
3   --------------------------------------
4   Date                 : August 2019
5   Copyright            : (C) 2019 by Nyall Dawson
6   Email                : nyall dot dawson at gmail dot com
7  ***************************************************************************
8  *                                                                         *
9  *   This program is free software; you can redistribute it and/or modify  *
10  *   it under the terms of the GNU General Public License as published by  *
11  *   the Free Software Foundation; either version 2 of the License, or     *
12  *   (at your option) any later version.                                   *
13  *                                                                         *
14  ***************************************************************************/
15 
16 #include "qgsmaprendererstagedrenderjob.h"
17 
18 #include "qgsfeedback.h"
19 #include "qgslabelingengine.h"
20 #include "qgslogger.h"
21 #include "qgsproject.h"
22 #include "qgsmaplayerrenderer.h"
23 #include "qgsmaplayerlistutils.h"
24 #include "qgsrendereditemresults.h"
25 
QgsMapRendererStagedRenderJob(const QgsMapSettings & settings,Flags flags)26 QgsMapRendererStagedRenderJob::QgsMapRendererStagedRenderJob( const QgsMapSettings &settings, Flags flags )
27   : QgsMapRendererAbstractCustomPainterJob( settings )
28   , mFlags( flags )
29 {
30 }
31 
~QgsMapRendererStagedRenderJob()32 QgsMapRendererStagedRenderJob::~QgsMapRendererStagedRenderJob()
33 {
34   // final cleanup
35   cleanupJobs( mLayerJobs );
36   cleanupLabelJob( mLabelJob );
37 }
38 
39 
startPrivate()40 void QgsMapRendererStagedRenderJob::startPrivate()
41 {
42   mRenderingStart.start();
43   mErrors.clear();
44 
45   QgsDebugMsgLevel( QStringLiteral( "Preparing list of layer jobs for rendering" ), 5 );
46   QElapsedTimer prepareTime;
47   prepareTime.start();
48 
49   mLabelingEngineV2.reset();
50 
51   if ( mSettings.testFlag( Qgis::MapSettingsFlag::DrawLabeling ) )
52   {
53     if ( mFlags & RenderLabelsByMapLayer )
54       mLabelingEngineV2.reset( new QgsStagedRenderLabelingEngine() );
55     else
56       mLabelingEngineV2.reset( new QgsDefaultLabelingEngine() );
57     mLabelingEngineV2->setMapSettings( mSettings );
58   }
59 
60   mLayerJobs = prepareJobs( nullptr, mLabelingEngineV2.get(), true );
61   mLabelJob = prepareLabelingJob( nullptr, mLabelingEngineV2.get(), false );
62 
63   mJobIt = mLayerJobs.begin();
64 }
65 
cancel()66 void QgsMapRendererStagedRenderJob::cancel()
67 {
68 }
69 
cancelWithoutBlocking()70 void QgsMapRendererStagedRenderJob::cancelWithoutBlocking()
71 {
72 }
73 
waitForFinished()74 void QgsMapRendererStagedRenderJob::waitForFinished()
75 {
76 }
77 
isActive() const78 bool QgsMapRendererStagedRenderJob::isActive() const
79 {
80   return true;
81 }
82 
usedCachedLabels() const83 bool QgsMapRendererStagedRenderJob::usedCachedLabels() const
84 {
85   return false;
86 }
87 
takeLabelingResults()88 QgsLabelingResults *QgsMapRendererStagedRenderJob::takeLabelingResults()
89 {
90   if ( mLabelingEngineV2 )
91     return mLabelingEngineV2->takeResults();
92   else
93     return nullptr;
94 }
95 
renderCurrentPart(QPainter * painter)96 bool QgsMapRendererStagedRenderJob::renderCurrentPart( QPainter *painter )
97 {
98   if ( isFinished() )
99     return false;
100 
101   preparePainter( painter );
102 
103   if ( mJobIt != mLayerJobs.end() )
104   {
105     LayerRenderJob &job = *mJobIt;
106     job.renderer->renderContext()->setPainter( painter );
107 
108     if ( job.context()->useAdvancedEffects() )
109     {
110       // Set the QPainter composition mode so that this layer is rendered using
111       // the desired blending mode
112       painter->setCompositionMode( job.blendMode );
113     }
114 
115     if ( job.img )
116     {
117       job.img->fill( 0 );
118       job.imageInitialized = true;
119     }
120 
121     job.completed = job.renderer->render();
122 
123     if ( job.img )
124     {
125       // If we flattened this layer for alternate blend modes, composite it now
126       painter->setOpacity( job.opacity );
127       painter->drawImage( 0, 0, *job.img );
128       painter->setOpacity( 1.0 );
129     }
130     job.context()->setPainter( nullptr );
131   }
132   else
133   {
134     if ( !mLabelingEngineV2 )
135       return false;
136 
137     if ( mFlags & RenderLabelsByMapLayer )
138     {
139       if ( !mPreparedStagedLabelJob || mLabelLayerIt == mLabelingLayers.end() )
140         return false;
141 
142       mLabelJob.context.setPainter( painter );
143 
144       // Reset the composition mode before rendering the labels
145       painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
146 
147       // render just the current layer's labels
148       static_cast< QgsStagedRenderLabelingEngine * >( mLabelingEngineV2.get() )->renderLabelsForLayer( mLabelJob.context, *mLabelLayerIt );
149 
150       mLabelJob.context.setPainter( nullptr );
151     }
152     else
153     {
154       mLabelJob.context.setPainter( painter );
155       drawLabeling( mLabelJob.context, mLabelingEngineV2.get(), painter );
156       mLabelJob.complete = true;
157       mLabelJob.participatingLayers = _qgis_listRawToQPointer( mLabelingEngineV2->participatingLayers() );
158       mLabelJob.context.setPainter( nullptr );
159     }
160   }
161   return true;
162 }
163 
nextPart()164 bool QgsMapRendererStagedRenderJob::nextPart()
165 {
166   if ( isFinished() )
167     return false;
168 
169   if ( mJobIt != mLayerJobs.end() )
170   {
171     ++mJobIt;
172     if ( mJobIt != mLayerJobs.end() )
173       return true;
174   }
175 
176   if ( mLabelingEngineV2 )
177   {
178     if ( mFlags & RenderLabelsByMapLayer )
179     {
180       if ( !mPreparedStagedLabelJob )
181       {
182         mLabelingEngineV2->run( mLabelJob.context );
183         mPreparedStagedLabelJob = true;
184         mLabelingLayers = mLabelingEngineV2->participatingLayerIds();
185         mLabelLayerIt = mLabelingLayers.begin();
186         if ( mLabelLayerIt == mLabelingLayers.end() )
187         {
188           // no label layers to render!
189           static_cast< QgsStagedRenderLabelingEngine * >( mLabelingEngineV2.get() )->finalize();
190           return false;
191         }
192         return true;
193       }
194       else
195       {
196         if ( mLabelLayerIt != mLabelingLayers.end() )
197         {
198           ++mLabelLayerIt;
199           if ( mLabelLayerIt != mLabelingLayers.end() )
200             return true;
201         }
202       }
203       return false;
204     }
205     else
206     {
207       if ( mNextIsLabel )
208       {
209         mExportedLabels = true;
210       }
211       else if ( !mExportedLabels )
212       {
213         mNextIsLabel = true;
214         return true;
215       }
216     }
217   }
218   return false;
219 }
220 
isFinished() const221 bool QgsMapRendererStagedRenderJob::isFinished() const
222 {
223   return currentStage() == Finished;
224 }
225 
currentLayerId() const226 QString QgsMapRendererStagedRenderJob::currentLayerId() const
227 {
228   if ( mJobIt != mLayerJobs.end() )
229   {
230     const LayerRenderJob &job = *mJobIt;
231     return job.layerId;
232   }
233   else if ( mFlags & RenderLabelsByMapLayer && mPreparedStagedLabelJob )
234   {
235     if ( mLabelLayerIt != mLabelingLayers.end() )
236       return *mLabelLayerIt;
237   }
238   return QString();
239 }
240 
currentLayerOpacity() const241 double QgsMapRendererStagedRenderJob::currentLayerOpacity() const
242 {
243   if ( mJobIt != mLayerJobs.end() )
244   {
245     const LayerRenderJob &job = *mJobIt;
246     return job.opacity;
247   }
248   return 1.0;
249 }
250 
currentLayerCompositionMode() const251 QPainter::CompositionMode QgsMapRendererStagedRenderJob::currentLayerCompositionMode() const
252 {
253   if ( mJobIt != mLayerJobs.end() )
254   {
255     const LayerRenderJob &job = *mJobIt;
256     return job.blendMode;
257   }
258   return QPainter::CompositionMode_SourceOver;
259 }
260 
currentStage() const261 QgsMapRendererStagedRenderJob::RenderStage QgsMapRendererStagedRenderJob::currentStage() const
262 {
263   if ( mJobIt != mLayerJobs.end() )
264     return Symbology;
265   else if ( mLabelingEngineV2 && mFlags & RenderLabelsByMapLayer )
266   {
267     if ( !mPreparedStagedLabelJob )
268       return Labels;
269     if ( mLabelLayerIt != mLabelingLayers.end() )
270       return Labels;
271   }
272   else if ( mNextIsLabel && !mExportedLabels )
273     return Labels;
274 
275   return Finished;
276 }
277