[calligra] krita: Added an ability to transform layers recursively with a Transform Tool

Dmitry Kazakov dimula73 at gmail.com
Thu Jan 31 15:39:58 UTC 2013


Git commit e30395b251684b8cfdbbcce6606d9e2b0edb30e1 by Dmitry Kazakov.
Committed on 31/01/2013 at 16:16.
Pushed by dkazakov into branch 'master'.

Added an ability to transform layers recursively with a Transform Tool

Now the transform tool supports transforming of a group of layers or
masks recursively in a single pass. You can use it for transforming a
set of layers grouped into a Group Layer or just transform a single
layer with all its masks (e.g. with transparency masks). This option
is enabled by default, but you can disable it by switching a small
button in the transform tool configuration docker. This button has a
icon of a small spider (the tool "crawls" through the layers :) ) [0].

It would be cool, if someone tested it a bit ;)

[0] - you can suggest any other icon for this, actually =)

BUG:297927,314176
CCMAIL:kimageshop at kde.org

M  +5    -4    krita/image/kis_processing_visitor.cpp
M  +4    -2    krita/image/kis_processing_visitor.h
M  +1    -0    krita/plugins/tools/tool_transform2/CMakeLists.txt
M  +56   -40   krita/plugins/tools/tool_transform2/kis_tool_transform.cc
M  +5    -5    krita/plugins/tools/tool_transform2/kis_tool_transform.h
M  +11   -0    krita/plugins/tools/tool_transform2/kis_tool_transform_config_widget.cpp
M  +3    -0    krita/plugins/tools/tool_transform2/kis_tool_transform_config_widget.h
A  +-    --    krita/plugins/tools/tool_transform2/krita_tool_transform_recursive.png
M  +96   -44   krita/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.cpp
M  +31   -15   krita/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.h
M  +35   -16   krita/plugins/tools/tool_transform2/wdg_tool_transform.ui

http://commits.kde.org/calligra/e30395b251684b8cfdbbcce6606d9e2b0edb30e1

diff --git a/krita/image/kis_processing_visitor.cpp b/krita/image/kis_processing_visitor.cpp
index 00e1993..5761544 100644
--- a/krita/image/kis_processing_visitor.cpp
+++ b/krita/image/kis_processing_visitor.cpp
@@ -30,23 +30,24 @@ KisProcessingVisitor::ProgressHelper::ProgressHelper(const KisNode *node)
     if(progressProxy) {
         m_progressUpdater = new KoProgressUpdater(progressProxy);
         m_progressUpdater->start();
-        m_updater = m_progressUpdater->startSubtask();
         m_progressUpdater->moveToThread(node->thread());
     }
     else {
         m_progressUpdater = 0;
-        m_updater = 0;
     }
 }
 
 KisProcessingVisitor::ProgressHelper::~ProgressHelper()
 {
-    m_progressUpdater->deleteLater();
+    if (m_progressUpdater) {
+        m_progressUpdater->deleteLater();
+    }
 }
 
 KoUpdater* KisProcessingVisitor::ProgressHelper::updater() const
 {
-    return m_updater;
+    QMutexLocker l(&m_progressMutex);
+    return m_progressUpdater ? m_progressUpdater->startSubtask() : 0;
 }
 
 
diff --git a/krita/image/kis_processing_visitor.h b/krita/image/kis_processing_visitor.h
index 9984d3c..dc3aef2 100644
--- a/krita/image/kis_processing_visitor.h
+++ b/krita/image/kis_processing_visitor.h
@@ -22,6 +22,8 @@
 #include "krita_export.h"
 #include "kis_shared.h"
 
+#include <QMutex>
+
 class KisNode;
 class KoUpdater;
 class KoProgressUpdater;
@@ -57,7 +59,7 @@ public:
     virtual void visit(KisTransparencyMask *mask, KisUndoAdapter *undoAdapter) = 0;
     virtual void visit(KisSelectionMask *mask, KisUndoAdapter *undoAdapter) = 0;
 
-protected:
+public:
     class ProgressHelper {
     public:
         ProgressHelper(const KisNode *node);
@@ -66,7 +68,7 @@ protected:
         KoUpdater* updater() const;
     private:
         KoProgressUpdater *m_progressUpdater;
-        KoUpdater *m_updater;
+        mutable QMutex m_progressMutex;
     };
 };
 
diff --git a/krita/plugins/tools/tool_transform2/CMakeLists.txt b/krita/plugins/tools/tool_transform2/CMakeLists.txt
index 598c009..7a6c19b 100644
--- a/krita/plugins/tools/tool_transform2/CMakeLists.txt
+++ b/krita/plugins/tools/tool_transform2/CMakeLists.txt
@@ -25,5 +25,6 @@ install(TARGETS kritatooltransform  DESTINATION ${PLUGIN_INSTALL_DIR})
 
 install( FILES  rotate_cursor.xpm
                 krita_tool_transform.png
+                krita_tool_transform_recursive.png
     DESTINATION ${DATA_INSTALL_DIR}/krita/pics)
 install( FILES  kritatooltransform.desktop  DESTINATION ${SERVICES_INSTALL_DIR})
diff --git a/krita/plugins/tools/tool_transform2/kis_tool_transform.cc b/krita/plugins/tools/tool_transform2/kis_tool_transform.cc
index 63300c2..7e14215 100644
--- a/krita/plugins/tools/tool_transform2/kis_tool_transform.cc
+++ b/krita/plugins/tools/tool_transform2/kis_tool_transform.cc
@@ -859,11 +859,11 @@ void KisToolTransform::mousePressEvent(KoPointerEvent *event)
 
     KisImageWSP kisimage = image();
 
-    if (!currentNode() || !currentNode()->paintDevice())
+    if (!currentNode())
         return;
 
     setMode(KisTool::PAINT_MODE);
-    if (kisimage && currentNode()->paintDevice() && event->button() == Qt::LeftButton) {
+    if (kisimage && event->button() == Qt::LeftButton) {
         QPointF mousePos = QPointF(event->point.x() * kisimage->xRes(), event->point.y() * kisimage->yRes());
         if (!m_strokeId) {
             startStroke(m_currentArgs.mode());
@@ -1932,8 +1932,7 @@ void KisToolTransform::updateSelectionPath()
     if (selection) {
         selectionOutline = selection->outline();
     } else {
-        KisPaintDeviceSP dev = currentNode()->paintDevice();
-        selectionOutline << dev->exactBounds();
+        selectionOutline << m_selectedPortionCache->exactBounds();
     }
 
     const KisCoordinatesConverter *converter = m_canvas->coordinatesConverter();
@@ -1946,32 +1945,16 @@ void KisToolTransform::updateSelectionPath()
     }
 }
 
-void KisToolTransform::initThumbnailImage()
+void KisToolTransform::initThumbnailImage(KisPaintDeviceSP previewDevice)
 {
     m_transform = QTransform();
     m_origImg = QImage();
     m_currImg = QImage();
-
-    KisPaintDeviceSP dev;
-    KisSelectionSP selection = currentSelection();
-
-    if (!currentNode() || !(dev = currentNode()->paintDevice())) {
-        return;
-    }
-
-    QRect srcRect(m_transaction.originalRect().toAlignedRect());
-
-    if (selection) {
-        m_selectedPortionCache = new KisPaintDevice(dev->colorSpace());
-        KisPainter gc(m_selectedPortionCache);
-        gc.setSelection(selection);
-        gc.bitBlt(srcRect.topLeft(), dev, srcRect);
-    } else {
-        m_selectedPortionCache = new KisPaintDevice(*dev);
-    }
+    m_selectedPortionCache = previewDevice;
 
     const int maxSize = 2000;
 
+    QRect srcRect(m_transaction.originalRect().toAlignedRect());
     int x, y, w, h;
     srcRect.getRect(&x, &y, &w, &h);
 
@@ -2039,7 +2022,7 @@ void KisToolTransform::startStroke(ToolTransformArgs::TransformMode mode)
 
     KisPaintDeviceSP dev;
 
-    if (!currentNode() || !(dev = currentNode()->paintDevice())) {
+    if (!currentNode()) {
         return;
     }
 
@@ -2058,25 +2041,25 @@ void KisToolTransform::startStroke(ToolTransformArgs::TransformMode mode)
         return;
     }
 
-    KisSelectionSP selection = currentSelection();
+    m_optWidget->setRecursiveOptionEnabled(false);
+    m_workRecursively = m_optWidget->workRecursively() ||
+        !currentNode()->paintDevice();
+
+    TransformStrokeStrategy *strategy = new TransformStrokeStrategy(currentNode(), currentSelection(), image()->postExecutionUndoAdapter(), image()->undoAdapter());
+    KisPaintDeviceSP previewDevice = strategy->previewDevice();
 
-    QRectF originalRect;
-    originalRect = selection ?
-        selection->selectedExactRect() : dev->exactBounds();
+    KisSelectionSP selection = currentSelection();
+    QRect srcRect = selection ? selection->selectedExactRect() : previewDevice->exactBounds();
 
-    m_transaction = TransformTransactionProperties(originalRect, &m_currentArgs);
+    m_transaction = TransformTransactionProperties(srcRect, &m_currentArgs);
 
-    initThumbnailImage();
+    initThumbnailImage(previewDevice);
     updateSelectionPath();
 
     initTransformMode(mode);
 
-    KisStrokeStrategy *strategy = new TransformStrokeStrategy(currentNode(), currentSelection(), m_selectedPortionCache, image()->postExecutionUndoAdapter(), image()->undoAdapter());
-
     m_strokeId = image()->startStroke(strategy);
-
-    image()->addJob(m_strokeId,
-                    new TransformStrokeStrategy::ClearSelectionData());
+    clearDevices(currentNode(), m_workRecursively);
 
     Q_ASSERT(m_changesTracker.isEmpty());
     commitChanges();
@@ -2087,15 +2070,13 @@ void KisToolTransform::endStroke()
     if (!m_strokeId) return;
 
     if (!m_currentArgs.isIdentity()) {
-        image()->addJob(m_strokeId,
-                        new TransformStrokeStrategy::TransformData(
-                            TransformStrokeStrategy::TransformData::PAINT_DEVICE,
-                            m_currentArgs));
+        transformDevices(currentNode(), m_workRecursively);
 
         image()->addJob(m_strokeId,
                         new TransformStrokeStrategy::TransformData(
                             TransformStrokeStrategy::TransformData::SELECTION,
-                            m_currentArgs));
+                            m_currentArgs,
+                            currentNode()));
 
         image()->endStroke(m_strokeId);
     } else {
@@ -2104,6 +2085,7 @@ void KisToolTransform::endStroke()
 
     m_strokeId.clear();
     m_changesTracker.reset();
+    m_optWidget->setRecursiveOptionEnabled(true);
 }
 
 void KisToolTransform::cancelStroke()
@@ -2113,6 +2095,7 @@ void KisToolTransform::cancelStroke()
     image()->cancelStroke(m_strokeId);
     m_strokeId.clear();
     m_changesTracker.reset();
+    m_optWidget->setRecursiveOptionEnabled(true);
 }
 
 void KisToolTransform::commitChanges()
@@ -2128,6 +2111,39 @@ void KisToolTransform::slotTrackerChangedConfig()
     updateOptionWidget();
 }
 
+void KisToolTransform::clearDevices(KisNodeSP node, bool recursive)
+{
+    if (recursive) {
+        // simple tail-recursive iteration
+        KisNodeSP prevNode = node->lastChild();
+        while(prevNode) {
+            clearDevices(prevNode, recursive);
+            prevNode = prevNode->prevSibling();
+        }
+    }
+
+    image()->addJob(m_strokeId,
+                    new TransformStrokeStrategy::ClearSelectionData(node));
+}
+
+void KisToolTransform::transformDevices(KisNodeSP node, bool recursive)
+{
+    if (recursive) {
+        // simple tail-recursive iteration
+        KisNodeSP prevNode = node->lastChild();
+        while(prevNode) {
+            transformDevices(prevNode, recursive);
+            prevNode = prevNode->prevSibling();
+        }
+    }
+
+    image()->addJob(m_strokeId,
+                    new TransformStrokeStrategy::TransformData(
+                        TransformStrokeStrategy::TransformData::PAINT_DEVICE,
+                        m_currentArgs,
+                        node));
+}
+
 QWidget* KisToolTransform::createOptionWidget() {
     m_optWidget = new KisToolTransformConfigWidget(&m_transaction, m_canvas, 0);
     Q_CHECK_PTR(m_optWidget);
diff --git a/krita/plugins/tools/tool_transform2/kis_tool_transform.h b/krita/plugins/tools/tool_transform2/kis_tool_transform.h
index 9e6fca9..16e043c 100644
--- a/krita/plugins/tools/tool_transform2/kis_tool_transform.h
+++ b/krita/plugins/tools/tool_transform2/kis_tool_transform.h
@@ -97,6 +97,9 @@ protected:
     void requestStrokeCancellation();
 
 private:
+    void clearDevices(KisNodeSP node, bool recursive);
+    void transformDevices(KisNodeSP node, bool recursive);
+
     void startStroke(ToolTransformArgs::TransformMode mode);
     void endStroke();
     void cancelStroke();
@@ -271,13 +274,9 @@ private:
 
     void initTransformMode(ToolTransformArgs::TransformMode mode);
 
-    void initThumbnailImage();
+    void initThumbnailImage(KisPaintDeviceSP previewDevice);
     void updateSelectionPath();
     void updateApplyResetAvailability();
-    void transformDevice(KisPaintDeviceSP device,
-                         KoUpdaterPtr warpUpdater,
-                         KoUpdaterPtr affineUpdater,
-                         KoUpdaterPtr perspectiveUpdater);
 
 private:
     enum function {ROTATE = 0, MOVE, RIGHTSCALE, TOPRIGHTSCALE, TOPSCALE, TOPLEFTSCALE,
@@ -311,6 +310,7 @@ private:
     QImage m_currImg; // origImg transformed using m_transform
     KisPaintDeviceSP m_selectedPortionCache;
     KisStrokeId m_strokeId;
+    bool m_workRecursively;
 
     QPainterPath m_selectionPath; // original (unscaled) selection outline, used for painting decorations
 
diff --git a/krita/plugins/tools/tool_transform2/kis_tool_transform_config_widget.cpp b/krita/plugins/tools/tool_transform2/kis_tool_transform_config_widget.cpp
index c98556b..3e65c72 100644
--- a/krita/plugins/tools/tool_transform2/kis_tool_transform_config_widget.cpp
+++ b/krita/plugins/tools/tool_transform2/kis_tool_transform_config_widget.cpp
@@ -39,6 +39,7 @@ KisToolTransformConfigWidget::KisToolTransformConfigWidget(TransformTransactionP
 {
     setupUi(this);
     showDecorationsBox->setIcon(koIcon("krita_tool_transform"));
+    chkWorkRecursively->setIcon(koIcon("krita_tool_transform_recursive.png"));
     label_shearX->setPixmap(koIcon("shear_horizontal").pixmap(16, 16));
     label_shearY->setPixmap(koIcon("shear_vertical").pixmap(16, 16));
 
@@ -272,6 +273,16 @@ void KisToolTransformConfigWidget::resetRotationCenterButtons()
     }
 }
 
+void KisToolTransformConfigWidget::setRecursiveOptionEnabled(bool value)
+{
+    chkWorkRecursively->setEnabled(value);
+}
+
+bool KisToolTransformConfigWidget::workRecursively() const
+{
+    return chkWorkRecursively->isChecked();;
+}
+
 void KisToolTransformConfigWidget::setTooBigLabelVisible(bool value)
 {
     tooBigLabelWidget->setVisible(value);
diff --git a/krita/plugins/tools/tool_transform2/kis_tool_transform_config_widget.h b/krita/plugins/tools/tool_transform2/kis_tool_transform_config_widget.h
index 2d3b0b6..f905fc9 100644
--- a/krita/plugins/tools/tool_transform2/kis_tool_transform_config_widget.h
+++ b/krita/plugins/tools/tool_transform2/kis_tool_transform_config_widget.h
@@ -39,6 +39,9 @@ public:
     void setTooBigLabelVisible(bool value);
     bool showDecorations() const;
 
+    bool workRecursively() const;
+    void setRecursiveOptionEnabled(bool value);
+
 
 public slots:
     void updateConfig(const ToolTransformArgs &config);
diff --git a/krita/plugins/tools/tool_transform2/krita_tool_transform_recursive.png b/krita/plugins/tools/tool_transform2/krita_tool_transform_recursive.png
new file mode 100644
index 0000000..b5d5c73
Binary files /dev/null and b/krita/plugins/tools/tool_transform2/krita_tool_transform_recursive.png differ
diff --git a/krita/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.cpp b/krita/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.cpp
index 7e5b20e..0e9d13d 100644
--- a/krita/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.cpp
+++ b/krita/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.cpp
@@ -32,26 +32,79 @@
 #include <kis_warptransform_worker.h>
 
 
-TransformStrokeStrategy::TransformStrokeStrategy(KisNodeSP node,
+TransformStrokeStrategy::TransformStrokeStrategy(KisNodeSP rootNode,
                                                  KisSelectionSP selection,
-                                                 KisPaintDeviceSP selectedPortionCache,
                                                  KisPostExecutionUndoAdapter *undoAdapter,
                                                  KisUndoAdapter *legacyUndoAdapter)
     : KisStrokeStrategyUndoCommandBased(i18n("Transform Stroke"), false, undoAdapter),
-      m_node(node),
       m_selection(selection),
-      m_selectedPortionCache(selectedPortionCache),
-      m_legacyUndoAdapter(legacyUndoAdapter),
-      m_progressUpdater(0)
+      m_legacyUndoAdapter(legacyUndoAdapter)
 {
-    Q_ASSERT(m_node);
+    if (rootNode->childCount() || !rootNode->paintDevice()) {
+        m_previewDevice = createDeviceCache(rootNode->projection());
+    } else {
+        m_previewDevice = createDeviceCache(rootNode->paintDevice());
+        putDeviceCache(rootNode->paintDevice(), m_previewDevice);
+    }
+
+    Q_ASSERT(m_previewDevice);
 }
 
 TransformStrokeStrategy::~TransformStrokeStrategy()
 {
-    if (m_progressUpdater) {
-        m_progressUpdater->deleteLater();
+}
+
+KisPaintDeviceSP TransformStrokeStrategy::previewDevice() const
+{
+    return m_previewDevice;
+}
+
+KisPaintDeviceSP TransformStrokeStrategy::createDeviceCache(KisPaintDeviceSP dev)
+{
+    KisPaintDeviceSP cache;
+
+    if (m_selection) {
+        QRect srcRect = m_selection->selectedExactRect();
+
+        cache = new KisPaintDevice(dev->colorSpace());
+        KisPainter gc(cache);
+        gc.setSelection(m_selection);
+        gc.bitBlt(srcRect.topLeft(), dev, srcRect);
+    } else {
+        cache = new KisPaintDevice(*dev);
     }
+
+    return cache;
+}
+
+bool TransformStrokeStrategy::haveDeviceInCache(KisPaintDeviceSP src)
+{
+    QMutexLocker l(&m_devicesCacheMutex);
+    return m_devicesCacheHash.contains(src.data());
+}
+
+void TransformStrokeStrategy::putDeviceCache(KisPaintDeviceSP src, KisPaintDeviceSP cache)
+{
+    QMutexLocker l(&m_devicesCacheMutex);
+    m_devicesCacheHash.insert(src.data(), cache);
+}
+
+KisPaintDeviceSP TransformStrokeStrategy::getDeviceCache(KisPaintDeviceSP src)
+{
+    QMutexLocker l(&m_devicesCacheMutex);
+    KisPaintDeviceSP cache = m_devicesCacheHash.value(src.data());
+    if (!cache) {
+        qWarning() << "WARNING: Transform Stroke: the device is absent in cache!";
+    }
+
+    return cache;
+}
+
+bool TransformStrokeStrategy::checkBelongsToSelection(KisPaintDeviceSP device) const
+{
+    return m_selection &&
+        (device == m_selection->pixelSelection().data() ||
+         device == m_selection->projection().data());
 }
 
 void TransformStrokeStrategy::doStrokeCallback(KisStrokeJobData *data)
@@ -61,20 +114,25 @@ void TransformStrokeStrategy::doStrokeCallback(KisStrokeJobData *data)
 
     if(td) {
         if (td->destination == TransformData::PAINT_DEVICE) {
-            QRect oldExtent = m_node->extent();
+            QRect oldExtent = td->node->extent();
+            KisPaintDeviceSP device = td->node->paintDevice();
 
-            KisPaintDeviceSP device = m_node->paintDevice();
+            if (device && !checkBelongsToSelection(device)) {
+                KisPaintDeviceSP cachedPortion = getDeviceCache(device);
+                Q_ASSERT(cachedPortion);
 
-            KisTransaction transaction("Transform Device", device);
+                KisTransaction transaction("Transform Device", device);
 
-            transformAndMergeDevice(td->config, m_selectedPortionCache,
-                                    device);
+                KisProcessingVisitor::ProgressHelper helper(td->node);
+                transformAndMergeDevice(td->config, cachedPortion,
+                                        device, &helper);
 
-            runAndSaveCommand(KUndo2CommandSP(transaction.endAndTake()),
-                              KisStrokeJobData::CONCURRENT,
-                              KisStrokeJobData::NORMAL);
+                runAndSaveCommand(KUndo2CommandSP(transaction.endAndTake()),
+                                  KisStrokeJobData::CONCURRENT,
+                                  KisStrokeJobData::NORMAL);
 
-            m_node->setDirty(oldExtent | m_node->extent());
+                td->node->setDirty(oldExtent | td->node->extent());
+            }
         } else if (m_selection) {
             // FIXME: do it undoable
             m_selection->flatten();
@@ -82,8 +140,10 @@ void TransformStrokeStrategy::doStrokeCallback(KisStrokeJobData *data)
 
             KisSelectionTransaction transaction("Transform Selection", m_legacyUndoAdapter, m_selection);
 
+            KisProcessingVisitor::ProgressHelper helper(td->node);
             transformDevice(td->config,
-                            m_selection->pixelSelection());
+                            m_selection->pixelSelection(),
+                            &helper);
 
             runAndSaveCommand(KUndo2CommandSP(transaction.endAndTake()),
                               KisStrokeJobData::CONCURRENT,
@@ -92,17 +152,20 @@ void TransformStrokeStrategy::doStrokeCallback(KisStrokeJobData *data)
             m_legacyUndoAdapter->emitSelectionChanged();
         }
     } else if (csd) {
-        clearSelection();
+        KisPaintDeviceSP device = csd->node->paintDevice();
+        if (device && !checkBelongsToSelection(device)) {
+            if (!haveDeviceInCache(device)) {
+                putDeviceCache(device, createDeviceCache(device));
+            }
+            clearSelection(device);
+        }
     } else {
         KisStrokeStrategyUndoCommandBased::doStrokeCallback(data);
     }
 }
 
-void TransformStrokeStrategy::clearSelection()
+void TransformStrokeStrategy::clearSelection(KisPaintDeviceSP device)
 {
-    KisPaintDeviceSP device = m_node->paintDevice();
-    Q_ASSERT(device);
-
     KisTransaction transaction("Clear Selection", device);
     if (m_selection) {
         device->clearSelection(m_selection);
@@ -118,11 +181,12 @@ void TransformStrokeStrategy::clearSelection()
 
 void TransformStrokeStrategy::transformAndMergeDevice(const ToolTransformArgs &config,
                                                       KisPaintDeviceSP src,
-                                                      KisPaintDeviceSP dst)
+                                                      KisPaintDeviceSP dst,
+                                                      KisProcessingVisitor::ProgressHelper *helper)
 {
-    KoUpdaterPtr mergeUpdater = src != dst ? fetchUpdater() : 0;
+    KoUpdaterPtr mergeUpdater = src != dst ? helper->updater() : 0;
 
-    transformDevice(config, src);
+    transformDevice(config, src, helper);
     if (src != dst) {
         QRect mergeRect = src->extent();
         KisPainter painter(dst);
@@ -132,24 +196,12 @@ void TransformStrokeStrategy::transformAndMergeDevice(const ToolTransformArgs &c
     }
 }
 
-KoUpdaterPtr TransformStrokeStrategy::fetchUpdater()
-{
-    QMutexLocker l(&m_progressMutex);
-
-    if (!m_progressUpdater) {
-        KisNodeProgressProxy *progressProxy = m_node->nodeProgressProxy();
-        m_progressUpdater = new KoProgressUpdater(progressProxy);
-        m_progressUpdater->moveToThread(m_node->thread());
-    }
-
-    return m_progressUpdater->startSubtask();
-}
-
 void TransformStrokeStrategy::transformDevice(const ToolTransformArgs &config,
-                                              KisPaintDeviceSP device)
+                                              KisPaintDeviceSP device,
+                                              KisProcessingVisitor::ProgressHelper *helper)
 {
     if (config.mode() == ToolTransformArgs::WARP) {
-        KoUpdaterPtr updater = fetchUpdater();
+        KoUpdaterPtr updater = helper->updater();
 
         KisWarpTransformWorker worker(config.warpType(),
                                       device,
@@ -178,8 +230,8 @@ void TransformStrokeStrategy::transformDevice(const ToolTransformArgs &config,
 
         QPointF translation = config.transformedCenter() - transformedCenter.toPointF();
 
-        KoUpdaterPtr updater1 = fetchUpdater();
-        KoUpdaterPtr updater2 = fetchUpdater();
+        KoUpdaterPtr updater1 = helper->updater();
+        KoUpdaterPtr updater2 = helper->updater();
 
         KisTransformWorker transformWorker(device,
                                            config.scaleX(), config.scaleY(),
diff --git a/krita/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.h b/krita/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.h
index 7eb5bf0..c56857e 100644
--- a/krita/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.h
+++ b/krita/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.h
@@ -24,6 +24,7 @@
 #include "kis_stroke_strategy_undo_command_based.h"
 #include "kis_types.h"
 #include "tool_transform_args.h"
+#include <kis_processing_visitor.h>
 
 class KisUndoAdapter;
 
@@ -42,56 +43,71 @@ public:
         };
 
     public:
-        TransformData(Destination _destination, const ToolTransformArgs &_config)
+    TransformData(Destination _destination, const ToolTransformArgs &_config, KisNodeSP _node)
             : KisStrokeJobData(CONCURRENT, NORMAL),
             destination(_destination),
-            config(_config)
+            config(_config),
+            node(_node)
         {
         }
 
         Destination destination;
         ToolTransformArgs config;
+        KisNodeSP node;
     };
 
     class KDE_EXPORT ClearSelectionData : public KisStrokeJobData {
     public:
-        ClearSelectionData()
-            : KisStrokeJobData(SEQUENTIAL, NORMAL)
+        ClearSelectionData(KisNodeSP _node)
+            : KisStrokeJobData(SEQUENTIAL, NORMAL),
+              node(_node)
         {
         }
+        KisNodeSP node;
     };
 
 public:
-    TransformStrokeStrategy(KisNodeSP node,
+    TransformStrokeStrategy(KisNodeSP rootNode,
                             KisSelectionSP selection,
-                            KisPaintDeviceSP selectedPortionCache,
                             KisPostExecutionUndoAdapter *undoAdapter,
                             KisUndoAdapter *legacyUndoAdapter);
 
     ~TransformStrokeStrategy();
 
+    KisPaintDeviceSP previewDevice() const;
+
     void doStrokeCallback(KisStrokeJobData *data);
 
 private:
-    KoUpdaterPtr fetchUpdater();
+    KoUpdaterPtr fetchUpdater(KisNodeSP node);
 
     void transformAndMergeDevice(const ToolTransformArgs &config,
                                  KisPaintDeviceSP src,
-                                 KisPaintDeviceSP dst);
+                                 KisPaintDeviceSP dst,
+                                 KisProcessingVisitor::ProgressHelper *helper);
     void transformDevice(const ToolTransformArgs &config,
-                         KisPaintDeviceSP device);
+                         KisPaintDeviceSP device,
+                         KisProcessingVisitor::ProgressHelper *helper);
+
+    void clearSelection(KisPaintDeviceSP device);
+    //void transformDevice(KisPaintDeviceSP src, KisPaintDeviceSP dst, KisProcessingVisitor::ProgressHelper *helper);
+
+    bool checkBelongsToSelection(KisPaintDeviceSP device) const;
 
-    void clearSelection();
-    void transformDevice(KisPaintDeviceSP src, KisPaintDeviceSP dst);
+    KisPaintDeviceSP createDeviceCache(KisPaintDeviceSP src);
+
+    bool haveDeviceInCache(KisPaintDeviceSP src);
+    void putDeviceCache(KisPaintDeviceSP src, KisPaintDeviceSP cache);
+    KisPaintDeviceSP getDeviceCache(KisPaintDeviceSP src);
 
 private:
-    KisNodeSP m_node;
     KisSelectionSP m_selection;
-    KisPaintDeviceSP m_selectedPortionCache;
     KisUndoAdapter *m_legacyUndoAdapter;
 
-    QMutex m_progressMutex;
-    KoProgressUpdater *m_progressUpdater;
+    QMutex m_devicesCacheMutex;
+    QHash<KisPaintDevice*, KisPaintDeviceSP> m_devicesCacheHash;
+
+    KisPaintDeviceSP m_previewDevice;
 };
 
 #endif /* __TRANSFORM_STROKE_STRATEGY_H */
diff --git a/krita/plugins/tools/tool_transform2/wdg_tool_transform.ui b/krita/plugins/tools/tool_transform2/wdg_tool_transform.ui
index 9b8519c..17254be 100644
--- a/krita/plugins/tools/tool_transform2/wdg_tool_transform.ui
+++ b/krita/plugins/tools/tool_transform2/wdg_tool_transform.ui
@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>225</width>
-    <height>258</height>
+    <width>294</width>
+    <height>266</height>
    </rect>
   </property>
   <property name="sizePolicy">
@@ -38,17 +38,8 @@
    <enum>Qt::LeftToRight</enum>
   </property>
   <layout class="QVBoxLayout" name="verticalLayout_5">
-   <property name="spacing">
-    <number>0</number>
-   </property>
-   <property name="margin">
-    <number>0</number>
-   </property>
    <item>
     <layout class="QHBoxLayout" name="horizontalLayout">
-     <property name="spacing">
-      <number>0</number>
-     </property>
      <item>
       <spacer name="horizontalSpacer_2">
        <property name="orientation">
@@ -56,7 +47,7 @@
        </property>
        <property name="sizeHint" stdset="0">
         <size>
-         <width>0</width>
+         <width>13</width>
          <height>20</height>
         </size>
        </property>
@@ -125,7 +116,7 @@
        </property>
        <property name="sizeHint" stdset="0">
         <size>
-         <width>0</width>
+         <width>13</width>
          <height>20</height>
         </size>
        </property>
@@ -1105,7 +1096,7 @@ big!</string>
        </property>
        <property name="maximumSize">
         <size>
-         <width>40</width>
+         <width>32</width>
          <height>16777215</height>
         </size>
        </property>
@@ -1124,14 +1115,42 @@ big!</string>
       </widget>
      </item>
      <item>
+      <widget class="QPushButton" name="chkWorkRecursively">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>32</width>
+         <height>16777215</height>
+        </size>
+       </property>
+       <property name="toolTip">
+        <string>Work Recursively</string>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+       <property name="checkable">
+        <bool>true</bool>
+       </property>
+       <property name="checked">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
       <spacer name="horizontalSpacer_5">
        <property name="orientation">
         <enum>Qt::Horizontal</enum>
        </property>
        <property name="sizeHint" stdset="0">
         <size>
-         <width>0</width>
-         <height>10</height>
+         <width>13</width>
+         <height>13</height>
         </size>
        </property>
       </spacer>



More information about the kimageshop mailing list