Skip to content

Commit ca37a1e

Browse files
committedDec 17, 2017
Fix drawing of map items (grids, overviews) when rendering map item as a raster item
1 parent 770ffdf commit ca37a1e

File tree

13 files changed

+157
-44
lines changed

13 files changed

+157
-44
lines changed
 

‎python/core/layout/qgslayoutitemmap.sip‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,7 @@ This is calculated using the width of the map item and the width of the
457457
current visible map extent.
458458
%End
459459

460-
QgsMapSettings mapSettings( const QgsRectangle &extent, QSizeF size, int dpi ) const;
460+
QgsMapSettings mapSettings( const QgsRectangle &extent, QSizeF size, double dpi ) const;
461461
%Docstring
462462
Return map settings that will be used for drawing of the map.
463463
%End

‎src/app/layout/qgslayoutdesignerdialog.cpp‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1529,7 +1529,7 @@ void QgsLayoutDesignerDialog::exportToRaster()
15291529
switch ( exporter.exportToImage( fileNExt.first, settings ) )
15301530
{
15311531
case QgsLayoutExporter::Success:
1532-
mMessageBar->pushInfo( tr( "Export layout" ), tr( "Successfully exported layout to %1" ).arg( fileNExt.first ) );
1532+
mMessageBar->pushInfo( tr( "Export layout" ), tr( "Successfully exported layout to <a href=\"%1\">%2</a>" ).arg( QUrl::fromLocalFile( fileNExt.first ).toString(), fileNExt.first ) );
15331533
break;
15341534

15351535
case QgsLayoutExporter::PrintError:

‎src/core/layout/qgslayoutitemmap.cpp‎

Lines changed: 87 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -756,11 +756,12 @@ void QgsLayoutItemMap::paint( QPainter *painter, const QStyleOptionGraphicsItem
756756
if ( thisPaintRect.width() == 0 || thisPaintRect.height() == 0 )
757757
return;
758758

759-
painter->save();
760-
painter->setClipRect( thisPaintRect );
759+
//TODO - try to reduce the amount of duplicate code here!
761760

762761
if ( mLayout->context().isPreviewRender() )
763762
{
763+
painter->save();
764+
painter->setClipRect( thisPaintRect );
764765
if ( !mCacheFinalImage || mCacheFinalImage->isNull() )
765766
{
766767
// No initial render available - so draw some preview text alerting user
@@ -800,6 +801,23 @@ void QgsLayoutItemMap::paint( QPainter *painter, const QStyleOptionGraphicsItem
800801
//restore rotation
801802
painter->restore();
802803
}
804+
805+
painter->setClipRect( thisPaintRect, Qt::NoClip );
806+
807+
if ( shouldDrawPart( OverviewMapExtent ) )
808+
{
809+
mOverviewStack->drawItems( painter );
810+
}
811+
if ( shouldDrawPart( Grid ) )
812+
{
813+
mGridStack->drawItems( painter );
814+
}
815+
drawAnnotations( painter );
816+
if ( shouldDrawPart( Frame ) )
817+
{
818+
drawMapFrame( painter );
819+
}
820+
painter->restore();
803821
}
804822
else
805823
{
@@ -811,42 +829,73 @@ void QgsLayoutItemMap::paint( QPainter *painter, const QStyleOptionGraphicsItem
811829
if ( !paintDevice )
812830
return;
813831

814-
// Fill with background color
815-
if ( shouldDrawPart( Background ) )
816-
{
817-
drawMapBackground( painter );
818-
}
819-
820832
QgsRectangle cExtent = extent();
821833
QSizeF size( cExtent.width() * mapUnitsToLayoutUnits(), cExtent.height() * mapUnitsToLayoutUnits() );
822834

823835
if ( containsAdvancedEffects() && ( !mLayout || !( mLayout->context().flags() & QgsLayoutContext::FlagForceVectorOutput ) ) )
824836
{
825837
// rasterise
826-
double destinationDpi = mLayout ? mLayout->context().dpi() : style->matrix.m11() * 25.4;
827-
828-
double layoutUnitsToPixels = mLayout ? mLayout->convertFromLayoutUnits( 1, QgsUnitTypes::LayoutPixels ).length() : destinationDpi / 25.4;
829-
double widthInPixels = boundingRect().width() * layoutUnitsToPixels;
830-
double heightInPixels = boundingRect().height() * layoutUnitsToPixels;
838+
double destinationDpi = style->matrix.m11() * 25.4;
839+
double layoutUnitsInInches = mLayout ? mLayout->convertFromLayoutUnits( 1, QgsUnitTypes::LayoutInches ).length() : 1;
840+
int widthInPixels = std::round( boundingRect().width() * layoutUnitsInInches * destinationDpi );
841+
int heightInPixels = std::round( boundingRect().height() * layoutUnitsInInches * destinationDpi );
831842
QImage image = QImage( widthInPixels, heightInPixels, QImage::Format_ARGB32 );
832843

833844
image.fill( Qt::transparent );
834845
image.setDotsPerMeterX( 1000 * destinationDpi / 25.4 );
835846
image.setDotsPerMeterY( 1000 * destinationDpi / 25.4 );
847+
double dotsPerMM = destinationDpi / 25.4;
836848
QPainter p( &image );
837-
double dotsPerMM = image.logicalDpiX() / 25.4;
838-
drawMap( &p, cExtent, image.size(), destinationDpi );
839-
p.end();
840849

841-
dotsPerMM = paintDevice->logicalDpiX() / 25.4;
850+
QPointF tl = -boundingRect().topLeft();
851+
QRect imagePaintRect( std::round( tl.x() * dotsPerMM ),
852+
std::round( tl.y() * dotsPerMM ),
853+
std::round( thisPaintRect.width() * dotsPerMM ),
854+
std::round( thisPaintRect.height() * dotsPerMM ) );
855+
p.setClipRect( imagePaintRect );
856+
857+
p.translate( imagePaintRect.topLeft() );
858+
859+
// Fill with background color - must be drawn onto the flattened image
860+
// so that layers with opacity or blend modes can correctly interact with it
861+
if ( shouldDrawPart( Background ) )
862+
{
863+
p.scale( dotsPerMM, dotsPerMM );
864+
drawMapBackground( &p );
865+
p.scale( 1.0 / dotsPerMM, 1.0 / dotsPerMM );
866+
}
867+
868+
drawMap( &p, cExtent, imagePaintRect.size(), image.logicalDpiX() );
869+
870+
// important - all other items, overviews, grids etc must be rendered to the
871+
// flattened image, in case these have blend modes must need to interact
872+
// with the map
873+
p.scale( dotsPerMM, dotsPerMM );
874+
875+
if ( shouldDrawPart( OverviewMapExtent ) )
876+
{
877+
mOverviewStack->drawItems( &p );
878+
}
879+
if ( shouldDrawPart( Grid ) )
880+
{
881+
mGridStack->drawItems( &p );
882+
}
883+
drawAnnotations( &p );
884+
842885
painter->save();
843886
painter->scale( 1 / dotsPerMM, 1 / dotsPerMM ); // scale painter from mm to dots
844-
painter->drawImage( 0, 0, image );
845-
painter->restore();
846-
887+
painter->drawImage( std::round( -tl.x()* dotsPerMM ), std::round( -tl.y() * dotsPerMM ), image );
888+
painter->scale( dotsPerMM, dotsPerMM );
847889
}
848890
else
849891
{
892+
// Fill with background color
893+
if ( shouldDrawPart( Background ) )
894+
{
895+
drawMapBackground( painter );
896+
}
897+
898+
painter->setClipRect( thisPaintRect );
850899
painter->save();
851900
painter->translate( mXOffset, mYOffset );
852901

@@ -856,31 +905,28 @@ void QgsLayoutItemMap::paint( QPainter *painter, const QStyleOptionGraphicsItem
856905
drawMap( painter, cExtent, size, paintDevice->logicalDpiX() );
857906

858907
painter->restore();
859-
}
860908

861-
mDrawing = false;
862-
}
863-
864-
painter->setClipRect( thisPaintRect, Qt::NoClip );
909+
painter->setClipRect( thisPaintRect, Qt::NoClip );
865910

866-
if ( shouldDrawPart( OverviewMapExtent ) )
867-
{
868-
mOverviewStack->drawItems( painter );
869-
}
870-
if ( shouldDrawPart( Grid ) )
871-
{
872-
mGridStack->drawItems( painter );
873-
}
911+
if ( shouldDrawPart( OverviewMapExtent ) )
912+
{
913+
mOverviewStack->drawItems( painter );
914+
}
915+
if ( shouldDrawPart( Grid ) )
916+
{
917+
mGridStack->drawItems( painter );
918+
}
919+
drawAnnotations( painter );
874920

875-
//draw canvas items
876-
drawAnnotations( painter );
921+
}
877922

878-
if ( shouldDrawPart( Frame ) )
879-
{
880-
drawMapFrame( painter );
923+
if ( shouldDrawPart( Frame ) )
924+
{
925+
drawMapFrame( painter );
926+
}
927+
painter->restore();
928+
mDrawing = false;
881929
}
882-
883-
painter->restore();
884930
}
885931

886932
int QgsLayoutItemMap::numberExportLayers() const
@@ -994,7 +1040,7 @@ void QgsLayoutItemMap::recreateCachedImageInBackground( double viewScaleFactor )
9941040
mPainterJob->start();
9951041
}
9961042

997-
QgsMapSettings QgsLayoutItemMap::mapSettings( const QgsRectangle &extent, QSizeF size, int dpi ) const
1043+
QgsMapSettings QgsLayoutItemMap::mapSettings( const QgsRectangle &extent, QSizeF size, double dpi ) const
9981044
{
9991045
QgsExpressionContext expressionContext = createExpressionContext();
10001046
QgsCoordinateReferenceSystem renderCrs = crs();

‎src/core/layout/qgslayoutitemmap.h‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,7 @@ class CORE_EXPORT QgsLayoutItemMap : public QgsLayoutItem
408408
/**
409409
* Return map settings that will be used for drawing of the map.
410410
*/
411-
QgsMapSettings mapSettings( const QgsRectangle &extent, QSizeF size, int dpi ) const;
411+
QgsMapSettings mapSettings( const QgsRectangle &extent, QSizeF size, double dpi ) const;
412412

413413
void finalizeRestoreFromXml() override;
414414

‎src/core/layout/qgslayoutitemmapgrid.cpp‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1219,7 +1219,11 @@ void QgsLayoutItemMapGrid::drawCoordinateAnnotation( QPainter *p, QPointF pos, c
12191219
ypos += ( mAnnotationFrameDistance + textHeight + gridFrameDistance );
12201220
xpos -= textWidth / 2.0;
12211221
if ( extension )
1222+
{
12221223
extension->bottom = std::max( extension->bottom, mAnnotationFrameDistance + gridFrameDistance + textHeight );
1224+
extension->left = std::max( extension->left, textWidth / 2.0 ); // annotation at bottom left/right may extend outside the bounds
1225+
extension->right = std::max( extension->right, textWidth / 2.0 );
1226+
}
12231227
}
12241228
else if ( mBottomGridAnnotationDirection == QgsLayoutItemMapGrid::VerticalDescending )
12251229
{

‎tests/src/core/testqgslayoutmap.cpp‎

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "qgsmapthemecollection.h"
2929
#include "qgsproperty.h"
3030
#include "qgslayoutpagecollection.h"
31+
#include "qgslayoutitempolyline.h"
3132
#include <QObject>
3233
#include "qgstest.h"
3334

@@ -53,6 +54,7 @@ class TestQgsLayoutMap : public QObject
5354
void mapPolygonVertices(); // test mapPolygon function with no map rotation
5455
void dataDefinedLayers(); //test data defined layer string
5556
void dataDefinedStyles(); //test data defined styles
57+
void rasterized();
5658

5759
private:
5860
QgsRasterLayer *mRasterLayer = nullptr;
@@ -440,5 +442,66 @@ void TestQgsLayoutMap::dataDefinedStyles()
440442
QVERIFY( checker.testLayout( mReport, 0, 0 ) );
441443
}
442444

445+
void TestQgsLayoutMap::rasterized()
446+
{
447+
// test a map which must be rasterised
448+
QgsLayout l( QgsProject::instance() );
449+
l.initializeDefaults();
450+
451+
QgsLayoutItemMap *map = new QgsLayoutItemMap( &l );
452+
map->attemptMove( QgsLayoutPoint( 20, 30 ) );
453+
map->attemptResize( QgsLayoutSize( 200, 100 ) );
454+
map->setFrameEnabled( true );
455+
map->setExtent( QgsRectangle( -110.0, 25.0, -90, 40.0 ) );
456+
QList<QgsMapLayer *> layers = QList<QgsMapLayer *>() << mLinesLayer;
457+
map->setLayers( layers );
458+
map->setBackgroundColor( Qt::yellow );
459+
l.addLayoutItem( map );
460+
461+
// add some guide lines, just for reference
462+
QPolygonF points;
463+
points << QPointF( 0, 30 ) << QPointF( 10, 30 );
464+
QgsLayoutItemPolyline *line1 = new QgsLayoutItemPolyline( points, &l );
465+
l.addLayoutItem( line1 );
466+
points.clear();
467+
points << QPointF( 0, 30 + map->rect().height() ) << QPointF( 10, 30 + map->rect().height() );
468+
QgsLayoutItemPolyline *line2 = new QgsLayoutItemPolyline( points, &l );
469+
l.addLayoutItem( line2 );
470+
points.clear();
471+
points << QPointF( 20, 0 ) << QPointF( 20, 20 );
472+
QgsLayoutItemPolyline *line3 = new QgsLayoutItemPolyline( points, &l );
473+
l.addLayoutItem( line3 );
474+
points.clear();
475+
points << QPointF( 220, 0 ) << QPointF( 220, 20 );
476+
QgsLayoutItemPolyline *line4 = new QgsLayoutItemPolyline( points, &l );
477+
l.addLayoutItem( line4 );
478+
479+
// force rasterization
480+
QgsLayoutItemMapGrid *grid = new QgsLayoutItemMapGrid( "test", map );
481+
grid->setIntervalX( 10 );
482+
grid->setIntervalY( 10 );
483+
grid->setBlendMode( QPainter::CompositionMode_Darken );
484+
grid->setAnnotationEnabled( true );
485+
grid->setAnnotationDisplay( QgsLayoutItemMapGrid::ShowAll, QgsLayoutItemMapGrid::Left );
486+
grid->setAnnotationDisplay( QgsLayoutItemMapGrid::ShowAll, QgsLayoutItemMapGrid::Top );
487+
grid->setAnnotationDisplay( QgsLayoutItemMapGrid::ShowAll, QgsLayoutItemMapGrid::Right );
488+
grid->setAnnotationDisplay( QgsLayoutItemMapGrid::ShowAll, QgsLayoutItemMapGrid::Bottom );
489+
map->grids()->addGrid( grid );
490+
map->updateBoundingRect();
491+
492+
QVERIFY( map->containsAdvancedEffects() );
493+
494+
QgsLayoutChecker checker( QStringLiteral( "layoutmap_rasterized" ), &l );
495+
checker.setControlPathPrefix( QStringLiteral( "composer_map" ) );
496+
QVERIFY( checker.testLayout( mReport, 0, 0 ) );
497+
498+
// try rendering again, without requiring rasterization, for comparison
499+
// (we can use the same test image, because CompositionMode_Darken doesn't actually have any noticable
500+
// rendering differences for the black grid!)
501+
grid->setBlendMode( QPainter::CompositionMode_SourceOver );
502+
QVERIFY( !map->containsAdvancedEffects() );
503+
QVERIFY( checker.testLayout( mReport, 0, 0 ) );
504+
}
505+
443506
QGSTEST_MAIN( TestQgsLayoutMap )
444507
#include "testqgslayoutmap.moc"
29.1 KB
Loading
39.8 KB
Loading
File renamed without changes.
File renamed without changes.
13.4 KB

Error rendering embedded code

Invalid image source.

740 Bytes
Loading
75 Bytes

Error rendering embedded code

Invalid image source.

0 commit comments

Comments
 (0)
Please sign in to comment.