Skip to content

Commit

Permalink
Fix drawing of map items (grids, overviews) when rendering map item a…
Browse files Browse the repository at this point in the history
…s a raster item
  • Loading branch information
nyalldawson committed Dec 17, 2017
1 parent 770ffdf commit ca37a1e
Show file tree
Hide file tree
Showing 13 changed files with 157 additions and 44 deletions.
2 changes: 1 addition & 1 deletion python/core/layout/qgslayoutitemmap.sip
Expand Up @@ -457,7 +457,7 @@ This is calculated using the width of the map item and the width of the
current visible map extent.
%End

QgsMapSettings mapSettings( const QgsRectangle &extent, QSizeF size, int dpi ) const;
QgsMapSettings mapSettings( const QgsRectangle &extent, QSizeF size, double dpi ) const;
%Docstring
Return map settings that will be used for drawing of the map.
%End
Expand Down
2 changes: 1 addition & 1 deletion src/app/layout/qgslayoutdesignerdialog.cpp
Expand Up @@ -1529,7 +1529,7 @@ void QgsLayoutDesignerDialog::exportToRaster()
switch ( exporter.exportToImage( fileNExt.first, settings ) )
{
case QgsLayoutExporter::Success:
mMessageBar->pushInfo( tr( "Export layout" ), tr( "Successfully exported layout to %1" ).arg( fileNExt.first ) );
mMessageBar->pushInfo( tr( "Export layout" ), tr( "Successfully exported layout to <a href=\"%1\">%2</a>" ).arg( QUrl::fromLocalFile( fileNExt.first ).toString(), fileNExt.first ) );
break;

case QgsLayoutExporter::PrintError:
Expand Down
128 changes: 87 additions & 41 deletions src/core/layout/qgslayoutitemmap.cpp
Expand Up @@ -756,11 +756,12 @@ void QgsLayoutItemMap::paint( QPainter *painter, const QStyleOptionGraphicsItem
if ( thisPaintRect.width() == 0 || thisPaintRect.height() == 0 )
return;

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

if ( mLayout->context().isPreviewRender() )
{
painter->save();
painter->setClipRect( thisPaintRect );
if ( !mCacheFinalImage || mCacheFinalImage->isNull() )
{
// No initial render available - so draw some preview text alerting user
Expand Down Expand Up @@ -800,6 +801,23 @@ void QgsLayoutItemMap::paint( QPainter *painter, const QStyleOptionGraphicsItem
//restore rotation
painter->restore();
}

painter->setClipRect( thisPaintRect, Qt::NoClip );

if ( shouldDrawPart( OverviewMapExtent ) )
{
mOverviewStack->drawItems( painter );
}
if ( shouldDrawPart( Grid ) )
{
mGridStack->drawItems( painter );
}
drawAnnotations( painter );
if ( shouldDrawPart( Frame ) )
{
drawMapFrame( painter );
}
painter->restore();
}
else
{
Expand All @@ -811,42 +829,73 @@ void QgsLayoutItemMap::paint( QPainter *painter, const QStyleOptionGraphicsItem
if ( !paintDevice )
return;

// Fill with background color
if ( shouldDrawPart( Background ) )
{
drawMapBackground( painter );
}

QgsRectangle cExtent = extent();
QSizeF size( cExtent.width() * mapUnitsToLayoutUnits(), cExtent.height() * mapUnitsToLayoutUnits() );

if ( containsAdvancedEffects() && ( !mLayout || !( mLayout->context().flags() & QgsLayoutContext::FlagForceVectorOutput ) ) )
{
// rasterise
double destinationDpi = mLayout ? mLayout->context().dpi() : style->matrix.m11() * 25.4;

double layoutUnitsToPixels = mLayout ? mLayout->convertFromLayoutUnits( 1, QgsUnitTypes::LayoutPixels ).length() : destinationDpi / 25.4;
double widthInPixels = boundingRect().width() * layoutUnitsToPixels;
double heightInPixels = boundingRect().height() * layoutUnitsToPixels;
double destinationDpi = style->matrix.m11() * 25.4;
double layoutUnitsInInches = mLayout ? mLayout->convertFromLayoutUnits( 1, QgsUnitTypes::LayoutInches ).length() : 1;
int widthInPixels = std::round( boundingRect().width() * layoutUnitsInInches * destinationDpi );
int heightInPixels = std::round( boundingRect().height() * layoutUnitsInInches * destinationDpi );
QImage image = QImage( widthInPixels, heightInPixels, QImage::Format_ARGB32 );

image.fill( Qt::transparent );
image.setDotsPerMeterX( 1000 * destinationDpi / 25.4 );
image.setDotsPerMeterY( 1000 * destinationDpi / 25.4 );
double dotsPerMM = destinationDpi / 25.4;
QPainter p( &image );
double dotsPerMM = image.logicalDpiX() / 25.4;
drawMap( &p, cExtent, image.size(), destinationDpi );
p.end();

dotsPerMM = paintDevice->logicalDpiX() / 25.4;
QPointF tl = -boundingRect().topLeft();
QRect imagePaintRect( std::round( tl.x() * dotsPerMM ),
std::round( tl.y() * dotsPerMM ),
std::round( thisPaintRect.width() * dotsPerMM ),
std::round( thisPaintRect.height() * dotsPerMM ) );
p.setClipRect( imagePaintRect );

p.translate( imagePaintRect.topLeft() );

// Fill with background color - must be drawn onto the flattened image
// so that layers with opacity or blend modes can correctly interact with it
if ( shouldDrawPart( Background ) )
{
p.scale( dotsPerMM, dotsPerMM );
drawMapBackground( &p );
p.scale( 1.0 / dotsPerMM, 1.0 / dotsPerMM );
}

drawMap( &p, cExtent, imagePaintRect.size(), image.logicalDpiX() );

// important - all other items, overviews, grids etc must be rendered to the
// flattened image, in case these have blend modes must need to interact
// with the map
p.scale( dotsPerMM, dotsPerMM );

if ( shouldDrawPart( OverviewMapExtent ) )
{
mOverviewStack->drawItems( &p );
}
if ( shouldDrawPart( Grid ) )
{
mGridStack->drawItems( &p );
}
drawAnnotations( &p );

painter->save();
painter->scale( 1 / dotsPerMM, 1 / dotsPerMM ); // scale painter from mm to dots
painter->drawImage( 0, 0, image );
painter->restore();

painter->drawImage( std::round( -tl.x()* dotsPerMM ), std::round( -tl.y() * dotsPerMM ), image );
painter->scale( dotsPerMM, dotsPerMM );
}
else
{
// Fill with background color
if ( shouldDrawPart( Background ) )
{
drawMapBackground( painter );
}

painter->setClipRect( thisPaintRect );
painter->save();
painter->translate( mXOffset, mYOffset );

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

painter->restore();
}

mDrawing = false;
}

painter->setClipRect( thisPaintRect, Qt::NoClip );
painter->setClipRect( thisPaintRect, Qt::NoClip );

if ( shouldDrawPart( OverviewMapExtent ) )
{
mOverviewStack->drawItems( painter );
}
if ( shouldDrawPart( Grid ) )
{
mGridStack->drawItems( painter );
}
if ( shouldDrawPart( OverviewMapExtent ) )
{
mOverviewStack->drawItems( painter );
}
if ( shouldDrawPart( Grid ) )
{
mGridStack->drawItems( painter );
}
drawAnnotations( painter );

//draw canvas items
drawAnnotations( painter );
}

if ( shouldDrawPart( Frame ) )
{
drawMapFrame( painter );
if ( shouldDrawPart( Frame ) )
{
drawMapFrame( painter );
}
painter->restore();
mDrawing = false;
}

painter->restore();
}

int QgsLayoutItemMap::numberExportLayers() const
Expand Down Expand Up @@ -994,7 +1040,7 @@ void QgsLayoutItemMap::recreateCachedImageInBackground( double viewScaleFactor )
mPainterJob->start();
}

QgsMapSettings QgsLayoutItemMap::mapSettings( const QgsRectangle &extent, QSizeF size, int dpi ) const
QgsMapSettings QgsLayoutItemMap::mapSettings( const QgsRectangle &extent, QSizeF size, double dpi ) const
{
QgsExpressionContext expressionContext = createExpressionContext();
QgsCoordinateReferenceSystem renderCrs = crs();
Expand Down
2 changes: 1 addition & 1 deletion src/core/layout/qgslayoutitemmap.h
Expand Up @@ -408,7 +408,7 @@ class CORE_EXPORT QgsLayoutItemMap : public QgsLayoutItem
/**
* Return map settings that will be used for drawing of the map.
*/
QgsMapSettings mapSettings( const QgsRectangle &extent, QSizeF size, int dpi ) const;
QgsMapSettings mapSettings( const QgsRectangle &extent, QSizeF size, double dpi ) const;

void finalizeRestoreFromXml() override;

Expand Down
4 changes: 4 additions & 0 deletions src/core/layout/qgslayoutitemmapgrid.cpp
Expand Up @@ -1219,7 +1219,11 @@ void QgsLayoutItemMapGrid::drawCoordinateAnnotation( QPainter *p, QPointF pos, c
ypos += ( mAnnotationFrameDistance + textHeight + gridFrameDistance );
xpos -= textWidth / 2.0;
if ( extension )
{
extension->bottom = std::max( extension->bottom, mAnnotationFrameDistance + gridFrameDistance + textHeight );
extension->left = std::max( extension->left, textWidth / 2.0 ); // annotation at bottom left/right may extend outside the bounds
extension->right = std::max( extension->right, textWidth / 2.0 );
}
}
else if ( mBottomGridAnnotationDirection == QgsLayoutItemMapGrid::VerticalDescending )
{
Expand Down
63 changes: 63 additions & 0 deletions tests/src/core/testqgslayoutmap.cpp
Expand Up @@ -28,6 +28,7 @@
#include "qgsmapthemecollection.h"
#include "qgsproperty.h"
#include "qgslayoutpagecollection.h"
#include "qgslayoutitempolyline.h"
#include <QObject>
#include "qgstest.h"

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

private:
QgsRasterLayer *mRasterLayer = nullptr;
Expand Down Expand Up @@ -440,5 +442,66 @@ void TestQgsLayoutMap::dataDefinedStyles()
QVERIFY( checker.testLayout( mReport, 0, 0 ) );
}

void TestQgsLayoutMap::rasterized()
{
// test a map which must be rasterised
QgsLayout l( QgsProject::instance() );
l.initializeDefaults();

QgsLayoutItemMap *map = new QgsLayoutItemMap( &l );
map->attemptMove( QgsLayoutPoint( 20, 30 ) );
map->attemptResize( QgsLayoutSize( 200, 100 ) );
map->setFrameEnabled( true );
map->setExtent( QgsRectangle( -110.0, 25.0, -90, 40.0 ) );
QList<QgsMapLayer *> layers = QList<QgsMapLayer *>() << mLinesLayer;
map->setLayers( layers );
map->setBackgroundColor( Qt::yellow );
l.addLayoutItem( map );

// add some guide lines, just for reference
QPolygonF points;
points << QPointF( 0, 30 ) << QPointF( 10, 30 );
QgsLayoutItemPolyline *line1 = new QgsLayoutItemPolyline( points, &l );
l.addLayoutItem( line1 );
points.clear();
points << QPointF( 0, 30 + map->rect().height() ) << QPointF( 10, 30 + map->rect().height() );
QgsLayoutItemPolyline *line2 = new QgsLayoutItemPolyline( points, &l );
l.addLayoutItem( line2 );
points.clear();
points << QPointF( 20, 0 ) << QPointF( 20, 20 );
QgsLayoutItemPolyline *line3 = new QgsLayoutItemPolyline( points, &l );
l.addLayoutItem( line3 );
points.clear();
points << QPointF( 220, 0 ) << QPointF( 220, 20 );
QgsLayoutItemPolyline *line4 = new QgsLayoutItemPolyline( points, &l );
l.addLayoutItem( line4 );

// force rasterization
QgsLayoutItemMapGrid *grid = new QgsLayoutItemMapGrid( "test", map );
grid->setIntervalX( 10 );
grid->setIntervalY( 10 );
grid->setBlendMode( QPainter::CompositionMode_Darken );
grid->setAnnotationEnabled( true );
grid->setAnnotationDisplay( QgsLayoutItemMapGrid::ShowAll, QgsLayoutItemMapGrid::Left );
grid->setAnnotationDisplay( QgsLayoutItemMapGrid::ShowAll, QgsLayoutItemMapGrid::Top );
grid->setAnnotationDisplay( QgsLayoutItemMapGrid::ShowAll, QgsLayoutItemMapGrid::Right );
grid->setAnnotationDisplay( QgsLayoutItemMapGrid::ShowAll, QgsLayoutItemMapGrid::Bottom );
map->grids()->addGrid( grid );
map->updateBoundingRect();

QVERIFY( map->containsAdvancedEffects() );

QgsLayoutChecker checker( QStringLiteral( "layoutmap_rasterized" ), &l );
checker.setControlPathPrefix( QStringLiteral( "composer_map" ) );
QVERIFY( checker.testLayout( mReport, 0, 0 ) );

// try rendering again, without requiring rasterization, for comparison
// (we can use the same test image, because CompositionMode_Darken doesn't actually have any noticable
// rendering differences for the black grid!)
grid->setBlendMode( QPainter::CompositionMode_SourceOver );
QVERIFY( !map->containsAdvancedEffects() );
QVERIFY( checker.testLayout( mReport, 0, 0 ) );
}

QGSTEST_MAIN( TestQgsLayoutMap )
#include "testqgslayoutmap.moc"
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit ca37a1e

Please sign in to comment.