28#include <QtConcurrentRun>
30#include "moc_qgsmaprenderercustompainterjob.cpp"
32using namespace Qt::StringLiterals;
45 painter->fillRect( 0, 0,
mSettings.deviceOutputSize().width(),
mSettings.deviceOutputSize().height(), backgroundColor );
52 QPaintDevice *paintDevice = painter->device();
53 const QString errMsg = u
"pre-set DPI not equal to painter's DPI (%1 vs %2)"_s.arg( paintDevice->logicalDpiX() ).arg(
mSettings.outputDpi() );
54 Q_ASSERT_X(
qgsDoubleNear( paintDevice->logicalDpiX(),
mSettings.outputDpi(), 1.0 ),
"Job::startRender()", errMsg.toLatin1().data() );
73 Q_ASSERT( !mFutureWatcher.isRunning() );
77void QgsMapRendererCustomPainterJob::startPrivate()
92 QElapsedTimer prepareTime;
97 mLabelingEngineV2.reset();
101 mLabelingEngineV2 = std::make_unique<QgsDefaultLabelingEngine>(
mSettings );
105 mLayerJobs =
prepareJobs( mPainter, mLabelingEngineV2.get() );
106 mLabelJob =
prepareLabelingJob( mPainter, mLabelingEngineV2.get(), canUseLabelCache );
109 QgsDebugMsgLevel( u
"Rendering prepared in (seconds): %1"_s.arg( prepareTime.elapsed() / 1000.0 ), 4 );
111 if ( mRenderSynchronously )
122 connect( &mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererCustomPainterJob::futureFinished );
124 mFuture = QtConcurrent::run( staticRender,
this );
125 mFutureWatcher.setFuture( mFuture );
138 disconnect( &mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererCustomPainterJob::futureFinished );
144 mFutureWatcher.waitForFinished();
146 QgsDebugMsgLevel( u
"QPAINER cancel waited %1 ms"_s.arg( t.elapsed() / 1000.0 ), 5 );
161 mLabelJob.context.setRenderingStopped(
true );
162 for ( LayerRenderJob &job : mLayerJobs )
164 job.context()->setRenderingStopped(
true );
165 if ( job.renderer && job.renderer->feedback() )
166 job.renderer->feedback()->cancel();
175 disconnect( &mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererCustomPainterJob::futureFinished );
180 mFutureWatcher.waitForFinished();
182 QgsDebugMsgLevel( u
"waitForFinished: %1 ms"_s.arg( t.elapsed() / 1000.0 ), 4 );
194 return mLabelJob.cached;
199 if ( mLabelingEngineV2 )
200 return mLabelingEngineV2->takeResults();
209 connect( &mFutureWatcher, &QFutureWatcher<void>::finished, &loop, &QEventLoop::quit );
216 mRenderSynchronously =
true;
219 mRenderSynchronously =
false;
224 mRenderSynchronously =
true;
237 mRenderSynchronously =
false;
238 mPrepareOnly =
false;
242void QgsMapRendererCustomPainterJob::futureFinished()
267 catch ( QgsException &e )
272 catch ( std::exception &e )
275 QgsDebugError(
"Caught unhandled std::exception: " + QString::fromLatin1( e.what() ) );
283void QgsMapRendererCustomPainterJob::doRender()
285 const bool hasSecondPass = !mSecondPassLayerJobs.empty();
287 QElapsedTimer renderTime;
290 const QgsElevationShadingRenderer mapShadingRenderer =
mSettings.elevationShadingRenderer();
291 std::unique_ptr<QgsElevationMap> mainElevationMap;
292 if ( mapShadingRenderer.
isActive() )
293 mainElevationMap = std::make_unique<QgsElevationMap>(
mSettings.deviceOutputSize(),
mSettings.devicePixelRatio() );
295 for ( LayerRenderJob &job : mLayerJobs )
297 if ( job.context()->renderingStopped() )
307 mPainter->setCompositionMode( job.blendMode );
312 QElapsedTimer layerTime;
315 if ( job.previewRenderImage && !job.previewRenderImageInitialized )
317 job.previewRenderImage->fill( 0 );
318 job.previewRenderImageInitialized =
true;
321 if ( job.destinationImage )
323 job.destinationImage->fill( 0 );
324 job.imageInitialized =
true;
327 job.completed = job.renderer->render();
331 job.renderer->renderContext()->painter()->end();
334 job.renderingTime +=
static_cast< int >( layerTime.elapsed() );
337 if ( !hasSecondPass && job.destinationImage )
340 mPainter->setOpacity( job.opacity );
341 mPainter->drawImage( 0, 0, *job.destinationImage );
342 mPainter->setOpacity( 1.0 );
345 if ( mainElevationMap && job.context()->elevationMap() )
347 const QgsElevationMap &layerElevationMap = *job.context()->elevationMap();
348 if ( layerElevationMap.
isValid() )
358 if ( mapShadingRenderer.
isActive() && mainElevationMap )
360 QImage image( mainElevationMap->rawElevationImage().size(), QImage::Format_RGB32 );
361 image.setDevicePixelRatio(
mSettings.devicePixelRatio() );
362 image.fill( Qt::white );
364 QgsScopedQPainterState painterState( mPainter );
365 mPainter->setCompositionMode( QPainter::CompositionMode_Multiply );
366 mPainter->drawImage( 0, 0, image );
371 if ( !mLabelJob.cached )
373 QElapsedTimer labelTime;
379 mLabelJob.img->fill( 0 );
380 painter.begin( mLabelJob.img.get() );
381 mLabelJob.context.setPainter( &painter );
382 drawLabeling( mLabelJob.context, mLabelingEngineV2.get(), &painter );
385 else if ( mLabelJob.picture )
388 painter.begin( mLabelJob.picture.get() );
389 mLabelJob.context.setPainter( &painter );
390 drawLabeling( mLabelJob.context, mLabelingEngineV2.get(), &painter );
395 drawLabeling( mLabelJob.context, mLabelingEngineV2.get(), mPainter );
398 mLabelJob.complete =
true;
399 mLabelJob.renderingTime =
static_cast< int >( labelTime.elapsed() );
404 if ( !hasSecondPass )
406 if ( mLabelJob.img && mLabelJob.complete )
408 mPainter->setCompositionMode( QPainter::CompositionMode_SourceOver );
409 mPainter->setOpacity( 1.0 );
410 mPainter->drawImage( 0, 0, *mLabelJob.img );
417 for ( LayerRenderJob &job : mSecondPassLayerJobs )
419 if ( job.context()->renderingStopped() )
424 QElapsedTimer layerTime;
427 if ( job.previewRenderImage && !job.previewRenderImageInitialized )
429 job.previewRenderImage->fill( 0 );
430 job.previewRenderImageInitialized =
true;
433 if ( job.destinationImage )
435 job.destinationImage->fill( 0 );
436 job.imageInitialized =
true;
439 job.completed = job.renderer->render();
443 job.renderer->renderContext()->painter()->end();
446 job.renderingTime +=
static_cast< int >( layerTime.elapsed() );
452 for ( LayerRenderJob &job : mLayerJobs )
461 mPainter->setOpacity( job.opacity );
462 mPainter->setCompositionMode( job.blendMode );
463 mPainter->drawImage( 0, 0, *job.destinationImage );
464 mPainter->setCompositionMode( QPainter::CompositionMode_SourceOver );
465 mPainter->setOpacity( 1.0 );
469 if ( mLabelJob.picture )
473 else if ( mLabelJob.img )
475 mPainter->setCompositionMode( QPainter::CompositionMode_SourceOver );
476 mPainter->setOpacity( 1.0 );
477 mPainter->drawImage( 0, 0, *mLabelJob.img );
481 QgsDebugMsgLevel( u
"Rendering completed in (seconds): %1"_s.arg( renderTime.elapsed() / 1000.0 ), 2 );
@ ForceVector
Always force vector-based rendering, even when the result will be visually different to a raster-base...
@ UseAdvancedEffects
Enable layer opacity and blending effects.
@ 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...
bool isValid() const
Returns whether the elevation map is valid.
Qgis::ElevationMapCombineMethod combinedElevationMethod() const
Returns the method used when conbining different elevation sources.
bool isActive() const
Returns whether this shading renderer is active.
void renderShading(const QgsElevationMap &elevation, QImage &image, const QgsRenderContext &context) const
Render shading on image condidering the elevation map elevation and the renderer context context If e...
Stores computed placement from labeling engine.
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.
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.
QList< QPointer< QgsMapLayer > > participatingLabelLayers(QgsLabelingEngine *engine)
Returns a list of the layers participating in the map labeling.
void cleanupSecondPassJobs(std::vector< LayerRenderJob > &jobs)
void initSecondPassJobs(std::vector< LayerRenderJob > &secondPassJobs, LabelRenderJob &labelJob) const
Initialize secondPassJobs according to what have been rendered (mask clipping path e....
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).
QgsMapRendererJob(const QgsMapSettings &settings)
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.
Contains configuration for rendering maps.
QColor backgroundColor() const
Returns the background color of the map.
bool testFlag(Qgis::MapSettingsFlag flag) const
Check whether a particular flag is enabled.
static void drawPicture(QPainter *painter, const QPointF &point, const QPicture &picture)
Draws a picture onto a painter, correctly applying workarounds to avoid issues with incorrect scaling...
static QgsRenderContext fromMapSettings(const QgsMapSettings &mapSettings)
create initialized QgsRenderContext instance from given QgsMapSettings
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)
#define QgsDebugError(str)