[krita] krita: Implement D&D of frames across different layers

Dmitry Kazakov dimula73 at gmail.com
Fri Dec 11 10:22:43 UTC 2015


Git commit 979701e8278583fb2b76ec960d72ad90e8d517c4 by Dmitry Kazakov.
Committed on 11/12/2015 at 08:49.
Pushed by dkazakov into branch 'master'.

Implement D&D of frames across different layers

It was planned long ago, and now just implemented

CC:kimageshop at kde.org

M  +23   -0    krita/image/kis_keyframe_channel.cpp
M  +2    -0    krita/image/kis_keyframe_channel.h
M  +34   -0    krita/image/kis_paint_device.cc
M  +8    -0    krita/image/kis_paint_device_frames_interface.h
M  +14   -0    krita/image/kis_raster_keyframe_channel.cpp
M  +1    -0    krita/image/kis_raster_keyframe_channel.h
M  +15   -0    krita/image/kis_scalar_keyframe_channel.cpp
M  +1    -0    krita/image/kis_scalar_keyframe_channel.h
M  +111  -0    krita/image/tests/kis_paint_device_test.cpp
M  +2    -0    krita/image/tests/kis_paint_device_test.h
M  +26   -10   krita/plugins/extensions/dockers/animation/kis_animation_utils.cpp
M  +5    -7    krita/plugins/extensions/dockers/animation/timeline_frames_model.cpp

http://commits.kde.org/krita/979701e8278583fb2b76ec960d72ad90e8d517c4

diff --git a/krita/image/kis_keyframe_channel.cpp b/krita/image/kis_keyframe_channel.cpp
index 89815b0..8ccb6e8 100644
--- a/krita/image/kis_keyframe_channel.cpp
+++ b/krita/image/kis_keyframe_channel.cpp
@@ -415,6 +415,29 @@ KisKeyframeSP KisKeyframeChannel::insertKeyframe(int time, const KisKeyframeSP c
     return keyframe;
 }
 
+KisKeyframeSP KisKeyframeChannel::copyExternalKeyframe(KisKeyframeChannel *srcChannel, int srcTime, int dstTime, KUndo2Command *parentCommand)
+{
+    if (srcChannel->id() != id()) {
+        warnKrita << "Cannot copy frames from different ids:" << ppVar(srcChannel->id()) << ppVar(id());
+        return KisKeyframeSP();
+    }
+
+    LAZY_INITIALIZE_PARENT_COMMAND(parentCommand);
+
+    KisKeyframeSP dstFrame = keyframeAt(dstTime);
+    if (dstFrame) {
+        deleteKeyframeImpl(dstFrame, parentCommand, false);
+    }
+
+    KisKeyframeSP newKeyframe = createKeyframe(dstTime, KisKeyframeSP(), parentCommand);
+    uploadExternalKeyframe(srcChannel, srcTime, newKeyframe);
+
+    KUndo2Command *cmd = new InsertFrameCommand(this, newKeyframe, true, parentCommand);
+    cmd->redo();
+
+    return newKeyframe;
+}
+
 KisKeyframeChannel::KeyframesMap::const_iterator
 KisKeyframeChannel::activeKeyIterator(int time) const
 {
diff --git a/krita/image/kis_keyframe_channel.h b/krita/image/kis_keyframe_channel.h
index ca961ca..7bb5e67 100644
--- a/krita/image/kis_keyframe_channel.h
+++ b/krita/image/kis_keyframe_channel.h
@@ -52,6 +52,7 @@ public:
     bool deleteKeyframe(KisKeyframeSP keyframe, KUndo2Command *parentCommand = 0);
     bool moveKeyframe(KisKeyframeSP keyframe, int newTime, KUndo2Command *parentCommand = 0);
     KisKeyframeSP copyKeyframe(const KisKeyframeSP keyframe, int newTime, KUndo2Command *parentCommand = 0);
+    KisKeyframeSP copyExternalKeyframe(KisKeyframeChannel *srcChannel, int srcTime, int dstTime, KUndo2Command *parentCommand = 0);
 
     KisKeyframeSP keyframeAt(int time) const;
     KisKeyframeSP activeKeyframeAt(int time) const;
@@ -109,6 +110,7 @@ protected:
 
     virtual KisKeyframeSP createKeyframe(int time, const KisKeyframeSP copySrc, KUndo2Command *parentCommand) = 0;
     virtual void destroyKeyframe(KisKeyframeSP key, KUndo2Command *parentCommand) = 0;
+    virtual void uploadExternalKeyframe(KisKeyframeChannel *srcChannel, int srcTime, KisKeyframeSP dstFrame) = 0;
 
     virtual QRect affectedRect(KisKeyframeSP key) = 0;
     virtual void requestUpdate(const KisTimeRange &range, const QRect &rect);
diff --git a/krita/image/kis_paint_device.cc b/krita/image/kis_paint_device.cc
index 8daebd7..76dcdb2 100644
--- a/krita/image/kis_paint_device.cc
+++ b/krita/image/kis_paint_device.cc
@@ -319,6 +319,8 @@ public:
     }
 
     void fetchFrame(int frameId, KisPaintDeviceSP targetDevice);
+    void uploadFrame(int srcFrameId, int dstFrameId, KisPaintDeviceSP srcDevice);
+
     QRegion syncLodData(int newLod);
 
     void tesingFetchLodDevice(KisPaintDeviceSP targetDevice);
@@ -636,6 +638,33 @@ void KisPaintDevice::Private::fetchFrame(int frameId, KisPaintDeviceSP targetDev
     transferFromData(data, targetDevice);
 }
 
+void KisPaintDevice::Private::uploadFrame(int srcFrameId, int dstFrameId, KisPaintDeviceSP srcDevice)
+{
+    DataSP dstData = m_frames[dstFrameId];
+    KIS_ASSERT_RECOVER_RETURN(dstData);
+
+    DataSP srcData = srcDevice->m_d->m_frames[srcFrameId];
+    KIS_ASSERT_RECOVER_RETURN(srcData);
+
+    if (srcData->colorSpace() != dstData->colorSpace() &&
+        !(*srcData->colorSpace() == *dstData->colorSpace())) {
+
+        KUndo2Command tempCommand;
+
+        srcData = toQShared(new Data(srcData.data(), true));
+        srcData->convertDataColorSpace(dstData->colorSpace(),
+                                       KoColorConversionTransformation::internalRenderingIntent(),
+                                       KoColorConversionTransformation::internalConversionFlags(),
+                                       &tempCommand);
+    }
+
+    dstData->dataManager()->clear();
+    dstData->cache()->invalidate();
+
+    const QRect rect = srcData->dataManager()->extent();
+    dstData->dataManager()->bitBltRough(srcData->dataManager(), rect);
+}
+
 void KisPaintDevice::Private::tesingFetchLodDevice(KisPaintDeviceSP targetDevice)
 {
     Data *data = m_lodData.data();
@@ -1679,6 +1708,11 @@ void KisPaintDeviceFramesInterface::fetchFrame(int frameId, KisPaintDeviceSP tar
     q->m_d->fetchFrame(frameId, targetDevice);
 }
 
+void KisPaintDeviceFramesInterface::uploadFrame(int srcFrameId, int dstFrameId, KisPaintDeviceSP srcDevice)
+{
+    q->m_d->uploadFrame(srcFrameId, dstFrameId, srcDevice);
+}
+
 QRect KisPaintDeviceFramesInterface::frameBounds(int frameId)
 {
     return q->m_d->frameBounds(frameId);
diff --git a/krita/image/kis_paint_device_frames_interface.h b/krita/image/kis_paint_device_frames_interface.h
index 49388ce..b34dabc 100644
--- a/krita/image/kis_paint_device_frames_interface.h
+++ b/krita/image/kis_paint_device_frames_interface.h
@@ -59,6 +59,14 @@ public:
     void fetchFrame(int frameId, KisPaintDeviceSP targetDevice);
 
     /**
+     * Copy the given paint device contents into the specified frame
+     * @param dstFrameId ID of the frame to be overwritten (must exist)
+     * @param srcFrameId ID of the frame to copy from (must exist)
+     * @param sourceDevice paint device to copy from
+     */
+    void uploadFrame(int srcFrameId, int dstFrameId, KisPaintDeviceSP srcDevice);
+
+    /**
      * @return extent() of \p frameId
      */
     QRect frameBounds(int frameId);
diff --git a/krita/image/kis_raster_keyframe_channel.cpp b/krita/image/kis_raster_keyframe_channel.cpp
index b46574f..94e4c0c 100644
--- a/krita/image/kis_raster_keyframe_channel.cpp
+++ b/krita/image/kis_raster_keyframe_channel.cpp
@@ -111,6 +111,20 @@ void KisRasterKeyframeChannel::destroyKeyframe(KisKeyframeSP key, KUndo2Command
     m_d->paintDevice->framesInterface()->deleteFrame(key->value(), parentCommand);
 }
 
+void KisRasterKeyframeChannel::uploadExternalKeyframe(KisKeyframeChannel *srcChannel, int srcTime, KisKeyframeSP dstFrame)
+{
+    KisRasterKeyframeChannel *srcRasterChannel = dynamic_cast<KisRasterKeyframeChannel*>(srcChannel);
+    KIS_ASSERT_RECOVER_RETURN(srcRasterChannel);
+
+    const int srcId = srcRasterChannel->frameIdAt(srcTime);
+    const int dstId = dstFrame->value();
+
+    m_d->paintDevice->framesInterface()->
+        uploadFrame(srcId,
+                    dstId,
+                    srcRasterChannel->m_d->paintDevice);
+}
+
 QRect KisRasterKeyframeChannel::affectedRect(KisKeyframeSP key)
 {
     KeyframesMap::iterator it = keys().find(key->time());
diff --git a/krita/image/kis_raster_keyframe_channel.h b/krita/image/kis_raster_keyframe_channel.h
index f832c55..1505309 100644
--- a/krita/image/kis_raster_keyframe_channel.h
+++ b/krita/image/kis_raster_keyframe_channel.h
@@ -66,6 +66,7 @@ public:
 protected:
     KisKeyframeSP createKeyframe(int time, const KisKeyframeSP copySrc, KUndo2Command *parentCommand);
     void destroyKeyframe(KisKeyframeSP key, KUndo2Command *parentCommand);
+    void uploadExternalKeyframe(KisKeyframeChannel *srcChannel, int srcTime, KisKeyframeSP dstFrame);
 
     QRect affectedRect(KisKeyframeSP key);
     void requestUpdate(const KisTimeRange &range, const QRect &rect);
diff --git a/krita/image/kis_scalar_keyframe_channel.cpp b/krita/image/kis_scalar_keyframe_channel.cpp
index 42bfd68..e3d33ae 100644
--- a/krita/image/kis_scalar_keyframe_channel.cpp
+++ b/krita/image/kis_scalar_keyframe_channel.cpp
@@ -163,6 +163,21 @@ void KisScalarKeyframeChannel::destroyKeyframe(KisKeyframeSP key, KUndo2Command
     cmd->redo();
 }
 
+void KisScalarKeyframeChannel::uploadExternalKeyframe(KisKeyframeChannel *srcChannel, int srcTime, KisKeyframeSP dstFrame)
+{
+    KisScalarKeyframeChannel *srcScalarChannel = dynamic_cast<KisScalarKeyframeChannel*>(srcChannel);
+    KIS_ASSERT_RECOVER_RETURN(srcScalarChannel);
+
+    KisKeyframeSP srcFrame = srcScalarChannel->keyframeAt(srcTime);
+    KIS_ASSERT_RECOVER_RETURN(srcFrame);
+
+    const qreal newValue = scalarValue(srcFrame);
+
+    const int dstId = dstFrame->value();
+    KIS_ASSERT_RECOVER_RETURN(m_d->values.contains(dstId));
+    m_d->values[dstId] = newValue;
+}
+
 QRect KisScalarKeyframeChannel::affectedRect(KisKeyframeSP key)
 {
     Q_UNUSED(key);
diff --git a/krita/image/kis_scalar_keyframe_channel.h b/krita/image/kis_scalar_keyframe_channel.h
index 206f92e..85b659e 100644
--- a/krita/image/kis_scalar_keyframe_channel.h
+++ b/krita/image/kis_scalar_keyframe_channel.h
@@ -37,6 +37,7 @@ public:
 protected:
     KisKeyframeSP createKeyframe(int time, const KisKeyframeSP copySrc, KUndo2Command *parentCommand);
     void destroyKeyframe(KisKeyframeSP key, KUndo2Command *parentCommand);
+    void uploadExternalKeyframe(KisKeyframeChannel *srcChannel, int srcTime, KisKeyframeSP dstFrame);
 
     QRect affectedRect(KisKeyframeSP key);
 
diff --git a/krita/image/tests/kis_paint_device_test.cpp b/krita/image/tests/kis_paint_device_test.cpp
index 840c891..3e48bb9 100644
--- a/krita/image/tests/kis_paint_device_test.cpp
+++ b/krita/image/tests/kis_paint_device_test.cpp
@@ -1912,6 +1912,117 @@ void KisPaintDeviceTest::testFramesUndoRedoWithChannel()
     QVERIFY(o.m_currentData == o.m_frames.begin().value());
 }
 
+void fillRect(KisPaintDeviceSP dev, int time, const QRect &rc, TestUtil::TestingTimedDefaultBounds *bounds)
+{
+    KUndo2Command parentCommand;
+    KisRasterKeyframeChannel *channel = dev->keyframeChannel();
+    KisKeyframeSP frame = channel->addKeyframe(time, &parentCommand);
+
+    const int oldTime = bounds->currentTime();
+    bounds->testingSetTime(time);
+
+    KoColor color(Qt::red, dev->colorSpace());
+    dev->fill(rc, color);
+
+    bounds->testingSetTime(oldTime);
+}
+
+bool checkRect(KisPaintDeviceSP dev, int time, const QRect &rc, TestUtil::TestingTimedDefaultBounds *bounds)
+{
+    const int oldTime = bounds->currentTime();
+    bounds->testingSetTime(time);
+
+    bool result = dev->exactBounds() == rc;
+
+    if (!result) {
+        qDebug() << "Failed to check frame:" << ppVar(time) << ppVar(rc) << ppVar(dev->exactBounds());
+    }
+
+    bounds->testingSetTime(oldTime);
+
+    return result;
+}
+
+void testCrossDeviceFrameCopyImpl(bool useChannel)
+{
+    const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
+    KisPaintDeviceSP dev1 = new KisPaintDevice(cs);
+    KisPaintDeviceSP dev2 = new KisPaintDevice(cs);
+
+    const KoColorSpace *cs3 = KoColorSpaceRegistry::instance()->rgb16();
+    KisPaintDeviceSP dev3 = new KisPaintDevice(cs3);
+
+    TestUtil::TestingTimedDefaultBounds *bounds = new TestUtil::TestingTimedDefaultBounds();
+    dev1->setDefaultBounds(bounds);
+    dev2->setDefaultBounds(bounds);
+    dev3->setDefaultBounds(bounds);
+
+    KisRasterKeyframeChannel *channel1 = dev1->createKeyframeChannel(KisKeyframeChannel::Content, 0);
+    KisPaintDeviceFramesInterface *i1 = dev1->framesInterface();
+    QVERIFY(channel1);
+    QVERIFY(i1);
+
+    KisRasterKeyframeChannel *channel2 = dev2->createKeyframeChannel(KisKeyframeChannel::Content, 0);
+    KisPaintDeviceFramesInterface *i2 = dev2->framesInterface();
+    QVERIFY(channel2);
+    QVERIFY(i2);
+
+    KisRasterKeyframeChannel *channel3 = dev3->createKeyframeChannel(KisKeyframeChannel::Content, 0);
+    KisPaintDeviceFramesInterface *i3 = dev3->framesInterface();
+    QVERIFY(channel3);
+    QVERIFY(i3);
+
+    fillRect(dev1, 10, QRect(100,100,100,100), bounds);
+    fillRect(dev2, 20, QRect(200,200,100,100), bounds);
+    fillRect(dev3, 30, QRect(300,300,100,100), bounds);
+
+    QCOMPARE(dev1->exactBounds(), QRect());
+
+    const int dstFrameId1 = channel1->frameIdAt(10);
+    const int srcFrameId2 = channel2->frameIdAt(20);
+    const int srcFrameId3 = channel3->frameIdAt(30);
+
+    KUndo2Command cmd1;
+    if (!useChannel) {
+        dev1->framesInterface()->uploadFrame(srcFrameId2, dstFrameId1, dev2);
+    } else {
+        KisKeyframeSP k = channel1->copyExternalKeyframe(channel2, 20, 10, &cmd1);
+    }
+
+    QCOMPARE(dev1->exactBounds(), QRect());
+    QVERIFY(checkRect(dev1, 10, QRect(200,200,100,100), bounds));
+
+    if (useChannel) {
+        cmd1.undo();
+        QVERIFY(checkRect(dev1, 10, QRect(100,100,100,100), bounds));
+    }
+
+    KUndo2Command cmd2;
+    if (!useChannel) {
+        dev1->framesInterface()->uploadFrame(srcFrameId3, dstFrameId1, dev3);
+    } else {
+        KisKeyframeSP k = channel1->copyExternalKeyframe(channel3, 30, 10, &cmd2);
+    }
+
+    QCOMPARE(dev1->exactBounds(), QRect());
+    QVERIFY(checkRect(dev1, 10, QRect(300,300,100,100), bounds));
+
+    if (useChannel) {
+        cmd2.undo();
+        QVERIFY(checkRect(dev1, 10, QRect(100,100,100,100), bounds));
+    }
+}
+
+void KisPaintDeviceTest::testCrossDeviceFrameCopyDirect()
+{
+    testCrossDeviceFrameCopyImpl(false);
+}
+
+void KisPaintDeviceTest::testCrossDeviceFrameCopyChannel()
+{
+    testCrossDeviceFrameCopyImpl(true);
+}
+
 #include "kis_surrogate_undo_adapter.h"
 
 void KisPaintDeviceTest::testLazyFrameCreation()
diff --git a/krita/image/tests/kis_paint_device_test.h b/krita/image/tests/kis_paint_device_test.h
index f193592..9a2c89f 100644
--- a/krita/image/tests/kis_paint_device_test.h
+++ b/krita/image/tests/kis_paint_device_test.h
@@ -71,6 +71,8 @@ private Q_SLOTS:
     void testFramesLeaking();
     void testFramesUndoRedo();
     void testFramesUndoRedoWithChannel();
+    void testCrossDeviceFrameCopyDirect();
+    void testCrossDeviceFrameCopyChannel();
     void testLazyFrameCreation();
 
     void testCompositionAssociativity();
diff --git a/krita/plugins/extensions/dockers/animation/kis_animation_utils.cpp b/krita/plugins/extensions/dockers/animation/kis_animation_utils.cpp
index 861585d..67df50e 100644
--- a/krita/plugins/extensions/dockers/animation/kis_animation_utils.cpp
+++ b/krita/plugins/extensions/dockers/animation/kis_animation_utils.cpp
@@ -154,19 +154,35 @@ namespace KisAnimationUtils {
             const int dstTime = dstFrames[i].time;
             KisNodeSP dstNode = dstFrames[i].node;
 
-            if (srcNode != dstNode) continue;
+            if (srcNode == dstNode) {
+                KisKeyframeChannel *content =
+                    srcNode->getKeyframeChannel(KisKeyframeChannel::Content.id());
+
+                if (!content) continue;
+
+                KisKeyframeSP srcKeyframe = content->keyframeAt(srcTime);
+                if (srcKeyframe) {
+                    if (copy) {
+                        content->copyKeyframe(srcKeyframe, dstTime, cmd.data());
+                    } else {
+                        content->moveKeyframe(srcKeyframe, dstTime, cmd.data());
+                    }
+                }
+            } else {
+                KisKeyframeChannel *srcContent =
+                    srcNode->getKeyframeChannel(KisKeyframeChannel::Content.id());
+                KisKeyframeChannel *dstContent =
+                    dstNode->getKeyframeChannel(KisKeyframeChannel::Content.id());
 
-            KisKeyframeChannel *content =
-                srcNode->getKeyframeChannel(KisKeyframeChannel::Content.id());
+                if (!srcContent || !dstContent) continue;
 
-            if (!content) continue;
+                KisKeyframeSP srcKeyframe = srcContent->keyframeAt(srcTime);
+                if (!srcKeyframe) continue;
+
+                dstContent->copyExternalKeyframe(srcContent, srcTime, dstTime, cmd.data());
 
-            KisKeyframeSP srcKeyframe = content->keyframeAt(srcTime);
-            if (srcKeyframe) {
-                if (copy) {
-                    content->copyKeyframe(srcKeyframe, dstTime, cmd.data());
-                } else {
-                    content->moveKeyframe(srcKeyframe, dstTime, cmd.data());
+                if (!copy) {
+                    srcContent->deleteKeyframe(srcKeyframe, cmd.data());
                 }
             }
 
diff --git a/krita/plugins/extensions/dockers/animation/timeline_frames_model.cpp b/krita/plugins/extensions/dockers/animation/timeline_frames_model.cpp
index 343fd33..d3fe5a2 100644
--- a/krita/plugins/extensions/dockers/animation/timeline_frames_model.cpp
+++ b/krita/plugins/extensions/dockers/animation/timeline_frames_model.cpp
@@ -604,11 +604,11 @@ bool TimelineFramesModel::canDropFrameData(const QMimeData *data, const QModelIn
 {
     if (!index.isValid()) return false;
 
-    QByteArray encoded = data->data("application/x-krita-frame");
-    int baseRow, baseColumn;
-    decodeBaseIndex(&encoded, &baseRow, &baseColumn);
-
-    return baseRow == index.row();
+    /**
+     * Now we support D&D around any layer, so just return 'true' all
+     * the time.
+     */
+    return true;
 }
 
 bool TimelineFramesModel::offsetFrames(QVector<QPoint> srcIndexes, const QPoint &offset, bool copyFrames)
@@ -675,8 +675,6 @@ bool TimelineFramesModel::dropMimeData(const QMimeData *data, Qt::DropAction act
     int size, baseRow, baseColumn;
     stream >> size >> baseRow >> baseColumn;
 
-    if (baseRow != parent.row()) return result;
-
     QVector<QPoint> srcIndexes;
 
     for (int i = 0; i < size; i++) {



More information about the kimageshop mailing list