Skip to content
Permalink
Browse files

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.
  • Loading branch information
nyalldawson committed Jul 14, 2017
1 parent 436710a commit 38cbbe23aa475f1171007a2b7c99ff539c3fece6
@@ -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 );
}

0 comments on commit 38cbbe2

Please sign in to comment.
You can’t perform that action at this time.