25#include <QtConcurrentRun>
30static void _fixQPictureDPI( QPainter *p )
36 p->scale(
static_cast< double >(
qt_defaultDpiX() ) / p->device()->logicalDpiX(),
37 static_cast< double >(
qt_defaultDpiY() ) / p->device()->logicalDpiY() );
60 QPaintDevice *paintDevice = painter->device();
61 const QString errMsg = QStringLiteral(
"pre-set DPI not equal to painter's DPI (%1 vs %2)" )
62 .arg( paintDevice->logicalDpiX() )
65 "Job::startRender()", errMsg.toLatin1().data() );
78 , mRenderSynchronously( false )
86 Q_ASSERT( !mFutureWatcher.isRunning() );
90void QgsMapRendererCustomPainterJob::startPrivate()
104 QgsDebugMsgLevel( QStringLiteral(
"Preparing list of layer jobs for rendering" ), 5 );
105 QElapsedTimer prepareTime;
110 mLabelingEngineV2.reset();
115 mLabelingEngineV2->setMapSettings(
mSettings );
119 mLayerJobs =
prepareJobs( mPainter, mLabelingEngineV2.get() );
120 mLabelJob =
prepareLabelingJob( mPainter, mLabelingEngineV2.get(), canUseLabelCache );
123 QgsDebugMsgLevel( QStringLiteral(
"Rendering prepared in (seconds): %1" ).arg( prepareTime.elapsed() / 1000.0 ), 4 );
125 if ( mRenderSynchronously )
136 connect( &mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererCustomPainterJob::futureFinished );
138 mFuture = QtConcurrent::run( staticRender,
this );
139 mFutureWatcher.setFuture( mFuture );
152 disconnect( &mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererCustomPainterJob::futureFinished );
158 mFutureWatcher.waitForFinished();
160 QgsDebugMsgLevel( QStringLiteral(
"QPAINER cancel waited %1 ms" ).arg( t.elapsed() / 1000.0 ), 5 );
171 QgsDebugMsg( QStringLiteral(
"QPAINTER not running!" ) );
175 mLabelJob.context.setRenderingStopped(
true );
176 for ( LayerRenderJob &job : mLayerJobs )
178 job.context()->setRenderingStopped(
true );
179 if ( job.renderer && job.renderer->feedback() )
180 job.renderer->feedback()->cancel();
189 disconnect( &mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererCustomPainterJob::futureFinished );
194 mFutureWatcher.waitForFinished();
196 QgsDebugMsgLevel( QStringLiteral(
"waitForFinished: %1 ms" ).arg( t.elapsed() / 1000.0 ), 4 );
208 return mLabelJob.cached;
213 if ( mLabelingEngineV2 )
214 return mLabelingEngineV2->takeResults();
223 connect( &mFutureWatcher, &QFutureWatcher<void>::finished, &loop, &QEventLoop::quit );
230 mRenderSynchronously =
true;
233 mRenderSynchronously =
false;
238 mRenderSynchronously =
true;
251 mRenderSynchronously =
false;
252 mPrepareOnly =
false;
256void QgsMapRendererCustomPainterJob::futureFinished()
286 catch ( std::exception &e )
289 QgsDebugMsg(
"Caught unhandled std::exception: " + QString::fromLatin1( e.what() ) );
293 QgsDebugMsg( QStringLiteral(
"Caught unhandled unknown exception" ) );
297void QgsMapRendererCustomPainterJob::doRender()
299 const bool hasSecondPass = ! mSecondPassLayerJobs.empty();
301 QElapsedTimer renderTime;
305 std::unique_ptr<QgsElevationMap> mainElevationMap;
306 if ( mapShadingRenderer.
isActive() )
309 for ( LayerRenderJob &job : mLayerJobs )
311 if ( job.context()->renderingStopped() )
316 if ( ! hasSecondPass && job.context()->useAdvancedEffects() )
320 mPainter->setCompositionMode( job.blendMode );
325 QElapsedTimer layerTime;
331 job.imageInitialized =
true;
334 job.completed = job.renderer->render();
338 job.renderer->renderContext()->painter()->end();
341 job.renderingTime += layerTime.elapsed();
344 if ( ! hasSecondPass && job.img )
347 mPainter->setOpacity( job.opacity );
348 mPainter->drawImage( 0, 0, *job.img );
349 mPainter->setOpacity( 1.0 );
352 if ( mainElevationMap && job.context()->elevationMap() )
354 const QgsElevationMap &layerElevationMap = *job.context()->elevationMap();
355 if ( layerElevationMap.
isValid() )
365 if ( mapShadingRenderer.
isActive() && mainElevationMap )
367 QImage image( mainElevationMap->rawElevationImage().size(), QImage::Format_RGB32 );
368 image.fill( Qt::white );
371 mPainter->setCompositionMode( QPainter::CompositionMode_Multiply );
372 mPainter->drawImage( 0, 0, image );
378 if ( !mLabelJob.cached )
380 QElapsedTimer labelTime;
386 mLabelJob.img->fill( 0 );
387 painter.begin( mLabelJob.img );
388 mLabelJob.context.setPainter( &painter );
389 drawLabeling( mLabelJob.context, mLabelingEngineV2.get(), &painter );
392 else if ( mLabelJob.picture )
395 painter.begin( mLabelJob.picture.get() );
396 mLabelJob.context.setPainter( &painter );
397 drawLabeling( mLabelJob.context, mLabelingEngineV2.get(), &painter );
402 drawLabeling( mLabelJob.context, mLabelingEngineV2.get(), mPainter );
405 mLabelJob.complete =
true;
406 mLabelJob.renderingTime = labelTime.elapsed();
407 mLabelJob.participatingLayers = _qgis_listRawToQPointer( mLabelingEngineV2->participatingLayers() );
411 if ( ! hasSecondPass )
413 if ( mLabelJob.img && mLabelJob.complete )
415 mPainter->setCompositionMode( QPainter::CompositionMode_SourceOver );
416 mPainter->setOpacity( 1.0 );
417 mPainter->drawImage( 0, 0, *mLabelJob.img );
424 for ( LayerRenderJob &job : mSecondPassLayerJobs )
426 if ( job.context()->renderingStopped() )
431 QElapsedTimer layerTime;
437 job.imageInitialized =
true;
440 job.completed = job.renderer->render();
444 job.renderer->renderContext()->painter()->end();
447 job.renderingTime += layerTime.elapsed();
458 mPainter->setCompositionMode( QPainter::CompositionMode_SourceOver );
459 mPainter->setOpacity( 1.0 );
460 mPainter->drawImage( 0, 0, finalImage );
465 for ( LayerRenderJob &job : mLayerJobs )
471 _fixQPictureDPI( mPainter );
472 mPainter->drawPicture( 0, 0, *job.picture );
476 mPainter->drawImage( 0, 0, *job.img );
479 if ( mLabelJob.picture )
482 _fixQPictureDPI( mPainter );
483 mPainter->drawPicture( 0, 0, *mLabelJob.picture );
489 QgsDebugMsgLevel( QStringLiteral(
"Rendering completed in (seconds): %1" ).arg( renderTime.elapsed() / 1000.0 ), 2 );
@ 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...
Default QgsLabelingEngine implementation, which completes the whole labeling operation (including lab...
Stores digital elevation model in a raster image which may get updated as a part of map layer renderi...
void combine(const QgsElevationMap &otherElevationMap, Qgis::ElevationMapCombineMethod method)
Combines this elevation map with otherElevationMap.
bool isValid() const
Returns whether the elevation map is valid.
This class can render elevation shading on an image with different methods (eye dome lighting,...
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...
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 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).
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.
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.
QSize outputSize() const
Returns the size of the resulting map image, in pixels.
const QgsElevationShadingRenderer & elevationShadingRenderer() const
Returns the shading renderer used to render shading on the entire 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.
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)
Q_GUI_EXPORT int qt_defaultDpiX()
Q_GUI_EXPORT int qt_defaultDpiY()