git repos / SagoImageBrowser

blame: src/imageviewwidget.cpp

normal view · raw

61136b77 Poul Sander 2026-02-20 22:45 1
#include "imageviewwidget.h"
61136b77 Poul Sander 2026-02-20 22:45 2
61136b77 Poul Sander 2026-02-20 22:45 3
#include <QPainter>
61136b77 Poul Sander 2026-02-20 22:45 4
#include <QKeyEvent>
c8ae2669 Poul Sander 2026-02-22 17:52 5
#include <QMouseEvent>
61136b77 Poul Sander 2026-02-20 22:45 6
#include <QImageReader>
f61bf886 Poul Sander 2026-02-23 19:27 7
#include <QPalette>
c58dbdb7 Poul Sander 2026-02-22 17:49 8
#include <algorithm>
61136b77 Poul Sander 2026-02-20 22:45 9
61136b77 Poul Sander 2026-02-20 22:45 10
ImageViewWidget::ImageViewWidget(QWidget *parent)
61136b77 Poul Sander 2026-02-20 22:45 11
    : QWidget(parent)
61136b77 Poul Sander 2026-02-20 22:45 12
{
61136b77 Poul Sander 2026-02-20 22:45 13
    setFocusPolicy(Qt::StrongFocus);
f61bf886 Poul Sander 2026-02-23 19:27 14
    setAutoFillBackground(true);
f61bf886 Poul Sander 2026-02-23 19:27 15
    setBackgroundColor("black");
61136b77 Poul Sander 2026-02-20 22:45 16
}
61136b77 Poul Sander 2026-02-20 22:45 17
61136b77 Poul Sander 2026-02-20 22:45 18
void ImageViewWidget::setImage(const QString &path)
61136b77 Poul Sander 2026-02-20 22:45 19
{
61136b77 Poul Sander 2026-02-20 22:45 20
    QImageReader reader(path);
61136b77 Poul Sander 2026-02-20 22:45 21
    reader.setAutoTransform(true);
e1e7f5a6 Poul Sander 2026-02-23 19:40 22
    reader.setDecideFormatFromContent(true);
7889168c Poul Sander 2026-02-26 18:27 23
    // Disable the allocation limit so very large images can be viewed.
7889168c Poul Sander 2026-02-26 18:27 24
    // The default 128 MB limit rejects high-resolution photos.
7889168c Poul Sander 2026-02-26 18:27 25
    reader.setAllocationLimit(0);
61136b77 Poul Sander 2026-02-20 22:45 26
    QImage image = reader.read();
61136b77 Poul Sander 2026-02-20 22:45 27
e1e7f5a6 Poul Sander 2026-02-23 19:40 28
    if (!image.isNull()) {
e1e7f5a6 Poul Sander 2026-02-23 19:40 29
        // Ensure the image is in a high-quality format
e1e7f5a6 Poul Sander 2026-02-23 19:40 30
        if (image.format() != QImage::Format_RGB32 && image.format() != QImage::Format_ARGB32 && image.format() != QImage::Format_ARGB32_Premultiplied)
e1e7f5a6 Poul Sander 2026-02-23 19:40 31
            image = image.convertToFormat(QImage::Format_ARGB32);
61136b77 Poul Sander 2026-02-20 22:45 32
        m_pixmap = QPixmap::fromImage(image);
e1e7f5a6 Poul Sander 2026-02-23 19:40 33
    }
61136b77 Poul Sander 2026-02-20 22:45 34
    else
61136b77 Poul Sander 2026-02-20 22:45 35
        m_pixmap = QPixmap();
61136b77 Poul Sander 2026-02-20 22:45 36
c58dbdb7 Poul Sander 2026-02-22 17:49 37
    m_zoomMode = FitToScreen;
c58dbdb7 Poul Sander 2026-02-22 17:49 38
    m_zoomFactor = 1.0;
c8ae2669 Poul Sander 2026-02-22 17:52 39
    m_offset = QPoint(0, 0);
61136b77 Poul Sander 2026-02-20 22:45 40
    update();
61136b77 Poul Sander 2026-02-20 22:45 41
}
61136b77 Poul Sander 2026-02-20 22:45 42
f61bf886 Poul Sander 2026-02-23 19:27 43
void ImageViewWidget::setBackgroundColor(const QString &color)
f61bf886 Poul Sander 2026-02-23 19:27 44
{
f61bf886 Poul Sander 2026-02-23 19:27 45
    m_backgroundColor = color;
f61bf886 Poul Sander 2026-02-23 19:27 46
    QPalette pal = palette();
f61bf886 Poul Sander 2026-02-23 19:27 47
    pal.setColor(QPalette::Window, QColor(color));
f61bf886 Poul Sander 2026-02-23 19:27 48
    setPalette(pal);
f61bf886 Poul Sander 2026-02-23 19:27 49
    update();
f61bf886 Poul Sander 2026-02-23 19:27 50
}
f61bf886 Poul Sander 2026-02-23 19:27 51
61136b77 Poul Sander 2026-02-20 22:45 52
void ImageViewWidget::paintEvent(QPaintEvent *)
61136b77 Poul Sander 2026-02-20 22:45 53
{
61136b77 Poul Sander 2026-02-20 22:45 54
    if (m_pixmap.isNull())
61136b77 Poul Sander 2026-02-20 22:45 55
        return;
61136b77 Poul Sander 2026-02-20 22:45 56
61136b77 Poul Sander 2026-02-20 22:45 57
    QPainter painter(this);
e1e7f5a6 Poul Sander 2026-02-23 19:40 58
    painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
e1e7f5a6 Poul Sander 2026-02-23 19:40 59
    painter.setRenderHint(QPainter::Antialiasing, true);
e1e7f5a6 Poul Sander 2026-02-23 19:40 60
c58dbdb7 Poul Sander 2026-02-22 17:49 61
    QPixmap drawn;
c58dbdb7 Poul Sander 2026-02-22 17:49 62
c58dbdb7 Poul Sander 2026-02-22 17:49 63
    switch (m_zoomMode) {
c58dbdb7 Poul Sander 2026-02-22 17:49 64
    case FitToScreen:
c58dbdb7 Poul Sander 2026-02-22 17:49 65
        drawn = m_pixmap.scaled(size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
c58dbdb7 Poul Sander 2026-02-22 17:49 66
        break;
c58dbdb7 Poul Sander 2026-02-22 17:49 67
    case OriginalSize:
c58dbdb7 Poul Sander 2026-02-22 17:49 68
        drawn = m_pixmap;
c58dbdb7 Poul Sander 2026-02-22 17:49 69
        break;
c58dbdb7 Poul Sander 2026-02-22 17:49 70
    case CustomZoom: {
c58dbdb7 Poul Sander 2026-02-22 17:49 71
        QSize target(static_cast<int>(m_pixmap.width() * m_zoomFactor),
c58dbdb7 Poul Sander 2026-02-22 17:49 72
                     static_cast<int>(m_pixmap.height() * m_zoomFactor));
c58dbdb7 Poul Sander 2026-02-22 17:49 73
        drawn = m_pixmap.scaled(target, Qt::KeepAspectRatio, Qt::SmoothTransformation);
c58dbdb7 Poul Sander 2026-02-22 17:49 74
        break;
c58dbdb7 Poul Sander 2026-02-22 17:49 75
    }
c58dbdb7 Poul Sander 2026-02-22 17:49 76
    }
c58dbdb7 Poul Sander 2026-02-22 17:49 77
c8ae2669 Poul Sander 2026-02-22 17:52 78
    clampOffset(drawn.width(), drawn.height());
c8ae2669 Poul Sander 2026-02-22 17:52 79
c8ae2669 Poul Sander 2026-02-22 17:52 80
    int x = (width()  - drawn.width())  / 2 + m_offset.x();
c8ae2669 Poul Sander 2026-02-22 17:52 81
    int y = (height() - drawn.height()) / 2 + m_offset.y();
c58dbdb7 Poul Sander 2026-02-22 17:49 82
    painter.drawPixmap(x, y, drawn);
61136b77 Poul Sander 2026-02-20 22:45 83
}
61136b77 Poul Sander 2026-02-20 22:45 84
61136b77 Poul Sander 2026-02-20 22:45 85
void ImageViewWidget::keyPressEvent(QKeyEvent *event)
61136b77 Poul Sander 2026-02-20 22:45 86
{
1035a476 Poul Sander 2026-02-23 19:30 87
    if (event->key() == Qt::Key_Escape || event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter)
61136b77 Poul Sander 2026-02-20 22:45 88
    {
61136b77 Poul Sander 2026-02-20 22:45 89
        emit closeRequested();
61136b77 Poul Sander 2026-02-20 22:45 90
        event->accept();
61136b77 Poul Sander 2026-02-20 22:45 91
        return;
61136b77 Poul Sander 2026-02-20 22:45 92
    }
61136b77 Poul Sander 2026-02-20 22:45 93
1ffc1962 Poul Sander 2026-02-20 23:41 94
    if (event->key() == Qt::Key_PageDown)
1ffc1962 Poul Sander 2026-02-20 23:41 95
    {
1ffc1962 Poul Sander 2026-02-20 23:41 96
        emit nextRequested();
1ffc1962 Poul Sander 2026-02-20 23:41 97
        event->accept();
1ffc1962 Poul Sander 2026-02-20 23:41 98
        return;
1ffc1962 Poul Sander 2026-02-20 23:41 99
    }
1ffc1962 Poul Sander 2026-02-20 23:41 100
1ffc1962 Poul Sander 2026-02-20 23:41 101
    if (event->key() == Qt::Key_PageUp)
1ffc1962 Poul Sander 2026-02-20 23:41 102
    {
1ffc1962 Poul Sander 2026-02-20 23:41 103
        emit previousRequested();
1ffc1962 Poul Sander 2026-02-20 23:41 104
        event->accept();
1ffc1962 Poul Sander 2026-02-20 23:41 105
        return;
1ffc1962 Poul Sander 2026-02-20 23:41 106
    }
1ffc1962 Poul Sander 2026-02-20 23:41 107
c8ae2669 Poul Sander 2026-02-22 17:52 108
    // Arrow keys for panning
c8ae2669 Poul Sander 2026-02-22 17:52 109
    constexpr int panStep = 50;
c8ae2669 Poul Sander 2026-02-22 17:52 110
    if (event->key() == Qt::Key_Left)
c8ae2669 Poul Sander 2026-02-22 17:52 111
    {
c8ae2669 Poul Sander 2026-02-22 17:52 112
        m_offset.rx() += panStep;
c8ae2669 Poul Sander 2026-02-22 17:52 113
        update();
c8ae2669 Poul Sander 2026-02-22 17:52 114
        event->accept();
c8ae2669 Poul Sander 2026-02-22 17:52 115
        return;
c8ae2669 Poul Sander 2026-02-22 17:52 116
    }
c8ae2669 Poul Sander 2026-02-22 17:52 117
    if (event->key() == Qt::Key_Right)
c8ae2669 Poul Sander 2026-02-22 17:52 118
    {
c8ae2669 Poul Sander 2026-02-22 17:52 119
        m_offset.rx() -= panStep;
c8ae2669 Poul Sander 2026-02-22 17:52 120
        update();
c8ae2669 Poul Sander 2026-02-22 17:52 121
        event->accept();
c8ae2669 Poul Sander 2026-02-22 17:52 122
        return;
c8ae2669 Poul Sander 2026-02-22 17:52 123
    }
c8ae2669 Poul Sander 2026-02-22 17:52 124
    if (event->key() == Qt::Key_Up)
c8ae2669 Poul Sander 2026-02-22 17:52 125
    {
c8ae2669 Poul Sander 2026-02-22 17:52 126
        m_offset.ry() += panStep;
c8ae2669 Poul Sander 2026-02-22 17:52 127
        update();
c8ae2669 Poul Sander 2026-02-22 17:52 128
        event->accept();
c8ae2669 Poul Sander 2026-02-22 17:52 129
        return;
c8ae2669 Poul Sander 2026-02-22 17:52 130
    }
c8ae2669 Poul Sander 2026-02-22 17:52 131
    if (event->key() == Qt::Key_Down)
c8ae2669 Poul Sander 2026-02-22 17:52 132
    {
c8ae2669 Poul Sander 2026-02-22 17:52 133
        m_offset.ry() -= panStep;
c8ae2669 Poul Sander 2026-02-22 17:52 134
        update();
c8ae2669 Poul Sander 2026-02-22 17:52 135
        event->accept();
c8ae2669 Poul Sander 2026-02-22 17:52 136
        return;
c8ae2669 Poul Sander 2026-02-22 17:52 137
    }
c8ae2669 Poul Sander 2026-02-22 17:52 138
c58dbdb7 Poul Sander 2026-02-22 17:49 139
    if (event->key() == Qt::Key_Slash)
c58dbdb7 Poul Sander 2026-02-22 17:49 140
    {
c58dbdb7 Poul Sander 2026-02-22 17:49 141
        zoomOriginal();
c58dbdb7 Poul Sander 2026-02-22 17:49 142
        event->accept();
c58dbdb7 Poul Sander 2026-02-22 17:49 143
        return;
c58dbdb7 Poul Sander 2026-02-22 17:49 144
    }
c58dbdb7 Poul Sander 2026-02-22 17:49 145
c58dbdb7 Poul Sander 2026-02-22 17:49 146
    if (event->key() == Qt::Key_Asterisk)
c58dbdb7 Poul Sander 2026-02-22 17:49 147
    {
c58dbdb7 Poul Sander 2026-02-22 17:49 148
        zoomFitToScreen();
c58dbdb7 Poul Sander 2026-02-22 17:49 149
        event->accept();
c58dbdb7 Poul Sander 2026-02-22 17:49 150
        return;
c58dbdb7 Poul Sander 2026-02-22 17:49 151
    }
c58dbdb7 Poul Sander 2026-02-22 17:49 152
c58dbdb7 Poul Sander 2026-02-22 17:49 153
    if (event->key() == Qt::Key_Plus)
c58dbdb7 Poul Sander 2026-02-22 17:49 154
    {
c58dbdb7 Poul Sander 2026-02-22 17:49 155
        zoomIn();
c58dbdb7 Poul Sander 2026-02-22 17:49 156
        event->accept();
c58dbdb7 Poul Sander 2026-02-22 17:49 157
        return;
c58dbdb7 Poul Sander 2026-02-22 17:49 158
    }
c58dbdb7 Poul Sander 2026-02-22 17:49 159
c58dbdb7 Poul Sander 2026-02-22 17:49 160
    if (event->key() == Qt::Key_Minus)
c58dbdb7 Poul Sander 2026-02-22 17:49 161
    {
c58dbdb7 Poul Sander 2026-02-22 17:49 162
        zoomOut();
c58dbdb7 Poul Sander 2026-02-22 17:49 163
        event->accept();
c58dbdb7 Poul Sander 2026-02-22 17:49 164
        return;
c58dbdb7 Poul Sander 2026-02-22 17:49 165
    }
c58dbdb7 Poul Sander 2026-02-22 17:49 166
61136b77 Poul Sander 2026-02-20 22:45 167
    QWidget::keyPressEvent(event);
61136b77 Poul Sander 2026-02-20 22:45 168
}
c58dbdb7 Poul Sander 2026-02-22 17:49 169
c58dbdb7 Poul Sander 2026-02-22 17:49 170
void ImageViewWidget::zoomIn()
c58dbdb7 Poul Sander 2026-02-22 17:49 171
{
c58dbdb7 Poul Sander 2026-02-22 17:49 172
    if (m_zoomMode == FitToScreen) {
c58dbdb7 Poul Sander 2026-02-22 17:49 173
        // Calculate the current effective scale so zooming feels continuous
c58dbdb7 Poul Sander 2026-02-22 17:49 174
        double sx = static_cast<double>(width()) / m_pixmap.width();
c58dbdb7 Poul Sander 2026-02-22 17:49 175
        double sy = static_cast<double>(height()) / m_pixmap.height();
c58dbdb7 Poul Sander 2026-02-22 17:49 176
        m_zoomFactor = std::min(sx, sy);
c58dbdb7 Poul Sander 2026-02-22 17:49 177
    }
c58dbdb7 Poul Sander 2026-02-22 17:49 178
    m_zoomFactor *= 1.25;
c58dbdb7 Poul Sander 2026-02-22 17:49 179
    m_zoomMode = CustomZoom;
c58dbdb7 Poul Sander 2026-02-22 17:49 180
    update();
c58dbdb7 Poul Sander 2026-02-22 17:49 181
}
c58dbdb7 Poul Sander 2026-02-22 17:49 182
c58dbdb7 Poul Sander 2026-02-22 17:49 183
void ImageViewWidget::zoomOut()
c58dbdb7 Poul Sander 2026-02-22 17:49 184
{
c58dbdb7 Poul Sander 2026-02-22 17:49 185
    if (m_zoomMode == FitToScreen) {
c58dbdb7 Poul Sander 2026-02-22 17:49 186
        double sx = static_cast<double>(width()) / m_pixmap.width();
c58dbdb7 Poul Sander 2026-02-22 17:49 187
        double sy = static_cast<double>(height()) / m_pixmap.height();
c58dbdb7 Poul Sander 2026-02-22 17:49 188
        m_zoomFactor = std::min(sx, sy);
c58dbdb7 Poul Sander 2026-02-22 17:49 189
    }
c58dbdb7 Poul Sander 2026-02-22 17:49 190
    m_zoomFactor /= 1.25;
c58dbdb7 Poul Sander 2026-02-22 17:49 191
    if (m_zoomFactor < 0.01)
c58dbdb7 Poul Sander 2026-02-22 17:49 192
        m_zoomFactor = 0.01;
c58dbdb7 Poul Sander 2026-02-22 17:49 193
    m_zoomMode = CustomZoom;
c58dbdb7 Poul Sander 2026-02-22 17:49 194
    update();
c58dbdb7 Poul Sander 2026-02-22 17:49 195
}
c58dbdb7 Poul Sander 2026-02-22 17:49 196
c58dbdb7 Poul Sander 2026-02-22 17:49 197
void ImageViewWidget::zoomOriginal()
c58dbdb7 Poul Sander 2026-02-22 17:49 198
{
c58dbdb7 Poul Sander 2026-02-22 17:49 199
    m_zoomMode = OriginalSize;
c58dbdb7 Poul Sander 2026-02-22 17:49 200
    m_zoomFactor = 1.0;
c58dbdb7 Poul Sander 2026-02-22 17:49 201
    update();
c58dbdb7 Poul Sander 2026-02-22 17:49 202
}
c58dbdb7 Poul Sander 2026-02-22 17:49 203
c58dbdb7 Poul Sander 2026-02-22 17:49 204
void ImageViewWidget::zoomFitToScreen()
c58dbdb7 Poul Sander 2026-02-22 17:49 205
{
c58dbdb7 Poul Sander 2026-02-22 17:49 206
    m_zoomMode = FitToScreen;
c8ae2669 Poul Sander 2026-02-22 17:52 207
    m_offset = QPoint(0, 0);
c58dbdb7 Poul Sander 2026-02-22 17:49 208
    update();
c58dbdb7 Poul Sander 2026-02-22 17:49 209
}
c8ae2669 Poul Sander 2026-02-22 17:52 210
c8ae2669 Poul Sander 2026-02-22 17:52 211
void ImageViewWidget::mousePressEvent(QMouseEvent *event)
c8ae2669 Poul Sander 2026-02-22 17:52 212
{
c8ae2669 Poul Sander 2026-02-22 17:52 213
    if (event->button() == Qt::LeftButton) {
c8ae2669 Poul Sander 2026-02-22 17:52 214
        m_dragging = true;
c8ae2669 Poul Sander 2026-02-22 17:52 215
        m_dragStart = event->pos();
c8ae2669 Poul Sander 2026-02-22 17:52 216
        m_offsetAtDragStart = m_offset;
c8ae2669 Poul Sander 2026-02-22 17:52 217
        setCursor(Qt::ClosedHandCursor);
c8ae2669 Poul Sander 2026-02-22 17:52 218
        event->accept();
c8ae2669 Poul Sander 2026-02-22 17:52 219
    }
c8ae2669 Poul Sander 2026-02-22 17:52 220
}
c8ae2669 Poul Sander 2026-02-22 17:52 221
c8ae2669 Poul Sander 2026-02-22 17:52 222
void ImageViewWidget::mouseMoveEvent(QMouseEvent *event)
c8ae2669 Poul Sander 2026-02-22 17:52 223
{
c8ae2669 Poul Sander 2026-02-22 17:52 224
    if (m_dragging) {
c8ae2669 Poul Sander 2026-02-22 17:52 225
        m_offset = m_offsetAtDragStart + (event->pos() - m_dragStart);
c8ae2669 Poul Sander 2026-02-22 17:52 226
        update();
c8ae2669 Poul Sander 2026-02-22 17:52 227
        event->accept();
c8ae2669 Poul Sander 2026-02-22 17:52 228
    }
c8ae2669 Poul Sander 2026-02-22 17:52 229
}
c8ae2669 Poul Sander 2026-02-22 17:52 230
c8ae2669 Poul Sander 2026-02-22 17:52 231
void ImageViewWidget::mouseReleaseEvent(QMouseEvent *event)
c8ae2669 Poul Sander 2026-02-22 17:52 232
{
c8ae2669 Poul Sander 2026-02-22 17:52 233
    if (event->button() == Qt::LeftButton && m_dragging) {
c8ae2669 Poul Sander 2026-02-22 17:52 234
        m_dragging = false;
c8ae2669 Poul Sander 2026-02-22 17:52 235
        setCursor(Qt::ArrowCursor);
c8ae2669 Poul Sander 2026-02-22 17:52 236
        event->accept();
c8ae2669 Poul Sander 2026-02-22 17:52 237
    }
c8ae2669 Poul Sander 2026-02-22 17:52 238
}
c8ae2669 Poul Sander 2026-02-22 17:52 239
1035a476 Poul Sander 2026-02-23 19:30 240
void ImageViewWidget::mouseDoubleClickEvent(QMouseEvent *event)
1035a476 Poul Sander 2026-02-23 19:30 241
{
1035a476 Poul Sander 2026-02-23 19:30 242
    if (event->button() == Qt::LeftButton) {
1035a476 Poul Sander 2026-02-23 19:30 243
        emit closeRequested();
1035a476 Poul Sander 2026-02-23 19:30 244
        event->accept();
1035a476 Poul Sander 2026-02-23 19:30 245
    }
1035a476 Poul Sander 2026-02-23 19:30 246
}
1035a476 Poul Sander 2026-02-23 19:30 247
c8ae2669 Poul Sander 2026-02-22 17:52 248
void ImageViewWidget::clampOffset(int imgW, int imgH)
c8ae2669 Poul Sander 2026-02-22 17:52 249
{
c8ae2669 Poul Sander 2026-02-22 17:52 250
    // Only allow panning when the drawn image exceeds the widget
c8ae2669 Poul Sander 2026-02-22 17:52 251
    int overflowX = imgW - width();
c8ae2669 Poul Sander 2026-02-22 17:52 252
    int overflowY = imgH - height();
c8ae2669 Poul Sander 2026-02-22 17:52 253
c8ae2669 Poul Sander 2026-02-22 17:52 254
    if (overflowX <= 0)
c8ae2669 Poul Sander 2026-02-22 17:52 255
        m_offset.rx() = 0;
c8ae2669 Poul Sander 2026-02-22 17:52 256
    else {
c8ae2669 Poul Sander 2026-02-22 17:52 257
        int limit = overflowX / 2;
c8ae2669 Poul Sander 2026-02-22 17:52 258
        m_offset.rx() = std::clamp(m_offset.x(), -limit, limit);
c8ae2669 Poul Sander 2026-02-22 17:52 259
    }
c8ae2669 Poul Sander 2026-02-22 17:52 260
c8ae2669 Poul Sander 2026-02-22 17:52 261
    if (overflowY <= 0)
c8ae2669 Poul Sander 2026-02-22 17:52 262
        m_offset.ry() = 0;
c8ae2669 Poul Sander 2026-02-22 17:52 263
    else {
c8ae2669 Poul Sander 2026-02-22 17:52 264
        int limit = overflowY / 2;
c8ae2669 Poul Sander 2026-02-22 17:52 265
        m_offset.ry() = std::clamp(m_offset.y(), -limit, limit);
c8ae2669 Poul Sander 2026-02-22 17:52 266
    }
c8ae2669 Poul Sander 2026-02-22 17:52 267
}
1970-01-01 00:00 268