diff --git a/libs/ui/KisAsyncAnimationFramesSavingRenderer.cpp b/libs/ui/KisAsyncAnimationFramesSavingRenderer.cpp index 98ea261f28..c60e9a12cb 100644 --- a/libs/ui/KisAsyncAnimationFramesSavingRenderer.cpp +++ b/libs/ui/KisAsyncAnimationFramesSavingRenderer.cpp @@ -1,151 +1,152 @@ /* * Copyright (c) 2017 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KisAsyncAnimationFramesSavingRenderer.h" #include "kis_image.h" #include "kis_paint_device.h" #include "KisImportExportFilter.h" #include "KisPart.h" #include "KisDocument.h" #include "kis_time_range.h" #include "kis_paint_layer.h" struct KisAsyncAnimationFramesSavingRenderer::Private { Private(KisImageSP image, const KisTimeRange &_range, int _sequenceNumberingOffset, bool _onlyNeedsUniqueFrames, KisPropertiesConfigurationSP _exportConfiguration) : savingDoc(KisPart::instance()->createDocument()), range(_range), sequenceNumberingOffset(_sequenceNumberingOffset), onlyNeedsUniqueFrames(_onlyNeedsUniqueFrames), exportConfiguration(_exportConfiguration) { savingDoc->setInfiniteAutoSaveInterval(); savingDoc->setFileBatchMode(true); KisImageSP savingImage = new KisImage(savingDoc->createUndoStore(), image->bounds().width(), image->bounds().height(), image->colorSpace(), QString()); savingImage->setResolution(image->xRes(), image->yRes()); savingDoc->setCurrentImage(savingImage); KisPaintLayer* paintLayer = new KisPaintLayer(savingImage, "paint device", 255); savingImage->addNode(paintLayer, savingImage->root(), KisLayerSP(0)); savingDevice = paintLayer->paintDevice(); } QScopedPointer savingDoc; KisPaintDeviceSP savingDevice; KisTimeRange range; int sequenceNumberingOffset = 0; bool onlyNeedsUniqueFrames; QString filenamePrefix; QString filenameSuffix; QByteArray outputMimeType; KisPropertiesConfigurationSP exportConfiguration; }; KisAsyncAnimationFramesSavingRenderer::KisAsyncAnimationFramesSavingRenderer(KisImageSP image, const QString &fileNamePrefix, const QString &fileNameSuffix, const QByteArray &outputMimeType, const KisTimeRange &range, const int sequenceNumberingOffset, const bool onlyNeedsUniqueFrames, KisPropertiesConfigurationSP exportConfiguration) : m_d(new Private(image, range, sequenceNumberingOffset, onlyNeedsUniqueFrames, exportConfiguration)) { m_d->filenamePrefix = fileNamePrefix; m_d->filenameSuffix = fileNameSuffix; m_d->outputMimeType = outputMimeType; connect(this, SIGNAL(sigCompleteRegenerationInternal(int)), SLOT(notifyFrameCompleted(int))); connect(this, SIGNAL(sigCancelRegenerationInternal(int)), SLOT(notifyFrameCancelled(int))); } KisAsyncAnimationFramesSavingRenderer::~KisAsyncAnimationFramesSavingRenderer() { } void KisAsyncAnimationFramesSavingRenderer::frameCompletedCallback(int frame, const KisRegion &requestedRegion) { KisImageSP image = requestedImage(); if (!image) return; KIS_SAFE_ASSERT_RECOVER (requestedRegion == image->bounds()) { emit sigCancelRegenerationInternal(frame); return; } m_d->savingDevice->makeCloneFromRough(image->projection(), image->bounds()); KisImportExportErrorCode status = ImportExportCodes::OK; QString frameNumber = QString("%1").arg(frame + m_d->sequenceNumberingOffset, 4, 10, QChar('0')); QString filename = m_d->filenamePrefix + frameNumber + m_d->filenameSuffix; if (!m_d->savingDoc->exportDocumentSync(QUrl::fromLocalFile(filename), m_d->outputMimeType, m_d->exportConfiguration)) { status = ImportExportCodes::InternalError; } //Get all identical frames to this one and either copy or symlink based on settings. KisTimeRange identicals = KisTimeRange::calculateIdenticalFramesRecursive(image->root(), frame); + identicals &= m_d->range; if( !m_d->onlyNeedsUniqueFrames && identicals.start() < identicals.end() ) { for (int identicalFrame = (identicals.start() + 1); identicalFrame <= identicals.end(); identicalFrame++) { QString identicalFrameNumber = QString("%1").arg(identicalFrame + m_d->sequenceNumberingOffset, 4, 10, QChar('0')); QString identicalFrameName = m_d->filenamePrefix + identicalFrameNumber + m_d->filenameSuffix; QFile::copy(filename, identicalFrameName); /* This would be nice to do but sym-linking on windows isn't possible without * way more other work to be done. This works on linux though! * * if (m_d->linkRedundantFrames) { * QFile::link(filename, identicalFrameName); * } else { * QFile::copy(filename, identicalFrameName); * } */ } } if (status.isOk()) { emit sigCompleteRegenerationInternal(frame); } else { emit sigCancelRegenerationInternal(frame); } } void KisAsyncAnimationFramesSavingRenderer::frameCancelledCallback(int frame) { notifyFrameCancelled(frame); } diff --git a/libs/ui/dialogs/KisAsyncAnimationFramesSaveDialog.cpp b/libs/ui/dialogs/KisAsyncAnimationFramesSaveDialog.cpp index c492c3597d..7e63c5dc32 100644 --- a/libs/ui/dialogs/KisAsyncAnimationFramesSaveDialog.cpp +++ b/libs/ui/dialogs/KisAsyncAnimationFramesSaveDialog.cpp @@ -1,211 +1,217 @@ /* * Copyright (c) 2017 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KisAsyncAnimationFramesSaveDialog.h" #include #include #include #include "kis_properties_configuration.h" #include "KisMimeDatabase.h" #include #include #include struct KisAsyncAnimationFramesSaveDialog::Private { Private(KisImageSP _image, const KisTimeRange &_range, const QString &baseFilename, int _sequenceNumberingOffset, bool _onlyNeedsUniqueFrames, KisPropertiesConfigurationSP _exportConfiguration) : originalImage(_image), range(_range), sequenceNumberingOffset(_sequenceNumberingOffset), onlyNeedsUniqueFrames(_onlyNeedsUniqueFrames), exportConfiguration(_exportConfiguration) { int baseLength = baseFilename.lastIndexOf("."); if (baseLength > -1) { filenamePrefix = baseFilename.left(baseLength); filenameSuffix = baseFilename.right(baseFilename.length() - baseLength); } else { filenamePrefix = baseFilename; } outputMimeType = KisMimeDatabase::mimeTypeForFile(baseFilename, false).toLatin1(); } KisImageSP originalImage; KisTimeRange range; QString filenamePrefix; QString filenameSuffix; QByteArray outputMimeType; bool onlyNeedsUniqueFrames; int sequenceNumberingOffset; KisPropertiesConfigurationSP exportConfiguration; }; KisAsyncAnimationFramesSaveDialog::KisAsyncAnimationFramesSaveDialog(KisImageSP originalImage, const KisTimeRange &range, const QString &baseFilename, int sequenceNumberingOffset, bool onlyNeedsUniqeFrames, KisPropertiesConfigurationSP exportConfiguration) : KisAsyncAnimationRenderDialogBase(i18n("Saving frames..."), originalImage, 0), m_d(new Private(originalImage, range, baseFilename, sequenceNumberingOffset, onlyNeedsUniqeFrames, exportConfiguration)) { } KisAsyncAnimationFramesSaveDialog::~KisAsyncAnimationFramesSaveDialog() { } KisAsyncAnimationRenderDialogBase::Result KisAsyncAnimationFramesSaveDialog::regenerateRange(KisViewManager *viewManager) { QFileInfo info(savedFilesMaskWildcard()); QDir dir(info.absolutePath()); if (!dir.exists()) { dir.mkpath(info.absolutePath()); } KIS_SAFE_ASSERT_RECOVER_NOOP(dir.exists()); QStringList filesList = dir.entryList({ info.fileName() }); if (!filesList.isEmpty()) { if (batchMode()) { return RenderFailed; } QStringList filesWithinRange; const int numberOfDigits = 4; Q_FOREACH(const QString &filename, filesList) { // Counting based on suffix, since prefix may include the path while filename doesn't int digitsPosition = filename.length() - m_d->filenameSuffix.length() - numberOfDigits; int fileNumber = filename.midRef(digitsPosition, numberOfDigits).toInt(); auto frameNumber = fileNumber - m_d->sequenceNumberingOffset; if (m_d->range.contains(frameNumber)) { filesWithinRange.append(filename); } } filesList = filesWithinRange; QStringList truncatedList = filesList; while (truncatedList.size() > 3) { truncatedList.takeLast(); } QString exampleFiles = truncatedList.join(", "); if (truncatedList.size() != filesList.size()) { exampleFiles += QString(", ..."); } QMessageBox::StandardButton result = QMessageBox::warning(0, i18n("Delete old frames?"), i18n("Frames with the same naming " "scheme exist in the destination " "directory. They are going to be " "deleted, continue?\n\n" "Directory: %1\n" "Files: %2", info.absolutePath(), exampleFiles), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (result == QMessageBox::Yes) { Q_FOREACH (const QString &file, filesList) { if (!dir.remove(file)) { QMessageBox::critical(0, i18n("Failed to delete"), i18n("Failed to delete an old frame file:\n\n" "%1\n\n" "Rendering cancelled.", dir.absoluteFilePath(file))); return RenderFailed; } } } else { return RenderCancelled; } } return KisAsyncAnimationRenderDialogBase::regenerateRange(viewManager); } QList KisAsyncAnimationFramesSaveDialog::calcDirtyFrames() const { QList result; for (int frame = m_d->range.start(); frame <= m_d->range.end(); frame++) { KisTimeRange heldFrameTimeRange = KisTimeRange::calculateIdenticalFramesRecursive(m_d->originalImage->root(), frame); + + if (!m_d->onlyNeedsUniqueFrames) { + // Clamp holds that begin before the rendered range onto it + heldFrameTimeRange &= m_d->range; + } + KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(heldFrameTimeRange.isValid(), result); result.append(heldFrameTimeRange.start()); if (heldFrameTimeRange.isInfinite()) { break; } else { frame = heldFrameTimeRange.end(); } } return result; } KisAsyncAnimationRendererBase *KisAsyncAnimationFramesSaveDialog::createRenderer(KisImageSP image) { return new KisAsyncAnimationFramesSavingRenderer(image, m_d->filenamePrefix, m_d->filenameSuffix, m_d->outputMimeType, m_d->range, m_d->sequenceNumberingOffset, m_d->onlyNeedsUniqueFrames, m_d->exportConfiguration); } void KisAsyncAnimationFramesSaveDialog::initializeRendererForFrame(KisAsyncAnimationRendererBase *renderer, KisImageSP image, int frame) { Q_UNUSED(renderer); Q_UNUSED(image); Q_UNUSED(frame); } QString KisAsyncAnimationFramesSaveDialog::savedFilesMask() const { return m_d->filenamePrefix + "%04d" + m_d->filenameSuffix; } QString KisAsyncAnimationFramesSaveDialog::savedFilesMaskWildcard() const { return m_d->filenamePrefix + "????" + m_d->filenameSuffix; } QList KisAsyncAnimationFramesSaveDialog::getUniqueFrames() const { return this->calcDirtyFrames(); }