File indexing completed on 2024-05-12 16:01:45

0001 /*
0002  *  SPDX-FileCopyrightText: 2017 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "KisAsyncAnimationFramesSavingRenderer.h"
0008 
0009 #include "kis_image.h"
0010 #include "kis_paint_device.h"
0011 #include "KisImportExportFilter.h"
0012 #include "KisPart.h"
0013 #include "KisDocument.h"
0014 #include "kis_time_span.h"
0015 #include "kis_paint_layer.h"
0016 
0017 
0018 struct KisAsyncAnimationFramesSavingRenderer::Private
0019 {
0020     Private(KisImageSP image, const KisTimeSpan &_range, int _sequenceNumberingOffset, bool _onlyNeedsUniqueFrames, KisPropertiesConfigurationSP _exportConfiguration)
0021         : savingDoc(KisPart::instance()->createDocument()),
0022           range(_range),
0023           sequenceNumberingOffset(_sequenceNumberingOffset),
0024           onlyNeedsUniqueFrames(_onlyNeedsUniqueFrames),
0025           exportConfiguration(_exportConfiguration)
0026     {
0027 
0028         savingDoc->setInfiniteAutoSaveInterval();
0029         savingDoc->setFileBatchMode(true);
0030 
0031         KisImageSP savingImage = new KisImage(savingDoc->createUndoStore(),
0032                                               image->bounds().width(),
0033                                               image->bounds().height(),
0034                                               image->colorSpace(),
0035                                               QString());
0036 
0037         savingImage->setResolution(image->xRes(), image->yRes());
0038         savingDoc->setCurrentImage(savingImage);
0039 
0040         KisPaintLayer* paintLayer = new KisPaintLayer(savingImage, "paint device", 255);
0041         savingImage->addNode(paintLayer, savingImage->root(), KisLayerSP(0));
0042 
0043         savingDevice = paintLayer->paintDevice();
0044     }
0045 
0046     QScopedPointer<KisDocument> savingDoc;
0047     KisPaintDeviceSP savingDevice;
0048 
0049     KisTimeSpan range;
0050     int sequenceNumberingOffset = 0;
0051 
0052     bool onlyNeedsUniqueFrames;
0053 
0054     QString filenamePrefix;
0055     QString filenameSuffix;
0056 
0057     QByteArray outputMimeType;
0058     KisPropertiesConfigurationSP exportConfiguration;
0059 };
0060 
0061 KisAsyncAnimationFramesSavingRenderer::KisAsyncAnimationFramesSavingRenderer(KisImageSP image,
0062                                                                              const QString &fileNamePrefix,
0063                                                                              const QString &fileNameSuffix,
0064                                                                              const QByteArray &outputMimeType,
0065                                                                              const KisTimeSpan &range,
0066                                                                              const int startNumberingAt,
0067                                                                              const bool onlyNeedsUniqueFrames,
0068                                                                              KisPropertiesConfigurationSP exportConfiguration)
0069     : m_d(new Private(image, range, qMax(startNumberingAt - range.start(), range.start() * -1), onlyNeedsUniqueFrames, exportConfiguration))
0070 {
0071     m_d->filenamePrefix = fileNamePrefix;
0072     m_d->filenameSuffix = fileNameSuffix;
0073     m_d->outputMimeType = outputMimeType;
0074 
0075     connect(this, SIGNAL(sigCompleteRegenerationInternal(int)), SLOT(notifyFrameCompleted(int)));
0076     connect(this, SIGNAL(sigCancelRegenerationInternal(int, KisAsyncAnimationRendererBase::CancelReason)), SLOT(notifyFrameCancelled(int, KisAsyncAnimationRendererBase::CancelReason)));
0077 }
0078 
0079 
0080 
0081 
0082 KisAsyncAnimationFramesSavingRenderer::~KisAsyncAnimationFramesSavingRenderer()
0083 {
0084 }
0085 
0086 void KisAsyncAnimationFramesSavingRenderer::frameCompletedCallback(int frame, const KisRegion &requestedRegion)
0087 {
0088     KisImageSP image = requestedImage();
0089     if (!image) return;
0090 
0091     KIS_SAFE_ASSERT_RECOVER (requestedRegion == image->bounds()) {
0092         emit sigCancelRegenerationInternal(frame, KisAsyncAnimationRendererBase::RenderingFailed);
0093         return;
0094     }
0095 
0096     m_d->savingDevice->makeCloneFromRough(image->projection(), image->bounds());
0097 
0098     KisImportExportErrorCode status = ImportExportCodes::OK;
0099 
0100     QString frameNumber = QString("%1").arg(frame + m_d->sequenceNumberingOffset, 4, 10, QChar('0'));
0101     QString filename = m_d->filenamePrefix + frameNumber + m_d->filenameSuffix;
0102 
0103     if (!m_d->savingDoc->exportDocumentSync(filename, m_d->outputMimeType, m_d->exportConfiguration)) {
0104         status = ImportExportCodes::InternalError;
0105     }
0106 
0107     //Get all identical frames to this one and either copy or symlink based on settings.
0108     KisTimeSpan identicals = KisTimeSpan::calculateIdenticalFramesRecursive(image->root(), frame);
0109     identicals &= m_d->range;
0110     if( !m_d->onlyNeedsUniqueFrames && identicals.start() < identicals.end() ) {
0111         for (int identicalFrame = (identicals.start() + 1); identicalFrame <= identicals.end(); identicalFrame++) {
0112             QString identicalFrameNumber = QString("%1").arg(identicalFrame + m_d->sequenceNumberingOffset, 4, 10, QChar('0'));
0113             QString identicalFrameName = m_d->filenamePrefix + identicalFrameNumber + m_d->filenameSuffix;
0114 
0115             QFile::copy(filename, identicalFrameName);
0116 
0117             /*  This would be nice to do but sym-linking on windows isn't possible without
0118              *  way more other work to be done. This works on linux though!
0119              *
0120              *  if (m_d->linkRedundantFrames) {
0121              *      QFile::link(filename, identicalFrameName);
0122              *  } else {
0123              *      QFile::copy(filename, identicalFrameName);
0124              *  }
0125              */
0126         }
0127     }
0128 
0129     if (status.isOk()) {
0130         emit sigCompleteRegenerationInternal(frame);
0131     } else {
0132         emit sigCancelRegenerationInternal(frame, KisAsyncAnimationRendererBase::RenderingFailed);
0133     }
0134 }
0135 
0136 void KisAsyncAnimationFramesSavingRenderer::frameCancelledCallback(int frame, CancelReason cancelReason)
0137 {
0138     notifyFrameCancelled(frame, cancelReason);
0139 }
0140