Files
Jungfraujoch/viewer/image_viewer/JFJochAzIntImage.cpp
T
leonarski_fandClaude Opus 5 a704a2cd33 Viewer: draw the rendered image directly instead of via a QPixmap
Every recolour ended with QPixmap::fromImage(), which allocates a second
full-size buffer and converts the whole image into the screen format. That
conversion was the largest single cost left in the colouring path.

Replace QGraphicsPixmapItem with a small item that paints qimg_buffer_ with
QPainter::drawImage. The buffer is already what the raster engine wants, so
nothing is converted or copied. The item declares its opaque area, as the
pixmap item did, so the view still skips the background fill underneath it,
and it turns SmoothPixmapTransform off before drawing to keep the
nearest-neighbour sampling QGraphicsPixmapItem gave us by default -- zoomed-in
detector pixels stay sharp squares.

GeneratePixmap is renamed RenderImage: it no longer makes a pixmap.

18.1 Mpx recolour: 22 -> 5.6 ms (28.0 ms before this series).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 18:58:55 +02:00

116 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 "JFJochAzIntImage.h"
#include <QVBoxLayout>
#include "../../common/JFJochException.h"
#include "../../common/JFJochMath.h"
JFJochAzIntImage::JFJochAzIntImage(QWidget *parent)
: JFJochImage(parent) {
}
void JFJochAzIntImage::Clear() {
W = 0;
H = 0;
image_fp.clear();
if (scene())
scene()->clear();
resetScenePointers();
}
void JFJochAzIntImage::imageLoaded(std::shared_ptr<const JFJochReaderImage> in_image) {
if (!in_image) {
Clear();
CalcROI();
return;
}
const auto &profile = in_image->ImageData().az_int_profile;
const auto &ds = in_image->Dataset();
int az_bins = ds.azimuthal_bins;
int q_bins = ds.q_bins;
if (az_bins > 0 && q_bins > 0 && profile.size() == static_cast<size_t>(az_bins * q_bins)
&& ds.az_int_bin_to_phi.size() == profile.size() && ds.az_int_bin_to_q.size() == profile.size()) {
image = in_image;
float local_min = range_min, local_max = range_max;
if (auto_range) {
local_min = std::numeric_limits<float>::infinity();
local_max = -std::numeric_limits<float>::infinity();
for (float v : profile) {
if (std::isfinite(v)) {
if (v < local_min) local_min = v;
if (v > local_max) local_max = v;
}
}
if (!std::isfinite(local_min) || !std::isfinite(local_max) || local_max <= local_min) {
Clear();
return;
}
} else {
if (!(std::isfinite(local_min) && std::isfinite(local_max) && local_max > local_min)) {
Clear();
return;
}
}
// Update base class members
W = q_bins;
H = az_bins;
image_fp = profile;
// Update foreground/background for color mapping
background = local_min;
foreground = local_max;
emit backgroundChanged(background);
emit foregroundChanged(foreground);
// Render the image and redraw using base class functionality
RenderImage();
Redraw();
CalcROI();
} else {
Clear();
}
}
void JFJochAzIntImage::mouseHover(QMouseEvent* event) {
if (!scene() || !image || W == 0 || H == 0) return;
QPointF scenePos = mapToScene(event->pos());
int x = static_cast<int>(scenePos.x());
int y = static_cast<int>(scenePos.y());
if (x >= 0 && x < static_cast<int>(W) && y >= 0 && y < static_cast<int>(H)) {
size_t idx = y * W + x;
QString statusText = QString("Q: %1 Å^-1 phi: %2° value: %3")
.arg(QString::number(image->Dataset().az_int_bin_to_q[idx], 'f', 3))
.arg(QString::number(image->Dataset().az_int_bin_to_phi[idx], 'f', 3))
.arg(QString::number(image_fp[idx], 'f', 3));
emit writeStatusBar(statusText, 0);
}
}
void JFJochAzIntImage::mouseDoubleClickEvent(QMouseEvent *event) {
if (!scene() || !image || W == 0 || H == 0) return;
QPointF scenePos = mapToScene(event->pos());
int x = static_cast<int>(scenePos.x());
int y = static_cast<int>(scenePos.y());
if (x >= 0 && x < static_cast<int>(W) && y >= 0 && y < static_cast<int>(H)) {
size_t idx = y * W + x;
float q = image->Dataset().az_int_bin_to_q[idx];
float phi = image->Dataset().az_int_bin_to_phi[idx];
auto geom = image->Dataset().experiment.GetDiffractionGeometry();
auto coord = geom.ResPhiToPxl(2 * PI / q, phi / 180.0 * PI);
emit zoomOnBin(QPointF(coord.first, coord.second));
}
}