[graphics/krita/krita/5.2] libs/ui: [string freeze exception] Fix a crash when trying to clear scrapad while it is busy

Dmitry Kazakov null at kde.org
Thu Oct 24 13:12:17 BST 2024


Git commit 29c864fe540b918a5b9fd5d86426f81bbbcf8d16 by Dmitry Kazakov.
Committed on 24/10/2024 at 12:11.
Pushed by dkazakov into branch 'krita/5.2'.

[string freeze exception] Fix a crash when trying to clear scrapad while it is busy

BUG:488800
CC:kimageshop at kde.org
CC:kde-i18n-doc at kde.org

M  +5    -0    libs/ui/kis_async_action_feedback.cpp
M  +24   -0    libs/ui/kis_async_action_feedback.h
M  +113  -41   libs/ui/widgets/kis_scratch_pad.cpp

https://invent.kde.org/graphics/krita/-/commit/29c864fe540b918a5b9fd5d86426f81bbbcf8d16

diff --git a/libs/ui/kis_async_action_feedback.cpp b/libs/ui/kis_async_action_feedback.cpp
index 149c90961e0..92df3a694e1 100644
--- a/libs/ui/kis_async_action_feedback.cpp
+++ b/libs/ui/kis_async_action_feedback.cpp
@@ -73,3 +73,8 @@ void KisAsyncActionFeedback::waitForMutexLikeImpl(std::unique_ptr<MutexLikeBase>
 
     mutex->unlock();
 }
+
+QString KisAsyncActionFeedback::DefaultWaitingMessageCallback::operator()() const
+{
+    return i18nc("progress dialog message when the user has to wait for the image to become unlocked", "Waiting for the action to complete...");
+}
diff --git a/libs/ui/kis_async_action_feedback.h b/libs/ui/kis_async_action_feedback.h
index 3007e4baa99..6df0f9d734d 100644
--- a/libs/ui/kis_async_action_feedback.h
+++ b/libs/ui/kis_async_action_feedback.h
@@ -16,6 +16,11 @@ class QMutex;
 
 class KisAsyncActionFeedback
 {
+private:
+    struct DefaultWaitingMessageCallback {
+        QString operator()() const;
+    };
+
 public:
     KisAsyncActionFeedback(const QString &message, QWidget *parent);
     ~KisAsyncActionFeedback();
@@ -28,6 +33,25 @@ public:
         waitForMutexLikeImpl(std::make_unique<MutexLike<Mutex>>(mutex));
     }
 
+    template<typename Mutex, typename CallbackFunc = DefaultWaitingMessageCallback>
+    class MutexWrapper : public Mutex
+    {
+    public:
+        template<typename ...Args>
+        MutexWrapper(Args ...args)
+            : Mutex(args...)
+        {
+        }
+
+        void lock() {
+            if (!Mutex::try_lock()) {
+                KisAsyncActionFeedback f(CallbackFunc{}(), 0);
+                f.waitForMutex(static_cast<Mutex&>(*this));
+                Mutex::lock();
+            }
+        }
+    };
+
 private:
 
     /**
diff --git a/libs/ui/widgets/kis_scratch_pad.cpp b/libs/ui/widgets/kis_scratch_pad.cpp
index be72890e39d..8be76deee56 100644
--- a/libs/ui/widgets/kis_scratch_pad.cpp
+++ b/libs/ui/widgets/kis_scratch_pad.cpp
@@ -38,6 +38,43 @@
 #include "kis_node_graph_listener.h"
 #include "kis_transaction.h"
 
+#include <KisAdaptedLock.h>
+#include <kis_async_action_feedback.h>
+
+namespace {
+class KisUpdateSchedulerLockAdapter
+{
+public:
+    KisUpdateSchedulerLockAdapter(KisUpdateScheduler *scheduler)
+        : m_scheduler(scheduler)
+    {
+    }
+
+    void lock() {
+        m_scheduler->barrierLock();
+    }
+
+    bool try_lock() {
+        return m_scheduler->tryBarrierLock();
+    }
+
+    void unlock() {
+        m_scheduler->unlock();
+    }
+
+private:
+    KisUpdateScheduler *m_scheduler;
+};
+
+/**
+ * Define an adapted lock that has application-wide busy-wait feedback
+ */
+KIS_DECLARE_ADAPTED_LOCK(KisUpdateSchedulerLockWithFeedback,
+                         KisAsyncActionFeedback::MutexWrapper<KisUpdateSchedulerLockAdapter>)
+
+}
+
+
 class KisScratchPadNodeListener : public KisNodeGraphListener
 {
 public:
@@ -533,10 +570,15 @@ void KisScratchPad::paintCustomImage(const QImage& loadedImage)
     KisPaintDeviceSP device = new KisPaintDevice(paintDevice->colorSpace());
     device->convertFromQImage(scaledImage, 0);
 
-    KisPainter painter(paintDevice);
-    painter.beginTransaction();
-    painter.bitBlt(overlayRect.topLeft(), device, imageRect);
-    painter.deleteTransaction();
+    {
+        KisUpdateSchedulerLockWithFeedback l(m_updateScheduler);
+        KisPainter painter(paintDevice);
+        painter.beginTransaction();
+        painter.bitBlt(overlayRect.topLeft(), device, imageRect);
+        painter.deleteTransaction();
+    }
+
+
     update();
 }
 
@@ -555,10 +597,14 @@ void KisScratchPad::loadScratchpadImage(QImage image)
     KisPaintDeviceSP device = new KisPaintDevice(paintDevice->colorSpace());
     device->convertFromQImage(image, 0);
 
-    KisPainter painter(paintDevice);
-    painter.beginTransaction();
-    painter.bitBlt(imageSize.topLeft(), device, imageSize);
-    painter.deleteTransaction();
+    {
+        KisUpdateSchedulerLockWithFeedback l(m_updateScheduler);
+        KisPainter painter(paintDevice);
+        painter.beginTransaction();
+        painter.bitBlt(imageSize.topLeft(), device, imageSize);
+        painter.deleteTransaction();
+    }
+
     update();
 }
 
@@ -586,10 +632,14 @@ void KisScratchPad::paintPresetImage()
     KisPaintDeviceSP device = new KisPaintDevice(paintDevice->colorSpace());
     device->convertFromQImage(scaledImage, 0);
 
-    KisPainter painter(paintDevice);
-    painter.beginTransaction();
-    painter.bitBlt(overlayRect.topLeft(), device, imageRect);
-    painter.deleteTransaction();
+    {
+        KisUpdateSchedulerLockWithFeedback l(m_updateScheduler);
+        KisPainter painter(paintDevice);
+        painter.beginTransaction();
+        painter.bitBlt(overlayRect.topLeft(), device, imageRect);
+        painter.deleteTransaction();
+    }
+
     update();
 }
 
@@ -606,10 +656,15 @@ void KisScratchPad::fillDefault()
     if(!m_paintLayer) return;
     KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice();
 
-    KisTransaction t(paintDevice);
-    paintDevice->setDefaultPixel(m_defaultColor);
-    paintDevice->clear();
-    t.end();
+    {
+        KisUpdateSchedulerLockWithFeedback l(m_updateScheduler);
+
+        KisTransaction t(paintDevice);
+        paintDevice->setDefaultPixel(m_defaultColor);
+        paintDevice->clear();
+        t.end();
+    }
+
     update();
 }
 
@@ -621,10 +676,15 @@ void KisScratchPad::fillTransparent() {
     KoColor transparentColor(transQColor, KoColorSpaceRegistry::instance()->rgb8());
     transparentColor.setOpacity(0.0);
 
-    KisTransaction t(paintDevice);
-    paintDevice->setDefaultPixel(transparentColor);
-    paintDevice->clear();
-    t.end();
+    {
+        KisUpdateSchedulerLockWithFeedback l(m_updateScheduler);
+
+        KisTransaction t(paintDevice);
+        paintDevice->setDefaultPixel(transparentColor);
+        paintDevice->clear();
+        t.end();
+    }
+
     update();
 }
 
@@ -641,21 +701,25 @@ void KisScratchPad::fillGradient()
     KoAbstractGradientSP gradient = m_resourceProvider->currentGradient();
     QRect gradientRect = widgetToDocument().mapRect(rect());
 
-    KisTransaction t(paintDevice);
-
-    paintDevice->clear();
 
-    KisGradientPainter painter(paintDevice);
-    painter.setGradient(gradient);
-    painter.setGradientShape(KisGradientPainter::GradientShapeLinear);
-    painter.paintGradient(gradientRect.topLeft(),
-                          gradientRect.bottomRight(),
-                          KisGradientPainter::GradientRepeatNone,
-                          0.2, false,
-                          gradientRect.left(), gradientRect.top(),
-                          gradientRect.width(), gradientRect.height());
+    {
+        KisUpdateSchedulerLockWithFeedback l(m_updateScheduler);
+        KisTransaction t(paintDevice);
+
+        paintDevice->clear();
+
+        KisGradientPainter painter(paintDevice);
+        painter.setGradient(gradient);
+        painter.setGradientShape(KisGradientPainter::GradientShapeLinear);
+        painter.paintGradient(gradientRect.topLeft(),
+                            gradientRect.bottomRight(),
+                            KisGradientPainter::GradientRepeatNone,
+                            0.2, false,
+                            gradientRect.left(), gradientRect.top(),
+                            gradientRect.width(), gradientRect.height());
+        t.end();
+    }
 
-    t.end();
     update();
 }
 
@@ -664,10 +728,14 @@ void KisScratchPad::fillBackground()
     if(!m_paintLayer) return;
     KisPaintDeviceSP paintDevice = m_paintLayer->paintDevice();
 
-    KisTransaction t(paintDevice);
-    paintDevice->setDefaultPixel(m_resourceProvider->bgColor());
-    paintDevice->clear();
-    t.end();
+    {
+        KisUpdateSchedulerLockWithFeedback l(m_updateScheduler);
+        KisTransaction t(paintDevice);
+        paintDevice->setDefaultPixel(m_resourceProvider->bgColor());
+        paintDevice->clear();
+        t.end();
+    }
+
     update();
 }
 
@@ -678,10 +746,14 @@ void KisScratchPad::fillLayer()
 
     QRect sourceRect(0, 0, paintDevice->exactBounds().width(), paintDevice->exactBounds().height());
 
-    KisPainter painter(paintDevice);
-    painter.beginTransaction();
-    painter.bitBlt(QPoint(0, 0), m_resourceProvider->currentImage()->projection(), sourceRect);
-    painter.deleteTransaction();
+    {
+        KisUpdateSchedulerLockWithFeedback l(m_updateScheduler);
+        KisPainter painter(paintDevice);
+        painter.beginTransaction();
+        painter.bitBlt(QPoint(0, 0), m_resourceProvider->currentImage()->projection(), sourceRect);
+        painter.deleteTransaction();
+    }
+
     update();
 }
 


More information about the kde-i18n-doc mailing list