33
33
#include " qgsvectorlayerrenderer.h"
34
34
#include " qgsvectorlayer.h"
35
35
#include " qgscsexception.h"
36
+ #include " qgslabelingengine.h"
37
+ #include " qgsmaplayerlistutils.h"
38
+ #include " qgsvectorlayerlabeling.h"
36
39
37
40
// /@cond PRIVATE
38
41
42
+ const QString QgsMapRendererJob::LABEL_CACHE_ID = QStringLiteral( " _labels_" );
43
+
39
44
QgsMapRendererJob::QgsMapRendererJob ( const QgsMapSettings& settings )
40
45
: mSettings( settings )
41
46
, mCache( nullptr )
@@ -66,6 +71,39 @@ const QgsMapSettings& QgsMapRendererJob::mapSettings() const
66
71
return mSettings ;
67
72
}
68
73
74
+ bool QgsMapRendererJob::prepareLabelCache () const
75
+ {
76
+ bool canCache = mCache ;
77
+
78
+ // calculate which layers will be labeled
79
+ QSet< QgsMapLayer* > labeledLayers;
80
+ Q_FOREACH ( const QgsMapLayer* ml, mSettings .layers () )
81
+ {
82
+ QgsVectorLayer* vl = const_cast < QgsVectorLayer* >( qobject_cast<const QgsVectorLayer *>( ml ) );
83
+ if ( vl && QgsPalLabeling::staticWillUseLayer ( vl ) )
84
+ labeledLayers << vl;
85
+ if ( vl && vl->labeling () && vl->labeling ()->requiresAdvancedEffects ( vl ) )
86
+ {
87
+ canCache = false ;
88
+ break ;
89
+ }
90
+ }
91
+
92
+ if ( mCache && mCache ->hasCacheImage ( LABEL_CACHE_ID ) )
93
+ {
94
+ // we may need to clear label cache and re-register labeled features - check for that here
95
+
96
+ // can we reuse the cached label solution?
97
+ bool canUseCache = canCache && mCache ->dependentLayers ( LABEL_CACHE_ID ).toSet () == labeledLayers;
98
+ if ( !canUseCache )
99
+ {
100
+ // no - participating layers have changed
101
+ mCache ->clearCacheImage ( LABEL_CACHE_ID );
102
+ }
103
+ }
104
+ return canCache;
105
+ }
106
+
69
107
70
108
bool QgsMapRendererJob::reprojectToLayerExtent ( const QgsMapLayer *ml, const QgsCoordinateTransform& ct, QgsRectangle &extent, QgsRectangle &r2 )
71
109
{
@@ -179,13 +217,15 @@ LayerRenderJobs QgsMapRendererJob::prepareJobs( QPainter* painter, QgsLabelingEn
179
217
QListIterator<QgsMapLayer*> li ( mSettings .layers () );
180
218
li.toBack ();
181
219
220
+ bool cacheValid = false ;
182
221
if ( mCache )
183
222
{
184
- bool cacheValid = mCache ->init ( mSettings .visibleExtent (), mSettings .scale () );
223
+ cacheValid = mCache ->init ( mSettings .visibleExtent (), mSettings .scale () );
185
224
QgsDebugMsg ( QString ( " CACHE VALID: %1" ).arg ( cacheValid ) );
186
- Q_UNUSED ( cacheValid );
187
225
}
188
226
227
+ bool requiresLabelRedraw = !( mCache && mCache ->hasCacheImage ( LABEL_CACHE_ID ) );
228
+
189
229
mGeometryCaches .clear ();
190
230
191
231
while ( li.hasPrevious () )
@@ -229,8 +269,12 @@ LayerRenderJobs QgsMapRendererJob::prepareJobs( QPainter* painter, QgsLabelingEn
229
269
if ( mCache && ml->type () == QgsMapLayer::VectorLayer )
230
270
{
231
271
QgsVectorLayer* vl = qobject_cast<QgsVectorLayer *>( ml );
232
- if ( vl->isEditable () || ( labelingEngine2 && QgsPalLabeling::staticWillUseLayer ( vl ) ) )
272
+ bool requiresLabeling = false ;
273
+ requiresLabeling = ( labelingEngine2 && QgsPalLabeling::staticWillUseLayer ( vl ) ) && requiresLabelRedraw;
274
+ if ( vl->isEditable () || requiresLabeling )
275
+ {
233
276
mCache ->clearCacheImage ( ml->id () );
277
+ }
234
278
}
235
279
236
280
layerJobs.append ( LayerRenderJob () );
@@ -312,6 +356,47 @@ LayerRenderJobs QgsMapRendererJob::prepareJobs( QPainter* painter, QgsLabelingEn
312
356
return layerJobs;
313
357
}
314
358
359
+ LabelRenderJob QgsMapRendererJob::prepareLabelingJob ( QPainter* painter, QgsLabelingEngine* labelingEngine2, bool canUseLabelCache )
360
+ {
361
+ LabelRenderJob job;
362
+ job.context = QgsRenderContext::fromMapSettings ( mSettings );
363
+ job.context .setPainter ( painter );
364
+ job.context .setLabelingEngine ( labelingEngine2 );
365
+ job.context .setExtent ( mSettings .visibleExtent () );
366
+
367
+ // if we can use the cache, let's do it and avoid rendering!
368
+ bool hasCache = canUseLabelCache && mCache && mCache ->hasCacheImage ( LABEL_CACHE_ID );
369
+ if ( hasCache )
370
+ {
371
+ job.cached = true ;
372
+ job.complete = true ;
373
+ job.img = new QImage ( mCache ->cacheImage ( LABEL_CACHE_ID ) );
374
+ job.context .setPainter ( nullptr );
375
+ }
376
+ else
377
+ {
378
+ if ( canUseLabelCache && ( mCache || !painter ) )
379
+ {
380
+ // Flattened image for drawing labels
381
+ QImage * mypFlattenedImage = nullptr ;
382
+ mypFlattenedImage = new QImage ( mSettings .outputSize ().width (),
383
+ mSettings .outputSize ().height (),
384
+ mSettings .outputImageFormat () );
385
+ if ( mypFlattenedImage->isNull () )
386
+ {
387
+ mErrors .append ( Error ( QStringLiteral ( " labels" ), tr ( " Insufficient memory for label image %1x%2" ).arg ( mSettings .outputSize ().width () ).arg ( mSettings .outputSize ().height () ) ) );
388
+ delete mypFlattenedImage;
389
+ }
390
+ else
391
+ {
392
+ job.img = mypFlattenedImage;
393
+ }
394
+ }
395
+ }
396
+
397
+ return job;
398
+ }
399
+
315
400
316
401
void QgsMapRendererJob::cleanupJobs ( LayerRenderJobs& jobs )
317
402
{
@@ -343,13 +428,29 @@ void QgsMapRendererJob::cleanupJobs( LayerRenderJobs& jobs )
343
428
}
344
429
}
345
430
431
+
346
432
jobs.clear ();
347
433
348
434
updateLayerGeometryCaches ();
349
435
}
350
436
437
+ void QgsMapRendererJob::cleanupLabelJob ( LabelRenderJob& job )
438
+ {
439
+ if ( job.img )
440
+ {
441
+ if ( mCache && !job.cached && !job.context .renderingStopped () )
442
+ {
443
+ QgsDebugMsg ( " caching label result image" );
444
+ mCache ->setCacheImage ( LABEL_CACHE_ID, *job.img , _qgis_listQPointerToRaw ( job.participatingLayers ) );
445
+ }
351
446
352
- QImage QgsMapRendererJob::composeImage ( const QgsMapSettings& settings, const LayerRenderJobs& jobs )
447
+ delete job.img ;
448
+ job.img = nullptr ;
449
+ }
450
+ }
451
+
452
+
453
+ QImage QgsMapRendererJob::composeImage ( const QgsMapSettings& settings, const LayerRenderJobs& jobs, const LabelRenderJob& labelJob )
353
454
{
354
455
QImage image ( settings.outputSize (), settings.outputImageFormat () );
355
456
image.fill ( settings.backgroundColor ().rgba () );
@@ -368,11 +469,21 @@ QImage QgsMapRendererJob::composeImage( const QgsMapSettings& settings, const La
368
469
painter.drawImage ( 0 , 0 , *job.img );
369
470
}
370
471
472
+ // IMPORTANT - don't draw labelJob img before the label job is complete,
473
+ // as the image is uninitialized and full of garbage before the label job
474
+ // commences
475
+ if ( labelJob.img && labelJob.complete )
476
+ {
477
+ painter.setCompositionMode ( QPainter::CompositionMode_SourceOver );
478
+ painter.setOpacity ( 1.0 );
479
+ painter.drawImage ( 0 , 0 , *labelJob.img );
480
+ }
481
+
371
482
painter.end ();
372
483
return image;
373
484
}
374
485
375
- void QgsMapRendererJob::logRenderingTime ( const LayerRenderJobs& jobs )
486
+ void QgsMapRendererJob::logRenderingTime ( const LayerRenderJobs& jobs, const LabelRenderJob& labelJob )
376
487
{
377
488
QSettings settings;
378
489
if ( !settings.value ( QStringLiteral ( " /Map/logCanvasRefreshEvent" ), false ).toBool () )
@@ -382,6 +493,8 @@ void QgsMapRendererJob::logRenderingTime( const LayerRenderJobs& jobs )
382
493
Q_FOREACH ( const LayerRenderJob& job, jobs )
383
494
elapsed.insert ( job.renderingTime , job.layer ? job.layer ->id () : QString () );
384
495
496
+ elapsed.insert ( labelJob.renderingTime , tr ( " Labeling" ) );
497
+
385
498
QList<int > tt ( elapsed.uniqueKeys () );
386
499
std::sort ( tt.begin (), tt.end (), std::greater<int >() );
387
500
Q_FOREACH ( int t, tt )
0 commit comments