From ee943e6fd578dc45706b81893a650486a4b45fa4 Mon Sep 17 00:00:00 2001 From: valery <71804886+vangeliq@users.noreply.github.com> Date: Sat, 31 Jan 2026 13:28:52 -0800 Subject: [PATCH] feat(iv): add flip, rotate and save image functionality Added flipping and rotation capabilities via metadata. Note that the actual pixel data remains unchanged, so only image formats that support the rotation metadata will reflect the changes Signed-off-by: valery <71804886+vangeliq@users.noreply.github.com> --- src/iv/imageviewer.cpp | 100 +++++++++++++++++++++++++++++++++++++++++ src/iv/imageviewer.h | 15 +++++++ 2 files changed, 115 insertions(+) diff --git a/src/iv/imageviewer.cpp b/src/iv/imageviewer.cpp index 12db1aa772..f4a4924a5e 100644 --- a/src/iv/imageviewer.cpp +++ b/src/iv/imageviewer.cpp @@ -520,6 +520,22 @@ ImageViewer::createActions() closeupAvgPixelsBox->setToolTip(closeupAvgPixelsTooltip); closeupAvgPixelsLabel->setToolTip(closeupAvgPixelsTooltip); + + rotateLeftAct = new QAction(tr("&Rotate Left"), this); + rotateLeftAct->setShortcut(tr("Ctrl+Shift+L")); + connect(rotateLeftAct, SIGNAL(triggered()), this, SLOT(rotateLeft())); + + rotateRightAct = new QAction(tr("&Rotate Right"), this); + rotateRightAct->setShortcut(tr("Ctrl+Shift+R")); + connect(rotateRightAct, SIGNAL(triggered()), this, SLOT(rotateRight())); + + flipHorizontalAct = new QAction(tr("&Flip Horizontal"), this); + connect(flipHorizontalAct, SIGNAL(triggered()), this, + SLOT(flipHorizontal())); + + flipVerticalAct = new QAction(tr("&Flip Vertical"), this); + connect(flipVerticalAct, SIGNAL(triggered()), this, SLOT(flipVertical())); + // Connect signals to ensure closeupAvgPixelsBox value is always <= closeupPixelsBox value connect(closeupPixelsBox, QOverload::of(&QSpinBox::valueChanged), [this](int value) { @@ -780,6 +796,11 @@ ImageViewer::createMenus() toolsMenu->addAction(toggleAreaSampleAct); toolsMenu->addMenu(slideMenu); toolsMenu->addMenu(sortMenu); + toolsMenu->addSeparator(); + toolsMenu->addAction(rotateLeftAct); + toolsMenu->addAction(rotateRightAct); + toolsMenu->addAction(flipHorizontalAct); + toolsMenu->addAction(flipVerticalAct); // Menus, toolbars, & status // Annotate @@ -2455,3 +2476,82 @@ ImageViewer::areaSampleMode() const { return m_areaSampleMode; } + + +void +ImageViewer::rotateLeft() +{ + IvImage* img = cur(); + if (!img) + return; + + ImageSpec* spec = curspecmod(); + + int curr_orientation = spec->get_int_attribute("Orientation", 1); + + if (curr_orientation >= 1 && curr_orientation <= 8) { + static int next_orientation[] = { 0, 8, 5, 6, 7, 4, 1, 2, 3 }; + curr_orientation = next_orientation[curr_orientation]; + spec->attribute("Orientation", curr_orientation); + } + displayCurrentImage(); +} + + +void +ImageViewer::rotateRight() +{ + IvImage* img = cur(); + if (!img) + return; + + ImageSpec* spec = curspecmod(); + int curr_orientation = spec->get_int_attribute("Orientation", 1); + + if (curr_orientation >= 1 && curr_orientation <= 8) { + static int next_orientation[] = { 0, 6, 7, 8, 5, 2, 3, 4, 1 }; + curr_orientation = next_orientation[curr_orientation]; + spec->attribute("Orientation", curr_orientation); + } + displayCurrentImage(); +} + + +void +ImageViewer::flipHorizontal() +{ + IvImage* img = cur(); + if (!img) + return; + + ImageSpec* spec = curspecmod(); + + int curr_orientation = spec->get_int_attribute("Orientation", 1); + + if (curr_orientation >= 1 && curr_orientation <= 8) { + static int next_orientation[] = { 0, 2, 1, 4, 3, 6, 5, 8, 7 }; + curr_orientation = next_orientation[curr_orientation]; + spec->attribute("Orientation", curr_orientation); + } + displayCurrentImage(); +} + + +void +ImageViewer::flipVertical() +{ + IvImage* img = cur(); + if (!img) + return; + + ImageSpec* spec = curspecmod(); + + int curr_orientation = spec->get_int_attribute("Orientation", 1); + + if (curr_orientation >= 1 && curr_orientation <= 8) { + static int next_orientation[] = { 0, 4, 3, 2, 1, 8, 7, 6, 5 }; + curr_orientation = next_orientation[curr_orientation]; + spec->attribute("Orientation", curr_orientation); + } + displayCurrentImage(); +} \ No newline at end of file diff --git a/src/iv/imageviewer.h b/src/iv/imageviewer.h index 3a255647bb..763aaf0dd7 100644 --- a/src/iv/imageviewer.h +++ b/src/iv/imageviewer.h @@ -219,6 +219,14 @@ class ImageViewer final : public QMainWindow { return img ? &img->spec() : NULL; } + /// Return a modifiable ref to the current image spec, or NULL if there is no + /// current image. + ImageSpec* curspecmod(void) const + { + IvImage* img = cur(); + return img ? &img->specmod() : NULL; + } + bool pixelviewOn(void) const { return showPixelviewWindowAct && showPixelviewWindowAct->isChecked(); @@ -334,6 +342,11 @@ private slots: void editPreferences(); ///< Edit viewer preferences void toggleAreaSample(); ///< Use area probe + void rotateLeft(); + void rotateRight(); + void flipHorizontal(); + void flipVertical(); + void useOCIOAction(bool checked); void ocioColorSpaceAction(); void ocioDisplayViewAction(); @@ -404,6 +417,8 @@ private slots: QAction* showPixelviewWindowAct; QAction* toggleAreaSampleAct; QAction* toggleWindowGuidesAct; + QAction *rotateLeftAct, *rotateRightAct, *flipHorizontalAct, + *flipVerticalAct; QMenu *fileMenu, *editMenu, /**imageMenu,*/ *viewMenu, *toolsMenu, *helpMenu; QMenu* openRecentMenu;