Skip to content

Commit

Permalink
[standalone] Add --layers option
Browse files Browse the repository at this point in the history
Consider mutliple input images as layers of a single image (top layer first)
  • Loading branch information
c-koi committed Sep 5, 2022
1 parent b52808d commit 222265c
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 56 deletions.
10 changes: 10 additions & 0 deletions STANDALONE.md
Expand Up @@ -137,3 +137,13 @@ OutputMode: 0
### Option `--show-last-after`

Print last applied plugin parameters after filter execution. (Indeed, some filters may change the value of their parameters.)

### Option `--layers`

Consider multiple input files as layers of the same image (first image is the top layer).

```sh
$ ./gmic_qt -p "Blend [Average All]" --layers --apply -o output.png toplayer.png middlelayer.png bottomlayer.png
```


162 changes: 106 additions & 56 deletions src/Host/None/host_none.cpp
Expand Up @@ -34,6 +34,7 @@
#include <QMessageBox>
#include <QPainter>
#include <QRegularExpression>
#include <QVector>
#include <algorithm>
#include <fstream>
#include <iostream>
Expand All @@ -51,9 +52,9 @@
namespace gmic_qt_standalone
{

QImage input_image;
QString current_image_filename;
QString input_image_filename;
QVector<QImage> input_images;
QVector<QString> current_image_filenames;
QVector<QString> input_image_filenames;
QString output_image_filename;
int jpeg_quality = -1;

Expand All @@ -75,16 +76,18 @@ void askForInputImageFilename()
QString filters;
gmic_qt_standalone::ImageDialog::supportedImageFormats(extensions, filters);
QString filename = QFileDialog::getOpenFileName(mainWidget, QObject::tr("Select an image to open..."), ".", filters, nullptr);
if (!filename.isEmpty() && QFileInfo(filename).isReadable() && input_image.load(filename)) {
input_image = input_image.convertToFormat(QImage::Format_ARGB32);
current_image_filename = QFileInfo(filename).fileName();
input_images.resize(1);
current_image_filenames.resize(1);
if (!filename.isEmpty() && QFileInfo(filename).isReadable() && input_images.first().load(filename)) {
input_images.first() = input_images.first().convertToFormat(QImage::Format_ARGB32);
current_image_filenames.first() = QFileInfo(filename).fileName();
} else {
if (!filename.isEmpty()) {
QMessageBox::warning(mainWidget, QObject::tr("Error"), QObject::tr("Could not open file."));
}
input_image.load(":/resources/gmicky.png");
input_image = input_image.convertToFormat(QImage::Format_ARGB32);
current_image_filename = QObject::tr("Default image");
input_images.first().load(":/resources/gmicky.png");
input_images.first() = input_images.first().convertToFormat(QImage::Format_ARGB32);
current_image_filenames.first() = QObject::tr("Default image");
}
}
const QImage & transparentImage()
Expand Down Expand Up @@ -136,6 +139,15 @@ std::string basename(const std::string & text)
}
}

const QImage & inputImage(int n)
{
if (n < gmic_qt_standalone::input_images.size() && !gmic_qt_standalone::input_images[n].isNull()) {
return gmic_qt_standalone::input_images[n];
} else {
return gmic_qt_standalone::transparentImage();
}
}

} // namespace gmic_qt_standalone

namespace GmicQtHost
Expand All @@ -146,25 +158,23 @@ const bool DarkThemeIsDefault = false;

void getLayersExtent(int * width, int * height, GmicQt::InputMode)
{
if (gmic_qt_standalone::input_image.isNull()) {
if (gmic_qt_standalone::input_images.isEmpty()) {
if (gmic_qt_standalone::visibleMainWindow()) {
gmic_qt_standalone::askForInputImageFilename();
*width = gmic_qt_standalone::input_image.width();
*height = gmic_qt_standalone::input_image.height();
*width = gmic_qt_standalone::input_images.first().width();
*height = gmic_qt_standalone::input_images.first().height();
} else {
*width = 640;
*height = 480;
}
} else {
*width = gmic_qt_standalone::input_image.width();
*height = gmic_qt_standalone::input_image.height();
*width = gmic_qt_standalone::input_images.first().width();
*height = gmic_qt_standalone::input_images.first().height();
}
}

void getCroppedImages(gmic_list<float> & images, gmic_list<char> & imageNames, double x, double y, double width, double height, GmicQt::InputMode mode)
{
const QImage & input_image = gmic_qt_standalone::input_image.isNull() ? gmic_qt_standalone::transparentImage() : gmic_qt_standalone::input_image;

const bool entireImage = x < 0 && y < 0 && width < 0 && height < 0;
if (entireImage) {
x = 0.0;
Expand All @@ -178,21 +188,24 @@ void getCroppedImages(gmic_list<float> & images, gmic_list<char> & imageNames, d
return;
}

images.assign(1);
imageNames.assign(1);
images.assign(gmic_qt_standalone::input_images.size());
imageNames.assign(gmic_qt_standalone::input_images.size());

QString noParenthesisName(gmic_qt_standalone::current_image_filename);
noParenthesisName.replace(QChar('('), QChar(21)).replace(QChar(')'), QChar(22));
for (int i = 0; i < gmic_qt_standalone::input_images.size(); ++i) {
QString noParenthesisName(gmic_qt_standalone::current_image_filenames[i]);
noParenthesisName.replace(QChar('('), QChar(21)).replace(QChar(')'), QChar(22));

QString name = QString("pos(0,0),name(%1)").arg(noParenthesisName);
QByteArray ba = name.toUtf8();
gmic_image<char>::string(ba.constData()).move_to(imageNames[0]);
QString name = QString("pos(0,0),name(%1)").arg(noParenthesisName);
QByteArray ba = name.toUtf8();
gmic_image<char>::string(ba.constData()).move_to(imageNames[i]);

const int ix = static_cast<int>(entireImage ? 0 : std::floor(x * input_image.width()));
const int iy = static_cast<int>(entireImage ? 0 : std::floor(y * input_image.height()));
const int iw = entireImage ? input_image.width() : std::min(input_image.width() - ix, static_cast<int>(1 + std::ceil(width * input_image.width())));
const int ih = entireImage ? input_image.height() : std::min(input_image.height() - iy, static_cast<int>(1 + std::ceil(height * input_image.height())));
GmicQt::convertQImageToCImg(input_image.copy(ix, iy, iw, ih), images[0]);
const QImage & input_image = gmic_qt_standalone::inputImage(i);
const int ix = static_cast<int>(entireImage ? 0 : std::floor(x * input_image.width()));
const int iy = static_cast<int>(entireImage ? 0 : std::floor(y * input_image.height()));
const int iw = entireImage ? input_image.width() : std::min(input_image.width() - ix, static_cast<int>(1 + std::ceil(width * input_image.width())));
const int ih = entireImage ? input_image.height() : std::min(input_image.height() - iy, static_cast<int>(1 + std::ceil(height * input_image.height())));
GmicQt::convertQImageToCImg(input_image.copy(ix, iy, iw, ih), images[i]);
}
}

void outputImages(gmic_list<float> & images, const gmic_list<char> & imageNames, GmicQt::OutputMode mode)
Expand All @@ -208,24 +221,28 @@ void outputImages(gmic_list<float> & images, const gmic_list<char> & imageNames,
dialog->addImage(images[i], name);
}
dialog->exec();
gmic_qt_standalone::input_image = dialog->currentImage();
gmic_qt_standalone::current_image_filename = gmic_qt_standalone::imageName((const char *)imageNames[dialog->currentImageIndex()]);
gmic_qt_standalone::input_images.resize(1);
gmic_qt_standalone::input_images.first() = dialog->currentImage();
gmic_qt_standalone::current_image_filenames.resize(1);
gmic_qt_standalone::current_image_filenames.first() = gmic_qt_standalone::imageName((const char *)imageNames[dialog->currentImageIndex()]);
delete dialog;
}
} else {
GmicQt::convertCImgToQImage(images[0], gmic_qt_standalone::input_image);
gmic_qt_standalone::input_images.resize(1);
gmic_qt_standalone::current_image_filenames.resize(1);
GmicQt::convertCImgToQImage(images[0], gmic_qt_standalone::input_images.first());
QString outputFilename = gmic_qt_standalone::output_image_filename;
if (outputFilename.contains("%b")) {
const QString basename = QFileInfo(gmic_qt_standalone::input_image_filename).completeBaseName();
const QString basename = QFileInfo(gmic_qt_standalone::input_image_filenames.first()).completeBaseName();
outputFilename.replace("%b", basename);
}
if (outputFilename.contains("%f")) {
const QString filename = QFileInfo(gmic_qt_standalone::input_image_filename).fileName();
const QString filename = QFileInfo(gmic_qt_standalone::input_image_filenames.first()).fileName();
outputFilename.replace("%f", filename);
}
std::cout << "[gmic_qt] Writing output file " << outputFilename.toStdString() << std::endl;
gmic_qt_standalone::input_image.save(outputFilename, nullptr, gmic_qt_standalone::jpeg_quality);
gmic_qt_standalone::current_image_filename = gmic_qt_standalone::imageName((const char *)imageNames[0]);
gmic_qt_standalone::input_images.first().save(outputFilename, nullptr, gmic_qt_standalone::jpeg_quality);
gmic_qt_standalone::current_image_filenames.first() = gmic_qt_standalone::imageName((const char *)imageNames[0]);
}
}
unused(mode);
Expand Down Expand Up @@ -260,6 +277,7 @@ void usage(const std::string & argv0)
" -a --apply : Apply filter or command and quit (requires one of -r -p -c)\n"
" -R --reapply-first : Launch GUI once for first input file, then apply selected filter\n"
" and parameters to all other files\n"
" -l --layers : Treat multiple input files as layers of a single image (top first)\n"
" --show-last : Print last applied plugin parameters\n"
" --show-last-after : Print last applied plugin parameters (after filter execution)\n";
}
Expand All @@ -282,6 +300,7 @@ int main(int argc, char * argv[])
bool reapplyFirst = false;
bool printLast = false;
bool printLastAfter = false;
bool layers = false;
std::string filterPath;
std::string command;
QStringList filenames;
Expand All @@ -292,6 +311,8 @@ int main(int argc, char * argv[])
return EXIT_SUCCESS;
} else if ((arg == "--apply") || (arg == "-a")) {
apply = true;
} else if ((arg == "--layers") || (arg == "-l")) {
layers = true;
} else if ((arg == "--reapply-first") || (arg == "-R")) {
reapplyFirst = true;
} else if ((arg == "--output") || (arg == "-o")) {
Expand Down Expand Up @@ -368,8 +389,11 @@ int main(int argc, char * argv[])

std::list<GmicQt::InputMode> disabledInputModes;
disabledInputModes.push_back(GmicQt::InputMode::NoInput);
// disabledInputModes.push_back(InputMode::Active);
disabledInputModes.push_back(GmicQt::InputMode::All);
if (layers && (filenames.size() > 1)) {
disabledInputModes.push_back(GmicQt::InputMode::Active);
} else {
disabledInputModes.push_back(GmicQt::InputMode::All);
}
disabledInputModes.push_back(GmicQt::InputMode::ActiveAndBelow);
disabledInputModes.push_back(GmicQt::InputMode::ActiveAndAbove);
disabledInputModes.push_back(GmicQt::InputMode::AllVisible);
Expand Down Expand Up @@ -397,28 +421,54 @@ int main(int argc, char * argv[])
return GmicQt::run(GmicQt::UserInterfaceMode::Full, parameters, disabledInputModes, disabledOutputModes);
}
bool firstLaunch = true;
for (const QString & filename : filenames) {
if (loadImage(gmic_qt_standalone::input_image, filename, argc, argv)) {
gmic_qt_standalone::input_image = gmic_qt_standalone::input_image.convertToFormat(QImage::Format_ARGB32);
gmic_qt_standalone::current_image_filename = QFileInfo(filename).fileName();
gmic_qt_standalone::input_image_filename = gmic_qt_standalone::current_image_filename;
GmicQt::UserInterfaceMode uiMode;
if (apply || (reapplyFirst && !firstLaunch)) {
uiMode = GmicQt::UserInterfaceMode::ProgressDialog;
if (layers) {
gmic_qt_standalone::input_images.resize(filenames.size());
gmic_qt_standalone::current_image_filenames.resize(filenames.size());
gmic_qt_standalone::input_image_filenames.resize(filenames.size());
int n = 0;
for (const QString & filename : filenames) {
if (loadImage(gmic_qt_standalone::input_images[n], filename, argc, argv)) {
gmic_qt_standalone::input_images[n] = gmic_qt_standalone::input_images[n].convertToFormat(QImage::Format_ARGB32);
gmic_qt_standalone::current_image_filenames[n] = QFileInfo(filename).fileName();
gmic_qt_standalone::input_image_filenames[n] = gmic_qt_standalone::current_image_filenames[n];
} else {
uiMode = GmicQt::UserInterfaceMode::Full;
}
if (reapplyFirst && !firstLaunch) {
parameters = GmicQt::lastAppliedFilterRunParameters(GmicQt::ReturnedRunParametersFlag::BeforeFilterExecution);
std::cerr << "Could not open image file " << filename.toStdString() << std::endl;
}
int status = GmicQt::run(uiMode, parameters);
if (status) {
std::cerr << "GmicQt::launchPlugin() returned status " << status << std::endl;
return status;
++n;
}
GmicQt::UserInterfaceMode uiMode = apply ? GmicQt::UserInterfaceMode::ProgressDialog : GmicQt::UserInterfaceMode::Full;
int status = GmicQt::run(uiMode, parameters, disabledInputModes, disabledOutputModes);
if (status) {
std::cerr << "GmicQt::launchPlugin() returned status " << status << std::endl;
return status;
}
} else {
for (const QString & filename : filenames) {
gmic_qt_standalone::input_images.resize(1);
gmic_qt_standalone::current_image_filenames.resize(1);
gmic_qt_standalone::input_image_filenames.resize(1);
if (loadImage(gmic_qt_standalone::input_images.first(), filename, argc, argv)) {
gmic_qt_standalone::input_images.first() = gmic_qt_standalone::input_images.first().convertToFormat(QImage::Format_ARGB32);
gmic_qt_standalone::current_image_filenames.first() = QFileInfo(filename).fileName();
gmic_qt_standalone::input_image_filenames.first() = gmic_qt_standalone::current_image_filenames.first();
GmicQt::UserInterfaceMode uiMode;
if (apply || (reapplyFirst && !firstLaunch)) {
uiMode = GmicQt::UserInterfaceMode::ProgressDialog;
} else {
uiMode = GmicQt::UserInterfaceMode::Full;
}
if (reapplyFirst && !firstLaunch) {
parameters = GmicQt::lastAppliedFilterRunParameters(GmicQt::ReturnedRunParametersFlag::BeforeFilterExecution);
}
int status = GmicQt::run(uiMode, parameters, disabledInputModes, disabledOutputModes);
if (status) {
std::cerr << "GmicQt::launchPlugin() returned status " << status << std::endl;
return status;
}
firstLaunch = false;
} else {
std::cerr << "Could not open image file " << filename.toStdString() << std::endl;
}
firstLaunch = false;
} else {
std::cerr << "Could not open image file " << filename.toStdString() << std::endl;
}
}
return EXIT_SUCCESS;
Expand Down

0 comments on commit 222265c

Please sign in to comment.