391 lines
11 KiB
C++
391 lines
11 KiB
C++
#include "imagescrollareagl.h"
|
|
#include <QOpenGLFunctions>
|
|
#include <QDebug>
|
|
#include <QKeyEvent>
|
|
#include <QOpenGLDebugLogger>
|
|
#include <QOpenGLPixelTransferOptions>
|
|
#include <QOpenGLFramebufferObject>
|
|
#include <QGridLayout>
|
|
|
|
struct RawImageType
|
|
{
|
|
QOpenGLTexture::PixelFormat pixelFormat;
|
|
QOpenGLTexture::TextureFormat textureFormat;
|
|
QOpenGLTexture::PixelType dataType;
|
|
bool bw;
|
|
};
|
|
|
|
const RawImageType rawImageTypes[] = {
|
|
{QOpenGLTexture::Red, QOpenGLTexture::R8_UNorm, QOpenGLTexture::UInt8, true},
|
|
{QOpenGLTexture::Red, QOpenGLTexture::R16_UNorm, QOpenGLTexture::UInt16, true},
|
|
{QOpenGLTexture::Red, QOpenGLTexture::R32F, QOpenGLTexture::Float32, true},
|
|
{QOpenGLTexture::RGB, QOpenGLTexture::RGB8_UNorm, QOpenGLTexture::UInt8, false},
|
|
{QOpenGLTexture::BGRA,QOpenGLTexture::RGB8_UNorm, QOpenGLTexture::UInt8, false},
|
|
{QOpenGLTexture::RGB, QOpenGLTexture::RGB16_UNorm, QOpenGLTexture::UInt16, false},
|
|
{QOpenGLTexture::RGB, QOpenGLTexture::RGB32F, QOpenGLTexture::Float32, false}
|
|
};
|
|
|
|
void setScrollRange(QScrollBar *scrollBar, int newRange)
|
|
{
|
|
int page = scrollBar->pageStep();
|
|
int pos = scrollBar->value() + page/2;
|
|
int range = scrollBar->maximum() + page;
|
|
float relPos = (float)pos/(float)range;
|
|
|
|
if(page >= newRange)
|
|
scrollBar->hide();
|
|
else
|
|
scrollBar->show();
|
|
|
|
scrollBar->setRange(0, newRange - page);
|
|
scrollBar->setValue(relPos*newRange - page/2);
|
|
}
|
|
|
|
ImageWidget::ImageWidget(QWidget *parent) : QOpenGLWidget(parent)
|
|
{
|
|
setFocusPolicy(Qt::ClickFocus);
|
|
m_range = UINT16_MAX;
|
|
m_low = 0;
|
|
m_mid = 0.5;
|
|
m_high = 1;
|
|
m_dx = m_dy = 0;
|
|
m_scale = 1.0f;
|
|
m_blockRepaint = false;
|
|
m_range = UINT16_MAX;
|
|
m_imgWidth = m_imgHeight = -1;
|
|
m_superpixel = m_invert = false;
|
|
}
|
|
|
|
ImageWidget::~ImageWidget()
|
|
{
|
|
makeCurrent();
|
|
}
|
|
|
|
void ImageWidget::setImage(const RawImage *image)
|
|
{
|
|
if(image == nullptr)return;
|
|
|
|
m_imgWidth = image->width();
|
|
m_imgHeight = image->height();
|
|
|
|
const RawImageType &rawImageType = rawImageTypes[image->type()];
|
|
|
|
m_image->destroy();
|
|
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, image->data(), m_transferOptions.get());
|
|
m_image->setLevelOfDetailRange(m_superpixel ? 1 : 0, m_image->mipMaxLevel());
|
|
m_image->generateMipMaps();
|
|
m_bwImg = rawImageType.bw;
|
|
update();
|
|
}
|
|
|
|
void ImageWidget::setImage(const QPixmap &pixmap)
|
|
{
|
|
QImage img = pixmap.toImage();
|
|
|
|
m_imgWidth = pixmap.width();
|
|
m_imgHeight = pixmap.height();
|
|
|
|
m_image->destroy();
|
|
m_image->setData(img);
|
|
m_image->setMinMagFilters(QOpenGLTexture::LinearMipMapLinear, QOpenGLTexture::Linear);
|
|
m_image->setWrapMode(QOpenGLTexture::ClampToBorder);
|
|
m_image->setBorderColor(0, 0, 0, 0);
|
|
m_bwImg = false;
|
|
update();
|
|
}
|
|
|
|
void ImageWidget::setScale(float scale)
|
|
{
|
|
m_scale = scale;
|
|
update();
|
|
}
|
|
|
|
void ImageWidget::blockRepaint(bool block)
|
|
{
|
|
m_blockRepaint = block;
|
|
if(!block)update();
|
|
}
|
|
|
|
void ImageWidget::setMTFParams(float low, float mid, float high)
|
|
{
|
|
m_low = low;
|
|
m_mid = mid;
|
|
m_high = high;
|
|
update();
|
|
}
|
|
|
|
void ImageWidget::setOffset(int dx, int dy)
|
|
{
|
|
m_dx = dx;
|
|
m_dy = dy;
|
|
update();
|
|
}
|
|
|
|
void ImageWidget::superPixel(bool enable)
|
|
{
|
|
m_superpixel = enable;
|
|
m_image->setLevelOfDetailRange(enable ? 1 : 0, m_image->mipMaxLevel());
|
|
update();
|
|
}
|
|
|
|
void ImageWidget::invert(bool enable)
|
|
{
|
|
m_invert = 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);
|
|
|
|
m_image->bind(0);
|
|
f->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
|
|
|
fbo.bindDefault();
|
|
return fbo.toImage(true);
|
|
}
|
|
|
|
void ImageWidget::paintGL()
|
|
{
|
|
if(m_blockRepaint)return;
|
|
|
|
float dx = m_dx;
|
|
float dy = m_dy;
|
|
if(width() > m_image->width()*m_scale)
|
|
dx = -width()*0.5f + m_image->width()*m_scale*0.5f;
|
|
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);
|
|
|
|
m_image->bind(0);
|
|
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);
|
|
}
|
|
|
|
void ImageWidget::initializeGL()
|
|
{
|
|
f = context()->functions();
|
|
f->glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
|
|
QOpenGLFunctions_3_2_Core *f3 = context()->versionFunctions<QOpenGLFunctions_3_2_Core>();
|
|
|
|
m_vao = std::unique_ptr<QOpenGLVertexArrayObject>(new QOpenGLVertexArrayObject);
|
|
m_vao->create();
|
|
m_vao->bind();
|
|
|
|
QOpenGLDebugLogger *logger = new QOpenGLDebugLogger(this);
|
|
logger->initialize();
|
|
logger->startLogging();
|
|
connect(logger, &QOpenGLDebugLogger::messageLogged, [](const QOpenGLDebugMessage &message)
|
|
{
|
|
qDebug() << message;
|
|
});
|
|
|
|
qDebug() << (char*)f->glGetString(GL_VENDOR);
|
|
qDebug() << (char*)f->glGetString(GL_RENDERER);
|
|
qDebug() << (char*)f->glGetString(GL_VERSION);
|
|
|
|
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(f3)f3->glBindFragDataLocation(m_program->programId(), 0, "color");
|
|
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_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_transferOptions = std::unique_ptr<QOpenGLPixelTransferOptions>(new QOpenGLPixelTransferOptions);
|
|
m_transferOptions->setAlignment(1);
|
|
}
|
|
|
|
ImageScrollAreaGL::ImageScrollAreaGL(QWidget *parent) : QWidget(parent)
|
|
{
|
|
QGridLayout *layout = new QGridLayout(this);
|
|
setLayout(layout);
|
|
|
|
m_imageWidget = new ImageWidget(this);
|
|
|
|
m_verticalScrollBar = new QScrollBar(Qt::Vertical, this);
|
|
m_horizontalScrollBar = new QScrollBar(Qt::Horizontal, this);
|
|
m_scale = 1.0f;
|
|
m_bestFit = false;
|
|
|
|
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()));
|
|
}
|
|
|
|
ImageScrollAreaGL::~ImageScrollAreaGL()
|
|
{
|
|
|
|
}
|
|
|
|
void ImageScrollAreaGL::setImage(RawImage *image)
|
|
{
|
|
m_imageWidget->setImage(image);
|
|
m_imgWidth = image->width();
|
|
m_imgHeight = image->height();
|
|
if(m_bestFit)bestFit();
|
|
updateScrollbars();
|
|
}
|
|
|
|
void ImageScrollAreaGL::setImage(const QPixmap &pixmap)
|
|
{
|
|
m_imageWidget->setImage(pixmap);
|
|
m_imgWidth = pixmap.width();
|
|
m_imgHeight = pixmap.height();
|
|
if(m_bestFit)bestFit();
|
|
updateScrollbars();
|
|
}
|
|
|
|
ImageWidget *ImageScrollAreaGL::imageWidget()
|
|
{
|
|
return m_imageWidget;
|
|
}
|
|
|
|
void ImageScrollAreaGL::updateScrollbars(bool zoom)
|
|
{
|
|
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());
|
|
updateScrollbars();
|
|
}
|
|
|
|
void ImageScrollAreaGL::mouseMoveEvent(QMouseEvent *event)
|
|
{
|
|
QPoint delta = m_lastPos - event->pos();
|
|
m_horizontalScrollBar->setValue(m_horizontalScrollBar->value() + delta.x());
|
|
m_verticalScrollBar->setValue(m_verticalScrollBar->value() + delta.y());
|
|
m_lastPos = event->pos();
|
|
}
|
|
|
|
void ImageScrollAreaGL::mousePressEvent(QMouseEvent *event)
|
|
{
|
|
m_lastPos = event->pos();
|
|
}
|
|
|
|
void ImageScrollAreaGL::wheelEvent(QWheelEvent *event)
|
|
{
|
|
m_bestFit = false;
|
|
if(event->angleDelta().y() != 0)
|
|
zoom(event->angleDelta().y() / 1200.0f);
|
|
}
|
|
|
|
void ImageScrollAreaGL::zoom(float delta)
|
|
{
|
|
if((m_scale >= 8.0f && delta > 0) || (m_scale <= 0.1f && delta < 0))return;
|
|
|
|
m_scale += delta;
|
|
m_imageWidget->blockRepaint(true);
|
|
m_imageWidget->setScale(m_scale);
|
|
updateScrollbars(true);
|
|
m_imageWidget->blockRepaint(false);
|
|
}
|
|
|
|
void ImageScrollAreaGL::zoomIn()
|
|
{
|
|
zoom(0.1f);
|
|
m_bestFit = false;
|
|
}
|
|
|
|
void ImageScrollAreaGL::zoomOut()
|
|
{
|
|
zoom(-0.1f);
|
|
m_bestFit = false;
|
|
}
|
|
|
|
void ImageScrollAreaGL::bestFit()
|
|
{
|
|
m_bestFit = true;
|
|
m_scale = std::min((float)m_imageWidget->width()/m_imgWidth, (float)m_imageWidget->height()/m_imgHeight);
|
|
zoom(0.0f);
|
|
}
|
|
|
|
void ImageScrollAreaGL::oneToOne()
|
|
{
|
|
m_scale = 1.0f;
|
|
zoom(0.0f);
|
|
m_bestFit = false;
|
|
}
|
|
|
|
void ImageScrollAreaGL::scrollEvent()
|
|
{
|
|
m_imageWidget->setOffset(m_horizontalScrollBar->value(), m_verticalScrollBar->value());
|
|
}
|