From 81a7656c8c245cbe9a008dbfb7fa9a0a3e9152e2 Mon Sep 17 00:00:00 2001 From: Aurele Boquet Date: Fri, 18 Sep 2026 16:11:37 +0200 Subject: [PATCH] fix(iv): 'save window' saves only the visible region Signed-off-by: Aurele Boquet --- src/iv/imageviewer.cpp | 23 +++++++++++++++++++++-- src/iv/ivgl.cpp | 35 +++++++++++++++++++++++++++++++++++ src/iv/ivgl.h | 4 ++++ 3 files changed, 60 insertions(+), 2 deletions(-) diff --git a/src/iv/imageviewer.cpp b/src/iv/imageviewer.cpp index 3f88b4b9d6..130ee56ba2 100644 --- a/src/iv/imageviewer.cpp +++ b/src/iv/imageviewer.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -1104,13 +1105,31 @@ ImageViewer::saveWindowAs() IvImage* img = cur(); if (!img) return; + ROI roi; + glwin->get_visible_image_roi(roi); + if (!roi.defined() || roi.width() <= 0 || roi.height() <= 0) { + // TODO: May not be visible. This should be replaced with a GUI dialog. + OIIO::print(stderr, + "Save failed: no pixels of the image are visible\n"); + return; + } QString name; name = QFileDialog::getSaveFileName(this, tr("Save Window"), QString(img->uname().c_str())); if (name.isEmpty()) return; - img->write(name.toStdString(), TypeUnknown, "", image_progress_callback, - this); + ImageBuf cropped; + if (!ImageBufAlgo::cut(cropped, *img, roi)) { + // TODO: May not be visible. This should be replaced with a GUI dialog. + OIIO::print(stderr, "Crop failed: {}\n", cropped.geterror()); + return; + } + bool ok = cropped.write(name.toStdString(), TypeUnknown, "", + image_progress_callback, this); + if (!ok) { + // TODO: May not be visible. This should be replaced with a GUI dialog. + OIIO::print(stderr, "Save failed: {}\n", cropped.geterror()); + } } diff --git a/src/iv/ivgl.cpp b/src/iv/ivgl.cpp index 145a772808..562b023dce 100644 --- a/src/iv/ivgl.cpp +++ b/src/iv/ivgl.cpp @@ -2186,6 +2186,41 @@ IvGL::get_given_image_pixel(int& x, int& y, int mouseX, int mouseY) +void +IvGL::get_visible_image_roi(ROI& roi) +{ + roi = ROI(); + IvImage* img = m_slots[0].image; + if (!img || !img->image_valid()) + return; + const ImageSpec& spec(img->spec()); + + // The two opposite corners of the viewport, in image pixel coordinates + int x0, y0, x1, y1; + get_given_image_pixel(x0, y0, 0, 0); + get_given_image_pixel(x1, y1, width() - 1, height() - 1); + + // Rotating or flipping the display may send the corners anywhere, so map + // both of them and then sort out which is the min and which is the max. + float scale_x = 1.0f, scale_y = 1.0f, rotate_z = 0.0f; + float x0_img = x0, y0_img = y0, x1_img = x1, y1_img = y1; + handle_orientation(img->orientation(), spec.width, spec.height, scale_x, + scale_y, rotate_z, x0_img, y0_img, true); + handle_orientation(img->orientation(), spec.width, spec.height, scale_x, + scale_y, rotate_z, x1_img, y1_img, true); + + ROI visible((int)floorf(std::min(x0_img, x1_img)), + (int)floorf(std::max(x0_img, x1_img)) + 1, + (int)floorf(std::min(y0_img, y1_img)), + (int)floorf(std::max(y0_img, y1_img)) + 1, 0, 1, 0, + spec.nchannels); + + // The window may show more than the image, so keep only real pixels. + roi = roi_intersection(visible, img->roi()); +} + + + void IvGL::get_focus_image_pixel(int& x, int& y) { diff --git a/src/iv/ivgl.h b/src/iv/ivgl.h index 645b6ab9fc..631db58d16 100644 --- a/src/iv/ivgl.h +++ b/src/iv/ivgl.h @@ -87,6 +87,10 @@ class IvGL : public QOpenGLWidget, protected QOpenGLExtraFunctions { /// void get_given_image_pixel(int& x, int& y, int mouseX, int mouseY); + /// Which region of the current image is currently visible in the + /// viewport? + void get_visible_image_roi(ROI& roi); + /// What are the min/max/avg values of each channel in the selected area? void update_area_probe_text();