Compare commits

..

46 Commits

Author SHA1 Message Date
nou 71fc1f2bd1 Release 20221215 2022-12-15 20:54:39 +01:00
nou f1ff04382b Add option to not use native file dialogs
Thanks to Patrick Chevalley for updated french tranlation
2022-12-15 20:36:19 +01:00
nou ad91adf1d9 Implement workaround for flatpak QFile::moveToTrash 2022-12-15 17:26:16 +01:00
nou b7369c2501 Remove unused variables in debayer shader 2022-12-15 00:25:00 +01:00
nou 79ed6b2059 Fix error in slovak translation 2022-12-15 00:10:52 +01:00
nou 380974a088 Allow saving debayered image 2022-12-15 00:03:29 +01:00
nou b1ad56ca1f Fix debayer shader 2022-12-15 00:03:14 +01:00
nou 58abef5a72 Update metainfo 2022-12-14 22:00:07 +01:00
nou 5427ff57cb Change superpixel tooltip 2022-12-14 21:58:24 +01:00
nou 6a2fa3f656 Update translation 2022-12-14 21:51:19 +01:00
nou c62ec7db8c Add move to trash action 2022-12-14 21:51:05 +01:00
nou d5eb0fdce5 Change key shortcuts 2022-12-14 21:50:30 +01:00
nou 6d25919e1f Add image formats to help 2022-12-14 21:50:05 +01:00
nou 26d1af6077 Add debayer 2022-12-14 21:18:25 +01:00
nou 44d8a8b856 Fix invert button 2022-12-14 17:42:13 +01:00
nou dab6c1f79d Add All files filter to open dialog 2022-12-14 17:16:32 +01:00
nou ce6a4cc40c Refresh dir when show hidden files is toggled 2022-12-13 21:31:23 +01:00
nou 0368c1f1dc Update metainfo xml 2022-12-10 07:48:17 +01:00
nou a1e98d818b Fix copy on btrfs 2022-12-09 19:58:15 +01:00
nou f3f194bcef Update translation and metainfo 2022-12-09 19:33:26 +01:00
nou efd36f96c3 Use native dialogs 2022-12-09 18:38:07 +01:00
nou 37923b37b3 Add error message for copy/move 2022-12-09 18:36:43 +01:00
nou 900453577e Fix includes 2022-11-28 17:36:37 +01:00
nou 34d466c3e0 Show checker pattern with transparent files 2022-11-28 17:32:23 +01:00
nou 9e98127084 Do gamma conversion manualy
Requesting sRGB capable framebuffer is unreliable
2022-11-28 17:31:50 +01:00
nou ba6062b925 Enale loading all image types that Qt can load 2022-11-27 21:10:43 +01:00
nou 6411b7cd15 Release 20221126 2022-11-26 12:03:36 +01:00
nou 223f7cd0ea Refractor save dialog 2022-11-26 11:02:29 +01:00
nou f8f9ee08b3 Add QFileDialog::DontUseNativeDialog 2022-11-22 11:04:04 +01:00
nou af5aed7ef8 Install metainfo generally 2022-11-21 19:09:19 +01:00
nou 8f5249b142 Add metainfo file 2022-11-21 17:44:48 +01:00
nou a7dc942c62 Add scalable icon 2022-11-21 15:49:09 +01:00
nou 1a1399434b Change domain name 2022-11-21 13:18:46 +01:00
nou be567841bf Workaround in AMD OpenGL driver bug
AMD OpenGL driver on Windows doesn't generate mipmaps for sRGB textures
correctly
2022-10-26 23:32:22 +02:00
nou 62d2671112 Update french translation 2022-10-23 12:11:40 +02:00
nou 1f8923512e Update translations 2022-10-23 10:18:21 +02:00
nou 455c3b2d64 Make it combilable with Qt 5.13 and older 2022-10-23 09:54:23 +02:00
nou 4fe546f0e5 Add support for ICC color profiles 2022-10-18 21:19:05 +02:00
nou 95c6fc5343 Enable sRGB to gamma correct scaling 2022-10-16 11:05:15 +02:00
nou 2bc54ea0cc Fix loading RAW on MacOS
LibRaw object is too big for stack so needs to be on heap
2022-10-15 09:56:14 +02:00
nou be6e472081 Settings dialog 2022-10-10 10:30:25 +02:00
nou 9746f8f653 Add option to show hidden files 2022-08-29 18:37:51 +02:00
nou b51a305c63 Remove unused method 2022-08-29 18:29:00 +02:00
nou 39775b5e98 Scale float images to 0,1 range on load 2022-07-22 11:36:10 +02:00
nou 93b56e2966 Workaround for QTBUG-87332 2022-07-03 13:40:54 +02:00
nou 2e41464ff4 Update build instructions 2022-06-27 10:26:27 +02:00
41 changed files with 1041 additions and 175 deletions
+20 -8
View File
@@ -25,6 +25,7 @@ set(TENMON_SRC
about.cpp about.cpp
database.cpp database.cpp
databaseview.cpp databaseview.cpp
delete.cpp
filesystemwidget.cpp filesystemwidget.cpp
imageinfo.cpp imageinfo.cpp
imageringlist.cpp imageringlist.cpp
@@ -35,12 +36,22 @@ set(TENMON_SRC
mainwindow.cpp mainwindow.cpp
markedfiles.cpp markedfiles.cpp
rawimage.cpp rawimage.cpp
settingsdialog.cpp
starfit.cpp starfit.cpp
statusbar.cpp statusbar.cpp
stfslider.cpp stfslider.cpp
stretchtoolbar.cpp stretchtoolbar.cpp
) )
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)
add_compile_definitions("COLOR_MANAGMENT")
endif(COLOR_MANAGMENT)
qt5_add_resources(TENMON_SRC resources.qrc) qt5_add_resources(TENMON_SRC resources.qrc)
if(WIN32) if(WIN32)
list(APPEND TENMON_SRC icon.rc) list(APPEND TENMON_SRC icon.rc)
@@ -53,6 +64,8 @@ elseif(APPLE)
else() else()
add_compile_definitions("__PCL_LINUX") add_compile_definitions("__PCL_LINUX")
set(tenmon_ICON "") set(tenmon_ICON "")
find_package(PkgConfig REQUIRED)
pkg_search_module(GIO REQUIRED gio-2.0)
endif() endif()
add_executable(tenmon WIN32 MACOSX_BUNDLE ${tenmon_ICON} ${TENMON_SRC}) add_executable(tenmon WIN32 MACOSX_BUNDLE ${tenmon_ICON} ${TENMON_SRC})
@@ -66,13 +79,14 @@ elseif(APPLE)
target_link_directories(tenmon PRIVATE 3rdparty/lib/MacOS) target_link_directories(tenmon PRIVATE 3rdparty/lib/MacOS)
else() else()
target_link_directories(tenmon PRIVATE 3rdparty/lib/Linux) target_link_directories(tenmon PRIVATE 3rdparty/lib/Linux)
target_include_directories(tenmon PRIVATE ${GIO_INCLUDE_DIRS})
endif() endif()
target_link_libraries(tenmon Qt5::Widgets Qt5::Sql ${OpenCV_LIBS} ${GSL_LIB} ${GSLCBLAS_LIB} ${EXIF_LIB} ${FITS_LIB} ${RAW_LIB} ${WCS_LIB}) target_link_libraries(tenmon Qt5::Widgets Qt5::Sql ${OpenCV_LIBS} ${GSL_LIB} ${GSLCBLAS_LIB} ${EXIF_LIB} ${FITS_LIB} ${RAW_LIB} ${WCS_LIB})
if(APPLE) if(APPLE)
target_link_libraries(tenmon PCL-pxi lcms-pxi lz4-pxi RFC6234-pxi zlib-pxi "-framework CoreFoundation") target_link_libraries(tenmon PCL-pxi lcms-pxi lz4-pxi RFC6234-pxi zlib-pxi "-framework CoreFoundation")
else() else()
target_link_libraries(tenmon PCL lcms lz4 RFC6234 zlib) target_link_libraries(tenmon PCL lcms lz4 RFC6234 zlib ${GIO_LDFLAGS})
endif(APPLE) endif(APPLE)
if(LIBRAW_STATIC) if(LIBRAW_STATIC)
@@ -82,18 +96,16 @@ endif()
install(TARGETS tenmon BUNDLE DESTINATION .) install(TARGETS tenmon BUNDLE DESTINATION .)
if(UNIX AND NOT APPLE) if(UNIX AND NOT APPLE)
include(GNUInstallDirs)
find_program(XDG-DESKTOP-MENU_EXECUTABLE xdg-desktop-menu) find_program(XDG-DESKTOP-MENU_EXECUTABLE xdg-desktop-menu)
if(XDG-DESKTOP-MENU_EXECUTABLE) if(XDG-DESKTOP-MENU_EXECUTABLE)
install(SCRIPT install.cmake) install(SCRIPT install.cmake)
else() else()
if(DEFINED ENV{FLATPAK_DEST}) install(FILES space.nouspiro.tenmon.desktop DESTINATION "${CMAKE_INSTALL_DATADIR}/applications")
install(FILES org.nou.tenmon.desktop DESTINATION "$ENV{FLATPAK_DEST}/share/applications") install(FILES space.nouspiro.tenmon.png DESTINATION "${CMAKE_INSTALL_DATADIR}/icons/hicolor/64x64/apps")
install(FILES org.nou.tenmon.png DESTINATION "$ENV{FLATPAK_DEST}/share/icons/hicolor/32x32/apps") install(FILES space.nouspiro.tenmon_128.png DESTINATION "${CMAKE_INSTALL_DATADIR}/icons/hicolor/128x128/apps" RENAME space.nouspiro.tenmon.png)
else()
install(FILES org.nou.tenmon.desktop DESTINATION "/usr/share/applications")
install(FILES org.nou.tenmon.png DESTINATION "/usr/share/icons/hicolor/32x32/apps")
endif()
endif() endif()
install(FILES space.nouspiro.tenmon.metainfo.xml DESTINATION "${CMAKE_INSTALL_DATADIR}/metainfo")
endif(UNIX AND NOT APPLE) endif(UNIX AND NOT APPLE)
option(RELEASE_BUILD "Release build" OFF) option(RELEASE_BUILD "Release build" OFF)
+16 -3
View File
@@ -2,10 +2,23 @@ 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 cmake sudo apt install qtbase5-dev libraw-dev libexif-dev libcfitsio-dev libgsl-dev wcslib-dev libopencv-dev cmake
on OpenSUSE
sudo zypper install opencv-devel gsl-devel exif-devel libraw-devel wcslib-devel libqt5-qtbase-devel
MacOS X
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.
homebrew install qt5 libraw cfitsio libexif libgsl wcslib opencv
You may need to set CMAKE_PREFIX_PATH for Qt5 and OpenCV so CMake can find them.
Then to build run standard cmake Then to build run standard cmake
cmake -B build -S . cmake -B build -S .
make cmake --build build
./tenmon ./build/tenmon
+1 -1
View File
@@ -13,7 +13,7 @@ img { margin: 5px; }
<ul> <ul>
<li>FITS 8, 16 bit integer and 32 bit float</li> <li>FITS 8, 16 bit integer and 32 bit float</li>
<li>XISF 8, 16 bit integer and 32 bit float</li> <li>XISF 8, 16 bit integer and 32 bit float</li>
<li>JPEG and PNG images</li> <li>JPEG, PNG, BMP, GIF, PBM, PGM, PPM and SVG images</li>
<li>CR2, NEF, DNG raw images</li> <li>CR2, NEF, DNG raw images</li>
</ul> </ul>
</p> </p>
+1 -1
View File
@@ -13,7 +13,7 @@ img { margin: 5px; }
<ul> <ul>
<li>FITS 8, 16 bit entier et 32 bit point flottant</li> <li>FITS 8, 16 bit entier et 32 bit point flottant</li>
<li>XISF 8, 16 bit entier et 32 bit point flottant</li> <li>XISF 8, 16 bit entier et 32 bit point flottant</li>
<li>images JPEG et PNG</li> <li>images JPEG, PNG, BMP, GIF, PBM, PGM, PPM et SVG</li>
<li>images RAW CR2, NEF, DNG</li> <li>images RAW CR2, NEF, DNG</li>
</ul> </ul>
</p> </p>
+1 -1
View File
@@ -12,7 +12,7 @@ p { padding:0px; margin:5px 5px 10px 5px; }
<ul> <ul>
<li>FITS 8, 16 bitové celočíselné a 32 bitové s plávajúcou čiarkou</li> <li>FITS 8, 16 bitové celočíselné a 32 bitové s plávajúcou čiarkou</li>
<li>XISF 8, 16 bitové celočíselné a 32 bitové s plávajúcou čiarkou</li> <li>XISF 8, 16 bitové celočíselné a 32 bitové s plávajúcou čiarkou</li>
<li>JPEG a PNG obrázky</li> <li>JPEG, PNG, BMP, GIF, PBM, PGM, PPM a SVG obrázky</li>
<li>CR2, NEF, DNG raw obrázky</li> <li>CR2, NEF, DNG raw obrázky</li>
</ul> </ul>
</p> </p>
+1 -1
View File
@@ -1,5 +1,5 @@
<table><tr> <table><tr>
<td style="padding-right:10px"><img src=":/org.nou.tenmon.png"></td> <td style="padding-right:10px"><img src=":/space.nouspiro.tenmon.png"></td>
<td><h3>Tenmon</h3> <td><h3>Tenmon</h3>
Tenmon is FITS/XISF image viewer and converter. It also index FITS keywords.<br> Tenmon is FITS/XISF image viewer and converter. It also index FITS keywords.<br>
v@GITVERSION@ Copyright © 2022 Dušan Poizl<br><br> v@GITVERSION@ Copyright © 2022 Dušan Poizl<br><br>
+30
View File
@@ -0,0 +1,30 @@
#ifdef __linux__
#define QT_NO_KEYWORDS
#include <QString>
#include <iostream>
#include <gio/gio.h>
//flatpak bug prevent to use QFile::moveToTrash
bool moveToTrash(const QString &path)
{
GFile *gfile = g_file_new_for_path(path.toLocal8Bit().data());
GError *error = nullptr;
g_file_trash(gfile, nullptr, &error);
if(error)std::cerr << "failed to trash file " << error->code << " " << error->message << std::endl;
g_clear_error(&error);
g_object_unref(gfile);
return true;
}
#else
#include <QFile>
#include <QString>
bool moveToTrash(const QString &path)
{
return QFile::moveToTrash(path);
}
#endif
+14 -6
View File
@@ -20,12 +20,6 @@ FilesystemWidget::FilesystemWidget(QAbstractItemModel *model, QWidget *parent) :
connect(m_listView->selectionModel(), &QItemSelectionModel::currentChanged, this, &FilesystemWidget::fileClicked); connect(m_listView->selectionModel(), &QItemSelectionModel::currentChanged, this, &FilesystemWidget::fileClicked);
} }
void FilesystemWidget::setDir(const QString &dir)
{
//m_model->setRootPath(dir);
//m_treeView->setRootIndex(m_model->index(m_model->rootPath()));
}
void FilesystemWidget::selectFile(int row) void FilesystemWidget::selectFile(int row)
{ {
QModelIndex index = m_model->index(row, 0); QModelIndex index = m_model->index(row, 0);
@@ -47,6 +41,9 @@ Filetree::Filetree(QWidget *parent) : QTreeView(parent)
m_fileSystemModel->setRootPath(m_rootDir); m_fileSystemModel->setRootPath(m_rootDir);
m_fileSystemModel->setNameFilters({"*.fits", "*.fit", "*.xisf", "*.jpg", "*.jpeg", "*.png", "*.cr2", "*.nef", "*.dng"}); m_fileSystemModel->setNameFilters({"*.fits", "*.fit", "*.xisf", "*.jpg", "*.jpeg", "*.png", "*.cr2", "*.nef", "*.dng"});
m_fileSystemModel->setNameFilterDisables(false); m_fileSystemModel->setNameFilterDisables(false);
if(settings.value("filetree/showHidden", false).toBool())
m_fileSystemModel->setFilter(m_fileSystemModel->filter() | QDir::Hidden);
setModel(m_fileSystemModel); setModel(m_fileSystemModel);
setRootIndex(m_fileSystemModel->index(m_rootDir)); setRootIndex(m_fileSystemModel->index(m_rootDir));
header()->restoreState(settings.value("filetree/header").toByteArray()); header()->restoreState(settings.value("filetree/header").toByteArray());
@@ -57,6 +54,7 @@ Filetree::~Filetree()
QSettings settings; QSettings settings;
settings.setValue("filetree/rootDir", m_rootDir); settings.setValue("filetree/rootDir", m_rootDir);
settings.setValue("filetree/header", header()->saveState()); settings.setValue("filetree/header", header()->saveState());
settings.setValue("filetree/showHidden", (bool)(m_fileSystemModel->filter() & QDir::Hidden));
} }
void Filetree::contextMenuEvent(QContextMenuEvent *event) void Filetree::contextMenuEvent(QContextMenuEvent *event)
@@ -85,6 +83,9 @@ void Filetree::contextMenuEvent(QContextMenuEvent *event)
QAction *resetRoot = menu.addAction(tr("Reset root")); QAction *resetRoot = menu.addAction(tr("Reset root"));
QAction *goUp = menu.addAction(tr("Go up")); QAction *goUp = menu.addAction(tr("Go up"));
QAction *showHidden = menu.addAction(tr("Show hidden files"));
showHidden->setCheckable(true);
showHidden->setChecked(m_fileSystemModel->filter() & QDir::Hidden);
QAction *a = menu.exec(event->globalPos()); QAction *a = menu.exec(event->globalPos());
if(a == nullptr) if(a == nullptr)
@@ -121,6 +122,13 @@ void Filetree::contextMenuEvent(QContextMenuEvent *event)
{ {
emit indexDirectory(m_fileSystemModel->filePath(index)); emit indexDirectory(m_fileSystemModel->filePath(index));
} }
else if(a == showHidden)
{
auto filter = m_fileSystemModel->filter();
filter ^= QDir::Hidden;
m_fileSystemModel->setFilter(filter);
m_fileSystemModel->setRootPath(m_rootDir);
}
} }
void Filetree::mouseDoubleClickEvent(QMouseEvent *event) void Filetree::mouseDoubleClickEvent(QMouseEvent *event)
-1
View File
@@ -13,7 +13,6 @@ class FilesystemWidget : public QWidget
QAbstractItemModel *m_model; QAbstractItemModel *m_model;
public: public:
explicit FilesystemWidget(QAbstractItemModel *model, QWidget *parent = nullptr); explicit FilesystemWidget(QAbstractItemModel *model, QWidget *parent = nullptr);
void setDir(const QString &dir);
private slots: private slots:
void selectFile(int row); void selectFile(int row);
void fileClicked(const QModelIndex &index, const QModelIndex &); void fileClicked(const QModelIndex &index, const QModelIndex &);
+6 -6
View File
@@ -7,7 +7,7 @@
using namespace std; using namespace std;
const int DEFAULT_WIDTH = 2; int DEFAULT_WIDTH = 2;
Image::Image(const QString name, int number, ImageRingList *ringList) : Image::Image(const QString name, int number, ImageRingList *ringList) :
m_loading(false), m_loading(false),
@@ -98,12 +98,14 @@ void Image::thumbnailLoadFinish(void *rawImage)
emit thumbnailLoaded(this); emit thumbnailLoaded(this);
} }
ImageRingList::ImageRingList(Database *database, QObject *parent) : QAbstractItemModel(parent) ImageRingList::ImageRingList(Database *database, const QStringList &nameFilter, QObject *parent) : QAbstractItemModel(parent)
, m_liveMode(false) , m_liveMode(false)
, m_analyzeLevel(None) , m_analyzeLevel(None)
, m_database(database) , m_database(database)
, m_nameFilter(nameFilter)
{ {
connect(&m_fileSystemWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(dirChanged(QString))); connect(&m_fileSystemWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(dirChanged(QString)));
m_nameFilter.replaceInStrings(QRegExp("^"), "*.");
m_thumbPool = new QThreadPool(this); m_thumbPool = new QThreadPool(this);
} }
@@ -122,10 +124,7 @@ bool ImageRingList::setDir(const QString path, const QString &currentFile)
if(dir.exists()) if(dir.exists())
{ {
QStringList nameFilter; QStringList list = dir.entryList(m_nameFilter, QDir::Files | QDir::Readable, m_liveMode ? QDir::Time : QDir::Name | QDir::IgnoreCase);
nameFilter << "*.jpg" << "*.jpeg" << "*.png" << "*.cr2" << "*.nef" << "*.dng" << "*.fit" << "*.fits" << "*.xisf";
QStringList list = dir.entryList(nameFilter, QDir::Files | QDir::Readable, m_liveMode ? QDir::Time : QDir::Name | QDir::IgnoreCase);
QStringList absolutePaths; QStringList absolutePaths;
foreach(const QString &file, list) foreach(const QString &file, list)
{ {
@@ -260,6 +259,7 @@ void ImageRingList::loadThumbnails()
void ImageRingList::stopLoading() void ImageRingList::stopLoading()
{ {
m_thumbPool->clear(); m_thumbPool->clear();
m_thumbPool->waitForDone();
} }
int ImageRingList::imageCount() const int ImageRingList::imageCount() const
+2 -1
View File
@@ -60,8 +60,9 @@ class ImageRingList : public QAbstractItemModel
AnalyzeLevel m_analyzeLevel; AnalyzeLevel m_analyzeLevel;
QThreadPool *m_thumbPool; QThreadPool *m_thumbPool;
Database *m_database; Database *m_database;
QStringList m_nameFilter;
public: public:
explicit ImageRingList(Database *database, QObject *parent = 0); explicit ImageRingList(Database *database, const QStringList &nameFilter, QObject *parent = 0);
~ImageRingList() override; ~ImageRingList() override;
bool setDir(const QString path, const QString &currentFile = QString()); bool setDir(const QString path, const QString &currentFile = QString());
void setFile(const QString &file); void setFile(const QString &file);
+108 -9
View File
@@ -11,6 +11,7 @@
#include <QCoreApplication> #include <QCoreApplication>
#include <QPainter> #include <QPainter>
#include <QFileInfo> #include <QFileInfo>
#include <cmath>
struct RawImageType struct RawImageType
{ {
@@ -24,13 +25,20 @@ const RawImageType rawImageTypes[] = {
{QOpenGLTexture::Red, QOpenGLTexture::R8_UNorm, QOpenGLTexture::UInt8, true}, {QOpenGLTexture::Red, QOpenGLTexture::R8_UNorm, QOpenGLTexture::UInt8, true},
{QOpenGLTexture::Red, QOpenGLTexture::R16_UNorm, QOpenGLTexture::UInt16, true}, {QOpenGLTexture::Red, QOpenGLTexture::R16_UNorm, QOpenGLTexture::UInt16, true},
{QOpenGLTexture::Red, QOpenGLTexture::R32F, QOpenGLTexture::Float32, true}, {QOpenGLTexture::Red, QOpenGLTexture::R32F, QOpenGLTexture::Float32, true},
#ifdef COLOR_MANAGMENT
{QOpenGLTexture::RGB, QOpenGLTexture::SRGB8, QOpenGLTexture::UInt8, false},
{QOpenGLTexture::RGBA,QOpenGLTexture::SRGB8_Alpha8, QOpenGLTexture::UInt8, false},
#else
{QOpenGLTexture::RGB, QOpenGLTexture::RGB8_UNorm, QOpenGLTexture::UInt8, false}, {QOpenGLTexture::RGB, QOpenGLTexture::RGB8_UNorm, QOpenGLTexture::UInt8, false},
{QOpenGLTexture::RGBA,QOpenGLTexture::RGB8_UNorm, QOpenGLTexture::UInt8, false}, {QOpenGLTexture::RGBA,QOpenGLTexture::RGBA8_UNorm, QOpenGLTexture::UInt8, false},
#endif
{QOpenGLTexture::RGB, QOpenGLTexture::RGB16_UNorm, QOpenGLTexture::UInt16, false}, {QOpenGLTexture::RGB, QOpenGLTexture::RGB16_UNorm, QOpenGLTexture::UInt16, false},
{QOpenGLTexture::RGBA, QOpenGLTexture::RGB16_UNorm, QOpenGLTexture::UInt16, false}, {QOpenGLTexture::RGBA, QOpenGLTexture::RGB16_UNorm, QOpenGLTexture::UInt16, false},
{QOpenGLTexture::RGB, QOpenGLTexture::RGB32F, QOpenGLTexture::Float32, false} {QOpenGLTexture::RGB, QOpenGLTexture::RGB32F, QOpenGLTexture::Float32, false}
}; };
static bool MANUAL_MIPMAP_GEN = false;
void setScrollRange(QScrollBar *scrollBar, int newRange) void setScrollRange(QScrollBar *scrollBar, int newRange)
{ {
int page = scrollBar->pageStep(); int page = scrollBar->pageStep();
@@ -97,8 +105,11 @@ void ImageWidget::setImage(std::shared_ptr<RawImage> image, int index)
m_currentImg = index; m_currentImg = index;
const RawImageType &rawImageType = rawImageTypes[image->type()]; const RawImageType &rawImageType = rawImageTypes[image->type()];
m_srgb = rawImageType.textureFormat == QOpenGLTexture::SRGB8 || rawImageType.textureFormat == QOpenGLTexture::SRGB8_Alpha8;
m_bwImg = rawImageType.bw;
m_image->destroy(); m_image->destroy();
m_image->setAutoMipMapGenerationEnabled(false);
m_image->setFormat(rawImageType.textureFormat); m_image->setFormat(rawImageType.textureFormat);
m_image->setSize(image->width(), image->height()); m_image->setSize(image->width(), image->height());
m_image->setMipLevels([&](){ int c = 0; int s = std::min(m_imgWidth, m_imgHeight); while(s>>=1)c++; return c; }()); m_image->setMipLevels([&](){ int c = 0; int s = std::min(m_imgWidth, m_imgHeight); while(s>>=1)c++; return c; }());
@@ -107,9 +118,46 @@ void ImageWidget::setImage(std::shared_ptr<RawImage> image, int index)
m_image->setWrapMode(QOpenGLTexture::ClampToEdge); m_image->setWrapMode(QOpenGLTexture::ClampToEdge);
m_image->setBorderColor(0, 0, 0, 0); m_image->setBorderColor(0, 0, 0, 0);
m_image->setData(0, rawImageType.pixelFormat, rawImageType.dataType, (const void*)image->data(), m_transferOptions.get()); m_image->setData(0, rawImageType.pixelFormat, rawImageType.dataType, (const void*)image->data(), m_transferOptions.get());
m_image->setLevelOfDetailRange(m_superpixel ? 1 : 0, m_image->mipMaxLevel());
m_image->generateMipMaps(); auto sRGB_linear = [](cv::Point3f &pixel, const int *pos)
m_bwImg = rawImageType.bw; {
pixel.x = pixel.x <= 0.04045f ? pixel.x / 12.92f : std::pow((pixel.x + 0.055) / 1.055f, 2.4f);
pixel.y = pixel.y <= 0.04045f ? pixel.y / 12.92f : std::pow((pixel.y + 0.055) / 1.055f, 2.4f);
pixel.z = pixel.z <= 0.04045f ? pixel.z / 12.92f : std::pow((pixel.z + 0.055) / 1.055f, 2.4f);
};
auto linear_sRGB = [](cv::Point3f &pixel, const int *pos)
{
pixel.x = pixel.x <= 0.0031308f ? pixel.x * 12.92f : 1.055f * std::pow(pixel.x , 1/2.4f) - 0.055f;
pixel.y = pixel.y <= 0.0031308f ? pixel.y * 12.92f : 1.055f * std::pow(pixel.y , 1/2.4f) - 0.055f;
pixel.z = pixel.z <= 0.0031308f ? pixel.z * 12.92f : 1.055f * std::pow(pixel.z , 1/2.4f) - 0.055f;
};
//AMD OpenGL driver on Windows doesn't generate mipmaps for sRGB textures correctly
if(m_srgb && MANUAL_MIPMAP_GEN)
{
cv::Mat img = image->mat();
img.convertTo(img, CV_32FC3, 1/255.0);
img.forEach<cv::Point3f>(sRGB_linear);
cv::Size size(img.cols, img.rows);
for(int i=1; i<m_image->mipLevels(); i++)
{
cv::Mat mip;
size /= 2;
cv::resize(img, mip, size);
mip.copyTo(img);
mip.forEach<cv::Point3f>(linear_sRGB);
mip.convertTo(mip, CV_8UC3, 255);
m_image->setData(i, rawImageType.pixelFormat, rawImageType.dataType, (const void*)mip.ptr(), m_transferOptions.get());
}
}
else m_image->generateMipMaps();
if(m_debayerTex)
{
f->glDeleteTextures(1, &m_debayerTex);
m_debayerTex = 0;
}
update(); update();
} }
@@ -168,7 +216,6 @@ void ImageWidget::setOffset(int dx, int dy)
void ImageWidget::superPixel(bool enable) void ImageWidget::superPixel(bool enable)
{ {
m_superpixel = enable; m_superpixel = enable;
m_image->setLevelOfDetailRange(enable ? 1 : 0, m_image->mipMaxLevel());
update(); update();
} }
@@ -192,7 +239,10 @@ QImage ImageWidget::renderToImage()
m_program->setUniformValue("offset", 0.0f, 0.0f); m_program->setUniformValue("offset", 0.0f, 0.0f);
m_program->setUniformValue("zoom", 1.0f); m_program->setUniformValue("zoom", 1.0f);
m_image->bind(0); if(m_superpixel && m_debayerTex)
f->glBindTexture(GL_TEXTURE_2D, m_debayerTex);
else
m_image->bind(0);
f->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); f->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
fbo.bindDefault(); fbo.bindDefault();
@@ -249,6 +299,7 @@ void ImageWidget::paintGL()
} }
m_thumbnailProgram->bind(); m_thumbnailProgram->bind();
f->glUniform3i(m_thumbnailProgram->uniformLocation("viewport_row"), width(), height(), width()/THUMB_SIZE_BORDER); f->glUniform3i(m_thumbnailProgram->uniformLocation("viewport_row"), width(), height(), width()/THUMB_SIZE_BORDER);
f->glUniform3i(m_thumbnailProgram->uniformLocation("thumb_size"), THUMB_SIZE_BORDER/2, THUMB_SIZE_BORDER, THUMB_SIZE_BORDER_Y);
m_thumbnailProgram->setUniformValue("mtf_param", m_low, m_mid, m_high); m_thumbnailProgram->setUniformValue("mtf_param", m_low, m_mid, m_high);
m_thumbnailProgram->setUniformValue("invert", m_invert); m_thumbnailProgram->setUniformValue("invert", m_invert);
m_thumbnailProgram->setUniformValue("offset", 0, m_dy); m_thumbnailProgram->setUniformValue("offset", 0, m_dy);
@@ -287,15 +338,24 @@ void ImageWidget::paintGL()
} }
else else
{ {
debayer();
m_vao->bind(); m_vao->bind();
m_image->bind(0); if(m_superpixel && m_debayerTex)
f->glBindTexture(GL_TEXTURE_2D, m_debayerTex);
else
m_image->bind(0);
m_program->bind(); m_program->bind();
m_program->setUniformValue("viewport", (float)width(), (float)height()); m_program->setUniformValue("viewport", (float)width(), (float)height());
m_program->setUniformValue("offset", dx, dy); m_program->setUniformValue("offset", dx, dy);
m_program->setUniformValue("mtf_param", m_low, m_mid, m_high); m_program->setUniformValue("mtf_param", m_low, m_mid, m_high);
m_program->setUniformValue("zoom", 1.0f/m_scale); m_program->setUniformValue("zoom", 1.0f/m_scale);
m_program->setUniformValue("bw", m_bwImg); m_program->setUniformValue("bw", m_bwImg && !m_superpixel);
m_program->setUniformValue("invert", m_invert); m_program->setUniformValue("invert", m_invert);
#ifdef COLOR_MANAGMENT
m_program->setUniformValue("srgb", m_srgb);
#endif
f->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); f->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
} }
@@ -335,6 +395,8 @@ void ImageWidget::initializeGL()
qDebug() << (char*)f->glGetString(GL_RENDERER); qDebug() << (char*)f->glGetString(GL_RENDERER);
qDebug() << (char*)f->glGetString(GL_VERSION); qDebug() << (char*)f->glGetString(GL_VERSION);
MANUAL_MIPMAP_GEN = QString((const char*)f->glGetString(GL_VENDOR)).startsWith("ATI Technologies Inc", Qt::CaseInsensitive);
qDebug() << context()->format(); qDebug() << context()->format();
// each vertex is x,y 2D position and s,t texture coordinates // each vertex is x,y 2D position and s,t texture coordinates
@@ -366,12 +428,27 @@ void ImageWidget::initializeGL()
m_program->setUniformValue("qt_Texture0", (GLuint)0); m_program->setUniformValue("qt_Texture0", (GLuint)0);
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->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/debayer.vert");
m_debayerProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/debayer.frag");
if(f3)f3->glBindFragDataLocation(m_debayerProgram->programId(), 0, "color");
m_debayerProgram->bind();
m_debayerProgram->enableAttributeArray("qt_Vertex");
m_debayerProgram->setAttributeBuffer("qt_Vertex", GL_FLOAT, 0, 2, sizeof(float)*4);
m_debayerProgram->enableAttributeArray("qt_MultiTexCoord0");
m_debayerProgram->setAttributeBuffer("qt_MultiTexCoord0", GL_FLOAT, sizeof(float)*2, 2, sizeof(float)*4);
m_debayerProgram->setUniformValue("qt_Texture0", (GLuint)0);
if(!m_debayerProgram->link())
{
qDebug() << "Link failed" << m_debayerProgram->log();
}
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, ":/shaders/thumb.vert");
m_thumbnailProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/thumb.frag"); m_thumbnailProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/thumb.frag");
if(f3)f3->glBindFragDataLocation(m_program->programId(), 0, "color"); if(f3)f3->glBindFragDataLocation(m_thumbnailProgram->programId(), 0, "color");
m_thumbnailProgram->bind(); m_thumbnailProgram->bind();
m_thumbnailProgram->enableAttributeArray("qt_Vertex"); m_thumbnailProgram->enableAttributeArray("qt_Vertex");
m_thumbnailProgram->setAttributeBuffer("qt_Vertex", GL_FLOAT, 0, 2, sizeof(float)*4); m_thumbnailProgram->setAttributeBuffer("qt_Vertex", GL_FLOAT, 0, 2, sizeof(float)*4);
@@ -545,6 +622,28 @@ void ImageWidget::thumbSelect(QMouseEvent *event)
} }
} }
void ImageWidget::debayer()
{
if(m_debayerTex > 0 || !m_superpixel || !m_bwImg || m_imgWidth < 0)return;
QOpenGLFramebufferObject fbo(m_imgWidth, m_imgHeight, QOpenGLFramebufferObject::NoAttachment, GL_TEXTURE_2D, GL_RGBA16);
fbo.bind();
f->glViewport(0, 0, m_imgWidth, m_imgHeight);
m_debayerProgram->bind();
m_image->bind(0);
f->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
fbo.release();
f->glViewport(0, 0, m_width, m_height);
m_debayerTex = fbo.takeTexture();
f->glBindTexture(GL_TEXTURE_2D, m_debayerTex);
f->glGenerateMipmap(GL_TEXTURE_2D);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
}
ImageScrollAreaGL::ImageScrollAreaGL(Database *database, QWidget *parent) : QWidget(parent) ImageScrollAreaGL::ImageScrollAreaGL(Database *database, QWidget *parent) : QWidget(parent)
{ {
QGridLayout *layout = new QGridLayout(this); QGridLayout *layout = new QGridLayout(this);
+4
View File
@@ -32,6 +32,7 @@ class ImageWidget : public QOpenGLWidget
QTimer *m_updateTimer; QTimer *m_updateTimer;
std::unique_ptr<QOpenGLShaderProgram> m_program; std::unique_ptr<QOpenGLShaderProgram> m_program;
std::unique_ptr<QOpenGLShaderProgram> m_thumbnailProgram; std::unique_ptr<QOpenGLShaderProgram> m_thumbnailProgram;
std::unique_ptr<QOpenGLShaderProgram> m_debayerProgram;
std::unique_ptr<QOpenGLBuffer> m_buffer; std::unique_ptr<QOpenGLBuffer> m_buffer;
std::unique_ptr<QOpenGLBuffer> m_bufferSizes; std::unique_ptr<QOpenGLBuffer> m_bufferSizes;
std::unique_ptr<QOpenGLTexture> m_image; std::unique_ptr<QOpenGLTexture> m_image;
@@ -39,6 +40,7 @@ class ImageWidget : public QOpenGLWidget
std::unique_ptr<QOpenGLVertexArrayObject> m_vaoThumb; std::unique_ptr<QOpenGLVertexArrayObject> m_vaoThumb;
std::unique_ptr<QOpenGLPixelTransferOptions> m_transferOptions; std::unique_ptr<QOpenGLPixelTransferOptions> m_transferOptions;
std::unique_ptr<QOpenGLTexture> m_thumbnailTexture; std::unique_ptr<QOpenGLTexture> m_thumbnailTexture;
GLuint m_debayerTex = 0;
std::shared_ptr<RawImage> m_rawImage; std::shared_ptr<RawImage> m_rawImage;
std::shared_ptr<WCSData> m_wcs; std::shared_ptr<WCSData> m_wcs;
int m_width, m_height; int m_width, m_height;
@@ -57,6 +59,7 @@ class ImageWidget : public QOpenGLWidget
bool m_showThumbnails; bool m_showThumbnails;
bool m_selecting; bool m_selecting;
bool m_sizesDirty; bool m_sizesDirty;
bool m_srgb;
int m_thumbnailCount; int m_thumbnailCount;
QVector<ImageThumb> m_thumnails; QVector<ImageThumb> m_thumnails;
Database *m_database; Database *m_database;
@@ -87,6 +90,7 @@ protected:
void mouseMoveEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override;
void thumbSelect(QMouseEvent *event); void thumbSelect(QMouseEvent *event);
void debayer();
signals: signals:
void fileDropped(const QString &path); void fileDropped(const QString &path);
void status(const QString &value, const QString &pixelCoords, const QString &celestialCoords); void status(const QString &value, const QString &pixelCoords, const QString &celestialCoords);
+3 -2
View File
@@ -1,4 +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 org.nou.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 32 org.nou.tenmon.png org.nou.tenmon 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 128 space.nouspiro.tenmon_128.png space.nouspiro.tenmon WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
+42 -15
View File
@@ -14,6 +14,11 @@
#include "starfit.h" #include "starfit.h"
#include "wcslib/wcshdr.h" #include "wcslib/wcshdr.h"
#ifdef COLOR_MANAGMENT
#include <QColorSpace>
static pcl::ICCProfile sRgbIccProfile((void*)QColorSpace(QColorSpace::SRgb).iccProfile().data());
#endif
LoadRunable::LoadRunable(const QString &file, Image *receiver, AnalyzeLevel level, bool thumbnail) : LoadRunable::LoadRunable(const QString &file, Image *receiver, AnalyzeLevel level, bool thumbnail) :
m_file(file), m_file(file),
m_receiver(receiver), m_receiver(receiver),
@@ -83,17 +88,17 @@ bool loadRAW(const QString path, ImageInfoData &info, RawImage **image)
if(!image) if(!image)
return false; return false;
LibRaw raw; std::unique_ptr<LibRaw> raw = std::make_unique<LibRaw>();
raw.open_file(path.toLocal8Bit().data()); raw->open_file(path.toLocal8Bit().data());
raw.imgdata.params.half_size = true; raw->imgdata.params.half_size = true;
raw.imgdata.params.use_camera_wb = true; raw->imgdata.params.use_camera_wb = true;
raw.imgdata.params.user_flip = 0; raw->imgdata.params.user_flip = 0;
if(raw.unpack()) if(raw->unpack())
return false; return false;
if(image) if(image)
{ {
libraw_rawdata_t rawdata = raw.imgdata.rawdata; libraw_rawdata_t rawdata = raw->imgdata.rawdata;
size_t size = rawdata.sizes.width*rawdata.sizes.height; size_t size = rawdata.sizes.width*rawdata.sizes.height;
std::vector<uint16_t> out; std::vector<uint16_t> out;
@@ -115,14 +120,14 @@ bool loadRAW(const QString path, ImageInfoData &info, RawImage **image)
memcpy((*image)->data(), &out[0], sizeof(uint16_t)*d); memcpy((*image)->data(), &out[0], sizeof(uint16_t)*d);
} }
QString shutterSpeed = QString::number(raw.imgdata.other.shutter); QString shutterSpeed = QString::number(raw->imgdata.other.shutter);
if(raw.imgdata.other.shutter < 1) if(raw->imgdata.other.shutter < 1)
{ {
shutterSpeed = QString("1/%1s").arg(1.0f/raw.imgdata.other.shutter); shutterSpeed = QString("1/%1s").arg(1.0f/raw->imgdata.other.shutter);
} }
//info.append(StringPair(QObject::tr("Width"), QString::number(rawImg->width))); //info.append(StringPair(QObject::tr("Width"), QString::number(rawImg->width)));
//info.append(StringPair(QObject::tr("Height"), QString::number(rawImg->height))); //info.append(StringPair(QObject::tr("Height"), QString::number(rawImg->height)));
info.info.append({QObject::tr("ISO"), QString::number(raw.imgdata.other.iso_speed)}); info.info.append({QObject::tr("ISO"), QString::number(raw->imgdata.other.iso_speed)});
info.info.append({QObject::tr("Shutter speed"), shutterSpeed}); info.info.append({QObject::tr("Shutter speed"), shutterSpeed});
#if LIBRAW_MINOR_VERSION>=19 #if LIBRAW_MINOR_VERSION>=19
// info.append(StringPair(QObject::tr("Camera temperature"), QString::number(raw.imgdata.other.CameraTemperature))); // info.append(StringPair(QObject::tr("Camera temperature"), QString::number(raw.imgdata.other.CameraTemperature)));
@@ -281,11 +286,26 @@ bool loadFITS(const QString path, ImageInfoData &info, RawImage **image)
return true; return true;
} }
#include "pcl/ICCProfileTransformation.h"
template<typename T, typename PCLtype, int CVtype> template<typename T, typename PCLtype, int CVtype>
bool loadPCLImage(pcl::XISFReader &xisf, RawImage **image) bool loadPCLImage(pcl::XISFReader &xisf, RawImage **image)
{ {
PCLtype pclImage; PCLtype pclImage;
xisf.ReadImage(pclImage); xisf.ReadImage(pclImage);
pclImage.Status().DisableInitialization();
#ifdef COLOR_MANAGMENT
pcl::ICCProfile iccProfile = xisf.ReadICCProfile();
if(iccProfile.IsProfile())
{
pcl::ICCProfileTransformation iccTran;
iccTran.DisableParallelProcessing();
iccTran.Add(iccProfile);
iccTran.Add(sRgbIccProfile);
iccTran >> pclImage;
}
#endif
int numChannels = pclImage.NumberOfChannels(); int numChannels = pclImage.NumberOfChannels();
cv::Mat cvImg[numChannels]; cv::Mat cvImg[numChannels];
@@ -315,6 +335,7 @@ bool loadXISF(const QString &path, ImageInfoData &info, RawImage **image)
{ {
try try
{ {
pcl::XISF::EnsurePTLUTInitialized();
pcl::String pclPath = path.utf16(); pcl::String pclPath = path.utf16();
pcl::XISFReader xisf; pcl::XISFReader xisf;
xisf.Open(pclPath); xisf.Open(pclPath);
@@ -384,6 +405,11 @@ void LoadRunable::run()
else else
{ {
QImage img(m_file); QImage img(m_file);
#ifdef COLOR_MANAGMENT
if(img.colorSpace().isValid() && img.colorSpace() != QColorSpace::SRgb)
img.convertToColorSpace(QColorSpace::SRgb);
#endif
ExifData *exif = exif_data_new_from_file(m_file.toLocal8Bit().constData()); ExifData *exif = exif_data_new_from_file(m_file.toLocal8Bit().constData());
info.info.append({QObject::tr("Width"), QString::number(img.width())}); info.info.append({QObject::tr("Width"), QString::number(img.width())});
info.info.append({QObject::tr("Height"), QString::number(img.height())}); info.info.append({QObject::tr("Height"), QString::number(img.height())});
@@ -515,9 +541,10 @@ bool readXISFHeader(const QString &path, ImageInfoData &info)
return true; return true;
} }
ConvertRunable::ConvertRunable(const QString &in, const QString &out) : ConvertRunable::ConvertRunable(const QString &in, const QString &out, const QString &format) :
m_infile(in), m_infile(in),
m_outfile(out) m_outfile(out),
m_format(format)
{ {
} }
@@ -622,7 +649,7 @@ void ConvertRunable::run()
if(rawimage) if(rawimage)
{ {
if(m_outfile.endsWith(".XISF", Qt::CaseInsensitive)) if(m_format == "XISF")
{ {
pcl::XISFOptions options; pcl::XISFOptions options;
pcl::FITSKeywordArray fitskeywords; pcl::FITSKeywordArray fitskeywords;
@@ -649,7 +676,7 @@ void ConvertRunable::run()
} }
if(m_outfile.endsWith(".FITS", Qt::CaseInsensitive) || m_outfile.endsWith(".FIT", Qt::CaseInsensitive)) if(m_format == "FITS")
{ {
int status = 0; int status = 0;
fitsfile *fw; fitsfile *fw;
+2 -1
View File
@@ -25,8 +25,9 @@ class ConvertRunable : public QRunnable
{ {
QString m_infile; QString m_infile;
QString m_outfile; QString m_outfile;
QString m_format;
public: public:
ConvertRunable(const QString &in, const QString &out); ConvertRunable(const QString &in, const QString &out, const QString &format);
void run() override; void run() override;
}; };
+1 -1
View File
@@ -20,7 +20,7 @@ int main(int argc, char *argv[])
QApplication a(argc, argv); QApplication a(argc, argv);
a.setOrganizationName("nou"); a.setOrganizationName("nou");
a.setApplicationName("Tenmon"); a.setApplicationName("Tenmon");
a.setWindowIcon(QIcon(":/org.nou.tenmon.png")); a.setWindowIcon(QIcon(":/space.nouspiro.tenmon.png"));
QTranslator translator; QTranslator translator;
QTranslator translator2; QTranslator translator2;
+132 -32
View File
@@ -13,13 +13,16 @@
#include <signal.h> #include <signal.h>
#include <unistd.h> #include <unistd.h>
#include <QSettings> #include <QSettings>
#include <QCoreApplication> #include <QGuiApplication>
#include <QThreadPool> #include <QThreadPool>
#include <QStatusBar> #include <QStatusBar>
#include <QImageReader>
#include <QMimeDatabase>
#include "loadrunable.h" #include "loadrunable.h"
#include "markedfiles.h" #include "markedfiles.h"
#include "about.h" #include "about.h"
#include "statusbar.h" #include "statusbar.h"
#include "settingsdialog.h"
#ifdef __linux__ #ifdef __linux__
#include <sys/ioctl.h> #include <sys/ioctl.h>
@@ -27,6 +30,8 @@
#include <sys/socket.h> #include <sys/socket.h>
#endif #endif
bool moveToTrash(const QString &path);
int MainWindow::socketPair[2] = {0, 0}; int MainWindow::socketPair[2] = {0, 0};
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
@@ -34,6 +39,27 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
qRegisterMetaType<ImageInfoData>("ImageInfoData"); qRegisterMetaType<ImageInfoData>("ImageInfoData");
qRegisterMetaType<RawImage*>("RawImage"); qRegisterMetaType<RawImage*>("RawImage");
SettingsDialog::loadSettings();
QStringList nameFilter;
_saveFilter = tr("FITS (*.fits *.fit);;XISF (*.xisf);;");
_openFilter = tr("Images (");
QMimeDatabase db;
auto supportedFormats = QImageReader::supportedMimeTypes();
QStringList filters;
for(auto format : supportedFormats)
{
QMimeType mimeType = db.mimeTypeForName(format);
_saveFilter.append(mimeType.filterString() + ";;");
_openFilter.append("*.");
_openFilter.append(mimeType.suffixes().join(" *."));
_openFilter.append(" ");
nameFilter.append(mimeType.suffixes());
}
_openFilter.append("*.fit *.fits *.xisf *.cr2 *.nef *.dng)");
_openFilter.append(tr(";;All files (*)"));
nameFilter.append({"fit", "fits", "xisf", "cr2", "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);
infoDock->setWidget(m_info); infoDock->setWidget(m_info);
@@ -59,7 +85,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
connect(m_stretchPanel, &StretchToolbar::invert, m_imageGL->imageWidget(), &ImageWidget::invert); connect(m_stretchPanel, &StretchToolbar::invert, m_imageGL->imageWidget(), &ImageWidget::invert);
connect(m_stretchPanel, &StretchToolbar::superPixel, m_imageGL->imageWidget(), &ImageWidget::superPixel); connect(m_stretchPanel, &StretchToolbar::superPixel, m_imageGL->imageWidget(), &ImageWidget::superPixel);
m_ringList = new ImageRingList(m_database, this); m_ringList = new ImageRingList(m_database, nameFilter, this);
m_filesystem = new FilesystemWidget(m_ringList, this); m_filesystem = new FilesystemWidget(m_ringList, this);
connect(m_filesystem, SIGNAL(fileSelected(int)), this, SLOT(loadFile(int))); connect(m_filesystem, SIGNAL(fileSelected(int)), this, SLOT(loadFile(int)));
@@ -105,8 +131,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
fileMenu->addAction(tr("Open"), this, SLOT(loadFile()), QKeySequence::Open); fileMenu->addAction(tr("Open"), this, SLOT(loadFile()), QKeySequence::Open);
fileMenu->addAction(tr("Save as"), this, SLOT(saveAs()), QKeySequence::Save); fileMenu->addAction(tr("Save as"), this, SLOT(saveAs()), QKeySequence::Save);
fileMenu->addSeparator(); fileMenu->addSeparator();
fileMenu->addAction(tr("Copy marked files"), this, SLOT(copyMarked())); fileMenu->addAction(tr("Copy marked files"), this, SLOT(copyMarked()), Qt::Key_F5);
fileMenu->addAction(tr("Move marked files"), this, SLOT(moveMarked())); fileMenu->addAction(tr("Move marked files"), this, SLOT(moveMarked()), Qt::Key_F6);
fileMenu->addAction(tr("Move marked files to trash"), this, &MainWindow::deleteMarked, QKeySequence::Delete);
fileMenu->addSeparator(); fileMenu->addSeparator();
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()));
@@ -117,6 +144,10 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
exitAction->setShortcut(QKeySequence::Quit); exitAction->setShortcut(QKeySequence::Quit);
menuBar()->addMenu(fileMenu); menuBar()->addMenu(fileMenu);
QMenu *editMenu = new QMenu(tr("Edit"), this);
editMenu->addAction(tr("Settings"), this, &MainWindow::showSettingsDialog);
menuBar()->addMenu(editMenu);
QMenu *viewMenu = new QMenu(tr("View"), this); QMenu *viewMenu = new QMenu(tr("View"), this);
viewMenu->addAction(tr("Zoom In"), m_imageGL, SLOT(zoomIn()), QKeySequence::ZoomIn); viewMenu->addAction(tr("Zoom In"), m_imageGL, SLOT(zoomIn()), QKeySequence::ZoomIn);
viewMenu->addAction(tr("Zoom Out"), m_imageGL, SLOT(zoomOut()), QKeySequence::ZoomOut); viewMenu->addAction(tr("Zoom Out"), m_imageGL, SLOT(zoomOut()), QKeySequence::ZoomOut);
@@ -134,7 +165,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
menuBar()->addMenu(viewMenu); menuBar()->addMenu(viewMenu);
QMenu *selectMenu = new QMenu(tr("Select"), this); QMenu *selectMenu = new QMenu(tr("Select"), this);
selectMenu->addAction(tr("Mark"), this, SLOT(markImage()), Qt::Key_F5); selectMenu->addAction(tr("Mark"), this, SLOT(markImage()), Qt::Key_F7);
selectMenu->addAction(tr("Unmark"), this, SLOT(unmarkImage()), Qt::Key_F8); selectMenu->addAction(tr("Unmark"), this, SLOT(unmarkImage()), Qt::Key_F8);
selectMenu->addSeparator(); selectMenu->addSeparator();
selectMenu->addAction(tr("Mark and next"), this, SLOT(markAndNext()), Qt::Key_M); selectMenu->addAction(tr("Mark and next"), this, SLOT(markAndNext()), Qt::Key_M);
@@ -190,7 +221,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
_lastDir = standardLocations.first(); _lastDir = standardLocations.first();
_lastDir = settings.value("mainwindow/lastdir", _lastDir).toString(); _lastDir = settings.value("mainwindow/lastdir", _lastDir).toString();
m_filesystem->setDir(_lastDir);
QStringList args = QCoreApplication::arguments(); QStringList args = QCoreApplication::arguments();
args.removeFirst(); args.removeFirst();
@@ -208,6 +238,16 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
} }
m_imageGL->setFocus(); m_imageGL->setFocus();
// workaround for nasty wayland backend bug https://bugreports.qt.io/browse/QTBUG-87332
if(static_cast<QGuiApplication*>(QCoreApplication::instance())->platformName() == "wayland")
{
infoDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable);
filesystemDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable);
databaseViewDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable);
filetreeDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable);
m_stretchPanel->setFloatable(false);
}
} }
MainWindow::~MainWindow() MainWindow::~MainWindow()
@@ -277,7 +317,10 @@ void MainWindow::closeEvent(QCloseEvent *event)
void MainWindow::copyOrMove(bool copy) void MainWindow::copyOrMove(bool copy)
{ {
QString dest = QFileDialog::getExistingDirectory(this, tr("Select destination"), _lastDir); QString dest = QFileDialog::getExistingDirectory(this,
tr("Select destination"),
_lastDir,
QFileDialog::ShowDirsOnly);
copyOrMove(copy, dest); copyOrMove(copy, dest);
} }
@@ -291,8 +334,9 @@ void MainWindow::copyOrMove(bool copy, const QString &dest)
QProgressDialog progress(copy ? tr("Copying") : tr("Moving"), tr("Cancel"), 0, files.size(), this); QProgressDialog progress(copy ? tr("Copying") : tr("Moving"), tr("Cancel"), 0, files.size(), this);
progress.setWindowModality(Qt::WindowModal); progress.setWindowModality(Qt::WindowModal);
progress.show(); progress.show();
foreach(const QString &file, files) for(const QString &file : files)
{ {
bool result = false;
QFileInfo info(file); QFileInfo info(file);
QFile srcFile(file); QFile srcFile(file);
QFile dstFile(dir.absoluteFilePath(info.fileName())); QFile dstFile(dir.absoluteFilePath(info.fileName()));
@@ -301,7 +345,7 @@ void MainWindow::copyOrMove(bool copy, const QString &dest)
continue; continue;
if(progress.wasCanceled()) if(progress.wasCanceled())
break; return;
#ifdef __linux__ #ifdef __linux__
if(copy) if(copy)
{ {
@@ -311,20 +355,30 @@ void MainWindow::copyOrMove(bool copy, const QString &dest)
{ {
dstFile.remove(); dstFile.remove();
dstFile.close(); dstFile.close();
qDebug() << dstFile.fileName(); result = srcFile.copy(dstFile.fileName());
srcFile.copy(dstFile.fileName());
} }
else result = true;
} }
else else
{ {
srcFile.rename(dstFile.fileName()); result = srcFile.rename(dstFile.fileName());
} }
#else #else
if(copy) if(copy)
srcFile.copy(dstFile.fileName()); result = srcFile.copy(dstFile.fileName());
else else
srcFile.rename(dstFile.fileName()); result = srcFile.rename(dstFile.fileName());
#endif #endif
if(!result)
{
QString t = copy ? tr("Failed to copy") : tr("Failed to move");
QString m = copy ? tr("Failed to copy from %1 to %2") : tr("Failed to move from %1 to %2");
QMessageBox::StandardButton button = QMessageBox::warning(this, t,
m.arg(srcFile.fileName()).arg(dir.absolutePath()),
QMessageBox::Ignore | QMessageBox::Abort);
qDebug() << button;
if(button == QMessageBox::Abort)return;
}
progress.setValue(i++); progress.setValue(i++);
} }
m_database->clearMarkedFiles(); m_database->clearMarkedFiles();
@@ -350,7 +404,10 @@ void MainWindow::pixmapLoaded(Image *image)
void MainWindow::loadFile() void MainWindow::loadFile()
{ {
QString file = QFileDialog::getOpenFileName(this, tr("Open file"), _lastDir, tr("Images (*.jpg *.jpeg *.png *.cr2 *.nef *.dng *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.NEF *.DNG *.FIT *.FITS *.XISF)")); QString file = QFileDialog::getOpenFileName(this,
tr("Open file"),
_lastDir,
_openFilter);
loadFile(file); loadFile(file);
} }
@@ -367,7 +424,6 @@ void MainWindow::loadFile(const QString &path)
_lastDir = info.canonicalPath(); _lastDir = info.canonicalPath();
QSettings settings; QSettings settings;
settings.setValue("mainwindow/lastdir", _lastDir); settings.setValue("mainwindow/lastdir", _lastDir);
m_filesystem->setDir(_lastDir);
} }
} }
@@ -378,7 +434,7 @@ void MainWindow::loadFile(int row)
void MainWindow::indexDir() void MainWindow::indexDir()
{ {
QString dir = QFileDialog::getExistingDirectory(this, tr("Index directory"), _lastDir); QString dir = QFileDialog::getExistingDirectory(this, tr("Index directory"), _lastDir, QFileDialog::ShowDirsOnly);
indexDir(dir); indexDir(dir);
} }
@@ -402,35 +458,45 @@ void MainWindow::reindex()
void MainWindow::saveAs() void MainWindow::saveAs()
{ {
QString selectedFilter; QString selectedFilter;
QString file = QFileDialog::getSaveFileName(this, tr("Save as"), _lastDir, tr("JPEG (*.jpg *.JPG);; PNG (*.png *.PNG);;FITS (*.fits *.FITS);;XISF (*.xisf *.XISF)"), &selectedFilter); QString file = QFileDialog::getSaveFileName(this,
tr("Save as"),
_lastDir,
_saveFilter,
&selectedFilter);
auto filterToFormat = [](const QString &file, const QString &filter) -> const char*
{
QString suffix = QFileInfo(file).suffix();
if(!suffix.compare("jpg", Qt::CaseInsensitive) || !suffix.compare("jpeg", Qt::CaseInsensitive))return "JPEG";
if(!suffix.compare("png", Qt::CaseInsensitive))return "PNG";
if(!suffix.compare("fits", Qt::CaseInsensitive) || !suffix.compare("fit", Qt::CaseInsensitive))return "FITS";
if(!suffix.compare("xisf", Qt::CaseInsensitive))return "XISF";
if(filter.contains("png"))return "PNG";
if(filter.contains("fits"))return "FITS";
if(filter.contains("xisf"))return "XISF";
return "JPEG";
};
if(!file.isEmpty()) if(!file.isEmpty())
{ {
QFileInfo info(file); QString format = filterToFormat(file, selectedFilter);
if(info.suffix().isEmpty())
{
if(selectedFilter.contains("jpg"))file += ".jpg";
if(selectedFilter.contains("png"))file += ".png";
if(selectedFilter.contains("fits"))file += ".fits";
if(selectedFilter.contains("xisf"))file += ".xisf";
}
if(file.endsWith(".fits") || file.endsWith(".xisf")) if(format == "FITS" || format == "XISF")
{ {
convert(file); convert(file, format);
} }
else else
{ {
QImage img = m_imageGL->imageWidget()->renderToImage(); QImage img = m_imageGL->imageWidget()->renderToImage();
if(!img.isNull()) if(!img.isNull())
img.save(file); img.save(file, filterToFormat(file, selectedFilter));
} }
} }
} }
void MainWindow::convert(const QString &outfile) void MainWindow::convert(const QString &outfile, const QString &format)
{ {
QString file = m_ringList->currentImage()->name(); QString file = m_ringList->currentImage()->name();
QThreadPool::globalInstance()->start(new ConvertRunable(file, outfile)); QThreadPool::globalInstance()->start(new ConvertRunable(file, outfile, format));
} }
void MainWindow::markImage() void MainWindow::markImage()
@@ -489,6 +555,34 @@ void MainWindow::moveMarked()
copyOrMove(false); copyOrMove(false);
} }
void MainWindow::deleteMarked()
{
QStringList files = m_database->getMarkedFiles();
if(QMessageBox::question(this, tr("Move files to trash?"), tr("Do you want to move %1 files to trash?").arg(files.size())) != QMessageBox::Yes)
return;
QProgressDialog progress(tr("Moving marked files to trash"), tr("Cancel"), 0, files.size(), this);
progress.setWindowModality(Qt::WindowModal);
progress.show();
int i = 0;
for(const QString &file : files)
{
if(!QFile::exists(file))
continue;
if(progress.wasCanceled())
return;
if(!moveToTrash(file))
{
QMessageBox::warning(this, tr("Failed to move file to trash"), tr("Failed to move file to trash %1").arg(file));
return;
}
progress.setValue(i++);
}
m_database->clearMarkedFiles();
}
void MainWindow::toggleFullScreen() void MainWindow::toggleFullScreen()
{ {
if(isFullScreen()) if(isFullScreen())
@@ -525,10 +619,16 @@ void MainWindow::starFinder(bool findStars)
void MainWindow::showMarkFilesDialog() void MainWindow::showMarkFilesDialog()
{ {
MarkedFiles markedFiles; MarkedFiles markedFiles(this);
markedFiles.exec(); markedFiles.exec();
} }
void MainWindow::showSettingsDialog()
{
SettingsDialog settingsDialog(this);
settingsDialog.exec();
}
void MainWindow::updateWindowTitle() void MainWindow::updateWindowTitle()
{ {
ImagePtr ptr = m_ringList->currentImage(); ImagePtr ptr = m_ringList->currentImage();
+5 -1
View File
@@ -28,6 +28,8 @@ class MainWindow : public QMainWindow
QSocketNotifier *socketNotifier; QSocketNotifier *socketNotifier;
QString _lastDir; QString _lastDir;
bool _maximized; bool _maximized;
QString _openFilter;
QString _saveFilter;
public: public:
MainWindow(QWidget *parent = 0); MainWindow(QWidget *parent = 0);
~MainWindow() override; ~MainWindow() override;
@@ -50,19 +52,21 @@ protected slots:
void indexDir(const QString &dir); void indexDir(const QString &dir);
void reindex(); void reindex();
void saveAs(); void saveAs();
void convert(const QString &outfile); void convert(const QString &outfile, const QString &format);
void markImage(); void markImage();
void unmarkImage(); void unmarkImage();
void markAndNext(); void markAndNext();
void unmarkAndNext(); void unmarkAndNext();
void copyMarked(); void copyMarked();
void moveMarked(); void moveMarked();
void deleteMarked();
void toggleFullScreen(); void toggleFullScreen();
void liveMode(bool active); void liveMode(bool active);
void imageStats(bool imageStats); void imageStats(bool imageStats);
void peakFinder(bool findPeaks); void peakFinder(bool findPeaks);
void starFinder(bool findStars); void starFinder(bool findStars);
void showMarkFilesDialog(); void showMarkFilesDialog();
void showSettingsDialog();
}; };
#endif // MAINWINDOW_H #endif // MAINWINDOW_H
+20
View File
@@ -1,5 +1,9 @@
#include "rawimage.h" #include "rawimage.h"
int THUMB_SIZE = 128;
int THUMB_SIZE_BORDER = 138;
int THUMB_SIZE_BORDER_Y = 158;
RawImage::ImgType CV2Type(int cvtype) RawImage::ImgType CV2Type(int cvtype)
{ {
switch (cvtype) switch (cvtype)
@@ -67,6 +71,7 @@ RawImage::RawImage(cv::Mat &img)
{ {
m_img = img; m_img = img;
m_stats = false; m_stats = false;
scaleToUnit();
} }
RawImage::RawImage(const RawImage &d) RawImage::RawImage(const RawImage &d)
@@ -378,3 +383,18 @@ bool RawImage::pixel(int x, int y, QVector3D &rgb) const
} }
return true; return true;
} }
void RawImage::scaleToUnit()
{
if(CV_MAT_DEPTH(m_img.type()) == CV_32F)
{
double min, max;
cv::minMaxIdx(m_img, &min, &max);
if(min < 0 || max > 1)
{
float scale = 1.0 / (max - min);
float zero = min * scale;
m_img = m_img * scale - zero;
}
}
}
+4 -3
View File
@@ -10,9 +10,9 @@
#include <QImage> #include <QImage>
#include <QVector3D> #include <QVector3D>
const int THUMB_SIZE = 128; extern int THUMB_SIZE;
const int THUMB_SIZE_BORDER = 138; extern int THUMB_SIZE_BORDER;
const int THUMB_SIZE_BORDER_Y = 158; extern int THUMB_SIZE_BORDER_Y;
class Peak class Peak
{ {
@@ -84,6 +84,7 @@ public:
float thumbAspect() const; float thumbAspect() const;
const cv::Mat& mat() const; const cv::Mat& mat() const;
bool pixel(int x, int y, QVector3D &rgb) const; bool pixel(int x, int y, QVector3D &rgb) const;
void scaleToUnit();
}; };
#endif // RAWIMAGE_H #endif // RAWIMAGE_H
+3 -1
View File
@@ -3,7 +3,7 @@
<file>invert.png</file> <file>invert.png</file>
<file>nuke.png</file> <file>nuke.png</file>
<file>bayer.png</file> <file>bayer.png</file>
<file>org.nou.tenmon.png</file> <file>space.nouspiro.tenmon.png</file>
<file>nuke_a.png</file> <file>nuke_a.png</file>
<file>about/tenmon</file> <file>about/tenmon</file>
<file>about/pcl</file> <file>about/pcl</file>
@@ -16,6 +16,8 @@
<file>shaders/image.vert</file> <file>shaders/image.vert</file>
<file>shaders/thumb.frag</file> <file>shaders/thumb.frag</file>
<file>shaders/thumb.vert</file> <file>shaders/thumb.vert</file>
<file>shaders/debayer.frag</file>
<file>shaders/debayer.vert</file>
</qresource> </qresource>
<qresource lang="en" prefix="/"> <qresource lang="en" prefix="/">
<file alias="help">about/help_en</file> <file alias="help">about/help_en</file>
+85
View File
@@ -0,0 +1,85 @@
#include "settingsdialog.h"
#include <QFormLayout>
#include <QDialogButtonBox>
#include <QLabel>
#include <QSettings>
#include <QApplication>
#include "rawimage.h"
extern int DEFAULT_WIDTH;
class EvenNumber : public QSpinBox
{
public:
explicit EvenNumber(QWidget *parent) : QSpinBox(parent){}
protected:
QValidator::State validate(QString &text, int &) const
{
bool ok;
int val = text.toInt(&ok);
if(ok && (val & 1) == 0)return QValidator::Acceptable;
if(ok)return QValidator::Intermediate;
return QValidator::Invalid;
}
void fixup(QString &input) const
{
bool ok;
int val = input.toInt(&ok);
val -= val & 1;
input = QString::number(val);
}
};
SettingsDialog::SettingsDialog(QWidget *parent) : QDialog(parent)
{
QFormLayout *layout = new QFormLayout(this);
setWindowTitle(tr("Settings"));
QSettings settings;
m_preloadImages = new QSpinBox(this);
m_preloadImages->setRange(0, 8);
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_thumSize = new EvenNumber(this);
m_thumSize->setRange(64, 512);
m_thumSize->setSingleStep(2);
m_thumSize->setValue(settings.value("settings/thumnailsize", THUMB_SIZE).toInt());
m_thumSize->setToolTip(tr("Thumbnail size in pixels"));
m_useNativeDialog = new QCheckBox(tr("Don't use native file dialog"), this);
m_useNativeDialog->setChecked(QApplication::testAttribute(Qt::AA_DontUseNativeDialogs));
layout->addRow(tr("Image preload count"), m_preloadImages);
layout->addRow(tr("Thumbnails size"), m_thumSize);
layout->addRow(m_useNativeDialog);
layout->addRow(new QLabel(tr("Changes in settings will take effect after program restart.")));
QDialogButtonBox *buttonBox = new QDialogButtonBox(this);
buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
connect(this, &QDialog::accepted, this, &SettingsDialog::saveSettings);
layout->addRow(buttonBox);
}
void SettingsDialog::loadSettings()
{
QSettings settings;
THUMB_SIZE = settings.value("settings/thumbnailsize", THUMB_SIZE).toInt();
THUMB_SIZE_BORDER = THUMB_SIZE + 10;
THUMB_SIZE_BORDER_Y = THUMB_SIZE + 30;
DEFAULT_WIDTH = settings.value("settings/preloadimagecount", DEFAULT_WIDTH).toInt();
QApplication::setAttribute(Qt::AA_DontUseNativeDialogs, settings.value("settings/dontusenativedialogs", false).toBool());
}
void SettingsDialog::saveSettings()
{
QSettings settings;
settings.setValue("settings/thumbnailsize", m_thumSize->value());
settings.setValue("settings/preloadimagecount", m_preloadImages->value());
settings.setValue("settings/dontusenativedialogs", m_useNativeDialog->isChecked());
QApplication::setAttribute(Qt::AA_DontUseNativeDialogs, m_useNativeDialog->isChecked());
}
+22
View File
@@ -0,0 +1,22 @@
#ifndef SETTINGSDIALOG_H
#define SETTINGSDIALOG_H
#include <QDialog>
#include <QSpinBox>
#include <QCheckBox>
class SettingsDialog : public QDialog
{
Q_OBJECT
public:
explicit SettingsDialog(QWidget *parent = nullptr);
static void loadSettings();
private:
void saveSettings();
QSpinBox *m_preloadImages;
QSpinBox *m_thumSize;
QCheckBox *m_useNativeDialog;
};
#endif // SETTINGSDIALOG_H
+41
View File
@@ -0,0 +1,41 @@
#version 330
uniform sampler2D qt_Texture0;
in vec2 qt_TexCoord0;
in vec2 center;
out vec4 color;
#define f(x, y) texelFetch(qt_Texture0, icenter + ivec2(x, y), 0).r
void main(void)
{
ivec2 texSize = textureSize(qt_Texture0, 0);
ivec2 icenter = ivec2(center);
ivec2 alternate = icenter % 2;
// cross, checker, theta, phi
const vec4 kA = vec4(-1.0, -1.5, 0.5, -1.0) / 8.0;
const vec4 kB = vec4( 2.0, 0.0, 0.0, 4.0) / 8.0;
const vec4 kC = vec4( 4.0, 6.0, 5.0, 5.0) / 8.0;
const vec4 kD = vec4( 0.0, 2.0, -1.0, -1.0) / 8.0;
const vec4 kE = vec4(-1.0, -1.5, -1.0, 0.5) / 8.0;
const vec4 kF = vec4( 2.0, 0.0, 4.0, 0.0) / 8.0;
float A = f(0,2) + f(0,-2);
float B = f(0,1) + f(0,-1);
float C = f(0,0);
float D = f(1,1) + f(-1,1) + f(1,-1) + f(-1,-1);
float E = f(2,0) + f(-2,0);
float F = f(1,0) + f(-1,0);
vec4 P = kA*A + kB*B + kC*C + kD*D + kE*E + kF*F;
color.rgb = alternate.y == 0 ?
(alternate.x == 0 ? vec3(C, P.xy) : // even row even col
vec3(P.z, C, P.w)) : // even row odd col
(alternate.x == 0 ? vec3(P.w, C, P.z) : // odd row even col
vec3(P.yx, C)); // odd row odd col
color.a = 1.0;
}
+16
View File
@@ -0,0 +1,16 @@
#version 330
uniform sampler2D qt_Texture0;
in vec2 qt_Vertex;
in vec2 qt_MultiTexCoord0;
out vec2 qt_TexCoord0;
out vec2 center;
void main(void)
{
vec2 texSize = vec2(textureSize(qt_Texture0, 0));
gl_Position = vec4(qt_Vertex, 0.0, 1.0);
qt_TexCoord0 = qt_MultiTexCoord0;
qt_TexCoord0.y = 1.0 - qt_TexCoord0.y;
center = qt_TexCoord0 * texSize.xy;
}
+19 -1
View File
@@ -4,9 +4,17 @@ uniform sampler2D qt_Texture0;
uniform vec3 mtf_param; uniform vec3 mtf_param;
uniform bool bw; uniform bool bw;
uniform bool invert; uniform bool invert;
uniform bool srgb;
in vec2 qt_TexCoord0; in vec2 qt_TexCoord0;
out vec4 color; out vec4 color;
vec3 Linear2sRGB(vec3 color)
{
return mix(12.92 * color.rgb,
1.055 * pow(color, vec3(1.0 / 2.4)) - 0.055,
greaterThan(color, vec3(0.0031308)));
}
vec4 MTF(vec4 x, vec3 m) vec4 MTF(vec4 x, vec3 m)
{ {
x = (x - m.x) / (m.z - m.x); x = (x - m.x) / (m.z - m.x);
@@ -14,13 +22,23 @@ vec4 MTF(vec4 x, vec3 m)
return ((m.y - 1) * x) / ((2 * m.y - 1) * x - m.y); return ((m.y - 1) * x) / ((2 * m.y - 1) * x - m.y);
} }
vec3 checker()
{
vec2 pattern = fract(gl_FragCoord.xy * 0.0625) - 0.5;
return vec3(step(pattern.x * pattern.y, 0.0) * 0.25 + 0.25);
}
void main(void) void main(void)
{ {
color = texture(qt_Texture0, qt_TexCoord0); color = texture(qt_Texture0, qt_TexCoord0);
if(bw)color = color.rrra; if(bw)color = color.rrra;
color = MTF(color, mtf_param); color = MTF(color, mtf_param);
if(invert)color = vec4(1.0) - color; if(invert)color.rgb = vec3(1.0) - color.rgb;
color.rgb = mix(checker(), color.rgb, color.a);
if(srgb)color.rgb = Linear2sRGB(color.rgb);
if(any(lessThan(qt_TexCoord0, vec2(0.0))) || any(greaterThan(qt_TexCoord0, vec2(1.0)))) if(any(lessThan(qt_TexCoord0, vec2(0.0))) || any(greaterThan(qt_TexCoord0, vec2(1.0))))
color = vec4(0.0, 0.0, 0.0, 1.0); color = vec4(0.0, 0.0, 0.0, 1.0);
+3 -2
View File
@@ -7,13 +7,14 @@ out vec3 qt_TexCoord0;
uniform ivec3 viewport_row; uniform ivec3 viewport_row;
uniform mat4 mvp; uniform mat4 mvp;
uniform vec2 offset; uniform vec2 offset;
uniform ivec3 thumb_size;
void main(void) void main(void)
{ {
vec2 pos = qt_Vertex * 0.5; vec2 pos = qt_Vertex * 0.5;
pos.y *= -1.0; pos.y *= -1.0;
pos = pos * imageSize_num.xy + 69; pos = pos * imageSize_num.xy + thumb_size.x;
ivec2 off = ivec2(imageSize_num.z % viewport_row.z, imageSize_num.z / viewport_row.z) * ivec2(138, 158); ivec2 off = ivec2(imageSize_num.z % viewport_row.z, imageSize_num.z / viewport_row.z) * thumb_size.yz;
gl_Position = mvp * vec4(pos - offset + off, 0.0, 1.0); gl_Position = mvp * vec4(pos - offset + off, 0.0, 1.0);
qt_TexCoord0 = vec3(qt_MultiTexCoord0, imageSize_num.z + 0.1); qt_TexCoord0 = vec3(qt_MultiTexCoord0, imageSize_num.z + 0.1);
@@ -1,9 +1,9 @@
[Desktop Entry] [Desktop Entry]
Type=Application Type=Application
Exec=tenmon %U Exec=tenmon %U
Icon=org.nou.tenmon Icon=space.nouspiro.tenmon
Comment=FITS Image viewer Comment=FITS Image viewer
Name=Tenmon Name=Tenmon
Categories=Graphics;2DGraphics;RasterGraphics;Viewer; Categories=Graphics;2DGraphics;RasterGraphics;Viewer;Science;Astronomy
MimeType=image/fits;image/x-xisf; MimeType=image/fits;image/x-xisf;
Terminal=false Terminal=false
+68
View File
@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop">
<id>space.nouspiro.tenmon</id>
<launchable type="desktop-id">space.nouspiro.tenmon.desktop</launchable>
<name>Tenmon</name>
<summary>FITS/XISF image viewer, converter, index and search</summary>
<metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-3.0</project_license>
<description>
<p>It is intended primarily for viewing astro photos and images. It supports the following formats:</p>
<ul>
<li>FITS 8, 16 bit integer and 32 bit float</li>
<li>XISF 8, 16 bit integer and 32 bit float</li>
<li>RAW CR2, DNG, NEF</li>
<li>JPEG, PNG, BMP, GIF, PBM, PGM, PPM and SVG images</li>
</ul>
<p>Features of application:</p>
<ul>
<li>Using same stretch function as PixInsight</li>
<li>OpenGL accelerated drawing</li>
<li>Index and search FITS XISF header data</li>
<li>Quick mark images and then copy/move marked files</li>
<li>Convert FITS &lt;-&gt; XISF</li>
<li>Convert FITS/XISF -&gt; JPEG/PNG</li>
<li>Image statistics mean, media, min, max</li>
<li>Support for WCS</li>
<li>Thumbnails</li>
<li>Convert CFA images to colour - debayer</li>
<li>Color space aware</li>
</ul>
</description>
<categories>
<category>Graphics</category>
<category>Viewer</category>
<category>Science</category>
<category>Astronomy</category>
</categories>
<keywords>
<keyword>astronomy</keyword>
</keywords>
<url type="homepage">https://nouspiro.space/?page_id=206</url>
<url type="bugtracker">https://github.com/flathub/space.nouspiro.tenmon/issues</url>
<screenshots>
<screenshot type="default">
<image>https://nouspiro.space/wp-content/uploads/2022/04/tenmon-1024x579.png</image>
<image>https://nouspiro.space/wp-content/uploads/2022/12/tenmon2-1024x645.png</image>
</screenshot>
</screenshots>
<content_rating type="oars-1.1"/>
<releases>
<release version="20221215" date="2022-12-15">
<description>
<ul>
<li>Add debayer support</li>
<li>Move to trash action</li>
<li>Change copy and move shortcuts to F5 and F6</li>
<li>Change mark and unmark key shortcuts to F7 and F8</li>
<li>Fix not refreshing file tree</li>
<li>Add option to not use native file dialog</li>
</ul>
</description>
</release>
<release version="20221209" date="2022-12-09"/>
<release version="20221126" date="2022-11-26"/>
<release version="20221121" date="2022-11-11"/>
<release version="20221023" date="2022-10-23"/>
</releases>
</component>

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

+65
View File
@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="128"
height="128"
viewBox="0 0 33.866666 33.866668"
version="1.1"
id="svg5"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
sodipodi:docname="space.nouspiro.tenmon.svg"
inkscape:export-filename="/home/nou/c++/tenmon/space.nouspiro.tenmon_128.png"
inkscape:export-xdpi="96.000008"
inkscape:export-ydpi="96.000008"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="false"
units="px"
width="128px"
inkscape:zoom="2.6547419"
inkscape:cx="54.430903"
inkscape:cy="78.162024"
inkscape:window-width="1862"
inkscape:window-height="1136"
inkscape:window-x="58"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs2" />
<g
inkscape:label="Vrstva 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#000000;stroke:#ffffff;stroke-width:0;stroke-linejoin:round"
id="rect1196"
width="33.866665"
height="33.866665"
x="5e-07"
y="5e-07" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:17.276px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.359917"
x="-0.41414261"
y="23.331123"
id="text8592"><tspan
sodipodi:role="line"
id="tspan8590"
style="fill:#ffffff;stroke-width:0.359917"
x="-0.41414261"
y="23.331123">天文</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

+1 -1
View File
@@ -34,7 +34,7 @@ StretchToolbar::StretchToolbar(QWidget *parent) : QToolBar(tr("Stretch toolbar")
invertButton->setCheckable(true); invertButton->setCheckable(true);
connect(invertButton, SIGNAL(toggled(bool)), this, SIGNAL(invert(bool))); connect(invertButton, SIGNAL(toggled(bool)), this, SIGNAL(invert(bool)));
QAction *superPixelButton = addAction(QIcon(":/bayer.png"), tr("Superpixel CFA draw 2x2 pixel as one")); QAction *superPixelButton = addAction(QIcon(":/bayer.png"), tr("Debayer CFA"));
superPixelButton->setCheckable(true); superPixelButton->setCheckable(true);
connect(superPixelButton, SIGNAL(toggled(bool)), this, SIGNAL(superPixel(bool))); connect(superPixelButton, SIGNAL(toggled(bool)), this, SIGNAL(superPixel(bool)));
-9
View File
@@ -1,9 +0,0 @@
[Desktop Entry]
Type=Application
Exec=Tenmon %U
Icon=org.nou.tenmon
Comment=FITS Image viewer
Name=Tenmon
Categories=Graphics;2DGraphics;RasterGraphics;Viewer;
MimeType=image/fits;image/x-xisf;
Terminal=false
Binary file not shown.
+101 -18
View File
@@ -71,6 +71,10 @@
<source>Go up</source> <source>Go up</source>
<translation>Go up</translation> <translation>Go up</translation>
</message> </message>
<message>
<source>Show hidden files</source>
<translation>Show hidden files</translation>
</message>
</context> </context>
<context> <context>
<name>HelpDialog</name> <name>HelpDialog</name>
@@ -150,10 +154,6 @@
<source>Filesystem</source> <source>Filesystem</source>
<translation>File system</translation> <translation>File system</translation>
</message> </message>
<message>
<source>FITS files database</source>
<translation type="vanished">FITS files database</translation>
</message>
<message> <message>
<source>Tenmon</source> <source>Tenmon</source>
<translation>Tenmon</translation> <translation>Tenmon</translation>
@@ -290,18 +290,10 @@
<source>Moving</source> <source>Moving</source>
<translation>Moving</translation> <translation>Moving</translation>
</message> </message>
<message>
<source>Images (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS *.XISF)</source>
<translation type="vanished">Images (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS *.XISF)</translation>
</message>
<message> <message>
<source>Indexing FITS files</source> <source>Indexing FITS files</source>
<translation>Indexing FITS files</translation> <translation>Indexing FITS files</translation>
</message> </message>
<message>
<source>JPEG (*.jpg *.JPG);; PNG (*.png *.PNG);;FITS (*.fits *.FITS);;XISF (*.xisf *.XISF)</source>
<translation>JPEG (*.jpg *.JPG);; PNG (*.png *.PNG);;FITS (*.fits *.FITS);;XISF (*.xisf *.XISF)</translation>
</message>
<message> <message>
<source>Reindex files</source> <source>Reindex files</source>
<translation>Reindex files</translation> <translation>Reindex files</translation>
@@ -319,8 +311,68 @@
<translation>Star finder</translation> <translation>Star finder</translation>
</message> </message>
<message> <message>
<source>Images (*.jpg *.jpeg *.png *.cr2 *.nef *.dng *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.NEF *.DNG *.FIT *.FITS *.XISF)</source> <source>Edit</source>
<translation>Images (*.jpg *.jpeg *.png *.cr2 *.nef *.dng *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.NEF *.DNG *.FIT *.FITS *.XISF)</translation> <translation>Edit</translation>
</message>
<message>
<source>FITS header editor</source>
<translation>FITS header editor</translation>
</message>
<message>
<source>Settings</source>
<translation>Settings</translation>
</message>
<message>
<source>Images (</source>
<translation>Images (</translation>
</message>
<message>
<source>FITS (*.fits *.fit);;XISF (*.xisf);;</source>
<translation>FITS image (*.fits *.fit);;XISF image (*.xisf);;</translation>
</message>
<message>
<source>Failed to copy</source>
<translation>Failed to copy</translation>
</message>
<message>
<source>Failed to move</source>
<translation>Failed to move</translation>
</message>
<message>
<source>Failed to move from %1 to %2</source>
<translation>Failed to move from %1 to %2</translation>
</message>
<message>
<source>Failed to copy from %1 to %2</source>
<translation>Failed to copy from %1 to %2</translation>
</message>
<message>
<source>;;All files (*)</source>
<translation>;;All files (*)</translation>
</message>
<message>
<source>Move files to trash?</source>
<translation>Move files to trash?</translation>
</message>
<message>
<source>Do you want to move %1 files to trash?</source>
<translation>Do you want to move %1 files to trash?</translation>
</message>
<message>
<source>Failed to move file to trash</source>
<translation>Failed to move file to trash</translation>
</message>
<message>
<source>Failed to move file to trash %1</source>
<translation>Failed to move file to trash %1</translation>
</message>
<message>
<source>Move marked files to trash</source>
<translation>Move marked files to trash</translation>
</message>
<message>
<source>Moving marked files to trash</source>
<translation>Moving marked files to trash</translation>
</message> </message>
</context> </context>
<context> <context>
@@ -420,6 +472,37 @@
<translation>Select columns</translation> <translation>Select columns</translation>
</message> </message>
</context> </context>
<context>
<name>SettingsDialog</name>
<message>
<source>Settings</source>
<translation>Settings</translation>
</message>
<message>
<source>How many images are preloaded before and after current image.</source>
<translation>How many images are preloaded before and after current image.</translation>
</message>
<message>
<source>Thumbnail size in pixels</source>
<translation>Thumbnail size in pixels</translation>
</message>
<message>
<source>Image preload count</source>
<translation>Image preload count</translation>
</message>
<message>
<source>Thumbnails size</source>
<translation>Thumbnails size</translation>
</message>
<message>
<source>Changes in settings will take effect after program restart.</source>
<translation>Changes in settings will take effect after program restart.</translation>
</message>
<message>
<source>Don&apos;t use native file dialog</source>
<translation>Don&apos;t use native file dialog</translation>
</message>
</context>
<context> <context>
<name>StretchToolbar</name> <name>StretchToolbar</name>
<message> <message>
@@ -438,13 +521,13 @@
<source>Invert colors</source> <source>Invert colors</source>
<translation>Invert colors</translation> <translation>Invert colors</translation>
</message> </message>
<message>
<source>Superpixel CFA draw 2x2 pixel as one</source>
<translation>Superpixel CFA draw 2x2 pixel as one</translation>
</message>
<message> <message>
<source>Apply auto stretch on load</source> <source>Apply auto stretch on load</source>
<translation>Apply auto stretch on load</translation> <translation>Apply auto stretch on load</translation>
</message> </message>
<message>
<source>Debayer CFA</source>
<translation>Debayer CFA</translation>
</message>
</context> </context>
</TS> </TS>
Binary file not shown.
+101 -18
View File
@@ -71,6 +71,10 @@
<source>Go up</source> <source>Go up</source>
<translation>Monter</translation> <translation>Monter</translation>
</message> </message>
<message>
<source>Show hidden files</source>
<translation>Afficher les fichiers cachés</translation>
</message>
</context> </context>
<context> <context>
<name>HelpDialog</name> <name>HelpDialog</name>
@@ -150,10 +154,6 @@
<source>Filesystem</source> <source>Filesystem</source>
<translation>Système de fichier</translation> <translation>Système de fichier</translation>
</message> </message>
<message>
<source>FITS files database</source>
<translation type="vanished">FITS files database</translation>
</message>
<message> <message>
<source>Tenmon</source> <source>Tenmon</source>
<translation>Tenmon</translation> <translation>Tenmon</translation>
@@ -290,18 +290,10 @@
<source>Moving</source> <source>Moving</source>
<translation>Déplacement</translation> <translation>Déplacement</translation>
</message> </message>
<message>
<source>Images (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS *.XISF)</source>
<translation type="vanished">Images (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS *.XISF)</translation>
</message>
<message> <message>
<source>Indexing FITS files</source> <source>Indexing FITS files</source>
<translation>Indexation des fichiers FITS</translation> <translation>Indexation des fichiers FITS</translation>
</message> </message>
<message>
<source>JPEG (*.jpg *.JPG);; PNG (*.png *.PNG);;FITS (*.fits *.FITS);;XISF (*.xisf *.XISF)</source>
<translation>JPEG (*.jpg *.JPG);; PNG (*.png *.PNG);;FITS (*.fits *.FITS);;XISF (*.xisf *.XISF)</translation>
</message>
<message> <message>
<source>Reindex files</source> <source>Reindex files</source>
<translation>-indexer les fichiers</translation> <translation>-indexer les fichiers</translation>
@@ -319,8 +311,68 @@
<translation>Détecteur d&apos;étoiles</translation> <translation>Détecteur d&apos;étoiles</translation>
</message> </message>
<message> <message>
<source>Images (*.jpg *.jpeg *.png *.cr2 *.nef *.dng *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.NEF *.DNG *.FIT *.FITS *.XISF)</source> <source>Edit</source>
<translation>Images (*.jpg *.jpeg *.png *.cr2 *.nef *.dng *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.NEF *.DNG *.FIT *.FITS *.XISF)</translation> <translation>Éditer</translation>
</message>
<message>
<source>FITS header editor</source>
<translation>Éditeur d&apos;en-tête FITS</translation>
</message>
<message>
<source>Settings</source>
<translation>Réglages</translation>
</message>
<message>
<source>Images (</source>
<translation>Images (</translation>
</message>
<message>
<source>FITS (*.fits *.fit);;XISF (*.xisf);;</source>
<translation>FITS image (*.fits *.fit);;XISF image (*.xisf);;</translation>
</message>
<message>
<source>Failed to copy</source>
<translation>Échec de la copie</translation>
</message>
<message>
<source>Failed to move</source>
<translation>Échec du déplacement</translation>
</message>
<message>
<source>Failed to move from %1 to %2</source>
<translation>Échec du déplacement de %1 vers %2</translation>
</message>
<message>
<source>Failed to copy from %1 to %2</source>
<translation>Échec de la copie de %1 vers %2</translation>
</message>
<message>
<source>;;All files (*)</source>
<translation>;;Tout les fichiers (*)</translation>
</message>
<message>
<source>Move files to trash?</source>
<translation>Déplacer les fichiers dans la corbeille?</translation>
</message>
<message>
<source>Do you want to move %1 files to trash?</source>
<translation>Voulez-vous déplacer le fichier %1 dans la corbeille?</translation>
</message>
<message>
<source>Failed to move file to trash</source>
<translation>Echec du déplacement dans la corbeille</translation>
</message>
<message>
<source>Failed to move file to trash %1</source>
<translation>Echec du déplacement de %1 dans la corbeille</translation>
</message>
<message>
<source>Move marked files to trash</source>
<translation>Déplacer les fichiers marqués dans la corbeille</translation>
</message>
<message>
<source>Moving marked files to trash</source>
<translation>Déplacement des fichiers marqués dans la corbeille</translation>
</message> </message>
</context> </context>
<context> <context>
@@ -420,6 +472,37 @@
<translation>Choix des colonnes</translation> <translation>Choix des colonnes</translation>
</message> </message>
</context> </context>
<context>
<name>SettingsDialog</name>
<message>
<source>Settings</source>
<translation>Réglages</translation>
</message>
<message>
<source>How many images are preloaded before and after current image.</source>
<translation>Combien d&apos;images sont préchargées avant et après l&apos;image courante.</translation>
</message>
<message>
<source>Thumbnail size in pixels</source>
<translation>Taille des vignettes en pixels</translation>
</message>
<message>
<source>Image preload count</source>
<translation>Nombre d&apos;images préchargées</translation>
</message>
<message>
<source>Thumbnails size</source>
<translation>Taille des vignette</translation>
</message>
<message>
<source>Changes in settings will take effect after program restart.</source>
<translation>Les changements de paramètres prendront effet après le redémarrage du programme.</translation>
</message>
<message>
<source>Don&apos;t use native file dialog</source>
<translation>N&apos;utilisez pas la boîte de dialogue de fichier natif</translation>
</message>
</context>
<context> <context>
<name>StretchToolbar</name> <name>StretchToolbar</name>
<message> <message>
@@ -438,13 +521,13 @@
<source>Invert colors</source> <source>Invert colors</source>
<translation>Inverser les couleurs</translation> <translation>Inverser les couleurs</translation>
</message> </message>
<message>
<source>Superpixel CFA draw 2x2 pixel as one</source>
<translation>Superpixel CFA dessine 2x2 pixels comme un seul</translation>
</message>
<message> <message>
<source>Apply auto stretch on load</source> <source>Apply auto stretch on load</source>
<translation>Appliquer la luminosité automatiquement au chargement</translation> <translation>Appliquer la luminosité automatiquement au chargement</translation>
</message> </message>
<message>
<source>Debayer CFA</source>
<translation>Débayeriser CFA</translation>
</message>
</context> </context>
</TS> </TS>
Binary file not shown.
+101 -30
View File
@@ -72,6 +72,10 @@
<source>Go up</source> <source>Go up</source>
<translation>O úroveň vyššie</translation> <translation>O úroveň vyššie</translation>
</message> </message>
<message>
<source>Show hidden files</source>
<translation>Zobraz skryté súbory</translation>
</message>
</context> </context>
<context> <context>
<name>HelpDialog</name> <name>HelpDialog</name>
@@ -151,14 +155,6 @@
<source>Filesystem</source> <source>Filesystem</source>
<translation>Zoznam súborov</translation> <translation>Zoznam súborov</translation>
</message> </message>
<message>
<source>FITS Editor</source>
<translation type="vanished">FITS editor</translation>
</message>
<message>
<source>FITS files database</source>
<translation type="vanished">Databáza FITS súborov</translation>
</message>
<message> <message>
<source>Tenmon</source> <source>Tenmon</source>
<translation>Tenmon</translation> <translation>Tenmon</translation>
@@ -243,14 +239,6 @@
<source>Open file</source> <source>Open file</source>
<translation>Otvoriť súbor</translation> <translation>Otvoriť súbor</translation>
</message> </message>
<message>
<source>Images (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS)</source>
<translation type="vanished">Obrázky (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS)</translation>
</message>
<message>
<source>Images (*.jpg *.png *.JPG *.PNG)</source>
<translation type="vanished">Obrázky (*.jpg *.png *.JPG *.PNG)</translation>
</message>
<message> <message>
<source>Select destination</source> <source>Select destination</source>
<translation>Vybrať cieľ</translation> <translation>Vybrať cieľ</translation>
@@ -303,18 +291,10 @@
<source>Moving</source> <source>Moving</source>
<translation>Presúvanie</translation> <translation>Presúvanie</translation>
</message> </message>
<message>
<source>Images (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS *.XISF)</source>
<translation type="vanished">Obrázky (*.jpg *.jpeg *.png *.cr2 *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.FIT *.FITS *.XISF)</translation>
</message>
<message> <message>
<source>Indexing FITS files</source> <source>Indexing FITS files</source>
<translation>Indexovanie FITS/XISF súborov</translation> <translation>Indexovanie FITS/XISF súborov</translation>
</message> </message>
<message>
<source>JPEG (*.jpg *.JPG);; PNG (*.png *.PNG);;FITS (*.fits *.FITS);;XISF (*.xisf *.XISF)</source>
<translation>JPEG (*.jpg *.JPG);; PNG (*.png *.PNG);;FITS (*.fits *.FITS);;XISF (*.xisf *.XISF)</translation>
</message>
<message> <message>
<source>Reindex files</source> <source>Reindex files</source>
<translation>Reindexuj súbory</translation> <translation>Reindexuj súbory</translation>
@@ -332,8 +312,68 @@
<translation>Vyhľadávač hviezd</translation> <translation>Vyhľadávač hviezd</translation>
</message> </message>
<message> <message>
<source>Images (*.jpg *.jpeg *.png *.cr2 *.nef *.dng *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.NEF *.DNG *.FIT *.FITS *.XISF)</source> <source>Edit</source>
<translation>Obrázky (*.jpg *.jpeg *.png *.cr2 *.nef *.dng *.fit *.fits *.xisf *.JPG *.JPEG *.PNG *.CR2 *.NEF *.DNG *.FIT *.FITS *.XISF)</translation> <translation>Upraviť</translation>
</message>
<message>
<source>FITS header editor</source>
<translation>Editor FITS hlavičky</translation>
</message>
<message>
<source>Settings</source>
<translation>Nastavenia</translation>
</message>
<message>
<source>Images (</source>
<translation>Obrázky (</translation>
</message>
<message>
<source>FITS (*.fits *.fit);;XISF (*.xisf);;</source>
<translation>Obrázok FITS (*.fits *.fit);;Obrázok XISF (*.xisf);;</translation>
</message>
<message>
<source>Failed to copy</source>
<translation>Zlyhalo kopírovanie</translation>
</message>
<message>
<source>Failed to move</source>
<translation>Zlyhalo presúvanie</translation>
</message>
<message>
<source>Failed to move from %1 to %2</source>
<translation>Zlyhalo presúvanie z %1 do %2</translation>
</message>
<message>
<source>Failed to copy from %1 to %2</source>
<translation>Zlyhalo kopírovanie z %1 do %2</translation>
</message>
<message>
<source>;;All files (*)</source>
<translation>;;Všetky súbory (*)</translation>
</message>
<message>
<source>Move files to trash?</source>
<translation>Presunúť súbory do koša?</translation>
</message>
<message>
<source>Do you want to move %1 files to trash?</source>
<translation>Presunúť %1 súborov do koša?</translation>
</message>
<message>
<source>Failed to move file to trash</source>
<translation>Zlyhalo presunutie súbora do koša</translation>
</message>
<message>
<source>Failed to move file to trash %1</source>
<translation>Zlyhalo presunutie súbora do koša %1</translation>
</message>
<message>
<source>Move marked files to trash</source>
<translation>Presunúť označené súbory do koša</translation>
</message>
<message>
<source>Moving marked files to trash</source>
<translation>Presúvanie do koša</translation>
</message> </message>
</context> </context>
<context> <context>
@@ -433,6 +473,37 @@
<translation>Výber stĺpcov</translation> <translation>Výber stĺpcov</translation>
</message> </message>
</context> </context>
<context>
<name>SettingsDialog</name>
<message>
<source>Settings</source>
<translation>Nastavenia</translation>
</message>
<message>
<source>How many images are preloaded before and after current image.</source>
<translation>Koľko obrázkov sa prednačíta pred a za aktuálnym obrázkom.</translation>
</message>
<message>
<source>Thumbnail size in pixels</source>
<translation>Veľkosť náhľadu v pixeloch</translation>
</message>
<message>
<source>Image preload count</source>
<translation>Počet prednačítaných obrázkov</translation>
</message>
<message>
<source>Thumbnails size</source>
<translation>Veľkosť náhľadu</translation>
</message>
<message>
<source>Changes in settings will take effect after program restart.</source>
<translation>Zmeny v nastaveniach sa prejavia po reštarte programu.</translation>
</message>
<message>
<source>Don&apos;t use native file dialog</source>
<translation>Nepoužívať natívny súborový dialóg</translation>
</message>
</context>
<context> <context>
<name>StretchToolbar</name> <name>StretchToolbar</name>
<message> <message>
@@ -451,13 +522,13 @@
<source>Invert colors</source> <source>Invert colors</source>
<translation>Invertuj farby</translation> <translation>Invertuj farby</translation>
</message> </message>
<message>
<source>Superpixel CFA draw 2x2 pixel as one</source>
<translation>Super pixel CFA kreslenie 2x2 pixelov ako jeden</translation>
</message>
<message> <message>
<source>Apply auto stretch on load</source> <source>Apply auto stretch on load</source>
<translation>Aplikuj automatické natiahnutie pri načítaní</translation> <translation>Aplikuj automatické natiahnutie pri načítaní</translation>
</message> </message>
<message>
<source>Debayer CFA</source>
<translation>Preveď CFA na farbu</translation>
</message>
</context> </context>
</TS> </TS>