Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_raster_keyframe_channel.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2015 Jouni Pentikäinen <joupent@gmail.com>
3 * SPDX-FileCopyrightText: 2020 Emmet O 'Neill <emmetoneill.pdx@gmail.com>
4 * SPDX-FileCopyrightText: 2020 Eoin O 'Neill <eoinoneill1991@gmail.com>
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
9#include "kis_node.h"
10#include "kis_dom_utils.h"
11
12#include "kis_global.h"
13#include "kis_paint_device.h"
15#include "kis_time_span.h"
16#include "kundo2command.h"
18#include "kis_layer_utils.h"
19
21 : KisKeyframe()
22{
23 m_paintDevice = paintDevice;
25
26 m_frameID = m_paintDevice->framesInterface()->createFrame(false, 0, QPoint(), nullptr);
27}
28
29KisRasterKeyframe::KisRasterKeyframe(KisPaintDeviceWSP paintDevice, const int &premadeFrameID, const int &colorLabelId)
30 : KisKeyframe()
31{
32 m_paintDevice = paintDevice;
33 m_frameID = premadeFrameID;
34 setColorLabel(colorLabelId);
35
37}
38
40{
41 // Note: Because keyframe ownership is shared, it's possible for them to outlive
42 // the paint device.
45 }
46}
47
49{
50 return m_frameID;
51}
52
54{
55 if (!m_paintDevice) {
56 return QRect();
57 }
58
60}
61
66
73
75{
76 if (newChannel) {
77 KisRasterKeyframeChannel* rasterChannel = dynamic_cast<KisRasterKeyframeChannel*>(newChannel);
78 KIS_ASSERT(rasterChannel);
79 KisPaintDeviceWSP targetDevice = rasterChannel->paintDevice();
80
81 if (targetDevice != m_paintDevice) {
82 int targetFrameID = targetDevice->framesInterface()->createFrame(false, 0, QPoint(), nullptr);
83 targetDevice->framesInterface()->uploadFrame(m_frameID, targetFrameID, m_paintDevice);
84 KisKeyframeSP key = toQShared(new KisRasterKeyframe(targetDevice, targetFrameID ));
85 key->setColorLabel(colorLabel());
86 return key;
87 }
88 }
89
90 int copyFrameID = m_paintDevice->framesInterface()->createFrame(true, m_frameID, QPoint(), nullptr);
92 key->setColorLabel(colorLabel());
93 return key;
94}
95
96
97// ===========================================================================================================
98// =======================================KisRasterKeyframeChannel============================================
99// ===========================================================================================================
100
101
122
125 m_d(new Private(paintDevice, QString()))
126{
127}
128
130 : KisKeyframeChannel(rhs),
131 m_d(new Private(newPaintDevice, rhs.m_d->filenameSuffix))
132{
133 KIS_ASSERT_RECOVER_NOOP(&rhs != this);
134
135 m_d->frameFilenames = rhs.m_d->frameFilenames;
136 m_d->onionSkinsEnabled = rhs.m_d->onionSkinsEnabled;
137
138 // Copy keyframes with attention to clones..
139 foreach (const int& frame, rhs.constKeys().keys()) {
140 KisRasterKeyframeSP copySource = rhs.keyframeAt<KisRasterKeyframe>(frame);
141 if (m_d->frameIDTimesMap.contains(copySource->frameID())){
142 continue;
143 }
144
145 KisRasterKeyframeSP transferredKey = toQShared(new KisRasterKeyframe(newPaintDevice, copySource->frameID(), copySource->colorLabel()));
146 foreach (const int& time, rhs.m_d->frameIDTimesMap.values(transferredKey->frameID())) {
147 keys().insert(time, transferredKey);
148 m_d->frameIDTimesMap.insert(transferredKey->frameID(), time);
149 }
150 }
151}
152
156
158{
159 KisRasterKeyframeSP key = keyframeAt<KisRasterKeyframe>(time);
160 if (!key) {
161 key = activeKeyframeAt<KisRasterKeyframe>(time);
162 }
163
164 key->writeFrameToDevice(targetDevice);
165}
166
167void KisRasterKeyframeChannel::importFrame(int time, KisPaintDeviceSP sourceDevice, KUndo2Command *parentCommand)
168{
169 addKeyframe(time, parentCommand);
170 KisRasterKeyframeSP keyframe = keyframeAt<KisRasterKeyframe>(time);
171 m_d->paintDevice->framesInterface()->uploadFrame(keyframe->frameID(), sourceDevice);
172}
173
175{
176 return m_d->paintDevice->framesInterface()->frameBounds(keyframe.dynamicCast<KisRasterKeyframe>()->frameID());
177}
178
180{
181 return m_d->frameFilenames.value(frameId, QString());
182}
183
185{
186 m_d->filenameSuffix = suffix;
187}
188
189void KisRasterKeyframeChannel::setFrameFilename(int frameId, const QString &filename)
190{
191 Q_ASSERT(!m_d->frameFilenames.contains(frameId));
192 m_d->frameFilenames.insert(frameId, filename);
193}
194
195QString KisRasterKeyframeChannel::chooseFrameFilename(int frameId, const QString &layerFilename)
196{
197 QString filename;
198
199 if (m_d->frameFilenames.isEmpty()) {
200 // Use legacy naming convention for first keyframe
201 filename = layerFilename + m_d->filenameSuffix;
202 } else {
203 filename = layerFilename + m_d->filenameSuffix + ".f" + QString::number(frameId);
204 }
205
206 setFrameFilename(frameId, filename);
207
208 return filename;
209}
210
211QDomElement KisRasterKeyframeChannel::toXML(QDomDocument doc, const QString &layerFilename)
212{
213 m_d->frameFilenames.clear();
214
215 return KisKeyframeChannel::toXML(doc, layerFilename);
216}
217
218void KisRasterKeyframeChannel::loadXML(const QDomElement &channelNode)
219{
220 m_d->frameFilenames.clear();
221
222 KisKeyframeChannel::loadXML(channelNode);
223}
224
226{
227 m_d->onionSkinsEnabled = value;
228}
229
231{
232 return m_d->onionSkinsEnabled;
233}
234
239
241{
242 KisRasterKeyframeSP rasterKey = keyframe.dynamicCast<KisRasterKeyframe>();
243 if (rasterKey) {
244 m_d->frameIDTimesMap.insert(rasterKey->frameID(), time);
245 }
246
247 KisKeyframeChannel::insertKeyframe(time, keyframe, parentUndoCmd);
248}
249
251{
252 Q_EMIT sigKeyframeAboutToBeRemoved(this, time);
253
254 KisRasterKeyframeSP rasterKey = keyframeAt<KisRasterKeyframe>(time);
255 if (rasterKey) {
256 m_d->frameIDTimesMap.remove(rasterKey->frameID(), time);
257 }
258
259 KisKeyframeChannel::removeKeyframeImpl(time, parentUndoCmd);
260
261 if (time == 0) { // There should always be a raster frame on frame 0.
262 addKeyframe(time, parentUndoCmd);
263 }
264}
265
266void KisRasterKeyframeChannel::cloneKeyframe(int source, int destination, KUndo2Command *parentUndoCmd)
267{
268 if (!keyframeAt(source)) return;
269
270 insertKeyframe(destination, keyframeAt<KisRasterKeyframe>(source), parentUndoCmd);
271}
272
273bool KisRasterKeyframeChannel::areClones(int timeA, int timeB)
274{
275 /* Edgecase
276 * If both times are empty, we shouldn't really consider the two "clones".. */
277 if (keyframeAt(timeA) == nullptr && keyframeAt(timeB) == nullptr) {
278 return false;
279 }
280
281 return (keyframeAt(timeA) == keyframeAt(timeB));
282}
283
285{
286 KisRasterKeyframeSP rasterKey = keyframeAt<KisRasterKeyframe>(time);
287
288 if (!rasterKey) {
289 return QSet<int>();
290 }
291
292 QList<int> values = m_d->frameIDTimesMap.values(rasterKey->frameID());
293 QSet<int> clones = QSet<int>(values.cbegin(), values.cend());
294 clones.remove(time); // Clones only! Remove input time from the list.
295 return clones;
296}
297
299{
300 QSet<int> clones;
301 if (m_d->frameIDTimesMap.contains(frameID)) {
302 QList<int> values = m_d->frameIDTimesMap.values(frameID);
303 clones = QSet<int>(values.cbegin(), values.cend());
304 }
305 return clones;
306}
307
308QSet<int> KisRasterKeyframeChannel::clonesOf(const KisNode *node, int time)
309{
310 QSet<int> clones;
311
312 QMap<QString, KisKeyframeChannel*> chans = node->keyframeChannels();
313 foreach (KisKeyframeChannel* channel, chans.values()){
314 KisRasterKeyframeChannel* rasterChan = dynamic_cast<KisRasterKeyframeChannel*>(channel);
315 if (!rasterChan) {
316 continue;
317 }
318
319 QSet<int> chanClones = rasterChan->clonesOf(rasterChan->activeKeyframeTime(time));
320 clones += chanClones;
321 }
322
323 return clones;
324}
325
327{
328 KisRasterKeyframeSP rasterKey = keyframeAt<KisRasterKeyframe>(time);
329
330 if (rasterKey && clonesOf(time).count() > 0) {
331 insertKeyframe(time, rasterKey->duplicate(), parentUndoCmd);
332 }
333}
334
336{
337 QRect affectedRect;
338
339 QList<KisRasterKeyframeSP> relevantFrames;
340 relevantFrames.append(keyframeAt<KisRasterKeyframe>(time));
341 relevantFrames.append(keyframeAt<KisRasterKeyframe>(previousKeyframeTime(time)));
342
343 Q_FOREACH (KisRasterKeyframeSP frame, relevantFrames) {
344 if (frame) {
345 affectedRect |= frame->contentBounds();
346 }
347 }
348
349 return affectedRect;
350}
351
352void KisRasterKeyframeChannel::saveKeyframe(KisKeyframeSP keyframe, QDomElement keyframeElement, const QString &layerFilename)
353{
354 KisRasterKeyframeSP rasterKeyframe = keyframe.dynamicCast<KisRasterKeyframe>();
355 KIS_SAFE_ASSERT_RECOVER_RETURN(rasterKeyframe);
356
357 int frame = rasterKeyframe->frameID();
358
359 QString filename = frameFilename(frame);
360 if (filename.isEmpty()) {
361 filename = chooseFrameFilename(frame, layerFilename);
362 }
363 keyframeElement.setAttribute("frame", filename);
364
365 QPoint offset = m_d->paintDevice->framesInterface()->frameOffset(frame);
366 KisDomUtils::saveValue(&keyframeElement, "offset", offset);
367}
368
369QPair<int, KisKeyframeSP> KisRasterKeyframeChannel::loadKeyframe(const QDomElement &keyframeNode)
370{
371 int time = keyframeNode.attribute("time").toInt();
373
374 KisRasterKeyframeSP keyframe;
375
376 QPoint offset;
377 KisDomUtils::loadValue(keyframeNode, "offset", &offset);
378 QString frameFilename = keyframeNode.attribute("frame");
379
380 if (m_d->frameFilenames.isEmpty()) {
381
382 // First keyframe loaded: use the existing frame
384 int firstKeyframeTime = constKeys().begin().key();
385 keyframe = keyframeAt<KisRasterKeyframe>(firstKeyframeTime);
386
387 // Remove from keys. It will get reinserted with new time once we return
389 m_d->paintDevice->framesInterface()->setFrameOffset(keyframe->frameID(), offset);
390 } else {
391
392 // If the filename already exists, it's **probably** a clone we can reinstance.
393 if (m_d->frameFilenames.values().contains(frameFilename)) {
394
395 const int frameId = m_d->frameFilenames.key(frameFilename);
396 const int cloneOf = m_d->frameIDTimesMap.values(frameId).first();
397 const KisRasterKeyframeSP instance = keyframeAt<KisRasterKeyframe>(cloneOf);
398 return QPair<int, KisKeyframeSP>(time, instance);
399 } else {
400
401 keyframe = toQShared(new KisRasterKeyframe(m_d->paintDevice));
402 m_d->paintDevice->framesInterface()->setFrameOffset(keyframe->frameID(), offset);
403 }
404 }
405
406 setFrameFilename(keyframe->frameID(), frameFilename);
407
408 return QPair<int, KisKeyframeSP>(time, keyframe);
409}
410
float value(const T *src, size_t ch)
KisMagneticGraph::vertex_descriptor source(typename KisMagneticGraph::edge_descriptor e, KisMagneticGraph g)
PythonPluginManager * instance
KisKeyframeChannel stores and manages KisKeyframes. Maps units of time to virtual keyframe values....
TimeKeyframeMap & keys()
int previousKeyframeTime(const int time) const
virtual void insertKeyframe(int time, KisKeyframeSP keyframe, KUndo2Command *parentUndoCmd=nullptr)
Insert an existing keyframe into the channel at the specified time.
KisKeyframeSP keyframeAt(int time) const
Get a keyframe at specified time. Used primarily when the value of a given keyframe is needed.
virtual void loadXML(const QDomElement &channelNode)
virtual QDomElement toXML(QDomDocument doc, const QString &layerFilename)
const TimeKeyframeMap & constKeys() const
void addKeyframe(int time, KUndo2Command *parentUndoCmd=nullptr)
Add a new keyframe to the channel at the specified time.
virtual void removeKeyframeImpl(int time, KUndo2Command *parentUndoCmd)
void sigKeyframeAboutToBeRemoved(const KisKeyframeChannel *channel, int time)
This signal is emitted just BEFORE a keyframe is removed from the channel.
int activeKeyframeTime(int time) const
Get the time of the active keyframe. Useful for snapping any time to that of the most recent keyframe...
Q_DECL_DEPRECATED void workaroundBrokenFrameTimeBug(int *time)
Between Krita 4.1 and 4.4 Krita had a bug which resulted in creating frames with negative time stamp....
Krita's base keyframe class. Mainly contained by KisKeyframeChannels. A core part of Krita's animatio...
int colorLabel() const
void setColorLabel(int colorIndex)
void writeFrameToDevice(int frameId, KisPaintDeviceSP targetDevice)
int createFrame(bool copy, int copySrc, const QPoint &offset, KUndo2Command *parentCommand)
void uploadFrame(int srcFrameId, int dstFrameId, KisPaintDeviceSP srcDevice)
void deleteFrame(int frame, KUndo2Command *parentCommand)
KisPaintDeviceFramesInterface * framesInterface()
The KisRasterKeyframeChannel is a concrete KisKeyframeChannel subclass that stores and manages KisRas...
void loadXML(const QDomElement &channelNode) override
QSet< int > timesForFrameID(int frameID) const
virtual void insertKeyframe(int time, KisKeyframeSP keyframe, KUndo2Command *parentUndoCmd=nullptr) override
Insert an existing keyframe into the channel at the specified time.
virtual void removeKeyframe(int time, KUndo2Command *parentUndoCmd=nullptr) override
Remove a keyframe from the channel at the specified time.
void writeToDevice(int time, KisPaintDeviceSP targetDevice)
QPair< int, KisKeyframeSP > loadKeyframe(const QDomElement &keyframeNode) override
void setFilenameSuffix(const QString &suffix)
KisKeyframeSP createKeyframe() override
Virtual keyframe creation function. Derived classes implement this function based on the needs of the...
QRect frameExtents(KisKeyframeSP keyframe)
void makeUnique(int time, KUndo2Command *parentUndoCmd=nullptr)
QDomElement toXML(QDomDocument doc, const QString &layerFilename) override
void cloneKeyframe(int source, int destination, KUndo2Command *parentUndoCmd=nullptr)
QRect affectedRect(int time) const override
void setFrameFilename(int frameId, const QString &filename)
void importFrame(int time, KisPaintDeviceSP sourceDevice, KUndo2Command *parentCommand)
KisRasterKeyframeChannel(const KoID &id, const KisPaintDeviceWSP paintDevice, const KisDefaultBoundsBaseSP bounds)
void saveKeyframe(KisKeyframeSP keyframe, QDomElement keyframeElement, const QString &layerFilename) override
QString chooseFrameFilename(int frameId, const QString &layerFilename)
QString frameFilename(int frameId) const
bool areClones(int timeA, int timeB)
The KisRasterKeyframe class is a concrete subclass of KisKeyframe that wraps a physical raster image ...
int m_frameID
m_frameID is a handle that references the "physical" frame stored in this keyframe's KisPaintDevice,...
int frameID() const
Get the frameID of the "physical" raster frame on the associated KisPaintDevice.
KisRasterKeyframe(KisPaintDeviceWSP paintDevice)
KisKeyframeSP duplicate(KisKeyframeChannel *newChannel=0) override
void writeFrameToDevice(KisPaintDeviceSP writeTarget)
Write this frame's raster content to another paint device. Useful for things like onion skinning wher...
Definition KoID.h:30
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define KIS_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:97
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
#define KIS_ASSERT(cond)
Definition kis_assert.h:33
#define bounds(x, a, b)
QSharedPointer< T > toQShared(T *ptr)
void saveValue(QDomElement *parent, const QString &tag, const QSize &size)
bool loadValue(const QDomElement &e, float *v)
QMap< QString, KisKeyframeChannel * > keyframeChannels
KisPaintDeviceWSP paintDevice
Weak pointer to the KisPaintDevice associated with this channel and a single layer of a KisImage....
Private(KisPaintDeviceWSP paintDevice, const QString filenameSuffix)