FullViewportUpdate redraws the whole viewport on any change. The attached
comment ("keep overlays in pixel units independent of zoom") does not describe
what the setting does, and nothing here needs it: SmartViewportUpdate repaints
the changed rectangles and falls back to a full repaint by itself once there
are too many to be worth tracking.
This is the view used by the calibration window and the magnifier, and the
magnifier is driven from every hover, so on a remote session it repainted
its whole viewport per pointer motion.
Note: not exercised visually -- both windows are opened from menus, which the
headless harness does not drive. The change is a repaint-mode switch with no
effect on what is drawn.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
118 lines
3.7 KiB
C++
118 lines
3.7 KiB
C++
// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
#include "JFJochSimpleImage.h"
|
|
#include <QGraphicsScene>
|
|
#include <QGraphicsPixmapItem>
|
|
#include <QMouseEvent>
|
|
#include <QtMath>
|
|
#include <cstring>
|
|
#include <algorithm>
|
|
|
|
#include "../../common/JFJochException.h"
|
|
|
|
JFJochSimpleImage::JFJochSimpleImage(QWidget *parent)
|
|
: JFJochImage(parent) {
|
|
auto *scn = new QGraphicsScene(this);
|
|
setScene(scn);
|
|
|
|
// Repaint only what changed. FullViewportUpdate redraws the entire viewport on any change,
|
|
// which is wasted work locally and, on a remote session, uploads far more than was touched.
|
|
setViewportUpdateMode(QGraphicsView::SmartViewportUpdate);
|
|
|
|
// The predicted/float image is unreadable with 3-decimal per-pixel labels.
|
|
label_decimals_ = 1;
|
|
}
|
|
|
|
void JFJochSimpleImage::setImage(std::shared_ptr<const SimpleImage> img) {
|
|
if (img) {
|
|
image_ = std::move(img);
|
|
loadImageInternal();
|
|
RenderImage();
|
|
Redraw();
|
|
CalcROI();
|
|
} else {
|
|
image_.reset();
|
|
W = 0; H = 0;
|
|
if (scene())
|
|
scene()->clear();
|
|
resetScenePointers();
|
|
CalcROI();
|
|
}
|
|
}
|
|
|
|
|
|
void JFJochSimpleImage::mouseHover(const QPointF &scenePos, Qt::KeyboardModifiers) {
|
|
if (image_) {
|
|
// Hover feedback / status bar display
|
|
if ((scenePos.x() >= 0)
|
|
&& (scenePos.x() < image_->image.GetWidth())
|
|
&& (scenePos.y() >= 0)
|
|
&& (scenePos.y() < image_->image.GetHeight())) {
|
|
const auto ix = int(scenePos.x());
|
|
const auto iy = int(scenePos.y());
|
|
const auto idx = iy * int(image_->image.GetWidth()) + ix;
|
|
if (idx >= 0 && idx < int(image_fp.size()))
|
|
emit writeStatusBar(QString("x=%1 y=%2 I=%3")
|
|
.arg(scenePos.x(), 0, 'f', 1)
|
|
.arg(scenePos.y(), 0, 'f', 1)
|
|
.arg(image_fp[size_t(idx)]), 3000);
|
|
} else {
|
|
emit writeStatusBar("", 1000);
|
|
}
|
|
}
|
|
}
|
|
|
|
template<class T>
|
|
void JFJochSimpleImage::loadImageInternal(const uint8_t *input) {
|
|
const size_t W = image_->image.GetWidth();
|
|
const size_t H = image_->image.GetHeight();
|
|
|
|
auto ptr = reinterpret_cast<const T *>(input);
|
|
|
|
for (int i = 0; i < W * H; i++)
|
|
image_fp[i] = static_cast<float>(ptr[i]);
|
|
}
|
|
|
|
void JFJochSimpleImage::loadImageInternal() {
|
|
W = image_->image.GetWidth();
|
|
H = image_->image.GetHeight();
|
|
if (W == 0 || H == 0) return;
|
|
|
|
image_fp.resize(W * H);
|
|
|
|
std::vector<uint8_t> image_buffer;
|
|
|
|
// Access uncompressed data
|
|
const uint8_t *src = image_->image.GetUncompressedPtr(image_buffer);
|
|
const auto mode = image_->image.GetMode();
|
|
|
|
switch (mode) {
|
|
case CompressedImageMode::Uint8:
|
|
loadImageInternal<uint8_t>(src);
|
|
break;
|
|
case CompressedImageMode::Int8:
|
|
loadImageInternal<int8_t>(src);
|
|
break;
|
|
case CompressedImageMode::Uint16:
|
|
loadImageInternal<uint16_t>(src);
|
|
break;
|
|
case CompressedImageMode::Int16:
|
|
loadImageInternal<int16_t>(src);
|
|
break;
|
|
case CompressedImageMode::Uint32:
|
|
loadImageInternal<uint32_t>(src);
|
|
break;
|
|
case CompressedImageMode::Int32:
|
|
loadImageInternal<int32_t>(src);
|
|
break;
|
|
case CompressedImageMode::Float32:
|
|
loadImageInternal<float>(src);
|
|
break;
|
|
case CompressedImageMode::Float64:
|
|
loadImageInternal<double>(src);
|
|
break;
|
|
default:
|
|
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Image format not supported");
|
|
}
|
|
} |