Commit 3f617761 authored by Emmet O'Neill's avatar Emmet O'Neill

Improved responsiveness of moving large SVG objects.

Added a KisSignalCompressor to the KisShapeLayerCanvas to reduce the
number of updates.

BUG:399363
parent 9d7d5967
......@@ -747,7 +747,6 @@ void KoShape::update() const
void KoShape::updateAbsolute(const QRectF &rect) const
{
if (rect.isEmpty() && !rect.isNull()) {
return;
}
......@@ -755,7 +754,7 @@ void KoShape::updateAbsolute(const QRectF &rect) const
Q_D(const KoShape);
if (!d->shapeManagers.empty() && isVisible()) {
Q_FOREACH (KoShapeManager * manager, d->shapeManagers) {
Q_FOREACH (KoShapeManager *manager, d->shapeManagers) {
manager->update(rect);
}
}
......
......@@ -61,9 +61,7 @@ struct KoInsets;
class KoShapeBackground;
class KisHandlePainterHelper;
/**
*
* Base class for all flake shapes. Shapes extend this class
* to allow themselves to be manipulated. This class just represents
* a graphical shape in the document and can be manipulated by some default
......
......@@ -36,7 +36,6 @@ class KoCanvasBase;
class KoPointerEvent;
class KoShapePaintingContext;
class QPainter;
class QPointF;
class QRectF;
......
......@@ -96,6 +96,7 @@ void KoShapeStroke::Private::paintBorder(KoShape *shape, QPainter &painter, cons
KoPathShape *pathShape = dynamic_cast<KoPathShape *>(shape);
if (pathShape) {
QPainterPath path = pathShape->pathStroke(pen);
painter.fillPath(path, pen.brush());
if (!pathShape->hasMarkers()) return;
......
......@@ -126,6 +126,7 @@ KisShapeLayerCanvas::KisShapeLayerCanvas(KisShapeLayer *parent, KisImageWSP imag
: KisShapeLayerCanvasBase(parent, image)
, m_projection(0)
, m_parentLayer(parent)
, m_canvasUpdateCompressor(new KisSignalCompressor(500, KisSignalCompressor::FIRST_INACTIVE, this))
, m_asyncUpdateSignalCompressor(100, KisSignalCompressor::FIRST_INACTIVE)
{
/**
......@@ -135,7 +136,9 @@ KisShapeLayerCanvas::KisShapeLayerCanvas(KisShapeLayer *parent, KisImageWSP imag
m_shapeManager->addShape(parent, KoShapeManager::AddWithoutRepaint);
m_shapeManager->selection()->setActiveLayer(parent);
connect(this, SIGNAL(forwardRepaint()), SLOT(repaint()), Qt::QueuedConnection);
connect(this, SIGNAL(forwardRepaint()), m_canvasUpdateCompressor, SLOT(start()));
connect(m_canvasUpdateCompressor, SIGNAL(timeout()), this, SLOT(repaint()));
connect(&m_asyncUpdateSignalCompressor, SIGNAL(timeout()), SLOT(slotStartAsyncRepaint()));
setImage(image);
......@@ -224,7 +227,7 @@ void KisShapeLayerCanvas::updateCanvas(const QVector<QRectF> &region)
*
* Here we just avoid the most obvious conflict of threads:
*
* 1) If the layer if modified by a non-gui (worker) thread, use a spontaneous jobs
* 1) If the layer is modified by a non-gui (worker) thread, use a spontaneous jobs
* to rerender the canvas. The job will be executed (almost) exclusively and it is
* the responsibility of the worker thread to add a barrier to wait until this job is
* completed, and not try to access the shapes concurrently.
......@@ -272,43 +275,44 @@ void KisShapeLayerCanvas::slotImageSizeChanged()
void KisShapeLayerCanvas::repaint()
{
QRect r;
QRect repaintRect;
{
QMutexLocker locker(&m_dirtyRegionMutex);
r = m_dirtyRegion.boundingRect();
repaintRect = m_dirtyRegion.boundingRect();
m_dirtyRegion = QRegion();
}
if (r.isEmpty()) return;
if (repaintRect.isEmpty()) {
return;
}
// Crop the update rect by the image bounds. We keep the cache consistent
// by tracking the size of the image in slotImageSizeChanged()
r = r.intersected(m_parentLayer->image()->bounds());
repaintRect = repaintRect.intersected(m_parentLayer->image()->bounds());
QImage image(r.width(), r.height(), QImage::Format_ARGB32);
QImage image(repaintRect.width(), repaintRect.height(), QImage::Format_ARGB32);
image.fill(0);
QPainter p(&image);
QPainter tempPainter(&image);
p.setRenderHint(QPainter::Antialiasing);
p.setRenderHint(QPainter::TextAntialiasing);
p.translate(-r.x(), -r.y());
p.setClipRect(r);
tempPainter.setRenderHint(QPainter::Antialiasing);
tempPainter.setRenderHint(QPainter::TextAntialiasing);
tempPainter.translate(-repaintRect.x(), -repaintRect.y());
tempPainter.setClipRect(repaintRect);
#ifdef DEBUG_REPAINT
QColor color = QColor(random() % 255, random() % 255, random() % 255);
p.fillRect(r, color);
tempPainter.fillRect(r, color);
#endif
m_shapeManager->paint(p, *m_viewConverter, false);
p.end();
m_shapeManager->paint(tempPainter, *m_viewConverter, false);
tempPainter.end();
KisPaintDeviceSP dev = new KisPaintDevice(m_projection->colorSpace());
dev->convertFromQImage(image, 0);
KisPainter::copyAreaOptimized(r.topLeft(), dev, m_projection, QRect(QPoint(), r.size()));
KisPainter::copyAreaOptimized(repaintRect.topLeft(), dev, m_projection, QRect(QPoint(), repaintRect.size()));
m_parentLayer->setDirty(r);
m_parentLayer->setDirty(repaintRect);
m_hasChangedWhileBeingInvisible |= !m_parentLayer->visible(true);
}
......@@ -347,4 +351,3 @@ void KisShapeLayerCanvas::rerenderAfterBeingInvisible()
m_hasChangedWhileBeingInvisible = false;
resetCache();
}
......@@ -35,6 +35,7 @@ class KUndo2Command;
class QWidget;
class KoUnit;
class KisImageViewConverter;
class KisSignalCompressor;
class KisShapeLayerCanvasBase : public KoCanvasBase
......@@ -113,6 +114,7 @@ Q_SIGNALS:
private:
KisPaintDeviceSP m_projection;
KisShapeLayer *m_parentLayer;
KisSignalCompressor *m_canvasUpdateCompressor;
KisThreadSafeSignalCompressor m_asyncUpdateSignalCompressor;
volatile bool m_hasUpdateInCompressor = false;
......
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