File indexing completed on 2024-05-19 04:28:52
0001 /* 0002 * SPDX-FileCopyrightText: 2015 Jouni Pentikäinen <joupent@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "kis_animation_importer.h" 0008 0009 #include <QStatusBar> 0010 0011 #include "KoColorSpace.h" 0012 #include <KoUpdater.h> 0013 #include <QApplication> 0014 #include <QQueue> 0015 #include "KisPart.h" 0016 #include "KisDocument.h" 0017 #include "kis_image.h" 0018 #include "kis_undo_adapter.h" 0019 #include "kis_paint_layer.h" 0020 #include "kis_group_layer.h" 0021 #include "kis_raster_keyframe_channel.h" 0022 #include "kis_assign_profile_processing_visitor.h" 0023 #include "commands/kis_image_layer_add_command.h" 0024 #include <QRegExp> 0025 0026 struct KisAnimationImporter::Private 0027 { 0028 KisImageSP image; 0029 KisDocument *document; 0030 bool stop; 0031 KoUpdaterPtr updater; 0032 }; 0033 0034 KisAnimationImporter::KisAnimationImporter(KisImageSP image, KoUpdaterPtr updater) 0035 : m_d(new Private()) 0036 { 0037 m_d->document = 0; 0038 m_d->image = image; 0039 m_d->stop = false; 0040 m_d->updater = updater; 0041 } 0042 0043 KisAnimationImporter::KisAnimationImporter(KisDocument* document) 0044 : m_d(new Private()) 0045 { 0046 m_d->document= document; 0047 m_d->image = document->image(); 0048 m_d->stop = false; 0049 } 0050 0051 KisAnimationImporter::~KisAnimationImporter() 0052 {} 0053 0054 KisImportExportErrorCode KisAnimationImporter::import(QStringList files, int firstFrame, int step, bool autoAddHoldframes, bool startfrom0, int isAscending, bool assignDocumentProfile, QList<int> optionalKeyframeTimeList) 0055 { 0056 //TODO: We should clean up this code -- 0057 // There are a lot of actions here that we should break into individual methods 0058 // so that we can better control code flow, and I'd prefer to use multiple import 0059 // calls to better handle all of these different options! 0060 // Additionally, we might prefer to use flags for multiple booleans to improve 0061 // legibility of calls. 0062 Q_ASSERT(step > 0); 0063 0064 KisUndoAdapter *undo = m_d->image->undoAdapter(); 0065 undo->beginMacro(kundo2_i18n("Import animation")); 0066 0067 QScopedPointer<KisDocument> importDoc(KisPart::instance()->createDocument()); 0068 importDoc->setFileBatchMode(true); 0069 0070 const bool usingPredefinedTimes = !optionalKeyframeTimeList.isEmpty() && !autoAddHoldframes; 0071 QQueue<int> predefinedFrameQueue; 0072 predefinedFrameQueue.append(optionalKeyframeTimeList); 0073 0074 KisImportExportErrorCode status = ImportExportCodes::OK; 0075 int frame = usingPredefinedTimes ? predefinedFrameQueue.dequeue() : firstFrame; 0076 int filesProcessed = 0; 0077 0078 if (usingPredefinedTimes) { 0079 KIS_ASSERT(files.count() == optionalKeyframeTimeList.count()); 0080 } 0081 0082 if (m_d->updater) { 0083 m_d->updater->setRange(0, files.size()); 0084 } 0085 0086 QPair<KisPaintLayerSP, KisRasterKeyframeChannel*> layerRasterChannelPair; 0087 0088 const QRegExp rx(QLatin1String("(\\d+)")); //regex for extracting numbers 0089 QStringList fileNumberRxList; 0090 0091 int pos = 0; 0092 0093 while ((pos = rx.indexIn(files.at(0), pos)) != -1) { 0094 fileNumberRxList << rx.cap(1); 0095 pos += rx.matchedLength(); 0096 } 0097 0098 int firstFrameNumber = 0; 0099 bool ok; 0100 0101 if (!fileNumberRxList.isEmpty()) { 0102 fileNumberRxList.last().toInt(&ok); // selects the last number of file name of the first frame (useful for descending order) 0103 // Note to self -- ^^ uh.... This isn't doing anything?? Shouldn't this assign `firstFrameNumber`? 0104 } 0105 0106 if (firstFrameNumber == 0){ 0107 startfrom0 = false; // if enabled, the zeroth frame will be places in -1 slot, leading to an error 0108 } 0109 0110 fileNumberRxList.clear(); 0111 const int offset = (startfrom0 ? 1 : 0); //offset added to consider file numbering starts from 1 instead of 0 0112 int autoframe = 0; 0113 0114 KisConfig cfg(true); 0115 0116 Q_FOREACH(QString file, files) { 0117 bool successfullyLoaded = importDoc->openPath(file, KisDocument::DontAddToRecent); 0118 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(successfullyLoaded, ImportExportCodes::InternalError); 0119 0120 if ( (!usingPredefinedTimes && frame == firstFrame) 0121 || (usingPredefinedTimes && frame == optionalKeyframeTimeList.first()) ) { 0122 layerRasterChannelPair = initializePaintLayer(importDoc, undo); 0123 } 0124 0125 if (m_d->updater) { 0126 if (m_d->updater->interrupted()) { 0127 m_d->stop = true; 0128 } else { 0129 m_d->updater->setValue(filesProcessed + 1); 0130 0131 // the updater doesn't call that automatically, 0132 // it is "threaded" by default 0133 qApp->processEvents(); 0134 } 0135 } 0136 0137 if (m_d->stop) { 0138 status = ImportExportCodes::Cancelled; 0139 break; 0140 } 0141 0142 if (cfg.trimFramesImport()) { 0143 importDoc->image()->projection()->crop(m_d->image->bounds()); 0144 } 0145 importDoc->image()->projection()->purgeDefaultPixels(); 0146 0147 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(layerRasterChannelPair.second, ImportExportCodes::InternalError); 0148 0149 if (!autoAddHoldframes) { 0150 layerRasterChannelPair.second->importFrame(frame, importDoc->image()->projection(), NULL); // as first frame added will go to second slot i.e #1 instead of #0 0151 } else { 0152 pos = 0; 0153 0154 while ((pos = rx.indexIn(file, pos)) != -1) { 0155 fileNumberRxList << rx.cap(1); 0156 pos += rx.matchedLength(); 0157 } 0158 0159 int filenum = fileNumberRxList.last().toInt(&ok); 0160 0161 if (isAscending == 0) { 0162 autoframe = firstFrame + filenum - offset; 0163 } else { 0164 autoframe = firstFrame + (firstFrameNumber - filenum); //places the first frame #0 (or #1) slot, and later frames are added as per the difference 0165 } 0166 0167 if (ok) { 0168 layerRasterChannelPair.second->importFrame(autoframe , importDoc->image()->projection(), NULL); 0169 } else { 0170 // if it fails to extract a number, the next frame will simply be added to next slot 0171 layerRasterChannelPair.second->importFrame(autoframe + 1, importDoc->image()->projection(), NULL); 0172 } 0173 fileNumberRxList.clear(); 0174 } 0175 0176 if (usingPredefinedTimes && predefinedFrameQueue.count()) { 0177 frame = predefinedFrameQueue.dequeue(); 0178 } else { 0179 frame += step; 0180 } 0181 0182 filesProcessed++; 0183 } 0184 0185 if (layerRasterChannelPair.first && assignDocumentProfile) { 0186 0187 if (layerRasterChannelPair.first->colorSpace()->colorModelId() == m_d->image->colorSpace()->colorModelId()) { 0188 0189 const KoColorSpace *srcColorSpace = layerRasterChannelPair.first->colorSpace(); 0190 const KoColorSpace *dstColorSpace = KoColorSpaceRegistry::instance()->colorSpace( 0191 srcColorSpace->colorModelId().id() 0192 , srcColorSpace->colorDepthId().id() 0193 , m_d->image->colorSpace()->profile()); 0194 0195 KisAssignProfileProcessingVisitor *visitor = new KisAssignProfileProcessingVisitor(srcColorSpace, dstColorSpace); 0196 visitor->visit(layerRasterChannelPair.first.data(), undo); 0197 } 0198 } 0199 0200 undo->endMacro(); 0201 0202 return status; 0203 } 0204 0205 QPair<KisPaintLayerSP, KisRasterKeyframeChannel*> KisAnimationImporter::initializePaintLayer(QScopedPointer<KisDocument>& doc, KisUndoAdapter *undoAdapter) 0206 { 0207 const KoColorSpace *cs = doc->image()->colorSpace(); 0208 KisPaintLayerSP paintLayer = new KisPaintLayer(m_d->image, m_d->image->nextLayerName(), OPACITY_OPAQUE_U8, cs); 0209 undoAdapter->addCommand(new KisImageLayerAddCommand(m_d->image, paintLayer, m_d->image->rootLayer(), m_d->image->rootLayer()->childCount())); 0210 0211 paintLayer->enableAnimation(); 0212 KisRasterKeyframeChannel* contentChannel = qobject_cast<KisRasterKeyframeChannel*>(paintLayer->getKeyframeChannel(KisKeyframeChannel::Raster.id(), true)); 0213 return QPair<KisPaintLayerSP, KisRasterKeyframeChannel*>(paintLayer, contentChannel); 0214 } 0215 0216 void KisAnimationImporter::cancel() 0217 { 0218 m_d->stop = true; 0219 }