From 5a8fc4e8c7bcad867ee05740ddcf22ee8d43899b Mon Sep 17 00:00:00 2001 From: scribblemaniac Date: Mon, 4 Dec 2023 01:38:41 -0700 Subject: [PATCH 1/7] Fix movie export not being a modal operation --- app/src/actioncommands.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/actioncommands.cpp b/app/src/actioncommands.cpp index 35727c69e0..a85d037684 100644 --- a/app/src/actioncommands.cpp +++ b/app/src/actioncommands.cpp @@ -293,7 +293,7 @@ Status ActionCommands::exportMovie(bool isGif) desc.loop = dialog->getLoop(); desc.alpha = dialog->getTransparency(); - DoubleProgressDialog progressDlg; + DoubleProgressDialog progressDlg(mParent); progressDlg.setWindowModality(Qt::WindowModal); progressDlg.setWindowTitle(tr("Exporting movie")); Qt::WindowFlags eFlags = Qt::Dialog | Qt::WindowTitleHint; From f41aa626884b9f18ac524eaf4124cc3c818f6d20 Mon Sep 17 00:00:00 2001 From: scribblemaniac Date: Mon, 4 Dec 2023 01:52:50 -0700 Subject: [PATCH 2/7] Make DoubleProgressDialog inherit from QProgressDialog Probably not done initially because it adds a lot of member functions and properties that are not used by the double progress dialog. However it handles canceling much better than we do manually, including canceling with keyboard shortcuts or by closing the dialog. --- app/src/doubleprogressdialog.cpp | 4 ++-- app/src/doubleprogressdialog.h | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/doubleprogressdialog.cpp b/app/src/doubleprogressdialog.cpp index 7d171a01b5..22c2dc3907 100644 --- a/app/src/doubleprogressdialog.cpp +++ b/app/src/doubleprogressdialog.cpp @@ -21,7 +21,7 @@ GNU General Public License for more details. #include DoubleProgressDialog::DoubleProgressDialog(QWidget *parent) : - QDialog(parent), + QProgressDialog(parent), ui(new Ui::DoubleProgressDialog) { ui->setupUi(this); @@ -29,7 +29,7 @@ DoubleProgressDialog::DoubleProgressDialog(QWidget *parent) : major = new ProgressBarControl(ui->majorProgressBar); minor = new ProgressBarControl(ui->minorProgressBar); - connect(ui->cancelButton, &QPushButton::pressed, this, &DoubleProgressDialog::canceled); + setCancelButton(ui->cancelButton); } DoubleProgressDialog::~DoubleProgressDialog() diff --git a/app/src/doubleprogressdialog.h b/app/src/doubleprogressdialog.h index 8dda8301ff..690eaeac11 100644 --- a/app/src/doubleprogressdialog.h +++ b/app/src/doubleprogressdialog.h @@ -18,14 +18,14 @@ GNU General Public License for more details. #ifndef DOUBLEPROGRESSDIALOG_H #define DOUBLEPROGRESSDIALOG_H -#include +#include #include namespace Ui { class DoubleProgressDialog; } -class DoubleProgressDialog : public QDialog +class DoubleProgressDialog : public QProgressDialog { Q_OBJECT @@ -63,8 +63,8 @@ class DoubleProgressDialog : public QDialog ProgressBarControl *major, *minor; -signals: - void canceled(); +//signals: + //void canceled(); private: Ui::DoubleProgressDialog *ui; From 9db12b74fee99490ef845572639af7e959a32683 Mon Sep 17 00:00:00 2001 From: scribblemaniac Date: Mon, 4 Dec 2023 03:38:19 -0700 Subject: [PATCH 3/7] Improve gif import progress dialog and refactor The progress dialog now updates as it imports the gif, and can be aborted during the import. Some refactoring was done to move some logic around. --- app/src/actioncommands.cpp | 47 +++++++++++++++++++++ app/src/actioncommands.h | 1 + app/src/mainwindow2.cpp | 47 +-------------------- core_lib/src/interface/editor.cpp | 69 +++++++++++++++++++++++++++++-- core_lib/src/interface/editor.h | 2 +- 5 files changed, 115 insertions(+), 51 deletions(-) diff --git a/app/src/actioncommands.cpp b/app/src/actioncommands.cpp index a85d037684..152ec1f98b 100644 --- a/app/src/actioncommands.cpp +++ b/app/src/actioncommands.cpp @@ -47,6 +47,8 @@ GNU General Public License for more details. #include "soundclip.h" #include "camera.h" +#include "importimageseqdialog.h" +#include "importpositiondialog.h" #include "movieimporter.h" #include "movieexporter.h" #include "filedialog.h" @@ -65,6 +67,50 @@ ActionCommands::ActionCommands(QWidget* parent) : QObject(parent) ActionCommands::~ActionCommands() {} +Status ActionCommands::importAnimatedImage(FileType type) +{ + ImportImageSeqDialog gifDialog(mParent, ImportExportDialog::Import, type); + gifDialog.exec(); + if (gifDialog.result() != QDialog::Accepted) + { + return Status::CANCELED; + } + int frameSpacing = gifDialog.getSpace(); + QString strImgFileLower = gifDialog.getFilePath(); + + ImportPositionDialog positionDialog(mEditor, mParent); + positionDialog.exec(); + if (positionDialog.result() != QDialog::Accepted) + { + return Status::CANCELED; + } + + // Show a progress dialog, as this could take a while if the gif is huge + QProgressDialog progressDialog(tr("Importing Animated GIF..."), tr("Abort"), 0, 100, mParent); + hideQuestionMark(progressDialog); + progressDialog.setWindowModality(Qt::WindowModal); + progressDialog.show(); + + Status st = mEditor->importAnimatedImage(strImgFileLower, frameSpacing, [&progressDialog](int prog) { + progressDialog.setValue(prog); + QApplication::processEvents(); + }, [&progressDialog]() { + return progressDialog.wasCanceled(); + }); + + progressDialog.setValue(100); + progressDialog.close(); + + if (!st.ok()) + { + ErrorDialog errorDialog(st.title(), st.description(), st.details().html()); + errorDialog.exec(); + return Status::SAFE; + } + + return Status::OK; +} + Status ActionCommands::importMovieVideo() { QString filePath = FileDialog::getOpenFileName(mParent, FileType::MOVIE); @@ -106,6 +152,7 @@ Status ActionCommands::importMovieVideo() { ErrorDialog errorDialog(st.title(), st.description(), st.details().html(), mParent); errorDialog.exec(); + return Status::SAFE; } mEditor->layers()->notifyAnimationLengthChanged(); diff --git a/app/src/actioncommands.h b/app/src/actioncommands.h index f55e832c8c..c9c3ae5b82 100644 --- a/app/src/actioncommands.h +++ b/app/src/actioncommands.h @@ -37,6 +37,7 @@ class ActionCommands : public QObject void setCore(Editor* e) { mEditor = e; } // file + Status importAnimatedImage(FileType type); Status importMovieVideo(); Status importSound(FileType type); Status exportMovie(bool isGif = false); diff --git a/app/src/mainwindow2.cpp b/app/src/mainwindow2.cpp index 81ca1189bc..d0c25878f3 100644 --- a/app/src/mainwindow2.cpp +++ b/app/src/mainwindow2.cpp @@ -951,55 +951,10 @@ void MainWindow2::importLayers() void MainWindow2::importGIF() { - auto gifDialog = new ImportImageSeqDialog(this, ImportExportDialog::Import, FileType::GIF); - gifDialog->exec(); - if (gifDialog->result() == QDialog::Rejected) - { - return; - } - // Flag this so we don't prompt the user about auto-save in the middle of the import. mSuppressAutoSaveDialog = true; - ImportPositionDialog* positionDialog = new ImportPositionDialog(mEditor, this); - OnScopeExit(delete positionDialog) - - positionDialog->exec(); - if (positionDialog->result() != QDialog::Accepted) - { - return; - } - - int space = gifDialog->getSpace(); - - // Show a progress dialog, as this could take a while if the gif is huge - QProgressDialog progress(tr("Importing Animated GIF..."), tr("Abort"), 0, 100, this); - hideQuestionMark(progress); - progress.setWindowModality(Qt::WindowModal); - progress.show(); - - QString strImgFileLower = gifDialog->getFilePath(); - if (!strImgFileLower.toLower().endsWith(".gif")) - { - ErrorDialog errorDialog(tr("Import failed"), tr("You can only import files ending with .gif.")); - errorDialog.exec(); - } - else - { - Status st = mEditor->importGIF(strImgFileLower, space); - - progress.setValue(50); - QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); // Required to make progress bar update - - progress.setValue(100); - progress.close(); - - if (!st.ok()) - { - ErrorDialog errorDialog(st.title(), st.description(), st.details().html()); - errorDialog.exec(); - } - } + mCommands->importAnimatedImage(FileType::GIF); mSuppressAutoSaveDialog = false; } diff --git a/core_lib/src/interface/editor.cpp b/core_lib/src/interface/editor.cpp index 7c4d5bc602..9e69aad8d2 100644 --- a/core_lib/src/interface/editor.cpp +++ b/core_lib/src/interface/editor.cpp @@ -1048,17 +1048,78 @@ Status Editor::importImage(const QString& filePath) } } -Status Editor::importGIF(const QString& filePath, int numOfImages) +Status Editor::importAnimatedImage(const QString& filePath, int frameSpacing, const std::function& progressChanged, const std::function& wasCanceled) { + frameSpacing = qMax(1, frameSpacing); + + DebugDetails dd; + dd << QString("Raw file path: %1").arg(filePath); + Layer* layer = layers()->currentLayer(); if (layer->type() != Layer::BITMAP) { - DebugDetails dd; - dd << QString("Raw file path: %1").arg(filePath); dd << QString("Current layer: %1").arg(layer->type()); return Status(Status::ERROR_INVALID_LAYER_TYPE, dd, tr("Import failed"), tr("You can only import images to a bitmap layer.")); } - return importBitmapImage(filePath, numOfImages); + LayerBitmap* bitmapLayer = static_cast(layers()->currentLayer()); + + QImageReader reader(filePath); + dd << QString("QImageReader format: %1").arg(QString(reader.format())); + if (!reader.supportsAnimation()) { + return Status(Status::ERROR_INVALID_LAYER_TYPE, dd, tr("Import failed"), tr("The selected image has a format that does not support animation.")); + } + + QImage img(reader.size(), QImage::Format_ARGB32_Premultiplied); + const QPoint pos(view()->getImportView().dx() - (img.width() / 2), + view()->getImportView().dy() - (img.height() / 2)); + int totalFrames = reader.imageCount(); + while (reader.read(&img)) + { + if (reader.error()) + { + dd << QString("QImageReader ImageReaderError type: %1").arg(reader.errorString()); + + QString errorDesc; + switch(reader.error()) + { + case QImageReader::ImageReaderError::FileNotFoundError: + errorDesc = tr("File not found at path \"%1\". Please check the image is present at the specified location and try again.").arg(filePath); + break; + case QImageReader::UnsupportedFormatError: + errorDesc = tr("Image format is not supported. Please convert the image file to one of the following formats and try again:\n%1") + .arg((QString)reader.supportedImageFormats().join(", ")); + break; + default: + errorDesc = tr("An error has occurred while reading the image. Please check that the file is a valid image and try again."); + } + + return Status(Status::FAIL, dd, tr("Import failed"), errorDesc); + } + + int frameNumber = mFrame; + if (!bitmapLayer->keyExists(frameNumber)) + { + addNewKey(); + } + BitmapImage* bitmapImage = bitmapLayer->getBitmapImageAtFrame(frameNumber); + BitmapImage importedBitmapImage(pos, img); + bitmapImage->paste(&importedBitmapImage); + emit frameModified(bitmapImage->pos()); + + if (wasCanceled()) + { + break; + } + + frameNumber += frameSpacing; + scrubTo(frameNumber); + + backup(tr("Import Image")); + + progressChanged(qFloor(qMin(static_cast(reader.currentImageNumber()) / totalFrames, 1.0) * 100)); + } + + return Status::OK; } void Editor::selectAll() const diff --git a/core_lib/src/interface/editor.h b/core_lib/src/interface/editor.h index eef05b2665..88fc9b8961 100644 --- a/core_lib/src/interface/editor.h +++ b/core_lib/src/interface/editor.h @@ -174,7 +174,7 @@ class Editor : public QObject void clearCurrentFrame(); Status importImage(const QString& filePath); - Status importGIF(const QString& filePath, int numOfImages = 0); + Status importAnimatedImage(const QString& filePath, int frameSpacing, const std::function& progressChanged, const std::function& wasCanceled); void restoreKey(); void scrubNextKeyFrame(); From 12a20849126b6f220578d480be65ec1bf56fb303 Mon Sep 17 00:00:00 2001 From: scribblemaniac Date: Mon, 4 Dec 2023 03:51:14 -0700 Subject: [PATCH 4/7] Remove animated image handling from Editor::importBitmapImage The error checking has also been modified to hopefully be more robust as it no longer depends on reader.size() being invalid, which might not occur during certain types of image corruption, and may occur without corruption for certain types of image formats. --- core_lib/src/interface/editor.cpp | 46 ++++++++++--------------------- core_lib/src/interface/editor.h | 2 +- 2 files changed, 15 insertions(+), 33 deletions(-) diff --git a/core_lib/src/interface/editor.cpp b/core_lib/src/interface/editor.cpp index 9e69aad8d2..49af52acb7 100644 --- a/core_lib/src/interface/editor.cpp +++ b/core_lib/src/interface/editor.cpp @@ -914,7 +914,7 @@ void Editor::updateObject() emit updateLayerCount(); } -Status Editor::importBitmapImage(const QString& filePath, int space) +Status Editor::importBitmapImage(const QString& filePath) { QImageReader reader(filePath); @@ -926,8 +926,7 @@ Status Editor::importBitmapImage(const QString& filePath, int space) dd << QString("Raw file path: %1").arg(filePath); QImage img(reader.size(), QImage::Format_ARGB32_Premultiplied); - if (img.isNull()) - { + if (!reader.read(&img)) { QString format = reader.format(); if (!format.isEmpty()) { @@ -955,33 +954,18 @@ Status Editor::importBitmapImage(const QString& filePath, int space) const QPoint pos(view()->getImportView().dx() - (img.width() / 2), view()->getImportView().dy() - (img.height() / 2)); - while (reader.read(&img)) + if (!layer->keyExists(mFrame)) { - int frameNumber = mFrame; - if (!layer->keyExists(frameNumber)) - { - addNewKey(); - } - BitmapImage* bitmapImage = layer->getBitmapImageAtFrame(frameNumber); - BitmapImage importedBitmapImage(pos, img); - bitmapImage->paste(&importedBitmapImage); - emit frameModified(bitmapImage->pos()); - - if (space > 1) { - frameNumber += space; - } else { - frameNumber += 1; - } - scrubTo(frameNumber); + addNewKey(); + } + BitmapImage* bitmapImage = layer->getBitmapImageAtFrame(mFrame); + BitmapImage importedBitmapImage(pos, img); + bitmapImage->paste(&importedBitmapImage); + emit frameModified(bitmapImage->pos()); - backup(tr("Import Image")); + scrubTo(mFrame+1); - // Workaround for tiff import getting stuck in this loop - if (!reader.supportsAnimation()) - { - break; - } - } + backup(tr("Import Image")); return status; } @@ -1096,12 +1080,11 @@ Status Editor::importAnimatedImage(const QString& filePath, int frameSpacing, co return Status(Status::FAIL, dd, tr("Import failed"), errorDesc); } - int frameNumber = mFrame; - if (!bitmapLayer->keyExists(frameNumber)) + if (!bitmapLayer->keyExists(mFrame)) { addNewKey(); } - BitmapImage* bitmapImage = bitmapLayer->getBitmapImageAtFrame(frameNumber); + BitmapImage* bitmapImage = bitmapLayer->getBitmapImageAtFrame(mFrame); BitmapImage importedBitmapImage(pos, img); bitmapImage->paste(&importedBitmapImage); emit frameModified(bitmapImage->pos()); @@ -1111,8 +1094,7 @@ Status Editor::importAnimatedImage(const QString& filePath, int frameSpacing, co break; } - frameNumber += frameSpacing; - scrubTo(frameNumber); + scrubTo(mFrame + frameSpacing); backup(tr("Import Image")); diff --git a/core_lib/src/interface/editor.h b/core_lib/src/interface/editor.h index 88fc9b8961..a3ba0e179e 100644 --- a/core_lib/src/interface/editor.h +++ b/core_lib/src/interface/editor.h @@ -230,7 +230,7 @@ class Editor : public QObject void resetAutoSaveCounter(); private: - Status importBitmapImage(const QString&, int space = 0); + Status importBitmapImage(const QString&); Status importVectorImage(const QString&); void pasteToCanvas(BitmapImage* bitmapImage, int frameNumber); From f965a9fafd3e31fd8f70eea6018a75eec7f2a51b Mon Sep 17 00:00:00 2001 From: scribblemaniac Date: Mon, 4 Dec 2023 04:31:33 -0700 Subject: [PATCH 5/7] Add support for WebP import through a generic "Animated Image" import --- app/src/actioncommands.cpp | 12 ++++++------ app/src/actioncommands.h | 2 +- app/src/filedialog.cpp | 6 ++++++ app/src/importimageseqdialog.cpp | 10 ++++++++-- app/src/mainwindow2.cpp | 6 +++--- app/src/mainwindow2.h | 2 +- app/ui/mainwindow2.ui | 6 +++--- core_lib/src/util/fileformat.h | 3 +++ core_lib/src/util/filetype.h | 1 + 9 files changed, 32 insertions(+), 16 deletions(-) diff --git a/app/src/actioncommands.cpp b/app/src/actioncommands.cpp index 152ec1f98b..eacb0befa8 100644 --- a/app/src/actioncommands.cpp +++ b/app/src/actioncommands.cpp @@ -67,16 +67,16 @@ ActionCommands::ActionCommands(QWidget* parent) : QObject(parent) ActionCommands::~ActionCommands() {} -Status ActionCommands::importAnimatedImage(FileType type) +Status ActionCommands::importAnimatedImage() { - ImportImageSeqDialog gifDialog(mParent, ImportExportDialog::Import, type); - gifDialog.exec(); - if (gifDialog.result() != QDialog::Accepted) + ImportImageSeqDialog fileDialog(mParent, ImportExportDialog::Import, FileType::ANIMATED_IMAGE); + fileDialog.exec(); + if (fileDialog.result() != QDialog::Accepted) { return Status::CANCELED; } - int frameSpacing = gifDialog.getSpace(); - QString strImgFileLower = gifDialog.getFilePath(); + int frameSpacing = fileDialog.getSpace(); + QString strImgFileLower = fileDialog.getFilePath(); ImportPositionDialog positionDialog(mEditor, mParent); positionDialog.exec(); diff --git a/app/src/actioncommands.h b/app/src/actioncommands.h index c9c3ae5b82..6d8ac3e340 100644 --- a/app/src/actioncommands.h +++ b/app/src/actioncommands.h @@ -37,7 +37,7 @@ class ActionCommands : public QObject void setCore(Editor* e) { mEditor = e; } // file - Status importAnimatedImage(FileType type); + Status importAnimatedImage(); Status importMovieVideo(); Status importSound(FileType type); Status exportMovie(bool isGif = false); diff --git a/app/src/filedialog.cpp b/app/src/filedialog.cpp index 8b35d69ca2..42ee65db25 100644 --- a/app/src/filedialog.cpp +++ b/app/src/filedialog.cpp @@ -119,6 +119,7 @@ QString FileDialog::getDefaultExtensionByFileType(const FileType fileType) case FileType::IMAGE: return PFF_DEFAULT_IMAGE_EXT; case FileType::IMAGE_SEQUENCE: return PFF_DEFAULT_IMAGE_SEQ_EXT; case FileType::GIF: return PFF_DEFAULT_ANIMATED_EXT; + case FileType::ANIMATED_IMAGE: return PFF_DEFAULT_ANIMATED_EXT; case FileType::PALETTE: return PFF_DEFAULT_PALETTE_EXT; case FileType::MOVIE: return PFF_DEFAULT_MOVIE_EXT; case FileType::SOUND: return PFF_DEFAULT_SOUND_EXT; @@ -167,6 +168,7 @@ QString FileDialog::openDialogCaption(FileType fileType) case FileType::IMAGE: return tr("Import image"); case FileType::IMAGE_SEQUENCE: return tr("Import image sequence"); case FileType::GIF: return tr("Import Animated GIF"); + case FileType::ANIMATED_IMAGE: return tr("Import animated image"); case FileType::MOVIE: return tr("Import movie"); case FileType::SOUND: return tr("Import sound"); case FileType::PALETTE: return tr("Open palette"); @@ -182,6 +184,7 @@ QString FileDialog::saveDialogCaption(FileType fileType) case FileType::IMAGE: return tr("Export image"); case FileType::IMAGE_SEQUENCE: return tr("Export image sequence"); case FileType::GIF: return tr("Export Animated GIF"); + case FileType::ANIMATED_IMAGE: return tr("Export animated image"); case FileType::MOVIE: return tr("Export movie"); case FileType::SOUND: return tr("Export sound"); case FileType::PALETTE: return tr("Export palette"); @@ -197,6 +200,7 @@ QString FileDialog::openFileFilters(FileType fileType) case FileType::IMAGE: return PFF_IMAGE_FILTER; case FileType::IMAGE_SEQUENCE: return PFF_IMAGE_SEQ_FILTER; case FileType::GIF: return PFF_GIF_EXT_FILTER; + case FileType::ANIMATED_IMAGE: return PFF_ANIMATED_IMAGE_EXT_FILTER; case FileType::MOVIE: return PFF_MOVIE_EXT; case FileType::SOUND: return PFF_SOUND_EXT_FILTER; case FileType::PALETTE: return PFF_PALETTE_EXT_FILTER; @@ -212,6 +216,7 @@ QString FileDialog::saveFileFilters(FileType fileType) case FileType::IMAGE: return ""; case FileType::IMAGE_SEQUENCE: return ""; case FileType::GIF: return QString("%1 (*.gif)").arg(tr("Animated GIF")); + case FileType::ANIMATED_IMAGE: return ""; case FileType::MOVIE: return "MP4 (*.mp4);; AVI (*.avi);; WebM (*.webm);; APNG (*.apng)"; case FileType::SOUND: return ""; case FileType::PALETTE: return PFF_PALETTE_EXT_FILTER; @@ -286,6 +291,7 @@ QString FileDialog::toSettingKey(FileType fileType) case FileType::IMAGE: return "Image"; case FileType::IMAGE_SEQUENCE: return "ImageSequence"; case FileType::GIF: return "Animated GIF"; + case FileType::ANIMATED_IMAGE: return "Animated Image"; case FileType::MOVIE: return "Movie"; case FileType::SOUND: return "Sound"; case FileType::PALETTE: return "Palette"; diff --git a/app/src/importimageseqdialog.cpp b/app/src/importimageseqdialog.cpp index 896067a889..422b583c33 100644 --- a/app/src/importimageseqdialog.cpp +++ b/app/src/importimageseqdialog.cpp @@ -58,9 +58,15 @@ void ImportImageSeqDialog::setupLayout() hideInstructionsLabel(true); - if (mFileType == FileType::GIF) { + switch (mFileType) + { + case FileType::GIF: setWindowTitle(tr("Import Animated GIF")); - } else { + break; + case FileType::IMAGE_SEQUENCE: + setWindowTitle(tr("Import animated image")); + break; + default: setWindowTitle(tr("Import image sequence")); } diff --git a/app/src/mainwindow2.cpp b/app/src/mainwindow2.cpp index d0c25878f3..da3d6ee2b6 100644 --- a/app/src/mainwindow2.cpp +++ b/app/src/mainwindow2.cpp @@ -254,7 +254,7 @@ void MainWindow2::createMenus() connect(ui->actionImport_ImageSeqNum, &QAction::triggered, this, &MainWindow2::importPredefinedImageSet); connect(ui->actionImportLayers_from_pclx, &QAction::triggered, this, &MainWindow2::importLayers); connect(ui->actionImport_MovieVideo, &QAction::triggered, this, &MainWindow2::importMovieVideo); - connect(ui->actionImport_Gif, &QAction::triggered, this, &MainWindow2::importGIF); + connect(ui->actionImport_AnimatedImage, &QAction::triggered, this, &MainWindow2::importAnimatedImage); connect(ui->actionImport_Sound, &QAction::triggered, [=] { mCommands->importSound(FileType::SOUND); }); connect(ui->actionImport_MovieAudio, &QAction::triggered, [=] { mCommands->importSound(FileType::MOVIE); }); @@ -949,12 +949,12 @@ void MainWindow2::importLayers() importLayers->open(); } -void MainWindow2::importGIF() +void MainWindow2::importAnimatedImage() { // Flag this so we don't prompt the user about auto-save in the middle of the import. mSuppressAutoSaveDialog = true; - mCommands->importAnimatedImage(FileType::GIF); + mCommands->importAnimatedImage(); mSuppressAutoSaveDialog = false; } diff --git a/app/src/mainwindow2.h b/app/src/mainwindow2.h index a9f9f7a0cb..9f1b683f6c 100644 --- a/app/src/mainwindow2.h +++ b/app/src/mainwindow2.h @@ -90,7 +90,7 @@ public slots: void importPredefinedImageSet(); void importLayers(); void importMovieVideo(); - void importGIF(); + void importAnimatedImage(); void lockWidgets(bool shouldLock); diff --git a/app/ui/mainwindow2.ui b/app/ui/mainwindow2.ui index 0d35271b71..d444be23e7 100644 --- a/app/ui/mainwindow2.ui +++ b/app/ui/mainwindow2.ui @@ -65,7 +65,7 @@ - + @@ -912,9 +912,9 @@ F1 - + - Animated GIF... + Animated Image... diff --git a/core_lib/src/util/fileformat.h b/core_lib/src/util/fileformat.h index 2ce07e5fa0..9abbec1664 100644 --- a/core_lib/src/util/fileformat.h +++ b/core_lib/src/util/fileformat.h @@ -51,6 +51,9 @@ GNU General Public License for more details. #define PFF_GIF_EXT_FILTER \ QCoreApplication::translate("FileFormat", "Animated GIF") + " (*.gif)" +#define PFF_ANIMATED_IMAGE_EXT_FILTER \ + QCoreApplication::translate("FileFormat", "Animated image formats") + " (*.gif *.webp);;GIF(*.gif);;WEBP(*.webp)" + #define PFF_SOUND_EXT_FILTER \ QCoreApplication::translate("FileFormat", "Sound formats") + " (*.wav *.mp3 *.wma *.ogg *.flac *.opus *.aiff *.aac *.caf);;WAV (*.wav);;MP3 (*.mp3);;WMA (*.wma);;OGG (*.ogg);;FLAC (*.flac);;Opus (*.opus);;AIFF (*.aiff);;AAC (*.aac);;CAF (*.caf)" diff --git a/core_lib/src/util/filetype.h b/core_lib/src/util/filetype.h index a0558fa5aa..9a88fad1e8 100644 --- a/core_lib/src/util/filetype.h +++ b/core_lib/src/util/filetype.h @@ -7,6 +7,7 @@ enum class FileType IMAGE, IMAGE_SEQUENCE, GIF, + ANIMATED_IMAGE, MOVIE, SOUND, PALETTE From b632734bf1d5fb9be22a26fa16e40bfa3a0a8e5c Mon Sep 17 00:00:00 2001 From: scribblemaniac Date: Mon, 4 Dec 2023 04:46:00 -0700 Subject: [PATCH 6/7] Add non-animated WEBP import/export --- app/ui/exportimageoptions.ui | 5 +++++ core_lib/src/structure/object.cpp | 4 ++++ core_lib/src/util/fileformat.cpp | 1 + core_lib/src/util/fileformat.h | 4 ++-- 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/app/ui/exportimageoptions.ui b/app/ui/exportimageoptions.ui index 23341ac933..98d1ae91ce 100644 --- a/app/ui/exportimageoptions.ui +++ b/app/ui/exportimageoptions.ui @@ -98,6 +98,11 @@ TIFF + + + WEBP + + diff --git a/core_lib/src/structure/object.cpp b/core_lib/src/structure/object.cpp index 17834e6f74..bb25cdb1e0 100644 --- a/core_lib/src/structure/object.cpp +++ b/core_lib/src/structure/object.cpp @@ -828,6 +828,10 @@ bool Object::exportFrames(int frameStart, int frameEnd, extension = ".bmp"; transparency = false; } + if (formatStr == "WEBP" || formatStr == "webp") { + format = "WEBP"; + extension = ".webp"; + } if (filePath.endsWith(extension, Qt::CaseInsensitive)) { filePath.chop(extension.size()); diff --git a/core_lib/src/util/fileformat.cpp b/core_lib/src/util/fileformat.cpp index 55707cfa57..3d4eb77079 100644 --- a/core_lib/src/util/fileformat.cpp +++ b/core_lib/src/util/fileformat.cpp @@ -59,6 +59,7 @@ QString detectFormatByFileNameExtension(const QString& fileName) { "tif", "TIF" }, { "tiff", "TIF" }, { "bmp", "BMP" }, + { "webp", "WEBP" }, { "mp4", "MP4" }, { "avi", "AVI" }, { "gif", "GIF" }, diff --git a/core_lib/src/util/fileformat.h b/core_lib/src/util/fileformat.h index 9abbec1664..6935d0aa44 100644 --- a/core_lib/src/util/fileformat.h +++ b/core_lib/src/util/fileformat.h @@ -40,10 +40,10 @@ GNU General Public License for more details. ";;SWF(*.swf);;FLV(*.flv);;WEBM(*.webm);;WMV(*.wmv)" #define PFF_IMAGE_FILTER \ - QCoreApplication::translate("FileFormat", "Image formats") + " (*.png *.jpg *.jpeg *.bmp *.tif *.tiff);;PNG (*.png);;JPG(*.jpg *.jpeg);;BMP(*.bmp);;TIFF(*.tif *.tiff)" + QCoreApplication::translate("FileFormat", "Image formats") + " (*.png *.jpg *.jpeg *.bmp *.tif *.tiff *.webp);;PNG (*.png);;JPG(*.jpg *.jpeg);;BMP(*.bmp);;TIFF(*.tif *.tiff);;WEBP(*.webp)" #define PFF_IMAGE_SEQ_FILTER \ - QCoreApplication::translate("FileFormat", "Image formats") + " (*.png *.jpg *.jpeg *.bmp *.tif *.tiff);;PNG (*.png);;JPG(*.jpg *.jpeg);;BMP(*.bmp);;TIFF(*.tif *.tiff)" + QCoreApplication::translate("FileFormat", "Image formats") + " (*.png *.jpg *.jpeg *.bmp *.tif *.tiff *.webp);;PNG (*.png);;JPG(*.jpg *.jpeg);;BMP(*.bmp);;TIFF(*.tif *.tiff);;WEBP(*.webp)" #define PFF_PALETTE_EXT_FILTER \ QCoreApplication::translate("FileFormat", "Palette formats") + " (*.xml *.gpl);;" + QCoreApplication::translate("FileFormat", "Pencil2D Palette") + " (*.xml);;" + QCoreApplication::translate("FileFormat", "GIMP Palette") + " (*.gpl)" From 18df641451da1290c874319b44f5286235f0ef5a Mon Sep 17 00:00:00 2001 From: scribblemaniac Date: Mon, 11 Dec 2023 16:06:49 -0700 Subject: [PATCH 7/7] Fix incorrect dialog titles --- app/src/actioncommands.cpp | 2 +- app/src/doubleprogressdialog.h | 3 --- app/src/importimageseqdialog.cpp | 4 ++-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/app/src/actioncommands.cpp b/app/src/actioncommands.cpp index eacb0befa8..347779eeb5 100644 --- a/app/src/actioncommands.cpp +++ b/app/src/actioncommands.cpp @@ -86,7 +86,7 @@ Status ActionCommands::importAnimatedImage() } // Show a progress dialog, as this could take a while if the gif is huge - QProgressDialog progressDialog(tr("Importing Animated GIF..."), tr("Abort"), 0, 100, mParent); + QProgressDialog progressDialog(tr("Importing Animated Image..."), tr("Abort"), 0, 100, mParent); hideQuestionMark(progressDialog); progressDialog.setWindowModality(Qt::WindowModal); progressDialog.show(); diff --git a/app/src/doubleprogressdialog.h b/app/src/doubleprogressdialog.h index 690eaeac11..93bfb67abf 100644 --- a/app/src/doubleprogressdialog.h +++ b/app/src/doubleprogressdialog.h @@ -63,9 +63,6 @@ class DoubleProgressDialog : public QProgressDialog ProgressBarControl *major, *minor; -//signals: - //void canceled(); - private: Ui::DoubleProgressDialog *ui; }; diff --git a/app/src/importimageseqdialog.cpp b/app/src/importimageseqdialog.cpp index 422b583c33..86c4515913 100644 --- a/app/src/importimageseqdialog.cpp +++ b/app/src/importimageseqdialog.cpp @@ -64,10 +64,10 @@ void ImportImageSeqDialog::setupLayout() setWindowTitle(tr("Import Animated GIF")); break; case FileType::IMAGE_SEQUENCE: - setWindowTitle(tr("Import animated image")); + setWindowTitle(tr("Import image sequence")); break; default: - setWindowTitle(tr("Import image sequence")); + setWindowTitle(tr("Import animated image")); } connect(uiOptionsBox->spaceSpinBox, static_cast(&QSpinBox::valueChanged), this, &ImportImageSeqDialog::setSpace);