diff --git a/viewer/widgets/JFJochPixelOverlayClass.cpp b/viewer/widgets/JFJochPixelOverlayClass.cpp new file mode 100644 index 00000000..37953ab9 --- /dev/null +++ b/viewer/widgets/JFJochPixelOverlayClass.cpp @@ -0,0 +1,5 @@ +// +// 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 new file mode 100644 index 00000000..8dc5f1a0 --- /dev/null +++ b/viewer/widgets/JFJochPixelOverlayClass.h @@ -0,0 +1,147 @@ +// +// 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