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