2511-eiger-mask #2
@@ -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<int>(std::floor(visibleRect.left())));
|
||||
const int endX = std::min(static_cast<int>(W), static_cast<int>(std::ceil(visibleRect.right())));
|
||||
const int startY = std::max(0, static_cast<int>(std::floor(visibleRect.top())));
|
||||
const int endY = std::min(static_cast<int>(H), static_cast<int>(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<int>(std::floor(visibleRect.left())));
|
||||
int endX = std::min(static_cast<int>(W), static_cast<int>(std::ceil(visibleRect.right())));
|
||||
int startY = std::max(0, static_cast<int>(std::floor(visibleRect.top())));
|
||||
int endY = std::min(static_cast<int>(H), static_cast<int>(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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
//
|
||||
// Created by jungfrau on 11/7/25.
|
||||
//
|
||||
|
||||
#include "JFJochPixelOverlayClass.h"
|
||||
@@ -1,147 +0,0 @@
|
||||
//
|
||||
// Created by jungfrau on 11/7/25.
|
||||
//
|
||||
|
||||
#ifndef JFJOCH_JFJOCHPIXELOVERLAYCLASS_H
|
||||
#define JFJOCH_JFJOCHPIXELOVERLAYCLASS_H
|
||||
|
||||
|
||||
#include <QGraphicsItem>
|
||||
#include <QPainter>
|
||||
#include <QStaticText>
|
||||
#include <QHash>
|
||||
#include <QFont>
|
||||
|
||||
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<int32_t, QString> m_valueToString;
|
||||
QHash<QString, QStaticText> m_staticCache;
|
||||
const QString m_cachedSat = QStringLiteral("Sat");
|
||||
const QString m_cachedGap = QStringLiteral("Gap");
|
||||
const QString m_cachedErr = QStringLiteral("Err");
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif //JFJOCH_JFJOCHPIXELOVERLAYCLASS_H
|
||||
Reference in New Issue
Block a user