Compare commits
17 Commits
20250302
...
0047607c1d
| Author | SHA1 | Date | |
|---|---|---|---|
| 0047607c1d | |||
| 45c368bbbb | |||
| c96cb86a29 | |||
| fe3e5f66be | |||
| 6fd17fbdf5 | |||
| f30dd2a520 | |||
| 21675d9479 | |||
| f669baa8a6 | |||
| c317012c99 | |||
| d0dbef20c7 | |||
| bd45900821 | |||
| 96a89bff92 | |||
| c05fc36ee3 | |||
| 05b0aa9a2f | |||
| 7b70b6cce5 | |||
| 5150ec5639 | |||
| 79529552d9 |
@@ -54,6 +54,8 @@ set(TENMON_SRC
|
|||||||
statusbar.cpp statusbar.h
|
statusbar.cpp statusbar.h
|
||||||
stfslider.cpp stfslider.h
|
stfslider.cpp stfslider.h
|
||||||
stretchtoolbar.cpp stretchtoolbar.h
|
stretchtoolbar.cpp stretchtoolbar.h
|
||||||
|
tfloat16.h
|
||||||
|
thumbnailer/genthumbnail.cpp thumbnailer/genthumbnail.h
|
||||||
)
|
)
|
||||||
|
|
||||||
qt_add_resources(TENMON_SRC resources/resources.qrc)
|
qt_add_resources(TENMON_SRC resources/resources.qrc)
|
||||||
|
|||||||
+3
-2
@@ -67,7 +67,8 @@ void BatchProcessing::scanScriptDir()
|
|||||||
if(idx>=0)_ui->scriptsList->setCurrentRow(idx);
|
if(idx>=0)_ui->scriptsList->setCurrentRow(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
BatchProcessing::BatchProcessing(QWidget *parent) : QDialog(parent)
|
BatchProcessing::BatchProcessing(Database *database, QWidget *parent) : QDialog(parent)
|
||||||
|
, _database(database)
|
||||||
{
|
{
|
||||||
_ui = new Ui::BatchProcessing;
|
_ui = new Ui::BatchProcessing;
|
||||||
_ui->setupUi(this);
|
_ui->setupUi(this);
|
||||||
@@ -200,7 +201,7 @@ void BatchProcessing::runScript()
|
|||||||
auto selectedItems = _ui->scriptsList->selectedItems();
|
auto selectedItems = _ui->scriptsList->selectedItems();
|
||||||
if(selectedItems.size())
|
if(selectedItems.size())
|
||||||
{
|
{
|
||||||
_engineThread = new Script::ScriptEngineThread(this);
|
_engineThread = new Script::ScriptEngineThread(_database, this);
|
||||||
connect(_engineThread, &Script::ScriptEngineThread::newMessage, this, &BatchProcessing::newMessage);
|
connect(_engineThread, &Script::ScriptEngineThread::newMessage, this, &BatchProcessing::newMessage);
|
||||||
connect(_engineThread, &Script::ScriptEngineThread::finished, this, &BatchProcessing::scriptFinished);
|
connect(_engineThread, &Script::ScriptEngineThread::finished, this, &BatchProcessing::scriptFinished);
|
||||||
QStringList paths;
|
QStringList paths;
|
||||||
|
|||||||
+4
-1
@@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
namespace Ui { class BatchProcessing; }
|
namespace Ui { class BatchProcessing; }
|
||||||
|
|
||||||
|
class Database;
|
||||||
|
|
||||||
class BatchProcessing : public QDialog
|
class BatchProcessing : public QDialog
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -15,10 +17,11 @@ class BatchProcessing : public QDialog
|
|||||||
QFileSystemWatcher _fileWatcher;
|
QFileSystemWatcher _fileWatcher;
|
||||||
Script::ScriptEngineThread *_engineThread = nullptr;
|
Script::ScriptEngineThread *_engineThread = nullptr;
|
||||||
QColor _textColor;
|
QColor _textColor;
|
||||||
|
Database *_database;
|
||||||
private slots:
|
private slots:
|
||||||
void scanScriptDir();
|
void scanScriptDir();
|
||||||
public:
|
public:
|
||||||
explicit BatchProcessing(QWidget *parent = nullptr);
|
explicit BatchProcessing(Database *database, QWidget *parent = nullptr);
|
||||||
~BatchProcessing();
|
~BatchProcessing();
|
||||||
protected:
|
protected:
|
||||||
void closeEvent(QCloseEvent *event);
|
void closeEvent(QCloseEvent *event);
|
||||||
|
|||||||
+12
-5
@@ -15,7 +15,7 @@ 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 database = QSqlDatabase::addDatabase("QSQLITE", connectionName);
|
database = QSqlDatabase::addDatabase("QSQLITE", connectionName);
|
||||||
|
|
||||||
if(!dir.mkpath("."))
|
if(!dir.mkpath("."))
|
||||||
return false;
|
return false;
|
||||||
@@ -27,7 +27,7 @@ bool Database::init(const QLatin1String &connectionName)
|
|||||||
{
|
{
|
||||||
QSqlQuery query(database);
|
QSqlQuery query(database);
|
||||||
query.exec("PRAGMA foreign_keys = ON");
|
query.exec("PRAGMA foreign_keys = ON");
|
||||||
int version = checkVersion();
|
int version = checkVersion(database);
|
||||||
if(version == 0)
|
if(version == 0)
|
||||||
{
|
{
|
||||||
query.exec("PRAGMA user_version = 1");
|
query.exec("PRAGMA user_version = 1");
|
||||||
@@ -76,6 +76,14 @@ bool Database::init(const QLatin1String &connectionName)
|
|||||||
}
|
}
|
||||||
qDebug() << error.text();
|
qDebug() << error.text();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qDebug() << "Failed to open database" << connectionName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qDebug() << "Database is invalid";
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -146,10 +154,9 @@ bool Database::checkError(QSqlQuery &query)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int Database::checkVersion()
|
int Database::checkVersion(QSqlDatabase &db)
|
||||||
{
|
{
|
||||||
QSqlDatabase db = QSqlDatabase::database();
|
QSqlQuery query("PRAGMA user_version", db);
|
||||||
QSqlQuery query("PRAGMA user_version");
|
|
||||||
if(query.next())
|
if(query.next())
|
||||||
return query.value(0).toInt();
|
return query.value(0).toInt();
|
||||||
return -1;
|
return -1;
|
||||||
|
|||||||
+2
-1
@@ -10,6 +10,7 @@
|
|||||||
class Database : public QObject
|
class Database : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
QSqlDatabase database;
|
||||||
QSqlQuery m_markQuery;
|
QSqlQuery m_markQuery;
|
||||||
QSqlQuery m_unmarkQuery;
|
QSqlQuery m_unmarkQuery;
|
||||||
QSqlQuery m_isMarkedQuery;
|
QSqlQuery m_isMarkedQuery;
|
||||||
@@ -40,7 +41,7 @@ protected:
|
|||||||
bool indexDir2(const QDir &dir, QProgressDialog *progress, QStringList &scannedDirs);
|
bool indexDir2(const QDir &dir, QProgressDialog *progress, QStringList &scannedDirs);
|
||||||
bool indexFile(const QFileInfo &file);
|
bool indexFile(const QFileInfo &file);
|
||||||
bool checkError(QSqlQuery &query);
|
bool checkError(QSqlQuery &query);
|
||||||
int checkVersion();
|
int checkVersion(QSqlDatabase &db);
|
||||||
signals:
|
signals:
|
||||||
void databaseChanged();
|
void databaseChanged();
|
||||||
};
|
};
|
||||||
|
|||||||
+1
-1
@@ -308,7 +308,7 @@ DataBaseView::DataBaseView(Database *database, QWidget *parent) : QWidget(parent
|
|||||||
}
|
}
|
||||||
|
|
||||||
QPushButton *filterButton = new QPushButton(tr("Filter"), this);
|
QPushButton *filterButton = new QPushButton(tr("Filter"), this);
|
||||||
connect(filterButton, SIGNAL(pressed()), this, SLOT(applyFilter()));
|
connect(filterButton, &QPushButton::pressed, this, &DataBaseView::applyFilter);
|
||||||
hlayout->addWidget(filterButton);
|
hlayout->addWidget(filterButton);
|
||||||
|
|
||||||
connect(m_database, &Database::databaseChanged, [this, &addFilterItems](){
|
connect(m_database, &Database::databaseChanged, [this, &addFilterItems](){
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ struct ImageInfoData
|
|||||||
QVector<QPair<QString, QString>> info;
|
QVector<QPair<QString, QString>> info;
|
||||||
std::shared_ptr<WCSDataT> wcs;
|
std::shared_ptr<WCSDataT> wcs;
|
||||||
SkyPointScale getCenterRaDec() const;
|
SkyPointScale getCenterRaDec() const;
|
||||||
|
int index = 0;
|
||||||
|
int num = 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
|
|||||||
+72
-23
@@ -23,13 +23,16 @@ Image::Image(const QString name, int number, ImageRingList *ringList) :
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void Image::load(QThreadPool *pool)
|
void Image::load(int index, QThreadPool *pool)
|
||||||
{
|
{
|
||||||
|
if(index != m_info.index && !m_loading)
|
||||||
|
m_rawImage.reset();
|
||||||
|
|
||||||
if(!m_rawImage && !m_loading)
|
if(!m_rawImage && !m_loading)
|
||||||
{
|
{
|
||||||
m_loading = true;
|
m_loading = true;
|
||||||
m_released = false;
|
m_released = false;
|
||||||
pool->start(new LoadRunable(m_name, this, m_ringList->analyzeLevel()));
|
pool->start(new LoadRunable(m_name, this, m_ringList->analyzeLevel(), index));
|
||||||
}
|
}
|
||||||
if(!m_loading && m_rawImage)
|
if(!m_loading && m_rawImage)
|
||||||
emit pixmapLoaded(this);
|
emit pixmapLoaded(this);
|
||||||
@@ -38,7 +41,7 @@ void Image::load(QThreadPool *pool)
|
|||||||
void Image::loadThumbnail(QThreadPool *pool)
|
void Image::loadThumbnail(QThreadPool *pool)
|
||||||
{
|
{
|
||||||
if(!m_thumbnail)
|
if(!m_thumbnail)
|
||||||
pool->start(new LoadRunable(m_name, this, AnalyzeLevel::None, true));
|
pool->start(new LoadRunable(m_name, this, AnalyzeLevel::None, 0, true));
|
||||||
else
|
else
|
||||||
emit thumbnailLoaded(this);
|
emit thumbnailLoaded(this);
|
||||||
}
|
}
|
||||||
@@ -113,8 +116,9 @@ ImageRingList::ImageRingList(Database *database, const QStringList &nameFilter,
|
|||||||
, m_analyzeLevel(None)
|
, m_analyzeLevel(None)
|
||||||
, m_database(database)
|
, m_database(database)
|
||||||
, m_nameFilter(nameFilter)
|
, m_nameFilter(nameFilter)
|
||||||
|
, m_fileSuffix(nameFilter)
|
||||||
{
|
{
|
||||||
connect(&m_fileSystemWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(dirChanged(QString)));
|
connect(&m_fileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, &ImageRingList::dirChanged);
|
||||||
m_nameFilter.replaceInStrings(QRegularExpression("^"), "*.");
|
m_nameFilter.replaceInStrings(QRegularExpression("^"), "*.");
|
||||||
m_loadPool = new QThreadPool(this);
|
m_loadPool = new QThreadPool(this);
|
||||||
m_loadPool->setThreadPriority(QThread::LowPriority);
|
m_loadPool->setThreadPriority(QThread::LowPriority);
|
||||||
@@ -145,6 +149,7 @@ bool ImageRingList::setDir(const QString path, const QString ¤tFile, bool
|
|||||||
|
|
||||||
if(dir.exists())
|
if(dir.exists())
|
||||||
{
|
{
|
||||||
|
m_currentDir = path;
|
||||||
QStringList scannedDirs;
|
QStringList scannedDirs;
|
||||||
QStringList absolutePaths;
|
QStringList absolutePaths;
|
||||||
std::function<void(const QString&)> scanDir = [&](const QString &path)
|
std::function<void(const QString&)> scanDir = [&](const QString &path)
|
||||||
@@ -170,10 +175,11 @@ bool ImageRingList::setDir(const QString path, const QString ¤tFile, bool
|
|||||||
};
|
};
|
||||||
|
|
||||||
scanDir(path);
|
scanDir(path);
|
||||||
qDebug() << absolutePaths.size();
|
//qDebug() << absolutePaths.size();
|
||||||
setFiles(absolutePaths, m_liveMode ? absolutePaths.first() : currentFile);
|
setFilesPrivate(absolutePaths, m_liveMode ? absolutePaths.first() : currentFile);
|
||||||
|
|
||||||
m_fileSystemWatcher.removePaths(m_fileSystemWatcher.directories());
|
if(m_fileSystemWatcher.directories().size())
|
||||||
|
m_fileSystemWatcher.removePaths(m_fileSystemWatcher.directories());
|
||||||
m_fileSystemWatcher.addPath(path);
|
m_fileSystemWatcher.addPath(path);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -192,6 +198,17 @@ void ImageRingList::setFile(const QString &file)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ImageRingList::setFiles(QStringList files)
|
||||||
|
{
|
||||||
|
QRegularExpression reg("(" + m_fileSuffix.join("|") + ")");
|
||||||
|
files.removeIf([®](const QString &file){
|
||||||
|
QFileInfo info(file);
|
||||||
|
auto match = reg.match(info.suffix());
|
||||||
|
return !match.hasMatch() || !info.exists() || !info.isReadable() || !info.isFile();
|
||||||
|
});
|
||||||
|
setFilesPrivate(files);
|
||||||
|
}
|
||||||
|
|
||||||
ImagePtr ImageRingList::currentImage()
|
ImagePtr ImageRingList::currentImage()
|
||||||
{
|
{
|
||||||
if(m_images.size())
|
if(m_images.size())
|
||||||
@@ -200,6 +217,11 @@ ImagePtr ImageRingList::currentImage()
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString ImageRingList::currentDir() const
|
||||||
|
{
|
||||||
|
return m_currentDir;
|
||||||
|
}
|
||||||
|
|
||||||
void ImageRingList::increment()
|
void ImageRingList::increment()
|
||||||
{
|
{
|
||||||
if(m_images.size())
|
if(m_images.size())
|
||||||
@@ -211,9 +233,9 @@ void ImageRingList::increment()
|
|||||||
(*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);
|
||||||
(*m_currImage)->load(m_loadPool);
|
(*m_currImage)->load(0, m_loadPool);
|
||||||
m_lastImage = increment(m_lastImage);
|
m_lastImage = increment(m_lastImage);
|
||||||
(*m_lastImage)->load(m_loadPool);
|
(*m_lastImage)->load(0, m_loadPool);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,20 +250,48 @@ void ImageRingList::decrement()
|
|||||||
(*m_lastImage)->release();
|
(*m_lastImage)->release();
|
||||||
m_firstImage = decrement(m_firstImage);
|
m_firstImage = decrement(m_firstImage);
|
||||||
m_currImage = decrement(m_currImage);
|
m_currImage = decrement(m_currImage);
|
||||||
(*m_currImage)->load(m_loadPool);
|
(*m_currImage)->load(0, m_loadPool);
|
||||||
m_lastImage = decrement(m_lastImage);
|
m_lastImage = decrement(m_lastImage);
|
||||||
(*m_firstImage)->load(m_loadPool);
|
(*m_firstImage)->load(0, m_loadPool);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageRingList::prevSubImage()
|
||||||
|
{
|
||||||
|
if(m_images.size())
|
||||||
|
{
|
||||||
|
if((*m_currImage)->isLoading())
|
||||||
|
return;
|
||||||
|
|
||||||
|
int index = (*m_currImage)->info().index;
|
||||||
|
int num = (*m_currImage)->info().num;
|
||||||
|
if(num > 1)
|
||||||
|
(*m_currImage)->load(index == 1 ? num - 1 : index - 1, m_loadPool);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageRingList::nextSubImage()
|
||||||
|
{
|
||||||
|
if(m_images.size())
|
||||||
|
{
|
||||||
|
if((*m_currImage)->isLoading())
|
||||||
|
return;
|
||||||
|
|
||||||
|
int index = (*m_currImage)->info().index;
|
||||||
|
int num = (*m_currImage)->info().num;
|
||||||
|
if(num > 1)
|
||||||
|
(*m_currImage)->load((index + 1) % num, m_loadPool);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageRingList::setMarked()
|
void ImageRingList::setMarked()
|
||||||
{
|
{
|
||||||
QStringList files = m_database->getMarkedFiles();
|
QStringList files = m_database->getMarkedFiles();
|
||||||
std::remove_if(files.begin(), files.end(), [](const QString &file){
|
files.removeIf([](const QString &file){
|
||||||
QFileInfo info(file);
|
QFileInfo info(file);
|
||||||
return !info.exists() || !info.isReadable();
|
return !info.exists() || !info.isReadable();
|
||||||
});
|
});
|
||||||
setFiles(files);
|
setFilesPrivate(files);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageRingList::setLiveMode(bool live)
|
void ImageRingList::setLiveMode(bool live)
|
||||||
@@ -284,7 +334,7 @@ void ImageRingList::loadFile(int row)
|
|||||||
if(m_images.empty())
|
if(m_images.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
(*m_currImage)->load(m_loadPool);
|
(*m_currImage)->load(0, m_loadPool);
|
||||||
|
|
||||||
m_width = DEFAULT_WIDTH<m_images.size()/2 ? DEFAULT_WIDTH : m_images.size()/2;
|
m_width = DEFAULT_WIDTH<m_images.size()/2 ? DEFAULT_WIDTH : m_images.size()/2;
|
||||||
if(m_liveMode)
|
if(m_liveMode)
|
||||||
@@ -293,9 +343,9 @@ void ImageRingList::loadFile(int row)
|
|||||||
for(int i=0; i<m_width; i++)
|
for(int i=0; i<m_width; i++)
|
||||||
{
|
{
|
||||||
m_firstImage = decrement(m_firstImage);
|
m_firstImage = decrement(m_firstImage);
|
||||||
(*m_firstImage)->load(m_loadPool);
|
(*m_firstImage)->load(0, m_loadPool);
|
||||||
m_lastImage = increment(m_lastImage);
|
m_lastImage = increment(m_lastImage);
|
||||||
(*m_lastImage)->load(m_loadPool);
|
(*m_lastImage)->load(0, m_loadPool);
|
||||||
}
|
}
|
||||||
if(m_lastImage != m_firstImage)
|
if(m_lastImage != m_firstImage)
|
||||||
{
|
{
|
||||||
@@ -419,9 +469,9 @@ void ImageRingList::setPreload(int width)
|
|||||||
for(int i = newWidth - m_width; i>0; i--)
|
for(int i = newWidth - m_width; i>0; i--)
|
||||||
{
|
{
|
||||||
m_firstImage = decrement(m_firstImage);
|
m_firstImage = decrement(m_firstImage);
|
||||||
(*m_firstImage)->load(m_loadPool);
|
(*m_firstImage)->load(0, m_loadPool);
|
||||||
m_lastImage = increment(m_lastImage);
|
m_lastImage = increment(m_lastImage);
|
||||||
(*m_lastImage)->load(m_loadPool);
|
(*m_lastImage)->load(0, m_loadPool);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(newWidth < m_width)
|
if(newWidth < m_width)
|
||||||
@@ -474,7 +524,7 @@ void ImageRingList::toggleSlideshow(bool start)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageRingList::setFiles(const QStringList files, const QString ¤tFile)
|
void ImageRingList::setFilesPrivate(const QStringList files, const QString ¤tFile)
|
||||||
{
|
{
|
||||||
m_loadPool->clear();
|
m_loadPool->clear();
|
||||||
m_thumbPool->clear();
|
m_thumbPool->clear();
|
||||||
@@ -486,8 +536,8 @@ void ImageRingList::setFiles(const QStringList files, const QString ¤tFile
|
|||||||
for(const QString &file : files)
|
for(const QString &file : files)
|
||||||
{
|
{
|
||||||
ImagePtr ptr = make_shared<Image>(file, i++, this);
|
ImagePtr ptr = make_shared<Image>(file, i++, this);
|
||||||
connect(ptr.get(), SIGNAL(pixmapLoaded(Image*)), this, SLOT(imageLoaded(Image*)));
|
connect(ptr.get(), &Image::pixmapLoaded, this, &ImageRingList::imageLoaded);
|
||||||
connect(ptr.get(), SIGNAL(thumbnailLoaded(Image*)), this, SIGNAL(thumbnailLoaded(Image*)));
|
connect(ptr.get(), &Image::thumbnailLoaded, this, &ImageRingList::thumbnailLoaded);
|
||||||
m_images.append(ptr);
|
m_images.append(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -526,9 +576,8 @@ void ImageRingList::imageLoaded(Image *image)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageRingList::dirChanged(QString dir)
|
void ImageRingList::dirChanged(QString)
|
||||||
{
|
{
|
||||||
m_currentDir = dir;
|
|
||||||
if(m_liveMode)
|
if(m_liveMode)
|
||||||
reloadDir();
|
reloadDir();
|
||||||
else
|
else
|
||||||
|
|||||||
+8
-3
@@ -28,7 +28,7 @@ class Image : public QObject
|
|||||||
ImageRingList *m_ringList;
|
ImageRingList *m_ringList;
|
||||||
public:
|
public:
|
||||||
explicit Image(const QString name, int number, ImageRingList *ringList);
|
explicit Image(const QString name, int number, ImageRingList *ringList);
|
||||||
void load(QThreadPool *pool);
|
void load(int index, QThreadPool *pool);
|
||||||
void loadThumbnail(QThreadPool *pool);
|
void loadThumbnail(QThreadPool *pool);
|
||||||
void release();
|
void release();
|
||||||
QString name() const;
|
QString name() const;
|
||||||
@@ -68,6 +68,7 @@ class ImageRingList : public QAbstractItemModel
|
|||||||
QThreadPool *m_thumbPool;
|
QThreadPool *m_thumbPool;
|
||||||
Database *m_database;
|
Database *m_database;
|
||||||
QStringList m_nameFilter;
|
QStringList m_nameFilter;
|
||||||
|
QStringList m_fileSuffix;
|
||||||
QTimer *m_slideShowTimer;
|
QTimer *m_slideShowTimer;
|
||||||
QTimer *m_dirChangeDelay;
|
QTimer *m_dirChangeDelay;
|
||||||
QString m_currentDir;
|
QString m_currentDir;
|
||||||
@@ -76,7 +77,9 @@ public:
|
|||||||
~ImageRingList() override;
|
~ImageRingList() override;
|
||||||
bool setDir(const QString path, const QString ¤tFile = QString(), bool recursive = false);
|
bool setDir(const QString path, const QString ¤tFile = QString(), bool recursive = false);
|
||||||
void setFile(const QString &file);
|
void setFile(const QString &file);
|
||||||
|
void setFiles(QStringList files);
|
||||||
ImagePtr currentImage();
|
ImagePtr currentImage();
|
||||||
|
QString currentDir() const;
|
||||||
void setLiveMode(bool live);
|
void setLiveMode(bool live);
|
||||||
void setCalculateStats(bool stats);
|
void setCalculateStats(bool stats);
|
||||||
void setFindPeaks(bool findPeaks);
|
void setFindPeaks(bool findPeaks);
|
||||||
@@ -103,9 +106,11 @@ public slots:
|
|||||||
void toggleSlideshow(bool start);
|
void toggleSlideshow(bool start);
|
||||||
void increment();
|
void increment();
|
||||||
void decrement();
|
void decrement();
|
||||||
|
void prevSubImage();
|
||||||
|
void nextSubImage();
|
||||||
void setMarked();
|
void setMarked();
|
||||||
protected:
|
protected:
|
||||||
void setFiles(const QStringList files, const QString ¤tFile = QString());
|
void setFilesPrivate(const QStringList files, const QString ¤tFile = QString());
|
||||||
QList<ImagePtr>::iterator increment(QList<ImagePtr>::iterator iter);
|
QList<ImagePtr>::iterator increment(QList<ImagePtr>::iterator iter);
|
||||||
QList<ImagePtr>::iterator decrement(QList<ImagePtr>::iterator iter);
|
QList<ImagePtr>::iterator decrement(QList<ImagePtr>::iterator iter);
|
||||||
signals:
|
signals:
|
||||||
@@ -115,7 +120,7 @@ signals:
|
|||||||
void currentImageChanged(int index);
|
void currentImageChanged(int index);
|
||||||
protected slots:
|
protected slots:
|
||||||
void imageLoaded(Image *image);
|
void imageLoaded(Image *image);
|
||||||
void dirChanged(QString dir);
|
void dirChanged(QString);
|
||||||
void reloadDir();
|
void reloadDir();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -27,8 +27,8 @@ ImageScrollArea::ImageScrollArea(Database *database, QWidget *parent) : QWidget(
|
|||||||
layout->addWidget(m_verticalScrollBar, 0, 1);
|
layout->addWidget(m_verticalScrollBar, 0, 1);
|
||||||
layout->addWidget(m_horizontalScrollBar, 1, 0);
|
layout->addWidget(m_horizontalScrollBar, 1, 0);
|
||||||
|
|
||||||
connect(m_verticalScrollBar, SIGNAL(valueChanged(int)), this, SLOT(scrollEvent()));
|
connect(m_verticalScrollBar, &QScrollBar::valueChanged, this, &ImageScrollArea::scrollEvent);
|
||||||
connect(m_horizontalScrollBar, SIGNAL(valueChanged(int)), this, SLOT(scrollEvent()));
|
connect(m_horizontalScrollBar, &QScrollBar::valueChanged, this, &ImageScrollArea::scrollEvent);
|
||||||
|
|
||||||
if(imageWidgetGL)
|
if(imageWidgetGL)
|
||||||
{
|
{
|
||||||
|
|||||||
+4
-4
@@ -79,7 +79,7 @@ ImageWidgetGL::ImageWidgetGL(Database *database, QWidget *parent) : QOpenGLWidge
|
|||||||
m_updateTimer = new QTimer(this);
|
m_updateTimer = new QTimer(this);
|
||||||
m_updateTimer->setInterval(500);
|
m_updateTimer->setInterval(500);
|
||||||
m_updateTimer->setSingleShot(true);
|
m_updateTimer->setSingleShot(true);
|
||||||
connect(m_updateTimer, SIGNAL(timeout()), this, SLOT(update()));
|
connect(m_updateTimer, &QTimer::timeout, this, static_cast<void (QOpenGLWidget::*)()>(&ImageWidgetGL::update));
|
||||||
setAcceptDrops(true);
|
setAcceptDrops(true);
|
||||||
QTimer::singleShot(1000, [this](){
|
QTimer::singleShot(1000, [this](){
|
||||||
if(!isValid())
|
if(!isValid())
|
||||||
@@ -392,9 +392,9 @@ void swPaint(std::shared_ptr<RawImage> &rawImage, float dx, float dy, float scal
|
|||||||
float iptr;
|
float iptr;
|
||||||
float fy = std::modf((y + oy) * iscale, &iptr);
|
float fy = std::modf((y + oy) * iscale, &iptr);
|
||||||
int64_t py = iptr;
|
int64_t py = iptr;
|
||||||
int64_t w = py * rawImage->widthBytes();
|
int64_t w = py * rawImage->widthSamples();
|
||||||
int64_t w2 = w;
|
int64_t w2 = w;
|
||||||
if(py+1 < imgHeight)w2 += rawImage->widthBytes();
|
if(py+1 < imgHeight)w2 += rawImage->widthSamples();
|
||||||
if(py >= imgHeight)break;
|
if(py >= imgHeight)break;
|
||||||
|
|
||||||
for(int64_t x = std::max((int64_t)0, -ox); x < width; x++)
|
for(int64_t x = std::max((int64_t)0, -ox); x < width; x++)
|
||||||
@@ -777,7 +777,7 @@ void ImageWidgetGL::initializeGL()
|
|||||||
m_lut->bind(2);
|
m_lut->bind(2);
|
||||||
|
|
||||||
QImage colormap = loadColormap();
|
QImage colormap = loadColormap();
|
||||||
m_colormap = std::make_unique<QOpenGLTexture>(QOpenGLTexture::Target1DArray);
|
m_colormap = std::make_unique<QOpenGLTexture>(QOpenGLTexture::Target2DArray);
|
||||||
m_colormap->setSize(colormap.width());
|
m_colormap->setSize(colormap.width());
|
||||||
m_colormap->setLayers(colormap.height());
|
m_colormap->setLayers(colormap.height());
|
||||||
m_colormap->setFormat(QOpenGLTexture::RGBA8_UNorm);
|
m_colormap->setFormat(QOpenGLTexture::RGBA8_UNorm);
|
||||||
|
|||||||
+40
-13
@@ -83,11 +83,10 @@ int loadFITSHeader(fitsfile *file, ImageInfoData &info)
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool loadFITS(const QString path, ImageInfoData &info, std::shared_ptr<RawImage> &image, bool planar)
|
bool loadFITS(const QString path, ImageInfoData &info, std::shared_ptr<RawImage> &image, bool planar, uint32_t index)
|
||||||
{
|
{
|
||||||
fitsfile *file;
|
fitsfile *file;
|
||||||
int status = 0;
|
int status = 0;
|
||||||
int type = -1;
|
|
||||||
int num = 0;
|
int num = 0;
|
||||||
long naxes[3] = {0};
|
long naxes[3] = {0};
|
||||||
|
|
||||||
@@ -105,16 +104,32 @@ bool loadFITS(const QString path, ImageInfoData &info, std::shared_ptr<RawImage>
|
|||||||
fits_get_num_hdus(file, &num, &status);
|
fits_get_num_hdus(file, &num, &status);
|
||||||
if(status)return checkError();
|
if(status)return checkError();
|
||||||
|
|
||||||
|
int hdutype;
|
||||||
int imgtype;
|
int imgtype;
|
||||||
int naxis;
|
int naxis;
|
||||||
for(int i=1; i <= num; i++)
|
std::vector<int> imageIdxs;
|
||||||
|
for(int i = 1; i <= num; i++)
|
||||||
{
|
{
|
||||||
fits_movabs_hdu(file, i, IMAGE_HDU, &status);if(status)return checkError();
|
fits_movabs_hdu(file, i, &hdutype, &status);if(status)return checkError();
|
||||||
fits_get_hdu_type(file, &type, &status);if(status)return checkError();
|
if(hdutype == IMAGE_HDU)
|
||||||
|
{
|
||||||
|
fits_get_img_param(file, 3, &imgtype, &naxis, naxes, &status);if(status)return checkError();
|
||||||
|
if(naxis >= 2 && naxis <= 3)imageIdxs.push_back(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info.num = imageIdxs.size();
|
||||||
|
info.index = index;
|
||||||
|
|
||||||
|
if(index >= imageIdxs.size())return false;
|
||||||
|
|
||||||
|
fits_movabs_hdu(file, imageIdxs[index], &hdutype, &status);if(status)return checkError();
|
||||||
|
if(hdutype == IMAGE_HDU)
|
||||||
|
{
|
||||||
|
naxes[0] = naxes[1] = naxes[2] = 0;
|
||||||
fits_get_img_param(file, 3, &imgtype, &naxis, naxes, &status);if(status)return checkError();
|
fits_get_img_param(file, 3, &imgtype, &naxis, naxes, &status);if(status)return checkError();
|
||||||
fits_get_img_equivtype(file, &imgtype, &status);if(status)return checkError();
|
fits_get_img_equivtype(file, &imgtype, &status);if(status)return checkError();
|
||||||
|
|
||||||
if(type == IMAGE_HDU && naxis >= 2 && naxis <= 3 && status == 0)
|
if(hdutype == IMAGE_HDU && naxis >= 2 && naxis <= 3 && status == 0)
|
||||||
{
|
{
|
||||||
RawImage::DataType type;
|
RawImage::DataType type;
|
||||||
int fitstype;
|
int fitstype;
|
||||||
@@ -133,6 +148,10 @@ bool loadFITS(const QString path, ImageInfoData &info, std::shared_ptr<RawImage>
|
|||||||
type = RawImage::UINT16;
|
type = RawImage::UINT16;
|
||||||
fitstype = TUSHORT;
|
fitstype = TUSHORT;
|
||||||
break;
|
break;
|
||||||
|
case LONG_IMG:
|
||||||
|
type = RawImage::UINT32;
|
||||||
|
fitstype = TINT;
|
||||||
|
break;
|
||||||
case ULONG_IMG:
|
case ULONG_IMG:
|
||||||
type = RawImage::UINT32;
|
type = RawImage::UINT32;
|
||||||
fitstype = TUINT;
|
fitstype = TUINT;
|
||||||
@@ -173,13 +192,18 @@ bool loadFITS(const QString path, ImageInfoData &info, std::shared_ptr<RawImage>
|
|||||||
for(size_t i=0; i<size; i++)
|
for(size_t i=0; i<size; i++)
|
||||||
s[i] -= INT16_MIN;
|
s[i] -= INT16_MIN;
|
||||||
}
|
}
|
||||||
|
else if(fitstype == TINT)
|
||||||
|
{
|
||||||
|
uint32_t *s = static_cast<uint32_t*>(img.data());
|
||||||
|
size_t size = img.size() * img.channels();
|
||||||
|
for(size_t i=0; i<size; i++)
|
||||||
|
s[i] -= INT32_MIN;
|
||||||
|
}
|
||||||
|
|
||||||
if(img.channels() == 1 || planar)
|
if(img.channels() == 1 || planar)
|
||||||
image = std::make_shared<RawImage>(std::move(img));
|
image = std::make_shared<RawImage>(std::move(img));
|
||||||
else
|
else
|
||||||
image = RawImage::fromPlanar(img);
|
image = RawImage::fromPlanar(img);
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
noload:
|
noload:
|
||||||
@@ -202,14 +226,15 @@ noload:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool loadXISF(const QString &path, ImageInfoData &info, std::shared_ptr<RawImage> &image, bool planar)
|
bool loadXISF(const QString &path, ImageInfoData &info, std::shared_ptr<RawImage> &image, bool planar, uint32_t index)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
LibXISF::XISFReader xisf;
|
LibXISF::XISFReader xisf;
|
||||||
xisf.open(path.toLocal8Bit().data());
|
xisf.open(path.toLocal8Bit().data());
|
||||||
|
|
||||||
const LibXISF::Image &xisfImage = xisf.getImage(0);
|
if(index >= (uint32_t)xisf.imagesCount())return false;
|
||||||
|
const LibXISF::Image &xisfImage = xisf.getImage(index);
|
||||||
|
|
||||||
auto fitskeywords = xisfImage.fitsKeywords();
|
auto fitskeywords = xisfImage.fitsKeywords();
|
||||||
for(auto fits : fitskeywords)
|
for(auto fits : fitskeywords)
|
||||||
@@ -222,6 +247,8 @@ bool loadXISF(const QString &path, ImageInfoData &info, std::shared_ptr<RawImage
|
|||||||
info.fitsHeader.append(prop);
|
info.fitsHeader.append(prop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
info.num = xisf.imagesCount();
|
||||||
|
info.index = index + 1;
|
||||||
info.wcs = std::make_shared<WCSDataT>(xisfImage.width(), xisfImage.height(), info.fitsHeader);
|
info.wcs = std::make_shared<WCSDataT>(xisfImage.width(), xisfImage.height(), info.fitsHeader);
|
||||||
info.info.append({QObject::tr("Width"), QString::number(xisfImage.width())});
|
info.info.append({QObject::tr("Width"), QString::number(xisfImage.width())});
|
||||||
info.info.append({QObject::tr("Height"), QString::number(xisfImage.height())});
|
info.info.append({QObject::tr("Height"), QString::number(xisfImage.height())});
|
||||||
@@ -378,7 +405,7 @@ bool loadRAW(const QString path, ImageInfoData &info, std::shared_ptr<RawImage>
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool loadImage(const QString &path, ImageInfoData &info, std::shared_ptr<RawImage> &rawImage, bool planar)
|
bool loadImage(const QString &path, ImageInfoData &info, std::shared_ptr<RawImage> &rawImage, int index, bool planar)
|
||||||
{
|
{
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
QElapsedTimer timer;
|
QElapsedTimer timer;
|
||||||
@@ -390,12 +417,12 @@ bool loadImage(const QString &path, ImageInfoData &info, std::shared_ptr<RawImag
|
|||||||
}
|
}
|
||||||
else if(path.endsWith(".FIT", Qt::CaseInsensitive) || path.endsWith(".FITS", Qt::CaseInsensitive) || path.endsWith(".FZ", Qt::CaseInsensitive) || path.endsWith(".FTS", Qt::CaseInsensitive))
|
else if(path.endsWith(".FIT", Qt::CaseInsensitive) || path.endsWith(".FITS", Qt::CaseInsensitive) || path.endsWith(".FZ", Qt::CaseInsensitive) || path.endsWith(".FTS", Qt::CaseInsensitive))
|
||||||
{
|
{
|
||||||
ret = loadFITS(path, info, rawImage, planar);
|
ret = loadFITS(path, info, rawImage, planar, index);
|
||||||
qDebug() << "LoadFITS" << timer.elapsed();
|
qDebug() << "LoadFITS" << timer.elapsed();
|
||||||
}
|
}
|
||||||
else if(path.endsWith(".XISF", Qt::CaseInsensitive))
|
else if(path.endsWith(".XISF", Qt::CaseInsensitive))
|
||||||
{
|
{
|
||||||
ret = loadXISF(path, info, rawImage, planar);
|
ret = loadXISF(path, info, rawImage, planar, index);
|
||||||
qDebug() << "LoadXISF" << timer.elapsed();
|
qDebug() << "LoadXISF" << timer.elapsed();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
+1
-1
@@ -9,6 +9,6 @@ class RawImage;
|
|||||||
QString makeUNCPath(const QString &path);
|
QString makeUNCPath(const QString &path);
|
||||||
bool readFITSHeader(const QString &path, ImageInfoData &info);
|
bool readFITSHeader(const QString &path, ImageInfoData &info);
|
||||||
bool readXISFHeader(const QString &path, ImageInfoData &info);
|
bool readXISFHeader(const QString &path, ImageInfoData &info);
|
||||||
bool loadImage(const QString &path, ImageInfoData &info, std::shared_ptr<RawImage> &rawImage, bool planar = false);
|
bool loadImage(const QString &path, ImageInfoData &info, std::shared_ptr<RawImage> &rawImage, int index, bool planar = false);
|
||||||
|
|
||||||
#endif // LOADIMAGE_H
|
#endif // LOADIMAGE_H
|
||||||
|
|||||||
+6
-10
@@ -10,11 +10,12 @@
|
|||||||
#include "loadimage.h"
|
#include "loadimage.h"
|
||||||
#include <lcms2.h>
|
#include <lcms2.h>
|
||||||
|
|
||||||
LoadRunable::LoadRunable(const QString &file, Image *receiver, AnalyzeLevel level, bool thumbnail) :
|
LoadRunable::LoadRunable(const QString &file, Image *receiver, AnalyzeLevel level, int index, bool thumbnail) :
|
||||||
m_file(makeUNCPath(file)),
|
m_file(makeUNCPath(file)),
|
||||||
m_receiver(receiver),
|
m_receiver(receiver),
|
||||||
m_analyzeLevel(level),
|
m_analyzeLevel(level),
|
||||||
m_thumbnail(thumbnail)
|
m_thumbnail(thumbnail),
|
||||||
|
m_index(index)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,7 +33,7 @@ void LoadRunable::run()
|
|||||||
info.info.append({QObject::tr("Filename"), finfo.fileName()});
|
info.info.append({QObject::tr("Filename"), finfo.fileName()});
|
||||||
|
|
||||||
std::shared_ptr<RawImage> rawImage;
|
std::shared_ptr<RawImage> rawImage;
|
||||||
if(!loadImage(m_file, info, rawImage))
|
if(!loadImage(m_file, info, rawImage, m_index))
|
||||||
info.info.append({QObject::tr("Error"), QObject::tr("Failed to load image")});
|
info.info.append({QObject::tr("Error"), QObject::tr("Failed to load image")});
|
||||||
|
|
||||||
|
|
||||||
@@ -187,7 +188,7 @@ void ConvertRunable::run()
|
|||||||
|
|
||||||
ImageInfoData imageinfo;
|
ImageInfoData imageinfo;
|
||||||
std::shared_ptr<RawImage> rawimage;
|
std::shared_ptr<RawImage> rawimage;
|
||||||
loadImage(m_infile, imageinfo, rawimage);
|
loadImage(m_infile, imageinfo, rawimage, 0);
|
||||||
QFileInfo info(m_outfile);
|
QFileInfo info(m_outfile);
|
||||||
info.dir().mkpath(".");
|
info.dir().mkpath(".");
|
||||||
|
|
||||||
@@ -293,7 +294,6 @@ void ConvertRunable::run()
|
|||||||
// if nothing else try QImage
|
// if nothing else try QImage
|
||||||
{
|
{
|
||||||
QImage::Format format = QImage::Format_Invalid;
|
QImage::Format format = QImage::Format_Invalid;
|
||||||
int width = rawimage->widthBytes();
|
|
||||||
|
|
||||||
switch(rawimage->type())
|
switch(rawimage->type())
|
||||||
{
|
{
|
||||||
@@ -306,7 +306,6 @@ void ConvertRunable::run()
|
|||||||
if(rawimage->channels() == 1)format = QImage::Format_Grayscale16;
|
if(rawimage->channels() == 1)format = QImage::Format_Grayscale16;
|
||||||
else if(rawimage->channels() == 3)format = QImage::Format_RGBX64;
|
else if(rawimage->channels() == 3)format = QImage::Format_RGBX64;
|
||||||
else if(rawimage->channels() == 4)format = QImage::Format_RGBA64;
|
else if(rawimage->channels() == 4)format = QImage::Format_RGBA64;
|
||||||
width *= 2;
|
|
||||||
break;
|
break;
|
||||||
case RawImage::FLOAT16:
|
case RawImage::FLOAT16:
|
||||||
case RawImage::FLOAT32:
|
case RawImage::FLOAT32:
|
||||||
@@ -316,15 +315,12 @@ void ConvertRunable::run()
|
|||||||
if(rawimage->channels() == 1)format = QImage::Format_Grayscale16;
|
if(rawimage->channels() == 1)format = QImage::Format_Grayscale16;
|
||||||
else if(rawimage->channels() == 3)format = QImage::Format_RGBX64;
|
else if(rawimage->channels() == 3)format = QImage::Format_RGBX64;
|
||||||
else if(rawimage->channels() == 4)format = QImage::Format_RGBA64;
|
else if(rawimage->channels() == 4)format = QImage::Format_RGBA64;
|
||||||
width *= 2;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(format == QImage::Format_Invalid)return;
|
if(format == QImage::Format_Invalid)return;
|
||||||
|
|
||||||
QImage qimage(rawimage->width(), rawimage->height(), format);
|
QImage qimage((const uchar*)rawimage->data(), rawimage->width(), rawimage->height(), rawimage->widthBytes(), format);
|
||||||
for(uint32_t i=0; i < rawimage->height(); i++)
|
|
||||||
std::memcpy(qimage.scanLine(i), rawimage->data(i), width);
|
|
||||||
qimage.save(m_outfile);
|
qimage.save(m_outfile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -15,8 +15,9 @@ class LoadRunable : public QRunnable
|
|||||||
Image *m_receiver;
|
Image *m_receiver;
|
||||||
AnalyzeLevel m_analyzeLevel;
|
AnalyzeLevel m_analyzeLevel;
|
||||||
bool m_thumbnail;
|
bool m_thumbnail;
|
||||||
|
int m_index = 0;
|
||||||
public:
|
public:
|
||||||
LoadRunable(const QString &file, Image *receiver, AnalyzeLevel level, bool thumbnail = false);
|
LoadRunable(const QString &file, Image *receiver, AnalyzeLevel level, int index, bool thumbnail = false);
|
||||||
void run() override;
|
void run() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QSurfaceFormat>
|
#include <QSurfaceFormat>
|
||||||
#include <QTranslator>
|
#include <QTranslator>
|
||||||
|
#include <QCommandLineParser>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include "thumbnailer/genthumbnail.h"
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
@@ -15,12 +17,39 @@ int main(int argc, char *argv[])
|
|||||||
#else
|
#else
|
||||||
bool useGLES = true;
|
bool useGLES = true;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
QCommandLineParser cmd;
|
||||||
|
cmd.addOption({"gl", "Use desktop OpenGL. This is default on x86 and MacOS platform."});
|
||||||
|
cmd.addOption({"gles", "Use OpenGL ES. This is default on ARM platform."});
|
||||||
|
cmd.addOption({{"thumb", "thumbnail"}, "Generate thumbnail and save it to path.", "path"});
|
||||||
|
cmd.addOption({{"s", "size"}, "Size of the thumbnails in pixels (default: 128)", "size", "128"});
|
||||||
|
cmd.addPositionalArgument("file", "File to open");
|
||||||
|
cmd.addHelpOption();
|
||||||
|
QStringList cmdArgs;
|
||||||
for(int i = 0; i < argc; i++)
|
for(int i = 0; i < argc; i++)
|
||||||
|
cmdArgs.append(argv[i]);
|
||||||
|
|
||||||
|
cmd.process(cmdArgs);
|
||||||
|
if(cmd.isSet("gl"))
|
||||||
|
useGLES = false;
|
||||||
|
if(cmd.isSet("gles"))
|
||||||
|
useGLES = true;
|
||||||
|
|
||||||
|
if(cmd.isSet("thumb"))
|
||||||
{
|
{
|
||||||
if(std::strcmp("-gl", argv[i]) == 0)
|
QCoreApplication app(argc, argv);
|
||||||
useGLES = false;
|
QStringList files = cmd.positionalArguments();
|
||||||
if(std::strcmp("-gles", argv[i]) == 0)
|
if(files.size() == 0)
|
||||||
useGLES = true;
|
return 1;
|
||||||
|
|
||||||
|
QString thumb = cmd.value("thumb");
|
||||||
|
int size = 128;
|
||||||
|
bool ok;
|
||||||
|
int size2 = cmd.value("s").toInt(&ok);
|
||||||
|
if(ok)
|
||||||
|
size = size2;
|
||||||
|
|
||||||
|
return generateThumbnail(files.front(), thumb, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
QSurfaceFormat format;
|
QSurfaceFormat format;
|
||||||
@@ -39,6 +68,7 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
QSurfaceFormat::setDefaultFormat(format);
|
QSurfaceFormat::setDefaultFormat(format);
|
||||||
|
|
||||||
|
|
||||||
QApplication a(argc, argv);
|
QApplication a(argc, argv);
|
||||||
a.setOrganizationName("nou");
|
a.setOrganizationName("nou");
|
||||||
a.setApplicationName("Tenmon");
|
a.setApplicationName("Tenmon");
|
||||||
@@ -54,5 +84,22 @@ int main(int argc, char *argv[])
|
|||||||
MainWindow w;
|
MainWindow w;
|
||||||
w.show();
|
w.show();
|
||||||
|
|
||||||
|
if(!cmd.positionalArguments().isEmpty())
|
||||||
|
{
|
||||||
|
QStringList files = cmd.positionalArguments();
|
||||||
|
QStringList paths;
|
||||||
|
for(auto &arg : files)
|
||||||
|
{
|
||||||
|
QUrl url(arg);
|
||||||
|
QFileInfo info(url.isLocalFile() ? url.toLocalFile() : arg);
|
||||||
|
if(info.exists())
|
||||||
|
paths.append(info.canonicalFilePath());
|
||||||
|
}
|
||||||
|
if(paths.size() == 1)
|
||||||
|
w.loadFile(paths.front());
|
||||||
|
else if(paths.size() > 1)
|
||||||
|
w.loadFiles(paths);
|
||||||
|
}
|
||||||
|
|
||||||
return a.exec();
|
return a.exec();
|
||||||
}
|
}
|
||||||
|
|||||||
+39
-34
@@ -95,7 +95,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
|||||||
|
|
||||||
m_ringList = new ImageRingList(m_database, nameFilter, 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, &FilesystemWidget::fileSelected, this, static_cast<void (MainWindow::*)(int)>(&MainWindow::loadFile));
|
||||||
connect(m_filesystem, &FilesystemWidget::sortChanged, m_ringList, &ImageRingList::setSort);
|
connect(m_filesystem, &FilesystemWidget::sortChanged, m_ringList, &ImageRingList::setSort);
|
||||||
connect(m_filesystem, &FilesystemWidget::reverseSort, m_ringList, &ImageRingList::reverseSort);
|
connect(m_filesystem, &FilesystemWidget::reverseSort, m_ringList, &ImageRingList::reverseSort);
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
|||||||
connect(m_filetree, &Filetree::indexDirectory, this, static_cast<void (MainWindow::*)(const QString &)>(&MainWindow::indexDir));
|
connect(m_filetree, &Filetree::indexDirectory, this, static_cast<void (MainWindow::*)(const QString &)>(&MainWindow::indexDir));
|
||||||
|
|
||||||
m_databaseView = new DataBaseView(m_database, this);
|
m_databaseView = new DataBaseView(m_database, this);
|
||||||
connect(m_databaseView, SIGNAL(loadFile(QString)), this, SLOT(loadFile(QString)));
|
connect(m_databaseView, &DataBaseView::loadFile, this, static_cast<void (MainWindow::*)(const QString &)>(&MainWindow::loadFile));
|
||||||
|
|
||||||
#ifdef PLATESOLVER
|
#ifdef PLATESOLVER
|
||||||
_plateSolving = new PlateSolving(this);
|
_plateSolving = new PlateSolving(this);
|
||||||
@@ -114,6 +114,19 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
|||||||
_plateSolving->hide();
|
_plateSolving->hide();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
QToolBar *navigationToolbar = new QToolBar(tr("Navigation toolbar"), this);
|
||||||
|
navigationToolbar->setObjectName("navigationtoolbar");
|
||||||
|
navigationToolbar->hide();
|
||||||
|
QAction *prevAction = navigationToolbar->addAction(style()->standardIcon(QStyle::SP_ArrowLeft), tr("Previous image"));
|
||||||
|
QAction *nextAction = navigationToolbar->addAction(style()->standardIcon(QStyle::SP_ArrowRight), tr("Next image"));
|
||||||
|
QAction *prevSubAction = navigationToolbar->addAction(style()->standardIcon(QStyle::SP_ArrowUp), tr("Prev sub image"));
|
||||||
|
QAction *nextSubAction = navigationToolbar->addAction(style()->standardIcon(QStyle::SP_ArrowDown), tr("Next sub image"));
|
||||||
|
connect(prevAction, &QAction::triggered, m_ringList, static_cast<void (ImageRingList::*)()>(&ImageRingList::decrement));
|
||||||
|
connect(nextAction, &QAction::triggered, m_ringList, static_cast<void (ImageRingList::*)()>(&ImageRingList::increment));
|
||||||
|
connect(prevSubAction, &QAction::triggered, m_ringList, static_cast<void (ImageRingList::*)()>(&ImageRingList::prevSubImage));
|
||||||
|
connect(nextSubAction, &QAction::triggered, m_ringList, static_cast<void (ImageRingList::*)()>(&ImageRingList::nextSubImage));
|
||||||
|
|
||||||
|
addToolBar(Qt::TopToolBarArea, navigationToolbar);
|
||||||
addToolBar(Qt::TopToolBarArea, m_stretchPanel);
|
addToolBar(Qt::TopToolBarArea, m_stretchPanel);
|
||||||
|
|
||||||
QDockWidget *filesystemDock = new QDockWidget(tr("Filesystem"), this);
|
QDockWidget *filesystemDock = new QDockWidget(tr("Filesystem"), this);
|
||||||
@@ -155,26 +168,26 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
|||||||
connect(m_image, &ImageScrollArea::fileDropped, this, static_cast<void (MainWindow::*)(const QString &)>(&MainWindow::loadFile));
|
connect(m_image, &ImageScrollArea::fileDropped, this, static_cast<void (MainWindow::*)(const QString &)>(&MainWindow::loadFile));
|
||||||
|
|
||||||
QMenu *fileMenu = new QMenu(tr("File"), this);
|
QMenu *fileMenu = new QMenu(tr("File"), this);
|
||||||
fileMenu->addAction(tr("Open"), QKeySequence::Open, this, SLOT(loadFile()));
|
fileMenu->addAction(tr("Open"), QKeySequence::Open, this, static_cast<void (MainWindow::*)()>(&MainWindow::loadFile));
|
||||||
fileMenu->addAction(tr("Open directory recursively"), this, &MainWindow::loadDir);
|
fileMenu->addAction(tr("Open directory recursively"), this, &MainWindow::loadDir);
|
||||||
QAction *saveAs = fileMenu->addAction(tr("Save as"), QKeySequence::Save, this, SLOT(saveAs()));
|
QAction *saveAs = fileMenu->addAction(tr("Save as"), QKeySequence::Save, this, &MainWindow::saveAs);
|
||||||
fileMenu->addSeparator();
|
fileMenu->addSeparator();
|
||||||
fileMenu->addAction(tr("Copy marked files"), Qt::Key_F5, this, SLOT(copyMarked()));
|
fileMenu->addAction(tr("Copy marked files"), Qt::Key_F5, this, &MainWindow::copyMarked);
|
||||||
fileMenu->addAction(tr("Move marked files"), Qt::Key_F6, this, SLOT(moveMarked()));
|
fileMenu->addAction(tr("Move marked files"), Qt::Key_F6, this, &MainWindow::moveMarked);
|
||||||
fileMenu->addAction(tr("Move marked files to trash"), QKeySequence::Delete, this, &MainWindow::deleteMarked);
|
fileMenu->addAction(tr("Move marked files to trash"), QKeySequence::Delete, this, &MainWindow::deleteMarked);
|
||||||
fileMenu->addSeparator();
|
fileMenu->addSeparator();
|
||||||
fileMenu->addAction(tr("Index directory"), this, SLOT(indexDir()));
|
fileMenu->addAction(tr("Index directory"), this, static_cast<void (MainWindow::*)()>(&MainWindow::indexDir));
|
||||||
fileMenu->addAction(tr("Reindex files"), this, SLOT(reindex()));
|
fileMenu->addAction(tr("Reindex files"), this, &MainWindow::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"), Qt::Key_B | Qt::CTRL, [this](){
|
fileMenu->addAction(tr("Batch processing"), Qt::Key_B | Qt::CTRL, [this](){
|
||||||
BatchProcessing *batchProcessing = new BatchProcessing(this);
|
BatchProcessing *batchProcessing = new BatchProcessing(m_database, this);
|
||||||
batchProcessing->exec();
|
batchProcessing->exec();
|
||||||
delete batchProcessing;
|
delete batchProcessing;
|
||||||
});
|
});
|
||||||
fileMenu->addSeparator();
|
fileMenu->addSeparator();
|
||||||
QAction *liveModeAction = fileMenu->addAction(tr("Live mode"), this, SLOT(liveMode(bool)));
|
QAction *liveModeAction = fileMenu->addAction(tr("Live mode"), this, &MainWindow::liveMode);
|
||||||
liveModeAction->setCheckable(true);
|
liveModeAction->setCheckable(true);
|
||||||
QAction *exitAction = fileMenu->addAction(tr("Exit"), this, SLOT(close()));
|
QAction *exitAction = fileMenu->addAction(tr("Exit"), this, &MainWindow::close);
|
||||||
exitAction->setShortcut(QKeySequence::Quit);
|
exitAction->setShortcut(QKeySequence::Quit);
|
||||||
menuBar()->addMenu(fileMenu);
|
menuBar()->addMenu(fileMenu);
|
||||||
|
|
||||||
@@ -243,11 +256,11 @@ 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"), Qt::Key_F7, this, SLOT(markImage()));
|
selectMenu->addAction(tr("Mark"), Qt::Key_F7, this, &MainWindow::markImage);
|
||||||
selectMenu->addAction(tr("Unmark"), Qt::Key_F8, this, SLOT(unmarkImage()));
|
selectMenu->addAction(tr("Unmark"), Qt::Key_F8, this, &MainWindow::unmarkImage);
|
||||||
selectMenu->addSeparator();
|
selectMenu->addSeparator();
|
||||||
selectMenu->addAction(tr("Mark and next"), Qt::Key_M, this, SLOT(markAndNext()));
|
selectMenu->addAction(tr("Mark and next"), Qt::Key_M, this, &MainWindow::markAndNext);
|
||||||
selectMenu->addAction(tr("Unmark and next"), Qt::Key_X, this, SLOT(unmarkAndNext()));
|
selectMenu->addAction(tr("Unmark and next"), Qt::Key_X, this, &MainWindow::unmarkAndNext);
|
||||||
selectMenu->addSeparator();
|
selectMenu->addSeparator();
|
||||||
selectMenu->addAction(tr("Show marked list"), this, &MainWindow::showMarkFilesDialog);
|
selectMenu->addAction(tr("Show marked list"), this, &MainWindow::showMarkFilesDialog);
|
||||||
QAction *openMarked = selectMenu->addAction(tr("Open marked"), m_ringList, &ImageRingList::setMarked);
|
QAction *openMarked = selectMenu->addAction(tr("Open marked"), m_ringList, &ImageRingList::setMarked);
|
||||||
@@ -282,6 +295,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
|||||||
QMenu *dockMenu = new QMenu(tr("Docks"), this);
|
QMenu *dockMenu = new QMenu(tr("Docks"), this);
|
||||||
dockMenu->addAction(infoDock->toggleViewAction());
|
dockMenu->addAction(infoDock->toggleViewAction());
|
||||||
dockMenu->addAction(m_stretchPanel->toggleViewAction());
|
dockMenu->addAction(m_stretchPanel->toggleViewAction());
|
||||||
|
dockMenu->addAction(navigationToolbar->toggleViewAction());
|
||||||
dockMenu->addAction(filesystemDock->toggleViewAction());
|
dockMenu->addAction(filesystemDock->toggleViewAction());
|
||||||
dockMenu->addAction(databaseViewDock->toggleViewAction());
|
dockMenu->addAction(databaseViewDock->toggleViewAction());
|
||||||
dockMenu->addAction(filetreeDock->toggleViewAction());
|
dockMenu->addAction(filetreeDock->toggleViewAction());
|
||||||
@@ -327,22 +341,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
|||||||
|
|
||||||
_lastDir = settings.value("mainwindow/lastdir", _lastDir).toString();
|
_lastDir = settings.value("mainwindow/lastdir", _lastDir).toString();
|
||||||
|
|
||||||
QStringList args = QCoreApplication::arguments();
|
|
||||||
args.removeFirst();
|
|
||||||
for(auto &arg : args)
|
|
||||||
{
|
|
||||||
QUrl url(arg);
|
|
||||||
QFileInfo info(url.isLocalFile() ? url.toLocalFile() : arg);
|
|
||||||
if(info.exists())
|
|
||||||
{
|
|
||||||
m_ringList->setFile(info.canonicalFilePath());
|
|
||||||
updateWindowTitle();
|
|
||||||
_lastDir = info.absoluteDir().absolutePath();
|
|
||||||
settings.setValue("mainwindow/lastdir", _lastDir);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_image->setFocus();
|
m_image->setFocus();
|
||||||
|
|
||||||
// workaround for nasty wayland backend bug https://bugreports.qt.io/browse/QTBUG-87332
|
// workaround for nasty wayland backend bug https://bugreports.qt.io/browse/QTBUG-87332
|
||||||
@@ -403,7 +401,7 @@ void MainWindow::setupSigterm()
|
|||||||
|
|
||||||
::socketpair(AF_UNIX, SOCK_STREAM, 0, socketPair);
|
::socketpair(AF_UNIX, SOCK_STREAM, 0, socketPair);
|
||||||
socketNotifier = new QSocketNotifier(socketPair[1], QSocketNotifier::Read, this);
|
socketNotifier = new QSocketNotifier(socketPair[1], QSocketNotifier::Read, this);
|
||||||
connect(socketNotifier, SIGNAL(activated(int)), this, SLOT(socketNotify()));
|
connect(socketNotifier, &QSocketNotifier::activated, this, &MainWindow::socketNotify);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -565,6 +563,11 @@ void MainWindow::loadFile(const QString &path)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::loadFiles(const QStringList &paths)
|
||||||
|
{
|
||||||
|
m_ringList->setFiles(paths);
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::loadFile(int row)
|
void MainWindow::loadFile(int row)
|
||||||
{
|
{
|
||||||
m_ringList->loadFile(row);
|
m_ringList->loadFile(row);
|
||||||
@@ -822,8 +825,10 @@ void MainWindow::updateWindowTitle()
|
|||||||
ImagePtr ptr = m_ringList->currentImage();
|
ImagePtr ptr = m_ringList->currentImage();
|
||||||
if(ptr)
|
if(ptr)
|
||||||
{
|
{
|
||||||
QFileInfo info(ptr->name());
|
QDir dir(m_ringList->currentDir());
|
||||||
QString title = info.fileName();
|
QString title = dir.relativeFilePath(ptr->name());
|
||||||
|
if(ptr->info().num > 1)
|
||||||
|
title += QString(" [%1/%2]").arg(ptr->info().index + 1).arg(ptr->info().num);
|
||||||
if(m_database->isMarked(ptr->name()))
|
if(m_database->isMarked(ptr->name()))
|
||||||
title += " *";
|
title += " *";
|
||||||
setWindowTitle(title);
|
setWindowTitle(title);
|
||||||
|
|||||||
+2
-1
@@ -41,11 +41,12 @@ protected:
|
|||||||
void closeEvent(QCloseEvent *event) override;
|
void closeEvent(QCloseEvent *event) override;
|
||||||
void copyOrMove(bool copy);
|
void copyOrMove(bool copy);
|
||||||
void copyOrMove(bool copy, const QString &dest);
|
void copyOrMove(bool copy, const QString &dest);
|
||||||
protected slots:
|
public slots:
|
||||||
void socketNotify();
|
void socketNotify();
|
||||||
void updateWindowTitle();
|
void updateWindowTitle();
|
||||||
void loadFile();
|
void loadFile();
|
||||||
void loadFile(const QString &path);
|
void loadFile(const QString &path);
|
||||||
|
void loadFiles(const QStringList &paths);
|
||||||
void loadFile(int row);
|
void loadFile(int row);
|
||||||
void loadDir();
|
void loadDir();
|
||||||
void indexDir();
|
void indexDir();
|
||||||
|
|||||||
+93
-13
@@ -1,6 +1,7 @@
|
|||||||
#include "rawimage.h"
|
#include "rawimage.h"
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <lcms2.h>
|
#include <lcms2.h>
|
||||||
|
#include <algorithm>
|
||||||
#ifndef NO_QT
|
#ifndef NO_QT
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QElapsedTimer>
|
#include <QElapsedTimer>
|
||||||
@@ -8,9 +9,16 @@
|
|||||||
#include <QColorSpace>
|
#include <QColorSpace>
|
||||||
using F16 = qfloat16;
|
using F16 = qfloat16;
|
||||||
#else
|
#else
|
||||||
#include <algorithm>
|
#define __STDC_WANT_IEC_60559_TYPES_EXT__
|
||||||
|
#include <float.h>
|
||||||
|
#ifdef FLT16_MAX
|
||||||
using F16 = _Float16;
|
using F16 = _Float16;
|
||||||
#endif
|
#else
|
||||||
|
#include "tfloat16.h"
|
||||||
|
using F16 = TFloat16;// this is only for MXE
|
||||||
|
#endif // FLT16_MAX
|
||||||
|
|
||||||
|
#endif // NO_QT
|
||||||
|
|
||||||
int THUMB_SIZE = 128;
|
int THUMB_SIZE = 128;
|
||||||
int THUMB_SIZE_BORDER = 138;
|
int THUMB_SIZE_BORDER = 138;
|
||||||
@@ -190,7 +198,7 @@ void calcStats(const T *data, size_t n, size_t w, RawImage::Stats &stats)
|
|||||||
auto findMedian = [histSize](std::vector<uint32_t> &histogram, size_t n) -> size_t
|
auto findMedian = [histSize](std::vector<uint32_t> &histogram, size_t n) -> size_t
|
||||||
{
|
{
|
||||||
size_t histSum = 0;
|
size_t histSum = 0;
|
||||||
for(size_t o=0; o < histSize; o++)
|
for(size_t o=1; o < histSize; o++)
|
||||||
{
|
{
|
||||||
histSum += histogram[o];
|
histSum += histogram[o];
|
||||||
if(histSum >= n/2)
|
if(histSum >= n/2)
|
||||||
@@ -231,6 +239,57 @@ void calcStats(const T *data, size_t n, size_t w, RawImage::Stats &stats)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if constexpr(std::is_floating_point_v<T>)
|
||||||
|
{
|
||||||
|
T mmin = *std::min_element(min, min + 4);
|
||||||
|
T mmax = *std::max_element(max, max + 4);
|
||||||
|
|
||||||
|
T a = 1.0 / (mmax - mmin);
|
||||||
|
T b = -mmin / (mmax - mmin);
|
||||||
|
|
||||||
|
auto histFunc = [&](T d, int x)
|
||||||
|
{
|
||||||
|
uint16_t idx = std::clamp((T)(d * a + b) * histSize, (T)0.0, (T)65535.0);
|
||||||
|
histogram[x][idx]++;
|
||||||
|
};
|
||||||
|
|
||||||
|
// calculate histogram again
|
||||||
|
if(mmin < 0.0 || mmax > 1.0)
|
||||||
|
{
|
||||||
|
for(int i=0; i<4; i++)
|
||||||
|
{
|
||||||
|
histogram[i].clear();
|
||||||
|
histogram[i].resize(histSize, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
histFunc(data[i*ch], 0);
|
||||||
|
if constexpr(ch >= 3)
|
||||||
|
{
|
||||||
|
histFunc(data[i*ch + 1], 1);
|
||||||
|
histFunc(data[i*ch + 2], 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr(ch == 1)
|
||||||
|
{
|
||||||
|
size_t h = (n / w) & (SIZE_MAX-1);
|
||||||
|
w &= (SIZE_MAX-1);
|
||||||
|
for(size_t y=0; y<h; y+=2)
|
||||||
|
{
|
||||||
|
for(size_t x=0; x<w; x+=2)
|
||||||
|
{
|
||||||
|
histFunc(data[y*w+x], 1);
|
||||||
|
histFunc(data[y*w+x+1], 2);
|
||||||
|
histFunc(data[(y+1)*w+x], 2);
|
||||||
|
histFunc(data[(y+1)*w+x+1], 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for(int i = 0; i < 4; i++)
|
for(int i = 0; i < 4; i++)
|
||||||
{
|
{
|
||||||
stats.m_min[i] = min[i];
|
stats.m_min[i] = min[i];
|
||||||
@@ -240,7 +299,8 @@ void calcStats(const T *data, size_t n, size_t w, RawImage::Stats &stats)
|
|||||||
double sum2 = (double)sum[i] * sum[i];
|
double sum2 = (double)sum[i] * sum[i];
|
||||||
stats.m_stdDev[i] = std::sqrt((sumSq[i] - sum2 / na[i]) / (na[i] - 1));
|
stats.m_stdDev[i] = std::sqrt((sumSq[i] - sum2 / na[i]) / (na[i] - 1));
|
||||||
|
|
||||||
uint32_t median = findMedian(histogram[i], na[i]);
|
size_t naclip = na[i] - histogram[i][0];
|
||||||
|
uint32_t median = findMedian(histogram[i], naclip);
|
||||||
stats.m_median[i] = median;
|
stats.m_median[i] = median;
|
||||||
std::vector<uint32_t> madHist(histSize, 0);
|
std::vector<uint32_t> madHist(histSize, 0);
|
||||||
madHist[0] = histogram[i][median];
|
madHist[0] = histogram[i][median];
|
||||||
@@ -249,7 +309,7 @@ void calcStats(const T *data, size_t n, size_t w, RawImage::Stats &stats)
|
|||||||
if(median + o < histSize)madHist[o] += histogram[i][median + o];
|
if(median + o < histSize)madHist[o] += histogram[i][median + o];
|
||||||
if(o <= median)madHist[o] += histogram[i][median - o];
|
if(o <= median)madHist[o] += histogram[i][median - o];
|
||||||
}
|
}
|
||||||
stats.m_mad[i] = findMedian(madHist, na[i]);
|
stats.m_mad[i] = findMedian(madHist, naclip);
|
||||||
if constexpr(!std::numeric_limits<T>::is_integer)
|
if constexpr(!std::numeric_limits<T>::is_integer)
|
||||||
{
|
{
|
||||||
stats.m_median[i] /= 65535.0;
|
stats.m_median[i] /= 65535.0;
|
||||||
@@ -344,6 +404,11 @@ uint32_t RawImage::norm() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint32_t RawImage::widthBytes() const
|
uint32_t RawImage::widthBytes() const
|
||||||
|
{
|
||||||
|
return m_ch * m_width * typeSize(m_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t RawImage::widthSamples() const
|
||||||
{
|
{
|
||||||
return m_ch * m_width;
|
return m_ch * m_width;
|
||||||
}
|
}
|
||||||
@@ -414,13 +479,25 @@ void RawImage::convertToThumbnail()
|
|||||||
|
|
||||||
if(m_channels == 1)
|
if(m_channels == 1)
|
||||||
{
|
{
|
||||||
out[idx] = out[idx + 1] = out[idx + 2] = (F16)(in[idx2] * scale);
|
if(scale == 1.0f)
|
||||||
|
out[idx] = out[idx + 1] = out[idx + 2] = (F16)(in[idx2]);
|
||||||
|
else
|
||||||
|
out[idx] = out[idx + 1] = out[idx + 2] = (F16)(in[idx2] * scale);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
out[idx] = (F16)(in[idx2] * scale);
|
if(scale == 1.0f)
|
||||||
out[idx + 1] = (F16)(in[idx2 + 1] * scale);
|
{
|
||||||
out[idx + 2] = (F16)(in[idx2 + 2] * scale);
|
out[idx] = (F16)(in[idx2]);
|
||||||
|
out[idx + 1] = (F16)(in[idx2 + 1]);
|
||||||
|
out[idx + 2] = (F16)(in[idx2 + 2]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
out[idx] = (F16)(in[idx2] * scale);
|
||||||
|
out[idx + 1] = (F16)(in[idx2 + 1] * scale);
|
||||||
|
out[idx + 2] = (F16)(in[idx2 + 2] * scale);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
out[idx + 3] = (F16)1.0f;
|
out[idx + 3] = (F16)1.0f;
|
||||||
}
|
}
|
||||||
@@ -496,9 +573,9 @@ void convertType2(size_t size, const T *src, U *dst)
|
|||||||
|
|
||||||
if constexpr(std::is_integral_v<T> && (std::is_floating_point_v<U> || std::is_same_v<U, F16>))
|
if constexpr(std::is_integral_v<T> && (std::is_floating_point_v<U> || std::is_same_v<U, F16>))
|
||||||
{
|
{
|
||||||
U scale = (U)(1.0 / (double)std::numeric_limits<T>::max());
|
float scale = (float)(1.0 / (double)std::numeric_limits<T>::max());
|
||||||
for(size_t i = 0; i < size; i++)
|
for(size_t i = 0; i < size; i++)
|
||||||
dst[i] = (U)src[i] * scale;
|
dst[i] = (U)(src[i] * scale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -824,7 +901,7 @@ std::pair<float, float> RawImage::unitScale() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(min < 0.0f || max > 1.0f)
|
if(min < 0.0f || max > 1.0f)
|
||||||
return {1.0f / (max - min), min / (max - min)};
|
return {1.0f / (max - min), -min / (max - min)};
|
||||||
else
|
else
|
||||||
return {1.0f, 0.0f};
|
return {1.0f, 0.0f};
|
||||||
}
|
}
|
||||||
@@ -1112,11 +1189,14 @@ void RawImage::applySTF(const MTFParam &mtfParams)
|
|||||||
if constexpr(std::numeric_limits<std::remove_reference_t<decltype(*src)>>::is_integer)
|
if constexpr(std::numeric_limits<std::remove_reference_t<decltype(*src)>>::is_integer)
|
||||||
s = (float)std::numeric_limits<std::remove_reference_t<decltype(*src)>>::max();
|
s = (float)std::numeric_limits<std::remove_reference_t<decltype(*src)>>::max();
|
||||||
|
|
||||||
|
auto unit = unitScale();
|
||||||
float iscale = 1.0f / s;
|
float iscale = 1.0f / s;
|
||||||
size_t len = size() * m_ch;
|
size_t len = size() * m_ch;
|
||||||
for(size_t i = 0; i < len; i++)
|
for(size_t i = 0; i < len; i++)
|
||||||
{
|
{
|
||||||
float x = src[i] * iscale;
|
float x;
|
||||||
|
if constexpr(std::numeric_limits<std::remove_reference_t<decltype(*src)>>::is_integer)x = src[i] * iscale;
|
||||||
|
else x = src[i] * unit.first + unit.second;
|
||||||
x = (x - mtfParams.blackPoint[0]) / (mtfParams.whitePoint[0] - mtfParams.blackPoint[0]);
|
x = (x - mtfParams.blackPoint[0]) / (mtfParams.whitePoint[0] - mtfParams.blackPoint[0]);
|
||||||
x = std::clamp(x, 0.0f, 1.0f);
|
x = std::clamp(x, 0.0f, 1.0f);
|
||||||
x = ((mtfParams.midPoint[0] - 1.0f) * x) / ((2.0f * mtfParams.midPoint[0] - 1.0f) * x - mtfParams.midPoint[0]);
|
x = ((mtfParams.midPoint[0] - 1.0f) * x) / ((2.0f * mtfParams.midPoint[0] - 1.0f) * x - mtfParams.midPoint[0]);
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ public:
|
|||||||
DataType type() const;
|
DataType type() const;
|
||||||
uint32_t norm() const;
|
uint32_t norm() const;
|
||||||
uint32_t widthBytes() const;
|
uint32_t widthBytes() const;
|
||||||
|
uint32_t widthSamples() const;
|
||||||
void* data();
|
void* data();
|
||||||
const void* data() const;
|
const void* data() const;
|
||||||
void* data(uint32_t row, uint32_t col = 0);
|
void* data(uint32_t row, uint32_t col = 0);
|
||||||
|
|||||||
+14
-10
@@ -17,9 +17,9 @@
|
|||||||
namespace Script
|
namespace Script
|
||||||
{
|
{
|
||||||
|
|
||||||
ScriptEngine::ScriptEngine(BatchProcessing *parent)
|
ScriptEngine::ScriptEngine(Database *database, BatchProcessing *parent)
|
||||||
: _jsEngine(new QJSEngine(this))
|
: _jsEngine(new QJSEngine(this))
|
||||||
, _database(new Database(this))
|
, _database(database)
|
||||||
, _parent(parent)
|
, _parent(parent)
|
||||||
, _pool(new QThreadPool(this))
|
, _pool(new QThreadPool(this))
|
||||||
{
|
{
|
||||||
@@ -27,7 +27,6 @@ ScriptEngine::ScriptEngine(BatchProcessing *parent)
|
|||||||
_jsEngine->globalObject().setProperty("core", core);
|
_jsEngine->globalObject().setProperty("core", core);
|
||||||
QJSValue fitsRecordObject = _jsEngine->newQMetaObject(&FITSRecordModify::staticMetaObject);
|
QJSValue fitsRecordObject = _jsEngine->newQMetaObject(&FITSRecordModify::staticMetaObject);
|
||||||
_jsEngine->globalObject().setProperty("FITSRecordModify", fitsRecordObject);
|
_jsEngine->globalObject().setProperty("FITSRecordModify", fitsRecordObject);
|
||||||
_database->init(QLatin1String("scriptengine"));
|
|
||||||
_semaphore.release(_pool->maxThreadCount());
|
_semaphore.release(_pool->maxThreadCount());
|
||||||
_pool->setThreadPriority(QThread::LowPriority);
|
_pool->setThreadPriority(QThread::LowPriority);
|
||||||
|
|
||||||
@@ -73,17 +72,22 @@ void ScriptEngine::log(const QString &message)
|
|||||||
|
|
||||||
void ScriptEngine::mark(File *file)
|
void ScriptEngine::mark(File *file)
|
||||||
{
|
{
|
||||||
_database->mark(file->absoluteFilePath());
|
QString path = file->absoluteFilePath();
|
||||||
|
QMetaObject::invokeMethod(_database, [this, path](){ _database->mark(path); }, Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptEngine::unmark(File *file)
|
void ScriptEngine::unmark(File *file)
|
||||||
{
|
{
|
||||||
_database->unmark(file->absoluteFilePath());
|
QString path = file->absoluteFilePath();
|
||||||
|
QMetaObject::invokeMethod(_database, [this, path](){ _database->unmark(path); }, Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ScriptEngine::isMarked(const File *file) const
|
bool ScriptEngine::isMarked(const File *file)
|
||||||
{
|
{
|
||||||
return _database->isMarked(file->absoluteFilePath());
|
bool ret;
|
||||||
|
QString path = file->absoluteFilePath();
|
||||||
|
QMetaObject::invokeMethod(_database, [this, path](){ return _database->isMarked(path); }, Qt::BlockingQueuedConnection, &ret);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptEngine::setMaxThread(int maxthread)
|
void ScriptEngine::setMaxThread(int maxthread)
|
||||||
@@ -733,7 +737,7 @@ QJSValue File::stats()
|
|||||||
{
|
{
|
||||||
ImageInfoData info;
|
ImageInfoData info;
|
||||||
std::shared_ptr<RawImage> rawImage;
|
std::shared_ptr<RawImage> rawImage;
|
||||||
loadImage(_path, info, rawImage);
|
loadImage(_path, info, rawImage, 0);
|
||||||
rawImage->calcStats();
|
rawImage->calcStats();
|
||||||
RawImage::Stats stats = rawImage->imageStats();
|
RawImage::Stats stats = rawImage->imageStats();
|
||||||
_stats = _engine->newObject();
|
_stats = _engine->newObject();
|
||||||
@@ -765,11 +769,11 @@ QJSValue File::extractStars(bool hfr)
|
|||||||
}
|
}
|
||||||
#endif // PLATESOLVER
|
#endif // PLATESOLVER
|
||||||
|
|
||||||
ScriptEngineThread::ScriptEngineThread(BatchProcessing *parent) : QObject(parent)
|
ScriptEngineThread::ScriptEngineThread(Database *database, BatchProcessing *parent) : QObject(parent)
|
||||||
{
|
{
|
||||||
_thread = new QThread();
|
_thread = new QThread();
|
||||||
_thread->setObjectName("ScriptEngine");
|
_thread->setObjectName("ScriptEngine");
|
||||||
_engine = new ScriptEngine(parent);
|
_engine = new ScriptEngine(database, parent);
|
||||||
_engine->moveToThread(_thread);
|
_engine->moveToThread(_thread);
|
||||||
connect(_engine, &ScriptEngine::finished, _thread, &QThread::quit);
|
connect(_engine, &ScriptEngine::finished, _thread, &QThread::quit);
|
||||||
connect(_engine, &ScriptEngine::newMessage, this, &ScriptEngineThread::newMessage);
|
connect(_engine, &ScriptEngine::newMessage, this, &ScriptEngineThread::newMessage);
|
||||||
|
|||||||
+3
-3
@@ -31,7 +31,7 @@ class ScriptEngine : public QObject
|
|||||||
QList<QPair<QString, QString>> _paths;
|
QList<QPair<QString, QString>> _paths;
|
||||||
Solver *_solver = nullptr;
|
Solver *_solver = nullptr;
|
||||||
public:
|
public:
|
||||||
explicit ScriptEngine(BatchProcessing *parent = nullptr);
|
explicit ScriptEngine(Database *database, BatchProcessing *parent = nullptr);
|
||||||
void setParams(const QString &scriptPath, const QList<QPair<QString, QString>> &paths, const QString &outputDir);
|
void setParams(const QString &scriptPath, const QList<QPair<QString, QString>> &paths, const QString &outputDir);
|
||||||
void reportError(const QString &message);
|
void reportError(const QString &message);
|
||||||
const QString& outputDir() const;
|
const QString& outputDir() const;
|
||||||
@@ -40,7 +40,7 @@ public:
|
|||||||
Q_INVOKABLE void log(const QString &message);
|
Q_INVOKABLE void log(const QString &message);
|
||||||
Q_INVOKABLE void mark(File *file);
|
Q_INVOKABLE void mark(File *file);
|
||||||
Q_INVOKABLE void unmark(File *file);
|
Q_INVOKABLE void unmark(File *file);
|
||||||
Q_INVOKABLE bool isMarked(const File *file) const;
|
Q_INVOKABLE bool isMarked(const File *file);
|
||||||
Q_INVOKABLE void setMaxThread(int maxthread);
|
Q_INVOKABLE void setMaxThread(int maxthread);
|
||||||
Q_INVOKABLE void sync();
|
Q_INVOKABLE void sync();
|
||||||
Q_INVOKABLE QJSValue getString(const QString &label = QString(), const QString &text = QString()) const;
|
Q_INVOKABLE QJSValue getString(const QString &label = QString(), const QString &text = QString()) const;
|
||||||
@@ -71,7 +71,7 @@ class ScriptEngineThread : public QObject
|
|||||||
QThread *_thread;
|
QThread *_thread;
|
||||||
ScriptEngine *_engine;
|
ScriptEngine *_engine;
|
||||||
public:
|
public:
|
||||||
ScriptEngineThread(BatchProcessing *parent = nullptr);
|
ScriptEngineThread(Database *database, BatchProcessing *parent = nullptr);
|
||||||
~ScriptEngineThread();
|
~ScriptEngineThread();
|
||||||
void setParams(const QString &scriptPath, const QList<QPair<QString, QString>> &paths, const QString &outputDir);
|
void setParams(const QString &scriptPath, const QList<QPair<QString, QString>> &paths, const QString &outputDir);
|
||||||
void start();
|
void start();
|
||||||
|
|||||||
@@ -4,6 +4,12 @@
|
|||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
#include <QProcess>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QPushButton>
|
||||||
#include "rawimage.h"
|
#include "rawimage.h"
|
||||||
|
|
||||||
extern int DEFAULT_WIDTH;
|
extern int DEFAULT_WIDTH;
|
||||||
@@ -80,6 +86,7 @@ SettingsDialog::SettingsDialog(QWidget *parent) : QDialog(parent)
|
|||||||
m_bestFit->setToolTip(tr("Set Best Fit zoom level when opening new image."));
|
m_bestFit->setToolTip(tr("Set Best Fit zoom level when opening new image."));
|
||||||
m_bestFit->setChecked(BESTFIT);
|
m_bestFit->setChecked(BESTFIT);
|
||||||
|
|
||||||
|
|
||||||
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);
|
||||||
@@ -88,8 +95,16 @@ SettingsDialog::SettingsDialog(QWidget *parent) : QDialog(parent)
|
|||||||
layout->addRow(m_qualityThumbnail);
|
layout->addRow(m_qualityThumbnail);
|
||||||
layout->addRow(m_useNativeDialog);
|
layout->addRow(m_useNativeDialog);
|
||||||
layout->addRow(m_bestFit);
|
layout->addRow(m_bestFit);
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN64
|
||||||
|
QPushButton *installThumbnailer = new QPushButton(tr("Install"), this);
|
||||||
|
installThumbnailer->setToolTip(tr("This will install thumnail generation for FITS and XISF files in File Explorer"));
|
||||||
|
connect(installThumbnailer, &QPushButton::clicked, this, &SettingsDialog::installThumbnailer);
|
||||||
|
layout->addRow(tr("Install thumbnailer"), installThumbnailer);
|
||||||
|
#endif
|
||||||
//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.")));
|
||||||
|
|
||||||
|
|
||||||
QDialogButtonBox *buttonBox = new QDialogButtonBox(this);
|
QDialogButtonBox *buttonBox = new QDialogButtonBox(this);
|
||||||
buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||||
connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||||
@@ -123,6 +138,25 @@ bool SettingsDialog::loadThumbsizes()
|
|||||||
return OLD_THUMB_SIZE != THUMB_SIZE;
|
return OLD_THUMB_SIZE != THUMB_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SettingsDialog::installThumbnailer()
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WIN64
|
||||||
|
QString path = QCoreApplication::instance()->applicationDirPath() + "/tenmonthumbnailer.dll";
|
||||||
|
if(!QFileInfo::exists(path))
|
||||||
|
{
|
||||||
|
QMessageBox::critical(this, tr("Missing dll"), tr("Can't find ") + path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QProcess regsvr;
|
||||||
|
int ret = regsvr.execute("regsvr32.exe", {"/s", path});
|
||||||
|
if(ret == 0)
|
||||||
|
QMessageBox::information(this, tr("Thumbnail support"), tr("Thumbnail generation support sucessufully installed."));
|
||||||
|
else
|
||||||
|
QMessageBox::critical(this, tr("Error"), tr("Failed to register thumbnailer. %1").arg(ret));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void SettingsDialog::saveSettings()
|
void SettingsDialog::saveSettings()
|
||||||
{
|
{
|
||||||
QSettings settings;
|
QSettings settings;
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ public:
|
|||||||
explicit SettingsDialog(QWidget *parent = nullptr);
|
explicit SettingsDialog(QWidget *parent = nullptr);
|
||||||
static void loadSettings();
|
static void loadSettings();
|
||||||
static bool loadThumbsizes();
|
static bool loadThumbsizes();
|
||||||
|
public slots:
|
||||||
|
void installThumbnailer();
|
||||||
signals:
|
signals:
|
||||||
void preloadChanged(int witdth);
|
void preloadChanged(int witdth);
|
||||||
private:
|
private:
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
uniform sampler2D qt_Texture0;
|
uniform sampler2D qt_Texture0;
|
||||||
uniform sampler3D lut_table;
|
uniform sampler3D lut_table;
|
||||||
uniform sampler1DArray colormap;
|
uniform sampler2DArray colormap;
|
||||||
uniform vec3 mtf_param[3];
|
uniform vec3 mtf_param[3];
|
||||||
uniform vec2 unit_scale;
|
uniform vec2 unit_scale;
|
||||||
uniform bool bw;
|
uniform bool bw;
|
||||||
@@ -30,7 +30,7 @@ vec3 falsecolor(float color)
|
|||||||
{
|
{
|
||||||
color *= 255.0 / 256.0;
|
color *= 255.0 / 256.0;
|
||||||
color += 0.5 / 256.0;
|
color += 0.5 / 256.0;
|
||||||
return texture(colormap, vec2(color, colormapIdx)).rgb;
|
return texture(colormap, vec3(color, 0.5, colormapIdx)).rgb;
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 checker()
|
vec3 checker()
|
||||||
|
|||||||
@@ -58,6 +58,15 @@
|
|||||||
</screenshots>
|
</screenshots>
|
||||||
<content_rating type="oars-1.1"/>
|
<content_rating type="oars-1.1"/>
|
||||||
<releases>
|
<releases>
|
||||||
|
<release version="20250318" date="2025-03-18">
|
||||||
|
<description>
|
||||||
|
<ul>
|
||||||
|
<li>Fix OpenGL ES drawings</li>
|
||||||
|
<li>Fix mark/unmark files from script</li>
|
||||||
|
<li>Fix stretching of float images with values outside of 0-1 range</li>
|
||||||
|
</ul>
|
||||||
|
</description>
|
||||||
|
</release>
|
||||||
<release version="20250302" date="2025-03-02">
|
<release version="20250302" date="2025-03-02">
|
||||||
<description>
|
<description>
|
||||||
<ul>
|
<ul>
|
||||||
|
|||||||
+68
@@ -0,0 +1,68 @@
|
|||||||
|
#ifndef TFLOAT16_H
|
||||||
|
#define TFLOAT16_H
|
||||||
|
|
||||||
|
// crude implementation of float16 for platforms that do not support _Float16
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
class TFloat16
|
||||||
|
{
|
||||||
|
uint16_t b16;
|
||||||
|
public:
|
||||||
|
TFloat16(){ b16 = 0; }
|
||||||
|
explicit inline TFloat16(float f)
|
||||||
|
{
|
||||||
|
uint32_t i = *reinterpret_cast<uint32_t*>(&f);
|
||||||
|
uint32_t sign = (i >> 16) & 0x8000;
|
||||||
|
uint32_t exp = (i >> 23) & 0xff;
|
||||||
|
uint32_t mantisa = (i & 0x7fffff) >> 13;
|
||||||
|
|
||||||
|
b16 = 0;
|
||||||
|
if(exp < 111)
|
||||||
|
{
|
||||||
|
// do nothing it map to 0
|
||||||
|
}
|
||||||
|
else if(exp == 111)
|
||||||
|
{
|
||||||
|
b16 |= sign;
|
||||||
|
b16 |= mantisa;
|
||||||
|
}
|
||||||
|
else if(exp == 255)//inf or nan
|
||||||
|
{
|
||||||
|
b16 = 0x7c00;
|
||||||
|
b16 |= sign;
|
||||||
|
b16 |= mantisa;
|
||||||
|
}
|
||||||
|
else if(exp > 142)
|
||||||
|
{
|
||||||
|
b16 = 0x7c00;// inf
|
||||||
|
b16 |= sign;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
b16 |= sign;
|
||||||
|
b16 |= (exp - 112) << 10;
|
||||||
|
b16 |= mantisa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
friend TFloat16 operator*(TFloat16 a, TFloat16 b)
|
||||||
|
{
|
||||||
|
return TFloat16(static_cast<float>(a) * static_cast<float>(b));
|
||||||
|
}
|
||||||
|
operator float() const
|
||||||
|
{
|
||||||
|
uint32_t i = 0;
|
||||||
|
uint32_t sign = b16 & 0x8000;
|
||||||
|
uint32_t exp = (b16 & 0x7c00) >> 10;
|
||||||
|
if(b16)
|
||||||
|
{
|
||||||
|
i |= sign << 16;
|
||||||
|
if(exp==31)i |= 0x7f800000;
|
||||||
|
else i |= (exp + 112) << 23;
|
||||||
|
i |= (b16 & 0x3ff) << 13;
|
||||||
|
}
|
||||||
|
return *reinterpret_cast<float*>(&i);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TFLOAT16_H
|
||||||
@@ -3,19 +3,24 @@ option(BUILD_THUMBNAILER "Build generator of thumbnails" OFF)
|
|||||||
if(BUILD_THUMBNAILER)
|
if(BUILD_THUMBNAILER)
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
add_library(tenmonthumbnailer SHARED
|
add_library(tenmonthumbnailer SHARED
|
||||||
winmain.cpp
|
Dll.cpp
|
||||||
|
loadxisf.cpp
|
||||||
|
TenmonThumbnailProvider.cpp
|
||||||
|
../rawimage.h
|
||||||
../rawimage.cpp
|
../rawimage.cpp
|
||||||
../rawimage_sse.cpp)
|
../rawimage_sse.cpp)
|
||||||
|
set_target_properties(tenmonthumbnailer PROPERTIES PREFIX "")
|
||||||
|
|
||||||
target_compile_definitions(tenmonthumbnailer PRIVATE NO_QT)
|
target_compile_definitions(tenmonthumbnailer PRIVATE NO_QT)
|
||||||
target_include_directories(tenmonthumbnailer PRIVATE ../libXISF)
|
target_include_directories(tenmonthumbnailer PRIVATE ../libXISF)
|
||||||
target_link_libraries(tenmonthumbnailer PRIVATE ${LCMS2_LIB} XISF)
|
target_link_libraries(tenmonthumbnailer PRIVATE shlwapi ${LCMS2_LIB} ${FITS_LIB} XISF)
|
||||||
|
target_link_options(tenmonthumbnailer PRIVATE "-static")
|
||||||
else(WIN32)
|
else(WIN32)
|
||||||
qt_add_executable(tenmonthumbnailer
|
qt_add_executable(tenmonthumbnailer
|
||||||
main.cpp
|
main.cpp
|
||||||
|
../loadimage.cpp
|
||||||
../rawimage.cpp
|
../rawimage.cpp
|
||||||
../rawimage_sse.cpp
|
../rawimage_sse.cpp
|
||||||
../loadimage.cpp
|
|
||||||
../imageinfodata.cpp)
|
../imageinfodata.cpp)
|
||||||
|
|
||||||
target_link_libraries(tenmonthumbnailer PRIVATE Qt6::Core Qt6::Gui ${EXIF_LIB} ${FITS_LIB} ${RAW_LIB} ${WCS_LIB} ${LCMS2_LIB} XISF)
|
target_link_libraries(tenmonthumbnailer PRIVATE Qt6::Core Qt6::Gui ${EXIF_LIB} ${FITS_LIB} ${RAW_LIB} ${WCS_LIB} ${LCMS2_LIB} XISF)
|
||||||
|
|||||||
@@ -0,0 +1,244 @@
|
|||||||
|
#include <objbase.h>
|
||||||
|
#include <shlwapi.h>
|
||||||
|
#include <thumbcache.h> // For IThumbnailProvider.
|
||||||
|
#include <shlobj.h> // For SHChangeNotify
|
||||||
|
#include <new>
|
||||||
|
|
||||||
|
extern HRESULT TenmonThumbnailer_CreateInstance(REFIID riid, void **ppv);
|
||||||
|
|
||||||
|
#define SZ_CLSID_TENMONTHUMBHANDLER L"{0f3881d7-c9f0-45cb-aadb-40192aead2b4}"
|
||||||
|
#define SZ_TENMONTHUMBHANDLER L"Tenmon Thumbnail Handler"
|
||||||
|
|
||||||
|
const CLSID CLSID_TenmonThumbHandler = {0x0f3881d7, 0xc9f0, 0x45cb, {0xaa, 0xdb, 0x40, 0x19, 0x2a, 0xea, 0xd2, 0xb4}};
|
||||||
|
|
||||||
|
typedef HRESULT (*PFNCREATEINSTANCE)(REFIID riid, void **ppvObject);
|
||||||
|
struct CLASS_OBJECT_INIT
|
||||||
|
{
|
||||||
|
const CLSID *pClsid;
|
||||||
|
PFNCREATEINSTANCE pfnCreate;
|
||||||
|
};
|
||||||
|
|
||||||
|
// add classes supported by this module here
|
||||||
|
const CLASS_OBJECT_INIT c_rgClassObjectInit[] =
|
||||||
|
{
|
||||||
|
{ &CLSID_TenmonThumbHandler, TenmonThumbnailer_CreateInstance }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
long g_cRefModule = 0;
|
||||||
|
|
||||||
|
// Handle the the DLL's module
|
||||||
|
HINSTANCE g_hInst = NULL;
|
||||||
|
|
||||||
|
// Standard DLL functions
|
||||||
|
STDAPI_(BOOL) DllMain(HINSTANCE hInstance, DWORD dwReason, void *)
|
||||||
|
{
|
||||||
|
if (dwReason == DLL_PROCESS_ATTACH)
|
||||||
|
{
|
||||||
|
g_hInst = hInstance;
|
||||||
|
DisableThreadLibraryCalls(hInstance);
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDAPI DllCanUnloadNow()
|
||||||
|
{
|
||||||
|
// Only allow the DLL to be unloaded after all outstanding references have been released
|
||||||
|
return (g_cRefModule == 0) ? S_OK : S_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DllAddRef()
|
||||||
|
{
|
||||||
|
InterlockedIncrement(&g_cRefModule);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DllRelease()
|
||||||
|
{
|
||||||
|
InterlockedDecrement(&g_cRefModule);
|
||||||
|
}
|
||||||
|
|
||||||
|
class CClassFactory : public IClassFactory
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static HRESULT CreateInstance(REFCLSID clsid, const CLASS_OBJECT_INIT *pClassObjectInits, size_t cClassObjectInits, REFIID riid, void **ppv)
|
||||||
|
{
|
||||||
|
*ppv = NULL;
|
||||||
|
HRESULT hr = CLASS_E_CLASSNOTAVAILABLE;
|
||||||
|
for (size_t i = 0; i < cClassObjectInits; i++)
|
||||||
|
{
|
||||||
|
if (clsid == *pClassObjectInits[i].pClsid)
|
||||||
|
{
|
||||||
|
IClassFactory *pClassFactory = new (std::nothrow) CClassFactory(pClassObjectInits[i].pfnCreate);
|
||||||
|
hr = pClassFactory ? S_OK : E_OUTOFMEMORY;
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
hr = pClassFactory->QueryInterface(riid, ppv);
|
||||||
|
pClassFactory->Release();
|
||||||
|
}
|
||||||
|
break; // match found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
CClassFactory(PFNCREATEINSTANCE pfnCreate) : _cRef(1), _pfnCreate(pfnCreate)
|
||||||
|
{
|
||||||
|
DllAddRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
// IUnknown
|
||||||
|
IFACEMETHODIMP QueryInterface(REFIID riid, void ** ppv)
|
||||||
|
{
|
||||||
|
static const QITAB qit[] =
|
||||||
|
{
|
||||||
|
QITABENT(CClassFactory, IClassFactory),
|
||||||
|
{ 0 }
|
||||||
|
};
|
||||||
|
return QISearch(this, qit, riid, ppv);
|
||||||
|
}
|
||||||
|
|
||||||
|
IFACEMETHODIMP_(ULONG) AddRef()
|
||||||
|
{
|
||||||
|
return InterlockedIncrement(&_cRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
IFACEMETHODIMP_(ULONG) Release()
|
||||||
|
{
|
||||||
|
long cRef = InterlockedDecrement(&_cRef);
|
||||||
|
if (cRef == 0)
|
||||||
|
{
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
return cRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
// IClassFactory
|
||||||
|
IFACEMETHODIMP CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
|
||||||
|
{
|
||||||
|
return punkOuter ? CLASS_E_NOAGGREGATION : _pfnCreate(riid, ppv);
|
||||||
|
}
|
||||||
|
|
||||||
|
IFACEMETHODIMP LockServer(BOOL fLock)
|
||||||
|
{
|
||||||
|
if (fLock)
|
||||||
|
{
|
||||||
|
DllAddRef();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DllRelease();
|
||||||
|
}
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
~CClassFactory()
|
||||||
|
{
|
||||||
|
DllRelease();
|
||||||
|
}
|
||||||
|
|
||||||
|
long _cRef;
|
||||||
|
PFNCREATEINSTANCE _pfnCreate;
|
||||||
|
};
|
||||||
|
|
||||||
|
STDAPI DllGetClassObject(REFCLSID clsid, REFIID riid, void **ppv)
|
||||||
|
{
|
||||||
|
return CClassFactory::CreateInstance(clsid, c_rgClassObjectInit, ARRAYSIZE(c_rgClassObjectInit), riid, ppv);
|
||||||
|
}
|
||||||
|
|
||||||
|
// A struct to hold the information required for a registry entry
|
||||||
|
|
||||||
|
struct REGISTRY_ENTRY
|
||||||
|
{
|
||||||
|
HKEY hkeyRoot;
|
||||||
|
PCWSTR pszKeyName;
|
||||||
|
PCWSTR pszValueName;
|
||||||
|
PCWSTR pszData;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Creates a registry key (if needed) and sets the default value of the key
|
||||||
|
|
||||||
|
HRESULT CreateRegKeyAndSetValue(const REGISTRY_ENTRY *pRegistryEntry)
|
||||||
|
{
|
||||||
|
HKEY hKey;
|
||||||
|
HRESULT hr = HRESULT_FROM_WIN32(RegCreateKeyExW(pRegistryEntry->hkeyRoot, pRegistryEntry->pszKeyName,
|
||||||
|
0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hKey, NULL));
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, pRegistryEntry->pszValueName, 0, REG_SZ,
|
||||||
|
(LPBYTE) pRegistryEntry->pszData,
|
||||||
|
((DWORD) wcslen(pRegistryEntry->pszData) + 1) * sizeof(WCHAR)));
|
||||||
|
RegCloseKey(hKey);
|
||||||
|
}
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Registers this COM server
|
||||||
|
//
|
||||||
|
STDAPI DllRegisterServer()
|
||||||
|
{
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
WCHAR szModuleName[MAX_PATH];
|
||||||
|
|
||||||
|
if (!GetModuleFileNameW(g_hInst, szModuleName, ARRAYSIZE(szModuleName)))
|
||||||
|
{
|
||||||
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// List of registry entries we want to create
|
||||||
|
const REGISTRY_ENTRY rgRegistryEntries[] =
|
||||||
|
{
|
||||||
|
// RootKey KeyName ValueName Data
|
||||||
|
{HKEY_CURRENT_USER, L"Software\\Classes\\CLSID\\" SZ_CLSID_TENMONTHUMBHANDLER, NULL, SZ_TENMONTHUMBHANDLER},
|
||||||
|
{HKEY_CURRENT_USER, L"Software\\Classes\\CLSID\\" SZ_CLSID_TENMONTHUMBHANDLER L"\\InProcServer32", NULL, szModuleName},
|
||||||
|
{HKEY_CURRENT_USER, L"Software\\Classes\\CLSID\\" SZ_CLSID_TENMONTHUMBHANDLER L"\\InProcServer32", L"ThreadingModel", L"Apartment"},
|
||||||
|
{HKEY_CURRENT_USER, L"Software\\Classes\\.xisf\\ShellEx\\{e357fccd-a995-4576-b01f-234630154e96}", NULL, SZ_CLSID_TENMONTHUMBHANDLER},
|
||||||
|
{HKEY_CURRENT_USER, L"Software\\Classes\\.fits\\ShellEx\\{e357fccd-a995-4576-b01f-234630154e96}", NULL, SZ_CLSID_TENMONTHUMBHANDLER},
|
||||||
|
{HKEY_CURRENT_USER, L"Software\\Classes\\.fit\\ShellEx\\{e357fccd-a995-4576-b01f-234630154e96}", NULL, SZ_CLSID_TENMONTHUMBHANDLER},
|
||||||
|
};
|
||||||
|
|
||||||
|
hr = S_OK;
|
||||||
|
for (int i = 0; i < ARRAYSIZE(rgRegistryEntries) && SUCCEEDED(hr); i++)
|
||||||
|
{
|
||||||
|
hr = CreateRegKeyAndSetValue(&rgRegistryEntries[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
// This tells the shell to invalidate the thumbnail cache. This is important because any .xisf files
|
||||||
|
// viewed before registering this handler would otherwise show cached blank thumbnails.
|
||||||
|
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
|
||||||
|
}
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Unregisters this COM server
|
||||||
|
//
|
||||||
|
STDAPI DllUnregisterServer()
|
||||||
|
{
|
||||||
|
HRESULT hr = S_OK;
|
||||||
|
|
||||||
|
const PCWSTR rgpszKeys[] =
|
||||||
|
{
|
||||||
|
L"Software\\Classes\\CLSID\\" SZ_CLSID_TENMONTHUMBHANDLER,
|
||||||
|
L"Software\\Classes\\.xisf\\ShellEx\\{e357fccd-a995-4576-b01f-234630154e96}"
|
||||||
|
L"Software\\Classes\\.fits\\ShellEx\\{e357fccd-a995-4576-b01f-234630154e96}"
|
||||||
|
L"Software\\Classes\\.fit\\ShellEx\\{e357fccd-a995-4576-b01f-234630154e96}"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Delete the registry entries
|
||||||
|
for (int i = 0; i < ARRAYSIZE(rgpszKeys) && SUCCEEDED(hr); i++)
|
||||||
|
{
|
||||||
|
hr = HRESULT_FROM_WIN32(RegDeleteTreeW(HKEY_CURRENT_USER, rgpszKeys[i]));
|
||||||
|
if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
|
||||||
|
{
|
||||||
|
// If the registry entry has already been deleted, say S_OK.
|
||||||
|
hr = S_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
#include <shlwapi.h>
|
||||||
|
#include <thumbcache.h> // For IThumbnailProvider.
|
||||||
|
#include <new>
|
||||||
|
#include "libxisf.h"
|
||||||
|
|
||||||
|
bool loadXISF(const LibXISF::ByteArray &data, HBITMAP *hbmp, UINT thumbSize);
|
||||||
|
bool loadFITS(const LibXISF::ByteArray &data, HBITMAP *hbmp, UINT thumbSize);
|
||||||
|
|
||||||
|
class TenmonThumbProvider : public IInitializeWithStream,
|
||||||
|
public IThumbnailProvider
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TenmonThumbProvider() : _cRef(1), _pStream(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~TenmonThumbProvider()
|
||||||
|
{
|
||||||
|
if (_pStream)
|
||||||
|
{
|
||||||
|
_pStream->Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IUnknown
|
||||||
|
IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv)
|
||||||
|
{
|
||||||
|
static const QITAB qit[] =
|
||||||
|
{
|
||||||
|
QITABENT(TenmonThumbProvider, IInitializeWithStream),
|
||||||
|
QITABENT(TenmonThumbProvider, IThumbnailProvider),
|
||||||
|
{ 0 },
|
||||||
|
};
|
||||||
|
return QISearch(this, qit, riid, ppv);
|
||||||
|
}
|
||||||
|
|
||||||
|
IFACEMETHODIMP_(ULONG) AddRef()
|
||||||
|
{
|
||||||
|
return InterlockedIncrement(&_cRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
IFACEMETHODIMP_(ULONG) Release()
|
||||||
|
{
|
||||||
|
ULONG cRef = InterlockedDecrement(&_cRef);
|
||||||
|
if (!cRef)
|
||||||
|
{
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
return cRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
// IInitializeWithStream
|
||||||
|
IFACEMETHODIMP Initialize(IStream *pStream, DWORD grfMode);
|
||||||
|
|
||||||
|
// IThumbnailProvider
|
||||||
|
IFACEMETHODIMP GetThumbnail(UINT cx, HBITMAP *phbmp, WTS_ALPHATYPE *pdwAlpha);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
long _cRef;
|
||||||
|
IStream *_pStream; // provided during initialization.
|
||||||
|
};
|
||||||
|
|
||||||
|
HRESULT TenmonThumbnailer_CreateInstance(REFIID riid, void **ppv)
|
||||||
|
{
|
||||||
|
TenmonThumbProvider *pNew = new (std::nothrow) TenmonThumbProvider();
|
||||||
|
HRESULT hr = pNew ? S_OK : E_OUTOFMEMORY;
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
hr = pNew->QueryInterface(riid, ppv);
|
||||||
|
pNew->Release();
|
||||||
|
}
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// IInitializeWithStream
|
||||||
|
IFACEMETHODIMP TenmonThumbProvider::Initialize(IStream *pStream, DWORD)
|
||||||
|
{
|
||||||
|
HRESULT hr = E_UNEXPECTED; // can only be inited once
|
||||||
|
if (_pStream == NULL)
|
||||||
|
{
|
||||||
|
// take a reference to the stream if we have not been inited yet
|
||||||
|
hr = pStream->QueryInterface(&_pStream);
|
||||||
|
}
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// IThumbnailProvider
|
||||||
|
IFACEMETHODIMP TenmonThumbProvider::GetThumbnail(UINT cx, HBITMAP *phbmp, WTS_ALPHATYPE *pdwAlpha)
|
||||||
|
{
|
||||||
|
LibXISF::ByteArray data;
|
||||||
|
ULONG readSize = 0;
|
||||||
|
ULONG read;
|
||||||
|
data.resize(1024*1024);
|
||||||
|
|
||||||
|
while(_pStream->Read(data.data() + readSize, data.size() - readSize, &read) == S_OK)
|
||||||
|
{
|
||||||
|
readSize += read;
|
||||||
|
data.resize(data.size() + 1024*1024);
|
||||||
|
}
|
||||||
|
readSize += read;
|
||||||
|
|
||||||
|
*pdwAlpha = WTSAT_RGB;
|
||||||
|
|
||||||
|
data.resize(readSize);
|
||||||
|
if(data[0] == 'X' && data[1] == 'I' && data[2] == 'S' && data[3] == 'F')
|
||||||
|
{
|
||||||
|
if(loadXISF(data, phbmp, cx))
|
||||||
|
return S_OK;
|
||||||
|
else
|
||||||
|
return E_FAIL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(loadFITS(data, phbmp, cx))
|
||||||
|
return S_OK;
|
||||||
|
else
|
||||||
|
return E_FAIL;
|
||||||
|
}
|
||||||
|
return E_FAIL;
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
#include "genthumbnail.h"
|
||||||
|
#include "../rawimage.h"
|
||||||
|
#include "../loadimage.h"
|
||||||
|
|
||||||
|
|
||||||
|
int generateThumbnail(const QString &input, const QString &output, uint32_t size)
|
||||||
|
{
|
||||||
|
ImageInfoData info;
|
||||||
|
std::shared_ptr<RawImage> rawImage;
|
||||||
|
if(!loadImage(input, info, rawImage, 0))
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
if(!rawImage)
|
||||||
|
return 3;
|
||||||
|
|
||||||
|
QSize rect(rawImage->width(), rawImage->height());
|
||||||
|
rect.scale(size, size, Qt::KeepAspectRatio);
|
||||||
|
rawImage->calcStats();
|
||||||
|
rawImage->resize(rect.width(), rect.height());
|
||||||
|
if(rawImage->imageStats().m_median[0] < rawImage->norm() * 0.2f)
|
||||||
|
{
|
||||||
|
MTFParam mtfParams = rawImage->calcMTFParams(true);
|
||||||
|
rawImage->applySTF(mtfParams);
|
||||||
|
}
|
||||||
|
rawImage->convertToType(RawImage::UINT8);
|
||||||
|
|
||||||
|
QImage img;
|
||||||
|
if(rawImage->channels() == 1)
|
||||||
|
img = QImage((const uchar*)rawImage->data(), rawImage->width(), rawImage->height(), rawImage->widthBytes(), QImage::Format_Grayscale8);
|
||||||
|
else
|
||||||
|
img = QImage((const uchar*)rawImage->data(), rawImage->width(), rawImage->height(), rawImage->widthBytes(), QImage::Format_RGBA8888);
|
||||||
|
|
||||||
|
if(!img.save(output, "png"))
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#ifndef GENTHUMBNAIL_H
|
||||||
|
#define GENTHUMBNAIL_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
int generateThumbnail(const QString &input, const QString &output, uint32_t size);
|
||||||
|
|
||||||
|
#endif // GENTHUMBNAIL_H
|
||||||
@@ -0,0 +1,237 @@
|
|||||||
|
#include "libxisf.h"
|
||||||
|
#include <thumbcache.h>
|
||||||
|
#include "../rawimage.h"
|
||||||
|
#include <fitsio2.h>
|
||||||
|
|
||||||
|
bool OpenGLES = false;
|
||||||
|
|
||||||
|
void RawImageToHTBITMAP(std::shared_ptr<RawImage> &rawImage, HBITMAP *hbmp, UINT thumbSize)
|
||||||
|
{
|
||||||
|
rawImage->calcStats();
|
||||||
|
|
||||||
|
DWORD thre = 10;
|
||||||
|
DWORD dataSize = 4;
|
||||||
|
//HRESULT hr = HRESULT_FROM_WIN32(RegGetValueW(HKEY_CURRENT_USER, L"SOFTWARE\\nou\\Tenmon\\settings", L"thumbnailstretchthreshold", RRF_RT_DWORD, NULL, &thre, &dataSize));
|
||||||
|
|
||||||
|
float thref = 0.1f;
|
||||||
|
/*if(hr == S_OK)
|
||||||
|
thref = thre / 100.0f;*/
|
||||||
|
|
||||||
|
if(rawImage->imageStats().m_mean[0] < rawImage->norm() * thref)
|
||||||
|
{
|
||||||
|
//OutputDebugStringA("Stretch image");
|
||||||
|
MTFParam params = rawImage->calcMTFParams();
|
||||||
|
rawImage->applySTF(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT w = rawImage->width();
|
||||||
|
UINT h = rawImage->height();
|
||||||
|
|
||||||
|
UINT cw = thumbSize;
|
||||||
|
UINT ch = thumbSize;
|
||||||
|
if (w > h)
|
||||||
|
ch = h * thumbSize / w;
|
||||||
|
else
|
||||||
|
cw = w * thumbSize / h;
|
||||||
|
|
||||||
|
|
||||||
|
rawImage->resize(cw, ch);
|
||||||
|
rawImage->convertToType(RawImage::UINT8);
|
||||||
|
|
||||||
|
BITMAPINFO bmi = {};
|
||||||
|
bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
|
||||||
|
bmi.bmiHeader.biWidth = cw;
|
||||||
|
bmi.bmiHeader.biHeight = -static_cast<LONG>(ch);
|
||||||
|
bmi.bmiHeader.biPlanes = 1;
|
||||||
|
bmi.bmiHeader.biBitCount = 32;
|
||||||
|
bmi.bmiHeader.biCompression = BI_RGB;
|
||||||
|
UINT lw = cw * 4;
|
||||||
|
|
||||||
|
BYTE *pBits;
|
||||||
|
HBITMAP bmp = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, reinterpret_cast<void **>(&pBits), NULL, 0);
|
||||||
|
|
||||||
|
const unsigned char *p = (const unsigned char*)rawImage->data();
|
||||||
|
const unsigned short *ps = (const unsigned short*)rawImage->data();
|
||||||
|
if(rawImage->channels() == 1)
|
||||||
|
{
|
||||||
|
for(UINT y = 0; y < ch; y++)
|
||||||
|
{
|
||||||
|
for(UINT x = 0; x < cw; x++)
|
||||||
|
{
|
||||||
|
pBits[(y * lw) + x * 4 + 0] = p[y * cw + x];
|
||||||
|
pBits[(y * lw) + x * 4 + 1] = p[y * cw + x];
|
||||||
|
pBits[(y * lw) + x * 4 + 2] = p[y * cw + x];
|
||||||
|
pBits[(y * lw) + x * 4 + 3] = 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for(UINT y = 0; y < ch; y++)
|
||||||
|
{
|
||||||
|
for(UINT x = 0; x < cw; x++)
|
||||||
|
{
|
||||||
|
pBits[(y * lw) + x * 4 + 0] = p[y * cw * 4 + x * 4 + 2];
|
||||||
|
pBits[(y * lw) + x * 4 + 1] = p[y * cw * 4 + x * 4 + 1];
|
||||||
|
pBits[(y * lw) + x * 4 + 2] = p[y * cw * 4 + x * 4 + 0];
|
||||||
|
pBits[(y * lw) + x * 4 + 3] = 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*hbmp = bmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool loadXISF(const LibXISF::ByteArray &data, HBITMAP *hbmp, UINT thumbSize)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
LibXISF::XISFReader xisf;
|
||||||
|
xisf.open(data);
|
||||||
|
|
||||||
|
const LibXISF::Image &xisfImage = xisf.getImage(0);
|
||||||
|
|
||||||
|
RawImage::DataType type;
|
||||||
|
switch(xisfImage.sampleFormat())
|
||||||
|
{
|
||||||
|
case LibXISF::Image::UInt8: type = RawImage::UINT8; break;
|
||||||
|
case LibXISF::Image::UInt16: type = RawImage::UINT16; break;
|
||||||
|
case LibXISF::Image::UInt32: type = RawImage::UINT32; break;
|
||||||
|
case LibXISF::Image::Float32: type = RawImage::FLOAT32; break;
|
||||||
|
case LibXISF::Image::Float64: type = RawImage::FLOAT64; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
LibXISF::Image tmpImage = xisfImage;
|
||||||
|
tmpImage.convertPixelStorageTo(LibXISF::Image::Planar);
|
||||||
|
std::shared_ptr<RawImage> rawImage;
|
||||||
|
|
||||||
|
if(tmpImage.colorSpace() == LibXISF::Image::ColorSpace::Gray)
|
||||||
|
{
|
||||||
|
rawImage = std::make_shared<RawImage>(tmpImage.width(), tmpImage.height(), 1, type);
|
||||||
|
std::memcpy(rawImage->data(), tmpImage.imageData(), tmpImage.imageDataSize() / tmpImage.channelCount());
|
||||||
|
}
|
||||||
|
else if(tmpImage.channelCount() == 3 || tmpImage.channelCount() == 4)
|
||||||
|
{
|
||||||
|
rawImage = RawImage::fromPlanar(tmpImage.imageData(), tmpImage.width(), tmpImage.height(), tmpImage.channelCount(), type);
|
||||||
|
}
|
||||||
|
|
||||||
|
RawImageToHTBITMAP(rawImage, hbmp, thumbSize);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (LibXISF::Error &err)
|
||||||
|
{
|
||||||
|
char text[1024];
|
||||||
|
sprintf_s(text, 1000, "Failed to open XISF image %s", err.what());
|
||||||
|
OutputDebugStringA(text);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool loadFITS(const LibXISF::ByteArray &data, HBITMAP *hbmp, UINT thumbSize)
|
||||||
|
{
|
||||||
|
fitsfile *file;
|
||||||
|
|
||||||
|
int status = 0;
|
||||||
|
int type = -1;
|
||||||
|
int num = 0;
|
||||||
|
long naxes[3] = {0};
|
||||||
|
|
||||||
|
auto checkError = [&status]()
|
||||||
|
{
|
||||||
|
char err[100];
|
||||||
|
char text[1000];
|
||||||
|
fits_get_errstatus(status, err);
|
||||||
|
sprintf_s(text, 1000, "Failed to load FITS file %s", err);
|
||||||
|
OutputDebugStringA(text);
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const void *dataPtr = data.data();
|
||||||
|
size_t size = data.size();
|
||||||
|
fits_open_memfile(&file, "file.fits", READONLY, (void**)&dataPtr, &size, 0, nullptr, &status);
|
||||||
|
if(status)return checkError();
|
||||||
|
fits_get_num_hdus(file, &num, &status);
|
||||||
|
if(status)return checkError();
|
||||||
|
|
||||||
|
int imgtype;
|
||||||
|
int naxis;
|
||||||
|
for(int i=1; i <= num; i++)
|
||||||
|
{
|
||||||
|
fits_movabs_hdu(file, i, IMAGE_HDU, &status);if(status)return checkError();
|
||||||
|
fits_get_hdu_type(file, &type, &status);if(status)return checkError();
|
||||||
|
fits_get_img_param(file, 3, &imgtype, &naxis, naxes, &status);if(status)return checkError();
|
||||||
|
fits_get_img_equivtype(file, &imgtype, &status);if(status)return checkError();
|
||||||
|
|
||||||
|
if(type == IMAGE_HDU && naxis >= 2 && naxis <= 3 && status == 0)
|
||||||
|
{
|
||||||
|
RawImage::DataType type;
|
||||||
|
int fitstype;
|
||||||
|
long fpixel[3] = {1,1,1};
|
||||||
|
switch(imgtype)
|
||||||
|
{
|
||||||
|
case BYTE_IMG:
|
||||||
|
type = RawImage::UINT8;
|
||||||
|
fitstype = TBYTE;
|
||||||
|
break;
|
||||||
|
case SHORT_IMG:
|
||||||
|
type = RawImage::UINT16;
|
||||||
|
fitstype = TSHORT;
|
||||||
|
break;
|
||||||
|
case USHORT_IMG:
|
||||||
|
type = RawImage::UINT16;
|
||||||
|
fitstype = TUSHORT;
|
||||||
|
break;
|
||||||
|
case ULONG_IMG:
|
||||||
|
type = RawImage::UINT32;
|
||||||
|
fitstype = TUINT;
|
||||||
|
break;
|
||||||
|
case FLOAT_IMG:
|
||||||
|
type = RawImage::FLOAT32;
|
||||||
|
fitstype = TFLOAT;
|
||||||
|
break;
|
||||||
|
case DOUBLE_IMG:
|
||||||
|
type = RawImage::FLOAT64;
|
||||||
|
fitstype = TDOUBLE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size = naxes[0]*naxes[1];
|
||||||
|
size_t w = naxes[0];
|
||||||
|
size_t h = naxes[1];
|
||||||
|
|
||||||
|
RawImage img(w, h, naxis == 2 ? 1 : naxes[2], type);
|
||||||
|
uint8_t *data = static_cast<uint8_t*>(img.data());
|
||||||
|
for (int i=1; i==1 || i<=naxes[2]; i++)
|
||||||
|
{
|
||||||
|
fpixel[2] = i;
|
||||||
|
fits_read_pix(file, fitstype, fpixel, size, NULL, data + img.size() * RawImage::typeSize(type) * (i-1), NULL, &status);
|
||||||
|
if(status)return checkError();
|
||||||
|
}
|
||||||
|
if(fitstype == TSHORT)
|
||||||
|
{
|
||||||
|
uint16_t *s = static_cast<uint16_t*>(img.data());
|
||||||
|
size_t size = img.size() * img.channels();
|
||||||
|
for(size_t i=0; i<size; i++)
|
||||||
|
s[i] -= INT16_MIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<RawImage> image;
|
||||||
|
if(img.channels() == 1)
|
||||||
|
image = std::make_shared<RawImage>(std::move(img));
|
||||||
|
else
|
||||||
|
image = RawImage::fromPlanar(img);
|
||||||
|
|
||||||
|
RawImageToHTBITMAP(image, hbmp, thumbSize);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
+24
-10
@@ -28,21 +28,35 @@ int main(int argc, char *argv[])
|
|||||||
ImageInfoData info;
|
ImageInfoData info;
|
||||||
std::shared_ptr<RawImage> rawImage;
|
std::shared_ptr<RawImage> rawImage;
|
||||||
if(!loadImage(input, info, rawImage))
|
if(!loadImage(input, info, rawImage))
|
||||||
return 1;
|
|
||||||
|
|
||||||
if(!rawImage)
|
|
||||||
return 2;
|
return 2;
|
||||||
|
|
||||||
|
if(!rawImage)
|
||||||
|
return 3;
|
||||||
|
|
||||||
|
bool ok;
|
||||||
|
int size = parser.value("s").toInt(&ok);
|
||||||
|
if(!ok)
|
||||||
|
size = 128;
|
||||||
|
|
||||||
|
QSize rect(rawImage->width(), rawImage->height());
|
||||||
|
rect.scale(size, size, Qt::KeepAspectRatio);
|
||||||
|
rawImage->calcStats();
|
||||||
|
rawImage->resize(rect.width(), rect.height());
|
||||||
|
if(rawImage->imageStats().m_median[0] < rawImage->norm() * 0.2f)
|
||||||
|
{
|
||||||
|
MTFParam mtfParams = rawImage->calcMTFParams(true);
|
||||||
|
rawImage->applySTF(mtfParams);
|
||||||
|
}
|
||||||
rawImage->convertToType(RawImage::UINT8);
|
rawImage->convertToType(RawImage::UINT8);
|
||||||
|
|
||||||
QImage img((const uchar*)rawImage->data(), rawImage->width(), rawImage->height(), QImage::Format_RGBA8888);
|
QImage img;
|
||||||
bool ok = false;
|
if(rawImage->channels() == 1)
|
||||||
int size = parser.value("s").toInt(&ok);
|
img = QImage((const uchar*)rawImage->data(), rawImage->width(), rawImage->height(), rawImage->widthBytes(), QImage::Format_Grayscale8);
|
||||||
if(!ok)size = 128;
|
else
|
||||||
img = img.scaled(size, size, Qt::KeepAspectRatio);
|
img = QImage((const uchar*)rawImage->data(), rawImage->width(), rawImage->height(), rawImage->widthBytes(), QImage::Format_RGBA8888);
|
||||||
img.save(output, "png");
|
|
||||||
|
|
||||||
//rawImage->convertTosRGB();
|
if(!img.save(output, "png"))
|
||||||
|
return 4;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
bool OpenGLES = false;
|
|
||||||
Reference in New Issue
Block a user