[calligra/krita-animation-pentikainen] krita: Implement D&D of frames across different layers
Dmitry Kazakov
dimula73 at gmail.com
Fri Dec 4 07:53:25 UTC 2015
Git commit 25ae5156b6e143df5ca246fcbc4a46d7fed08a81 by Dmitry Kazakov.
Committed on 04/12/2015 at 07:50.
Pushed by dkazakov into branch 'krita-animation-pentikainen'.
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/calligra/25ae5156b6e143df5ca246fcbc4a46d7fed08a81
diff --git a/krita/image/kis_keyframe_channel.cpp b/krita/image/kis_keyframe_channel.cpp
index 54d525e..d117913 100644
--- a/krita/image/kis_keyframe_channel.cpp
+++ b/krita/image/kis_keyframe_channel.cpp
@@ -414,6 +414,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 c4b6d45..73bb8de 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 2b773f7..038b16b 100644
--- a/krita/image/kis_paint_device.cc
+++ b/krita/image/kis_paint_device.cc
@@ -320,6 +320,8 @@ public:
}
void fetchFrame(int frameId, KisPaintDeviceSP targetDevice);
+ void uploadFrame(int srcFrameId, int dstFrameId, KisPaintDeviceSP srcDevice);
+
QRegion syncLodData(int newLod);
void tesingFetchLodDevice(KisPaintDeviceSP targetDevice);
@@ -637,6 +639,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();
@@ -1688,6 +1717,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 ebc4c8a..1bdf355 100644
--- a/krita/image/kis_raster_keyframe_channel.cpp
+++ b/krita/image/kis_raster_keyframe_channel.cpp
@@ -110,6 +110,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 7990517..0c01a62 100644
--- a/krita/image/kis_scalar_keyframe_channel.cpp
+++ b/krita/image/kis_scalar_keyframe_channel.cpp
@@ -161,6 +161,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 206f92ed..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 5be8219..3cfb1a4 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 f15b927..66ed519 100644
--- a/krita/plugins/extensions/dockers/animation/kis_animation_utils.cpp
+++ b/krita/plugins/extensions/dockers/animation/kis_animation_utils.cpp
@@ -152,19 +152,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 3f8544d..f87e5e4 100644
--- a/krita/plugins/extensions/dockers/animation/timeline_frames_model.cpp
+++ b/krita/plugins/extensions/dockers/animation/timeline_frames_model.cpp
@@ -602,11 +602,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)
@@ -673,8 +673,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