diff --git a/viewer/widgets/JFJochImage.cpp b/viewer/widgets/JFJochImage.cpp index 024c5a9a..147eb8d8 100644 --- a/viewer/widgets/JFJochImage.cpp +++ b/viewer/widgets/JFJochImage.cpp @@ -519,52 +519,70 @@ void JFJochImage::centerOnSpot(QPointF point) { centerOn(point); } +void JFJochImage::writePixelLabels() { + + static QFont font([] { + QFont f("DejaVu Sans Mono"); + f.setStyleHint(QFont::TypeWriter); + f.setPixelSize(1); + return f; + }()); + static const QString kGap = QStringLiteral("Gap"); + static const QString kErr = QStringLiteral("Err"); + static const QString kSat = QStringLiteral("Sat"); + + QRectF visibleRect = mapToScene(viewport()->geometry()).boundingRect(); + + const int startX = std::max(0, static_cast(std::floor(visibleRect.left()))); + const int endX = std::min(static_cast(W), static_cast(std::ceil(visibleRect.right()))); + const int startY = std::max(0, static_cast(std::floor(visibleRect.top()))); + const int endY = std::min(static_cast(H), static_cast(std::ceil(visibleRect.bottom()))); + + const int visW = std::max(0, endX - startX); + const int visH = std::max(0, endY - startY); + int maxLabels = 1000; + + if (visW * visH <= maxLabels) { + QString numBuf; // reused buffer + + for (int y = startY; y < endY; y ++) { + for (int x = startX; x < endX; x++) { + const int idx = y * W + x; + const float val = image_fp[idx]; + + const QString* pText = nullptr; + if (std::isnan(val)) { + pText = &kGap; + } else if (std::isinf(val)) { + pText = std::signbit(val) ? &kErr : &kSat; + } else { + // Fixed format reduces overhead and string length variability + numBuf = QString::number(val, 'g', 4); + pText = &numBuf; + } + + QGraphicsTextItem* textItem = scene()->addText(*pText, font); + if (luminance(image_rgb[idx]) > 128.0) + textItem->setDefaultTextColor(Qt::black); + else + textItem->setDefaultTextColor(Qt::white); + + textItem->setPos(x - 0.7, y - 0.8); + textItem->setScale(0.2); + } + } + } +} + void JFJochImage::updateOverlay() { if (!scene() || W*H <= 0) return; scene()->clear(); 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). + if (scale_factor > 30.0) + writePixelLabels(); - // Get the visible area in the scene coordinates - QRectF visibleRect = mapToScene(viewport()->geometry()).boundingRect(); - - // Calculate the range of pixels to process - int startX = std::max(0, static_cast(std::floor(visibleRect.left()))); - int endX = std::min(static_cast(W), static_cast(std::ceil(visibleRect.right()))); - int startY = std::max(0, static_cast(std::floor(visibleRect.top()))); - int endY = std::min(static_cast(H), static_cast(std::ceil(visibleRect.bottom()))); - - if (scale_factor > 30.0 && (endX - startX + 1) * (endY - startY + 1) < 1000) { - // Iterate through the pixels within the visible range - for (int y = startY; y < endY; ++y) { - for (int x = startX; x < endX; ++x) { - QString pixelText; - float val = image_fp[x + W * y]; - - if (std::isnan(val)) - pixelText = "Gap"; - else if (std::isinf(val)) { - if (std::signbit(val)) - pixelText = "Err"; - else - pixelText = "Sat"; - } else - pixelText = QString("%1").arg(val); - - // Add or update text in the scene - QGraphicsTextItem *textItem = scene()->addText(pixelText, font); - if (luminance(image_rgb[x + y * W]) > 128.0) - textItem->setDefaultTextColor(Qt::black); // Text color - else - textItem->setDefaultTextColor(Qt::white); // Text color - textItem->setPos(x - 0.7, y - 0.8); // Position the text over the pixel - textItem->setScale(0.2); // Scale down to 10% of the original size - } - } - } DrawROI(); diff --git a/viewer/widgets/JFJochImage.h b/viewer/widgets/JFJochImage.h index 91624b07..7e7e9539 100644 --- a/viewer/widgets/JFJochImage.h +++ b/viewer/widgets/JFJochImage.h @@ -13,7 +13,7 @@ class JFJochImage : public QGraphicsView { void DrawROI(); virtual void addCustomOverlay(); void updateROI(); - + void writePixelLabels(); void wheelEvent(QWheelEvent* event) override; void resizeEvent(QResizeEvent *event) override; diff --git a/viewer/widgets/JFJochPixelOverlayClass.cpp b/viewer/widgets/JFJochPixelOverlayClass.cpp deleted file mode 100644 index 37953ab9..00000000 --- a/viewer/widgets/JFJochPixelOverlayClass.cpp +++ /dev/null @@ -1,5 +0,0 @@ -// -// Created by jungfrau on 11/7/25. -// - -#include "JFJochPixelOverlayClass.h" \ No newline at end of file diff --git a/viewer/widgets/JFJochPixelOverlayClass.h b/viewer/widgets/JFJochPixelOverlayClass.h deleted file mode 100644 index 8dc5f1a0..00000000 --- a/viewer/widgets/JFJochPixelOverlayClass.h +++ /dev/null @@ -1,147 +0,0 @@ -// -// Created by jungfrau on 11/7/25. -// - -#ifndef JFJOCH_JFJOCHPIXELOVERLAYCLASS_H -#define JFJOCH_JFJOCHPIXELOVERLAYCLASS_H - - -#include -#include -#include -#include -#include - -static constexpr int32_t SATURATED_PXL_VALUE = 0x7FFFFFFF; // adjust to your constants -static constexpr int32_t GAP_PXL_VALUE = 0x7FFFFFFE; -static constexpr int32_t ERROR_PXL_VALUE = 0x7FFFFFFD; - -inline qreal luminance(QRgb c) { - // sRGB luma approximation - return 0.2126 * qRed(c) + 0.7152 * qGreen(c) + 0.0722 * qBlue(c); -} - -class PixelTextOverlayItem : public QGraphicsItem { -public: - PixelTextOverlayItem(const int32_t* values, const QRgb* rgb, int width, int height) - : m_values(values), m_rgb(rgb), m_w(width), m_h(height) { - setFlag(ItemUsesExtendedStyleOption, true); - - // Choose a legible monospaced font; adjust as needed - m_font.setFamily("DejaVu Sans Mono"); - m_font.setStyleHint(QFont::TypeWriter); - m_font.setPixelSize(10); // base size; tune with zoom externally if desired - } - - // Item covers the whole image in item coordinates - QRectF boundingRect() const override { - return QRectF(0, 0, m_w, m_h); - } - - // Call when viewport/zoom changes - void setView(int startX, int startY, int endX, int endY, double scaleFactor) { - m_sx = std::clamp(startX, 0, m_w); - m_sy = std::clamp(startY, 0, m_h); - m_ex = std::clamp(endX, 0, m_w); - m_ey = std::clamp(endY, 0, m_h); - m_scale = scaleFactor; - update(); - } - - // Optional: keep text a constant on-screen size by adjusting pixel size with zoom - void setFontPixelSize(int px) { - if (px <= 0) return; - if (m_font.pixelSize() != px) { - m_font.setPixelSize(px); - m_staticCache.clear(); // font size change invalidates cached layout - update(); - } - } - - // Optional: tweak font or color policy externally - void setFont(const QFont& f) { - m_font = f; - m_staticCache.clear(); - update(); - } - - void setMinScaleToShow(double s) { m_minScale = s; } - -protected: - void paint(QPainter* p, const QStyleOptionGraphicsItem*, QWidget*) override { - if (!m_values || !m_rgb) return; - - // Gate: only draw when zoomed in enough and area small enough - const int w = m_ex - m_sx; - const int h = m_ey - m_sy; - if (m_scale < m_minScale || w <= 0 || h <= 0) return; - if (1LL * w * h > 500) return; // keep fast; same idea as your original guard - - p->setRenderHint(QPainter::TextAntialiasing, false); - p->setFont(m_font); - - for (int y = m_sy; y < m_ey; ++y) { - const int rowOff = y * m_w; - for (int x = m_sx; x < m_ex; ++x) { - const int idx = rowOff + x; - const int32_t v = m_values[idx]; - - const QString& label = toLabel(v); - const QStaticText& st = cachedStaticText(label); - - // Contrast-aware text color - const qreal lum = luminance(m_rgb[idx]); - p->setPen(lum > 128.0 ? Qt::black : Qt::white); - - // Offsets so text sits within/near the pixel; tune as you like - // If you want exact pixel center, use QPointF(x + 0.5, y + 0.5) and align text. - p->drawStaticText(QPointF(x - 0.7, y - 0.8), st); - } - } - } - -private: - const QString& toLabel(int32_t v) { - if (v == SATURATED_PXL_VALUE) return m_cachedSat; - if (v == GAP_PXL_VALUE) return m_cachedGap; - if (v == ERROR_PXL_VALUE) return m_cachedErr; - - // Cache numeric strings by value to avoid reformatting - auto it = m_valueToString.find(v); - if (it != m_valueToString.end()) return it.value(); - return m_valueToString.insert(v, QString::number(v)).value(); - } - - const QStaticText& cachedStaticText(const QString& s) { - auto it = m_staticCache.find(s); - if (it != m_staticCache.end()) return it.value(); - - QStaticText st(s); - st.setTextFormat(Qt::PlainText); - st.setPerformanceHint(QStaticText::AggressiveCaching); - // No width wrapping; tiny labels - st.setTextWidth(-1); - return m_staticCache.insert(s, st).value(); - } - - const int32_t* m_values = nullptr; // image values (x + y*w) - const QRgb* m_rgb = nullptr; // RGB image for luminance - int m_w = 0, m_h = 0; - - int m_sx = 0, m_sy = 0, m_ex = 0, m_ey = 0; - double m_scale = 1.0; - double m_minScale = 20.0; // match your logic - - QFont m_font; - - // Caches - QHash m_valueToString; - QHash m_staticCache; - const QString m_cachedSat = QStringLiteral("Sat"); - const QString m_cachedGap = QStringLiteral("Gap"); - const QString m_cachedErr = QStringLiteral("Err"); -}; - - - -#endif //JFJOCH_JFJOCHPIXELOVERLAYCLASS_H \ No newline at end of file