792 lines
25 KiB
C++
792 lines
25 KiB
C++
#include "imagescrollareagl.h"
|
|
#include <QOpenGLFunctions>
|
|
#include <QDebug>
|
|
#include <QKeyEvent>
|
|
#include <QOpenGLDebugLogger>
|
|
#include <QOpenGLPixelTransferOptions>
|
|
#include <QOpenGLFramebufferObject>
|
|
#include <QGridLayout>
|
|
#include <QMimeData>
|
|
#include <QMessageBox>
|
|
#include <QCoreApplication>
|
|
#include <QPainter>
|
|
#include <QFileInfo>
|
|
#include <cmath>
|
|
#include <QElapsedTimer>
|
|
|
|
struct RawImageType
|
|
{
|
|
QOpenGLTexture::PixelFormat pixelFormat;
|
|
QOpenGLTexture::TextureFormat textureFormat;
|
|
QOpenGLTexture::PixelType dataType;
|
|
};
|
|
|
|
RawImageType getRawImageType(const RawImage *img)
|
|
{
|
|
RawImageType type;
|
|
switch(img->type())
|
|
{
|
|
case RawImage::UINT8:
|
|
if(img->channels() >= 3)
|
|
type.textureFormat = QOpenGLTexture::SRGB8_Alpha8;
|
|
else
|
|
type.textureFormat = QOpenGLTexture::R8_UNorm;
|
|
type.dataType = QOpenGLTexture::UInt8;
|
|
break;
|
|
case RawImage::UINT16:
|
|
if(img->channels() >= 3)
|
|
type.textureFormat = QOpenGLTexture::RGBA16_UNorm;
|
|
else
|
|
type.textureFormat = QOpenGLTexture::R16_UNorm;
|
|
type.dataType = QOpenGLTexture::UInt16;
|
|
break;
|
|
case RawImage::FLOAT32:
|
|
if(img->channels() >= 3)
|
|
type.textureFormat = QOpenGLTexture::RGBA32F;
|
|
else
|
|
type.textureFormat = QOpenGLTexture::R32F;
|
|
type.dataType = QOpenGLTexture::Float32;
|
|
}
|
|
|
|
if(img->channels() >= 3)
|
|
type.pixelFormat = QOpenGLTexture::RGBA;
|
|
else
|
|
type.pixelFormat = QOpenGLTexture::Red;
|
|
|
|
return type;
|
|
}
|
|
|
|
ImageWidget::ImageWidget(Database *database, QWidget *parent) : QOpenGLWidget(parent)
|
|
, m_database(database)
|
|
{
|
|
setFocusPolicy(Qt::ClickFocus);
|
|
m_updateTimer = new QTimer(this);
|
|
m_updateTimer->setInterval(500);
|
|
m_updateTimer->setSingleShot(true);
|
|
connect(m_updateTimer, SIGNAL(timeout()), this, SLOT(update()));
|
|
setAcceptDrops(true);
|
|
QTimer::singleShot(1000, [this](){
|
|
if(!isValid())
|
|
{
|
|
QMessageBox::critical(this, tr("OpenGL error"), tr("Could not initialize OpenGL 3.3 context. Ensure that proper GPU driver is installed."));
|
|
QCoreApplication::exit(-1);
|
|
}
|
|
});
|
|
|
|
setMouseTracking(true);
|
|
}
|
|
|
|
ImageWidget::~ImageWidget()
|
|
{
|
|
makeCurrent();
|
|
}
|
|
|
|
void ImageWidget::setImage(std::shared_ptr<RawImage> image, int index)
|
|
{
|
|
if(image == nullptr)return;
|
|
makeCurrent();
|
|
m_rawImage = image;
|
|
m_rawImage->downscaleTo(m_maxTextureSize);
|
|
|
|
m_imgWidth = image->width();
|
|
m_imgHeight = image->height();
|
|
m_currentImg = index;
|
|
|
|
if(!m_image)return;
|
|
|
|
RawImageType rawImageType = getRawImageType(image.get());
|
|
m_srgb = rawImageType.textureFormat == QOpenGLTexture::SRGB8_Alpha8;
|
|
m_bwImg = image->channels() == 1;
|
|
|
|
QElapsedTimer timer;
|
|
timer.start();
|
|
m_image->destroy();
|
|
m_image->setAutoMipMapGenerationEnabled(false);
|
|
m_image->setFormat(rawImageType.textureFormat);
|
|
m_image->setSize(image->width(), image->height());
|
|
m_image->setMipLevels([&](){ int c = 0; int s = std::min(m_imgWidth, m_imgHeight); while(s>>=1)c++; return c; }());
|
|
m_image->allocateStorage();
|
|
m_image->setMinMagFilters(QOpenGLTexture::LinearMipMapLinear, QOpenGLTexture::Linear);
|
|
m_image->setWrapMode(QOpenGLTexture::ClampToEdge);
|
|
m_image->setBorderColor(0, 0, 0, 0);
|
|
m_image->setData(0, rawImageType.pixelFormat, rawImageType.dataType, (const void*)image->data(), m_transferOptions.get());
|
|
m_image->generateMipMaps();
|
|
qDebug() << "setImage" << timer.elapsed();
|
|
|
|
m_unit_scale[0] = 1.0f;
|
|
m_unit_scale[1] = 0.0f;
|
|
auto &stats = image->imageStats();
|
|
if(image->type() == RawImage::FLOAT32 || image->type() == RawImage::FLOAT64)
|
|
{
|
|
float min = *std::min_element(stats.m_min, stats.m_min + 4);
|
|
float max = *std::max_element(stats.m_max, stats.m_max + 4);
|
|
if(min < 0.0f || max > 1.0f)
|
|
{
|
|
m_unit_scale[0] = 1.0f / (max - min);
|
|
m_unit_scale[1] = min * m_unit_scale[0];
|
|
}
|
|
}
|
|
|
|
if(m_debayerTex)
|
|
{
|
|
f->glDeleteTextures(1, &m_debayerTex);
|
|
m_debayerTex = 0;
|
|
}
|
|
|
|
if(m_bestFit)bestFit();
|
|
else setOffset(m_dx, m_dy);
|
|
}
|
|
|
|
void ImageWidget::setWCS(std::shared_ptr<WCSData> wcs)
|
|
{
|
|
m_wcs = wcs;
|
|
}
|
|
|
|
void ImageWidget::zoom(int zoom, const QPointF &mousePos)
|
|
{
|
|
m_bestFit = false;
|
|
if(zoom != 0)
|
|
m_scaleStop = std::clamp(m_scaleStop + (zoom > 0 ? 1 : -1), -10, 10);
|
|
else
|
|
m_scaleStop = 0;
|
|
|
|
QPointF focus(m_width * 0.5f, m_height * 0.5f);
|
|
if(!mousePos.isNull())
|
|
focus = mousePos;
|
|
|
|
if(width() > m_image->width() * m_scale)
|
|
m_dx = -width() * 0.5f + m_image->width() * m_scale * 0.5f;
|
|
if(height() > m_image->height() * m_scale)
|
|
m_dy = -height() * 0.5f + m_image->height() * m_scale * 0.5f;
|
|
|
|
float newScale = std::sqrt(std::pow(2.0f, (float)m_scaleStop));
|
|
float r = newScale / m_scale;
|
|
m_scale = newScale;
|
|
|
|
setOffset(m_dx * r + focus.x() * (r - 1), m_dy * r + focus.y() * (r - 1));
|
|
}
|
|
|
|
void ImageWidget::bestFit()
|
|
{
|
|
m_bestFit = true;
|
|
m_scale = std::min((float)m_width/m_imgWidth, (float)m_height/m_imgHeight);
|
|
setOffset(0, 0);
|
|
}
|
|
|
|
void ImageWidget::blockRepaint(bool block)
|
|
{
|
|
m_blockRepaint = block;
|
|
if(!block)update();
|
|
}
|
|
|
|
void ImageWidget::allocateThumbnails(const QStringList &paths)
|
|
{
|
|
makeCurrent();
|
|
int count = paths.size();
|
|
m_thumbnailCount = count;
|
|
m_thumnails.clear();
|
|
QStringList marked = m_database->getMarkedFiles();
|
|
for(auto &path : paths)
|
|
{
|
|
QString name = QFileInfo(path).fileName();
|
|
m_thumnails.push_back({name, path, QSize(0, 0), marked.contains(path), false});
|
|
}
|
|
|
|
m_thumbnailTexture->destroy();
|
|
m_thumbnailTexture->create();
|
|
m_thumbnailTexture->setFormat(QOpenGLTexture::RGB16_UNorm);
|
|
m_thumbnailTexture->setSize(THUMB_SIZE, THUMB_SIZE);
|
|
m_thumbnailTexture->setLayers(paths.size());
|
|
m_thumbnailTexture->allocateStorage();
|
|
}
|
|
|
|
QVector2D ImageWidget::getImagePixelCoord(const QVector2D &pos)
|
|
{
|
|
float dx = m_dx;
|
|
float dy = m_dy;
|
|
if(m_width > m_image->width()*m_scale)
|
|
dx = -width()*0.5f + m_image->width()*m_scale*0.5f;
|
|
if(m_height > m_image->height()*m_scale)
|
|
dy = -height()*0.5f + m_image->height()*m_scale*0.5f;
|
|
|
|
QVector2D offset(dx, dy);
|
|
return (pos + offset) / m_scale;
|
|
}
|
|
|
|
void ImageWidget::setMTFParams(const MTFParam ¶ms)
|
|
{
|
|
m_mtfParams = params;
|
|
update();
|
|
}
|
|
|
|
void ImageWidget::setOffset(float dx, float dy)
|
|
{
|
|
m_dx = std::clamp(dx, 0.0f, std::max(0.0f, m_imgWidth * m_scale - m_width));
|
|
if(m_showThumbnails)
|
|
m_dy = std::clamp(dy, 0.0f, std::max(0.0f, (float)((m_thumbnailCount / (m_width / THUMB_SIZE_BORDER) + 2) * THUMB_SIZE_BORDER_Y - m_height)));
|
|
else
|
|
m_dy = std::clamp(dy, 0.0f, std::max(0.0f, m_imgHeight * m_scale - m_height));
|
|
updateScrollBars();
|
|
update();
|
|
}
|
|
|
|
void ImageWidget::superPixel(bool enable)
|
|
{
|
|
m_superpixel = enable;
|
|
update();
|
|
}
|
|
|
|
void ImageWidget::invert(bool enable)
|
|
{
|
|
m_invert = enable;
|
|
update();
|
|
}
|
|
|
|
void ImageWidget::falseColor(bool enable)
|
|
{
|
|
m_falseColor = enable;
|
|
update();
|
|
}
|
|
|
|
QImage ImageWidget::renderToImage()
|
|
{
|
|
if(m_imgWidth < 0)return QImage();
|
|
makeCurrent();
|
|
QOpenGLFramebufferObject fbo(m_imgWidth, m_imgHeight);
|
|
fbo.bind();
|
|
|
|
f->glViewport(0, 0, m_imgWidth, m_imgHeight);
|
|
|
|
m_program->bind();
|
|
m_program->setUniformValue("viewport", (float)m_imgWidth, (float)m_imgHeight);
|
|
m_program->setUniformValue("offset", 0.0f, 0.0f);
|
|
m_program->setUniformValue("zoom", 1.0f);
|
|
|
|
if(m_superpixel && m_debayerTex)
|
|
f->glBindTexture(GL_TEXTURE_2D, m_debayerTex);
|
|
else
|
|
m_image->bind(0);
|
|
f->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
|
|
|
fbo.bindDefault();
|
|
return fbo.toImage(true);
|
|
}
|
|
|
|
void ImageWidget::thumbnailLoaded(const Image *image)
|
|
{
|
|
makeCurrent();
|
|
const RawImage *raw = image->thumbnail();
|
|
if(!raw)return;
|
|
m_thumbnailTexture->setData(0, image->number(), QOpenGLTexture::RGBA, 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_sizesDirty = true;
|
|
m_thumnails[image->number()].size = QSize(sizes[0], sizes[1]);
|
|
if(!m_updateTimer->isActive())m_updateTimer->start();
|
|
}
|
|
|
|
void ImageWidget::showThumbnail(bool enable)
|
|
{
|
|
m_showThumbnails = enable;
|
|
setOffset(m_dx, m_dy);
|
|
}
|
|
|
|
void ImageWidget::paintGL()
|
|
{
|
|
if(m_blockRepaint)return;
|
|
|
|
float dx = m_dx;
|
|
float dy = m_dy;
|
|
if(m_width > m_image->width() * m_scale)
|
|
dx = -width() * 0.5f + m_image->width() * m_scale * 0.5f;
|
|
if(m_height > m_image->height() * m_scale)
|
|
dy = -height() * 0.5f + m_image->height() * m_scale * 0.5f;
|
|
QBrush highlight = style()->standardPalette().highlight();
|
|
|
|
if(m_showThumbnails)
|
|
{
|
|
m_vaoThumb->bind();
|
|
m_thumbnailTexture->bind(1);
|
|
if(m_sizesDirty)
|
|
{
|
|
m_bufferSizes->bind();
|
|
int i = 0;
|
|
std::vector<int> sizes(m_thumbnailCount*3);
|
|
for(auto &s : m_thumnails)
|
|
{
|
|
sizes[3*i] = s.size.width();
|
|
sizes[3*i+1] = s.size.height();
|
|
sizes[3*i+2] = i;
|
|
i++;
|
|
}
|
|
m_bufferSizes->allocate(&sizes[0], sizes.size()*sizeof(int));
|
|
m_sizesDirty = false;
|
|
}
|
|
m_thumbnailProgram->bind();
|
|
f->glUniform3i(m_thumbnailProgram->uniformLocation("viewport_row"), width(), height(), width()/THUMB_SIZE_BORDER);
|
|
f->glUniform3i(m_thumbnailProgram->uniformLocation("thumb_size"), THUMB_SIZE_BORDER/2, THUMB_SIZE_BORDER, THUMB_SIZE_BORDER_Y);
|
|
m_thumbnailProgram->setUniformValueArray("mtf_param", m_mtfParams.blackPoint, 3, 3);
|
|
m_thumbnailProgram->setUniformValue("invert", m_invert);
|
|
m_thumbnailProgram->setUniformValue("offset", 0, m_dy);
|
|
QMatrix4x4 mvp;
|
|
mvp.ortho(rect());
|
|
m_thumbnailProgram->setUniformValue("mvp", mvp);
|
|
if(f3)f3->glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, m_thumbnailCount);
|
|
|
|
QPainter painter(this);
|
|
const int w = width()/THUMB_SIZE_BORDER;
|
|
const int off = (THUMB_SIZE_BORDER - THUMB_SIZE) / 2;
|
|
for(int i=0; i < m_thumbnailCount; i++)
|
|
{
|
|
float x = (i % w) * THUMB_SIZE_BORDER;
|
|
float y = i / w * THUMB_SIZE_BORDER_Y + THUMB_SIZE - m_dy + off;
|
|
QRectF rect(x, y, THUMB_SIZE_BORDER, 32);
|
|
painter.drawText(rect, Qt::AlignCenter | Qt::TextWrapAnywhere, QString(m_thumnails[i].name));
|
|
if(m_thumnails[i].selected)
|
|
{
|
|
painter.save();
|
|
QRectF thumbRect;
|
|
painter.setPen(Qt::red);
|
|
thumbRect.setSize(m_thumnails[i].size);
|
|
thumbRect.moveCenter(QPointF(x + THUMB_SIZE_BORDER / 2, y - THUMB_SIZE / 2));
|
|
painter.drawRect(thumbRect);
|
|
painter.restore();
|
|
}
|
|
if(m_currentImg == i)
|
|
{
|
|
painter.save();
|
|
painter.setPen(QPen(highlight, 2.0));
|
|
painter.drawRect((i % w) * THUMB_SIZE_BORDER + off, i / w * THUMB_SIZE_BORDER_Y - m_dy + off, THUMB_SIZE, THUMB_SIZE);
|
|
painter.restore();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
debayer();
|
|
|
|
m_vao->bind();
|
|
if(m_superpixel && m_debayerTex)
|
|
f->glBindTexture(GL_TEXTURE_2D, m_debayerTex);
|
|
else
|
|
m_image->bind(0);
|
|
|
|
m_program->bind();
|
|
m_program->setUniformValue("viewport", (float)width(), (float)height());
|
|
m_program->setUniformValue("offset", std::floor(dx), std::floor(dy));
|
|
m_program->setUniformValueArray("mtf_param", m_mtfParams.blackPoint, 3, 3);
|
|
m_program->setUniformValue("unit_scale", m_unit_scale[0], m_unit_scale[1]);
|
|
m_program->setUniformValue("zoom", 1.0f/m_scale);
|
|
m_program->setUniformValue("bw", m_bwImg && !m_superpixel);
|
|
m_program->setUniformValue("false_color", m_falseColor && m_bwImg);
|
|
m_program->setUniformValue("invert", m_invert);
|
|
#ifdef COLOR_MANAGMENT
|
|
m_program->setUniformValue("srgb", m_srgb);
|
|
#endif
|
|
f->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
|
}
|
|
|
|
}
|
|
|
|
void ImageWidget::resizeGL(int w, int h)
|
|
{
|
|
m_width = w;
|
|
m_height = h;
|
|
f->glViewport(0, 0, w, h);
|
|
if(m_bestFit)bestFit();
|
|
updateScrollBars();
|
|
}
|
|
|
|
void ImageWidget::initializeGL()
|
|
{
|
|
f = context()->functions();
|
|
f->glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
|
|
f3 = context()->versionFunctions<QOpenGLFunctions_3_3_Core>();
|
|
|
|
if(f3 == nullptr)
|
|
QMessageBox::critical(this, tr("OpenGL error"), tr("Could not initialize OpenGL 3.3 context. Ensure that proper GPU driver is installed."));
|
|
|
|
m_vao = std::unique_ptr<QOpenGLVertexArrayObject>(new QOpenGLVertexArrayObject);
|
|
m_vaoThumb = std::unique_ptr<QOpenGLVertexArrayObject>(new QOpenGLVertexArrayObject);
|
|
m_vao->create();
|
|
m_vaoThumb->create();
|
|
m_vao->bind();
|
|
|
|
QOpenGLDebugLogger *logger = new QOpenGLDebugLogger(this);
|
|
logger->initialize();
|
|
logger->startLogging();
|
|
connect(logger, &QOpenGLDebugLogger::messageLogged, [](const QOpenGLDebugMessage &message)
|
|
{
|
|
qDebug() << message;
|
|
});
|
|
|
|
qDebug() << "Vendor:" << (char*)f->glGetString(GL_VENDOR);
|
|
qDebug() << "Renderer:" << (char*)f->glGetString(GL_RENDERER);
|
|
qDebug() << "Version:" << (char*)f->glGetString(GL_VERSION);
|
|
f->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize);
|
|
f->glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &m_maxArrayLayers);
|
|
qDebug() << "Max texture size:" << m_maxTextureSize << "max layers:" << m_maxArrayLayers;
|
|
|
|
//MANUAL_MIPMAP_GEN = QString((const char*)f->glGetString(GL_VENDOR)).startsWith("ATI Technologies Inc", Qt::CaseInsensitive);
|
|
|
|
qDebug() << context()->format();
|
|
|
|
// each vertex is x,y 2D position and s,t texture coordinates
|
|
float vertexs[] = {-1.0f, -1.0f, 0.0f, 1.0f,
|
|
1.0f, -1.0f, 1.0f, 1.0f,
|
|
-1.0f, 1.0f, 0.0f, 0.0f,
|
|
1.0f, 1.0f, 1.0f, 0.0f,};
|
|
m_buffer = std::unique_ptr<QOpenGLBuffer>(new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer));
|
|
m_buffer->setUsagePattern(QOpenGLBuffer::StaticDraw);
|
|
m_buffer->create();
|
|
m_buffer->bind();
|
|
m_buffer->allocate(vertexs, sizeof(vertexs));
|
|
// f->glVertexAttribPointer(0, 2, GL_FLOAT, false, sizeof(float)*4, 0);
|
|
|
|
m_program = std::unique_ptr<QOpenGLShaderProgram>(new QOpenGLShaderProgram);
|
|
m_program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/image.vert");
|
|
m_program->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/image.frag");
|
|
|
|
if(!m_program->link())
|
|
{
|
|
qDebug() << "Link failed" << m_program->log();
|
|
}
|
|
|
|
m_program->bind();
|
|
m_program->enableAttributeArray("qt_Vertex");
|
|
m_program->setAttributeBuffer("qt_Vertex", GL_FLOAT, 0, 2, sizeof(float)*4);
|
|
m_program->enableAttributeArray("qt_MultiTexCoord0");
|
|
m_program->setAttributeBuffer("qt_MultiTexCoord0", GL_FLOAT, sizeof(float)*2, 2, sizeof(float)*4);
|
|
m_program->setUniformValue("qt_Texture0", (GLuint)0);
|
|
m_program->setUniformValue("scale", 1.0f, 0.0f);
|
|
|
|
m_debayerProgram = std::unique_ptr<QOpenGLShaderProgram>(new QOpenGLShaderProgram);
|
|
m_debayerProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/debayer.vert");
|
|
m_debayerProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/debayer.frag");
|
|
|
|
m_debayerProgram->bind();
|
|
m_debayerProgram->enableAttributeArray("qt_Vertex");
|
|
m_debayerProgram->setAttributeBuffer("qt_Vertex", GL_FLOAT, 0, 2, sizeof(float)*4);
|
|
m_debayerProgram->enableAttributeArray("qt_MultiTexCoord0");
|
|
m_debayerProgram->setAttributeBuffer("qt_MultiTexCoord0", GL_FLOAT, sizeof(float)*2, 2, sizeof(float)*4);
|
|
m_debayerProgram->setUniformValue("qt_Texture0", (GLuint)0);
|
|
if(!m_debayerProgram->link())
|
|
{
|
|
qDebug() << "Link failed" << m_debayerProgram->log();
|
|
}
|
|
|
|
m_vaoThumb->bind();
|
|
|
|
m_thumbnailProgram = std::unique_ptr<QOpenGLShaderProgram>(new QOpenGLShaderProgram);
|
|
m_thumbnailProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/thumb.vert");
|
|
m_thumbnailProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/thumb.frag");
|
|
|
|
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<QOpenGLBuffer>(new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer));
|
|
m_bufferSizes->setUsagePattern(QOpenGLBuffer::StaticDraw);
|
|
m_bufferSizes->create();
|
|
m_bufferSizes->bind();
|
|
m_bufferSizes->allocate(12);
|
|
|
|
m_thumbnailProgram->enableAttributeArray("imageSize_num");
|
|
f3->glVertexAttribIPointer(m_thumbnailProgram->attributeLocation("imageSize_num"), 3, GL_INT, 0, nullptr);
|
|
f3->glVertexAttribDivisor(m_thumbnailProgram->attributeLocation("imageSize_num"), 1);
|
|
|
|
m_image = std::unique_ptr<QOpenGLTexture>(new QOpenGLTexture(QOpenGLTexture::Target2D));
|
|
m_image->setFormat(QOpenGLTexture::RGB8U);
|
|
m_image->allocateStorage();
|
|
m_image->bind(0);
|
|
m_image->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear);
|
|
m_image->setMagnificationFilter(QOpenGLTexture::Linear);
|
|
|
|
m_thumbnailTexture = std::unique_ptr<QOpenGLTexture>(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->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear);
|
|
|
|
m_transferOptions = std::unique_ptr<QOpenGLPixelTransferOptions>(new QOpenGLPixelTransferOptions);
|
|
m_transferOptions->setAlignment(1);
|
|
|
|
if(m_rawImage)
|
|
setImage(m_rawImage, m_currentImg);
|
|
}
|
|
|
|
void ImageWidget::dragEnterEvent(QDragEnterEvent *event)
|
|
{
|
|
if(event->mimeData()->hasUrls() && event->proposedAction() & (Qt::CopyAction | Qt::MoveAction))
|
|
event->acceptProposedAction();
|
|
}
|
|
|
|
void ImageWidget::dropEvent(QDropEvent *event)
|
|
{
|
|
if(event->mimeData()->hasUrls() && event->proposedAction() & (Qt::CopyAction | Qt::MoveAction))
|
|
{
|
|
for(const QUrl &url : event->mimeData()->urls())
|
|
{
|
|
if(url.isLocalFile())
|
|
{
|
|
emit fileDropped(url.toLocalFile());
|
|
event->accept();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
event->ignore();
|
|
}
|
|
|
|
void ImageWidget::mousePressEvent(QMouseEvent *event)
|
|
{
|
|
if(m_showThumbnails && event->button() == Qt::LeftButton && event->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier))
|
|
m_selecting = true;
|
|
|
|
if(m_selecting)
|
|
{
|
|
thumbSelect(event);
|
|
}
|
|
else
|
|
{
|
|
if(event->button() == Qt::LeftButton)
|
|
m_lastPos = event->localPos();
|
|
}
|
|
}
|
|
|
|
void ImageWidget::mouseMoveEvent(QMouseEvent *event)
|
|
{
|
|
if(m_selecting)
|
|
{
|
|
thumbSelect(event);
|
|
}
|
|
else if(!m_lastPos.isNull())
|
|
{
|
|
QPointF off = event->localPos() - m_lastPos;
|
|
m_lastPos = event->localPos();
|
|
setOffset(m_dx - off.x(), m_dy - off.y());
|
|
return;
|
|
}
|
|
|
|
if(!m_showThumbnails && m_rawImage)
|
|
{
|
|
QVector2D pix = getImagePixelCoord(QVector2D(event->pos()));
|
|
double r,g,b;
|
|
|
|
SkyPoint sky;
|
|
if(m_wcs)
|
|
{
|
|
m_wcs->pixelToWorld(QPointF(pix.x(), pix.y()), sky);
|
|
}
|
|
|
|
if(m_rawImage->pixel(pix.x(), pix.y(), r, g, b))
|
|
{
|
|
if(m_bwImg)
|
|
emit status(tr("L:%1").arg(r), tr("X:%3 Y:%4").arg((int)pix.x()).arg((int)pix.y()), sky.toString());
|
|
else
|
|
emit status(tr("R:%1 G:%2 B:%3").arg(r).arg(g).arg(b), tr("X:%3 Y:%4").arg((int)pix.x()).arg((int)pix.y()), sky.toString());
|
|
}
|
|
}
|
|
}
|
|
|
|
void ImageWidget::mouseReleaseEvent(QMouseEvent *event)
|
|
{
|
|
if(m_selecting)
|
|
{
|
|
m_selecting = false;
|
|
event->accept();
|
|
QStringList mark;
|
|
QStringList unmark;
|
|
for(auto &thumb : m_thumnails)
|
|
{
|
|
if(thumb.dirty)
|
|
{
|
|
if(thumb.selected)
|
|
mark.append(thumb.path);
|
|
else
|
|
unmark.append(thumb.path);
|
|
|
|
thumb.dirty = false;
|
|
}
|
|
}
|
|
if(!mark.isEmpty())
|
|
m_database->mark(mark);
|
|
if(!unmark.isEmpty())
|
|
m_database->unmark(unmark);
|
|
}
|
|
else
|
|
{
|
|
m_lastPos = QPointF();
|
|
}
|
|
}
|
|
|
|
void ImageWidget::wheelEvent(QWheelEvent *event)
|
|
{
|
|
if(m_showThumbnails)
|
|
{
|
|
setOffset(0, m_dy - event->angleDelta().y());
|
|
}
|
|
else
|
|
{
|
|
if(std::abs(event->angleDelta().y()) > 15)
|
|
zoom(event->angleDelta().y(), event->modifiers() & Qt::ShiftModifier ? QPointF() : event->posF());
|
|
}
|
|
}
|
|
|
|
void ImageWidget::thumbSelect(QMouseEvent *event)
|
|
{
|
|
QPoint p = event->pos();
|
|
const int off = (THUMB_SIZE_BORDER - THUMB_SIZE) / 2;
|
|
p.ry() += m_dy;
|
|
const int w = width()/THUMB_SIZE_BORDER;
|
|
int x = p.x() / THUMB_SIZE_BORDER;
|
|
int y = p.y() / THUMB_SIZE_BORDER_Y;
|
|
int i = y * w + x;
|
|
event->accept();
|
|
QRect rect(x * THUMB_SIZE_BORDER + off, y * THUMB_SIZE_BORDER_Y + off, THUMB_SIZE, THUMB_SIZE);
|
|
if(x < w && i < m_thumbnailCount && rect.contains(p, true))
|
|
{
|
|
bool oldVal = m_thumnails[i].selected;
|
|
bool newVal = oldVal;
|
|
if(event->modifiers() == Qt::ShiftModifier)
|
|
newVal = true;
|
|
if(event->modifiers() == Qt::ControlModifier)
|
|
newVal = false;
|
|
|
|
if(newVal != oldVal)
|
|
{
|
|
m_thumnails[i].selected = newVal;
|
|
m_thumnails[i].dirty = true;
|
|
update();
|
|
}
|
|
}
|
|
}
|
|
|
|
void ImageWidget::debayer()
|
|
{
|
|
if(m_debayerTex > 0 || !m_superpixel || !m_bwImg || m_imgWidth < 0)return;
|
|
|
|
QOpenGLFramebufferObject fbo(m_imgWidth, m_imgHeight, QOpenGLFramebufferObject::NoAttachment, GL_TEXTURE_2D, GL_RGBA16);
|
|
fbo.bind();
|
|
|
|
f->glViewport(0, 0, m_imgWidth, m_imgHeight);
|
|
|
|
m_debayerProgram->bind();
|
|
m_image->bind(0);
|
|
f->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
|
|
|
fbo.release();
|
|
f->glViewport(0, 0, m_width, m_height);
|
|
m_debayerTex = fbo.takeTexture();
|
|
f->glBindTexture(GL_TEXTURE_2D, m_debayerTex);
|
|
f->glGenerateMipmap(GL_TEXTURE_2D);
|
|
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
|
}
|
|
|
|
void ImageWidget::updateScrollBars()
|
|
{
|
|
if(m_showThumbnails)
|
|
emit scrollBarsUpdate(0, 0, -1, m_dy, m_height, (m_thumbnailCount / (m_width / THUMB_SIZE_BORDER) + 2) * THUMB_SIZE_BORDER_Y - m_height);
|
|
else
|
|
emit scrollBarsUpdate(m_dx, m_width, m_imgWidth * m_scale - m_width, m_dy, m_height, m_imgHeight * m_scale - m_height);
|
|
}
|
|
|
|
ImageScrollAreaGL::ImageScrollAreaGL(Database *database, QWidget *parent) : QWidget(parent)
|
|
{
|
|
QGridLayout *layout = new QGridLayout(this);
|
|
setLayout(layout);
|
|
|
|
m_imageWidget = new ImageWidget(database, this);
|
|
|
|
m_verticalScrollBar = new QScrollBar(Qt::Vertical, this);
|
|
m_horizontalScrollBar = new QScrollBar(Qt::Horizontal, this);
|
|
|
|
layout->setSpacing(0);
|
|
layout->addWidget(m_imageWidget, 0, 0);
|
|
layout->addWidget(m_verticalScrollBar, 0, 1);
|
|
layout->addWidget(m_horizontalScrollBar, 1, 0);
|
|
|
|
connect(m_verticalScrollBar, SIGNAL(valueChanged(int)), this, SLOT(scrollEvent()));
|
|
connect(m_horizontalScrollBar, SIGNAL(valueChanged(int)), this, SLOT(scrollEvent()));
|
|
connect(m_imageWidget, &ImageWidget::scrollBarsUpdate, this, &ImageScrollAreaGL::updateScrollbars);
|
|
}
|
|
|
|
ImageScrollAreaGL::~ImageScrollAreaGL()
|
|
{
|
|
|
|
}
|
|
|
|
void ImageScrollAreaGL::setImage(Image *image)
|
|
{
|
|
if(image && image->rawImage())
|
|
{
|
|
m_imageWidget->setImage(image->rawImage(), image->number());
|
|
m_imageWidget->setWCS(image->info().wcs);
|
|
}
|
|
}
|
|
|
|
ImageWidget *ImageScrollAreaGL::imageWidget()
|
|
{
|
|
return m_imageWidget;
|
|
}
|
|
|
|
void ImageScrollAreaGL::updateScrollbars(int valueH, int stepH, int maxH, int valueV, int stepV, int maxV)
|
|
{
|
|
if(maxH > 0)
|
|
{
|
|
m_horizontalScrollBar->show();
|
|
m_horizontalScrollBar->setRange(0, maxH);
|
|
m_horizontalScrollBar->setPageStep(stepH);
|
|
m_horizontalScrollBar->setValue(valueH);
|
|
}
|
|
else
|
|
m_horizontalScrollBar->hide();
|
|
|
|
if(maxV > 0)
|
|
{
|
|
m_verticalScrollBar->show();
|
|
m_verticalScrollBar->setRange(0, maxV);
|
|
m_verticalScrollBar->setPageStep(stepV);
|
|
m_verticalScrollBar->setValue(valueV);
|
|
}
|
|
else
|
|
m_verticalScrollBar->hide();
|
|
}
|
|
|
|
void ImageScrollAreaGL::zoomIn()
|
|
{
|
|
m_imageWidget->zoom(1);
|
|
}
|
|
|
|
void ImageScrollAreaGL::zoomOut()
|
|
{
|
|
m_imageWidget->zoom(-1);
|
|
}
|
|
|
|
void ImageScrollAreaGL::bestFit()
|
|
{
|
|
m_horizontalScrollBar->hide();
|
|
m_verticalScrollBar->hide();
|
|
m_imageWidget->bestFit();
|
|
}
|
|
|
|
void ImageScrollAreaGL::oneToOne()
|
|
{
|
|
m_imageWidget->zoom(0);
|
|
}
|
|
|
|
void ImageScrollAreaGL::scrollEvent()
|
|
{
|
|
m_imageWidget->setOffset(m_horizontalScrollBar->value(), m_verticalScrollBar->value());
|
|
}
|