25 #include <QtConcurrentRun>
44 #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
49 QPaintDevice *paintDevice = painter->device();
50 const QString errMsg = QStringLiteral(
"pre-set DPI not equal to painter's DPI (%1 vs %2)" )
51 .arg( paintDevice->logicalDpiX() )
54 "Job::startRender()", errMsg.toLatin1().data() );
67 , mRenderSynchronously( false )
75 Q_ASSERT( !mFutureWatcher.isRunning() );
79 void QgsMapRendererCustomPainterJob::startPrivate()
93 QgsDebugMsgLevel( QStringLiteral(
"Preparing list of layer jobs for rendering" ), 5 );
94 QElapsedTimer prepareTime;
99 mLabelingEngineV2.reset();
104 mLabelingEngineV2->setMapSettings(
mSettings );
108 mLayerJobs =
prepareJobs( mPainter, mLabelingEngineV2.get() );
109 mLabelJob =
prepareLabelingJob( mPainter, mLabelingEngineV2.get(), canUseLabelCache );
112 QgsDebugMsgLevel( QStringLiteral(
"Rendering prepared in (seconds): %1" ).arg( prepareTime.elapsed() / 1000.0 ), 4 );
114 if ( mRenderSynchronously )
125 connect( &mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererCustomPainterJob::futureFinished );
127 mFuture = QtConcurrent::run( staticRender,
this );
128 mFutureWatcher.setFuture( mFuture );
141 disconnect( &mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererCustomPainterJob::futureFinished );
147 mFutureWatcher.waitForFinished();
149 QgsDebugMsgLevel( QStringLiteral(
"QPAINER cancel waited %1 ms" ).arg( t.elapsed() / 1000.0 ), 5 );
160 QgsDebugMsg( QStringLiteral(
"QPAINTER not running!" ) );
164 mLabelJob.context.setRenderingStopped(
true );
165 for ( LayerRenderJob &job : mLayerJobs )
167 job.context()->setRenderingStopped(
true );
168 if ( job.renderer && job.renderer->feedback() )
169 job.renderer->feedback()->cancel();
178 disconnect( &mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererCustomPainterJob::futureFinished );
183 mFutureWatcher.waitForFinished();
185 QgsDebugMsgLevel( QStringLiteral(
"waitForFinished: %1 ms" ).arg( t.elapsed() / 1000.0 ), 4 );
197 return mLabelJob.cached;
202 if ( mLabelingEngineV2 )
203 return mLabelingEngineV2->takeResults();
212 connect( &mFutureWatcher, &QFutureWatcher<void>::finished, &loop, &QEventLoop::quit );
219 mRenderSynchronously =
true;
222 mRenderSynchronously =
false;
227 mRenderSynchronously =
true;
240 mRenderSynchronously =
false;
241 mPrepareOnly =
false;
245 void QgsMapRendererCustomPainterJob::futureFinished()
275 catch ( std::exception &e )
278 QgsDebugMsg(
"Caught unhandled std::exception: " + QString::fromLatin1( e.what() ) );
282 QgsDebugMsg( QStringLiteral(
"Caught unhandled unknown exception" ) );
286 void QgsMapRendererCustomPainterJob::doRender()
288 const bool hasSecondPass = ! mSecondPassLayerJobs.empty();
290 QElapsedTimer renderTime;
293 for ( LayerRenderJob &job : mLayerJobs )
295 if ( job.context()->renderingStopped() )
300 if ( ! hasSecondPass && job.context()->useAdvancedEffects() )
304 mPainter->setCompositionMode( job.blendMode );
309 QElapsedTimer layerTime;
315 job.imageInitialized =
true;
318 job.completed = job.renderer->render();
320 job.renderingTime += layerTime.elapsed();
323 if ( ! hasSecondPass && job.img )
326 mPainter->setOpacity( job.opacity );
327 mPainter->drawImage( 0, 0, *job.img );
328 mPainter->setOpacity( 1.0 );
339 if ( !mLabelJob.cached )
341 QElapsedTimer labelTime;
347 mLabelJob.img->fill( 0 );
348 painter.begin( mLabelJob.img );
349 mLabelJob.context.setPainter( &painter );
350 drawLabeling( mLabelJob.context, mLabelingEngineV2.get(), &painter );
355 drawLabeling( mLabelJob.context, mLabelingEngineV2.get(), mPainter );
358 mLabelJob.complete =
true;
359 mLabelJob.renderingTime = labelTime.elapsed();
360 mLabelJob.participatingLayers = _qgis_listRawToQPointer( mLabelingEngineV2->participatingLayers() );
364 if ( ! hasSecondPass )
366 if ( mLabelJob.img && mLabelJob.complete )
368 mPainter->setCompositionMode( QPainter::CompositionMode_SourceOver );
369 mPainter->setOpacity( 1.0 );
370 mPainter->drawImage( 0, 0, *mLabelJob.img );
375 for ( LayerRenderJob &job : mSecondPassLayerJobs )
377 if ( job.context()->renderingStopped() )
382 QElapsedTimer layerTime;
388 job.imageInitialized =
true;
391 job.completed = job.renderer->render();
393 job.renderingTime += layerTime.elapsed();
401 mPainter->setCompositionMode( QPainter::CompositionMode_SourceOver );
402 mPainter->setOpacity( 1.0 );
403 mPainter->drawImage( 0, 0, finalImage );
406 QgsDebugMsgLevel( QStringLiteral(
"Rendering completed in (seconds): %1" ).arg( renderTime.elapsed() / 1000.0 ), 2 );
@ LosslessImageRendering
Render images losslessly whenever possible, instead of the default lossy jpeg rendering used for some...
@ Antialiasing
Enable anti-aliasing for map rendering.
@ DrawLabeling
Enable drawing of labels on top of the map.
@ HighQualityImageTransforms
Enable high quality image transformations, which results in better appearance of scaled or rotated ra...
Default QgsLabelingEngine implementation, which completes the whole labeling operation (including lab...
Defines a QGIS exception class.
Class that stores computed placement from labeling engine.
Abstract base class for map renderer jobs which use custom painters.
void preparePainter(QPainter *painter, const QColor &backgroundColor=Qt::transparent)
Prepares the given painter ready for a map render.
QgsMapRendererAbstractCustomPainterJob(const QgsMapSettings &settings)
Constructor for QgsMapRendererAbstractCustomPainterJob, using the given map settings.
Job implementation that renders everything sequentially using a custom painter.
QgsMapRendererCustomPainterJob(const QgsMapSettings &settings, QPainter *painter)
~QgsMapRendererCustomPainterJob() override
void renderSynchronously()
Render the map synchronously in this thread.
void cancel() override
Stop the rendering job - does not return until the job has terminated.
void waitForFinishedWithEventLoop(QEventLoop::ProcessEventsFlags flags=QEventLoop::AllEvents)
Wait for the job to be finished - and keep the thread's event loop running while waiting.
void renderPrepared()
Render a pre-prepared job.
QgsLabelingResults * takeLabelingResults() override
Gets pointer to internal labeling engine (in order to get access to the results).
void cancelWithoutBlocking() override
Triggers cancellation of the rendering job without blocking.
void prepare()
Prepares the job for rendering synchronously in a background thread.
void waitForFinished() override
Block until the job has finished.
bool isActive() const override
Tell whether the rendering job is currently running in background.
bool usedCachedLabels() const override
Returns true if the render job was able to use a cached labeling solution.
Abstract base class for map rendering implementations.
void logRenderingTime(const std::vector< LayerRenderJob > &jobs, const std::vector< LayerRenderJob > &secondPassJobs, const LabelRenderJob &labelJob)
static void composeSecondPass(std::vector< LayerRenderJob > &secondPassJobs, LabelRenderJob &labelJob)
Compose second pass images into first pass images.
static QImage composeImage(const QgsMapSettings &settings, const std::vector< LayerRenderJob > &jobs, const LabelRenderJob &labelJob, const QgsMapRendererCache *cache=nullptr)
void cleanupSecondPassJobs(std::vector< LayerRenderJob > &jobs)
static Q_DECL_DEPRECATED void drawLabeling(const QgsMapSettings &settings, QgsRenderContext &renderContext, QgsLabelingEngine *labelingEngine2, QPainter *painter)
void layerRendered(const QString &layerId)
Emitted when a layer has completed rendering.
std::vector< LayerRenderJob > prepareJobs(QPainter *painter, QgsLabelingEngine *labelingEngine2, bool deferredPainterSet=false)
Creates a list of layer rendering jobs and prepares them for later render.
void renderingLayersFinished()
Emitted when the layers are rendered.
void cleanupJobs(std::vector< LayerRenderJob > &jobs)
QElapsedTimer mRenderingStart
void finished()
emitted when asynchronous rendering is finished (or canceled).
void start()
Start the rendering job and immediately return.
void layerRenderingStarted(const QString &layerId)
Emitted just before rendering starts for a particular layer.
std::vector< LayerRenderJob > prepareSecondPassJobs(std::vector< LayerRenderJob > &firstPassJobs, LabelRenderJob &labelJob)
Prepares jobs for a second pass, if selective masks exist (from labels or symbol layers).
LabelRenderJob prepareLabelingJob(QPainter *painter, QgsLabelingEngine *labelingEngine2, bool canUseLabelCache=true)
Prepares a labeling job.
void cleanupLabelJob(LabelRenderJob &job)
Handles clean up tasks for a label job, including deletion of images and storing cached label results...
bool prepareLabelCache() const
Prepares the cache for storing the result of labeling.
The QgsMapSettings class contains configuration for rendering of the map.
QSize deviceOutputSize() const
Returns the device output size of the map render.
QColor backgroundColor() const
Returns the background color of the map.
double outputDpi() const
Returns the DPI (dots per inch) used for conversion between real world units (e.g.
bool testFlag(Qgis::MapSettingsFlag flag) const
Check whether a particular flag is enabled.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
#define QgsDebugMsgLevel(str, level)