Skip to content

Commit d08ecc2

Browse files
committed
[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.
1 parent 7f4123b commit d08ecc2

14 files changed

+81
-16
lines changed

python/core/auto_generated/layout/qgslayoutitem.sip.in

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,13 @@ Base class for graphical items within a :py:class:`QgsLayout`.
238238
UndoCustomCommand,
239239
};
240240

241+
enum Flag
242+
{
243+
FlagOverridesPaint,
244+
};
245+
typedef QFlags<QgsLayoutItem::Flag> Flags;
246+
247+
241248
explicit QgsLayoutItem( QgsLayout *layout, bool manageZValue = true );
242249
%Docstring
243250
Constructor for QgsLayoutItem, with the specified parent ``layout``.
@@ -279,6 +286,13 @@ upon creation.
279286
.. seealso:: :py:func:`id`
280287

281288
.. seealso:: :py:func:`setId`
289+
%End
290+
291+
virtual Flags itemFlags() const;
292+
%Docstring
293+
Returns the item's flags, which indicate how the item behaves.
294+
295+
.. versionadded:: 3.4.3
282296
%End
283297

284298
QString id() const;
@@ -1166,6 +1180,9 @@ Applies any present data defined size overrides to the specified layout ``size``
11661180

11671181
};
11681182

1183+
QFlags<QgsLayoutItem::Flag> operator|(QgsLayoutItem::Flag f1, QFlags<QgsLayoutItem::Flag> f2);
1184+
1185+
11691186

11701187

11711188

python/core/auto_generated/layout/qgslayoutitemlegend.sip.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ The caller takes responsibility for deleting the returned object.
6767

6868
virtual QIcon icon() const;
6969

70+
virtual QgsLayoutItem::Flags itemFlags() const;
71+
7072
virtual QString displayName() const;
7173

7274

python/core/auto_generated/layout/qgslayoutitemmap.sip.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ Constructor for QgsLayoutItemMap, with the specified parent ``layout``.
4141

4242
virtual QIcon icon() const;
4343

44+
virtual QgsLayoutItem::Flags itemFlags() const;
45+
4446

4547
void assignFreeId();
4648
%Docstring

python/core/auto_generated/layout/qgslayoutitempicture.sip.in

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -286,8 +286,6 @@ Forces a recalculation of the picture's frame size
286286

287287
virtual void refreshDataDefinedProperty( QgsLayoutObject::DataDefinedProperty property = QgsLayoutObject::AllProperties );
288288

289-
virtual bool containsAdvancedEffects() const;
290-
291289

292290
signals:
293291
void pictureRotationChanged( double newRotation );

src/core/layout/qgslayoutitem.cpp

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "qgslayoutundostack.h"
2828
#include "qgslayoutpagecollection.h"
2929
#include "qgslayoutitempage.h"
30+
#include "qgsimageoperation.h"
3031
#include <QPainter>
3132
#include <QStyleOptionGraphicsItem>
3233
#include <QUuid>
@@ -123,6 +124,11 @@ int QgsLayoutItem::type() const
123124
return QgsLayoutItemRegistry::LayoutItem;
124125
}
125126

127+
QgsLayoutItem::Flags QgsLayoutItem::itemFlags() const
128+
{
129+
return nullptr;
130+
}
131+
126132
void QgsLayoutItem::setId( const QString &id )
127133
{
128134
if ( id == mId )
@@ -326,6 +332,8 @@ void QgsLayoutItem::paint( QPainter *painter, const QStyleOptionGraphicsItem *it
326332
drawFrame( context );
327333
p.end();
328334

335+
QgsImageOperation::multiplyOpacity( image, mEvaluatedOpacity );
336+
329337
painter->save();
330338
// scale painter from mm to dots
331339
painter->scale( 1.0 / context.scaleFactor(), 1.0 / context.scaleFactor() );
@@ -854,7 +862,9 @@ void QgsLayoutItem::setBlendMode( const QPainter::CompositionMode mode )
854862
void QgsLayoutItem::setItemOpacity( double opacity )
855863
{
856864
mOpacity = opacity;
857-
refreshOpacity( true );
865+
refreshOpacity( mItemCachedImage.isNull() );
866+
if ( !mItemCachedImage.isNull() )
867+
invalidateCache();
858868
}
859869

860870
bool QgsLayoutItem::excludeFromExports() const
@@ -870,12 +880,13 @@ void QgsLayoutItem::setExcludeFromExports( bool exclude )
870880

871881
bool QgsLayoutItem::containsAdvancedEffects() const
872882
{
873-
return false;
883+
return itemFlags() & Flag::FlagOverridesPaint ? false : mEvaluatedOpacity < 1.0;
874884
}
875885

876886
bool QgsLayoutItem::requiresRasterization() const
877887
{
878-
return itemOpacity() < 1.0 || blendMode() != QPainter::CompositionMode_SourceOver;
888+
return ( itemFlags() & Flag::FlagOverridesPaint && itemOpacity() < 1.0 ) ||
889+
blendMode() != QPainter::CompositionMode_SourceOver;
879890
}
880891

881892
double QgsLayoutItem::estimatedFrameBleed() const
@@ -1360,7 +1371,14 @@ void QgsLayoutItem::refreshOpacity( bool updateItem )
13601371
double opacity = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::Opacity, createExpressionContext(), mOpacity * 100.0 );
13611372

13621373
// Set the QGraphicItem's opacity
1363-
setOpacity( opacity / 100.0 );
1374+
mEvaluatedOpacity = opacity / 100.0;
1375+
1376+
if ( itemFlags() & QgsLayoutItem::FlagOverridesPaint )
1377+
{
1378+
// item handles it's own painting, so it won't use the built-in opacity handling in QgsLayoutItem::paint, and
1379+
// we have to rely on QGraphicsItem opacity to handle this
1380+
setOpacity( mEvaluatedOpacity );
1381+
}
13641382

13651383
if ( updateItem )
13661384
{

src/core/layout/qgslayoutitem.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,16 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
287287
UndoCustomCommand, //!< Base id for plugin based item undo commands
288288
};
289289

290+
/**
291+
* Flags for controlling how an item behaves.
292+
* \since QGIS 3.4.3
293+
*/
294+
enum Flag
295+
{
296+
FlagOverridesPaint = 1 << 1, //!< Item overrides the default layout item painting method
297+
};
298+
Q_DECLARE_FLAGS( Flags, Flag )
299+
290300
/**
291301
* Constructor for QgsLayoutItem, with the specified parent \a layout.
292302
*
@@ -324,6 +334,12 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
324334
*/
325335
virtual QString uuid() const { return mUuid; }
326336

337+
/**
338+
* Returns the item's flags, which indicate how the item behaves.
339+
* \since QGIS 3.4.3
340+
*/
341+
virtual Flags itemFlags() const;
342+
327343
/**
328344
* Returns the item's ID name. This is not necessarily unique, and duplicate ID names may exist
329345
* for a layout.
@@ -1109,6 +1125,7 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
11091125

11101126
//! Item opacity, between 0 and 1
11111127
double mOpacity = 1.0;
1128+
double mEvaluatedOpacity = 1.0;
11121129

11131130
QImage mItemCachedImage;
11141131
double mItemCacheDpi = -1;
@@ -1137,7 +1154,6 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
11371154
void preparePainter( QPainter *painter );
11381155
bool shouldDrawAntialiased() const;
11391156
bool shouldDrawDebugRect() const;
1140-
11411157
QSizeF applyMinimumSize( QSizeF targetSize );
11421158
QSizeF applyFixedSize( QSizeF targetSize );
11431159
QgsLayoutPoint applyDataDefinedPosition( const QgsLayoutPoint &position );
@@ -1157,6 +1173,8 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
11571173
friend class QgsCompositionConverter;
11581174
};
11591175

1176+
Q_DECLARE_OPERATORS_FOR_FLAGS( QgsLayoutItem::Flags )
1177+
11601178
#endif //QGSLAYOUTITEM_H
11611179

11621180

src/core/layout/qgslayoutitemlegend.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ QIcon QgsLayoutItemLegend::icon() const
6464
return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemLegend.svg" ) );
6565
}
6666

67+
QgsLayoutItem::Flags QgsLayoutItemLegend::itemFlags() const
68+
{
69+
return QgsLayoutItem::FlagOverridesPaint;
70+
}
71+
6772
void QgsLayoutItemLegend::paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget )
6873
{
6974
if ( !painter )

src/core/layout/qgslayoutitemlegend.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ class CORE_EXPORT QgsLayoutItemLegend : public QgsLayoutItem
7878

7979
int type() const override;
8080
QIcon icon() const override;
81+
QgsLayoutItem::Flags itemFlags() const override;
8182
//Overridden to show legend title
8283
QString displayName() const override;
8384

src/core/layout/qgslayoutitemmap.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ QIcon QgsLayoutItemMap::icon() const
7777
return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemMap.svg" ) );
7878
}
7979

80+
QgsLayoutItem::Flags QgsLayoutItemMap::itemFlags() const
81+
{
82+
return QgsLayoutItem::FlagOverridesPaint;
83+
}
84+
8085
void QgsLayoutItemMap::assignFreeId()
8186
{
8287
if ( !mLayout )

src/core/layout/qgslayoutitemmap.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ class CORE_EXPORT QgsLayoutItemMap : public QgsLayoutItem
7171

7272
int type() const override;
7373
QIcon icon() const override;
74+
QgsLayoutItem::Flags itemFlags() const override;
7475

7576
/**
7677
* Sets the map id() to a number not yet used in the layout. The existing id() is kept if it is not in use.

src/core/layout/qgslayoutitempicture.cpp

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -669,14 +669,6 @@ void QgsLayoutItemPicture::refreshDataDefinedProperty( const QgsLayoutObject::Da
669669
QgsLayoutItem::refreshDataDefinedProperty( property );
670670
}
671671

672-
bool QgsLayoutItemPicture::containsAdvancedEffects() const
673-
{
674-
if ( QgsLayoutItem::containsAdvancedEffects() )
675-
return true;
676-
677-
return mMode == FormatSVG && itemOpacity() < 1.0;
678-
}
679-
680672
void QgsLayoutItemPicture::setPicturePath( const QString &path )
681673
{
682674
mSourcePath = path;

src/core/layout/qgslayoutitempicture.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,6 @@ class CORE_EXPORT QgsLayoutItemPicture: public QgsLayoutItem
259259
void recalculateSize();
260260

261261
void refreshDataDefinedProperty( QgsLayoutObject::DataDefinedProperty property = QgsLayoutObject::AllProperties ) override;
262-
bool containsAdvancedEffects() const override;
263262

264263
signals:
265264
//! Is emitted on picture rotation change

src/plugins/georeferencer/qgsresidualplotitem.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ QgsResidualPlotItem::QgsResidualPlotItem( QgsLayout *layout )
2727
setBackgroundEnabled( false );
2828
}
2929

30+
QgsLayoutItem::Flags QgsResidualPlotItem::itemFlags() const
31+
{
32+
return QgsLayoutItem::FlagOverridesPaint;
33+
}
34+
3035
void QgsResidualPlotItem::paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget )
3136
{
3237
Q_UNUSED( itemStyle );

src/plugins/georeferencer/qgsresidualplotitem.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ class QgsResidualPlotItem: public QgsLayoutItem
3030
public:
3131
explicit QgsResidualPlotItem( QgsLayout *layout );
3232

33+
QgsLayoutItem::Flags itemFlags() const override;
34+
3335
//! \brief Reimplementation of QCanvasItem::paint
3436
void paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget ) override;
3537

0 commit comments

Comments
 (0)