From 6ec751f6349ba8740d7161381cc05c955b694024 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 3 Oct 2014 23:11:47 +1000 Subject: [PATCH] [composer] Cache transformed grid lines and intersections to slightly speed up grid drawing --- python/core/composer/qgscomposermapgrid.sip | 2 +- python/core/composer/qgscomposermapitem.sip | 2 +- .../core/composer/qgscomposermapoverview.sip | 2 +- src/core/composer/qgscomposermapgrid.cpp | 229 +++++++++++++----- src/core/composer/qgscomposermapgrid.h | 28 ++- src/core/composer/qgscomposermapitem.h | 2 +- src/core/composer/qgscomposermapoverview.cpp | 2 +- src/core/composer/qgscomposermapoverview.h | 2 +- 8 files changed, 192 insertions(+), 77 deletions(-) mode change 100644 => 100755 src/core/composer/qgscomposermapgrid.h mode change 100644 => 100755 src/core/composer/qgscomposermapitem.h mode change 100644 => 100755 src/core/composer/qgscomposermapoverview.cpp mode change 100644 => 100755 src/core/composer/qgscomposermapoverview.h diff --git a/python/core/composer/qgscomposermapgrid.sip b/python/core/composer/qgscomposermapgrid.sip index 7485be1657a..9add174b0f0 100644 --- a/python/core/composer/qgscomposermapgrid.sip +++ b/python/core/composer/qgscomposermapgrid.sip @@ -223,7 +223,7 @@ class QgsComposerMapGrid : QgsComposerMapItem /**Draws a grid * @param painter destination QPainter */ - void draw( QPainter* painter ) const; + void draw( QPainter* painter ); /**Stores grid state in DOM element * @param elem is DOM element corresponding to a 'ComposerMap' tag diff --git a/python/core/composer/qgscomposermapitem.sip b/python/core/composer/qgscomposermapitem.sip index 73d3cbc8f72..3b966962a16 100644 --- a/python/core/composer/qgscomposermapitem.sip +++ b/python/core/composer/qgscomposermapitem.sip @@ -21,7 +21,7 @@ class QgsComposerMapItem : QgsComposerObject /**Draws the item on to a painter * @param painter destination QPainter */ - virtual void draw( QPainter* painter ) const = 0; + virtual void draw( QPainter* painter ) = 0; /**Stores map item state in DOM element * @param elem is DOM element corresponding to a 'ComposerMap' tag diff --git a/python/core/composer/qgscomposermapoverview.sip b/python/core/composer/qgscomposermapoverview.sip index 7c1fbfaf51b..3d213f4a395 100644 --- a/python/core/composer/qgscomposermapoverview.sip +++ b/python/core/composer/qgscomposermapoverview.sip @@ -125,7 +125,7 @@ class QgsComposerMapOverview : QgsComposerMapItem /**Draws an overview * @param painter destination QPainter */ - void draw( QPainter* painter ) const; + void draw( QPainter* painter ); /**Stores overview state in DOM element * @param elem is DOM element corresponding to a 'ComposerMap' tag diff --git a/src/core/composer/qgscomposermapgrid.cpp b/src/core/composer/qgscomposermapgrid.cpp index d7f7b8c65a5..1cfd33d7cf1 100755 --- a/src/core/composer/qgscomposermapgrid.cpp +++ b/src/core/composer/qgscomposermapgrid.cpp @@ -144,6 +144,7 @@ double QgsComposerMapGridStack::maxGridExtension() const QgsComposerMapGrid::QgsComposerMapGrid( const QString& name, QgsComposerMap* map ) : QgsComposerMapItem( name, map ) + , mTransformDirty( true ) , mGridStyle( QgsComposerMapGrid::Solid ) , mGridIntervalX( 0.0 ) , mGridIntervalY( 0.0 ) @@ -382,6 +383,12 @@ bool QgsComposerMapGrid::readXML( const QDomElement& itemElem, const QDomDocumen return ok; } +void QgsComposerMapGrid::setCrs( const QgsCoordinateReferenceSystem &crs ) +{ + mCRS = crs; + mTransformDirty = true; +} + bool QgsComposerMapGrid::usesAdvancedEffects() const { return mBlendMode == QPainter::CompositionMode_SourceOver; @@ -394,13 +401,87 @@ QPolygonF QgsComposerMapGrid::scalePolygon( const QPolygonF &polygon, const doub } void QgsComposerMapGrid::drawGridCRSTransform( QgsRenderContext &context, double dotsPerMM, QList< QPair< double, QLineF > > &horizontalLines, - QList< QPair< double, QLineF > > &verticalLines ) const + QList< QPair< double, QLineF > > &verticalLines ) { if ( !mComposerMap || !mEnabled ) { return; } + //has map extent/scale changed? + QPolygonF mapPolygon = mComposerMap->transformedMapPolygon(); + if ( mapPolygon != mPrevMapPolygon ) + { + mTransformDirty = true; + mPrevMapPolygon = mapPolygon; + } + + if ( mTransformDirty ) + { + calculateCRSTransformLines(); + } + + //draw lines + if ( mGridStyle == QgsComposerMapGrid::Solid ) + { + QList< QPair< double, QPolygonF > >::const_iterator xGridIt = mTransformedXLines.constBegin(); + for ( ; xGridIt != mTransformedXLines.constEnd(); ++xGridIt ) + { + drawGridLine( scalePolygon( xGridIt->second, dotsPerMM ), context ); + } + + QList< QPair< double, QPolygonF > >::const_iterator yGridIt = mTransformedYLines.constBegin(); + for ( ; yGridIt != mTransformedYLines.constEnd(); ++yGridIt ) + { + drawGridLine( scalePolygon( yGridIt->second, dotsPerMM ), context ); + } + } + else if ( mGridStyle == QgsComposerMapGrid::Cross || mGridStyle == QgsComposerMapGrid::Markers ) + { + double maxX = mComposerMap->rect().width(); + double maxY = mComposerMap->rect().height(); + + QList< QgsPoint >::const_iterator intersectionIt = mTransformedIntersections.constBegin(); + for ( ; intersectionIt != mTransformedIntersections.constEnd(); ++intersectionIt ) + { + double x = intersectionIt->x(); + double y = intersectionIt->y(); + if ( mGridStyle == QgsComposerMapGrid::Cross ) + { + //ensure that crosses don't overshoot the map item bounds + QLineF line1 = QLineF( x - mCrossLength, y, x + mCrossLength, y ); + line1.p1().rx() = line1.p1().x() < 0 ? 0 : line1.p1().x(); + line1.p2().rx() = line1.p2().x() > maxX ? maxX : line1.p2().x(); + QLineF line2 = QLineF( x, y - mCrossLength, x, y + mCrossLength ); + line2.p1().ry() = line2.p1().y() < 0 ? 0 : line2.p1().y(); + line2.p2().ry() = line2.p2().y() > maxY ? maxY : line2.p2().y(); + + //draw line using coordinates scaled to dots + drawGridLine( QLineF( line1.p1() * dotsPerMM, line1.p2() * dotsPerMM ), context ); + drawGridLine( QLineF( line2.p1() * dotsPerMM, line2.p2() * dotsPerMM ), context ); + } + else if ( mGridStyle == QgsComposerMapGrid::Markers ) + { + drawGridMarker( QPointF( x, y ) * dotsPerMM , context ); + } + } + } + + //convert QPolygonF to QLineF to draw grid frames and annotations + QList< QPair< double, QPolygonF > >::const_iterator yGridLineIt = mTransformedYLines.constBegin(); + for ( ; yGridLineIt != mTransformedYLines.constEnd(); ++yGridLineIt ) + { + verticalLines.push_back( qMakePair( yGridLineIt->first, QLineF( yGridLineIt->second.first(), yGridLineIt->second.last() ) ) ); + } + QList< QPair< double, QPolygonF > >::const_iterator xGridLineIt = mTransformedXLines.constBegin(); + for ( ; xGridLineIt != mTransformedXLines.constEnd(); ++xGridLineIt ) + { + horizontalLines.push_back( qMakePair( xGridLineIt->first, QLineF( xGridLineIt->second.first(), xGridLineIt->second.last() ) ) ); + } +} + +void QgsComposerMapGrid::calculateCRSTransformLines() +{ QgsRectangle crsBoundingRect; QgsCoordinateTransform inverseTr; if ( crsGridParams( crsBoundingRect, inverseTr ) != 0 ) @@ -408,34 +489,22 @@ void QgsComposerMapGrid::drawGridCRSTransform( QgsRenderContext &context, double return; } - //x grid lines - QList< QPair< double, QPolygonF > > xGridLines; - xGridLinesCRSTransform( crsBoundingRect, inverseTr, xGridLines ); + //calculate x grid lines + mTransformedXLines.clear(); + xGridLinesCRSTransform( crsBoundingRect, inverseTr, mTransformedXLines ); - //y grid lines - QList< QPair< double, QPolygonF > > yGridLines; - yGridLinesCRSTransform( crsBoundingRect, inverseTr, yGridLines ); + //calculate y grid lines + mTransformedYLines.clear(); + yGridLinesCRSTransform( crsBoundingRect, inverseTr, mTransformedYLines ); - if ( mGridStyle == QgsComposerMapGrid::Solid ) + if ( mGridStyle == QgsComposerMapGrid::Cross || mGridStyle == QgsComposerMapGrid::Markers ) { - QList< QPair< double, QPolygonF > >::const_iterator xGridIt = xGridLines.constBegin(); - for ( ; xGridIt != xGridLines.constEnd(); ++xGridIt ) - { - drawGridLine( scalePolygon( xGridIt->second, dotsPerMM ), context ); - } + //cross or markers style - we also need to calculate intersections of lines - QList< QPair< double, QPolygonF > >::const_iterator yGridIt = yGridLines.constBegin(); - for ( ; yGridIt != yGridLines.constEnd(); ++yGridIt ) - { - drawGridLine( scalePolygon( yGridIt->second, dotsPerMM ), context ); - } - } - else if ( mGridStyle != QgsComposerMapGrid::FrameAnnotationsOnly ) //cross or markers - { - //convert lines to QgsGeometry + //first convert lines to QgsGeometry QList< QgsGeometry* > yLines; - QList< QPair< double, QPolygonF > >::const_iterator yGridIt = yGridLines.constBegin(); - for ( ; yGridIt != yGridLines.constEnd(); ++yGridIt ) + QList< QPair< double, QPolygonF > >::const_iterator yGridIt = mTransformedYLines.constBegin(); + for ( ; yGridIt != mTransformedYLines.constEnd(); ++yGridIt ) { QgsPolyline yLine; for ( int i = 0; i < ( *yGridIt ).second.size(); ++i ) @@ -445,8 +514,8 @@ void QgsComposerMapGrid::drawGridCRSTransform( QgsRenderContext &context, double yLines << QgsGeometry::fromPolyline( yLine ); } QList< QgsGeometry* > xLines; - QList< QPair< double, QPolygonF > >::const_iterator xGridIt = xGridLines.constBegin(); - for ( ; xGridIt != xGridLines.constEnd(); ++xGridIt ) + QList< QPair< double, QPolygonF > >::const_iterator xGridIt = mTransformedXLines.constBegin(); + for ( ; xGridIt != mTransformedXLines.constEnd(); ++xGridIt ) { QgsPolyline xLine; for ( int i = 0; i < ( *xGridIt ).second.size(); ++i ) @@ -456,9 +525,8 @@ void QgsComposerMapGrid::drawGridCRSTransform( QgsRenderContext &context, double xLines << QgsGeometry::fromPolyline( xLine ); } - double maxX = mComposerMap->rect().width(); - double maxY = mComposerMap->rect().height(); - + //now, loop through geometries and calculate intersection points + mTransformedIntersections.clear(); QList< QgsGeometry* >::const_iterator yLineIt = yLines.constBegin(); for ( ; yLineIt != yLines.constEnd(); ++yLineIt ) { @@ -473,52 +541,23 @@ void QgsComposerMapGrid::drawGridCRSTransform( QgsRenderContext &context, double QgsPoint vertex = intersects->vertexAt( i ); while ( vertex != QgsPoint( 0, 0 ) ) { - if ( mGridStyle == QgsComposerMapGrid::Cross ) - { - //ensure that crosses don't overshoot the map item bounds - QLineF line1 = QLineF( vertex.x() - mCrossLength, vertex.y(), vertex.x() + mCrossLength, vertex.y() ); - line1.p1().rx() = line1.p1().x() < 0 ? 0 : line1.p1().x(); - line1.p2().rx() = line1.p2().x() > maxX ? maxX : line1.p2().x(); - QLineF line2 = QLineF( vertex.x() , vertex.y() - mCrossLength, vertex.x(), vertex.y() + mCrossLength ); - line2.p1().ry() = line2.p1().y() < 0 ? 0 : line2.p1().y(); - line2.p2().ry() = line2.p2().y() > maxY ? maxY : line2.p2().y(); - - //draw line using coordinates scaled to dots - drawGridLine( QLineF( line1.p1() * dotsPerMM, line1.p2() * dotsPerMM ), context ); - drawGridLine( QLineF( line2.p1() * dotsPerMM, line2.p2() * dotsPerMM ), context ); - } - else if ( mGridStyle == QgsComposerMapGrid::Markers ) - { - drawGridMarker( QPointF( vertex.x(), vertex.y() ) * dotsPerMM , context ); - } - + mTransformedIntersections << vertex; i = i + 1; vertex = intersects->vertexAt( i ); } } } - + //clean up qDeleteAll( yLines ); yLines.clear(); qDeleteAll( xLines ); xLines.clear(); } - //convert QPolygonF to QLineF to draw grid frames and annotations - QList< QPair< double, QPolygonF > >::const_iterator yGridLineIt = yGridLines.constBegin(); - for ( ; yGridLineIt != yGridLines.constEnd(); ++yGridLineIt ) - { - verticalLines.push_back( qMakePair( yGridLineIt->first, QLineF( yGridLineIt->second.first(), yGridLineIt->second.last() ) ) ); - } - QList< QPair< double, QPolygonF > >::const_iterator xGridLineIt = xGridLines.constBegin(); - for ( ; xGridLineIt != xGridLines.constEnd(); ++xGridLineIt ) - { - horizontalLines.push_back( qMakePair( xGridLineIt->first, QLineF( xGridLineIt->second.first(), xGridLineIt->second.last() ) ) ); - } - + mTransformDirty = false; } -void QgsComposerMapGrid::draw( QPainter* p ) const +void QgsComposerMapGrid::draw( QPainter* p ) { if ( !mComposerMap || !mEnabled ) { @@ -536,6 +575,12 @@ void QgsComposerMapGrid::draw( QPainter* p ) const QRectF thisPaintRect = QRectF( 0, 0, mComposerMap->rect().width(), mComposerMap->rect().height() ); p->setClipRect( thisPaintRect ); + if ( thisPaintRect != mPrevPaintRect ) + { + //rect has changed, so need to recalculate transform + mTransformDirty = true; + mPrevPaintRect = thisPaintRect; + } //setup painter scaling to dots so that raster symbology is drawn to scale double dotsPerMM = thePaintDevice->logicalDpiX() / 25.4; @@ -1694,6 +1739,66 @@ double QgsComposerMapGrid::maxExtension() const return maxExtension + mAnnotationFrameDistance + gridFrameDist; } +void QgsComposerMapGrid::setUnits( const QgsComposerMapGrid::GridUnit unit ) +{ + if ( unit == mGridUnit ) + { + return; + } + mGridUnit = unit; + mTransformDirty = true; +} + +void QgsComposerMapGrid::setIntervalX( const double interval ) +{ + if ( interval == mGridIntervalX ) + { + return; + } + mGridIntervalX = interval; + mTransformDirty = true; +} + +void QgsComposerMapGrid::setIntervalY( const double interval ) +{ + if ( interval == mGridIntervalY ) + { + return; + } + mGridIntervalY = interval; + mTransformDirty = true; +} + +void QgsComposerMapGrid::setOffsetX( const double offset ) +{ + if ( offset == mGridOffsetX ) + { + return; + } + mGridOffsetX = offset; + mTransformDirty = true; +} + +void QgsComposerMapGrid::setOffsetY( const double offset ) +{ + if ( offset == mGridOffsetY ) + { + return; + } + mGridOffsetY = offset; + mTransformDirty = true; +} + +void QgsComposerMapGrid::setStyle( const QgsComposerMapGrid::GridStyle style ) +{ + if ( style == mGridStyle ) + { + return; + } + mGridStyle = style; + mTransformDirty = true; +} + void QgsComposerMapGrid::setAnnotationDirection( const QgsComposerMapGrid::AnnotationDirection direction, const QgsComposerMapGrid::BorderSide border ) { switch ( border ) diff --git a/src/core/composer/qgscomposermapgrid.h b/src/core/composer/qgscomposermapgrid.h old mode 100644 new mode 100755 index a2a980c9aed..d5566d098ba --- a/src/core/composer/qgscomposermapgrid.h +++ b/src/core/composer/qgscomposermapgrid.h @@ -258,7 +258,7 @@ class CORE_EXPORT QgsComposerMapGrid : public QgsComposerMapItem /**Draws a grid * @param painter destination QPainter */ - void draw( QPainter* painter ) const; + void draw( QPainter* painter ); /**Stores grid state in DOM element * @param elem is DOM element corresponding to a 'ComposerMap' tag @@ -278,7 +278,7 @@ class CORE_EXPORT QgsComposerMapGrid : public QgsComposerMapItem * @param crs coordinate reference system for grid * @see crs */ - void setCrs( const QgsCoordinateReferenceSystem& crs ) { mCRS = crs; } + void setCrs( const QgsCoordinateReferenceSystem& crs ); /**Retrieves the CRS for the grid. * @returns coordinate reference system for grid @@ -315,7 +315,7 @@ class CORE_EXPORT QgsComposerMapGrid : public QgsComposerMapItem * @param unit unit for grid measurements * @see units */ - void setUnits( const GridUnit unit ) { mGridUnit = unit; } + void setUnits( const GridUnit unit ); /**Gets the units used for grid measurements such as the interval * and offset for grid lines. @@ -330,7 +330,7 @@ class CORE_EXPORT QgsComposerMapGrid : public QgsComposerMapItem * @see setIntervalY * @see intervalX */ - void setIntervalX( const double interval ) { mGridIntervalX = interval; } + void setIntervalX( const double interval ); /**Gets the interval between grid lines in the x-direction. The units * are retrieved through the units() method. @@ -346,7 +346,7 @@ class CORE_EXPORT QgsComposerMapGrid : public QgsComposerMapItem * @see setIntervalX * @see intervalY */ - void setIntervalY( const double interval ) { mGridIntervalY = interval; } + void setIntervalY( const double interval ); /**Gets the interval between grid lines in the y-direction. The units * are retrieved through the units() method. @@ -362,7 +362,7 @@ class CORE_EXPORT QgsComposerMapGrid : public QgsComposerMapItem * @see setOffsetY * @see offsetX */ - void setOffsetX( const double offset ) { mGridOffsetX = offset; } + void setOffsetX( const double offset ); /**Gets the offset for grid lines in the x-direction. The units * are retrieved through the units() method. @@ -378,7 +378,7 @@ class CORE_EXPORT QgsComposerMapGrid : public QgsComposerMapItem * @see setOffsetX * @see offsetY */ - void setOffsetY( const double offset ) { mGridOffsetY = offset; } + void setOffsetY( const double offset ); /**Gets the offset for grid lines in the y-direction. The units * are retrieved through the units() method. @@ -397,7 +397,7 @@ class CORE_EXPORT QgsComposerMapGrid : public QgsComposerMapItem * @param style desired grid style * @see style */ - void setStyle( const GridStyle style ) { mGridStyle = style; } + void setStyle( const GridStyle style ); /**Gets the grid's style, which controls how the grid is drawn * over the map's contents @@ -741,6 +741,9 @@ class CORE_EXPORT QgsComposerMapGrid : public QgsComposerMapItem QgsComposerMapGrid(); //forbidden + /*True if a re-transformation of grid lines is required*/ + bool mTransformDirty; + /**Solid or crosses*/ GridStyle mGridStyle; /**Grid line interval in x-direction (map units)*/ @@ -799,6 +802,12 @@ class CORE_EXPORT QgsComposerMapGrid : public QgsComposerMapItem QPainter::CompositionMode mBlendMode; + QList< QPair< double, QPolygonF > > mTransformedXLines; + QList< QPair< double, QPolygonF > > mTransformedYLines; + QList< QgsPoint > mTransformedIntersections; + QRectF mPrevPaintRect; + QPolygonF mPrevMapPolygon; + /**Draws the map grid*/ void drawGridFrame( QPainter* p, const QList< QPair< double, QLineF > >& hLines, const QList< QPair< double, QLineF > >& vLines ) const; @@ -852,7 +861,7 @@ class CORE_EXPORT QgsComposerMapGrid : public QgsComposerMapItem /**Draws grid if CRS is different to map CRS*/ void drawGridCRSTransform( QgsRenderContext &context , double dotsPerMM, QList< QPair< double, QLineF > > &horizontalLines, - QList< QPair< double, QLineF > > &verticalLines ) const; + QList< QPair< double, QLineF > > &verticalLines ); void drawGridNoTransform( QgsRenderContext &context, double dotsPerMM, QList > &horizontalLines, QList > &verticalLines ) const; @@ -868,6 +877,7 @@ class CORE_EXPORT QgsComposerMapGrid : public QgsComposerMapItem void drawGridFrameLineBorder( QPainter *p, BorderSide border ) const; + void calculateCRSTransformLines(); }; #endif // QGSCOMPOSERMAPGRID_H diff --git a/src/core/composer/qgscomposermapitem.h b/src/core/composer/qgscomposermapitem.h old mode 100644 new mode 100755 index b7238899fc9..ea4e5dc7c05 --- a/src/core/composer/qgscomposermapitem.h +++ b/src/core/composer/qgscomposermapitem.h @@ -43,7 +43,7 @@ class CORE_EXPORT QgsComposerMapItem : public QgsComposerObject /**Draws the item on to a painter * @param painter destination QPainter */ - virtual void draw( QPainter* painter ) const = 0; + virtual void draw( QPainter* painter ) = 0; /**Stores map item state in DOM element * @param elem is DOM element corresponding to a 'ComposerMap' tag diff --git a/src/core/composer/qgscomposermapoverview.cpp b/src/core/composer/qgscomposermapoverview.cpp old mode 100644 new mode 100755 index 7d77633e8de..17fc4975695 --- a/src/core/composer/qgscomposermapoverview.cpp +++ b/src/core/composer/qgscomposermapoverview.cpp @@ -55,7 +55,7 @@ QgsComposerMapOverview::~QgsComposerMapOverview() delete mFrameSymbol; } -void QgsComposerMapOverview::draw( QPainter *painter ) const +void QgsComposerMapOverview::draw( QPainter *painter ) { if ( !mEnabled || mFrameMapId == -1 || !mComposerMap || !mComposerMap->composition() ) { diff --git a/src/core/composer/qgscomposermapoverview.h b/src/core/composer/qgscomposermapoverview.h old mode 100644 new mode 100755 index 450b95a9045..764829d5e17 --- a/src/core/composer/qgscomposermapoverview.h +++ b/src/core/composer/qgscomposermapoverview.h @@ -149,7 +149,7 @@ class CORE_EXPORT QgsComposerMapOverview : public QgsComposerMapItem /**Draws an overview * @param painter destination QPainter */ - void draw( QPainter* painter ) const; + void draw( QPainter* painter ); /**Stores overview state in DOM element * @param elem is DOM element corresponding to a 'ComposerMap' tag