Commit 5afd7216 authored by Tusooa Zhu's avatar Tusooa Zhu 🅱

Fix a crash when editing calligraphic shape

We have to put a lock there because when editing calligraphic shape,
KoPathTool::paint() from the gui thread fetches the points from the shape,
but the points may at the same time be deleted by karbonSimplifyPath()
in the image thread.
parent ab5967d9
......@@ -213,6 +213,8 @@ set(kritaflake_SRCS
FlakeDebug.cpp
tests/MockShapes.cpp
KisLockableCanvas.cpp
)
ki18n_wrap_ui(kritaflake_SRCS
......
/*
* Copyright (c) 2019 Tusooa Zhu <tusooa@vista.aero>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "KisLockableCanvas.h"
#include <QMutex>
struct KisLockableCanvas::Private
{
QMutex mutex;
};
KisLockableCanvas::Locker::Locker(KisLockableCanvas *canvas)
: m_canvas(canvas)
{
m_canvas->lock();
}
KisLockableCanvas::Locker::~Locker()
{
m_canvas->unlock();
}
KisLockableCanvas::KisLockableCanvas(KoShapeControllerBase *shapeController, KoCanvasResourceProvider *sharedResourceManager)
: KoCanvasBase(shapeController, sharedResourceManager)
, m_d(new Private)
{
}
KisLockableCanvas::~KisLockableCanvas() = default;
void KisLockableCanvas::lock()
{
m_d->mutex.lock();
}
void KisLockableCanvas::unlock()
{
m_d->mutex.unlock();
}
/*
* Copyright (c) 2019 Tusooa Zhu <tusooa@vista.aero>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KISLOCKABLECANVAS_H
#define KISLOCKABLECANVAS_H
#include <KoCanvasBase.h>
class KRITAFLAKE_EXPORT KisLockableCanvas : public KoCanvasBase
{
public:
KisLockableCanvas(KoShapeControllerBase *shapeController, KoCanvasResourceProvider *sharedResourceManager = 0);
~KisLockableCanvas() override;
void lock();
void unlock();
class Locker
{
public:
Locker(KisLockableCanvas *canvas);
~Locker();
private:
KisLockableCanvas *m_canvas;
};
private:
struct Private;
QScopedPointer<Private> m_d;
};
#endif // KISLOCKABLECANVAS_H
......@@ -682,5 +682,14 @@ KoCanvasBase *KoShapeManager::canvas()
return d->canvas;
}
KisLockableCanvas::Locker *KoShapeManager::obtainLock()
{
KisLockableCanvas *lockableCanvas = dynamic_cast<KisLockableCanvas *>(d->canvas);
if (lockableCanvas) {
return new KisLockableCanvas::Locker(lockableCanvas);
}
return 0;
}
//have to include this because of Q_PRIVATE_SLOT
#include "moc_KoShapeManager.cpp"
......@@ -25,6 +25,7 @@
#include <QList>
#include <QObject>
#include <QSet>
#include <KisLockableCanvas.h>
#include "KoFlake.h"
#include "kritaflake_export.h"
......@@ -202,6 +203,9 @@ Q_SIGNALS:
/// emitted when any object changed (moved/rotated etc)
void contentChanged();
public:
KisLockableCanvas::Locker *obtainLock();
private:
KoCanvasBase *canvas();
......
......@@ -47,7 +47,7 @@
//#define DEBUG_REPAINT
KisShapeLayerCanvasBase::KisShapeLayerCanvasBase(KisShapeLayer *parent, KisImageWSP image)
: KoCanvasBase(0)
: KisLockableCanvas(0)
, m_viewConverter(new KisImageViewConverter(image))
, m_shapeManager(new KoShapeManager(this))
, m_selectedShapesProxy(new KoSelectedShapesProxySimple(m_shapeManager.data()))
......
......@@ -20,7 +20,7 @@
#include <QMutex>
#include <QRegion>
#include <KoCanvasBase.h>
#include <KisLockableCanvas.h>
#include <kis_types.h>
#include "kis_thread_safe_signal_compressor.h"
......@@ -37,7 +37,7 @@ class KoUnit;
class KisImageViewConverter;
class KisShapeLayerCanvasBase : public KoCanvasBase
class KisShapeLayerCanvasBase : public KisLockableCanvas
{
public:
......
......@@ -28,7 +28,7 @@
#include <kis_shape_controller.h>
KisShapeSelectionCanvas::KisShapeSelectionCanvas(KoShapeControllerBase *shapeController)
: KoCanvasBase(shapeController)
: KisLockableCanvas(shapeController)
, m_shapeManager(new KoShapeManager(this))
, m_selectedShapesProxy(new KoSelectedShapesProxySimple(m_shapeManager.data()))
{
......
......@@ -20,7 +20,7 @@
#define KIS_SHAPE_SELECTION_CANVAS_H
#include <QScopedPointer>
#include <KoCanvasBase.h>
#include <KisLockableCanvas.h>
#include <kis_types.h>
......@@ -35,7 +35,7 @@ class KisShapeController;
/**
* Dummy canvas just to have a shapemanager for the shape selection
*/
class KisShapeSelectionCanvas : public KoCanvasBase
class KisShapeSelectionCanvas : public KisLockableCanvas
{
Q_OBJECT
public:
......
......@@ -24,6 +24,7 @@
#include <kis_post_execution_undo_adapter.h>
#include <KisStrokesFacade.h>
#include <KisRunnableStrokeJobData.h>
#include <KoShapeManager.h>
struct KisNodeReplaceBasedStrokeStrategy::Private
{
......@@ -34,6 +35,7 @@ struct KisNodeReplaceBasedStrokeStrategy::Private
KisNodeSP affectedNode;
KisNodeSP originalState;
KisPostExecutionUndoAdapter *postExecUndoAdapter;
KoShapeManager *shapeManager;
bool nodeChanged = false;
class NodeReplaceCommand : public KUndo2Command
......@@ -77,10 +79,12 @@ void KisNodeReplaceBasedStrokeStrategy::Private::NodeReplaceCommand::redo()
m_affectedNode->copyFromNode(m_editedState.data());
}
KisNodeReplaceBasedStrokeStrategy::KisNodeReplaceBasedStrokeStrategy(QString id, KisResourcesSnapshotSP resources,
KisNodeReplaceBasedStrokeStrategy::KisNodeReplaceBasedStrokeStrategy(QString id, KisCanvas2 *canvas,
const KUndo2MagicString &name)
: KisRunnableBasedStrokeStrategy(id, name)
, m_d(new Private(resources))
, m_d(new Private(new KisResourcesSnapshot(canvas->image().toStrongRef(),
canvas->imageView()->currentNode(),
canvas->resourceManager())))
{
enableJob(KisSimpleStrokeStrategy::JOB_INIT);
enableJob(KisSimpleStrokeStrategy::JOB_FINISH, /* enable = */ true,
......@@ -88,16 +92,7 @@ KisNodeReplaceBasedStrokeStrategy::KisNodeReplaceBasedStrokeStrategy(QString id,
enableJob(KisSimpleStrokeStrategy::JOB_CANCEL);
enableJob(KisSimpleStrokeStrategy::JOB_DOSTROKE, /* enable = */ true,
KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE);
}
KisNodeReplaceBasedStrokeStrategy::KisNodeReplaceBasedStrokeStrategy(QString id, KisCanvas2 *canvas,
const KUndo2MagicString &name)
: KisNodeReplaceBasedStrokeStrategy(id,
new KisResourcesSnapshot(canvas->image().toStrongRef(),
canvas->imageView()->currentNode(),
canvas->resourceManager()),
name)
{
m_d->shapeManager = canvas->shapeManager();
}
KisNodeReplaceBasedStrokeStrategy::KisNodeReplaceBasedStrokeStrategy(const KisNodeReplaceBasedStrokeStrategy &rhs)
......@@ -114,6 +109,12 @@ void KisNodeReplaceBasedStrokeStrategy::initStrokeCallback()
{
}
void KisNodeReplaceBasedStrokeStrategy::doStrokeCallback(KisStrokeJobData *data)
{
QScopedPointer<KisLockableCanvas::Locker> lock(m_d->shapeManager->obtainLock());
KisRunnableBasedStrokeStrategy::doStrokeCallback(data);
}
void KisNodeReplaceBasedStrokeStrategy::cancelStrokeCallback()
{
m_d->affectedNode->copyFromNode(m_d->originalState.data());
......
......@@ -32,14 +32,13 @@ class KisStrokesFacade;
class KRITAUI_EXPORT KisNodeReplaceBasedStrokeStrategy : public KisRunnableBasedStrokeStrategy
{
public:
KisNodeReplaceBasedStrokeStrategy(QString id, KisResourcesSnapshotSP resources,
const KUndo2MagicString &name = KUndo2MagicString());
KisNodeReplaceBasedStrokeStrategy(QString id, KisCanvas2 *canvas,
const KUndo2MagicString &name = KUndo2MagicString());
KisNodeReplaceBasedStrokeStrategy(const KisNodeReplaceBasedStrokeStrategy &rhs);
~KisNodeReplaceBasedStrokeStrategy();
void initStrokeCallback() override;
void doStrokeCallback(KisStrokeJobData *data) override;
void cancelStrokeCallback() override;
void finishStrokeCallback() override;
......
......@@ -694,6 +694,7 @@ void KoPathTool::paint(QPainter& painter, const KoViewConverter& converter)
{
Q_D(KoToolBase);
QScopedPointer<KisLockableCanvas::Locker> locker(canvas()->shapeManager()->obtainLock());
Q_FOREACH (KoPathShape* shape, m_pointSelection.selectedShapes()) {
KisHandlePainterHelper helper =
KoShape::createHandlePainterHelper(&painter, shape, converter, m_handleRadius);
......
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