Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsmaprendererjob.h
3 : : --------------------------------------
4 : : Date : December 2013
5 : : Copyright : (C) 2013 by Martin Dobias
6 : : Email : wonder dot sk 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 : : #ifndef QGSMAPRENDERERJOB_H
17 : : #define QGSMAPRENDERERJOB_H
18 : :
19 : : #include "qgis_core.h"
20 : : #include "qgis_sip.h"
21 : : #include <QFutureWatcher>
22 : : #include <QImage>
23 : : #include <QPainter>
24 : : #include <QObject>
25 : : #include <QTime>
26 : : #include <QElapsedTimer>
27 : :
28 : : #include "qgsrendercontext.h"
29 : :
30 : : #include "qgsmapsettings.h"
31 : : #include "qgsmaskidprovider.h"
32 : :
33 : :
34 : : class QgsLabelingEngine;
35 : : class QgsLabelingResults;
36 : : class QgsMapLayerRenderer;
37 : : class QgsMapRendererCache;
38 : : class QgsFeatureFilterProvider;
39 : :
40 : : #ifndef SIP_RUN
41 : : /// @cond PRIVATE
42 : :
43 : : /**
44 : : * \ingroup core
45 : : * \brief Structure keeping low-level rendering job information.
46 : : */
47 : 0 : struct LayerRenderJob
48 : : {
49 : : QgsRenderContext context;
50 : :
51 : : /**
52 : : * Pointer to destination image.
53 : : *
54 : : * May be NULLPTR if it is not necessary to draw to separate image (e.g. sequential rendering).
55 : : */
56 : : QImage *img;
57 : : //! TRUE when img has been initialized (filled with transparent pixels)
58 : 0 : bool imageInitialized = false;
59 : :
60 : : bool imageCanBeComposed() const;
61 : :
62 : : QgsMapLayerRenderer *renderer; // must be deleted
63 : :
64 : : QPainter::CompositionMode blendMode;
65 : : double opacity;
66 : : //! If TRUE, img already contains cached image from previous rendering
67 : : bool cached;
68 : : QgsWeakMapLayerPointer layer;
69 : :
70 : : /**
71 : : * TRUE if the render job was successfully completed in its entirety (i.e. it was
72 : : * not canceled or aborted early).
73 : : *
74 : : * \since QGIS 3.18
75 : : */
76 : 0 : bool completed = false;
77 : :
78 : : int renderingTime; //!< Time it took to render the layer in ms (it is -1 if not rendered or still rendering)
79 : :
80 : : /**
81 : : * Estimated time for the layer to render, in ms.
82 : : *
83 : : * This can be used to specifies hints at the expected render times for the layer, so that
84 : : * the corresponding layer renderer can apply heuristics and determine appropriate update
85 : : * intervals during the render operation.
86 : : *
87 : : * \since QGIS 3.18
88 : : */
89 : 0 : int estimatedRenderingTime = 0;
90 : :
91 : : QStringList errors; //!< Rendering errors
92 : :
93 : : /**
94 : : * Identifies the associated layer by ID.
95 : : *
96 : : * \warning This should NEVER be used to retrieve map layers during a render job, and instead
97 : : * is intended for use as a string identifier only.
98 : : *
99 : : * \since QGIS 3.10
100 : : */
101 : : QString layerId;
102 : :
103 : : /**
104 : : * Selective masking handling.
105 : : *
106 : : * A layer can be involved in selective masking in two ways:
107 : : *
108 : : * - One of its symbol layer masks a symbol layer of another layer.
109 : : * In this case we need to compute a mask image during the regular
110 : : * rendering pass that will be stored here;
111 : : * - Some of its symbol layers are masked by a symbol layer of another layer (or by a label mask)
112 : : * In this case we need to render the layer once again in a second pass, but with some symbol
113 : : * layers disabled.
114 : : * This second rendering will be composed with mask images that have been computed in the first
115 : : * pass by another job. We then need to know which first pass image and which masks correspond.
116 : : */
117 : :
118 : : //! Mask image, needed during the first pass if a mask is defined
119 : 0 : QImage *maskImage = nullptr;
120 : :
121 : : /**
122 : : * Pointer to the first pass job, needed during the second pass
123 : : * to access first pass painter and image.
124 : : */
125 : 0 : LayerRenderJob *firstPassJob = nullptr;
126 : :
127 : : /**
128 : : * Pointer to first pass jobs that carry a mask image, needed during the second pass.
129 : : * This can be either a LayerRenderJob, in which case the second element of the QPair is ignored.
130 : : * Or this can be a LabelRenderJob if the first element is nullptr.
131 : : * In this latter case, the second element of the QPair gives the label mask id.
132 : : */
133 : : QList<QPair<LayerRenderJob *, int>> maskJobs;
134 : : };
135 : :
136 : : typedef QList<LayerRenderJob> LayerRenderJobs;
137 : :
138 : : /**
139 : : * \ingroup core
140 : : * \brief Structure keeping low-level label rendering job information.
141 : : */
142 : 0 : struct LabelRenderJob
143 : : {
144 : : QgsRenderContext context;
145 : :
146 : : /**
147 : : * May be NULLPTR if it is not necessary to draw to separate image (e.g. using composition modes which prevent "flattening" the layer).
148 : : * Note that if complete is FALSE then img will be uninitialized and contain random data!.
149 : : */
150 : 0 : QImage *img = nullptr;
151 : :
152 : : /**
153 : : * Mask images
154 : : *
155 : : * There is only one label job, with labels coming from different layers or rules (for rule-based labeling).
156 : : * So we may have different labels with different label masks. We then need one different mask image for each configuration of label masks.
157 : : * Labels that share the same kind of label masks, i.e. having the same set of symbol layers that are to be masked, should share the same mask image.
158 : : * Labels that have different label masks, i.e. having different set of symbol layers that are to be masked, should have different mask images.
159 : : * The index in the vector corresponds to the mask identifier.
160 : : * \see maskIdProvider
161 : : */
162 : : QVector<QImage *> maskImages;
163 : :
164 : : /**
165 : : * A mask id provider that is used to compute a mask image identifier for each label layer.
166 : : * \see maskImages
167 : : */
168 : : QgsMaskIdProvider maskIdProvider;
169 : :
170 : : //! If TRUE, img already contains cached image from previous rendering
171 : 0 : bool cached = false;
172 : : //! Will be TRUE if labeling is eligible for caching
173 : 0 : bool canUseCache = false;
174 : : //! If TRUE then label render is complete
175 : 0 : bool complete = false;
176 : : //! Time it took to render the labels in ms (it is -1 if not rendered or still rendering)
177 : 0 : int renderingTime = -1;
178 : : //! List of layers which participated in the labeling solution
179 : : QList< QPointer< QgsMapLayer > > participatingLayers;
180 : : };
181 : :
182 : : ///@endcond PRIVATE
183 : : #endif
184 : :
185 : : /**
186 : : * \ingroup core
187 : : * \brief Abstract base class for map rendering implementations.
188 : : *
189 : : * The API is designed in a way that rendering is done asynchronously, therefore
190 : : * the caller is not blocked while the rendering is in progress. Non-blocking
191 : : * operation is quite important because the rendering can take considerable
192 : : * amount of time.
193 : : *
194 : : * Common use case:
195 : : *
196 : : * 1. Prepare QgsMapSettings with rendering configuration (extent, layer, map size, ...)
197 : : * 2. Create QgsMapRendererJob subclass with QgsMapSettings instance
198 : : * 3. Connect to job's finished() signal
199 : : * 4. Call start(). Map rendering will start in background, the function immediately returns
200 : : * 5. At some point, slot connected to finished() signal is called, map rendering is done
201 : : *
202 : : * It is possible to cancel the rendering job while it is active by calling cancel() function.
203 : : *
204 : : * The following subclasses are available:
205 : : *
206 : : * - QgsMapRendererSequentialJob - renders map in one background thread to an image
207 : : * - QgsMapRendererParallelJob - renders map in multiple background threads to an image
208 : : * - QgsMapRendererCustomPainterJob - renders map with given QPainter in one background thread
209 : : *
210 : : * \since QGIS 2.4
211 : : */
212 : 0 : class CORE_EXPORT QgsMapRendererJob : public QObject
213 : : {
214 : 0 : Q_OBJECT
215 : : public:
216 : :
217 : : QgsMapRendererJob( const QgsMapSettings &settings );
218 : :
219 : : /**
220 : : * Start the rendering job and immediately return.
221 : : * Does nothing if the rendering is already in progress.
222 : : */
223 : : virtual void start() = 0;
224 : :
225 : : /**
226 : : * Stop the rendering job - does not return until the job has terminated.
227 : : * Does nothing if the rendering is not active.
228 : : */
229 : : virtual void cancel() = 0;
230 : :
231 : : /**
232 : : * Triggers cancellation of the rendering job without blocking. The render job will continue
233 : : * to operate until it is able to cancel, at which stage the finished() signal will be emitted.
234 : : * Does nothing if the rendering is not active.
235 : : */
236 : : virtual void cancelWithoutBlocking() = 0;
237 : :
238 : : //! Block until the job has finished.
239 : : virtual void waitForFinished() = 0;
240 : :
241 : : //! Tell whether the rendering job is currently running in background.
242 : : virtual bool isActive() const = 0;
243 : :
244 : : /**
245 : : * Returns TRUE if the render job was able to use a cached labeling solution.
246 : : * If so, any previously stored labeling results (see takeLabelingResults())
247 : : * should be retained.
248 : : * \see takeLabelingResults()
249 : : * \since QGIS 3.0
250 : : */
251 : : virtual bool usedCachedLabels() const = 0;
252 : :
253 : : /**
254 : : * Gets pointer to internal labeling engine (in order to get access to the results).
255 : : * This should not be used if cached labeling was redrawn - see usedCachedLabels().
256 : : * \see usedCachedLabels()
257 : : */
258 : : virtual QgsLabelingResults *takeLabelingResults() = 0 SIP_TRANSFER;
259 : :
260 : : /**
261 : : * Set the feature filter provider used by the QgsRenderContext of
262 : : * each LayerRenderJob.
263 : : * Ownership is not transferred and the provider must not be deleted
264 : : * before the render job.
265 : : * \since QGIS 3.0
266 : : */
267 : : void setFeatureFilterProvider( const QgsFeatureFilterProvider *f ) { mFeatureFilterProvider = f; }
268 : :
269 : : /**
270 : : * Returns the feature filter provider used by the QgsRenderContext of
271 : : * each LayerRenderJob.
272 : : * \since QGIS 3.0
273 : : */
274 : : const QgsFeatureFilterProvider *featureFilterProvider() const { return mFeatureFilterProvider; }
275 : :
276 : 0 : struct Error
277 : : {
278 : 0 : Error( const QString &lid, const QString &msg )
279 : 0 : : layerID( lid )
280 : 0 : , message( msg )
281 : 0 : {}
282 : :
283 : : QString layerID;
284 : : QString message;
285 : : };
286 : :
287 : : typedef QList<QgsMapRendererJob::Error> Errors;
288 : :
289 : : //! List of errors that happened during the rendering job - available when the rendering has been finished
290 : : Errors errors() const;
291 : :
292 : :
293 : : /**
294 : : * Assign a cache to be used for reading and storing rendered images of individual layers.
295 : : * Does not take ownership of the object.
296 : : */
297 : : void setCache( QgsMapRendererCache *cache );
298 : :
299 : : /**
300 : : * Returns the total time it took to finish the job (in milliseconds).
301 : : * \see perLayerRenderingTime()
302 : : */
303 : : int renderingTime() const { return mRenderingTime; }
304 : :
305 : : /**
306 : : * Returns the render time (in ms) per layer.
307 : : * \note Not available in Python bindings.
308 : : * \since QGIS 3.0
309 : : */
310 : : QHash< QgsMapLayer *, int > perLayerRenderingTime() const SIP_SKIP;
311 : :
312 : : /**
313 : : * Sets approximate render times (in ms) for map layers.
314 : : *
315 : : * This can be used to specifies hints at the expected render times for layers, so that
316 : : * the individual layer renderers can apply heuristics and determine appropriate update
317 : : * intervals during the render operation.
318 : : *
319 : : * The keys for \a hints must be set to the corresponding layer IDs.
320 : : *
321 : : * \note Not available in Python bindings.
322 : : * \since QGIS 3.18
323 : : */
324 : : void setLayerRenderingTimeHints( const QHash< QString, int > &hints ) SIP_SKIP;
325 : :
326 : : /**
327 : : * Returns map settings with which this job was started.
328 : : * \returns A QgsMapSettings instance with render settings
329 : : * \since QGIS 2.8
330 : : */
331 : : const QgsMapSettings &mapSettings() const;
332 : :
333 : : /**
334 : : * QgsMapRendererCache ID string for cached label image.
335 : : * \note not available in Python bindings
336 : : */
337 : : static const QString LABEL_CACHE_ID SIP_SKIP;
338 : :
339 : : /**
340 : : * QgsMapRendererCache ID string for cached label image during preview compositions only.
341 : : * \note not available in Python bindings
342 : : * \since QGIS 3.18
343 : : */
344 : : static const QString LABEL_PREVIEW_CACHE_ID SIP_SKIP;
345 : :
346 : : signals:
347 : :
348 : : /**
349 : : * Emitted when the layers are rendered.
350 : : * Rendering labels is not yet done. If the fully rendered layer including labels is required use
351 : : * finished() instead.
352 : : *
353 : : * \since QGIS 3.0
354 : : */
355 : : void renderingLayersFinished();
356 : :
357 : : //! emitted when asynchronous rendering is finished (or canceled).
358 : : void finished();
359 : :
360 : : protected:
361 : :
362 : : QgsMapSettings mSettings;
363 : : QElapsedTimer mRenderingStart;
364 : : Errors mErrors;
365 : :
366 : : QgsMapRendererCache *mCache = nullptr;
367 : :
368 : : int mRenderingTime = 0;
369 : :
370 : : //! Render time (in ms) per layer, by layer ID
371 : : QHash< QgsWeakMapLayerPointer, int > mPerLayerRenderingTime;
372 : :
373 : : /**
374 : : * Approximate expected layer rendering time per layer, by layer ID
375 : : *
376 : : * \since QGIS 3.18
377 : : */
378 : : QHash< QString, int > mLayerRenderingTimeHints;
379 : :
380 : : /**
381 : : * TRUE if layer rendering time should be recorded.
382 : : */
383 : : bool mRecordRenderingTime = true;
384 : :
385 : : /**
386 : : * Prepares the cache for storing the result of labeling. Returns FALSE if
387 : : * the render cannot use cached labels and should not cache the result.
388 : : * \note not available in Python bindings
389 : : */
390 : : bool prepareLabelCache() const SIP_SKIP;
391 : :
392 : : /**
393 : : * Creates a list of layer rendering jobs and prepares them for later render.
394 : : *
395 : : * The \a painter argument specifies the destination painter. If not set, the jobs will
396 : : * be rendered to temporary images. Alternatively, if the \a deferredPainterSet flag is TRUE,
397 : : * then a \a painter value of NULLPTR skips this default temporary image creation. In this case,
398 : : * it is the caller's responsibility to correctly set a painter for all rendered jobs prior
399 : : * to rendering them.
400 : : *
401 : : * \note not available in Python bindings
402 : : */
403 : : LayerRenderJobs prepareJobs( QPainter *painter, QgsLabelingEngine *labelingEngine2, bool deferredPainterSet = false ) SIP_SKIP;
404 : :
405 : : /**
406 : : * Prepares a labeling job.
407 : : * \note not available in Python bindings
408 : : * \since QGIS 3.0
409 : : */
410 : : LabelRenderJob prepareLabelingJob( QPainter *painter, QgsLabelingEngine *labelingEngine2, bool canUseLabelCache = true ) SIP_SKIP;
411 : :
412 : : /**
413 : : * Prepares jobs for a second pass, if selective masks exist (from labels or symbol layers).
414 : : * Must be called after prepareJobs and prepareLabelingJob.
415 : : * It returns a list of new jobs for a second pass and also modifies labelJob and firstPassJobs if needed
416 : : * (image and mask image allocation if needed)
417 : : * \note not available in Python bindings
418 : : * \since QGIS 3.12
419 : : */
420 : : LayerRenderJobs prepareSecondPassJobs( LayerRenderJobs &firstPassJobs, LabelRenderJob &labelJob ) SIP_SKIP;
421 : :
422 : : //! \note not available in Python bindings
423 : : static QImage composeImage( const QgsMapSettings &settings,
424 : : const LayerRenderJobs &jobs,
425 : : const LabelRenderJob &labelJob,
426 : : const QgsMapRendererCache *cache = nullptr ) SIP_SKIP;
427 : :
428 : : //! \note not available in Python bindings
429 : : static QImage layerImageToBeComposed( const QgsMapSettings &settings, const LayerRenderJob &job, const QgsMapRendererCache *cache ) SIP_SKIP;
430 : :
431 : : /**
432 : : * Compose second pass images into first pass images.
433 : : * First pass jobs pointed to by the second pass jobs must still exist.
434 : : * \note not available in Python bindings
435 : : * \since QGIS 3.12
436 : : */
437 : : static void composeSecondPass( LayerRenderJobs &secondPassJobs, LabelRenderJob &labelJob ) SIP_SKIP;
438 : :
439 : : //! \note not available in Python bindings
440 : : void logRenderingTime( const LayerRenderJobs &jobs, const LayerRenderJobs &secondPassJobs, const LabelRenderJob &labelJob ) SIP_SKIP;
441 : :
442 : : //! \note not available in Python bindings
443 : : void cleanupJobs( LayerRenderJobs &jobs ) SIP_SKIP;
444 : :
445 : : //! \note not available in Python bindings
446 : : void cleanupSecondPassJobs( LayerRenderJobs &jobs ) SIP_SKIP;
447 : :
448 : : /**
449 : : * Handles clean up tasks for a label job, including deletion of images and storing cached
450 : : * label results.
451 : : * \note not available in Python bindings
452 : : * \since QGIS 3.0
453 : : */
454 : : void cleanupLabelJob( LabelRenderJob &job ) SIP_SKIP;
455 : :
456 : : /**
457 : : * \note not available in Python bindings
458 : : * \deprecated Will be removed in QGIS 4.0
459 : : */
460 : : Q_DECL_DEPRECATED static void drawLabeling( const QgsMapSettings &settings, QgsRenderContext &renderContext, QgsLabelingEngine *labelingEngine2, QPainter *painter ) SIP_SKIP;
461 : :
462 : : //! \note not available in Python bindings
463 : : static void drawLabeling( QgsRenderContext &renderContext, QgsLabelingEngine *labelingEngine2, QPainter *painter ) SIP_SKIP;
464 : :
465 : : private:
466 : :
467 : : /**
468 : : * Convenience function to project an extent into the layer source
469 : : * CRS, but also split it into two extents if it crosses
470 : : * the +/- 180 degree line. Modifies the given extent to be in the
471 : : * source CRS coordinates, and if it was split also sets the contents
472 : : * of the r2 parameter.
473 : : *
474 : : * If FALSE is returned then the extent could not be accurately
475 : : * transformed to the layer's CRS, and a "full globe" extent
476 : : * was used instead.
477 : : */
478 : : static bool reprojectToLayerExtent( const QgsMapLayer *ml, const QgsCoordinateTransform &ct, QgsRectangle &extent, QgsRectangle &r2 );
479 : :
480 : : const QgsFeatureFilterProvider *mFeatureFilterProvider = nullptr;
481 : :
482 : : //! Convenient method to allocate a new image and stack an error if not enough memory is available
483 : : QImage *allocateImage( QString layerId );
484 : :
485 : : //! Convenient method to allocate a new image and a new QPainter on this image
486 : : QPainter *allocateImageAndPainter( QString layerId, QImage *&image );
487 : : };
488 : :
489 : :
490 : : /**
491 : : * \ingroup core
492 : : * \brief Intermediate base class adding functionality that allows client to query the rendered image.
493 : : *
494 : : * The image can be queried even while the rendering is still in progress to get intermediate result
495 : : *
496 : : * \since QGIS 2.4
497 : : */
498 : 0 : class CORE_EXPORT QgsMapRendererQImageJob : public QgsMapRendererJob
499 : : {
500 : : Q_OBJECT
501 : :
502 : : public:
503 : : QgsMapRendererQImageJob( const QgsMapSettings &settings );
504 : :
505 : : //! Gets a preview/resulting image
506 : : virtual QImage renderedImage() = 0;
507 : :
508 : : };
509 : :
510 : :
511 : : #endif // QGSMAPRENDERERJOB_H
|