File indexing completed on 2024-05-12 15:58:39
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 KisKeyframeChannel::insertKeyframe(time, keyframe, parentUndoCmd); 0243 0244 KisRasterKeyframeSP rasterKey = keyframe.dynamicCast<KisRasterKeyframe>(); 0245 if (rasterKey) { 0246 m_d->frameIDTimesMap.insert(rasterKey->frameID(), time); 0247 } 0248 } 0249 0250 void KisRasterKeyframeChannel::removeKeyframe(int time, KUndo2Command *parentUndoCmd) 0251 { 0252 KisRasterKeyframeSP rasterKey = keyframeAt<KisRasterKeyframe>(time); 0253 if (rasterKey) { 0254 m_d->frameIDTimesMap.remove(rasterKey->frameID(), time); 0255 } 0256 0257 KisKeyframeChannel::removeKeyframe(time, parentUndoCmd); 0258 0259 if (time == 0) { // There should always be a raster frame on frame 0. 0260 addKeyframe(time, parentUndoCmd); 0261 } 0262 } 0263 0264 void KisRasterKeyframeChannel::cloneKeyframe(int source, int destination, KUndo2Command *parentUndoCmd) 0265 { 0266 if (!keyframeAt(source)) return; 0267 0268 insertKeyframe(destination, keyframeAt<KisRasterKeyframe>(source), parentUndoCmd); 0269 } 0270 0271 bool KisRasterKeyframeChannel::areClones(int timeA, int timeB) 0272 { 0273 /* Edgecase 0274 * If both times are empty, we shouldn't really consider the two "clones".. */ 0275 if (keyframeAt(timeA) == nullptr && keyframeAt(timeB) == nullptr) { 0276 return false; 0277 } 0278 0279 return (keyframeAt(timeA) == keyframeAt(timeB)); 0280 } 0281 0282 QSet<int> KisRasterKeyframeChannel::clonesOf(int time) 0283 { 0284 KisRasterKeyframeSP rasterKey = keyframeAt<KisRasterKeyframe>(time); 0285 0286 if (!rasterKey) { 0287 return QSet<int>(); 0288 } 0289 0290 QList<int> values = m_d->frameIDTimesMap.values(rasterKey->frameID()); 0291 #if QT_VERSION >= QT_VERSION_CHECK(5,14,0) 0292 QSet<int> clones = QSet<int>(values.cbegin(), values.cend()); 0293 #else 0294 QSet<int> clones = QSet<int>::fromList(values); 0295 #endif 0296 clones.remove(time); // Clones only! Remove input time from the list. 0297 return clones; 0298 } 0299 0300 QSet<int> KisRasterKeyframeChannel::timesForFrameID(int frameID) const 0301 { 0302 QSet<int> clones; 0303 if (m_d->frameIDTimesMap.contains(frameID)) { 0304 QList<int> values = m_d->frameIDTimesMap.values(frameID); 0305 #if QT_VERSION >= QT_VERSION_CHECK(5,14,0) 0306 clones = QSet<int>(values.cbegin(), values.cend()); 0307 #else 0308 clones = QSet<int>::fromList(values); 0309 #endif 0310 } 0311 return clones; 0312 } 0313 0314 QSet<int> KisRasterKeyframeChannel::clonesOf(const KisNode *node, int time) 0315 { 0316 QSet<int> clones; 0317 0318 QMap<QString, KisKeyframeChannel*> chans = node->keyframeChannels(); 0319 foreach (KisKeyframeChannel* channel, chans.values()){ 0320 KisRasterKeyframeChannel* rasterChan = dynamic_cast<KisRasterKeyframeChannel*>(channel); 0321 if (!rasterChan) { 0322 continue; 0323 } 0324 0325 QSet<int> chanClones = rasterChan->clonesOf(rasterChan->activeKeyframeTime(time)); 0326 clones += chanClones; 0327 } 0328 0329 return clones; 0330 } 0331 0332 void KisRasterKeyframeChannel::makeUnique(int time, KUndo2Command* parentUndoCmd) 0333 { 0334 KisRasterKeyframeSP rasterKey = keyframeAt<KisRasterKeyframe>(time); 0335 0336 if (rasterKey && clonesOf(time).count() > 0) { 0337 insertKeyframe(time, rasterKey->duplicate(), parentUndoCmd); 0338 } 0339 } 0340 0341 QRect KisRasterKeyframeChannel::affectedRect(int time) const 0342 { 0343 QRect affectedRect; 0344 0345 QList<KisRasterKeyframeSP> relevantFrames; 0346 relevantFrames.append(keyframeAt<KisRasterKeyframe>(time)); 0347 relevantFrames.append(keyframeAt<KisRasterKeyframe>(previousKeyframeTime(time))); 0348 0349 Q_FOREACH (KisRasterKeyframeSP frame, relevantFrames) { 0350 if (frame) { 0351 affectedRect |= frame->contentBounds(); 0352 } 0353 } 0354 0355 return affectedRect; 0356 } 0357 0358 void KisRasterKeyframeChannel::saveKeyframe(KisKeyframeSP keyframe, QDomElement keyframeElement, const QString &layerFilename) 0359 { 0360 KisRasterKeyframeSP rasterKeyframe = keyframe.dynamicCast<KisRasterKeyframe>(); 0361 KIS_SAFE_ASSERT_RECOVER_RETURN(rasterKeyframe); 0362 0363 int frame = rasterKeyframe->frameID(); 0364 0365 QString filename = frameFilename(frame); 0366 if (filename.isEmpty()) { 0367 filename = chooseFrameFilename(frame, layerFilename); 0368 } 0369 keyframeElement.setAttribute("frame", filename); 0370 0371 QPoint offset = m_d->paintDevice->framesInterface()->frameOffset(frame); 0372 KisDomUtils::saveValue(&keyframeElement, "offset", offset); 0373 } 0374 0375 QPair<int, KisKeyframeSP> KisRasterKeyframeChannel::loadKeyframe(const QDomElement &keyframeNode) 0376 { 0377 int time = keyframeNode.attribute("time").toInt(); 0378 workaroundBrokenFrameTimeBug(&time); 0379 0380 KisRasterKeyframeSP keyframe; 0381 0382 QPoint offset; 0383 KisDomUtils::loadValue(keyframeNode, "offset", &offset); 0384 QString frameFilename = keyframeNode.attribute("frame"); 0385 0386 if (m_d->frameFilenames.isEmpty()) { 0387 0388 // First keyframe loaded: use the existing frame 0389 KIS_SAFE_ASSERT_RECOVER_NOOP(keyframeCount() == 1); 0390 int firstKeyframeTime = constKeys().begin().key(); 0391 keyframe = keyframeAt<KisRasterKeyframe>(firstKeyframeTime); 0392 0393 // Remove from keys. It will get reinserted with new time once we return 0394 removeKeyframe(firstKeyframeTime); 0395 m_d->paintDevice->framesInterface()->setFrameOffset(keyframe->frameID(), offset); 0396 } else { 0397 0398 // If the filename already exists, it's **probably** a clone we can reinstance. 0399 if (m_d->frameFilenames.values().contains(frameFilename)) { 0400 0401 const int frameId = m_d->frameFilenames.key(frameFilename); 0402 const int cloneOf = m_d->frameIDTimesMap.values(frameId).first(); 0403 const KisRasterKeyframeSP instance = keyframeAt<KisRasterKeyframe>(cloneOf); 0404 return QPair<int, KisKeyframeSP>(time, instance); 0405 } else { 0406 0407 keyframe = toQShared(new KisRasterKeyframe(m_d->paintDevice)); 0408 m_d->paintDevice->framesInterface()->setFrameOffset(keyframe->frameID(), offset); 0409 } 0410 } 0411 0412 setFrameFilename(keyframe->frameID(), frameFilename); 0413 0414 return QPair<int, KisKeyframeSP>(time, keyframe); 0415 } 0416 0417 KisKeyframeSP KisRasterKeyframeChannel::createKeyframe() 0418 { 0419 return toQShared(new KisRasterKeyframe(m_d->paintDevice)); 0420 }