#include "imagescrollareagl.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int FILTERING = 1; 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; break; default: qWarning() << "Invalid format" << img->type(); break; } 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 image, int index) { m_currentImg = index; if(!image || !image->valid()) { m_imgWidth = 0; m_imgHeight = 0; m_error = tr("Failed to load image"); m_rawImage.reset(); update(); return; } m_error.clear(); makeCurrent(); m_rawImage = image; if((int)image->width() > m_maxTextureSize || (int)image->height() > m_maxTextureSize) m_rawImage->resize(std::min(m_maxTextureSize, (int)image->width()), std::min(m_maxTextureSize, (int)image->height())); m_imgWidth = image->width(); m_imgHeight = image->height(); 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; if(image->type() == RawImage::FLOAT32) { auto unitScaling = image->unitScale(); m_unit_scale[0] = unitScaling.first; m_unit_scale[1] = unitScaling.second; } 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 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(std::min((int)paths.size(), m_maxArrayLayers)); 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::setBayerMask(int mask) { m_firstRed[0] = mask & 0x1; m_firstRed[1] = (mask & 0x2) >> 1; if(m_debayerTex) { f->glDeleteTextures(1, &m_debayerTex); m_debayerTex = 0; } update(); } 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) { if(image->number() >= m_maxArrayLayers) return; makeCurrent(); const RawImage *raw = image->thumbnail(); if(!raw || !raw->valid())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 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; int start = std::max((int)(m_dy / THUMB_SIZE_BORDER_Y * w - w), 0); int end = std::min((int)(m_dy + m_height) / THUMB_SIZE_BORDER_Y * w + w, m_thumbnailCount); for(int i=start; i < end; 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 if(!m_error.isEmpty()) { QPainter painter(this); painter.setPen(Qt::red); painter.setFont(QFont("Sans", 24, QFont::Bold)); painter.drawText(0, 0, width(), height(), Qt::AlignCenter | Qt::AlignHCenter, m_error); } 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); m_program->setUniformValue("filtering", m_scale > 1.0f ? FILTERING : 1); #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 = QOpenGLVersionFunctionsFactory::get(context()); 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(new QOpenGLVertexArrayObject); m_vaoThumb = std::unique_ptr(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(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(new QOpenGLShaderProgram); m_program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/image.vert"); m_program->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/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(new QOpenGLShaderProgram); m_debayerProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/debayer.vert"); m_debayerProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/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(new QOpenGLShaderProgram); m_thumbnailProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/thumb.vert"); m_thumbnailProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/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(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(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(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(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->position(); } } void ImageWidget::mouseMoveEvent(QMouseEvent *event) { if(m_selecting) { thumbSelect(event); } else if(!m_lastPos.isNull()) { QPointF off = event->position() - m_lastPos; m_lastPos = event->position(); 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->position()); } } 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(); f->glUniform2i(m_debayerProgram->uniformLocation("firstRed"), m_firstRed[0], m_firstRed[1]); 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()); }