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