Skip to content

Commit

Permalink
Implement a cache for item content renders
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
nyalldawson committed Jul 18, 2017
1 parent 436710a commit 38cbbe2
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 9 deletions.
78 changes: 73 additions & 5 deletions src/core/layout/qgslayoutitem.cpp
Expand Up @@ -20,6 +20,8 @@
#include <QPainter>
#include <QStyleOptionGraphicsItem>

#define CACHE_SIZE_LIMIT 5000

QgsLayoutItem::QgsLayoutItem( QgsLayout *layout )
: QgsLayoutObject( layout )
, QGraphicsRectItem( 0 )
Expand All @@ -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 )
Expand Down
3 changes: 3 additions & 0 deletions src/core/layout/qgslayoutitem.h
Expand Up @@ -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
Expand Down
22 changes: 19 additions & 3 deletions src/core/layout/qgslayoutitemregistry.cpp
Expand Up @@ -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 )
Expand All @@ -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 );
}
Expand Down
3 changes: 3 additions & 0 deletions src/core/layout/qgslayoutitemregistry.h
Expand Up @@ -29,6 +29,7 @@
class QgsLayout;
class QgsLayoutView;
class QgsLayoutItem;
class QgsFillSymbol;

/**
* \ingroup core
Expand Down Expand Up @@ -271,8 +272,10 @@ class TestLayoutItem : public QgsLayoutItem

private:
QColor mColor;
QgsFillSymbol *mShapeStyleSymbol = nullptr;
};


///@endcond
#endif

Expand Down
3 changes: 2 additions & 1 deletion src/gui/layout/qgslayoutviewtooladditem.cpp
Expand Up @@ -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 );
}

Expand Down

0 comments on commit 38cbbe2

Please sign in to comment.