From 379feac2e96c29e416488ba949dc67501bcddca1 Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Thu, 30 Jul 2026 11:22:27 +0200 Subject: [PATCH] Viewer: lay out pixel labels only for the area being repainted drawPixelLabels took its range from the whole viewport rather than from the exposed rect it was given, so a 200x40 px hover repaint still walked up to 5000 cells doing mapFromScene + QImage::pixel + drawText for each, only to have the result clipped away. With hover feedback now rate-limited to 15 Hz that ran ~75 times a second at high zoom, against once per overlay rebuild before the rendering rework. Intersect with the exposed rect; same in the magnifier. Co-Authored-By: Claude Opus 5 (1M context) --- viewer/image_viewer/JFJochFollowerImage.cpp | 3 ++- viewer/image_viewer/JFJochImage.cpp | 9 ++++++--- viewer/image_viewer/JFJochImage.h | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/viewer/image_viewer/JFJochFollowerImage.cpp b/viewer/image_viewer/JFJochFollowerImage.cpp index fd113cb9..5aa201e9 100644 --- a/viewer/image_viewer/JFJochFollowerImage.cpp +++ b/viewer/image_viewer/JFJochFollowerImage.cpp @@ -91,7 +91,8 @@ void JFJochFollowerImage::drawForeground(QPainter *painter, const QRectF &rect) if (static_cast(pixels.size()) < static_cast(W) * H) return; // values belong to a different frame - const QRectF visible = mapToScene(viewport()->rect()).boundingRect(); + // Clipped to the exposed area, for the same reason as JFJochImage::drawPixelLabels. + const QRectF visible = mapToScene(viewport()->rect()).boundingRect() & rect; const int x0 = std::max(0, static_cast(std::floor(visible.left()))); const int x1 = std::min(W, static_cast(std::ceil(visible.right()))); const int y0 = std::max(0, static_cast(std::floor(visible.top()))); diff --git a/viewer/image_viewer/JFJochImage.cpp b/viewer/image_viewer/JFJochImage.cpp index 25077bae..11a41bf5 100644 --- a/viewer/image_viewer/JFJochImage.cpp +++ b/viewer/image_viewer/JFJochImage.cpp @@ -852,10 +852,13 @@ QString JFJochImage::PixelLabel(int x, int y) const { return QString::number(val, 'e', 1); } -void JFJochImage::drawPixelLabels(QPainter *painter) { +void JFJochImage::drawPixelLabels(QPainter *painter, const QRectF &rect) { constexpr int kMaxLabels = 5000; - const QRectF visibleRect = mapToScene(viewport()->rect()).boundingRect(); + // Only the exposed area, not the whole viewport: a hover repaint dirties a couple of hundred + // pixels, and laying out every visible cell for it would do thousands of mapFromScene / pixel / + // drawText calls whose output is then clipped away - at up to 15 repaints a second. + const QRectF visibleRect = mapToScene(viewport()->rect()).boundingRect() & rect; 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()))); @@ -895,7 +898,7 @@ void JFJochImage::drawForeground(QPainter *painter, const QRectF &rect) { QGraphicsView::drawForeground(painter, rect); if (scale_factor > 30.0 && W * H > 0 && frame_ && !frame_->isNull()) - drawPixelLabels(painter); + drawPixelLabels(painter, rect); } void JFJochImage::resetScenePointers() { diff --git a/viewer/image_viewer/JFJochImage.h b/viewer/image_viewer/JFJochImage.h index b3ead6b1..021f7581 100644 --- a/viewer/image_viewer/JFJochImage.h +++ b/viewer/image_viewer/JFJochImage.h @@ -82,7 +82,7 @@ class JFJochImage : public QGraphicsView { void DrawROI(); virtual void addCustomOverlay(); void updateROI(); - void drawPixelLabels(QPainter *painter); + void drawPixelLabels(QPainter *painter, const QRectF &rect); void wheelEvent(QWheelEvent* event) override; void resizeEvent(QResizeEvent *event) override; void contextMenuEvent(QContextMenuEvent *event) override;