28#include <QtConcurrentRun>
30#include "moc_qgsmaprenderercustompainterjob.cpp"
45 painter->fillRect( 0, 0,
mSettings.deviceOutputSize().width(),
mSettings.deviceOutputSize().height(), backgroundColor );
52 QPaintDevice *paintDevice = painter->device();
53 const QString errMsg = QStringLiteral(
"pre-set DPI not equal to painter's DPI (%1 vs %2)" )
54 .arg( paintDevice->logicalDpiX() )
57 "Job::startRender()", errMsg.toLatin1().data() );
76 Q_ASSERT( !mFutureWatcher.isRunning() );
80void QgsMapRendererCustomPainterJob::startPrivate()
94 QgsDebugMsgLevel( QStringLiteral(
"Preparing list of layer jobs for rendering" ), 5 );
95 QElapsedTimer prepareTime;
100 mLabelingEngineV2.reset();
104 mLabelingEngineV2 = std::make_unique<QgsDefaultLabelingEngine>( );
105 mLabelingEngineV2->setMapSettings(
mSettings );
109 mLayerJobs =
prepareJobs( mPainter, mLabelingEngineV2.get() );
110 mLabelJob =
prepareLabelingJob( mPainter, mLabelingEngineV2.get(), canUseLabelCache );
113 QgsDebugMsgLevel( QStringLiteral(
"Rendering prepared in (seconds): %1" ).arg( prepareTime.elapsed() / 1000.0 ), 4 );
115 if ( mRenderSynchronously )
126 connect( &mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererCustomPainterJob::futureFinished );
128 mFuture = QtConcurrent::run( staticRender,
this );
129 mFutureWatcher.setFuture( mFuture );
142 disconnect( &mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererCustomPainterJob::futureFinished );
148 mFutureWatcher.waitForFinished();
150 QgsDebugMsgLevel( QStringLiteral(
"QPAINER cancel waited %1 ms" ).arg( t.elapsed() / 1000.0 ), 5 );
165 mLabelJob.context.setRenderingStopped(
true );
166 for ( LayerRenderJob &job : mLayerJobs )
168 job.context()->setRenderingStopped(
true );
169 if ( job.renderer && job.renderer->feedback() )
170 job.renderer->feedback()->cancel();
179 disconnect( &mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererCustomPainterJob::futureFinished );
184 mFutureWatcher.waitForFinished();
186 QgsDebugMsgLevel( QStringLiteral(
"waitForFinished: %1 ms" ).arg( t.elapsed() / 1000.0 ), 4 );
198 return mLabelJob.cached;
203 if ( mLabelingEngineV2 )
204 return mLabelingEngineV2->takeResults();
213 connect( &mFutureWatcher, &QFutureWatcher<void>::finished, &loop, &QEventLoop::quit );
220 mRenderSynchronously =
true;
223 mRenderSynchronously =
false;
228 mRenderSynchronously =
true;
241 mRenderSynchronously =
false;
242 mPrepareOnly =
false;
246void QgsMapRendererCustomPainterJob::futureFinished()
271 catch ( QgsException &e )
276 catch ( std::exception &e )
279 QgsDebugError(
"Caught unhandled std::exception: " + QString::fromLatin1( e.what() ) );
283 QgsDebugError( QStringLiteral(
"Caught unhandled unknown exception" ) );
287void QgsMapRendererCustomPainterJob::doRender()
289 const bool hasSecondPass = ! mSecondPassLayerJobs.empty();
291 QElapsedTimer renderTime;
294 const QgsElevationShadingRenderer mapShadingRenderer =
mSettings.elevationShadingRenderer();
295 std::unique_ptr<QgsElevationMap> mainElevationMap;
296 if ( mapShadingRenderer.
isActive() )
297 mainElevationMap = std::make_unique<QgsElevationMap>(
mSettings.deviceOutputSize(),
mSettings.devicePixelRatio() );
299 for ( LayerRenderJob &job : mLayerJobs )
301 if ( job.context()->renderingStopped() )
312 mPainter->setCompositionMode( job.blendMode );
317 QElapsedTimer layerTime;
320 if ( job.previewRenderImage && !job.previewRenderImageInitialized )
322 job.previewRenderImage->fill( 0 );
323 job.previewRenderImageInitialized =
true;
329 job.imageInitialized =
true;
332 job.completed = job.renderer->render();
336 job.renderer->renderContext()->painter()->end();
339 job.renderingTime += layerTime.elapsed();
342 if ( ! hasSecondPass && job.img )
345 mPainter->setOpacity( job.opacity );
346 mPainter->drawImage( 0, 0, *job.img );
347 mPainter->setOpacity( 1.0 );
350 if ( mainElevationMap && job.context()->elevationMap() )
352 const QgsElevationMap &layerElevationMap = *job.context()->elevationMap();
353 if ( layerElevationMap.
isValid() )
363 if ( mapShadingRenderer.
isActive() && mainElevationMap )
365 QImage image( mainElevationMap->rawElevationImage().size(), QImage::Format_RGB32 );
366 image.setDevicePixelRatio(
mSettings.devicePixelRatio() );
367 image.fill( Qt::white );
370 mPainter->setCompositionMode( QPainter::CompositionMode_Multiply );
371 mPainter->drawImage( 0, 0, image );
377 if ( !mLabelJob.cached )
379 QElapsedTimer labelTime;
385 mLabelJob.img->fill( 0 );
386 painter.begin( mLabelJob.img );
387 mLabelJob.context.setPainter( &painter );
388 drawLabeling( mLabelJob.context, mLabelingEngineV2.get(), &painter );
391 else if ( mLabelJob.picture )
394 painter.begin( mLabelJob.picture.get() );
395 mLabelJob.context.setPainter( &painter );
396 drawLabeling( mLabelJob.context, mLabelingEngineV2.get(), &painter );
401 drawLabeling( mLabelJob.context, mLabelingEngineV2.get(), mPainter );
404 mLabelJob.complete =
true;
405 mLabelJob.renderingTime = labelTime.elapsed();
410 if ( ! hasSecondPass )
412 if ( mLabelJob.img && mLabelJob.complete )
414 mPainter->setCompositionMode( QPainter::CompositionMode_SourceOver );
415 mPainter->setOpacity( 1.0 );
416 mPainter->drawImage( 0, 0, *mLabelJob.img );
423 for ( LayerRenderJob &job : mSecondPassLayerJobs )
425 if ( job.context()->renderingStopped() )
430 QElapsedTimer layerTime;
433 if ( job.previewRenderImage && !job.previewRenderImageInitialized )
435 job.previewRenderImage->fill( 0 );
436 job.previewRenderImageInitialized =
true;
442 job.imageInitialized =
true;
445 job.completed = job.renderer->render();
449 job.renderer->renderContext()->painter()->end();
452 job.renderingTime += layerTime.elapsed();
463 mPainter->setCompositionMode( QPainter::CompositionMode_SourceOver );
464 mPainter->setOpacity( 1.0 );
465 mPainter->drawImage( 0, 0, finalImage );
470 for ( LayerRenderJob &job : mLayerJobs )
478 mPainter->drawImage( 0, 0, *job.img );
481 if ( mLabelJob.picture )
488 QgsDebugMsgLevel( QStringLiteral(
"Rendering completed in (seconds): %1" ).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.
@ ForceVectorOutput
Vector graphics should not be cached and drawn as raster images.
@ ForceRasterMasks
Force symbol masking to be applied using a raster method. This is considerably faster when compared t...
@ 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)
QList< QPointer< QgsMapLayer > > participatingLabelLayers(QgsLabelingEngine *engine)
Returns a list of the layers participating in the map labeling.
static QImage composeImage(const QgsMapSettings &settings, const std::vector< LayerRenderJob > &jobs, const LabelRenderJob &labelJob, const QgsMapRendererCache *cache=nullptr)
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.
static void composeSecondPass(std::vector< LayerRenderJob > &secondPassJobs, LabelRenderJob &labelJob, bool forceVector=false)
Compose second pass images into first pass images.
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)