mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-15 00:02:52 -04:00
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.
This commit is contained in:
parent
436710a177
commit
38cbbe23aa
@ -20,6 +20,8 @@
|
||||
#include <QPainter>
|
||||
#include <QStyleOptionGraphicsItem>
|
||||
|
||||
#define CACHE_SIZE_LIMIT 5000
|
||||
|
||||
QgsLayoutItem::QgsLayoutItem( QgsLayout *layout )
|
||||
: QgsLayoutObject( layout )
|
||||
, QGraphicsRectItem( 0 )
|
||||
@ -44,23 +46,89 @@ void QgsLayoutItem::paint( QPainter *painter, const QStyleOptionGraphicsItem *it
|
||||
}
|
||||
|
||||
//TODO - remember to disable saving/restoring on graphics view!!
|
||||
painter->save();
|
||||
preparePainter( painter );
|
||||
|
||||
if ( shouldDrawDebugRect() )
|
||||
{
|
||||
drawDebugRect( painter );
|
||||
return;
|
||||
}
|
||||
|
||||
double destinationDpi = itemStyle->matrix.m11() * 25.4;
|
||||
bool useImageCache = true;
|
||||
|
||||
if ( useImageCache )
|
||||
{
|
||||
double widthInPixels = boundingRect().width() * itemStyle->matrix.m11();
|
||||
double heightInPixels = boundingRect().height() * itemStyle->matrix.m11();
|
||||
|
||||
// limit size of image for better performance
|
||||
double scale = 1.0;
|
||||
if ( widthInPixels > CACHE_SIZE_LIMIT || heightInPixels > CACHE_SIZE_LIMIT )
|
||||
{
|
||||
if ( widthInPixels > heightInPixels )
|
||||
{
|
||||
scale = widthInPixels / CACHE_SIZE_LIMIT;
|
||||
widthInPixels = CACHE_SIZE_LIMIT;
|
||||
heightInPixels /= scale;
|
||||
}
|
||||
else
|
||||
{
|
||||
scale = heightInPixels / CACHE_SIZE_LIMIT;
|
||||
heightInPixels = CACHE_SIZE_LIMIT;
|
||||
widthInPixels /= scale;
|
||||
}
|
||||
destinationDpi = destinationDpi / scale;
|
||||
}
|
||||
|
||||
if ( !mItemCachedImage.isNull() && qgsDoubleNear( mItemCacheDpi, destinationDpi ) )
|
||||
{
|
||||
// can reuse last cached image
|
||||
QgsRenderContext context = QgsLayoutUtils::createRenderContextForMap( nullptr, painter, destinationDpi );
|
||||
painter->save();
|
||||
preparePainter( painter );
|
||||
double cacheScale = destinationDpi / mItemCacheDpi;
|
||||
painter->scale( cacheScale / context.scaleFactor(), cacheScale / context.scaleFactor() );
|
||||
painter->drawImage( boundingRect().x() * context.scaleFactor() / cacheScale,
|
||||
boundingRect().y() * context.scaleFactor() / cacheScale, mItemCachedImage );
|
||||
painter->restore();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
mItemCacheDpi = destinationDpi;
|
||||
|
||||
mItemCachedImage = QImage( widthInPixels, heightInPixels, QImage::Format_ARGB32 );
|
||||
mItemCachedImage.fill( Qt::transparent );
|
||||
mItemCachedImage.setDotsPerMeterX( 1000 * destinationDpi * 25.4 );
|
||||
mItemCachedImage.setDotsPerMeterY( 1000 * destinationDpi * 25.4 );
|
||||
QPainter p( &mItemCachedImage );
|
||||
|
||||
preparePainter( &p );
|
||||
QgsRenderContext context = QgsLayoutUtils::createRenderContextForMap( nullptr, &p, destinationDpi );
|
||||
// painter is already scaled to dots
|
||||
// need to translate so that item origin is at 0,0 in painter coordinates (not bounding rect origin)
|
||||
p.translate( -boundingRect().x() * context.scaleFactor(), -boundingRect().y() * context.scaleFactor() );
|
||||
draw( context, itemStyle );
|
||||
p.end();
|
||||
|
||||
painter->save();
|
||||
// scale painter from mm to dots
|
||||
painter->scale( 1.0 / context.scaleFactor(), 1.0 / context.scaleFactor() );
|
||||
painter->drawImage( boundingRect().x() * context.scaleFactor(),
|
||||
boundingRect().y() * context.scaleFactor(), mItemCachedImage );
|
||||
painter->restore();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
double destinationDpi = itemStyle->matrix.m11() * 25.4;
|
||||
// no caching or flattening
|
||||
painter->save();
|
||||
QgsRenderContext context = QgsLayoutUtils::createRenderContextForMap( nullptr, painter, destinationDpi );
|
||||
// scale painter from mm to dots
|
||||
painter->scale( 1.0 / context.scaleFactor(), 1.0 / context.scaleFactor() );
|
||||
draw( context, itemStyle );
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
void QgsLayoutItem::setReferencePoint( const QgsLayoutItem::ReferencePoint &point )
|
||||
|
@ -252,6 +252,9 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
|
||||
QgsLayoutPoint mItemPosition;
|
||||
double mItemRotation = 0.0;
|
||||
|
||||
QImage mItemCachedImage;
|
||||
double mItemCacheDpi = -1;
|
||||
|
||||
void initConnectionsToLayout();
|
||||
|
||||
//! Prepares a painter by setting rendering flags
|
||||
|
@ -95,6 +95,16 @@ TestLayoutItem::TestLayoutItem( QgsLayout *layout )
|
||||
int s = ( qrand() % ( 200 - 100 + 1 ) ) + 100;
|
||||
int v = ( qrand() % ( 130 - 255 + 1 ) ) + 130;
|
||||
mColor = QColor::fromHsv( h, s, v );
|
||||
|
||||
QgsStringMap properties;
|
||||
properties.insert( QStringLiteral( "color" ), mColor.name() );
|
||||
properties.insert( QStringLiteral( "style" ), QStringLiteral( "solid" ) );
|
||||
properties.insert( QStringLiteral( "style_border" ), QStringLiteral( "solid" ) );
|
||||
properties.insert( QStringLiteral( "color_border" ), QStringLiteral( "black" ) );
|
||||
properties.insert( QStringLiteral( "width_border" ), QStringLiteral( "0.3" ) );
|
||||
properties.insert( QStringLiteral( "joinstyle" ), QStringLiteral( "miter" ) );
|
||||
mShapeStyleSymbol = QgsFillSymbol::createSimple( properties );
|
||||
|
||||
}
|
||||
|
||||
void TestLayoutItem::draw( QgsRenderContext &context, const QStyleOptionGraphicsItem *itemStyle )
|
||||
@ -114,9 +124,15 @@ void TestLayoutItem::draw( QgsRenderContext &context, const QStyleOptionGraphics
|
||||
painter->setBrush( mColor );
|
||||
|
||||
double scale = context.convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters );
|
||||
QRectF r = QRectF( rect().left() * scale, rect().top() * scale,
|
||||
rect().width() * scale, rect().height() * scale );
|
||||
painter->drawRect( r );
|
||||
|
||||
QPolygonF shapePolygon = QPolygonF( QRectF( 0, 0, rect().width() * scale, rect().height() * scale ) );
|
||||
QList<QPolygonF> rings; //empty list
|
||||
|
||||
mShapeStyleSymbol->startRender( context );
|
||||
mShapeStyleSymbol->renderPolygon( shapePolygon, &rings, nullptr, context );
|
||||
mShapeStyleSymbol->stopRender( context );
|
||||
|
||||
// painter->drawRect( r );
|
||||
painter->restore();
|
||||
stack.end( context );
|
||||
}
|
||||
|
@ -29,6 +29,7 @@
|
||||
class QgsLayout;
|
||||
class QgsLayoutView;
|
||||
class QgsLayoutItem;
|
||||
class QgsFillSymbol;
|
||||
|
||||
/**
|
||||
* \ingroup core
|
||||
@ -271,8 +272,10 @@ class TestLayoutItem : public QgsLayoutItem
|
||||
|
||||
private:
|
||||
QColor mColor;
|
||||
QgsFillSymbol *mShapeStyleSymbol = nullptr;
|
||||
};
|
||||
|
||||
|
||||
///@endcond
|
||||
#endif
|
||||
|
||||
|
@ -86,7 +86,8 @@ void QgsLayoutViewToolAddItem::layoutReleaseEvent( QgsLayoutViewMouseEvent *even
|
||||
Q_UNUSED( clickOnly );
|
||||
|
||||
QgsLayoutItem *item = QgsApplication::layoutItemRegistry()->createItem( mItemType, layout() );
|
||||
item->setRect( rect );
|
||||
item->attemptResize( QgsLayoutSize( rect.width(), rect.height(), QgsUnitTypes::LayoutMillimeters ) );
|
||||
item->attemptMove( QgsLayoutPoint( rect.left(), rect.top(), QgsUnitTypes::LayoutMillimeters ) );
|
||||
layout()->addItem( item );
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user