diff --git a/viewer/image_viewer/JFJochImage.cpp b/viewer/image_viewer/JFJochImage.cpp index 5a9b97aa0..cc15d6837 100644 --- a/viewer/image_viewer/JFJochImage.cpp +++ b/viewer/image_viewer/JFJochImage.cpp @@ -8,6 +8,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include JFJochImage::JFJochImage(QWidget *parent) : QGraphicsView(parent) { setDragMode(QGraphicsView::NoDrag); // Disable default drag mode @@ -265,6 +272,81 @@ void JFJochImage::mouseReleaseEvent(QMouseEvent *event) { } +void JFJochImage::contextMenuEvent(QContextMenuEvent *event) { + QMenu menu(this); + + QAction *copyImageAct = menu.addAction(tr("Copy image")); + QAction *copyWithOverlayAct = menu.addAction(tr("Copy image with overlay")); + menu.addSeparator(); + QAction *fitAct = menu.addAction(tr("Fit image to view")); + QAction *clearRoiAct = menu.addAction(tr("Clear ROI")); + + const bool hasImage = (W > 0 && H > 0 && !pixmap.isNull()); + copyImageAct->setEnabled(hasImage); + copyWithOverlayAct->setEnabled(hasImage && scene()); + + QAction *chosen = menu.exec(event->globalPos()); + if (!chosen) return; + + if (chosen == copyImageAct) { + copyImageToClipboard(); + } else if (chosen == copyWithOverlayAct) { + copyImageWithOverlayToClipboard(); + } else if (chosen == fitAct) { + fitToView(); + } else if (chosen == clearRoiAct) { + clearROIInternal(); + } +} + +static void setClipboardAsJpegAndImage(const QImage &img, int quality = 95) { + // Provide both "image/jpeg" and generic image flavors for better compatibility + QByteArray ba; + ba.reserve(img.width() * img.height() * 3 / 2); + QBuffer buf(&ba); + buf.open(QIODevice::WriteOnly); + img.convertToFormat(QImage::Format_ARGB32); // ensure a known format for encoding + img.save(&buf, "JPEG", quality); + + auto *mime = new QMimeData(); + mime->setData("image/jpeg", ba); + mime->setImageData(img); // also set as generic bitmap + QGuiApplication::clipboard()->setMimeData(mime); +} + +void JFJochImage::copyImageToClipboard() { + if (W == 0 || H == 0 || pixmap.isNull()) return; + + // Use the underlying rendered image (no overlay) + QImage img = pixmap.toImage(); + setClipboardAsJpegAndImage(img, 95); + emit writeStatusBar(tr("Image copied to clipboard"), 2000); +} + +void JFJochImage::copyImageWithOverlayToClipboard() { + if (W == 0 || H == 0 || !scene()) return; + + // Render the entire scene (image + overlay) at native image resolution + QImage img(int(W), int(H), QImage::Format_ARGB32_Premultiplied); + img.fill(Qt::transparent); + QPainter p(&img); + // Ensure we render the same logical rect as the scene/image coordinates + const QRectF sourceRect(0, 0, qreal(W), qreal(H)); + scene()->render(&p, QRectF(0, 0, qreal(W), qreal(H)), sourceRect); + p.end(); + + setClipboardAsJpegAndImage(img, 95); + emit writeStatusBar(tr("Image with overlay copied to clipboard"), 2000); +} + +void JFJochImage::clearROIInternal() { + roiBox = QRectF(); // clear any ROI + // Keep current roi_type; ROI simply becomes empty + CalcROI(); // will emit a zeroed ROI message + updateOverlay(); + emit writeStatusBar(tr("ROI cleared"), 1500); +} + JFJochImage::ResizeHandle JFJochImage::hitTestROIHandle(const QPointF& scenePos, qreal tol) const { if (roiBox.isNull() || roiBox.width() <= 0 || roiBox.height() <= 0) diff --git a/viewer/image_viewer/JFJochImage.h b/viewer/image_viewer/JFJochImage.h index 3c6b1da42..a6c97d1f3 100644 --- a/viewer/image_viewer/JFJochImage.h +++ b/viewer/image_viewer/JFJochImage.h @@ -19,6 +19,7 @@ class JFJochImage : public QGraphicsView { void writePixelLabels(); void wheelEvent(QWheelEvent* event) override; void resizeEvent(QResizeEvent *event) override; + void contextMenuEvent(QContextMenuEvent *event) override; void mousePressEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; @@ -76,6 +77,10 @@ protected: // Perform initial fit-to-view (shorter direction), once per image size void fitToViewShorterSideOnce(); QSize last_fit_viewport_ = {}; + + void copyImageToClipboard(); + void copyImageWithOverlayToClipboard(); + void clearROIInternal(); signals: void foregroundChanged(float v); void backgroundChanged(float v);