diff --git a/image.frag b/image.frag index b538d56..fff497e 100644 --- a/image.frag +++ b/image.frag @@ -1,10 +1,10 @@ #version 130 uniform sampler2D qt_Texture0; -in vec2 qt_TexCoord0; uniform vec3 mtf_param; uniform bool bw; uniform bool invert; +in vec2 qt_TexCoord0; out vec4 color; vec4 MTF(vec4 x, vec3 m) @@ -16,7 +16,7 @@ vec4 MTF(vec4 x, vec3 m) void main(void) { - color = texture2D(qt_Texture0, qt_TexCoord0); + color = texture(qt_Texture0, qt_TexCoord0); if(bw)color = color.rrra; color = MTF(color, mtf_param); diff --git a/imageringlist.cpp b/imageringlist.cpp index 23ee944..d09b921 100644 --- a/imageringlist.cpp +++ b/imageringlist.cpp @@ -8,10 +8,11 @@ using namespace std; const int DEFAULT_WIDTH = 2; -Image::Image(const QString name, ImageRingList *ringList) : +Image::Image(const QString name, int number, ImageRingList *ringList) : m_loading(false), m_released(true), m_current(false), + m_number(number), m_name(name), m_ringList(ringList) { @@ -29,6 +30,14 @@ void Image::load() emit pixmapLoaded(this); } +void Image::loadThumbnail() +{ + if(!m_thumbnail) + QThreadPool::globalInstance()->start(new LoadRunable(m_name, this, AnalyzeLevel::None, true)); + else + emit thumbnailLoaded(this); +} + void Image::release() { m_rawImage.reset(); @@ -46,6 +55,11 @@ RawImage *Image::rawImage() return m_rawImage.get(); } +const RawImage *Image::thumbnail() const +{ + return m_thumbnail.get(); +} + ImageInfoData Image::info() const { return m_info; @@ -56,6 +70,11 @@ bool Image::isCurrent() const return !m_released; } +int Image::number() const +{ + return m_number; +} + void Image::imageLoaded(void *rawImage, ImageInfoData info) { m_loading = false; @@ -71,6 +90,13 @@ void Image::imageLoaded(void *rawImage, ImageInfoData info) } } +void Image::thumbnailLoadFinish(void *rawImage) +{ + m_thumbnail.reset(static_cast(rawImage)); + if(m_thumbnail) + emit thumbnailLoaded(this); +} + ImageRingList::ImageRingList(QObject *parent) : QAbstractItemModel(parent) , m_liveMode(false) , m_analyzeLevel(None) @@ -93,7 +119,7 @@ bool ImageRingList::setDir(const QString path, const QString ¤tFile) QStringList nameFilter; nameFilter << "*.jpg" << "*.jpeg" << "*.png" << "*.cr2" << "*.fit" << "*.fits" << "*.xisf"; - QStringList list = dir.entryList(nameFilter, QDir::Files | QDir::Readable, m_liveMode ? QDir::Time : QDir::Name); + QStringList list = dir.entryList(nameFilter, QDir::Files | QDir::Readable, m_liveMode ? QDir::Time : QDir::Name | QDir::IgnoreCase); QStringList absolutePaths; foreach(const QString &file, list) { @@ -209,6 +235,24 @@ void ImageRingList::loadFile(int row) } } +void ImageRingList::loadThumbnails() +{ + for(auto &img : m_images) + { + img->loadThumbnail(); + } +} + +void ImageRingList::stopLoading() +{ + QThreadPool::globalInstance()->clear(); +} + +int ImageRingList::imageCount() const +{ + return m_images.size(); +} + QModelIndex ImageRingList::index(int row, int column, const QModelIndex &parent) const { return createIndex(row, column, m_images.at(row).get()); @@ -261,10 +305,12 @@ void ImageRingList::setFiles(const QStringList files, const QString ¤tFile QThreadPool::globalInstance()->waitForDone(); beginResetModel(); m_images.clear(); - foreach(const QString &file, files) + int i = 0; + for(const QString &file : files) { - ImagePtr ptr = make_shared(file, this); + ImagePtr ptr = make_shared(file, i++, this); connect(ptr.get(), SIGNAL(pixmapLoaded(Image*)), this, SLOT(imageLoaded(Image*))); + connect(ptr.get(), SIGNAL(thumbnailLoaded(Image*)), this, SIGNAL(thumbnailLoaded(Image*))); m_images.append(ptr); } diff --git a/imageringlist.h b/imageringlist.h index c9daca4..3a5b537 100644 --- a/imageringlist.h +++ b/imageringlist.h @@ -17,22 +17,29 @@ class Image : public QObject bool m_loading; bool m_released; bool m_current; + int m_number; std::unique_ptr m_rawImage; + std::unique_ptr m_thumbnail; QString m_name; ImageInfoData m_info; ImageRingList *m_ringList; public: - explicit Image(const QString name, ImageRingList *ringList); + explicit Image(const QString name, int number, ImageRingList *ringList); void load(); + void loadThumbnail(); void release(); QString name() const; RawImage* rawImage(); + const RawImage* thumbnail() const; ImageInfoData info() const; bool isCurrent() const; + int number() const; signals: void pixmapLoaded(Image *ptr); + void thumbnailLoaded(Image *ptr); protected slots: void imageLoaded(void *rawImage, ImageInfoData info); + void thumbnailLoadFinish(void *rawImage); }; typedef std::shared_ptr ImagePtr; @@ -62,6 +69,9 @@ public: void setFindStars(bool findStars); AnalyzeLevel analyzeLevel() const; void loadFile(int row); + void loadThumbnails(); + void stopLoading(); + int imageCount() const; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &child) const override; @@ -75,6 +85,7 @@ protected: QList::iterator decrement(QList::iterator iter); signals: void pixmapLoaded(Image *image); + void thumbnailLoaded(Image *image); void infoLoaded(ImageInfoData info); void currentImageChanged(int index); protected slots: diff --git a/imagescrollareagl.cpp b/imagescrollareagl.cpp index a162efd..fbd4630 100644 --- a/imagescrollareagl.cpp +++ b/imagescrollareagl.cpp @@ -55,6 +55,8 @@ ImageWidget::ImageWidget(QWidget *parent) : QOpenGLWidget(parent) m_range = UINT16_MAX; m_imgWidth = m_imgHeight = -1; m_superpixel = m_invert = false; + m_showThumbnails = false; + m_thumbnailCount = 0; setAcceptDrops(true); } @@ -115,6 +117,19 @@ void ImageWidget::blockRepaint(bool block) if(!block)update(); } +void ImageWidget::allocateThumbnails(int count) +{ + m_thumbnailTexture->destroy(); + m_thumbnailTexture->create(); + m_thumbnailTexture->setFormat(QOpenGLTexture::RGB16_UNorm); + m_thumbnailTexture->setSize(THUMB_SIZE, THUMB_SIZE); + m_thumbnailTexture->setLayers(count); + m_thumbnailTexture->allocateStorage(); + m_bufferSizes->bind(); + m_bufferSizes->allocate(count * sizeof(float)*3); + m_thumbnailCount = count; +} + void ImageWidget::setMTFParams(float low, float mid, float high) { m_low = low; @@ -164,6 +179,23 @@ QImage ImageWidget::renderToImage() return fbo.toImage(true); } +void ImageWidget::thumbnailLoaded(const Image *image) +{ + const RawImage *raw = image->thumbnail(); + m_thumbnailTexture->setData(0, image->number(), QOpenGLTexture::RGB, QOpenGLTexture::UInt16, raw->data(), m_transferOptions.get()); + float a = raw->thumbAspect(); + int sizes[3] = { std::max(1, a > 1.0f ? THUMB_SIZE : (int)(THUMB_SIZE * a)), std::max(1, a < 1.0f ? THUMB_SIZE : (int)(THUMB_SIZE / a)), image->number() }; + m_bufferSizes->bind(); + m_bufferSizes->write(image->number() * sizeof(sizes), sizes, sizeof(sizes)); + update(); +} + +void ImageWidget::showThumbnail(bool enable) +{ + m_showThumbnails = enable; + update(); +} + void ImageWidget::paintGL() { if(m_blockRepaint)return; @@ -175,16 +207,35 @@ void ImageWidget::paintGL() if(height() > m_image->height()*m_scale) dy = -height()*0.5f + m_image->height()*m_scale*0.5f; - m_program->bind(); - m_program->setUniformValue("viewport", (float)width(), (float)height()); - m_program->setUniformValue("offset", dx, dy); - m_program->setUniformValue("mtf_param", m_low, m_mid, m_high); - m_program->setUniformValue("zoom", 1.0f/m_scale); - m_program->setUniformValue("bw", m_bwImg); - m_program->setUniformValue("invert", m_invert); + if(m_showThumbnails) + { + m_vaoThumb->bind(); + m_thumbnailTexture->bind(1); + m_thumbnailProgram->bind(); + f->glUniform3i(m_thumbnailProgram->uniformLocation("viewport_row"), width(), height(), width()/THUMB_SIZE_BORDER); + m_thumbnailProgram->setUniformValue("mtf_param", m_low, m_mid, m_high); + m_thumbnailProgram->setUniformValue("invert", m_invert); + m_thumbnailProgram->setUniformValue("offset", m_dx, m_dy); + f3->glVertexAttribDivisor(m_thumbnailProgram->attributeLocation("imageSize_num"), 1); + QMatrix4x4 mvp; + mvp.ortho(rect()); + m_thumbnailProgram->setUniformValue("mvp", mvp); + if(f3)f3->glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, m_thumbnailCount); + } + else + { + m_vao->bind(); + m_image->bind(0); + m_program->bind(); + m_program->setUniformValue("viewport", (float)width(), (float)height()); + m_program->setUniformValue("offset", dx, dy); + m_program->setUniformValue("mtf_param", m_low, m_mid, m_high); + m_program->setUniformValue("zoom", 1.0f/m_scale); + m_program->setUniformValue("bw", m_bwImg); + m_program->setUniformValue("invert", m_invert); + f->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + } - m_image->bind(0); - f->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } void ImageWidget::resizeGL(int w, int h) @@ -198,10 +249,12 @@ void ImageWidget::initializeGL() { f = context()->functions(); f->glClearColor(0.5f, 0.5f, 0.5f, 1.0f); - QOpenGLFunctions_3_2_Core *f3 = context()->versionFunctions(); + f3 = context()->versionFunctions(); m_vao = std::unique_ptr(new QOpenGLVertexArrayObject); + m_vaoThumb = std::unique_ptr(new QOpenGLVertexArrayObject); m_vao->create(); + m_vaoThumb->create(); m_vao->bind(); QOpenGLDebugLogger *logger = new QOpenGLDebugLogger(this); @@ -228,7 +281,7 @@ void ImageWidget::initializeGL() m_buffer->create(); m_buffer->bind(); m_buffer->allocate(vertexs, sizeof(vertexs)); - f->glVertexAttribPointer(0, 2, GL_FLOAT, false, sizeof(float)*4, 0); + // f->glVertexAttribPointer(0, 2, GL_FLOAT, false, sizeof(float)*4, 0); m_program = std::unique_ptr(new QOpenGLShaderProgram); m_program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/image.vert"); @@ -247,6 +300,33 @@ void ImageWidget::initializeGL() m_program->setUniformValue("qt_Texture0", (GLuint)0); m_program->setUniformValue("scale", 1.0f, 0.0f); + m_vaoThumb->bind(); + + m_thumbnailProgram = std::unique_ptr(new QOpenGLShaderProgram); + m_thumbnailProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/thumb.vert"); + m_thumbnailProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/thumb.frag"); + if(f3)f3->glBindFragDataLocation(m_program->programId(), 0, "color"); + m_thumbnailProgram->bind(); + m_thumbnailProgram->enableAttributeArray("qt_Vertex"); + m_thumbnailProgram->setAttributeBuffer("qt_Vertex", GL_FLOAT, 0, 2, sizeof(float)*4); + m_thumbnailProgram->enableAttributeArray("qt_MultiTexCoord0"); + m_thumbnailProgram->setAttributeBuffer("qt_MultiTexCoord0", GL_FLOAT, sizeof(float)*2, 2, sizeof(float)*4); + if(!m_thumbnailProgram->link()) + { + qDebug() << "Link failed" << m_thumbnailProgram->log(); + } + m_thumbnailProgram->setUniformValue("qt_Texture0", (GLuint)1); + + m_bufferSizes = std::unique_ptr(new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer)); + m_bufferSizes->setUsagePattern(QOpenGLBuffer::StaticDraw); + m_bufferSizes->create(); + m_bufferSizes->bind(); + + m_bufferSizes->allocate(12); + m_thumbnailProgram->enableAttributeArray("imageSize_num"); + m_thumbnailProgram->setAttributeBuffer("imageSize_num", GL_FLOAT, 0, 3); + f3->glVertexAttribDivisor(m_thumbnailProgram->attributeLocation("imageSize_num"), 1); + m_image = std::unique_ptr(new QOpenGLTexture(QOpenGLTexture::Target2D)); m_image->setFormat(QOpenGLTexture::RGB8U); m_image->allocateStorage(); @@ -254,6 +334,15 @@ void ImageWidget::initializeGL() m_image->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear); m_image->setMagnificationFilter(QOpenGLTexture::Linear); + m_thumbnailTexture = std::unique_ptr(new QOpenGLTexture(QOpenGLTexture::Target2DArray)); + m_thumbnailTexture->setFormat(QOpenGLTexture::RGB16_UNorm); + m_thumbnailTexture->setSize(THUMB_SIZE, THUMB_SIZE); + m_thumbnailTexture->setLayers(1); + m_thumbnailTexture->allocateStorage(); + m_thumbnailTexture->bind(1); + m_thumbnailTexture->setMinificationFilter(QOpenGLTexture::Linear); + m_thumbnailTexture->setMinificationFilter(QOpenGLTexture::Linear); + m_transferOptions = std::unique_ptr(new QOpenGLPixelTransferOptions); m_transferOptions->setAlignment(1); } @@ -292,6 +381,7 @@ ImageScrollAreaGL::ImageScrollAreaGL(QWidget *parent) : QWidget(parent) m_horizontalScrollBar = new QScrollBar(Qt::Horizontal, this); m_scale = 1.0f; m_bestFit = false; + m_thumbCount = 0; layout->setSpacing(0); layout->addWidget(m_imageWidget, 0, 0); @@ -330,32 +420,66 @@ ImageWidget *ImageScrollAreaGL::imageWidget() return m_imageWidget; } -void ImageScrollAreaGL::updateScrollbars(bool zoom) +void ImageScrollAreaGL::setThumbnails(int count) { - if(zoom) + m_thumbCount = count; + if(m_thumbCount) { - setScrollRange(m_verticalScrollBar, m_imgHeight*m_scale); - setScrollRange(m_horizontalScrollBar, m_imgWidth*m_scale); + m_verticalScrollBar->setRange(0, (m_thumbCount / (m_imageWidget->width() / THUMB_SIZE_BORDER) + 1) * THUMB_SIZE_BORDER); + m_verticalScrollBar->setPageStep(THUMB_SIZE); } else { - m_verticalScrollBar->setRange(0, m_imgHeight*m_scale - m_verticalScrollBar->pageStep()); - m_horizontalScrollBar->setRange(0, m_imgWidth*m_scale - m_horizontalScrollBar->pageStep()); + m_verticalScrollBar->setPageStep(m_imageWidget->height()); + m_horizontalScrollBar->setPageStep(m_imageWidget->width()); + } + updateScrollbars(); +} + +void ImageScrollAreaGL::updateScrollbars(bool zoom) +{ + if(m_thumbCount) + { + m_horizontalScrollBar->hide(); + m_verticalScrollBar->show(); + m_verticalScrollBar->setRange(0, (m_thumbCount / (m_imageWidget->width() / THUMB_SIZE_BORDER) + 1) * THUMB_SIZE_BORDER); + m_verticalScrollBar->setPageStep(128); + } + else + { + if(zoom) + { + setScrollRange(m_verticalScrollBar, m_imgHeight*m_scale); + setScrollRange(m_horizontalScrollBar, m_imgWidth*m_scale); + } + else + { + m_verticalScrollBar->setRange(0, m_imgHeight*m_scale - m_verticalScrollBar->pageStep()); + m_horizontalScrollBar->setRange(0, m_imgWidth*m_scale - m_horizontalScrollBar->pageStep()); + } } } void ImageScrollAreaGL::resizeEvent(QResizeEvent *event) { QWidget::resizeEvent(event); - m_verticalScrollBar->setPageStep(m_imageWidget->height()); - m_horizontalScrollBar->setPageStep(m_imageWidget->width()); + if(m_thumbCount) + { + m_verticalScrollBar->setRange(0, (m_thumbCount / (m_imageWidget->width() / THUMB_SIZE_BORDER) + 1) * THUMB_SIZE_BORDER); + m_verticalScrollBar->setPageStep(128); + } + else + { + m_verticalScrollBar->setPageStep(m_imageWidget->height()); + m_horizontalScrollBar->setPageStep(m_imageWidget->width()); + } updateScrollbars(); } void ImageScrollAreaGL::mouseMoveEvent(QMouseEvent *event) { QPoint delta = m_lastPos - event->pos(); - m_horizontalScrollBar->setValue(m_horizontalScrollBar->value() + delta.x()); + if(m_thumbCount == 0)m_horizontalScrollBar->setValue(m_horizontalScrollBar->value() + delta.x()); m_verticalScrollBar->setValue(m_verticalScrollBar->value() + delta.y()); m_lastPos = event->pos(); } @@ -367,9 +491,16 @@ void ImageScrollAreaGL::mousePressEvent(QMouseEvent *event) void ImageScrollAreaGL::wheelEvent(QWheelEvent *event) { - m_bestFit = false; - if(event->angleDelta().y() != 0) - zoom(event->angleDelta().y() / 1200.0f); + if(m_thumbCount) + { + m_verticalScrollBar->setValue(m_verticalScrollBar->value() - event->angleDelta().y()); + } + else + { + m_bestFit = false; + if(event->angleDelta().y() != 0) + zoom(event->angleDelta().y() / 1200.0f); + } } void ImageScrollAreaGL::zoom(float delta) diff --git a/imagescrollareagl.h b/imagescrollareagl.h index 2eb7363..5a80fdf 100644 --- a/imagescrollareagl.h +++ b/imagescrollareagl.h @@ -4,13 +4,14 @@ #include #include #include -#include +#include #include #include #include #include #include #include "rawimage.h" +#include "imageringlist.h" typedef enum { @@ -25,11 +26,16 @@ class ImageWidget : public QOpenGLWidget { Q_OBJECT QOpenGLFunctions *f; + QOpenGLFunctions_3_3_Core *f3; std::unique_ptr m_program; + std::unique_ptr m_thumbnailProgram; std::unique_ptr m_buffer; + std::unique_ptr m_bufferSizes; std::unique_ptr m_image; std::unique_ptr m_vao; + std::unique_ptr m_vaoThumb; std::unique_ptr m_transferOptions; + std::unique_ptr m_thumbnailTexture; int m_width, m_height; int m_imgWidth, m_imgHeight; float m_low; @@ -42,6 +48,8 @@ class ImageWidget : public QOpenGLWidget bool m_bwImg; bool m_invert; bool m_superpixel; + bool m_showThumbnails; + int m_thumbnailCount; public: explicit ImageWidget(QWidget *parent = nullptr); ~ImageWidget(); @@ -49,12 +57,15 @@ public: void setImage(const QPixmap &pixmap); void setScale(float scale); void blockRepaint(bool block); + void allocateThumbnails(int count); public slots: void setMTFParams(float low, float mid, float high); void setOffset(int dx, int dy); void superPixel(bool enable); void invert(bool enable); QImage renderToImage(); + void thumbnailLoaded(const Image *image); + void showThumbnail(bool enable); protected: void paintGL(); void resizeGL(int w, int h); @@ -75,12 +86,14 @@ class ImageScrollAreaGL : public QWidget QPoint m_lastPos; float m_scale; bool m_bestFit; + int m_thumbCount; public: explicit ImageScrollAreaGL(QWidget *parent = nullptr); ~ImageScrollAreaGL(); void setImage(RawImage *image); void setImage(const QPixmap &pixmap); ImageWidget* imageWidget(); + void setThumbnails(int count); protected: void updateScrollbars(bool zoom = false); void resizeEvent(QResizeEvent *event); diff --git a/loadrunable.cpp b/loadrunable.cpp index f66bf57..b8d1a9a 100644 --- a/loadrunable.cpp +++ b/loadrunable.cpp @@ -12,11 +12,13 @@ #include #include "rawimage.h" #include "starfit.h" +#include -LoadRunable::LoadRunable(const QString &file, Image *receiver, AnalyzeLevel level) : +LoadRunable::LoadRunable(const QString &file, Image *receiver, AnalyzeLevel level, bool thumbnail) : m_file(file), m_receiver(receiver), - m_analyzeLevel(level) + m_analyzeLevel(level), + m_thumbnail(thumbnail) { } @@ -334,7 +336,7 @@ bool loadXISF(const QString &path, ImageInfoData &info, RawImage **image) void LoadRunable::run() { - if(!m_receiver->isCurrent()) + if(!m_thumbnail && !m_receiver->isCurrent()) { return; } @@ -375,7 +377,7 @@ void LoadRunable::run() rawImage = new RawImage(img); } - if(rawImage && m_analyzeLevel >= Statistics) + if(rawImage && m_analyzeLevel >= Statistics && !m_thumbnail) { double mean, median, min, max, mad; double stdDev; @@ -440,7 +442,20 @@ void LoadRunable::run() } } - QMetaObject::invokeMethod(m_receiver, "imageLoaded", Qt::QueuedConnection, Q_ARG(void*, rawImage), Q_ARG(ImageInfoData, info)); + if(m_thumbnail) + { + if(rawImage) + { + rawImage->convertToThumbnail(); + QMetaObject::invokeMethod(m_receiver, "thumbnailLoadFinish", Qt::QueuedConnection, Q_ARG(void*, rawImage)); + } + else + { + qDebug() << "failed"; + } + } + else + QMetaObject::invokeMethod(m_receiver, "imageLoaded", Qt::QueuedConnection, Q_ARG(void*, rawImage), Q_ARG(ImageInfoData, info)); } bool readFITSHeader(const QString &path, ImageInfoData &info) diff --git a/loadrunable.h b/loadrunable.h index abf0e65..1aab8bf 100644 --- a/loadrunable.h +++ b/loadrunable.h @@ -15,8 +15,9 @@ class LoadRunable : public QRunnable QString m_file; Image *m_receiver; AnalyzeLevel m_analyzeLevel; + bool m_thumbnail; public: - LoadRunable(const QString &file, Image *receiver, AnalyzeLevel level); + LoadRunable(const QString &file, Image *receiver, AnalyzeLevel level, bool thumbnail = false); void run(); }; diff --git a/main.cpp b/main.cpp index 08b5fc6..2f08282 100644 --- a/main.cpp +++ b/main.cpp @@ -6,7 +6,7 @@ int main(int argc, char *argv[]) { QSurfaceFormat format; format.setMajorVersion(3); - format.setMinorVersion(2); + format.setMinorVersion(3); format.setOption(QSurfaceFormat::DebugContext); format.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile); QSurfaceFormat::setDefaultFormat(format); diff --git a/mainwindow.cpp b/mainwindow.cpp index c663bd0..d12e0bb 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -80,6 +80,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) connect(m_ringList, SIGNAL(currentImageChanged(int)), this, SLOT(updateWindowTitle())); connect(m_ringList, SIGNAL(infoLoaded(ImageInfoData)), m_info, SLOT(setInfo(const ImageInfoData&))); connect(m_ringList, SIGNAL(currentImageChanged(int)), m_filesystem, SLOT(selectFile(int))); + connect(m_ringList, &ImageRingList::thumbnailLoaded, m_imageGL->imageWidget(), &ImageWidget::thumbnailLoaded); connect(m_imageGL->imageWidget(), &ImageWidget::fileDropped, this, static_cast(&MainWindow::loadFile)); QMenu *fileMenu = new QMenu(tr("File"), this); @@ -98,6 +99,14 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) viewMenu->addAction(tr("Best Fit"), m_imageGL, SLOT(bestFit()), QKeySequence("Ctrl+1")); viewMenu->addAction(tr("100%"), m_imageGL, SLOT(oneToOne())); viewMenu->addAction(tr("Fullscreen"), this, SLOT(toggleFullScreen()), QKeySequence::FullScreen); + QAction *thumbnailsAction = viewMenu->addAction(tr("Thumbnails"), [this](bool checked){ + m_imageGL->imageWidget()->allocateThumbnails(m_ringList->imageCount()); + m_imageGL->imageWidget()->showThumbnail(checked); + m_imageGL->setThumbnails(checked ? m_ringList->imageCount() : 0); + if(checked)m_ringList->loadThumbnails(); + else m_ringList->stopLoading(); + }, Qt::Key_F2); + thumbnailsAction->setCheckable(true); menuBar()->addMenu(viewMenu); QMenu *selectMenu = new QMenu(tr("Select"), this); diff --git a/rawimage.cpp b/rawimage.cpp index 9a5031d..1ccc903 100644 --- a/rawimage.cpp +++ b/rawimage.cpp @@ -1,5 +1,4 @@ #include "rawimage.h" -#include RawImage::ImgType CV2Type(int cvtype) { @@ -259,3 +258,33 @@ const void *RawImage::data() const { return m_img.ptr(); } + +void RawImage::convertToThumbnail() +{ + m_thumbAspect = (float)width() / height(); + switch(CV_MAT_DEPTH(m_img.type())) + { + case CV_8U: + m_img.convertTo(m_img, CV_16U, 255); + break; + case CV_32F: + m_img.convertTo(m_img, CV_16U, 65535); + break; + case CV_16U: + break; + default: + break; + } + + if(m_img.channels() == 1) + cv::cvtColor(m_img, m_img, cv::COLOR_GRAY2RGB); + if(m_img.channels() == 4) + cv::cvtColor(m_img, m_img, cv::COLOR_BGRA2RGB); + cv::Size dsize(THUMB_SIZE, THUMB_SIZE); + cv::resize(m_img, m_img, dsize, 0, 0, cv::INTER_NEAREST); +} + +float RawImage::thumbAspect() const +{ + return m_thumbAspect; +} diff --git a/rawimage.h b/rawimage.h index 961b0ec..5b5b5f0 100644 --- a/rawimage.h +++ b/rawimage.h @@ -9,6 +9,9 @@ #include #include +const int THUMB_SIZE = 128; +const int THUMB_SIZE_BORDER = 138; + class Peak { uint32_t m_v; @@ -42,6 +45,7 @@ protected: double m_min; double m_max; double m_mad; + float m_thumbAspect; public: enum ImgType { @@ -72,6 +76,8 @@ public: uint32_t norm() const; void* data(); const void* data() const; + void convertToThumbnail(); + float thumbAspect() const; }; #endif // RAWIMAGE_H diff --git a/resources.qrc b/resources.qrc index 0baf72f..bd89ab2 100644 --- a/resources.qrc +++ b/resources.qrc @@ -2,6 +2,8 @@ image.frag image.vert + thumb.frag + thumb.vert icon.png diff --git a/thumb.frag b/thumb.frag new file mode 100644 index 0000000..29ed1cf --- /dev/null +++ b/thumb.frag @@ -0,0 +1,25 @@ +#version 130 + +uniform sampler2DArray qt_Texture0; +uniform vec3 mtf_param; +uniform bool invert; +in vec3 qt_TexCoord0; +out vec4 color; + +vec4 MTF(vec4 x, vec3 m) +{ + x = (x - m.x) / (m.z - m.x); + x = clamp(x, vec4(0.0), vec4(1.0)); + return ((m.y - 1) * x) / ((2 * m.y - 1) * x - m.y); +} + +void main(void) +{ + color = texture(qt_Texture0, qt_TexCoord0); + color = MTF(color, mtf_param); + if(invert)color = vec4(1.0) - color; + + //color = vec4(qt_TexCoord0, 0.0, 0.0); + + //color = vec4(mod(qt_TexCoord0.st, vec2(1.0)), 0, 1.0); +} diff --git a/thumb.vert b/thumb.vert new file mode 100644 index 0000000..12b96b1 --- /dev/null +++ b/thumb.vert @@ -0,0 +1,20 @@ +#version 130 + +in vec2 qt_Vertex; +in vec2 qt_MultiTexCoord0; +in ivec3 imageSize_num; +out vec3 qt_TexCoord0; +uniform ivec3 viewport_row; +uniform mat4 mvp; +uniform vec2 offset; + +void main(void) +{ + vec2 pos = qt_Vertex * 0.5; + pos.y *= -1.0; + pos = pos * imageSize_num.xy + 69; + ivec2 off = ivec2(imageSize_num.z % viewport_row.z, imageSize_num.z / viewport_row.z) * 138; + + gl_Position = mvp * vec4(pos - offset + off, 0.0, 1.0); + qt_TexCoord0 = vec3(qt_MultiTexCoord0, imageSize_num.z + 0.1); +}