Commit fac42c38 authored by Boudewijn Rempt's avatar Boudewijn Rempt

Nearly working shape caching

This implements caching of shapes using QPixmapCache following the
design of QGraphicsView's caching, with some flake-specific changes.
(Flake can show different sets of shapes on different canvases, while
QGraphicsView shows the same scene on different paint devices.)

Current problems:

* when editing, shapes get overpainted incorrectly
* in kword, the margin offset seems wrong, so the page is broken
off at the wrong place.
* editing text doesn't work atm

svn path=/branches/work/koffice-essen/; revision=1192455
parent 5044a84a
......@@ -36,6 +36,7 @@ set(flake_SRCS
KoShapeContainerDefaultModel.cpp
KoShapeGroup.cpp
KoShapeManagerPaintingStrategy.cpp
KoShapeManagerCachedPaintingStrategy.cpp
KoShapeManager.cpp
KoShapePainter.cpp
KoFrameShape.cpp
......@@ -257,6 +258,8 @@ install(
KoLoadingShapeUpdater.h
KoShapeConfigWidgetBase.h
KoShapeManager.h
KoShapeManagerPaintingStrategy.h
KoShapeManagerCachedPaintingStrategy.h
KoShapeRegistry.h
KoShapeSavingContext.h
KoShapeUserData.h
......
......@@ -3,6 +3,7 @@
Copyright (C) 2006-2010 Thomas Zander <zander@kde.org>
Copyright (C) 2006-2010 Thorsten Zachmann <zachmann@kde.org>
Copyright (C) 2007-2009 Jan Hambrecht <jaham@gmx.net>
CopyRight (C) 2010 Boudewijn Rempt <boud@kogmbh.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
......@@ -17,7 +18,7 @@
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
Boston, MA 02110-1301, USA.
*/
#include "KoShape.h"
......@@ -64,30 +65,43 @@
#include <QList>
#include <QMap>
#include <QByteArray>
#include <QPixmapCache>
#include <kdebug.h>
#include <limits>
// KoShapeCache
/// Empty all cached pixmaps from the pixmap cache
void KoShapeCache::purge()
{
qDeleteAll(deviceData);
deviceData.clear();
}
// KoShapePrivate
KoShapePrivate::KoShapePrivate(KoShape *shape)
: size(50, 50),
parent(0),
userData(0),
appData(0),
fill(0),
border(0),
q_ptr(shape),
shadow(0),
filterEffectStack(0),
transparency(0.0),
zIndex(0),
visible(true),
printable(true),
geometryProtected(false),
keepAspect(false),
selectable(true),
detectCollision(false),
protectContent(false)
parent(0),
userData(0),
appData(0),
fill(0),
border(0),
q_ptr(shape),
shadow(0),
filterEffectStack(0),
transparency(0.0),
zIndex(0),
visible(true),
printable(true),
geometryProtected(false),
keepAspect(false),
selectable(true),
detectCollision(false),
protectContent(false),
cacheMode(KoShape::ScaledCache),
cache(0)
{
connectors.append(QPointF(0.5, 0.0));
connectors.append(QPointF(1.0, 0.5));
......@@ -137,16 +151,16 @@ void KoShapePrivate::updateBorder()
QSizeF inner = q->size();
// update left
q->update(QRectF(-insets.left, -insets.top, insets.left,
inner.height() + insets.top + insets.bottom));
inner.height() + insets.top + insets.bottom));
// update top
q->update(QRectF(-insets.left, -insets.top,
inner.width() + insets.left + insets.right, insets.top));
inner.width() + insets.left + insets.right, insets.top));
// update right
q->update(QRectF(inner.width(), -insets.top, insets.right,
inner.height() + insets.top + insets.bottom));
inner.height() + insets.top + insets.bottom));
// update bottom
q->update(QRectF(-insets.left, inner.height(),
inner.width() + insets.left + insets.right, insets.bottom));
inner.width() + insets.left + insets.right, insets.bottom));
}
void KoShapePrivate::addShapeManager(KoShapeManager *manager)
......@@ -157,7 +171,37 @@ void KoShapePrivate::addShapeManager(KoShapeManager *manager)
void KoShapePrivate::removeShapeManager(KoShapeManager *manager)
{
shapeManagers.remove(manager);
if (cacheMode == KoShape::ScaledCache) {
if (KoShapeCache *cache = maybeShapeCache()) {
KoShapeCache::DeviceData *deviceData = cache->deviceData.take(manager);
delete deviceData;
}
}
}
KoShapeCache *KoShapePrivate::maybeShapeCache() const
{
return cache;
}
KoShapeCache *KoShapePrivate::shapeCache() const
{
if (!cache) {
const_cast<KoShapePrivate *>(this)->cache = new KoShapeCache;
}
return cache;
}
void KoShapePrivate::removeShapeCache()
{
if (cache) {
cache->purge();
delete cache;
cache = 0;
}
}
// static
QString KoShapePrivate::getStyleProperty(const char *property, KoShapeLoadingContext &context)
{
......@@ -175,7 +219,7 @@ QString KoShapePrivate::getStyleProperty(const char *property, KoShapeLoadingCon
// ======== KoShape
KoShape::KoShape()
: d_ptr(new KoShapePrivate(this))
: d_ptr(new KoShapePrivate(this))
{
notifyChanged();
}
......@@ -189,6 +233,7 @@ KoShape::~KoShape()
{
Q_D(KoShape);
d->shapeChanged(Deleted);
d->removeShapeCache();
delete d_ptr;
}
......@@ -206,7 +251,7 @@ void KoShape::paintDecorations(QPainter &painter, const KoViewConverter &convert
painter.setPen(pen);
painter.setBrush(Qt::NoBrush);
for (int i = 0; i < d->connectors.size(); ++i)
{
{
QPointF p = converter.documentToView(d->connectors[ i ]);
painter.drawLine(QPointF(p.x() - 2, p.y() + 2), QPointF(p.x() + 2, p.y() - 2));
painter.drawLine(QPointF(p.x() + 2, p.y() + 2), QPointF(p.x() - 2, p.y() - 2));
......@@ -455,20 +500,52 @@ int KoShape::zIndex() const
void KoShape::update() const
{
Q_D(const KoShape);
if (d->cacheMode != NoCache) {
KoShapeCache *cache = d->shapeCache();
foreach(KoShapeCache::DeviceData *data, cache->deviceData.values()) {
data->allExposed = true;
data->exposed.clear();
}
}
if (!d->shapeManagers.empty()) {
QRectF rect(boundingRect());
foreach(KoShapeManager * manager, d->shapeManagers)
foreach(KoShapeManager * manager, d->shapeManagers) {
manager->update(rect, this, true);
}
}
}
void KoShape::update(const QRectF &shape) const
void KoShape::update(const QRectF &rect) const
{
if (rect.isEmpty() && !rect.isNull()) {
return;
}
Q_D(const KoShape);
if (d->cacheMode != NoCache) {
KoShapeCache *cache = d->shapeCache();
foreach(KoShapeCache::DeviceData *data, cache->deviceData.values()) {
if (!data->allExposed) {
if (rect.isNull()) {
data->allExposed = true;
data->exposed.clear();
}
else {
data->exposed.append(rect);
}
}
}
}
if (!d->shapeManagers.empty() && isVisible()) {
QRectF rect(absoluteTransformation(0).mapRect(shape));
QRectF rc(absoluteTransformation(0).mapRect(rect));
foreach(KoShapeManager * manager, d->shapeManagers) {
manager->update(rect);
manager->update(rc);
}
}
}
......@@ -540,6 +617,10 @@ void KoShape::notifyChanged()
foreach(KoShapeManager * manager, d->shapeManagers) {
manager->notifyShapeChanged(this);
}
KoShapeCache *cache = d->maybeShapeCache();
if (cache) {
cache->purge();
}
}
void KoShape::setUserData(KoShapeUserData *userData)
......@@ -703,7 +784,14 @@ void KoShape::setZIndex(int zIndex)
void KoShape::setVisible(bool on)
{
Q_D(KoShape);
if (d->visible == on) return;
d->visible = on;
if (d->visible) {
KoShapeCache *cache = d->maybeShapeCache();
if (cache) {
cache->purge();
}
}
}
bool KoShape::isVisible(bool recursive) const
......@@ -966,8 +1054,8 @@ QString KoShape::saveStyle(KoGenStyle &style, KoShapeSavingContext &context) con
qreal bottom = parent()->size().height() - size().height() - top;
style.addProperty("fo:clip", QString("rect(%1pt, %2pt, %3pt, %4pt)")
.arg(top, 10, 'f').arg(right, 10, 'f')
.arg(bottom, 10, 'f').arg(left, 10, 'f'));
.arg(top, 10, 'f').arg(right, 10, 'f')
.arg(bottom, 10, 'f').arg(left, 10, 'f'));
}
......@@ -1110,8 +1198,8 @@ KoShapeBackground *KoShape::loadOdfFill(KoShapeLoadingContext &context) const
bg = new KoPatternBackground(context.imageCollection());
#ifndef NWORKAROUND_ODF_BUGS
} else if (fill.isEmpty()) {
bg = KoOdfWorkaround::fixBackgroundColor(this, context);
return bg;
bg = KoOdfWorkaround::fixBackgroundColor(this, context);
return bg;
#endif
} else {
return 0;
......@@ -1267,8 +1355,8 @@ QTransform KoShape::parseOdfTransform(const QString &transform)
QTransform m;
if (params.count() >= 6) {
m.setMatrix(params[0].toDouble(), params[1].toDouble(), 0,
params[2].toDouble(), params[3].toDouble(), 0,
KoUnit::parseValue(params[4]), KoUnit::parseValue(params[5]), 1);
params[2].toDouble(), params[3].toDouble(), 0,
KoUnit::parseValue(params[4]), KoUnit::parseValue(params[5]), 1);
}
matrix = matrix * m;
}
......@@ -1349,9 +1437,9 @@ void KoShape::saveOdfAttributes(KoShapeSavingContext &context, int attributes) c
context.xmlWriter().addAttribute("svg:y", QString("%1pt").arg(matrix.dy()));
} else {
QString m = QString("matrix(%1 %2 %3 %4 %5pt %6pt)")
.arg(matrix.m11()).arg(matrix.m12())
.arg(matrix.m21()).arg(matrix.m22())
.arg(matrix.dx()) .arg(matrix.dy());
.arg(matrix.m11()).arg(matrix.m12())
.arg(matrix.m21()).arg(matrix.m22())
.arg(matrix.dx()) .arg(matrix.dy());
context.xmlWriter().addAttribute("draw:transform", m);
}
}
......@@ -1528,3 +1616,22 @@ KoShapePrivate *KoShape::priv()
Q_D(KoShape);
return d;
}
KoShape::CacheMode KoShape::cacheMode() const
{
Q_D(const KoShape);
return d->cacheMode;
}
void KoShape::setCacheMode(CacheMode mode)
{
Q_D(KoShape);
d->cacheMode = mode;
if (mode == NoCache) {
d->removeShapeCache();
} else {
KoShapeCache *cache = d->shapeCache();
// Reset old cache
cache->purge();
}
}
......@@ -126,6 +126,12 @@ public:
ChildChanged ///< a child of a container was changed/removed. This is propagated to all parents
};
/// See QGraphicsItem::CacheMode
enum CacheMode {
NoCache, ///< no cache -- the default
ScaledCache, ///< cache at every zoomlevel
};
/**
* @brief Constructor
*/
......@@ -492,9 +498,9 @@ public:
* normalized.
* <p>This method will return immediately and only request a repaint. Successive calls
* will be merged into an appropriate repaint action.
* @param shape the rectangle (in pt) to queue for repaint.
* @param rect the rectangle (in pt) to queue for repaint.
*/
virtual void update(const QRectF &shape) const;
virtual void update(const QRectF &rect) const;
/**
* This is a method used to sort a list using the STL sorting methods.
......@@ -731,11 +737,11 @@ public:
* In this case it can be shown on screen probably partially but it should really not be printed
* until it is fully done processing.
* Warning! This method can be blocking for a long time
* @param asynchronous If set to true the processing will can take place in a different thread and the
* function will not block until the shape is finised.
* In case of printing Flake will call this method from a non-main thread and only
* @param asynchronous If set to true the processing will can take place in a different thread and the
* function will not block until the shape is finised.
* In case of printing Flake will call this method from a non-main thread and only
* start printing it when the in case of printing method returned.
* If set to false the processing needs to be done synchronously and will
* If set to false the processing needs to be done synchronously and will
* block until the result is finished.
*/
virtual void waitUntilReady(const KoViewConverter &converter, bool asynchronous = true) const;
......@@ -873,6 +879,17 @@ public:
*/
KoShapePrivate *priv();
/**
* Returns the cache mode for this shape. The default mode is NoCache (i.e.,
* cache is disabled and all painting is immediate).
*/
CacheMode cacheMode() const;
/**
* Set the shape's cache mode to @param mode.
*/
void setCacheMode(CacheMode cacheMode);
protected:
/// constructor
KoShape(KoShapePrivate &);
......@@ -955,6 +972,8 @@ protected:
/// return the current matrix that contains the rotation/scale/position of this shape
QTransform transform() const;
friend class KoShapeManagerCachedPaintingStrategy;
KoShapePrivate *d_ptr;
private:
......
......@@ -476,7 +476,7 @@ void KoShapeManager::paintShape(KoShape *shape, QPainter &painter, const KoViewC
painter.drawImage(clippingOffset, imageBuffers.value(lastEffect->output()));
painter.restore();
}
if (! forPrint) {
if (!forPrint) {
painter.setRenderHint(QPainter::Antialiasing, false);
shape->paintDecorations(painter, converter, d->canvas);
}
......@@ -634,4 +634,9 @@ void KoShapeManager::setPaintingStrategy(KoShapeManagerPaintingStrategy *strateg
d->strategy = strategy;
}
KoCanvasBase *KoShapeManager::canvas()
{
return d->canvas;
}
#include <KoShapeManager.moc>
......@@ -73,6 +73,7 @@ public:
KoShapeManager(KoCanvasBase *canvas, const QList<KoShape *> &shapes);
virtual ~KoShapeManager();
/**
* Remove all previously owned shapes and make the argument list the new shapes
* to be managed by this manager.
......@@ -202,6 +203,11 @@ signals:
void selectionContentChanged();
private:
friend class KoShapeManagerPaintingStrategy;
KoCanvasBase *canvas();
class Private;
Private * const d;
Q_PRIVATE_SLOT(d, void updateTree())
......
This diff is collapsed.
/* This file is part of the KDE project
* Copyright (C) 2010 KO GmbH <boud@kogmbh.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or ( at your option ) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOSHAPEMANAGERCACHEDPAINTINGSTRATEGY_H
#define KOSHAPEMANAGERCACHEDPAINTINGSTRATEGY_H
#include "KoShape.h"
#include "KoShapeManagerPaintingStrategy.h"
#include "flake_export.h"
/**
* The cached painting strategy uses QPixmapCache to paint shapes
* using cached QPixmaps whereever possible. Setting this strategy
* on your KoShapeManager can improve scrolling performance at the
* cost of extra memory usage.
*/
class FLAKE_EXPORT KoShapeManagerCachedPaintingStrategy : public KoShapeManagerPaintingStrategy
{
public:
KoShapeManagerCachedPaintingStrategy(KoShapeManager *shapeManager);
virtual ~KoShapeManagerCachedPaintingStrategy();
void paint(KoShape *shape, QPainter &painter, const KoViewConverter &converter, bool forPrint);
void adapt(KoShape *shape, QRectF &rect);
};
#endif // KOSHAPEMANAGERCACHEDPAINTINGSTRATEGY_H
......@@ -23,6 +23,7 @@
#include "flake_export.h"
class KoCanvasBase;
class KoShapeManager;
class KoShape;
class KoViewConverter;
......
/* This file is part of the KDE project
* Copyright (C) 2009 Thomas Zander <zander@kde.org>
* Copyright (C) 2010 Boudewijn Rempt <boud@kogmbh.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
......@@ -21,6 +22,48 @@
#include "KoShape.h"
#include <QPixmapCache>
#include <QPoint>
#include <QPaintDevice>
#include <KoCanvasBase.h>
/**
* Contains the information needed for caching the contents of a shape.
*
* There are two possibilities: one cache made at 100% zoom at 72 dpi,
* or a cache for every zoomlevel at the current resolution. The cache
* is one big QPixMap for the entire shape.
*/
class KoShapeCache
{
public:
struct DeviceData {
DeviceData() : allExposed(true) {}
~DeviceData()
{
QPixmapCache::remove(key);
}
// index into the global pixmap cache
QPixmapCache::Key key;
// List of logical exposed rects in document coordinates
// These are the rects that are queued for updating, not
// the rects that have already been painted.
QVector<QRectF> exposed;
// true if the whole shape has been exposed and asked to redraw
bool allExposed;
};
// Map the cache to the canvas it is shown on (in QGraphicsView this is QPaintDevice)
QMap<KoShapeManager *, DeviceData *> deviceData;
// Empty cache
void purge();
};
class KoShapePrivate
{
public:
......@@ -80,6 +123,25 @@ public:
int detectCollision : 1;
int protectContent : 1;
KoShape::CacheMode cacheMode;
KoShapeCache *cache;
/**
* @return the shape cache if there is one, else 0
*/
KoShapeCache *maybeShapeCache() const;
/**
* return the shape cache if there is one, else create on
*/
KoShapeCache *shapeCache() const;
/**
* purge and remove the shape cache
*/
void removeShapeCache();
Q_DECLARE_PUBLIC(KoShape)
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment