The magnifier and the main view are two views of the same image at different
position and zoom, but the magnifier ran the whole pipeline again on its own
copy: it wrapped the same int32 buffer in a SimpleImage, converted it to float,
coloured every pixel and kept its own full-size QImage. That is a second
conversion and two extra full-detector buffers (20 MB at 2.8 Mpx, 138 MB at
18 Mpx) to feed a 320x320 window.
Separate producing a frame from displaying one:
- JFJochImage keeps the rendered frame in a shared_ptr<QImage> (the pointer is
stable for the widget's lifetime; only the contents change, so the existing
buffer reuse is unaffected), publishes it via Frame() and announces new
pixels with frameRendered().
- JFJochImageItem holds that shared_ptr instead of a reference to a member of
its owner, which also removes a lifetime coupling.
- JFJochFollowerImage is a small read-only view of such a frame with its own
zoom and centre. It shows only the image: overlays, ROI tools and per-pixel
labels belong to the view that owns the data.
- The magnifier becomes one of those, fed from frameRendered().
Consequences beyond the saving: the magnifier now agrees with the main view on
colour map, contrast and HDR mode, which it never did -- it was wired to
neither, so it always drew with its own defaults. And the visibility guard
added in 6d1af4921 is gone: there is no longer any per-frame work to skip, so
nothing needs guarding. That guard was a workaround for this design.
Stepping 30 frames with the magnifier open: 5550 -> 4810 ms CPU, which is what
it costs with the magnifier closed (4770 ms) -- it is now free either way.
Verified: main image panel and a drag-pan stay pixel-identical to the
pre-refactor binary (AE=0); the magnifier follows the cursor, updates on a new
frame, and now tracks a colour-map change.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
61 lines
1.8 KiB
C++
61 lines
1.8 KiB
C++
// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
#include "JFJochFollowerImage.h"
|
|
#include "JFJochImage.h"
|
|
|
|
#include <algorithm>
|
|
|
|
#include <QGraphicsScene>
|
|
#include <QWheelEvent>
|
|
|
|
JFJochFollowerImage::JFJochFollowerImage(QWidget *parent) : QGraphicsView(parent) {
|
|
setScene(new QGraphicsScene(this));
|
|
setTransformationAnchor(QGraphicsView::AnchorViewCenter);
|
|
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
setTransform(QTransform::fromScale(zoom_, zoom_));
|
|
}
|
|
|
|
void JFJochFollowerImage::SetFrame(std::shared_ptr<const QImage> frame) {
|
|
const bool same_pointer = (frame_ == frame);
|
|
const bool same_size = frame_ && frame && frame_->size() == frame->size();
|
|
frame_ = std::move(frame);
|
|
|
|
if (!frame_ || frame_->isNull()) {
|
|
scene()->clear();
|
|
item_ = nullptr;
|
|
return;
|
|
}
|
|
|
|
// The producer reuses one buffer, so normally only the pixels changed and the item stays.
|
|
if (!item_ || !same_pointer) {
|
|
scene()->clear();
|
|
item_ = new JFJochImageItem(frame_);
|
|
scene()->addItem(item_);
|
|
}
|
|
|
|
if (!same_size) {
|
|
item_->refresh();
|
|
scene()->setSceneRect(0, 0, frame_->width(), frame_->height());
|
|
}
|
|
|
|
viewport()->update();
|
|
}
|
|
|
|
void JFJochFollowerImage::CenterAt(QPointF scenePos) {
|
|
if (!frame_ || frame_->isNull())
|
|
return;
|
|
centerOn(scenePos);
|
|
}
|
|
|
|
void JFJochFollowerImage::wheelEvent(QWheelEvent *event) {
|
|
constexpr double step = 1.15;
|
|
zoom_ *= (event->angleDelta().y() > 0) ? step : 1.0 / step;
|
|
zoom_ = std::clamp(zoom_, 1.0, 200.0);
|
|
|
|
const QPointF center = mapToScene(viewport()->rect().center());
|
|
setTransform(QTransform::fromScale(zoom_, zoom_));
|
|
centerOn(center);
|
|
}
|