Skip to content

Commit 38cbbe2

Browse files
committedJul 18, 2017
Implement a cache for item content renders
Speeds up redraw of items, making use of layout designer much faster with slow to redraw items. This will also make it possible to use live effects on layout items without killing performance of the designer.
1 parent 436710a commit 38cbbe2

File tree

5 files changed

+100
-9
lines changed

5 files changed

+100
-9
lines changed
 

‎src/core/layout/qgslayoutitem.cpp

Lines changed: 73 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
#include <QPainter>
2121
#include <QStyleOptionGraphicsItem>
2222

23+
#define CACHE_SIZE_LIMIT 5000
24+
2325
QgsLayoutItem::QgsLayoutItem( QgsLayout *layout )
2426
: QgsLayoutObject( layout )
2527
, QGraphicsRectItem( 0 )
@@ -44,23 +46,89 @@ void QgsLayoutItem::paint( QPainter *painter, const QStyleOptionGraphicsItem *it
4446
}
4547

4648
//TODO - remember to disable saving/restoring on graphics view!!
47-
painter->save();
48-
preparePainter( painter );
4949

5050
if ( shouldDrawDebugRect() )
5151
{
5252
drawDebugRect( painter );
53+
return;
54+
}
55+
56+
double destinationDpi = itemStyle->matrix.m11() * 25.4;
57+
bool useImageCache = true;
58+
59+
if ( useImageCache )
60+
{
61+
double widthInPixels = boundingRect().width() * itemStyle->matrix.m11();
62+
double heightInPixels = boundingRect().height() * itemStyle->matrix.m11();
63+
64+
// limit size of image for better performance
65+
double scale = 1.0;
66+
if ( widthInPixels > CACHE_SIZE_LIMIT || heightInPixels > CACHE_SIZE_LIMIT )
67+
{
68+
if ( widthInPixels > heightInPixels )
69+
{
70+
scale = widthInPixels / CACHE_SIZE_LIMIT;
71+
widthInPixels = CACHE_SIZE_LIMIT;
72+
heightInPixels /= scale;
73+
}
74+
else
75+
{
76+
scale = heightInPixels / CACHE_SIZE_LIMIT;
77+
heightInPixels = CACHE_SIZE_LIMIT;
78+
widthInPixels /= scale;
79+
}
80+
destinationDpi = destinationDpi / scale;
81+
}
82+
83+
if ( !mItemCachedImage.isNull() && qgsDoubleNear( mItemCacheDpi, destinationDpi ) )
84+
{
85+
// can reuse last cached image
86+
QgsRenderContext context = QgsLayoutUtils::createRenderContextForMap( nullptr, painter, destinationDpi );
87+
painter->save();
88+
preparePainter( painter );
89+
double cacheScale = destinationDpi / mItemCacheDpi;
90+
painter->scale( cacheScale / context.scaleFactor(), cacheScale / context.scaleFactor() );
91+
painter->drawImage( boundingRect().x() * context.scaleFactor() / cacheScale,
92+
boundingRect().y() * context.scaleFactor() / cacheScale, mItemCachedImage );
93+
painter->restore();
94+
return;
95+
}
96+
else
97+
{
98+
mItemCacheDpi = destinationDpi;
99+
100+
mItemCachedImage = QImage( widthInPixels, heightInPixels, QImage::Format_ARGB32 );
101+
mItemCachedImage.fill( Qt::transparent );
102+
mItemCachedImage.setDotsPerMeterX( 1000 * destinationDpi * 25.4 );
103+
mItemCachedImage.setDotsPerMeterY( 1000 * destinationDpi * 25.4 );
104+
QPainter p( &mItemCachedImage );
105+
106+
preparePainter( &p );
107+
QgsRenderContext context = QgsLayoutUtils::createRenderContextForMap( nullptr, &p, destinationDpi );
108+
// painter is already scaled to dots
109+
// need to translate so that item origin is at 0,0 in painter coordinates (not bounding rect origin)
110+
p.translate( -boundingRect().x() * context.scaleFactor(), -boundingRect().y() * context.scaleFactor() );
111+
draw( context, itemStyle );
112+
p.end();
113+
114+
painter->save();
115+
// scale painter from mm to dots
116+
painter->scale( 1.0 / context.scaleFactor(), 1.0 / context.scaleFactor() );
117+
painter->drawImage( boundingRect().x() * context.scaleFactor(),
118+
boundingRect().y() * context.scaleFactor(), mItemCachedImage );
119+
painter->restore();
120+
}
53121
}
54122
else
55123
{
56-
double destinationDpi = itemStyle->matrix.m11() * 25.4;
124+
// no caching or flattening
125+
painter->save();
57126
QgsRenderContext context = QgsLayoutUtils::createRenderContextForMap( nullptr, painter, destinationDpi );
58127
// scale painter from mm to dots
59128
painter->scale( 1.0 / context.scaleFactor(), 1.0 / context.scaleFactor() );
60129
draw( context, itemStyle );
130+
painter->restore();
61131
}
62-
63-
painter->restore();
64132
}
65133

66134
void QgsLayoutItem::setReferencePoint( const QgsLayoutItem::ReferencePoint &point )

‎src/core/layout/qgslayoutitem.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,9 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
252252
QgsLayoutPoint mItemPosition;
253253
double mItemRotation = 0.0;
254254

255+
QImage mItemCachedImage;
256+
double mItemCacheDpi = -1;
257+
255258
void initConnectionsToLayout();
256259

257260
//! Prepares a painter by setting rendering flags

‎src/core/layout/qgslayoutitemregistry.cpp

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,16 @@ TestLayoutItem::TestLayoutItem( QgsLayout *layout )
9595
int s = ( qrand() % ( 200 - 100 + 1 ) ) + 100;
9696
int v = ( qrand() % ( 130 - 255 + 1 ) ) + 130;
9797
mColor = QColor::fromHsv( h, s, v );
98+
99+
QgsStringMap properties;
100+
properties.insert( QStringLiteral( "color" ), mColor.name() );
101+
properties.insert( QStringLiteral( "style" ), QStringLiteral( "solid" ) );
102+
properties.insert( QStringLiteral( "style_border" ), QStringLiteral( "solid" ) );
103+
properties.insert( QStringLiteral( "color_border" ), QStringLiteral( "black" ) );
104+
properties.insert( QStringLiteral( "width_border" ), QStringLiteral( "0.3" ) );
105+
properties.insert( QStringLiteral( "joinstyle" ), QStringLiteral( "miter" ) );
106+
mShapeStyleSymbol = QgsFillSymbol::createSimple( properties );
107+
98108
}
99109

100110
void TestLayoutItem::draw( QgsRenderContext &context, const QStyleOptionGraphicsItem *itemStyle )
@@ -114,9 +124,15 @@ void TestLayoutItem::draw( QgsRenderContext &context, const QStyleOptionGraphics
114124
painter->setBrush( mColor );
115125

116126
double scale = context.convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters );
117-
QRectF r = QRectF( rect().left() * scale, rect().top() * scale,
118-
rect().width() * scale, rect().height() * scale );
119-
painter->drawRect( r );
127+
128+
QPolygonF shapePolygon = QPolygonF( QRectF( 0, 0, rect().width() * scale, rect().height() * scale ) );
129+
QList<QPolygonF> rings; //empty list
130+
131+
mShapeStyleSymbol->startRender( context );
132+
mShapeStyleSymbol->renderPolygon( shapePolygon, &rings, nullptr, context );
133+
mShapeStyleSymbol->stopRender( context );
134+
135+
// painter->drawRect( r );
120136
painter->restore();
121137
stack.end( context );
122138
}

‎src/core/layout/qgslayoutitemregistry.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
class QgsLayout;
3030
class QgsLayoutView;
3131
class QgsLayoutItem;
32+
class QgsFillSymbol;
3233

3334
/**
3435
* \ingroup core
@@ -271,8 +272,10 @@ class TestLayoutItem : public QgsLayoutItem
271272

272273
private:
273274
QColor mColor;
275+
QgsFillSymbol *mShapeStyleSymbol = nullptr;
274276
};
275277

278+
276279
///@endcond
277280
#endif
278281

‎src/gui/layout/qgslayoutviewtooladditem.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@ void QgsLayoutViewToolAddItem::layoutReleaseEvent( QgsLayoutViewMouseEvent *even
8686
Q_UNUSED( clickOnly );
8787

8888
QgsLayoutItem *item = QgsApplication::layoutItemRegistry()->createItem( mItemType, layout() );
89-
item->setRect( rect );
89+
item->attemptResize( QgsLayoutSize( rect.width(), rect.height(), QgsUnitTypes::LayoutMillimeters ) );
90+
item->attemptMove( QgsLayoutPoint( rect.left(), rect.top(), QgsUnitTypes::LayoutMillimeters ) );
9091
layout()->addItem( item );
9192
}
9293

0 commit comments

Comments
 (0)
Please sign in to comment.