Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ed5fc9c1c2 | |||
| cd6a64a98b | |||
| 67355a82b7 | |||
| 8fc2078a3a | |||
| da9b389409 | |||
| 7818b8d3e9 | |||
| 11294bfcb0 | |||
| faecb385aa | |||
| e5be04926b | |||
| eaf2c7094b | |||
| aef41f5f6b | |||
| 2134f13b06 | |||
| 0e9c980325 | |||
| b9bf6bf183 |
@@ -17,7 +17,7 @@ if(SANITIZE_ADDRESS_LEAK)
|
|||||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address -fsanitize=leak")
|
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address -fsanitize=leak")
|
||||||
endif(SANITIZE_ADDRESS_LEAK)
|
endif(SANITIZE_ADDRESS_LEAK)
|
||||||
|
|
||||||
find_package(Qt5 COMPONENTS Widgets Sql OpenGL REQUIRED)
|
find_package(Qt6 COMPONENTS Widgets Sql OpenGLWidgets Qml REQUIRED)
|
||||||
find_library(GSL_LIB gsl REQUIRED)
|
find_library(GSL_LIB gsl REQUIRED)
|
||||||
find_library(GSLCBLAS_LIB gslcblas REQUIRED)
|
find_library(GSLCBLAS_LIB gslcblas REQUIRED)
|
||||||
find_library(EXIF_LIB exif REQUIRED)
|
find_library(EXIF_LIB exif REQUIRED)
|
||||||
@@ -29,6 +29,7 @@ add_subdirectory(libXISF)
|
|||||||
|
|
||||||
set(TENMON_SRC
|
set(TENMON_SRC
|
||||||
about.cpp about.h
|
about.cpp about.h
|
||||||
|
batchprocessing.cpp batchprocessing.h batchprocessing.ui
|
||||||
database.cpp database.h
|
database.cpp database.h
|
||||||
databaseview.cpp databaseview.h
|
databaseview.cpp databaseview.h
|
||||||
delete.cpp
|
delete.cpp
|
||||||
@@ -44,6 +45,7 @@ set(TENMON_SRC
|
|||||||
markedfiles.cpp markedfiles.h
|
markedfiles.cpp markedfiles.h
|
||||||
rawimage.cpp rawimage.h
|
rawimage.cpp rawimage.h
|
||||||
rawimage_sse.cpp
|
rawimage_sse.cpp
|
||||||
|
scriptengine.cpp scriptengine.h
|
||||||
settingsdialog.cpp settingsdialog.h
|
settingsdialog.cpp settingsdialog.h
|
||||||
starfit.cpp starfit.h
|
starfit.cpp starfit.h
|
||||||
statusbar.cpp statusbar.h
|
statusbar.cpp statusbar.h
|
||||||
@@ -52,28 +54,27 @@ set(TENMON_SRC
|
|||||||
)
|
)
|
||||||
|
|
||||||
option(COLOR_MANAGMENT "Enable sRGB framebuffer support for gamma correct images and color profiles support" ON)
|
option(COLOR_MANAGMENT "Enable sRGB framebuffer support for gamma correct images and color profiles support" ON)
|
||||||
if(${Qt5Core_VERSION_STRING} VERSION_LESS "5.14")
|
|
||||||
set(COLOR_MANAGMENT OFF)
|
|
||||||
endif(${Qt5Core_VERSION_STRING} VERSION_LESS "5.14")
|
|
||||||
|
|
||||||
if(COLOR_MANAGMENT)
|
if(COLOR_MANAGMENT)
|
||||||
add_compile_definitions("COLOR_MANAGMENT")
|
add_compile_definitions("COLOR_MANAGMENT")
|
||||||
endif(COLOR_MANAGMENT)
|
endif(COLOR_MANAGMENT)
|
||||||
|
|
||||||
qt5_add_resources(TENMON_SRC resources.qrc)
|
qt_add_resources(TENMON_SRC resources/resources.qrc)
|
||||||
|
qt_add_resources(TENMON_SRC shaders/shaders.qrc)
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
list(APPEND TENMON_SRC icon.rc)
|
list(APPEND TENMON_SRC resources/icon.rc)
|
||||||
set(tenmon_ICON "")
|
set(tenmon_ICON "")
|
||||||
elseif(APPLE)
|
elseif(APPLE)
|
||||||
set(tenmon_ICON ${CMAKE_CURRENT_SOURCE_DIR}/tenmon.icns)
|
set(tenmon_ICON ${CMAKE_CURRENT_SOURCE_DIR}/resources/tenmon.icns)
|
||||||
set_source_files_properties(${tenmon_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
|
set_source_files_properties(${tenmon_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
|
||||||
else()
|
else()
|
||||||
set(tenmon_ICON "")
|
set(tenmon_ICON "")
|
||||||
|
find_package(Qt6 COMPONENTS DBus REQUIRED)
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
pkg_search_module(GIO REQUIRED gio-2.0)
|
pkg_search_module(GIO REQUIRED gio-2.0)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_executable(tenmon WIN32 MACOSX_BUNDLE ${tenmon_ICON} ${TENMON_SRC})
|
qt_add_executable(tenmon WIN32 MACOSX_BUNDLE ${tenmon_ICON} ${TENMON_SRC})
|
||||||
|
|
||||||
find_path(FITS_INCLUDE fitsio2.h PATH_SUFFIXES cfitsio REQUIRED)
|
find_path(FITS_INCLUDE fitsio2.h PATH_SUFFIXES cfitsio REQUIRED)
|
||||||
target_include_directories(tenmon PRIVATE ${FITS_INCLUDE} ${CMAKE_BINARY_DIR} ${libXISF_SOURCE_DIR})
|
target_include_directories(tenmon PRIVATE ${FITS_INCLUDE} ${CMAKE_BINARY_DIR} ${libXISF_SOURCE_DIR})
|
||||||
@@ -82,16 +83,16 @@ if(UNIX AND NOT APPLE)
|
|||||||
target_include_directories(tenmon PRIVATE ${GIO_INCLUDE_DIRS})
|
target_include_directories(tenmon PRIVATE ${GIO_INCLUDE_DIRS})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_link_libraries(tenmon Qt5::Widgets Qt5::Sql ${GSL_LIB} ${GSLCBLAS_LIB} ${EXIF_LIB} ${FITS_LIB} ${RAW_LIB} ${WCS_LIB} XISF)
|
target_link_libraries(tenmon PRIVATE Qt6::Widgets Qt6::Sql Qt6::OpenGLWidgets Qt6::Qml ${GSL_LIB} ${GSLCBLAS_LIB} ${EXIF_LIB} ${FITS_LIB} ${RAW_LIB} ${WCS_LIB} XISF)
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
target_link_libraries(tenmon "-framework CoreFoundation")
|
target_link_libraries(tenmon PRIVATE "-framework CoreFoundation")
|
||||||
else()
|
elseif(UNIX)
|
||||||
target_link_libraries(tenmon ${GIO_LDFLAGS})
|
target_link_libraries(tenmon PRIVATE Qt6::DBus ${GIO_LDFLAGS})
|
||||||
endif(APPLE)
|
endif(APPLE)
|
||||||
|
|
||||||
if(LIBRAW_STATIC)
|
if(LIBRAW_STATIC)
|
||||||
add_compile_definitions("LIBRAW_NODLL")
|
add_compile_definitions("LIBRAW_NODLL")
|
||||||
target_link_libraries(tenmon jasper)
|
target_link_libraries(tenmon PRIVATE jasper)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
install(TARGETS tenmon BUNDLE DESTINATION .)
|
install(TARGETS tenmon BUNDLE DESTINATION .)
|
||||||
@@ -102,8 +103,8 @@ if(UNIX AND NOT APPLE)
|
|||||||
install(SCRIPT install.cmake)
|
install(SCRIPT install.cmake)
|
||||||
else()
|
else()
|
||||||
install(FILES space.nouspiro.tenmon.desktop DESTINATION "${CMAKE_INSTALL_DATADIR}/applications")
|
install(FILES space.nouspiro.tenmon.desktop DESTINATION "${CMAKE_INSTALL_DATADIR}/applications")
|
||||||
install(FILES space.nouspiro.tenmon.png DESTINATION "${CMAKE_INSTALL_DATADIR}/icons/hicolor/64x64/apps")
|
install(FILES resources/space.nouspiro.tenmon.png DESTINATION "${CMAKE_INSTALL_DATADIR}/icons/hicolor/64x64/apps")
|
||||||
install(FILES space.nouspiro.tenmon_128.png DESTINATION "${CMAKE_INSTALL_DATADIR}/icons/hicolor/128x128/apps" RENAME space.nouspiro.tenmon.png)
|
install(FILES resources/space.nouspiro.tenmon_128.png DESTINATION "${CMAKE_INSTALL_DATADIR}/icons/hicolor/128x128/apps" RENAME space.nouspiro.tenmon.png)
|
||||||
endif()
|
endif()
|
||||||
install(FILES space.nouspiro.tenmon.metainfo.xml DESTINATION "${CMAKE_INSTALL_DATADIR}/metainfo")
|
install(FILES space.nouspiro.tenmon.metainfo.xml DESTINATION "${CMAKE_INSTALL_DATADIR}/metainfo")
|
||||||
endif(UNIX AND NOT APPLE)
|
endif(UNIX AND NOT APPLE)
|
||||||
|
|||||||
@@ -2,20 +2,20 @@ FITS/XISF image viewer with multithreaded image loading
|
|||||||
|
|
||||||
To get all dependencies install these packages
|
To get all dependencies install these packages
|
||||||
|
|
||||||
sudo apt install qtbase5-dev libraw-dev libexif-dev libcfitsio-dev libgsl-dev wcslib-dev libopencv-dev cmake
|
sudo apt install qt6-base-dev libqt6opengl6-dev libraw-dev libexif-dev libcfitsio-dev libgsl-dev wcslib-dev cmake
|
||||||
|
|
||||||
on OpenSUSE
|
on OpenSUSE
|
||||||
|
|
||||||
sudo zypper install opencv-devel gsl-devel exif-devel libraw-devel wcslib-devel libqt5-qtbase-devel
|
sudo zypper install gsl-devel exif-devel libraw-devel wcslib-devel libqt6-qtbase-devel
|
||||||
|
|
||||||
MacOS X
|
MacOS X
|
||||||
|
|
||||||
To compile on MacOS install XCode first. Then install homebrew in x86_64 mode
|
To compile on MacOS install XCode first. Then install homebrew in x86_64 mode
|
||||||
with "arch -i x86_64". Building on native ARM is not supported.
|
with "arch -i x86_64". Building on native ARM is not supported.
|
||||||
|
|
||||||
homebrew install qt5 libraw cfitsio libexif libgsl wcslib opencv
|
homebrew install qt6 libraw cfitsio libexif libgsl wcslib
|
||||||
|
|
||||||
You may need to set CMAKE_PREFIX_PATH for Qt5 and OpenCV so CMake can find them.
|
You may need to set CMAKE_PREFIX_PATH for Qt6 so CMake can find them.
|
||||||
|
|
||||||
Then to build run standard cmake
|
Then to build run standard cmake
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,218 @@
|
|||||||
|
#include "batchprocessing.h"
|
||||||
|
#include "ui_batchprocessing.h"
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include <QProcess>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QCloseEvent>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include "scriptengine.h"
|
||||||
|
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
#include <QCloseEvent>
|
||||||
|
#include <QDBusConnection>
|
||||||
|
#include <QDBusMessage>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void scanDirectory(const QString &path, QStringList &files)
|
||||||
|
{
|
||||||
|
QFileInfo info(path);
|
||||||
|
if(info.isDir())
|
||||||
|
{
|
||||||
|
QDir dir(path);
|
||||||
|
QStringList entries = dir.entryList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);
|
||||||
|
for(QString &entry : entries)
|
||||||
|
scanDirectory(dir.absoluteFilePath(entry), files);
|
||||||
|
}
|
||||||
|
else if(info.isFile())
|
||||||
|
{
|
||||||
|
files.append(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList scanDirectories(const QStringList &paths)
|
||||||
|
{
|
||||||
|
QStringList files;
|
||||||
|
|
||||||
|
for(const QString &path : paths)
|
||||||
|
scanDirectory(path, files);
|
||||||
|
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BatchProcessing::scanScriptDir()
|
||||||
|
{
|
||||||
|
_ui->scriptsList->clear();
|
||||||
|
QDir dir(_scriptBasePath);
|
||||||
|
for(const QString &script : dir.entryList(QDir::Files | QDir::Readable))
|
||||||
|
{
|
||||||
|
_ui->scriptsList->addItem(script);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BatchProcessing::BatchProcessing(QWidget *parent) : QDialog(parent)
|
||||||
|
{
|
||||||
|
_ui = new Ui::BatchProcessing;
|
||||||
|
_ui->setupUi(this);
|
||||||
|
|
||||||
|
QStringList scriptsPath = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation);
|
||||||
|
if(scriptsPath.size())
|
||||||
|
{
|
||||||
|
QDir dir(scriptsPath.first());
|
||||||
|
if(!dir.exists("scripts"))
|
||||||
|
{
|
||||||
|
if(!dir.mkpath("scripts"))
|
||||||
|
qWarning() << "Failed to create scripts directory";
|
||||||
|
}
|
||||||
|
dir.cd("scripts");
|
||||||
|
|
||||||
|
_scriptBasePath = dir.absolutePath() + "/";
|
||||||
|
scanScriptDir();
|
||||||
|
_fileWatcher.addPath(_scriptBasePath);
|
||||||
|
connect(&_fileWatcher, &QFileSystemWatcher::directoryChanged, this, &BatchProcessing::scanScriptDir);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qWarning() << "Failed to get app data location";
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(_ui->addFilesButton, &QPushButton::released, this, &BatchProcessing::addFiles);
|
||||||
|
connect(_ui->addDirButton, &QPushButton::released, this, &BatchProcessing::addDir);
|
||||||
|
connect(_ui->removeButton, &QPushButton::released, this, &BatchProcessing::removePath);
|
||||||
|
connect(_ui->removeAllButton, &QPushButton::released, this, &BatchProcessing::removeAllPaths);
|
||||||
|
connect(_ui->startButton, &QPushButton::released, this, &BatchProcessing::runScript);
|
||||||
|
connect(_ui->stopButton, &QPushButton::released, this, &BatchProcessing::stopScript);
|
||||||
|
connect(_ui->browseButton, &QPushButton::released, this, &BatchProcessing::browse);
|
||||||
|
connect(_ui->openScriptsButton, &QPushButton::released, this, &BatchProcessing::openScriptDir);
|
||||||
|
|
||||||
|
QSettings settings;
|
||||||
|
_ui->outputPath->setText(settings.value("batchprocessing/outputpath", QStandardPaths::standardLocations(QStandardPaths::PicturesLocation).first()).toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
BatchProcessing::~BatchProcessing()
|
||||||
|
{
|
||||||
|
delete _engineThread;
|
||||||
|
QSettings settings;
|
||||||
|
settings.setValue("batchprocessing/outputpath", _ui->outputPath->text());
|
||||||
|
delete _ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BatchProcessing::closeEvent(QCloseEvent *event)
|
||||||
|
{
|
||||||
|
if(_engineThread)
|
||||||
|
{
|
||||||
|
QMessageBox::StandardButton ret = QMessageBox::question(this, tr("Interrupt running script?"), tr("Interrupt running script?"));
|
||||||
|
if(ret == QMessageBox::StandardButton::Yes)
|
||||||
|
{
|
||||||
|
_engineThread->interrupt();
|
||||||
|
event->accept();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
event->ignore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
event->accept();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BatchProcessing::addFiles()
|
||||||
|
{
|
||||||
|
QStringList files = QFileDialog::getOpenFileNames(this, tr("Select files"), "/home/nou/Obrázky/astro");
|
||||||
|
_ui->pathsList->addItems(files);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BatchProcessing::addDir()
|
||||||
|
{
|
||||||
|
QString dir = QFileDialog::getExistingDirectory(this, tr("Select directory"), "/home/nou/Obrázky/astro");
|
||||||
|
if(!dir.isEmpty())
|
||||||
|
_ui->pathsList->addItem(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BatchProcessing::removePath()
|
||||||
|
{
|
||||||
|
for(auto &item : _ui->pathsList->selectedItems())
|
||||||
|
delete item;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BatchProcessing::removeAllPaths()
|
||||||
|
{
|
||||||
|
_ui->pathsList->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BatchProcessing::browse()
|
||||||
|
{
|
||||||
|
QString output = QFileDialog::getExistingDirectory(this, tr("Select output directory"), "/home/nou/Obrázky");
|
||||||
|
if(!output.isEmpty())
|
||||||
|
_ui->outputPath->setText(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BatchProcessing::openScriptDir()
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
QDBusConnection con = QDBusConnection::sessionBus();
|
||||||
|
QDBusMessage message = QDBusMessage::createMethodCall("org.freedesktop.FileManager1", "/org/freedesktop/FileManager1", "org.freedesktop.FileManager1", "ShowFolders");
|
||||||
|
QList<QVariant> args = {QStringList(QUrl::fromLocalFile(_scriptBasePath).toString()), QString()};
|
||||||
|
message.setArguments(args);
|
||||||
|
con.call(message);
|
||||||
|
#endif
|
||||||
|
#ifdef Q_OS_WINDOWS
|
||||||
|
QProcess::startDetached("explorer.exe", {QDir::toNativeSeparators(_scriptBasePath)});
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void BatchProcessing::runScript()
|
||||||
|
{
|
||||||
|
_ui->log->clear();
|
||||||
|
auto selectedItems = _ui->scriptsList->selectedItems();
|
||||||
|
if(selectedItems.size())
|
||||||
|
{
|
||||||
|
_engineThread = new Script::ScriptEngineThread(this);
|
||||||
|
connect(_engineThread, &Script::ScriptEngineThread::newMessage, this, &BatchProcessing::newMessage);
|
||||||
|
connect(_engineThread, &Script::ScriptEngineThread::finished, this, &BatchProcessing::scriptFinished);
|
||||||
|
QStringList paths;
|
||||||
|
for(int i=0; i<_ui->pathsList->count(); i++)
|
||||||
|
paths.append(_ui->pathsList->item(i)->text());
|
||||||
|
|
||||||
|
QFileInfo outDir(_ui->outputPath->text());
|
||||||
|
if(outDir.exists() && outDir.isWritable())
|
||||||
|
{
|
||||||
|
_engineThread->setParams(_scriptBasePath + selectedItems.first()->text(), scanDirectories(paths), _ui->outputPath->text());
|
||||||
|
_engineThread->start();
|
||||||
|
_ui->startButton->setEnabled(false);
|
||||||
|
_ui->stopButton->setEnabled(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QMessageBox::warning(this, tr("Invalid output directory"), tr("Output directory path doesn't exist or is not writable"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BatchProcessing::stopScript()
|
||||||
|
{
|
||||||
|
qDebug() << "Stop script";
|
||||||
|
if(_engineThread)
|
||||||
|
_engineThread->interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BatchProcessing::scriptFinished()
|
||||||
|
{
|
||||||
|
_ui->startButton->setEnabled(true);
|
||||||
|
_ui->stopButton->setEnabled(false);
|
||||||
|
qDebug() << "script finished";
|
||||||
|
delete _engineThread;
|
||||||
|
_engineThread = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BatchProcessing::newMessage(const QString &message, bool error)
|
||||||
|
{
|
||||||
|
QColor color = _ui->log->textColor();
|
||||||
|
if(error)_ui->log->setTextColor(Qt::red);
|
||||||
|
_ui->log->append(message);
|
||||||
|
if(error)_ui->log->setTextColor(color);
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
#ifndef BATCHPROCESSING_H
|
||||||
|
#define BATCHPROCESSING_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QFileSystemWatcher>
|
||||||
|
#include "scriptengine.h"
|
||||||
|
|
||||||
|
namespace Ui { class BatchProcessing; }
|
||||||
|
|
||||||
|
class BatchProcessing : public QDialog
|
||||||
|
{
|
||||||
|
Ui::BatchProcessing *_ui;
|
||||||
|
QString _scriptBasePath;
|
||||||
|
QFileSystemWatcher _fileWatcher;
|
||||||
|
Script::ScriptEngineThread *_engineThread = nullptr;
|
||||||
|
private slots:
|
||||||
|
void scanScriptDir();
|
||||||
|
public:
|
||||||
|
explicit BatchProcessing(QWidget *parent = nullptr);
|
||||||
|
~BatchProcessing();
|
||||||
|
protected:
|
||||||
|
void closeEvent(QCloseEvent *event);
|
||||||
|
public slots:
|
||||||
|
void scriptDirChanged();
|
||||||
|
void addFiles();
|
||||||
|
void addDir();
|
||||||
|
void removePath();
|
||||||
|
void removeAllPaths();
|
||||||
|
void browse();
|
||||||
|
void openScriptDir();
|
||||||
|
void runScript();
|
||||||
|
void stopScript();
|
||||||
|
void scriptFinished();
|
||||||
|
void newMessage(const QString &message, bool error);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // BATCHPROCESSING_H
|
||||||
@@ -0,0 +1,226 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>BatchProcessing</class>
|
||||||
|
<widget class="QDialog" name="BatchProcessing">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>1024</width>
|
||||||
|
<height>768</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Batch Processing</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Input files and directories</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QListWidget" name="pathsList">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>1</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="selectionMode">
|
||||||
|
<enum>QAbstractItemView::MultiSelection</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="addFilesButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Add files</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="addDirButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Add directories</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="removeButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Remove</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="removeAllButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Remove all</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Output directory</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="outputPath">
|
||||||
|
<property name="readOnly">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="browseButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Browse</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="text">
|
||||||
|
<string>Scripts</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="openScriptsButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Open scripts</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QListWidget" name="scriptsList">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>1</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string>Log</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QTextEdit" name="log">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>4</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<family>FreeMono</family>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="readOnly">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="startButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Start script</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="stopButton">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Stop script</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="closeButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Close</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>closeButton</sender>
|
||||||
|
<signal>released()</signal>
|
||||||
|
<receiver>BatchProcessing</receiver>
|
||||||
|
<slot>close()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>973</x>
|
||||||
|
<y>745</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>511</x>
|
||||||
|
<y>383</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
||||||
@@ -10,12 +10,12 @@ Database::Database(QObject *parent) : QObject(parent)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Database::init()
|
bool Database::init(const QLatin1String &connectionName)
|
||||||
{
|
{
|
||||||
QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
||||||
QDir dir(path);
|
QDir dir(path);
|
||||||
|
|
||||||
QSqlDatabase m_database = QSqlDatabase::addDatabase("QSQLITE");
|
QSqlDatabase m_database = QSqlDatabase::addDatabase("QSQLITE", connectionName);
|
||||||
|
|
||||||
if(!dir.mkpath("."))
|
if(!dir.mkpath("."))
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class Database : public QObject
|
|||||||
int m_progress;
|
int m_progress;
|
||||||
public:
|
public:
|
||||||
explicit Database(QObject *parent = 0);
|
explicit Database(QObject *parent = 0);
|
||||||
bool init();
|
bool init(const QLatin1String &connectionName = QLatin1String(QSqlDatabase::defaultConnection));
|
||||||
bool mark(const QString &filename);
|
bool mark(const QString &filename);
|
||||||
bool unmark(const QString &filename);
|
bool unmark(const QString &filename);
|
||||||
bool mark(const QStringList &filenames);
|
bool mark(const QStringList &filenames);
|
||||||
|
|||||||
@@ -201,7 +201,8 @@ void FITSFileModel::prepareQuery()
|
|||||||
if(lastError().type() != QSqlError::NoError)
|
if(lastError().type() != QSqlError::NoError)
|
||||||
qDebug() << "Database error" << lastError();
|
qDebug() << "Database error" << lastError();
|
||||||
|
|
||||||
m_markedFiles = m_database->getMarkedFiles().toSet();
|
QStringList list = m_database->getMarkedFiles();
|
||||||
|
m_markedFiles = QSet<QString>(list.begin(), list.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
DatabaseTableView::DatabaseTableView(QWidget *parent) : QTableView(parent)
|
DatabaseTableView::DatabaseTableView(QWidget *parent) : QTableView(parent)
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#include "imageringlist.h"
|
#include "imageringlist.h"
|
||||||
#include <QThreadPool>
|
#include <QThreadPool>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QTimer>
|
||||||
#include "loadrunable.h"
|
#include "loadrunable.h"
|
||||||
#include "rawimage.h"
|
#include "rawimage.h"
|
||||||
#include "database.h"
|
#include "database.h"
|
||||||
@@ -108,6 +110,9 @@ ImageRingList::ImageRingList(Database *database, const QStringList &nameFilter,
|
|||||||
connect(&m_fileSystemWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(dirChanged(QString)));
|
connect(&m_fileSystemWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(dirChanged(QString)));
|
||||||
m_nameFilter.replaceInStrings(QRegularExpression("^"), "*.");
|
m_nameFilter.replaceInStrings(QRegularExpression("^"), "*.");
|
||||||
m_thumbPool = new QThreadPool(this);
|
m_thumbPool = new QThreadPool(this);
|
||||||
|
|
||||||
|
m_slideShowTimer = new QTimer(this);
|
||||||
|
connect(m_slideShowTimer, &QTimer::timeout, this, static_cast<void (ImageRingList::*)()>(&ImageRingList::increment));
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageRingList::~ImageRingList()
|
ImageRingList::~ImageRingList()
|
||||||
@@ -163,6 +168,10 @@ void ImageRingList::increment()
|
|||||||
{
|
{
|
||||||
if(m_images.size())
|
if(m_images.size())
|
||||||
{
|
{
|
||||||
|
//don't increment if current image was not loaded yet
|
||||||
|
if(!(*m_currImage)->rawImage())
|
||||||
|
return;
|
||||||
|
|
||||||
(*m_firstImage)->release();
|
(*m_firstImage)->release();
|
||||||
m_firstImage = increment(m_firstImage);
|
m_firstImage = increment(m_firstImage);
|
||||||
m_currImage = increment(m_currImage);
|
m_currImage = increment(m_currImage);
|
||||||
@@ -295,11 +304,13 @@ void ImageRingList::clearThumbnails()
|
|||||||
|
|
||||||
QModelIndex ImageRingList::index(int row, int column, const QModelIndex &parent) const
|
QModelIndex ImageRingList::index(int row, int column, const QModelIndex &parent) const
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(parent);
|
||||||
return createIndex(row, column, m_images.at(row).get());
|
return createIndex(row, column, m_images.at(row).get());
|
||||||
}
|
}
|
||||||
|
|
||||||
QModelIndex ImageRingList::parent(const QModelIndex &child) const
|
QModelIndex ImageRingList::parent(const QModelIndex &child) const
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(child);
|
||||||
return QModelIndex();
|
return QModelIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,6 +324,7 @@ int ImageRingList::rowCount(const QModelIndex &parent) const
|
|||||||
|
|
||||||
int ImageRingList::columnCount(const QModelIndex &parent) const
|
int ImageRingList::columnCount(const QModelIndex &parent) const
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(parent);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -398,6 +410,20 @@ void ImageRingList::reverseSort()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ImageRingList::toggleSlideshow(bool start)
|
||||||
|
{
|
||||||
|
if(start)
|
||||||
|
{
|
||||||
|
QSettings settings;
|
||||||
|
int time = settings.value("settings/slideshowtime", 1.0).toDouble() * 1000;
|
||||||
|
m_slideShowTimer->start(time);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_slideShowTimer->stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ImageRingList::setFiles(const QStringList files, const QString ¤tFile)
|
void ImageRingList::setFiles(const QStringList files, const QString ¤tFile)
|
||||||
{
|
{
|
||||||
QThreadPool::globalInstance()->clear();
|
QThreadPool::globalInstance()->clear();
|
||||||
|
|||||||
@@ -65,14 +65,13 @@ class ImageRingList : public QAbstractItemModel
|
|||||||
QThreadPool *m_thumbPool;
|
QThreadPool *m_thumbPool;
|
||||||
Database *m_database;
|
Database *m_database;
|
||||||
QStringList m_nameFilter;
|
QStringList m_nameFilter;
|
||||||
|
QTimer *m_slideShowTimer;
|
||||||
public:
|
public:
|
||||||
explicit ImageRingList(Database *database, const QStringList &nameFilter, QObject *parent = 0);
|
explicit ImageRingList(Database *database, const QStringList &nameFilter, QObject *parent = 0);
|
||||||
~ImageRingList() override;
|
~ImageRingList() override;
|
||||||
bool setDir(const QString path, const QString ¤tFile = QString());
|
bool setDir(const QString path, const QString ¤tFile = QString());
|
||||||
void setFile(const QString &file);
|
void setFile(const QString &file);
|
||||||
ImagePtr currentImage();
|
ImagePtr currentImage();
|
||||||
void increment();
|
|
||||||
void decrement();
|
|
||||||
void setLiveMode(bool live);
|
void setLiveMode(bool live);
|
||||||
void setCalculateStats(bool stats);
|
void setCalculateStats(bool stats);
|
||||||
void setFindPeaks(bool findPeaks);
|
void setFindPeaks(bool findPeaks);
|
||||||
@@ -96,6 +95,9 @@ public slots:
|
|||||||
void setPreload(int width);
|
void setPreload(int width);
|
||||||
void setSort(QDir::SortFlag sort);
|
void setSort(QDir::SortFlag sort);
|
||||||
void reverseSort();
|
void reverseSort();
|
||||||
|
void toggleSlideshow(bool start);
|
||||||
|
void increment();
|
||||||
|
void decrement();
|
||||||
protected:
|
protected:
|
||||||
void setFiles(const QStringList files, const QString ¤tFile = QString());
|
void setFiles(const QStringList files, const QString ¤tFile = QString());
|
||||||
QList<ImagePtr>::iterator increment(QList<ImagePtr>::iterator iter);
|
QList<ImagePtr>::iterator increment(QList<ImagePtr>::iterator iter);
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ void ImageScrollArea::wheelEvent(QWheelEvent *event)
|
|||||||
m_scale = (float)size().width()/m_pixmap.size().width();
|
m_scale = (float)size().width()/m_pixmap.size().width();
|
||||||
|
|
||||||
QPointF top(horizontalScrollBar()->value(), verticalScrollBar()->value());
|
QPointF top(horizontalScrollBar()->value(), verticalScrollBar()->value());
|
||||||
QPointF mousePos = (top + event->posF()) / m_scale;
|
QPointF mousePos = (top + event->position()) / m_scale;
|
||||||
|
|
||||||
QPoint delta = event->angleDelta();
|
QPoint delta = event->angleDelta();
|
||||||
if(delta.y() > 0)
|
if(delta.y() > 0)
|
||||||
@@ -115,7 +115,7 @@ void ImageScrollArea::wheelEvent(QWheelEvent *event)
|
|||||||
setScale(m_scale - 0.1);
|
setScale(m_scale - 0.1);
|
||||||
|
|
||||||
mousePos *= m_scale;
|
mousePos *= m_scale;
|
||||||
top = mousePos - event->posF();
|
top = mousePos - event->position();
|
||||||
horizontalScrollBar()->setValue(top.x());
|
horizontalScrollBar()->setValue(top.x());
|
||||||
verticalScrollBar()->setValue(top.y());
|
verticalScrollBar()->setValue(top.y());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "imagescrollareagl.h"
|
#include "imagescrollareagl.h"
|
||||||
#include <QOpenGLFunctions>
|
#include <QOpenGLFunctions>
|
||||||
|
#include <QOpenGLVersionFunctionsFactory>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
#include <QOpenGLDebugLogger>
|
#include <QOpenGLDebugLogger>
|
||||||
@@ -14,6 +15,8 @@
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <QElapsedTimer>
|
#include <QElapsedTimer>
|
||||||
|
|
||||||
|
int FILTERING = 1;
|
||||||
|
|
||||||
struct RawImageType
|
struct RawImageType
|
||||||
{
|
{
|
||||||
QOpenGLTexture::PixelFormat pixelFormat;
|
QOpenGLTexture::PixelFormat pixelFormat;
|
||||||
@@ -46,6 +49,10 @@ RawImageType getRawImageType(const RawImage *img)
|
|||||||
else
|
else
|
||||||
type.textureFormat = QOpenGLTexture::R32F;
|
type.textureFormat = QOpenGLTexture::R32F;
|
||||||
type.dataType = QOpenGLTexture::Float32;
|
type.dataType = QOpenGLTexture::Float32;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qWarning() << "Invalid format" << img->type();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(img->channels() >= 3)
|
if(img->channels() >= 3)
|
||||||
@@ -115,16 +122,11 @@ void ImageWidget::setImage(std::shared_ptr<RawImage> image, int index)
|
|||||||
|
|
||||||
m_unit_scale[0] = 1.0f;
|
m_unit_scale[0] = 1.0f;
|
||||||
m_unit_scale[1] = 0.0f;
|
m_unit_scale[1] = 0.0f;
|
||||||
auto &stats = image->imageStats();
|
if(image->type() == RawImage::FLOAT32)
|
||||||
if(image->type() == RawImage::FLOAT32 || image->type() == RawImage::FLOAT64)
|
|
||||||
{
|
{
|
||||||
float min = *std::min_element(stats.m_min, stats.m_min + 4);
|
auto unitScaling = image->unitScale();
|
||||||
float max = *std::max_element(stats.m_max, stats.m_max + 4);
|
m_unit_scale[0] = unitScaling.first;
|
||||||
if(min < 0.0f || max > 1.0f)
|
m_unit_scale[1] = unitScaling.second;
|
||||||
{
|
|
||||||
m_unit_scale[0] = 1.0f / (max - min);
|
|
||||||
m_unit_scale[1] = min * m_unit_scale[0];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(m_debayerTex)
|
if(m_debayerTex)
|
||||||
@@ -213,6 +215,18 @@ QVector2D ImageWidget::getImagePixelCoord(const QVector2D &pos)
|
|||||||
return (pos + offset) / m_scale;
|
return (pos + offset) / m_scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ImageWidget::setBayerMask(int mask)
|
||||||
|
{
|
||||||
|
m_firstRed[0] = mask & 0x1;
|
||||||
|
m_firstRed[1] = (mask & 0x2) >> 1;
|
||||||
|
if(m_debayerTex)
|
||||||
|
{
|
||||||
|
f->glDeleteTextures(1, &m_debayerTex);
|
||||||
|
m_debayerTex = 0;
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
void ImageWidget::setMTFParams(const MTFParam ¶ms)
|
void ImageWidget::setMTFParams(const MTFParam ¶ms)
|
||||||
{
|
{
|
||||||
m_mtfParams = params;
|
m_mtfParams = params;
|
||||||
@@ -380,6 +394,7 @@ void ImageWidget::paintGL()
|
|||||||
m_program->setUniformValue("bw", m_bwImg && !m_superpixel);
|
m_program->setUniformValue("bw", m_bwImg && !m_superpixel);
|
||||||
m_program->setUniformValue("false_color", m_falseColor && m_bwImg);
|
m_program->setUniformValue("false_color", m_falseColor && m_bwImg);
|
||||||
m_program->setUniformValue("invert", m_invert);
|
m_program->setUniformValue("invert", m_invert);
|
||||||
|
m_program->setUniformValue("filtering", m_scale > 1.0f ? FILTERING : 1);
|
||||||
#ifdef COLOR_MANAGMENT
|
#ifdef COLOR_MANAGMENT
|
||||||
m_program->setUniformValue("srgb", m_srgb);
|
m_program->setUniformValue("srgb", m_srgb);
|
||||||
#endif
|
#endif
|
||||||
@@ -401,7 +416,7 @@ void ImageWidget::initializeGL()
|
|||||||
{
|
{
|
||||||
f = context()->functions();
|
f = context()->functions();
|
||||||
f->glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
|
f->glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
|
||||||
f3 = context()->versionFunctions<QOpenGLFunctions_3_3_Core>();
|
f3 = QOpenGLVersionFunctionsFactory::get<QOpenGLFunctions_3_3_Core>(context());
|
||||||
|
|
||||||
if(f3 == nullptr)
|
if(f3 == nullptr)
|
||||||
QMessageBox::critical(this, tr("OpenGL error"), tr("Could not initialize OpenGL 3.3 context. Ensure that proper GPU driver is installed."));
|
QMessageBox::critical(this, tr("OpenGL error"), tr("Could not initialize OpenGL 3.3 context. Ensure that proper GPU driver is installed."));
|
||||||
@@ -444,8 +459,8 @@ void ImageWidget::initializeGL()
|
|||||||
// f->glVertexAttribPointer(0, 2, GL_FLOAT, false, sizeof(float)*4, 0);
|
// f->glVertexAttribPointer(0, 2, GL_FLOAT, false, sizeof(float)*4, 0);
|
||||||
|
|
||||||
m_program = std::unique_ptr<QOpenGLShaderProgram>(new QOpenGLShaderProgram);
|
m_program = std::unique_ptr<QOpenGLShaderProgram>(new QOpenGLShaderProgram);
|
||||||
m_program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/image.vert");
|
m_program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/image.vert");
|
||||||
m_program->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/image.frag");
|
m_program->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/image.frag");
|
||||||
|
|
||||||
if(!m_program->link())
|
if(!m_program->link())
|
||||||
{
|
{
|
||||||
@@ -461,8 +476,8 @@ void ImageWidget::initializeGL()
|
|||||||
m_program->setUniformValue("scale", 1.0f, 0.0f);
|
m_program->setUniformValue("scale", 1.0f, 0.0f);
|
||||||
|
|
||||||
m_debayerProgram = std::unique_ptr<QOpenGLShaderProgram>(new QOpenGLShaderProgram);
|
m_debayerProgram = std::unique_ptr<QOpenGLShaderProgram>(new QOpenGLShaderProgram);
|
||||||
m_debayerProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/debayer.vert");
|
m_debayerProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/debayer.vert");
|
||||||
m_debayerProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/debayer.frag");
|
m_debayerProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/debayer.frag");
|
||||||
|
|
||||||
m_debayerProgram->bind();
|
m_debayerProgram->bind();
|
||||||
m_debayerProgram->enableAttributeArray("qt_Vertex");
|
m_debayerProgram->enableAttributeArray("qt_Vertex");
|
||||||
@@ -478,8 +493,8 @@ void ImageWidget::initializeGL()
|
|||||||
m_vaoThumb->bind();
|
m_vaoThumb->bind();
|
||||||
|
|
||||||
m_thumbnailProgram = std::unique_ptr<QOpenGLShaderProgram>(new QOpenGLShaderProgram);
|
m_thumbnailProgram = std::unique_ptr<QOpenGLShaderProgram>(new QOpenGLShaderProgram);
|
||||||
m_thumbnailProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/thumb.vert");
|
m_thumbnailProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/thumb.vert");
|
||||||
m_thumbnailProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/thumb.frag");
|
m_thumbnailProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/thumb.frag");
|
||||||
|
|
||||||
m_thumbnailProgram->bind();
|
m_thumbnailProgram->bind();
|
||||||
m_thumbnailProgram->enableAttributeArray("qt_Vertex");
|
m_thumbnailProgram->enableAttributeArray("qt_Vertex");
|
||||||
@@ -559,7 +574,7 @@ void ImageWidget::mousePressEvent(QMouseEvent *event)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(event->button() == Qt::LeftButton)
|
if(event->button() == Qt::LeftButton)
|
||||||
m_lastPos = event->localPos();
|
m_lastPos = event->position();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -571,8 +586,8 @@ void ImageWidget::mouseMoveEvent(QMouseEvent *event)
|
|||||||
}
|
}
|
||||||
else if(!m_lastPos.isNull())
|
else if(!m_lastPos.isNull())
|
||||||
{
|
{
|
||||||
QPointF off = event->localPos() - m_lastPos;
|
QPointF off = event->position() - m_lastPos;
|
||||||
m_lastPos = event->localPos();
|
m_lastPos = event->position();
|
||||||
setOffset(m_dx - off.x(), m_dy - off.y());
|
setOffset(m_dx - off.x(), m_dy - off.y());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -638,7 +653,7 @@ void ImageWidget::wheelEvent(QWheelEvent *event)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(std::abs(event->angleDelta().y()) > 15)
|
if(std::abs(event->angleDelta().y()) > 15)
|
||||||
zoom(event->angleDelta().y(), event->modifiers() & Qt::ShiftModifier ? QPointF() : event->posF());
|
zoom(event->angleDelta().y(), event->modifiers() & Qt::ShiftModifier ? QPointF() : event->position());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -681,6 +696,7 @@ void ImageWidget::debayer()
|
|||||||
f->glViewport(0, 0, m_imgWidth, m_imgHeight);
|
f->glViewport(0, 0, m_imgWidth, m_imgHeight);
|
||||||
|
|
||||||
m_debayerProgram->bind();
|
m_debayerProgram->bind();
|
||||||
|
f->glUniform2i(m_debayerProgram->uniformLocation("firstRed"), m_firstRed[0], m_firstRed[1]);
|
||||||
m_image->bind(0);
|
m_image->bind(0);
|
||||||
f->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
f->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||||
|
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ class ImageWidget : public QOpenGLWidget
|
|||||||
int m_thumbnailCount = 0;
|
int m_thumbnailCount = 0;
|
||||||
int m_maxTextureSize = 0;
|
int m_maxTextureSize = 0;
|
||||||
int m_maxArrayLayers = 0;
|
int m_maxArrayLayers = 0;
|
||||||
|
int m_firstRed[2] = {0, 0};
|
||||||
QVector<ImageThumb> m_thumnails;
|
QVector<ImageThumb> m_thumnails;
|
||||||
Database *m_database = nullptr;
|
Database *m_database = nullptr;
|
||||||
QPointF m_lastPos;
|
QPointF m_lastPos;
|
||||||
@@ -79,6 +80,7 @@ public:
|
|||||||
void blockRepaint(bool block);
|
void blockRepaint(bool block);
|
||||||
void allocateThumbnails(const QStringList &paths);
|
void allocateThumbnails(const QStringList &paths);
|
||||||
QVector2D getImagePixelCoord(const QVector2D &pos);
|
QVector2D getImagePixelCoord(const QVector2D &pos);
|
||||||
|
void setBayerMask(int mask);
|
||||||
public slots:
|
public slots:
|
||||||
void setMTFParams(const MTFParam ¶ms);
|
void setMTFParams(const MTFParam ¶ms);
|
||||||
void setOffset(float dx, float dy);
|
void setOffset(float dx, float dy);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
find_program(XDG-DESKTOP-MENU_EXECUTABLE xdg-desktop-menu)
|
find_program(XDG-DESKTOP-MENU_EXECUTABLE xdg-desktop-menu)
|
||||||
find_program(XDG-ICON-RESOURCE_EXECUTABLE xdg-icon-resource)
|
find_program(XDG-ICON-RESOURCE_EXECUTABLE xdg-icon-resource)
|
||||||
execute_process(COMMAND ${XDG-DESKTOP-MENU_EXECUTABLE} install --novendor space.nouspiro.tenmon.desktop WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
|
execute_process(COMMAND ${XDG-DESKTOP-MENU_EXECUTABLE} install --novendor space.nouspiro.tenmon.desktop WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
|
||||||
execute_process(COMMAND ${XDG-ICON-RESOURCE_EXECUTABLE} install --novendor --size 64 space.nouspiro.tenmon.png space.nouspiro.tenmon WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
|
execute_process(COMMAND ${XDG-ICON-RESOURCE_EXECUTABLE} install --novendor --size 64 resources/space.nouspiro.tenmon.png space.nouspiro.tenmon WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
|
||||||
execute_process(COMMAND ${XDG-ICON-RESOURCE_EXECUTABLE} install --novendor --size 128 space.nouspiro.tenmon_128.png space.nouspiro.tenmon WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
|
execute_process(COMMAND ${XDG-ICON-RESOURCE_EXECUTABLE} install --novendor --size 128 resources/space.nouspiro.tenmon_128.png space.nouspiro.tenmon WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
|
||||||
|
|||||||
@@ -364,7 +364,7 @@ void LoadRunable::run()
|
|||||||
|
|
||||||
std::shared_ptr<RawImage> rawImage;
|
std::shared_ptr<RawImage> rawImage;
|
||||||
timer.start();
|
timer.start();
|
||||||
if(m_file.endsWith(".CR2", Qt::CaseInsensitive) || m_file.endsWith(".NEF", Qt::CaseInsensitive) || m_file.endsWith(".DNG", Qt::CaseInsensitive))
|
if(m_file.endsWith(".CR2", Qt::CaseInsensitive) || m_file.endsWith(".CR3", Qt::CaseInsensitive) || m_file.endsWith(".NEF", Qt::CaseInsensitive) || m_file.endsWith(".DNG", Qt::CaseInsensitive))
|
||||||
{
|
{
|
||||||
loadRAW(m_file, info, rawImage);
|
loadRAW(m_file, info, rawImage);
|
||||||
qDebug() << "LoadRAW" << timer.elapsed();
|
qDebug() << "LoadRAW" << timer.elapsed();
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <QProgressDialog>
|
#include <QProgressDialog>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QDockWidget>
|
#include <QDockWidget>
|
||||||
|
#include <QActionGroup>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
@@ -24,6 +25,7 @@
|
|||||||
#include "statusbar.h"
|
#include "statusbar.h"
|
||||||
#include "settingsdialog.h"
|
#include "settingsdialog.h"
|
||||||
#include "histogram.h"
|
#include "histogram.h"
|
||||||
|
#include "batchprocessing.h"
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
@@ -57,9 +59,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
|||||||
_openFilter.append(" ");
|
_openFilter.append(" ");
|
||||||
nameFilter.append(mimeType.suffixes());
|
nameFilter.append(mimeType.suffixes());
|
||||||
}
|
}
|
||||||
_openFilter.append("*.fit *.fits *.xisf *.cr2 *.nef *.dng)");
|
_openFilter.append("*.fit *.fits *.xisf *.cr2 *.cr3 *.nef *.dng)");
|
||||||
_openFilter.append(tr(";;All files (*)"));
|
_openFilter.append(tr(";;All files (*)"));
|
||||||
nameFilter.append({"fit", "fits", "xisf", "cr2", "nef", "dng"});
|
nameFilter.append({"fit", "fits", "xisf", "cr2", "cr3", "nef", "dng"});
|
||||||
|
|
||||||
m_info = new ImageInfo(this);
|
m_info = new ImageInfo(this);
|
||||||
QDockWidget *infoDock = new QDockWidget(tr("Image info"), this);
|
QDockWidget *infoDock = new QDockWidget(tr("Image info"), this);
|
||||||
@@ -150,6 +152,11 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
|||||||
fileMenu->addAction(tr("Index directory"), this, SLOT(indexDir()));
|
fileMenu->addAction(tr("Index directory"), this, SLOT(indexDir()));
|
||||||
fileMenu->addAction(tr("Reindex files"), this, SLOT(reindex()));
|
fileMenu->addAction(tr("Reindex files"), this, SLOT(reindex()));
|
||||||
fileMenu->addAction(tr("Export database to CSV"), this, &MainWindow::exportCSV);
|
fileMenu->addAction(tr("Export database to CSV"), this, &MainWindow::exportCSV);
|
||||||
|
/*fileMenu->addAction(tr("Batch processing"), [this](){
|
||||||
|
BatchProcessing *batchProcessing = new BatchProcessing(this);
|
||||||
|
batchProcessing->exec();
|
||||||
|
delete batchProcessing;
|
||||||
|
}, Qt::Key_B | Qt::CTRL);*/
|
||||||
fileMenu->addSeparator();
|
fileMenu->addSeparator();
|
||||||
QAction *liveModeAction = fileMenu->addAction(tr("Live mode"), this, SLOT(liveMode(bool)));
|
QAction *liveModeAction = fileMenu->addAction(tr("Live mode"), this, SLOT(liveMode(bool)));
|
||||||
liveModeAction->setCheckable(true);
|
liveModeAction->setCheckable(true);
|
||||||
@@ -166,6 +173,26 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
|||||||
viewMenu->addAction(tr("Zoom Out"), m_imageGL, SLOT(zoomOut()), QKeySequence::ZoomOut);
|
viewMenu->addAction(tr("Zoom Out"), m_imageGL, SLOT(zoomOut()), QKeySequence::ZoomOut);
|
||||||
viewMenu->addAction(tr("Best Fit"), m_imageGL, SLOT(bestFit()), QKeySequence("Ctrl+1"));
|
viewMenu->addAction(tr("Best Fit"), m_imageGL, SLOT(bestFit()), QKeySequence("Ctrl+1"));
|
||||||
viewMenu->addAction(tr("100%"), m_imageGL, SLOT(oneToOne()));
|
viewMenu->addAction(tr("100%"), m_imageGL, SLOT(oneToOne()));
|
||||||
|
viewMenu->addSeparator();
|
||||||
|
QMenu *bayerMenu = viewMenu->addMenu(tr("Bayer mask"));
|
||||||
|
QActionGroup *bayerActionGroup = new QActionGroup(this);
|
||||||
|
QAction *rggbAction = bayerActionGroup->addAction(tr("RGGB"));//0 0
|
||||||
|
QAction *grbgAction = bayerActionGroup->addAction(tr("GRBG"));//1 0
|
||||||
|
QAction *gbrgAction = bayerActionGroup->addAction(tr("GBRG"));//0 1
|
||||||
|
QAction *bggrAction = bayerActionGroup->addAction(tr("BGGR"));//1 1
|
||||||
|
rggbAction->setCheckable(true); rggbAction->setData(0);
|
||||||
|
grbgAction->setCheckable(true); grbgAction->setData(1);
|
||||||
|
gbrgAction->setCheckable(true); gbrgAction->setData(2);
|
||||||
|
bggrAction->setCheckable(true); bggrAction->setData(3);
|
||||||
|
bayerMenu->addActions({rggbAction, grbgAction, gbrgAction, bggrAction});
|
||||||
|
viewMenu->addMenu(bayerMenu);
|
||||||
|
connect(bayerActionGroup, &QActionGroup::triggered, [this](QAction *action){
|
||||||
|
int data = action->data().toInt();
|
||||||
|
m_imageGL->imageWidget()->setBayerMask(data);
|
||||||
|
QSettings settings;
|
||||||
|
settings.setValue("mainwindow/bayermask", data);
|
||||||
|
});
|
||||||
|
|
||||||
QAction *thumbnailsAction = viewMenu->addAction(tr("Thumbnails"), [this](bool checked){
|
QAction *thumbnailsAction = viewMenu->addAction(tr("Thumbnails"), [this](bool checked){
|
||||||
if(SettingsDialog::loadThumbsizes())m_ringList->clearThumbnails();
|
if(SettingsDialog::loadThumbsizes())m_ringList->clearThumbnails();
|
||||||
m_imageGL->imageWidget()->allocateThumbnails(m_ringList->imageNames());
|
m_imageGL->imageWidget()->allocateThumbnails(m_ringList->imageNames());
|
||||||
@@ -174,6 +201,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
|||||||
else m_ringList->stopLoading();
|
else m_ringList->stopLoading();
|
||||||
}, Qt::Key_F2);
|
}, Qt::Key_F2);
|
||||||
thumbnailsAction->setCheckable(true);
|
thumbnailsAction->setCheckable(true);
|
||||||
|
QAction *slideshowAction = viewMenu->addAction(tr("Slideshow"), m_ringList, &ImageRingList::toggleSlideshow, Qt::Key_F3);
|
||||||
|
slideshowAction->setCheckable(true);
|
||||||
menuBar()->addMenu(viewMenu);
|
menuBar()->addMenu(viewMenu);
|
||||||
|
|
||||||
QMenu *selectMenu = new QMenu(tr("Select"), this);
|
QMenu *selectMenu = new QMenu(tr("Select"), this);
|
||||||
@@ -228,6 +257,19 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
|||||||
QSettings settings;
|
QSettings settings;
|
||||||
restoreGeometry(settings.value("mainwindow/geometry").toByteArray());
|
restoreGeometry(settings.value("mainwindow/geometry").toByteArray());
|
||||||
restoreState(settings.value("mainwindow/state").toByteArray());
|
restoreState(settings.value("mainwindow/state").toByteArray());
|
||||||
|
int bayermask = settings.value("mainwindow/bayermask", 0).toInt();
|
||||||
|
switch(bayermask)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case 0:
|
||||||
|
rggbAction->setChecked(true); break;
|
||||||
|
case 1:
|
||||||
|
grbgAction->setChecked(true); break;
|
||||||
|
case 2:
|
||||||
|
gbrgAction->setChecked(true); break;
|
||||||
|
case 3:
|
||||||
|
bggrAction->setChecked(true); break;
|
||||||
|
}
|
||||||
|
|
||||||
QStringList standardLocations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation);
|
QStringList standardLocations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation);
|
||||||
if(standardLocations.size())
|
if(standardLocations.size())
|
||||||
|
|||||||
@@ -567,6 +567,23 @@ void RawImage::downscaleTo(uint32_t size)
|
|||||||
}*/
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<float, float> RawImage::unitScale() const
|
||||||
|
{
|
||||||
|
float min = *std::min_element(m_stats.m_min, m_stats.m_min + 4);
|
||||||
|
float max = *std::max_element(m_stats.m_max, m_stats.m_max + 4);
|
||||||
|
|
||||||
|
if(m_origType == UINT32)
|
||||||
|
{
|
||||||
|
min /= (float)UINT32_MAX;
|
||||||
|
max /= (float)UINT32_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(min < 0.0f || max > 1.0f)
|
||||||
|
return {1.0f / (max - min), min / (max - min)};
|
||||||
|
else
|
||||||
|
return {1.0f, 0.0f};
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<RawImage> RawImage::fromPlanar(const RawImage &img)
|
std::shared_ptr<RawImage> RawImage::fromPlanar(const RawImage &img)
|
||||||
{
|
{
|
||||||
return RawImage::fromPlanar(img.data(), img.width(), img.height(), img.channels(), img.type());
|
return RawImage::fromPlanar(img.data(), img.width(), img.height(), img.channels(), img.type());
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ public:
|
|||||||
float thumbAspect() const;
|
float thumbAspect() const;
|
||||||
bool pixel(int x, int y, double &r, double &g, double &b) const;
|
bool pixel(int x, int y, double &r, double &g, double &b) const;
|
||||||
void downscaleTo(uint32_t size);
|
void downscaleTo(uint32_t size);
|
||||||
|
std::pair<float, float> unitScale() const;
|
||||||
|
|
||||||
static std::shared_ptr<RawImage> fromPlanar(const RawImage &img);
|
static std::shared_ptr<RawImage> fromPlanar(const RawImage &img);
|
||||||
static std::shared_ptr<RawImage> fromPlanar(const void *pixels, uint32_t w, uint32_t h, uint32_t ch, DataType type);
|
static std::shared_ptr<RawImage> fromPlanar(const void *pixels, uint32_t w, uint32_t h, uint32_t ch, DataType type);
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
<RCC>
|
|
||||||
<qresource prefix="/">
|
|
||||||
<file>invert.png</file>
|
|
||||||
<file>nuke.png</file>
|
|
||||||
<file>bayer.png</file>
|
|
||||||
<file>space.nouspiro.tenmon.png</file>
|
|
||||||
<file>nuke_a.png</file>
|
|
||||||
<file>about/tenmon</file>
|
|
||||||
<file>translations/tenmon_en.qm</file>
|
|
||||||
<file>translations/tenmon_sk.qm</file>
|
|
||||||
<file>about/filter.png</file>
|
|
||||||
<file>about/stretch-panel.png</file>
|
|
||||||
<file>translations/tenmon_fr.qm</file>
|
|
||||||
<file>shaders/image.frag</file>
|
|
||||||
<file>shaders/image.vert</file>
|
|
||||||
<file>shaders/thumb.frag</file>
|
|
||||||
<file>shaders/thumb.vert</file>
|
|
||||||
<file>shaders/debayer.frag</file>
|
|
||||||
<file>shaders/debayer.vert</file>
|
|
||||||
<file>falsecolor.png</file>
|
|
||||||
<file>link.png</file>
|
|
||||||
</qresource>
|
|
||||||
<qresource prefix="/" lang="en">
|
|
||||||
<file alias="help">about/help_en</file>
|
|
||||||
</qresource>
|
|
||||||
<qresource prefix="/" lang="sk">
|
|
||||||
<file alias="help">about/help_sk</file>
|
|
||||||
</qresource>
|
|
||||||
<qresource prefix="/" lang="fr">
|
|
||||||
<file alias="help">about/help_fr</file>
|
|
||||||
</qresource>
|
|
||||||
</RCC>
|
|
||||||
|
Before Width: | Height: | Size: 380 B After Width: | Height: | Size: 380 B |
|
Before Width: | Height: | Size: 947 B After Width: | Height: | Size: 947 B |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 454 B After Width: | Height: | Size: 454 B |
@@ -0,0 +1,26 @@
|
|||||||
|
<RCC>
|
||||||
|
<qresource prefix="/">
|
||||||
|
<file>invert.png</file>
|
||||||
|
<file>nuke.png</file>
|
||||||
|
<file>bayer.png</file>
|
||||||
|
<file>space.nouspiro.tenmon.png</file>
|
||||||
|
<file>nuke_a.png</file>
|
||||||
|
<file>../about/tenmon</file>
|
||||||
|
<file>../translations/tenmon_en.qm</file>
|
||||||
|
<file>../translations/tenmon_sk.qm</file>
|
||||||
|
<file>../about/filter.png</file>
|
||||||
|
<file>../about/stretch-panel.png</file>
|
||||||
|
<file>../translations/tenmon_fr.qm</file>
|
||||||
|
<file>falsecolor.png</file>
|
||||||
|
<file>link.png</file>
|
||||||
|
</qresource>
|
||||||
|
<qresource lang="en" prefix="/">
|
||||||
|
<file alias="help">../about/help_en</file>
|
||||||
|
</qresource>
|
||||||
|
<qresource lang="sk" prefix="/">
|
||||||
|
<file alias="help">../about/help_sk</file>
|
||||||
|
</qresource>
|
||||||
|
<qresource lang="fr" prefix="/">
|
||||||
|
<file alias="help">../about/help_fr</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
||||||
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
@@ -0,0 +1,263 @@
|
|||||||
|
#include "scriptengine.h"
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QDebug>
|
||||||
|
#include "loadrunable.h"
|
||||||
|
|
||||||
|
namespace Script
|
||||||
|
{
|
||||||
|
|
||||||
|
ScriptEngine::ScriptEngine(QObject *parent) : QObject(parent)
|
||||||
|
, _jsEngine(new QJSEngine(this))
|
||||||
|
, _database(new Database(this))
|
||||||
|
{
|
||||||
|
QJSValue engine = _jsEngine->newQObject(this);
|
||||||
|
_jsEngine->globalObject().setProperty("engine", engine);
|
||||||
|
_database->init(QLatin1String("scriptengine"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptEngine::setParams(const QString &scriptPath, const QStringList &paths, const QString &outputDir)
|
||||||
|
{
|
||||||
|
_scriptPath = scriptPath;
|
||||||
|
_paths = paths;
|
||||||
|
_outputDir = outputDir + "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptEngine::reportError(const QString &message)
|
||||||
|
{
|
||||||
|
_jsEngine->throwError(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &ScriptEngine::outputDir() const
|
||||||
|
{
|
||||||
|
return _outputDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptEngine::interrupt()
|
||||||
|
{
|
||||||
|
_jsEngine->setInterrupted(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptEngine::logError(const QString &message)
|
||||||
|
{
|
||||||
|
emit newMessage(message, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptEngine::log(const QString &message)
|
||||||
|
{
|
||||||
|
emit newMessage(message, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptEngine::mark(File *file)
|
||||||
|
{
|
||||||
|
_database->mark(file->absoluteFilePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptEngine::unmark(File *file)
|
||||||
|
{
|
||||||
|
_database->unmark(file->absoluteFilePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScriptEngine::isMarked(const File *file) const
|
||||||
|
{
|
||||||
|
return _database->isMarked(file->absoluteFilePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptEngine::run()
|
||||||
|
{
|
||||||
|
QJSValue jsPaths = _jsEngine->newArray(_paths.size());
|
||||||
|
for(qsizetype i=0; i<_paths.size(); i++)
|
||||||
|
jsPaths.setProperty(i, _jsEngine->newQObject(new File(_paths[i], this)));
|
||||||
|
|
||||||
|
_jsEngine->globalObject().setProperty("files", jsPaths);
|
||||||
|
|
||||||
|
QFile scriptFile(_scriptPath);
|
||||||
|
if(!scriptFile.open(QIODevice::ReadOnly))
|
||||||
|
return;
|
||||||
|
|
||||||
|
QTextStream stream(&scriptFile);
|
||||||
|
QString contents = stream.readAll();
|
||||||
|
scriptFile.close();
|
||||||
|
QJSValue result = _jsEngine->evaluate(contents, _scriptPath);
|
||||||
|
qDebug() << result.isError() << result.toString();
|
||||||
|
if(result.isError())
|
||||||
|
{
|
||||||
|
QString error = result.property("name").toString() + " on line " + result.property("lineNumber").toString() + " : " + result.toString();
|
||||||
|
error += "\n" + result.property("stack").toString();
|
||||||
|
emit newMessage(error, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit finished();
|
||||||
|
}
|
||||||
|
|
||||||
|
void File::loadFitsKeywords()
|
||||||
|
{
|
||||||
|
if(!_fitsKeywordsLoaded)
|
||||||
|
{
|
||||||
|
_fitsKeywordsLoaded = true;
|
||||||
|
ImageInfoData info;
|
||||||
|
if(suffix() == "xisf")
|
||||||
|
{
|
||||||
|
readXISFHeader(_path, info);
|
||||||
|
}
|
||||||
|
else if(suffix() == "fits")
|
||||||
|
{
|
||||||
|
readFITSHeader(_path, info);
|
||||||
|
}
|
||||||
|
else return;
|
||||||
|
|
||||||
|
for(const FITSRecord &record : info.fitsHeader)
|
||||||
|
{
|
||||||
|
_fitsKeywords[record.key] = record.value.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool File::mkpath(const QString &path) const
|
||||||
|
{
|
||||||
|
QFileInfo info(path);
|
||||||
|
if(!info.isRelative())
|
||||||
|
{
|
||||||
|
_engine->logError("Destination path is not relative");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
QDir dir(_engine->outputDir());
|
||||||
|
if(dir.mkpath(info.path()))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_engine->logError("Failed to create dir " + info.path());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
File::File(const QString &path, Script::ScriptEngine *engine) :
|
||||||
|
_engine(engine),
|
||||||
|
_path(path),
|
||||||
|
_info(path)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QString File::fileName() const
|
||||||
|
{
|
||||||
|
return _info.fileName();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString File::absoluteFilePath() const
|
||||||
|
{
|
||||||
|
return _info.absoluteFilePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString File::absolutePath() const
|
||||||
|
{
|
||||||
|
return _info.absolutePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString File::baseName() const
|
||||||
|
{
|
||||||
|
return _info.baseName();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString File::completeBaseName() const
|
||||||
|
{
|
||||||
|
return _info.completeBaseName();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString File::suffix() const
|
||||||
|
{
|
||||||
|
return _info.suffix();
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 File::size() const
|
||||||
|
{
|
||||||
|
return _info.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList File::fitsKeywords()
|
||||||
|
{
|
||||||
|
QThread::msleep(500);
|
||||||
|
loadFitsKeywords();
|
||||||
|
return _fitsKeywords.keys();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString File::fitsValue(const QString &key)
|
||||||
|
{
|
||||||
|
loadFitsKeywords();
|
||||||
|
if(_fitsKeywords.contains(key))
|
||||||
|
return _fitsKeywords[key];
|
||||||
|
else
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool File::isMarked() const
|
||||||
|
{
|
||||||
|
return _engine->isMarked(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool File::copy(const QString &newpath) const
|
||||||
|
{
|
||||||
|
if(mkpath(newpath))
|
||||||
|
{
|
||||||
|
if(QFile::copy(_path, _engine->outputDir() + newpath))
|
||||||
|
return true;
|
||||||
|
_engine->logError("Failed copy to " + newpath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool File::move(const QString &newpath) const
|
||||||
|
{
|
||||||
|
if(mkpath(newpath))
|
||||||
|
{
|
||||||
|
if(QFile::rename(_path, _engine->outputDir() + newpath))
|
||||||
|
return true;
|
||||||
|
_engine->logError("Failed move to " + newpath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool File::convertTo(const QString &format)
|
||||||
|
{
|
||||||
|
_engine->reportError("Not implemented");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptEngineThread::ScriptEngineThread(QObject *parent) : QObject(parent)
|
||||||
|
{
|
||||||
|
_thread = new QThread();
|
||||||
|
_thread->setObjectName("ScriptEngine");
|
||||||
|
_engine = new ScriptEngine;
|
||||||
|
_engine->moveToThread(_thread);
|
||||||
|
connect(_engine, &ScriptEngine::finished, _thread, &QThread::quit);
|
||||||
|
connect(_engine, &ScriptEngine::newMessage, this, &ScriptEngineThread::newMessage);
|
||||||
|
connect(_thread, &QThread::started, _engine, &ScriptEngine::run);
|
||||||
|
connect(_thread, &QThread::finished, _engine, &ScriptEngine::deleteLater);
|
||||||
|
connect(_thread, &QThread::finished, _thread, &QThread::deleteLater);
|
||||||
|
connect(_thread, &QThread::finished, this, &ScriptEngineThread::finished);
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptEngineThread::~ScriptEngineThread()
|
||||||
|
{
|
||||||
|
if(_engine)_engine->interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptEngineThread::setParams(const QString &scriptPath, const QStringList &paths, const QString &outputDir)
|
||||||
|
{
|
||||||
|
_engine->setParams(scriptPath, paths, outputDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptEngineThread::start()
|
||||||
|
{
|
||||||
|
_thread->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptEngineThread::interrupt()
|
||||||
|
{
|
||||||
|
if(_engine)_engine->interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
#ifndef SCRIPTENGINE_H
|
||||||
|
#define SCRIPTENGINE_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QJSEngine>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QThread>
|
||||||
|
#include "database.h"
|
||||||
|
|
||||||
|
namespace Script
|
||||||
|
{
|
||||||
|
|
||||||
|
class File;
|
||||||
|
|
||||||
|
class ScriptEngine : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QJSEngine *_jsEngine;
|
||||||
|
Database *_database;
|
||||||
|
QString _scriptPath;
|
||||||
|
QString _outputDir;
|
||||||
|
QStringList _paths;
|
||||||
|
public:
|
||||||
|
explicit ScriptEngine(QObject *parent = nullptr);
|
||||||
|
void setParams(const QString &scriptPath, const QStringList &paths, const QString &outputDir);
|
||||||
|
void reportError(const QString &message);
|
||||||
|
const QString& outputDir() const;
|
||||||
|
void interrupt();
|
||||||
|
void logError(const QString &message);
|
||||||
|
Q_INVOKABLE void log(const QString &message);
|
||||||
|
Q_INVOKABLE void mark(File *file);
|
||||||
|
Q_INVOKABLE void unmark(File *file);
|
||||||
|
Q_INVOKABLE bool isMarked(const File *file) const;
|
||||||
|
public slots:
|
||||||
|
void run();
|
||||||
|
signals:
|
||||||
|
void newMessage(const QString &message, bool error);
|
||||||
|
void finished();
|
||||||
|
};
|
||||||
|
|
||||||
|
class ScriptEngineThread : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QThread *_thread;
|
||||||
|
ScriptEngine *_engine;
|
||||||
|
public:
|
||||||
|
ScriptEngineThread(QObject *parent = nullptr);
|
||||||
|
~ScriptEngineThread();
|
||||||
|
void setParams(const QString &scriptPath, const QStringList &paths, const QString &outputDir);
|
||||||
|
void start();
|
||||||
|
void interrupt();
|
||||||
|
signals:
|
||||||
|
void newMessage(const QString &message, bool error);
|
||||||
|
void finished();
|
||||||
|
};
|
||||||
|
|
||||||
|
class File : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
ScriptEngine *_engine;
|
||||||
|
QString _path;
|
||||||
|
QFileInfo _info;
|
||||||
|
bool _fitsKeywordsLoaded = false;
|
||||||
|
QMap<QString, QString> _fitsKeywords;
|
||||||
|
void loadFitsKeywords();
|
||||||
|
bool mkpath(const QString &path) const;
|
||||||
|
public:
|
||||||
|
explicit File(const QString &path, ScriptEngine *engine);
|
||||||
|
Q_INVOKABLE QString fileName() const;
|
||||||
|
Q_INVOKABLE QString absoluteFilePath() const;
|
||||||
|
Q_INVOKABLE QString absolutePath() const;
|
||||||
|
Q_INVOKABLE QString baseName() const;
|
||||||
|
Q_INVOKABLE QString completeBaseName() const;
|
||||||
|
Q_INVOKABLE QString suffix() const;
|
||||||
|
Q_INVOKABLE qint64 size() const;
|
||||||
|
Q_INVOKABLE QStringList fitsKeywords();
|
||||||
|
Q_INVOKABLE QString fitsValue(const QString &key);
|
||||||
|
Q_INVOKABLE bool isMarked() const;
|
||||||
|
Q_INVOKABLE bool copy(const QString &newpath) const;
|
||||||
|
Q_INVOKABLE bool move(const QString &newpath) const;
|
||||||
|
Q_INVOKABLE bool convertTo(const QString &format);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SCRIPTENGINE_H
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
extern int DEFAULT_WIDTH;
|
extern int DEFAULT_WIDTH;
|
||||||
extern double SATURATION;
|
extern double SATURATION;
|
||||||
|
extern int FILTERING;
|
||||||
|
|
||||||
class EvenNumber : public QSpinBox
|
class EvenNumber : public QSpinBox
|
||||||
{
|
{
|
||||||
@@ -39,7 +40,7 @@ SettingsDialog::SettingsDialog(QWidget *parent) : QDialog(parent)
|
|||||||
QSettings settings;
|
QSettings settings;
|
||||||
|
|
||||||
m_preloadImages = new QSpinBox(this);
|
m_preloadImages = new QSpinBox(this);
|
||||||
m_preloadImages->setRange(0, 8);
|
m_preloadImages->setRange(0, 32);
|
||||||
m_preloadImages->setValue(settings.value("settings/preloadimagecount", DEFAULT_WIDTH).toInt());
|
m_preloadImages->setValue(settings.value("settings/preloadimagecount", DEFAULT_WIDTH).toInt());
|
||||||
m_preloadImages->setToolTip(tr("How many images are preloaded before and after current image."));
|
m_preloadImages->setToolTip(tr("How many images are preloaded before and after current image."));
|
||||||
|
|
||||||
@@ -56,12 +57,25 @@ SettingsDialog::SettingsDialog(QWidget *parent) : QDialog(parent)
|
|||||||
m_saturation->setValue(settings.value("settings/saturation", SATURATION * 100.0).toDouble());
|
m_saturation->setValue(settings.value("settings/saturation", SATURATION * 100.0).toDouble());
|
||||||
m_saturation->setToolTip(tr("Set threshold value that is considered saturated when showing statistics.\nFor RAW files you may set 22%"));
|
m_saturation->setToolTip(tr("Set threshold value that is considered saturated when showing statistics.\nFor RAW files you may set 22%"));
|
||||||
|
|
||||||
|
m_slideShowTime = new QDoubleSpinBox(this);
|
||||||
|
m_slideShowTime->setMinimum(0.01);
|
||||||
|
m_slideShowTime->setMaximum(10);
|
||||||
|
m_slideShowTime->setSuffix(" s");
|
||||||
|
m_slideShowTime->setValue(settings.value("settings/slideshowtime", 1.0).toDouble());
|
||||||
|
m_slideShowTime->setSingleStep(0.1);
|
||||||
|
|
||||||
m_useNativeDialog = new QCheckBox(tr("Don't use native file dialog"), this);
|
m_useNativeDialog = new QCheckBox(tr("Don't use native file dialog"), this);
|
||||||
m_useNativeDialog->setChecked(QApplication::testAttribute(Qt::AA_DontUseNativeDialogs));
|
m_useNativeDialog->setChecked(QApplication::testAttribute(Qt::AA_DontUseNativeDialogs));
|
||||||
|
|
||||||
|
m_filtering = new QComboBox(this);
|
||||||
|
m_filtering->addItems({tr("Nearest"), tr("Linear"), tr("Cubic")});
|
||||||
|
m_filtering->setCurrentIndex(FILTERING);
|
||||||
|
|
||||||
layout->addRow(tr("Image preload count"), m_preloadImages);
|
layout->addRow(tr("Image preload count"), m_preloadImages);
|
||||||
layout->addRow(tr("Thumbnails size"), m_thumSize);
|
layout->addRow(tr("Thumbnails size"), m_thumSize);
|
||||||
layout->addRow(tr("Saturation"), m_saturation);
|
layout->addRow(tr("Saturation"), m_saturation);
|
||||||
|
layout->addRow(tr("Slideshow interval"), m_slideShowTime);
|
||||||
|
layout->addRow(tr("Image filtering"), m_filtering);
|
||||||
layout->addRow(m_useNativeDialog);
|
layout->addRow(m_useNativeDialog);
|
||||||
//layout->addRow(new QLabel(tr("Changes in settings will take effect after program restart.")));
|
//layout->addRow(new QLabel(tr("Changes in settings will take effect after program restart.")));
|
||||||
|
|
||||||
@@ -82,6 +96,7 @@ void SettingsDialog::loadSettings()
|
|||||||
THUMB_SIZE_BORDER_Y = THUMB_SIZE + 30;
|
THUMB_SIZE_BORDER_Y = THUMB_SIZE + 30;
|
||||||
DEFAULT_WIDTH = settings.value("settings/preloadimagecount", DEFAULT_WIDTH).toInt();
|
DEFAULT_WIDTH = settings.value("settings/preloadimagecount", DEFAULT_WIDTH).toInt();
|
||||||
SATURATION = settings.value("settings/saturation", 95.0).toDouble() / 100.0;
|
SATURATION = settings.value("settings/saturation", 95.0).toDouble() / 100.0;
|
||||||
|
FILTERING = settings.value("settings/filtering", FILTERING).toInt();
|
||||||
QApplication::setAttribute(Qt::AA_DontUseNativeDialogs, settings.value("settings/dontusenativedialogs", false).toBool());
|
QApplication::setAttribute(Qt::AA_DontUseNativeDialogs, settings.value("settings/dontusenativedialogs", false).toBool());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,6 +117,9 @@ void SettingsDialog::saveSettings()
|
|||||||
settings.setValue("settings/preloadimagecount", m_preloadImages->value());
|
settings.setValue("settings/preloadimagecount", m_preloadImages->value());
|
||||||
settings.setValue("settings/dontusenativedialogs", m_useNativeDialog->isChecked());
|
settings.setValue("settings/dontusenativedialogs", m_useNativeDialog->isChecked());
|
||||||
settings.setValue("settings/saturation", m_saturation->value());
|
settings.setValue("settings/saturation", m_saturation->value());
|
||||||
|
settings.setValue("settings/slideshowtime", m_slideShowTime->value());
|
||||||
|
FILTERING = m_filtering->currentIndex();
|
||||||
|
settings.setValue("settings/filtering", FILTERING);
|
||||||
SATURATION = m_saturation->value() / 100.0;
|
SATURATION = m_saturation->value() / 100.0;
|
||||||
QApplication::setAttribute(Qt::AA_DontUseNativeDialogs, m_useNativeDialog->isChecked());
|
QApplication::setAttribute(Qt::AA_DontUseNativeDialogs, m_useNativeDialog->isChecked());
|
||||||
if(DEFAULT_WIDTH != m_preloadImages->value())
|
if(DEFAULT_WIDTH != m_preloadImages->value())
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include <QSpinBox>
|
#include <QSpinBox>
|
||||||
#include <QCheckBox>
|
#include <QCheckBox>
|
||||||
|
#include <QComboBox>
|
||||||
|
|
||||||
class SettingsDialog : public QDialog
|
class SettingsDialog : public QDialog
|
||||||
{
|
{
|
||||||
@@ -19,8 +20,10 @@ private:
|
|||||||
|
|
||||||
QSpinBox *m_preloadImages;
|
QSpinBox *m_preloadImages;
|
||||||
QSpinBox *m_thumSize;
|
QSpinBox *m_thumSize;
|
||||||
|
QDoubleSpinBox *m_slideShowTime;
|
||||||
QDoubleSpinBox *m_saturation;
|
QDoubleSpinBox *m_saturation;
|
||||||
QCheckBox *m_useNativeDialog;
|
QCheckBox *m_useNativeDialog;
|
||||||
|
QComboBox *m_filtering;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SETTINGSDIALOG_H
|
#endif // SETTINGSDIALOG_H
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#version 330
|
#version 330
|
||||||
|
|
||||||
uniform sampler2D qt_Texture0;
|
uniform sampler2D qt_Texture0;
|
||||||
|
uniform ivec2 firstRed;
|
||||||
in vec2 qt_TexCoord0;
|
in vec2 qt_TexCoord0;
|
||||||
in vec2 center;
|
in vec2 center;
|
||||||
layout(location = 0) out vec4 color;
|
layout(location = 0) out vec4 color;
|
||||||
@@ -11,7 +12,7 @@ void main(void)
|
|||||||
{
|
{
|
||||||
ivec2 texSize = textureSize(qt_Texture0, 0);
|
ivec2 texSize = textureSize(qt_Texture0, 0);
|
||||||
ivec2 icenter = ivec2(center);
|
ivec2 icenter = ivec2(center);
|
||||||
ivec2 alternate = icenter % 2;
|
ivec2 alternate = (icenter + firstRed) % 2;
|
||||||
|
|
||||||
// cross, checker, theta, phi
|
// cross, checker, theta, phi
|
||||||
const vec4 kA = vec4(-1.0, -1.5, 0.5, -1.0) / 8.0;
|
const vec4 kA = vec4(-1.0, -1.5, 0.5, -1.0) / 8.0;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ uniform bool bw;
|
|||||||
uniform bool invert;
|
uniform bool invert;
|
||||||
uniform bool srgb;
|
uniform bool srgb;
|
||||||
uniform bool false_color;
|
uniform bool false_color;
|
||||||
|
uniform int filtering;
|
||||||
in vec2 qt_TexCoord0;
|
in vec2 qt_TexCoord0;
|
||||||
layout(location = 0) out vec4 color;
|
layout(location = 0) out vec4 color;
|
||||||
|
|
||||||
@@ -35,7 +36,8 @@ vec3 falsecolor(float color)
|
|||||||
vec3(1.0, 0.0, 0.0), vec3(1.0, 0.0, 0.0));//red
|
vec3(1.0, 0.0, 0.0), vec3(1.0, 0.0, 0.0));//red
|
||||||
color *= 5.0;
|
color *= 5.0;
|
||||||
int i = int(color);
|
int i = int(color);
|
||||||
return mix(pallete[i], pallete[i+1], fract(color));
|
float f = fract(color);
|
||||||
|
return mix(pallete[i], pallete[i+1], f);// * (f * 0.5 + 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 checker()
|
vec3 checker()
|
||||||
@@ -44,9 +46,104 @@ vec3 checker()
|
|||||||
return vec3(step(pattern.x * pattern.y, 0.0) * 0.25 + 0.25);
|
return vec3(step(pattern.x * pattern.y, 0.0) * 0.25 + 0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vec4 cubic(float v)
|
||||||
|
{
|
||||||
|
vec4 n = vec4(1.0, 2.0, 3.0, 4.0) - v;
|
||||||
|
vec4 s = n * n * n;
|
||||||
|
float x = s.x;
|
||||||
|
float y = s.y - 4.0 * s.x;
|
||||||
|
float z = s.z - 4.0 * s.y + 6.0 * s.x;
|
||||||
|
float w = 6.0 - x - y - z;
|
||||||
|
return vec4(x, y, z, w) * (1.0/6.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 textureBicubic(sampler2D sampler, vec2 texCoords)
|
||||||
|
{
|
||||||
|
vec2 texSize = textureSize(sampler, 0);
|
||||||
|
vec2 invTexSize = 1.0 / texSize;
|
||||||
|
|
||||||
|
texCoords = texCoords * texSize - 0.5;
|
||||||
|
|
||||||
|
vec2 fxy = fract(texCoords);
|
||||||
|
texCoords -= fxy;
|
||||||
|
|
||||||
|
vec4 xcubic = cubic(fxy.x);
|
||||||
|
vec4 ycubic = cubic(fxy.y);
|
||||||
|
|
||||||
|
vec4 c = texCoords.xxyy + vec2 (-0.5, +1.5).xyxy;
|
||||||
|
|
||||||
|
vec4 s = vec4(xcubic.xz + xcubic.yw, ycubic.xz + ycubic.yw);
|
||||||
|
vec4 offset = c + vec4 (xcubic.yw, ycubic.yw) / s;
|
||||||
|
|
||||||
|
offset *= invTexSize.xxyy;
|
||||||
|
|
||||||
|
vec4 sample0 = texture(sampler, offset.xz);
|
||||||
|
vec4 sample1 = texture(sampler, offset.yz);
|
||||||
|
vec4 sample2 = texture(sampler, offset.xw);
|
||||||
|
vec4 sample3 = texture(sampler, offset.yw);
|
||||||
|
|
||||||
|
float sx = s.x / (s.x + s.y);
|
||||||
|
float sy = s.z / (s.z + s.w);
|
||||||
|
|
||||||
|
return mix(mix(sample3, sample2, sx), mix(sample1, sample0, sx), sy);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 textureCatmul(sampler2D sampler, vec2 texCoords)
|
||||||
|
{
|
||||||
|
ivec2 texSize = textureSize(sampler, 0);
|
||||||
|
|
||||||
|
texCoords = texCoords * vec2(texSize) - 0.5;
|
||||||
|
|
||||||
|
ivec2 texel = ivec2(floor(texCoords));
|
||||||
|
vec2 fra = fract(texCoords);
|
||||||
|
texSize -= 1;
|
||||||
|
|
||||||
|
const mat4 CatMul = mat4(0, 1, 0, 0, -0.5, 0, 0.5, 0, 1, -2.5, 2, -0.5, -0.5, 1.5, -1.5, 0.5);
|
||||||
|
vec4 xx = CatMul * vec4(1.0, fra.x, fra.x*fra.x, fra.x*fra.x*fra.x);
|
||||||
|
vec4 yy = CatMul * vec4(1.0, fra.y, fra.y*fra.y, fra.y*fra.y*fra.y);
|
||||||
|
|
||||||
|
vec4 a00 = texelFetch(sampler, clamp(texel + ivec2(-1, -1), ivec2(0, 0), texSize), 0) * xx.x;
|
||||||
|
vec4 a01 = texelFetch(sampler, clamp(texel + ivec2( 0, -1), ivec2(0, 0), texSize), 0) * xx.y;
|
||||||
|
vec4 a02 = texelFetch(sampler, clamp(texel + ivec2( 1, -1), ivec2(0, 0), texSize), 0) * xx.z;
|
||||||
|
vec4 a03 = texelFetch(sampler, clamp(texel + ivec2( 2, -1), ivec2(0, 0), texSize), 0) * xx.w;
|
||||||
|
vec4 a10 = texelFetch(sampler, clamp(texel + ivec2(-1, 0), ivec2(0, 0), texSize), 0) * xx.x;
|
||||||
|
vec4 a11 = texelFetch(sampler, clamp(texel + ivec2( 0, 0), ivec2(0, 0), texSize), 0) * xx.y;
|
||||||
|
vec4 a12 = texelFetch(sampler, clamp(texel + ivec2( 1, 0), ivec2(0, 0), texSize), 0) * xx.z;
|
||||||
|
vec4 a13 = texelFetch(sampler, clamp(texel + ivec2( 2, 0), ivec2(0, 0), texSize), 0) * xx.w;
|
||||||
|
vec4 a20 = texelFetch(sampler, clamp(texel + ivec2(-1, 1), ivec2(0, 0), texSize), 0) * xx.x;
|
||||||
|
vec4 a21 = texelFetch(sampler, clamp(texel + ivec2( 0, 1), ivec2(0, 0), texSize), 0) * xx.y;
|
||||||
|
vec4 a22 = texelFetch(sampler, clamp(texel + ivec2( 1, 1), ivec2(0, 0), texSize), 0) * xx.z;
|
||||||
|
vec4 a23 = texelFetch(sampler, clamp(texel + ivec2( 2, 1), ivec2(0, 0), texSize), 0) * xx.w;
|
||||||
|
vec4 a30 = texelFetch(sampler, clamp(texel + ivec2(-1, 2), ivec2(0, 0), texSize), 0) * xx.x;
|
||||||
|
vec4 a31 = texelFetch(sampler, clamp(texel + ivec2( 0, 2), ivec2(0, 0), texSize), 0) * xx.y;
|
||||||
|
vec4 a32 = texelFetch(sampler, clamp(texel + ivec2( 1, 2), ivec2(0, 0), texSize), 0) * xx.z;
|
||||||
|
vec4 a33 = texelFetch(sampler, clamp(texel + ivec2( 2, 2), ivec2(0, 0), texSize), 0) * xx.w;
|
||||||
|
|
||||||
|
vec4 c = vec4(0.0);
|
||||||
|
c += (a00 + a01 + a02 + a03) * yy.x;
|
||||||
|
c += (a10 + a11 + a12 + a13) * yy.y;
|
||||||
|
c += (a20 + a21 + a22 + a23) * yy.z;
|
||||||
|
c += (a30 + a31 + a32 + a33) * yy.w;
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
void main(void)
|
void main(void)
|
||||||
{
|
{
|
||||||
|
switch(filtering)
|
||||||
|
{
|
||||||
|
case 0://nearest
|
||||||
|
color = texelFetch(qt_Texture0, ivec2(qt_TexCoord0 * textureSize(qt_Texture0, 0)), 0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
case 1://bilinear
|
||||||
color = texture(qt_Texture0, qt_TexCoord0);
|
color = texture(qt_Texture0, qt_TexCoord0);
|
||||||
|
break;
|
||||||
|
case 2://catmul bicubic
|
||||||
|
color = textureCatmul(qt_Texture0, qt_TexCoord0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
color.rgb = color.rgb * unit_scale.x + unit_scale.y;
|
color.rgb = color.rgb * unit_scale.x + unit_scale.y;
|
||||||
if(bw)color = color.rrra;
|
if(bw)color = color.rrra;
|
||||||
color = MTF(color, vec4(mtf_param[0], 0.0), vec4(mtf_param[1], 0.5), vec4(mtf_param[2], 1.0));
|
color = MTF(color, vec4(mtf_param[0], 0.0), vec4(mtf_param[1], 0.5), vec4(mtf_param[2], 1.0));
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<RCC>
|
||||||
|
<qresource prefix="/">
|
||||||
|
<file>debayer.frag</file>
|
||||||
|
<file>debayer.vert</file>
|
||||||
|
<file>image.frag</file>
|
||||||
|
<file>image.vert</file>
|
||||||
|
<file>thumb.frag</file>
|
||||||
|
<file>thumb.vert</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
||||||
@@ -49,6 +49,16 @@
|
|||||||
</screenshots>
|
</screenshots>
|
||||||
<content_rating type="oars-1.1"/>
|
<content_rating type="oars-1.1"/>
|
||||||
<releases>
|
<releases>
|
||||||
|
<release version="20240108" date="2023-01-08">
|
||||||
|
<description>
|
||||||
|
<ul>
|
||||||
|
<li>Update to Qt6</li>
|
||||||
|
<li>Add support for CR3 RAW files</li>
|
||||||
|
<li>Slideshow</li>
|
||||||
|
<li>Improved rapid image view</li>
|
||||||
|
</ul>
|
||||||
|
</description>
|
||||||
|
</release>
|
||||||
<release version="20231116" date="2023-11-16">
|
<release version="20231116" date="2023-11-16">
|
||||||
<description>
|
<description>
|
||||||
<ul>
|
<ul>
|
||||||
|
|||||||
@@ -105,43 +105,44 @@ void STFSlider::paintEvent(QPaintEvent *event)
|
|||||||
|
|
||||||
void STFSlider::mouseMoveEvent(QMouseEvent *event)
|
void STFSlider::mouseMoveEvent(QMouseEvent *event)
|
||||||
{
|
{
|
||||||
if(std::abs(m_blackPoint*width() - event->x()) < 5 ||
|
const qreal x = event->position().x();
|
||||||
std::abs((m_blackPoint + (m_whitePoint - m_blackPoint) * m_midPoint)*width() - event->x()) < 5 ||
|
if(std::abs(m_blackPoint*width() - x) < 5 ||
|
||||||
std::abs(m_whitePoint*width() - event->x()) < 5)
|
std::abs((m_blackPoint + (m_whitePoint - m_blackPoint) * m_midPoint)*width() - x) < 5 ||
|
||||||
|
std::abs(m_whitePoint*width() - x) < 5)
|
||||||
setCursor(Qt::SplitHCursor);
|
setCursor(Qt::SplitHCursor);
|
||||||
else
|
else
|
||||||
unsetCursor();
|
unsetCursor();
|
||||||
|
|
||||||
qreal x = (qreal)event->x()/width();
|
qreal xw = x/width();
|
||||||
if(event->modifiers() & Qt::ShiftModifier && !m_fineTune)
|
if(event->modifiers() & Qt::ShiftModifier && !m_fineTune)
|
||||||
{
|
{
|
||||||
m_fineTune = true;
|
m_fineTune = true;
|
||||||
m_fineTuneX = x;
|
m_fineTuneX = xw;
|
||||||
}
|
}
|
||||||
if(!(event->modifiers() & Qt::ShiftModifier) && m_fineTune)
|
if(!(event->modifiers() & Qt::ShiftModifier) && m_fineTune)
|
||||||
m_fineTune = false;
|
m_fineTune = false;
|
||||||
|
|
||||||
if(m_fineTune)
|
if(m_fineTune)
|
||||||
{
|
{
|
||||||
x = m_fineTuneX + (x - m_fineTuneX) * 0.2;
|
xw = m_fineTuneX + (xw - m_fineTuneX) * 0.2;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(m_grabbed)
|
switch(m_grabbed)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
m_blackPoint = clamp(x);
|
m_blackPoint = clamp(xw);
|
||||||
m_whitePoint = std::max(m_whitePoint, m_blackPoint);
|
m_whitePoint = std::max(m_whitePoint, m_blackPoint);
|
||||||
QToolTip::showText(event->globalPos(), QString::number(m_blackPoint), this);
|
QToolTip::showText(event->globalPosition().toPoint(), QString::number(m_blackPoint), this);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
m_midPoint = (x - m_blackPoint) / (m_whitePoint - m_blackPoint);
|
m_midPoint = (xw - m_blackPoint) / (m_whitePoint - m_blackPoint);
|
||||||
m_midPoint = clamp(m_midPoint);
|
m_midPoint = clamp(m_midPoint);
|
||||||
QToolTip::showText(event->globalPos(), QString::number(m_midPoint), this);
|
QToolTip::showText(event->globalPosition().toPoint(), QString::number(m_midPoint), this);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
m_whitePoint = clamp(x);
|
m_whitePoint = clamp(xw);
|
||||||
m_blackPoint = std::min(m_blackPoint, m_whitePoint);
|
m_blackPoint = std::min(m_blackPoint, m_whitePoint);
|
||||||
QToolTip::showText(event->globalPos(), QString::number(m_whitePoint), this);
|
QToolTip::showText(event->globalPosition().toPoint(), QString::number(m_whitePoint), this);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(m_grabbed >= 0)
|
if(m_grabbed >= 0)
|
||||||
@@ -153,17 +154,18 @@ void STFSlider::mouseMoveEvent(QMouseEvent *event)
|
|||||||
|
|
||||||
void STFSlider::mousePressEvent(QMouseEvent *event)
|
void STFSlider::mousePressEvent(QMouseEvent *event)
|
||||||
{
|
{
|
||||||
|
const qreal x = event->position().x();
|
||||||
if(event->modifiers() & Qt::ShiftModifier)
|
if(event->modifiers() & Qt::ShiftModifier)
|
||||||
{
|
{
|
||||||
m_fineTune = true;
|
m_fineTune = true;
|
||||||
m_fineTuneX = (qreal)event->x()/width();
|
m_fineTuneX = x/width();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(std::abs((m_blackPoint + (m_whitePoint - m_blackPoint) * m_midPoint)*width() - event->x()) < 5)
|
if(std::abs((m_blackPoint + (m_whitePoint - m_blackPoint) * m_midPoint)*width() - x) < 5)
|
||||||
m_grabbed = 1;
|
m_grabbed = 1;
|
||||||
else if(std::abs(m_blackPoint*width() - event->x()) < 5)
|
else if(std::abs(m_blackPoint*width() - x) < 5)
|
||||||
m_grabbed = 0;
|
m_grabbed = 0;
|
||||||
else if(std::abs(m_whitePoint*width() - event->x()) < 5)
|
else if(std::abs(m_whitePoint*width() - x) < 5)
|
||||||
m_grabbed = 2;
|
m_grabbed = 2;
|
||||||
else
|
else
|
||||||
m_grabbed = -1;
|
m_grabbed = -1;
|
||||||
|
|||||||