Skip to content

Commit

Permalink
Improve support for animated image importing (#1801)
Browse files Browse the repository at this point in the history
* Fix movie export not being a modal operation

* 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.

* 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.

* 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.

* Add support for WebP import through a generic "Animated Image" import

* Add non-animated WEBP import/export

* Fix incorrect dialog titles
  • Loading branch information
scribblemaniac authored Dec 12, 2023
1 parent 363dcd7 commit 3c9a7a4
Show file tree
Hide file tree
Showing 16 changed files with 166 additions and 97 deletions.
49 changes: 48 additions & 1 deletion app/src/actioncommands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -65,6 +67,50 @@ ActionCommands::ActionCommands(QWidget* parent) : QObject(parent)

ActionCommands::~ActionCommands() {}

Status ActionCommands::importAnimatedImage()
{
ImportImageSeqDialog fileDialog(mParent, ImportExportDialog::Import, FileType::ANIMATED_IMAGE);
fileDialog.exec();
if (fileDialog.result() != QDialog::Accepted)
{
return Status::CANCELED;
}
int frameSpacing = fileDialog.getSpace();
QString strImgFileLower = fileDialog.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 Image..."), 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);
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -293,7 +340,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;
Expand Down
1 change: 1 addition & 0 deletions app/src/actioncommands.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class ActionCommands : public QObject
void setCore(Editor* e) { mEditor = e; }

// file
Status importAnimatedImage();
Status importMovieVideo();
Status importSound(FileType type);
Status exportMovie(bool isGif = false);
Expand Down
4 changes: 2 additions & 2 deletions app/src/doubleprogressdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ GNU General Public License for more details.
#include <QtMath>

DoubleProgressDialog::DoubleProgressDialog(QWidget *parent) :
QDialog(parent),
QProgressDialog(parent),
ui(new Ui::DoubleProgressDialog)
{
ui->setupUi(this);

major = new ProgressBarControl(ui->majorProgressBar);
minor = new ProgressBarControl(ui->minorProgressBar);

connect(ui->cancelButton, &QPushButton::pressed, this, &DoubleProgressDialog::canceled);
setCancelButton(ui->cancelButton);
}

DoubleProgressDialog::~DoubleProgressDialog()
Expand Down
7 changes: 2 additions & 5 deletions app/src/doubleprogressdialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ GNU General Public License for more details.
#ifndef DOUBLEPROGRESSDIALOG_H
#define DOUBLEPROGRESSDIALOG_H

#include <QDialog>
#include <QProgressDialog>
#include <QProgressBar>

namespace Ui {
class DoubleProgressDialog;
}

class DoubleProgressDialog : public QDialog
class DoubleProgressDialog : public QProgressDialog
{
Q_OBJECT

Expand Down Expand Up @@ -63,9 +63,6 @@ class DoubleProgressDialog : public QDialog

ProgressBarControl *major, *minor;

signals:
void canceled();

private:
Ui::DoubleProgressDialog *ui;
};
Expand Down
6 changes: 6 additions & 0 deletions app/src/filedialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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");
Expand All @@ -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");
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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";
Expand Down
10 changes: 8 additions & 2 deletions app/src/importimageseqdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,16 @@ 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 image sequence"));
break;
default:
setWindowTitle(tr("Import animated image"));
}

connect(uiOptionsBox->spaceSpinBox, static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &ImportImageSeqDialog::setSpace);
Expand Down
51 changes: 3 additions & 48 deletions app/src/mainwindow2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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); });
Expand Down Expand Up @@ -949,57 +949,12 @@ void MainWindow2::importLayers()
importLayers->open();
}

void MainWindow2::importGIF()
void MainWindow2::importAnimatedImage()
{
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();

mSuppressAutoSaveDialog = false;
}
Expand Down
2 changes: 1 addition & 1 deletion app/src/mainwindow2.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ public slots:
void importPredefinedImageSet();
void importLayers();
void importMovieVideo();
void importGIF();
void importAnimatedImage();

void lockWidgets(bool shouldLock);

Expand Down
5 changes: 5 additions & 0 deletions app/ui/exportimageoptions.ui
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@
<string>TIFF</string>
</property>
</item>
<item>
<property name="text">
<string>WEBP</string>
</property>
</item>
</widget>
</item>
<item>
Expand Down
6 changes: 3 additions & 3 deletions app/ui/mainwindow2.ui
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
<addaction name="actionImport_ImageSeq"/>
<addaction name="actionImport_ImageSeqNum"/>
<addaction name="actionImport_MovieVideo"/>
<addaction name="actionImport_Gif"/>
<addaction name="actionImport_AnimatedImage"/>
<addaction name="actionImportLayers_from_pclx"/>
<addaction name="separator"/>
<addaction name="actionImport_Sound"/>
Expand Down Expand Up @@ -912,9 +912,9 @@
<string>F1</string>
</property>
</action>
<action name="actionImport_Gif">
<action name="actionImport_AnimatedImage">
<property name="text">
<string>Animated GIF...</string>
<string>Animated Image...</string>
</property>
</action>
<action name="actionExport_Animated_GIF">
Expand Down
Loading

0 comments on commit 3c9a7a4

Please sign in to comment.