// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #include "JFJochSimpleImage.h" #include #include #include #include #include #include #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); } void JFJochSimpleImage::setImage(std::shared_ptr img) { if (img) { image_ = std::move(img); loadImageInternal(); GeneratePixmap(); Redraw(); } else { image_.reset(); W = 0; H = 0; if (scene()) scene()->clear(); } } void JFJochSimpleImage::mouseHover(QMouseEvent *event) { 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 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(input); for (int i = 0; i < W * H; i++) image_fp[i] = static_cast(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 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(src); break; case CompressedImageMode::Int8: loadImageInternal(src); break; case CompressedImageMode::Uint16: loadImageInternal(src); break; case CompressedImageMode::Int16: loadImageInternal(src); break; case CompressedImageMode::Uint32: loadImageInternal(src); break; case CompressedImageMode::Int32: loadImageInternal(src); break; case CompressedImageMode::Float32: loadImageInternal(src); break; case CompressedImageMode::Float64: loadImageInternal(src); break; default: throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Image format not supported"); } } void JFJochSimpleImage::updateOverlay() { if (!scene()) return; scene()->clear(); if (W*H <= 0) return; // Add base image scene()->addItem(new QGraphicsPixmapItem(pixmap)); QFont font("Arial", 1); // Font for pixel value text font.setPixelSize(1); // This will render very small text (1-pixel high). // Get the visible area in the scene coordinates QRectF visibleRect = mapToScene(viewport()->geometry()).boundingRect(); // Calculate the range of pixels to process const int W = int(image_->image.GetWidth()); const int H = int(image_->image.GetHeight()); const int startX = std::max(0, static_cast(std::floor(visibleRect.left()))); const int endX = std::min(W, static_cast(std::ceil(visibleRect.right()))); const int startY = std::max(0, static_cast(std::floor(visibleRect.top()))); const int endY = std::min(H, static_cast(std::ceil(visibleRect.bottom()))); // Only when zoomed in and region small (performance) if (scale_factor > 20.0 && (endX - startX + 1) * (endY - startY + 1) < 500) { const qreal f = std::clamp(scale_factor, 0.5, 50.0); QFont font("Arial", 1); font.setPixelSize(1); // 1 px font; we use scaling below as needed for (int y = startY; y < endY; ++y) { const size_t base = size_t(y) * size_t(W); for (int x = startX; x < endX; ++x) { const size_t idx = base + size_t(x); const float raw = image_fp[idx]; const rgb c = image_rgb[idx]; // If no scalar was stored (e.g., RGB source), you can skip or compute some alt text if (std::isnan(raw)) continue; // Text content from original value const QString pixelText = QString::number(raw); // Contrast against colored pixel QGraphicsTextItem *textItem = scene()->addText(pixelText, font); if (luminance(c) > 128.0) textItem->setDefaultTextColor(Qt::black); else textItem->setDefaultTextColor(Qt::white); textItem->setPos(x - 0.7, y - 0.8); textItem->setScale(0.2); textItem->setZValue(10.0); } } } DrawROI(); }