[layouts] Avoid rasterizing the whole layout when only a single item has opacity set

Instead, only rasterize that one item and pre-apply it's opacity
to the rasterized version. This keeps all the rest of the layout
content as vectors/text.
This commit is contained in:
Nyall Dawson 2018-12-05 16:59:48 +10:00
parent 7f4123bfdd
commit d08ecc2b52
14 changed files with 81 additions and 16 deletions

View File

@ -238,6 +238,13 @@ Base class for graphical items within a :py:class:`QgsLayout`.
UndoCustomCommand,
};
enum Flag
{
FlagOverridesPaint,
};
typedef QFlags<QgsLayoutItem::Flag> Flags;
explicit QgsLayoutItem( QgsLayout *layout, bool manageZValue = true );
%Docstring
Constructor for QgsLayoutItem, with the specified parent ``layout``.
@ -279,6 +286,13 @@ upon creation.
.. seealso:: :py:func:`id`
.. seealso:: :py:func:`setId`
%End
virtual Flags itemFlags() const;
%Docstring
Returns the item's flags, which indicate how the item behaves.
.. versionadded:: 3.4.3
%End
QString id() const;
@ -1166,6 +1180,9 @@ Applies any present data defined size overrides to the specified layout ``size``
};
QFlags<QgsLayoutItem::Flag> operator|(QgsLayoutItem::Flag f1, QFlags<QgsLayoutItem::Flag> f2);

View File

@ -67,6 +67,8 @@ The caller takes responsibility for deleting the returned object.
virtual QIcon icon() const;
virtual QgsLayoutItem::Flags itemFlags() const;
virtual QString displayName() const;

View File

@ -41,6 +41,8 @@ Constructor for QgsLayoutItemMap, with the specified parent ``layout``.
virtual QIcon icon() const;
virtual QgsLayoutItem::Flags itemFlags() const;
void assignFreeId();
%Docstring

View File

@ -286,8 +286,6 @@ Forces a recalculation of the picture's frame size
virtual void refreshDataDefinedProperty( QgsLayoutObject::DataDefinedProperty property = QgsLayoutObject::AllProperties );
virtual bool containsAdvancedEffects() const;
signals:
void pictureRotationChanged( double newRotation );

View File

@ -27,6 +27,7 @@
#include "qgslayoutundostack.h"
#include "qgslayoutpagecollection.h"
#include "qgslayoutitempage.h"
#include "qgsimageoperation.h"
#include <QPainter>
#include <QStyleOptionGraphicsItem>
#include <QUuid>
@ -123,6 +124,11 @@ int QgsLayoutItem::type() const
return QgsLayoutItemRegistry::LayoutItem;
}
QgsLayoutItem::Flags QgsLayoutItem::itemFlags() const
{
return nullptr;
}
void QgsLayoutItem::setId( const QString &id )
{
if ( id == mId )
@ -326,6 +332,8 @@ void QgsLayoutItem::paint( QPainter *painter, const QStyleOptionGraphicsItem *it
drawFrame( context );
p.end();
QgsImageOperation::multiplyOpacity( image, mEvaluatedOpacity );
painter->save();
// scale painter from mm to dots
painter->scale( 1.0 / context.scaleFactor(), 1.0 / context.scaleFactor() );
@ -854,7 +862,9 @@ void QgsLayoutItem::setBlendMode( const QPainter::CompositionMode mode )
void QgsLayoutItem::setItemOpacity( double opacity )
{
mOpacity = opacity;
refreshOpacity( true );
refreshOpacity( mItemCachedImage.isNull() );
if ( !mItemCachedImage.isNull() )
invalidateCache();
}
bool QgsLayoutItem::excludeFromExports() const
@ -870,12 +880,13 @@ void QgsLayoutItem::setExcludeFromExports( bool exclude )
bool QgsLayoutItem::containsAdvancedEffects() const
{
return false;
return itemFlags() & Flag::FlagOverridesPaint ? false : mEvaluatedOpacity < 1.0;
}
bool QgsLayoutItem::requiresRasterization() const
{
return itemOpacity() < 1.0 || blendMode() != QPainter::CompositionMode_SourceOver;
return ( itemFlags() & Flag::FlagOverridesPaint && itemOpacity() < 1.0 ) ||
blendMode() != QPainter::CompositionMode_SourceOver;
}
double QgsLayoutItem::estimatedFrameBleed() const
@ -1360,7 +1371,14 @@ void QgsLayoutItem::refreshOpacity( bool updateItem )
double opacity = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::Opacity, createExpressionContext(), mOpacity * 100.0 );
// Set the QGraphicItem's opacity
setOpacity( opacity / 100.0 );
mEvaluatedOpacity = opacity / 100.0;
if ( itemFlags() & QgsLayoutItem::FlagOverridesPaint )
{
// item handles it's own painting, so it won't use the built-in opacity handling in QgsLayoutItem::paint, and
// we have to rely on QGraphicsItem opacity to handle this
setOpacity( mEvaluatedOpacity );
}
if ( updateItem )
{

View File

@ -287,6 +287,16 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
UndoCustomCommand, //!< Base id for plugin based item undo commands
};
/**
* Flags for controlling how an item behaves.
* \since QGIS 3.4.3
*/
enum Flag
{
FlagOverridesPaint = 1 << 1, //!< Item overrides the default layout item painting method
};
Q_DECLARE_FLAGS( Flags, Flag )
/**
* Constructor for QgsLayoutItem, with the specified parent \a layout.
*
@ -324,6 +334,12 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
*/
virtual QString uuid() const { return mUuid; }
/**
* Returns the item's flags, which indicate how the item behaves.
* \since QGIS 3.4.3
*/
virtual Flags itemFlags() const;
/**
* Returns the item's ID name. This is not necessarily unique, and duplicate ID names may exist
* for a layout.
@ -1109,6 +1125,7 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
//! Item opacity, between 0 and 1
double mOpacity = 1.0;
double mEvaluatedOpacity = 1.0;
QImage mItemCachedImage;
double mItemCacheDpi = -1;
@ -1137,7 +1154,6 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
void preparePainter( QPainter *painter );
bool shouldDrawAntialiased() const;
bool shouldDrawDebugRect() const;
QSizeF applyMinimumSize( QSizeF targetSize );
QSizeF applyFixedSize( QSizeF targetSize );
QgsLayoutPoint applyDataDefinedPosition( const QgsLayoutPoint &position );
@ -1157,6 +1173,8 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
friend class QgsCompositionConverter;
};
Q_DECLARE_OPERATORS_FOR_FLAGS( QgsLayoutItem::Flags )
#endif //QGSLAYOUTITEM_H

View File

@ -64,6 +64,11 @@ QIcon QgsLayoutItemLegend::icon() const
return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemLegend.svg" ) );
}
QgsLayoutItem::Flags QgsLayoutItemLegend::itemFlags() const
{
return QgsLayoutItem::FlagOverridesPaint;
}
void QgsLayoutItemLegend::paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget )
{
if ( !painter )

View File

@ -78,6 +78,7 @@ class CORE_EXPORT QgsLayoutItemLegend : public QgsLayoutItem
int type() const override;
QIcon icon() const override;
QgsLayoutItem::Flags itemFlags() const override;
//Overridden to show legend title
QString displayName() const override;

View File

@ -77,6 +77,11 @@ QIcon QgsLayoutItemMap::icon() const
return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemMap.svg" ) );
}
QgsLayoutItem::Flags QgsLayoutItemMap::itemFlags() const
{
return QgsLayoutItem::FlagOverridesPaint;
}
void QgsLayoutItemMap::assignFreeId()
{
if ( !mLayout )

View File

@ -71,6 +71,7 @@ class CORE_EXPORT QgsLayoutItemMap : public QgsLayoutItem
int type() const override;
QIcon icon() const override;
QgsLayoutItem::Flags itemFlags() const override;
/**
* Sets the map id() to a number not yet used in the layout. The existing id() is kept if it is not in use.

View File

@ -669,14 +669,6 @@ void QgsLayoutItemPicture::refreshDataDefinedProperty( const QgsLayoutObject::Da
QgsLayoutItem::refreshDataDefinedProperty( property );
}
bool QgsLayoutItemPicture::containsAdvancedEffects() const
{
if ( QgsLayoutItem::containsAdvancedEffects() )
return true;
return mMode == FormatSVG && itemOpacity() < 1.0;
}
void QgsLayoutItemPicture::setPicturePath( const QString &path )
{
mSourcePath = path;

View File

@ -259,7 +259,6 @@ class CORE_EXPORT QgsLayoutItemPicture: public QgsLayoutItem
void recalculateSize();
void refreshDataDefinedProperty( QgsLayoutObject::DataDefinedProperty property = QgsLayoutObject::AllProperties ) override;
bool containsAdvancedEffects() const override;
signals:
//! Is emitted on picture rotation change

View File

@ -27,6 +27,11 @@ QgsResidualPlotItem::QgsResidualPlotItem( QgsLayout *layout )
setBackgroundEnabled( false );
}
QgsLayoutItem::Flags QgsResidualPlotItem::itemFlags() const
{
return QgsLayoutItem::FlagOverridesPaint;
}
void QgsResidualPlotItem::paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget )
{
Q_UNUSED( itemStyle );

View File

@ -30,6 +30,8 @@ class QgsResidualPlotItem: public QgsLayoutItem
public:
explicit QgsResidualPlotItem( QgsLayout *layout );
QgsLayoutItem::Flags itemFlags() const override;
//! \brief Reimplementation of QCanvasItem::paint
void paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget ) override;