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>
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);
|
|
|
|
// Keep overlays in pixel units independent of zoom (for labels font sizing)
|
|
setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
|
|
|
|
// 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(QMouseEvent *event) {
|
|
if (image_) {
|
|
const QPointF scenePos = mapToScene(event->pos());
|
|
// 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");
|
|
}
|
|
} |