Commit fef9ff85 authored by Dmitry Kazakov's avatar Dmitry Kazakov

Fix freezes when changing brush properties/curves

This patch basically makes brush preview to be calculated asynchronously
in a separate worker thread and update preview only on a completion-signal
arrival.

WARNING: this patch can theoretically cause a bug, which will make
         the strokes on canvas be painted in extremely small size
         (< 25px). If it happens, then originalPresetSize recovering
         should be restored.

BUG:410158
parent 48294d3a
......@@ -22,7 +22,7 @@
#include <brushengine/kis_paintop_settings.h>
KisPaintOpConfigWidget::KisPaintOpConfigWidget(QWidget * parent, Qt::WindowFlags f)
: KisConfigWidget(parent, f, 10),
: KisConfigWidget(parent, f, 100),
m_isInsideUpdateCall(0)
{
}
......
......@@ -142,8 +142,8 @@ void KisPaintOpPreset::setSettings(KisPaintOpSettingsSP settings)
m_d->settings->setPreset(KisPaintOpPresetWSP(this));
if (oldOptionsWidget) {
m_d->settings->setOptionsWidget(oldOptionsWidget);
oldOptionsWidget->setConfigurationSafe(m_d->settings);
m_d->settings->setOptionsWidget(oldOptionsWidget);
}
}
......
......@@ -845,6 +845,6 @@ void KisPaintOpPresetsPopup::slotUpdatePresetSettings()
// don't update the live preview if the widget is not visible.
if (m_d->uiWdgPaintOpPresetSettings.liveBrushPreviewView->isVisible()) {
m_d->uiWdgPaintOpPresetSettings.liveBrushPreviewView->setCurrentPreset(m_d->resourceProvider->currentPreset());
m_d->uiWdgPaintOpPresetSettings.liveBrushPreviewView->updateStroke();
m_d->uiWdgPaintOpPresetSettings.liveBrushPreviewView->requestUpdateStroke();
}
}
......@@ -26,9 +26,11 @@
#include "KisAsyncronousStrokeUpdateHelper.h"
#include <kis_brush.h>
KisPresetLivePreviewView::KisPresetLivePreviewView(QWidget *parent): QGraphicsView(parent)
KisPresetLivePreviewView::KisPresetLivePreviewView(QWidget *parent)
: QGraphicsView(parent),
m_updateCompressor(100, KisSignalCompressor::FIRST_ACTIVE)
{
connect(&m_updateCompressor, SIGNAL(timeout()), SLOT(updateStroke()));
}
KisPresetLivePreviewView::~KisPresetLivePreviewView()
......@@ -71,27 +73,37 @@ void KisPresetLivePreviewView::setCurrentPreset(KisPaintOpPresetSP preset)
m_currentPreset = preset;
}
void KisPresetLivePreviewView::updateStroke()
void KisPresetLivePreviewView::requestUpdateStroke()
{
paintBackground();
m_updateCompressor.start();
}
void KisPresetLivePreviewView::updateStroke()
{
// do not paint a stroke if we are any of these engines (they have some issue currently)
if (m_currentPreset->paintOp().id() == "roundmarker" ||
m_currentPreset->paintOp().id() == "experimentbrush" ||
m_currentPreset->paintOp().id() == "duplicate") {
paintBackground();
slotPreviewGenerationCompleted();
return;
}
setupAndPaintStroke();
if (!m_previewGenerationInProgress) {
paintBackground();
setupAndPaintStroke();
} else {
m_updateCompressor.start();
}
}
// crop the layer so a brush stroke won't go outside of the area
m_layer->paintDevice()->crop(0,0, m_layer->image()->width(), m_layer->image()->height());
void KisPresetLivePreviewView::slotPreviewGenerationCompleted()
{
m_previewGenerationInProgress = false;
QImage m_temp_image;
m_temp_image = m_layer->paintDevice()->convertToQImage(0);
m_temp_image = m_layer->paintDevice()->convertToQImage(0, m_image->bounds());
// only add the object once...then just update the pixmap so we can move the preview around
if (!m_sceneImageItem) {
......@@ -99,11 +111,8 @@ void KisPresetLivePreviewView::updateStroke()
} else {
m_sceneImageItem->setPixmap(QPixmap::fromImage(m_temp_image));
}
}
void KisPresetLivePreviewView::paintBackground()
{
// clean up "no preview" text object if it exists. we will add it later if we need it
......@@ -176,6 +185,30 @@ void KisPresetLivePreviewView::paintBackground()
}
}
class NotificationStroke : public QObject, public KisSimpleStrokeStrategy
{
Q_OBJECT
public:
NotificationStroke()
{
setClearsRedoOnStart(false);
this->enableJob(JOB_INIT, true, KisStrokeJobData::BARRIER);
this->enableJob(JOB_CANCEL, true, KisStrokeJobData::BARRIER);
}
void initStrokeCallback() {
emit timeout();
}
void cancelStrokeCallback() {
emit cancelled();
}
Q_SIGNALS:
void timeout();
void cancelled();
};
void KisPresetLivePreviewView::setupAndPaintStroke()
{
// limit the brush stroke size. larger brush strokes just don't look good and are CPU intensive
......@@ -192,7 +225,7 @@ void KisPresetLivePreviewView::setupAndPaintStroke()
KisPaintOpPresetSP proxy_preset = m_currentPreset->clone();
KisPaintOpSettingsSP settings = proxy_preset->settings();
proxy_preset->settings()->setPaintOpSize(previewSize);
settings->setPaintOpSize(previewSize);
int maxTextureSize = 200;
int textureOffsetX = settings->getInt("Texture/Pattern/MaximumOffsetX")*2;
int textureOffsetY = settings->getInt("Texture/Pattern/MaximumOffsetY")*2;
......@@ -202,7 +235,7 @@ void KisPresetLivePreviewView::setupAndPaintStroke()
double result = qreal(maxTextureSize) / maxSize;
settings->setProperty("Texture/Pattern/Scale", result);
}
if (m_currentPreset->paintOp().id() == "spraybrush") {
if (proxy_preset->paintOp().id() == "spraybrush") {
QDomElement element;
QDomDocument d;
......@@ -241,7 +274,7 @@ void KisPresetLivePreviewView::setupAndPaintStroke()
// Preset preview cannot display gradient color source: there is
// no resource manager for KisResourcesSnapshot, therefore gradient is nullptr.
// BUG: 385521 (Selecting "Gradient" in brush editor crashes krita)
if (m_currentPreset->paintOp().id() == "paintbrush") {
if (proxy_preset->paintOp().id() == "paintbrush") {
QString colorSourceType = settings->getString("ColorSource/Type", "plain");
if (colorSourceType == "gradient") {
settings->setProperty("ColorSource/Type", "plain");
......@@ -264,16 +297,10 @@ void KisPresetLivePreviewView::setupAndPaintStroke()
KisStrokeId strokeId = m_image->startStroke(stroke);
//m_brushPreviewPainter->setPaintOpPreset(proxy_preset, m_layer, m_image);
// paint the stroke. The sketchbrush gets a different shape than the others to show how it works
if (m_currentPreset->paintOp().id() == "sketchbrush"
|| m_currentPreset->paintOp().id() == "curvebrush"
|| m_currentPreset->paintOp().id() == "particlebrush") {
if (proxy_preset->paintOp().id() == "sketchbrush"
|| proxy_preset->paintOp().id() == "curvebrush"
|| proxy_preset->paintOp().id() == "particlebrush") {
qreal startX = m_canvasCenterPoint.x() - (this->width()*0.4);
qreal endX = m_canvasCenterPoint.x() + (this->width()*0.4);
qreal middle = m_canvasCenterPoint.y();
......@@ -334,12 +361,21 @@ void KisPresetLivePreviewView::setupAndPaintStroke()
m_image->addJob(strokeId, new KisAsyncronousStrokeUpdateHelper::UpdateData(true));
}
m_image->endStroke(strokeId);
m_image->waitForDone();
m_previewGenerationInProgress = true;
NotificationStroke *notificationStroke = new NotificationStroke();
connect(notificationStroke, SIGNAL(timeout()), SLOT(slotPreviewGenerationCompleted()));
KisStrokeId notificationId = m_image->startStroke(notificationStroke);
m_image->endStroke(notificationId);
// TODO: if we don't have any regressions because of it until 4.2.8, then
// just remove this code.
// even though the brush is cloned, the proxy_preset still has some connection to the original preset which will mess brush sizing
// we need to return brush size to normal.The normal brush sends out a lot of extra signals, so keeping the proxy for now
proxy_preset->settings()->setPaintOpSize(originalPresetSize);
//proxy_preset->settings()->setPaintOpSize(originalPresetSize);
}
#include "kis_preset_live_preview_view.moc"
......@@ -34,6 +34,7 @@
#include <kis_image.h>
#include <kis_types.h>
#include <KoColor.h>
#include "kis_signal_compressor.h"
/**
* Widget for displaying a live brush preview of your
......@@ -65,8 +66,11 @@ public:
* @param preset the current preset from the resource manager
*/
void setCurrentPreset(KisPaintOpPresetSP preset);
void updateStroke();
void requestUpdateStroke();
private Q_SLOTS:
void updateStroke();
void slotPreviewGenerationCompleted();
private:
......@@ -120,6 +124,9 @@ private:
/// do zooming and other things internally if it has changed
float m_currentBrushSize = 1.0;
bool m_previewGenerationInProgress = false;
KisSignalCompressor m_updateCompressor;
/// the range of brush sizes that will control zooming in/out
const float m_minBrushVal = 10.0;
const float m_maxBrushVal = 100.0;
......
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