From 23b660ec74229c5ec12f92a1376a8ea3e715e13e Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Mon, 28 Apr 2014 19:28:53 +1000 Subject: [PATCH] [FEATURE][composer] Implement different resize modes for composer picture items, including zoom, stretch, clip, frame to image size (fix #7886). (Sponsored by City of Uster, Switzerland) --- src/app/composer/qgscomposerpicturewidget.cpp | 20 +++ src/app/composer/qgscomposerpicturewidget.h | 1 + src/core/composer/qgscomposerpicture.cpp | 123 +++++++++++++++--- src/core/composer/qgscomposerpicture.h | 36 ++++- src/ui/qgscomposerpicturewidgetbase.ui | 38 +++++- 5 files changed, 190 insertions(+), 28 deletions(-) diff --git a/src/app/composer/qgscomposerpicturewidget.cpp b/src/app/composer/qgscomposerpicturewidget.cpp index 653fbc7c957..e68060b5369 100644 --- a/src/app/composer/qgscomposerpicturewidget.cpp +++ b/src/app/composer/qgscomposerpicturewidget.cpp @@ -198,6 +198,21 @@ void QgsComposerPictureWidget::on_mRemoveDirectoryButton_clicked() s.setValue( "/Composer/PictureWidgetDirectories", userDirList ); } +void QgsComposerPictureWidget::on_mResizeModeComboBox_currentIndexChanged( int index ) +{ + if ( !mPicture ) + { + return; + } + + mPicture->beginCommand( tr( "Picture resize mode changed" ) ); + mPicture->setResizeMode(( QgsComposerPicture::ResizeMode )index ); + mPicture->endCommand(); + + //disable picture rotation for non-zoom modes + mRotationGroupBox->setEnabled( mPicture->resizeMode() == QgsComposerPicture::Zoom ); +} + void QgsComposerPictureWidget::on_mRotationFromComposerMapCheckBox_stateChanged( int state ) { if ( !mPicture ) @@ -326,6 +341,7 @@ void QgsComposerPictureWidget::setGuiElementValues() mPictureLineEdit->blockSignals( true ); mComposerMapComboBox->blockSignals( true ); mRotationFromComposerMapCheckBox->blockSignals( true ); + mResizeModeComboBox->blockSignals( true ); mPictureLineEdit->setText( mPicture->pictureFile() ); // QRectF pictureRect = mPicture->rect(); @@ -352,11 +368,15 @@ void QgsComposerPictureWidget::setGuiElementValues() mComposerMapComboBox->setEnabled( false ); } + mResizeModeComboBox->setCurrentIndex(( int )mPicture->resizeMode() ); + //disable picture rotation for non-zoom modes + mRotationGroupBox->setEnabled( mPicture->resizeMode() == QgsComposerPicture::Zoom ); mRotationFromComposerMapCheckBox->blockSignals( false ); mPictureRotationSpinBox->blockSignals( false ); mPictureLineEdit->blockSignals( false ); mComposerMapComboBox->blockSignals( false ); + mResizeModeComboBox->blockSignals( false ); } } diff --git a/src/app/composer/qgscomposerpicturewidget.h b/src/app/composer/qgscomposerpicturewidget.h index 6f1f2827e88..b0ef645e4d4 100644 --- a/src/app/composer/qgscomposerpicturewidget.h +++ b/src/app/composer/qgscomposerpicturewidget.h @@ -45,6 +45,7 @@ class QgsComposerPictureWidget: public QWidget, private Ui::QgsComposerPictureWi void on_mRemoveDirectoryButton_clicked(); void on_mRotationFromComposerMapCheckBox_stateChanged( int state ); void on_mComposerMapComboBox_activated( const QString & text ); + void on_mResizeModeComboBox_currentIndexChanged( int index ); protected: void showEvent( QShowEvent * event ); diff --git a/src/core/composer/qgscomposerpicture.cpp b/src/core/composer/qgscomposerpicture.cpp index 5709c095e5c..920a5b33817 100644 --- a/src/core/composer/qgscomposerpicture.cpp +++ b/src/core/composer/qgscomposerpicture.cpp @@ -28,12 +28,13 @@ QgsComposerPicture::QgsComposerPicture( QgsComposition *composition ) - : QgsComposerItem( composition ), mMode( Unknown ), mPictureRotation( 0 ), mRotationMap( 0 ) + : QgsComposerItem( composition ), mMode( Unknown ), mPictureRotation( 0 ), mRotationMap( 0 ), + mResizeMode( QgsComposerPicture::Zoom ) { mPictureWidth = rect().width(); } -QgsComposerPicture::QgsComposerPicture(): QgsComposerItem( 0 ), mMode( Unknown ), mPictureRotation( 0 ), mRotationMap( 0 ) +QgsComposerPicture::QgsComposerPicture(): QgsComposerItem( 0 ), mMode( Unknown ), mPictureRotation( 0 ), mRotationMap( 0 ), mResizeMode( QgsComposerPicture::Zoom ) { mPictureHeight = rect().height(); } @@ -58,13 +59,39 @@ void QgsComposerPicture::paint( QPainter* painter, const QStyleOptionGraphicsIte if ( mMode != Unknown ) { - double boundRectWidthMM = mPictureWidth; - double boundRectHeightMM = mPictureHeight; - + double boundRectWidthMM; + double boundRectHeightMM; + double imageRectWidthMM; + double imageRectHeightMM; + if ( mResizeMode == QgsComposerPicture::Zoom || mResizeMode == QgsComposerPicture::ZoomResizeFrame ) + { + boundRectWidthMM = mPictureWidth; + boundRectHeightMM = mPictureHeight; + imageRectWidthMM = mImage.width(); + imageRectHeightMM = mImage.height(); + } + else if ( mResizeMode == QgsComposerPicture::Stretch ) + { + boundRectWidthMM = rect().width(); + boundRectHeightMM = rect().height(); + imageRectWidthMM = mImage.width(); + imageRectHeightMM = mImage.height(); + } + else + { + boundRectWidthMM = rect().width(); + boundRectHeightMM = rect().height(); + imageRectWidthMM = rect().width() * mComposition->printResolution() / 25.4; + imageRectHeightMM = rect().height() * mComposition->printResolution() / 25.4; + } painter->save(); - painter->translate( rect().width() / 2.0, rect().height() / 2.0 ); - painter->rotate( mPictureRotation ); - painter->translate( -boundRectWidthMM / 2.0, -boundRectHeightMM / 2.0 ); + + if ( mResizeMode == Zoom ) + { + painter->translate( rect().width() / 2.0, rect().height() / 2.0 ); + painter->rotate( mPictureRotation ); + painter->translate( -boundRectWidthMM / 2.0, -boundRectHeightMM / 2.0 ); + } if ( mMode == SVG ) { @@ -72,7 +99,7 @@ void QgsComposerPicture::paint( QPainter* painter, const QStyleOptionGraphicsIte } else if ( mMode == RASTER ) { - painter->drawImage( QRectF( 0, 0, boundRectWidthMM, boundRectHeightMM ), mImage, QRectF( 0, 0, mImage.width(), mImage.height() ) ); + painter->drawImage( QRectF( 0, 0, boundRectWidthMM, boundRectHeightMM ), mImage, QRectF( 0, 0, imageRectWidthMM, imageRectHeightMM ) ); } painter->restore(); @@ -204,14 +231,53 @@ QRectF QgsComposerPicture::boundedSVGRect( double deviceWidth, double deviceHeig void QgsComposerPicture::setSceneRect( const QRectF& rectangle ) { - QgsComposerItem::setSceneRect( rectangle ); + + QSizeF currentPictureSize = pictureSize(); + + if ( mResizeMode == QgsComposerPicture::Clip ) + { + QgsComposerItem::setSceneRect( rectangle ); + mPictureWidth = rectangle.width(); + mPictureHeight = rectangle.height(); + return; + } + + QRectF newRect = rectangle; + + if ( mResizeMode == ZoomResizeFrame && !rect().isEmpty() ) + { + //if width has changed less than height, then fix width and set height correspondingly + //else, do the opposite + if ( qAbs( rect().width() - rectangle.width() ) < + qAbs( rect().height() - rectangle.height() ) ) + { + newRect.setHeight( currentPictureSize.height() * newRect.width() / currentPictureSize.width() ); + } + else + { + newRect.setWidth( currentPictureSize.width() * newRect.height() / currentPictureSize.height() ); + } + } + else if ( mResizeMode == FrameToImageSize ) + { + newRect.setWidth( currentPictureSize.width() * 25.4 / mComposition->printResolution() ); + newRect.setHeight( currentPictureSize.height() * 25.4 / mComposition->printResolution() ); + } //find largest scaling of picture with this rotation which fits in item - QSizeF currentPictureSize = pictureSize(); - QRectF rotatedImageRect = largestRotatedRectWithinBounds( QRectF( 0, 0, currentPictureSize.width(), currentPictureSize.height() ), rectangle, mPictureRotation ); - mPictureWidth = rotatedImageRect.width(); - mPictureHeight = rotatedImageRect.height(); + if ( mResizeMode == Zoom ) + { + QRectF rotatedImageRect = largestRotatedRectWithinBounds( QRectF( 0, 0, currentPictureSize.width(), currentPictureSize.height() ), newRect, mPictureRotation ); + mPictureWidth = rotatedImageRect.width(); + mPictureHeight = rotatedImageRect.height(); + } + else + { + mPictureWidth = newRect.width(); + mPictureHeight = newRect.height(); + } + QgsComposerItem::setSceneRect( newRect ); emit itemChanged(); } @@ -225,13 +291,16 @@ void QgsComposerPicture::setPictureRotation( double r ) { mPictureRotation = r; - //find largest scaling of picture with this rotation which fits in item - QSizeF currentPictureSize = pictureSize(); - QRectF rotatedImageRect = largestRotatedRectWithinBounds( QRectF( 0, 0, currentPictureSize.width(), currentPictureSize.height() ), rect(), mPictureRotation ); - mPictureWidth = rotatedImageRect.width(); - mPictureHeight = rotatedImageRect.height(); + if ( mResizeMode == Zoom ) + { + //find largest scaling of picture with this rotation which fits in item + QSizeF currentPictureSize = pictureSize(); + QRectF rotatedImageRect = largestRotatedRectWithinBounds( QRectF( 0, 0, currentPictureSize.width(), currentPictureSize.height() ), rect(), mPictureRotation ); + mPictureWidth = rotatedImageRect.width(); + mPictureHeight = rotatedImageRect.height(); + update(); + } - update(); emit pictureRotationChanged( mPictureRotation ); } @@ -264,6 +333,18 @@ void QgsComposerPicture::setRotationMap( int composerMapId ) emit pictureRotationChanged( mPictureRotation ); } +void QgsComposerPicture::setResizeMode( QgsComposerPicture::ResizeMode mode ) +{ + mResizeMode = mode; + if ( mode == QgsComposerPicture::ZoomResizeFrame || mode == QgsComposerPicture::FrameToImageSize + || ( mode == QgsComposerPicture::Zoom && mPictureRotation != 0 ) ) + { + //call set scene rect to force item to resize to fit picture + setSceneRect( QRectF( pos().x(), pos().y(), rect().width(), rect().height() ) ); + } + update(); +} + QString QgsComposerPicture::pictureFile() const { return mSourceFile.fileName(); @@ -279,6 +360,7 @@ bool QgsComposerPicture::writeXML( QDomElement& elem, QDomDocument & doc ) const composerPictureElem.setAttribute( "file", QgsProject::instance()->writePath( mSourceFile.fileName() ) ); composerPictureElem.setAttribute( "pictureWidth", QString::number( mPictureWidth ) ); composerPictureElem.setAttribute( "pictureHeight", QString::number( mPictureHeight ) ); + composerPictureElem.setAttribute( "resizeMode", QString::number(( int )mResizeMode ) ); //rotation composerPictureElem.setAttribute( "pictureRotation", QString::number( mPictureRotation ) ); @@ -305,6 +387,7 @@ bool QgsComposerPicture::readXML( const QDomElement& itemElem, const QDomDocumen mPictureWidth = itemElem.attribute( "pictureWidth", "10" ).toDouble(); mPictureHeight = itemElem.attribute( "pictureHeight", "10" ).toDouble(); + mResizeMode = QgsComposerPicture::ResizeMode( itemElem.attribute( "resizeMode", "0" ).toInt() ); QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" ); if ( composerItemList.size() > 0 ) diff --git a/src/core/composer/qgscomposerpicture.h b/src/core/composer/qgscomposerpicture.h index 07a53548fa6..5fd7f11b7f8 100644 --- a/src/core/composer/qgscomposerpicture.h +++ b/src/core/composer/qgscomposerpicture.h @@ -31,6 +31,23 @@ class CORE_EXPORT QgsComposerPicture: public QgsComposerItem { Q_OBJECT public: + + enum ResizeMode + { + Zoom, + Stretch, + Clip, + ZoomResizeFrame, + FrameToImageSize + }; + + enum Mode //SVG or raster graphic format + { + SVG, + RASTER, + Unknown + }; + QgsComposerPicture( QgsComposition *composition ); ~QgsComposerPicture(); @@ -76,6 +93,10 @@ class CORE_EXPORT QgsComposerPicture: public QgsComposerItem /**True if the rotation is taken from a map item*/ bool useRotationMap() const {return mRotationMap;} + /**Returns the resize mode used for drawing the picture within the composer item + */ + ResizeMode resizeMode() const { return mResizeMode;}; + /**Calculates width and hight of the picture (in mm) such that it fits into the item frame with the given rotation * @deprecated Use bool QgsComposerItem::imageSizeConsideringRotation( double& width, double& height, double rotation ) * instead @@ -92,6 +113,8 @@ class CORE_EXPORT QgsComposerPicture: public QgsComposerItem */ Q_DECL_DEPRECATED void sizeChangedByRotation( double& width, double& height ); + Mode mode() const { return mMode; }; + public slots: /**Sets the picture rotation within the item bounds. This does not affect the item rectangle, only the way the picture is drawn within the item. @@ -104,19 +127,16 @@ class CORE_EXPORT QgsComposerPicture: public QgsComposerItem @note this function was added in version 2.1*/ virtual void setPictureRotation( double r ); + /**Sets the resize mode used for drawing the picture within the item bounds + @note this function was added in version 2.1*/ + virtual void setResizeMode( ResizeMode mode ); + signals: /**Is emitted on picture rotation change*/ void pictureRotationChanged( double newRotation ); private: - enum Mode //SVG or raster graphic format - { - SVG, - RASTER, - Unknown - }; - //default constructor is forbidden QgsComposerPicture(); /**Calculates bounding rect for svg file (mSourcefile) such that aspect ratio is correct*/ @@ -142,6 +162,8 @@ class CORE_EXPORT QgsComposerPicture: public QgsComposerItem double mPictureWidth; /**Height of the picture (in mm)*/ double mPictureHeight; + + ResizeMode mResizeMode; }; #endif diff --git a/src/ui/qgscomposerpicturewidgetbase.ui b/src/ui/qgscomposerpicturewidgetbase.ui index 622938a02cd..c2e30cb1960 100644 --- a/src/ui/qgscomposerpicturewidgetbase.ui +++ b/src/ui/qgscomposerpicturewidgetbase.ui @@ -105,6 +105,42 @@ + + + + Resize mode + + + + + + + + Zoom + + + + + Stretch + + + + + Clip + + + + + Zoom and resize frame + + + + + Resize frame to image size + + + + @@ -211,7 +247,7 @@ - + Image rotation