File indexing completed on 2024-05-19 04:26:32
0001 /* 0002 * SPDX-FileCopyrightText: 2015 Jouni Pentikäinen <joupent@gmail.com> 0003 * SPDX-FileCopyrightText: 2020 Emmet O 'Neill <emmetoneill.pdx@gmail.com> 0004 * SPDX-FileCopyrightText: 2020 Eoin O 'Neill <eoinoneill1991@gmail.com> 0005 * 0006 * SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 #include "kis_raster_keyframe_channel.h" 0009 #include "kis_node.h" 0010 #include "kis_dom_utils.h" 0011 0012 #include "kis_global.h" 0013 #include "kis_paint_device.h" 0014 #include "kis_paint_device_frames_interface.h" 0015 #include "kis_time_span.h" 0016 #include "kundo2command.h" 0017 #include "kis_onion_skin_compositor.h" 0018 #include "kis_layer_utils.h" 0019 0020 KisRasterKeyframe::KisRasterKeyframe(KisPaintDeviceWSP paintDevice) 0021 : KisKeyframe() 0022 { 0023 m_paintDevice = paintDevice; 0024 KIS_ASSERT(m_paintDevice); 0025 0026 m_frameID = m_paintDevice->framesInterface()->createFrame(false, 0, QPoint(), nullptr); 0027 } 0028 0029 KisRasterKeyframe::KisRasterKeyframe(KisPaintDeviceWSP paintDevice, const int &premadeFrameID, const int &colorLabelId) 0030 : KisKeyframe() 0031 { 0032 m_paintDevice = paintDevice; 0033 m_frameID = premadeFrameID; 0034 setColorLabel(colorLabelId); 0035 0036 KIS_ASSERT(m_paintDevice); 0037 } 0038 0039 KisRasterKeyframe::~KisRasterKeyframe() 0040 { 0041 // Note: Because keyframe ownership is shared, it's possible for them to outlive 0042 // the paint device. 0043 if (m_paintDevice && m_paintDevice->framesInterface()) { 0044 m_paintDevice->framesInterface()->deleteFrame(m_frameID, nullptr); 0045 } 0046 } 0047 0048 int KisRasterKeyframe::frameID() const 0049 { 0050 return m_frameID; 0051 } 0052 0053 QRect KisRasterKeyframe::contentBounds() 0054 { 0055 if (!m_paintDevice) { 0056 return QRect(); 0057 } 0058 0059 return m_paintDevice->framesInterface()->frameBounds(m_frameID); 0060 } 0061 0062 bool KisRasterKeyframe::hasContent() 0063 { 0064 return !m_paintDevice->framesInterface()->frameBounds(m_frameID).isEmpty(); 0065 } 0066 0067 void KisRasterKeyframe::writeFrameToDevice(KisPaintDeviceSP writeTarget) 0068 { 0069 KIS_SAFE_ASSERT_RECOVER_RETURN(m_paintDevice); 0070 0071 m_paintDevice->framesInterface()->writeFrameToDevice(m_frameID, writeTarget); 0072 } 0073 0074 KisKeyframeSP KisRasterKeyframe::duplicate(KisKeyframeChannel *newChannel) 0075 { 0076 if (newChannel) { 0077 KisRasterKeyframeChannel* rasterChannel = dynamic_cast<KisRasterKeyframeChannel*>(newChannel); 0078 KIS_ASSERT(rasterChannel); 0079 KisPaintDeviceWSP targetDevice = rasterChannel->paintDevice(); 0080 0081 if (targetDevice != m_paintDevice) { 0082 int targetFrameID = targetDevice->framesInterface()->createFrame(false, 0, QPoint(), nullptr); 0083 targetDevice->framesInterface()->uploadFrame(m_frameID, targetFrameID, m_paintDevice); 0084 KisKeyframeSP key = toQShared(new KisRasterKeyframe(targetDevice, targetFrameID )); 0085 key->setColorLabel(colorLabel()); 0086 return key; 0087 } 0088 } 0089 0090 int copyFrameID = m_paintDevice->framesInterface()->createFrame(true, m_frameID, QPoint(), nullptr); 0091 KisKeyframeSP key = toQShared(new KisRasterKeyframe(m_paintDevice, copyFrameID)); 0092 key->setColorLabel(colorLabel()); 0093 return key; 0094 } 0095 0096 0097 // =========================================================================================================== 0098 // =======================================KisRasterKeyframeChannel============================================ 0099 // =========================================================================================================== 0100 0101 0102 struct KisRasterKeyframeChannel::Private 0103 { 0104 Private(KisPaintDeviceWSP paintDevice, const QString filenameSuffix) 0105 : paintDevice(paintDevice), 0106 filenameSuffix(filenameSuffix), 0107 onionSkinsEnabled(false) 0108 {} 0109 0110 /** @brief Weak pointer to the KisPaintDevice associated with this 0111 * channel and a single layer of a KisImage. While the channel maintains 0112 * "virtual" KisRasterKeyframes, the real "physical" frame images are stored 0113 * within this paint device at the frameID index held in the KisRasterKeyframe. */ 0114 KisPaintDeviceWSP paintDevice; 0115 0116 QMultiHash<int, int> frameIDTimesMap; 0117 0118 QMap<int, QString> frameFilenames; 0119 QString filenameSuffix; 0120 bool onionSkinsEnabled; 0121 }; 0122 0123 KisRasterKeyframeChannel::KisRasterKeyframeChannel(const KoID &id, const KisPaintDeviceWSP paintDevice, const KisDefaultBoundsBaseSP bounds) 0124 : KisKeyframeChannel(id, bounds), 0125 m_d(new Private(paintDevice, QString())) 0126 { 0127 } 0128 0129 KisRasterKeyframeChannel::KisRasterKeyframeChannel(const KisRasterKeyframeChannel &rhs, const KisPaintDeviceWSP newPaintDevice) 0130 : KisKeyframeChannel(rhs), 0131 m_d(new Private(newPaintDevice, rhs.m_d->filenameSuffix)) 0132 { 0133 KIS_ASSERT_RECOVER_NOOP(&rhs != this); 0134 0135 m_d->frameFilenames = rhs.m_d->frameFilenames; 0136 m_d->onionSkinsEnabled = rhs.m_d->onionSkinsEnabled; 0137 0138 // Copy keyframes with attention to clones.. 0139 foreach (const int& frame, rhs.constKeys().keys()) { 0140 KisRasterKeyframeSP copySource = rhs.keyframeAt<KisRasterKeyframe>(frame); 0141 if (m_d->frameIDTimesMap.contains(copySource->frameID())){ 0142 continue; 0143 } 0144 0145 KisRasterKeyframeSP transferredKey = toQShared(new KisRasterKeyframe(newPaintDevice, copySource->frameID(), copySource->colorLabel())); 0146 foreach (const int& time, rhs.m_d->frameIDTimesMap.values(transferredKey->frameID())) { 0147 keys().insert(time, transferredKey); 0148 m_d->frameIDTimesMap.insert(transferredKey->frameID(), time); 0149 } 0150 } 0151 } 0152 0153 KisRasterKeyframeChannel::~KisRasterKeyframeChannel() 0154 { 0155 } 0156 0157 void KisRasterKeyframeChannel::writeToDevice(int time, KisPaintDeviceSP targetDevice) 0158 { 0159 KisRasterKeyframeSP key = keyframeAt<KisRasterKeyframe>(time); 0160 if (!key) { 0161 key = activeKeyframeAt<KisRasterKeyframe>(time); 0162 } 0163 0164 key->writeFrameToDevice(targetDevice); 0165 } 0166 0167 void KisRasterKeyframeChannel::importFrame(int time, KisPaintDeviceSP sourceDevice, KUndo2Command *parentCommand) 0168 { 0169 addKeyframe(time, parentCommand); 0170 KisRasterKeyframeSP keyframe = keyframeAt<KisRasterKeyframe>(time); 0171 m_d->paintDevice->framesInterface()->uploadFrame(keyframe->frameID(), sourceDevice); 0172 } 0173 0174 QRect KisRasterKeyframeChannel::frameExtents(KisKeyframeSP keyframe) 0175 { 0176 return m_d->paintDevice->framesInterface()->frameBounds(keyframe.dynamicCast<KisRasterKeyframe>()->frameID()); 0177 } 0178 0179 QString KisRasterKeyframeChannel::frameFilename(int frameId) const 0180 { 0181 return m_d->frameFilenames.value(frameId, QString()); 0182 } 0183 0184 void KisRasterKeyframeChannel::setFilenameSuffix(const QString &suffix) 0185 { 0186 m_d->filenameSuffix = suffix; 0187 } 0188 0189 void KisRasterKeyframeChannel::setFrameFilename(int frameId, const QString &filename) 0190 { 0191 Q_ASSERT(!m_d->frameFilenames.contains(frameId)); 0192 m_d->frameFilenames.insert(frameId, filename); 0193 } 0194 0195 QString KisRasterKeyframeChannel::chooseFrameFilename(int frameId, const QString &layerFilename) 0196 { 0197 QString filename; 0198 0199 if (m_d->frameFilenames.isEmpty()) { 0200 // Use legacy naming convention for first keyframe 0201 filename = layerFilename + m_d->filenameSuffix; 0202 } else { 0203 filename = layerFilename + m_d->filenameSuffix + ".f" + QString::number(frameId); 0204 } 0205 0206 setFrameFilename(frameId, filename); 0207 0208 return filename; 0209 } 0210 0211 QDomElement KisRasterKeyframeChannel::toXML(QDomDocument doc, const QString &layerFilename) 0212 { 0213 m_d->frameFilenames.clear(); 0214 0215 return KisKeyframeChannel::toXML(doc, layerFilename); 0216 } 0217 0218 void KisRasterKeyframeChannel::loadXML(const QDomElement &channelNode) 0219 { 0220 m_d->frameFilenames.clear(); 0221 0222 KisKeyframeChannel::loadXML(channelNode); 0223 } 0224 0225 void KisRasterKeyframeChannel::setOnionSkinsEnabled(bool value) 0226 { 0227 m_d->onionSkinsEnabled = value; 0228 } 0229 0230 bool KisRasterKeyframeChannel::onionSkinsEnabled() const 0231 { 0232 return m_d->onionSkinsEnabled; 0233 } 0234 0235 KisPaintDeviceWSP KisRasterKeyframeChannel::paintDevice() 0236 { 0237 return m_d->paintDevice; 0238 } 0239 0240 void KisRasterKeyframeChannel::insertKeyframe(int time, KisKeyframeSP keyframe, KUndo2Command *parentUndoCmd) 0241 { 0242 KisRasterKeyframeSP rasterKey = keyframe.dynamicCast<KisRasterKeyframe>(); 0243 if (rasterKey) { 0244 m_d->frameIDTimesMap.insert(rasterKey->frameID(), time); 0245 } 0246 0247 KisKeyframeChannel::insertKeyframe(time, keyframe, parentUndoCmd); 0248 } 0249 0250 void KisRasterKeyframeChannel::removeKeyframe(int time, KUndo2Command *parentUndoCmd) 0251 { 0252 emit sigKeyframeAboutToBeRemoved(this, time); 0253 0254 KisRasterKeyframeSP rasterKey = keyframeAt<KisRasterKeyframe>(time); 0255 if (rasterKey) { 0256 m_d->frameIDTimesMap.remove(rasterKey->frameID(), time); 0257 } 0258 0259 KisKeyframeChannel::removeKeyframeImpl(time, parentUndoCmd); 0260 0261 if (time == 0) { // There should always be a raster frame on frame 0. 0262 addKeyframe(time, parentUndoCmd); 0263 } 0264 } 0265 0266 void KisRasterKeyframeChannel::cloneKeyframe(int source, int destination, KUndo2Command *parentUndoCmd) 0267 { 0268 if (!keyframeAt(source)) return; 0269 0270 insertKeyframe(destination, keyframeAt<KisRasterKeyframe>(source), parentUndoCmd); 0271 } 0272 0273 bool KisRasterKeyframeChannel::areClones(int timeA, int timeB) 0274 { 0275 /* Edgecase 0276 * If both times are empty, we shouldn't really consider the two "clones".. */ 0277 if (keyframeAt(timeA) == nullptr && keyframeAt(timeB) == nullptr) { 0278 return false; 0279 } 0280 0281 return (keyframeAt(timeA) == keyframeAt(timeB)); 0282 } 0283 0284 QSet<int> KisRasterKeyframeChannel::clonesOf(int time) 0285 { 0286 KisRasterKeyframeSP rasterKey = keyframeAt<KisRasterKeyframe>(time); 0287 0288 if (!rasterKey) { 0289 return QSet<int>(); 0290 } 0291 0292 QList<int> values = m_d->frameIDTimesMap.values(rasterKey->frameID()); 0293 #if QT_VERSION >= QT_VERSION_CHECK(5,14,0) 0294 QSet<int> clones = QSet<int>(values.cbegin(), values.cend()); 0295 #else 0296 QSet<int> clones = QSet<int>::fromList(values); 0297 #endif 0298 clones.remove(time); // Clones only! Remove input time from the list. 0299 return clones; 0300 } 0301 0302 QSet<int> KisRasterKeyframeChannel::timesForFrameID(int frameID) const 0303 { 0304 QSet<int> clones; 0305 if (m_d->frameIDTimesMap.contains(frameID)) { 0306 QList<int> values = m_d->frameIDTimesMap.values(frameID); 0307 #if QT_VERSION >= QT_VERSION_CHECK(5,14,0) 0308 clones = QSet<int>(values.cbegin(), values.cend()); 0309 #else 0310 clones = QSet<int>::fromList(values); 0311 #endif 0312 } 0313 return clones; 0314 } 0315 0316 QSet<int> KisRasterKeyframeChannel::clonesOf(const KisNode *node, int time) 0317 { 0318 QSet<int> clones; 0319 0320 QMap<QString, KisKeyframeChannel*> chans = node->keyframeChannels(); 0321 foreach (KisKeyframeChannel* channel, chans.values()){ 0322 KisRasterKeyframeChannel* rasterChan = dynamic_cast<KisRasterKeyframeChannel*>(channel); 0323 if (!rasterChan) { 0324 continue; 0325 } 0326 0327 QSet<int> chanClones = rasterChan->clonesOf(rasterChan->activeKeyframeTime(time)); 0328 clones += chanClones; 0329 } 0330 0331 return clones; 0332 } 0333 0334 void KisRasterKeyframeChannel::makeUnique(int time, KUndo2Command* parentUndoCmd) 0335 { 0336 KisRasterKeyframeSP rasterKey = keyframeAt<KisRasterKeyframe>(time); 0337 0338 if (rasterKey && clonesOf(time).count() > 0) { 0339 insertKeyframe(time, rasterKey->duplicate(), parentUndoCmd); 0340 } 0341 } 0342 0343 QRect KisRasterKeyframeChannel::affectedRect(int time) const 0344 { 0345 QRect affectedRect; 0346 0347 QList<KisRasterKeyframeSP> relevantFrames; 0348 relevantFrames.append(keyframeAt<KisRasterKeyframe>(time)); 0349 relevantFrames.append(keyframeAt<KisRasterKeyframe>(previousKeyframeTime(time))); 0350 0351 Q_FOREACH (KisRasterKeyframeSP frame, relevantFrames) { 0352 if (frame) { 0353 affectedRect |= frame->contentBounds(); 0354 } 0355 } 0356 0357 return affectedRect; 0358 } 0359 0360 void KisRasterKeyframeChannel::saveKeyframe(KisKeyframeSP keyframe, QDomElement keyframeElement, const QString &layerFilename) 0361 { 0362 KisRasterKeyframeSP rasterKeyframe = keyframe.dynamicCast<KisRasterKeyframe>(); 0363 KIS_SAFE_ASSERT_RECOVER_RETURN(rasterKeyframe); 0364 0365 int frame = rasterKeyframe->frameID(); 0366 0367 QString filename = frameFilename(frame); 0368 if (filename.isEmpty()) { 0369 filename = chooseFrameFilename(frame, layerFilename); 0370 } 0371 keyframeElement.setAttribute("frame", filename); 0372 0373 QPoint offset = m_d->paintDevice->framesInterface()->frameOffset(frame); 0374 KisDomUtils::saveValue(&keyframeElement, "offset", offset); 0375 } 0376 0377 QPair<int, KisKeyframeSP> KisRasterKeyframeChannel::loadKeyframe(const QDomElement &keyframeNode) 0378 { 0379 int time = keyframeNode.attribute("time").toInt(); 0380 workaroundBrokenFrameTimeBug(&time); 0381 0382 KisRasterKeyframeSP keyframe; 0383 0384 QPoint offset; 0385 KisDomUtils::loadValue(keyframeNode, "offset", &offset); 0386 QString frameFilename = keyframeNode.attribute("frame"); 0387 0388 if (m_d->frameFilenames.isEmpty()) { 0389 0390 // First keyframe loaded: use the existing frame 0391 KIS_SAFE_ASSERT_RECOVER_NOOP(keyframeCount() == 1); 0392 int firstKeyframeTime = constKeys().begin().key(); 0393 keyframe = keyframeAt<KisRasterKeyframe>(firstKeyframeTime); 0394 0395 // Remove from keys. It will get reinserted with new time once we return 0396 removeKeyframe(firstKeyframeTime); 0397 m_d->paintDevice->framesInterface()->setFrameOffset(keyframe->frameID(), offset); 0398 } else { 0399 0400 // If the filename already exists, it's **probably** a clone we can reinstance. 0401 if (m_d->frameFilenames.values().contains(frameFilename)) { 0402 0403 const int frameId = m_d->frameFilenames.key(frameFilename); 0404 const int cloneOf = m_d->frameIDTimesMap.values(frameId).first(); 0405 const KisRasterKeyframeSP instance = keyframeAt<KisRasterKeyframe>(cloneOf); 0406 return QPair<int, KisKeyframeSP>(time, instance); 0407 } else { 0408 0409 keyframe = toQShared(new KisRasterKeyframe(m_d->paintDevice)); 0410 m_d->paintDevice->framesInterface()->setFrameOffset(keyframe->frameID(), offset); 0411 } 0412 } 0413 0414 setFrameFilename(keyframe->frameID(), frameFilename); 0415 0416 return QPair<int, KisKeyframeSP>(time, keyframe); 0417 } 0418 0419 KisKeyframeSP KisRasterKeyframeChannel::createKeyframe() 0420 { 0421 return toQShared(new KisRasterKeyframe(m_d->paintDevice)); 0422 }