diff --git a/rtdata/languages/default b/rtdata/languages/default index fdcab9568..b68b25b8a 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1587,8 +1587,8 @@ HISTORY_MSG_TONE_EQUALIZER_ENABLED;Tone equalizer HISTORY_MSG_TONE_EQUALIZER_PIVOT;Tone equalizer - Pivot HISTORY_MSG_TONE_EQUALIZER_REGULARIZATION;Tone equalizer - Regularization HISTORY_MSG_TONE_EQUALIZER_SHOW_COLOR_MAP;Tone equalizer - Tonal map -HISTORY_MSG_TRANS_SCALE;Geometry - Scale HISTORY_MSG_TRANS_METHOD;Geometry - Method +HISTORY_MSG_TRANS_SCALE;Geometry - Scale HISTORY_MSG_WAVBALCHROM;Equalizer chrominance HISTORY_MSG_WAVBALLUM;Equalizer luminance HISTORY_MSG_WAVBL;Blur levels @@ -2709,6 +2709,35 @@ TP_FLATFIELD_CLIPCONTROL;Clip control TP_FLATFIELD_CLIPCONTROL_TOOLTIP;Clip control avoids clipped highlights caused by applying the flat field. If there are already clipped highlights before applying the flat field, value 0 is used. TP_FLATFIELD_FROMMETADATA;From Metadata TP_FLATFIELD_LABEL;Flat-Field +TP_FRAMING_ABSOLUTE_HEIGHT;Border Height +TP_FRAMING_ABSOLUTE_WIDTH;Border Width +TP_FRAMING_ALLOW_UPSCALING;Allow upscaling to frame +TP_FRAMING_ASPECT_RATIO;Aspect Ratio: +TP_FRAMING_BASIS;Basis +TP_FRAMING_BASIS_AUTO;Auto +TP_FRAMING_BASIS_HEIGHT;Height +TP_FRAMING_BASIS_LONG_SIDE;Long Edge +TP_FRAMING_BASIS_SHORT_SIDE;Short Edge +TP_FRAMING_BASIS_WIDTH;Width +TP_FRAMING_BLUE;Blue +TP_FRAMING_BORDER_COLOR;Border Color +TP_FRAMING_BORDER_SIZE;Size +TP_FRAMING_BORDER_SIZE_ABSOLUTE;Absolute +TP_FRAMING_BORDER_SIZE_METHOD;Sizing: +TP_FRAMING_BORDER_SIZE_RELATIVE;Relative +TP_FRAMING_FRAMED_HEIGHT;Framed Height +TP_FRAMING_FRAMED_WIDTH;Framed Width +TP_FRAMING_GREEN;Green +TP_FRAMING_LABEL;Framing +TP_FRAMING_LIMIT_MINIMUM;Limit minimum size +TP_FRAMING_METHOD;Method: +TP_FRAMING_METHOD_BBOX;Bounding Box +TP_FRAMING_METHOD_FIXED;Fixed Frame +TP_FRAMING_METHOD_STANDARD;Standard +TP_FRAMING_MIN_HEIGHT;Minimum Height +TP_FRAMING_MIN_WIDTH;Minimum Width +TP_FRAMING_ORIENTATION;Orientation: +TP_FRAMING_RED;Red TP_GENERAL_11SCALE_TOOLTIP;The effects of this tool are only visible or only accurate at a preview scale of 1:1. TP_GRADIENT_CENTER;Center TP_GRADIENT_CENTER_X;Center X @@ -2876,11 +2905,11 @@ TP_LABCURVE_LCREDSK_TOOLTIP;If enabled, the LC Curve affects only red and skin-t TP_LABCURVE_RSTPROTECTION;Red and skin-tones protection TP_LABCURVE_RSTPRO_TOOLTIP;Works on the Chromaticity slider and the CC curve. TP_LENSGEOM_AUTOCROP;Auto-Crop -TP_LENSGEOM_SCALE;Scale TP_LENSGEOM_FILL;Auto-fill TP_LENSGEOM_LABEL;Lens / Geometry TP_LENSGEOM_LIN;Linear TP_LENSGEOM_LOG;Logarithmic +TP_LENSGEOM_SCALE;Scale TP_LENSPROFILE_CORRECTION_AUTOMATCH;Automatically selected TP_LENSPROFILE_CORRECTION_LCPFILE;LCP file TP_LENSPROFILE_CORRECTION_MANUAL;Manually selected diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 63882d816..0a0c9bd68 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -2425,6 +2425,56 @@ bool ResizeParams::operator !=(const ResizeParams& other) const return !(*this == other); } +FramingParams::FramingParams() : + enabled(false), + framingMethod(FramingMethod::STANDARD), + aspectRatio(0), + orientation(Orientation::AS_IMAGE), + framedWidth(800), + framedHeight(600), + allowUpscaling(false), + borderSizingMethod(BorderSizing::PERCENTAGE), + basis(Basis::AUTO), + relativeBorderSize(0.1), + minSizeEnabled(false), + minWidth(0), + minHeight(0), + absWidth(0), + absHeight(0), + borderRed(255), + borderGreen(255), + borderBlue(255) +{ +} + +bool FramingParams::operator ==(const FramingParams& other) const +{ + return + enabled == other.enabled + && framingMethod == other.framingMethod + && aspectRatio == other.aspectRatio + && orientation == other.orientation + && framedWidth == other.framedWidth + && framedHeight == other.framedHeight + && allowUpscaling == other.allowUpscaling + && borderSizingMethod == other.borderSizingMethod + && basis == other.basis + && relativeBorderSize == other.relativeBorderSize + && minSizeEnabled == other.minSizeEnabled + && minWidth == other.minWidth + && minHeight == other.minHeight + && absWidth == other.absWidth + && absHeight == other.absHeight + && borderRed == other.borderRed + && borderGreen == other.borderGreen + && borderBlue == other.borderBlue; +} + +bool FramingParams::operator !=(const FramingParams& other) const +{ + return !(*this == other); +} + const Glib::ustring ColorManagementParams::NoICMString = Glib::ustring("No ICM: sRGB output"); const Glib::ustring ColorManagementParams::NoProfileString = Glib::ustring("(none)"); diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 80a9100d1..7c2833ba1 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1984,6 +1984,64 @@ struct ResizeParams { bool operator !=(const ResizeParams& other) const; }; +struct FramingParams { + // How is framed size determined? + enum class FramingMethod { + STANDARD, // Unconstrained framed size + BBOX, // Framed size within bounding box + FIXED_SIZE // Fixed framed size + }; + + // Orientation of framed image + enum class Orientation { + AS_IMAGE, + LANDSCAPE, + PORTRAIT + }; + + // How to size border? + enum class BorderSizing { + PERCENTAGE, // Percentage of image size + FIXED_SIZE // Fixed pixel dimensions + }; + + // Which dimension to use for percentage based border sizing? + enum class Basis { + AUTO, // Determine by aspect ratio of image and frame + WIDTH, + HEIGHT, + LONG, // Use long side of image + SHORT // Use short side of image + }; + + FramingParams(); + + bool enabled; + + FramingMethod framingMethod; + double aspectRatio; // 0 - Use aspect ratio of image + Orientation orientation; + int framedWidth; + int framedHeight; + bool allowUpscaling; + + BorderSizing borderSizingMethod; + Basis basis; + double relativeBorderSize; + bool minSizeEnabled; + int minWidth; + int minHeight; + int absWidth; + int absHeight; + + int borderRed; + int borderGreen; + int borderBlue; + + bool operator ==(const FramingParams& other) const; + bool operator !=(const FramingParams& other) const; +}; + /** * Parameters entry */ diff --git a/rtgui/CMakeLists.txt b/rtgui/CMakeLists.txt index 6e4908bcb..289992653 100644 --- a/rtgui/CMakeLists.txt +++ b/rtgui/CMakeLists.txt @@ -77,6 +77,7 @@ set(NONCLISOURCEFILES filterpanel.cc flatcurveeditorsubgroup.cc flatfield.cc + framing.cc gradient.cc guiutils.cc histogrampanel.cc diff --git a/rtgui/batchtoolpanelcoord.cc b/rtgui/batchtoolpanelcoord.cc index 219054ca4..35e4f7892 100644 --- a/rtgui/batchtoolpanelcoord.cc +++ b/rtgui/batchtoolpanelcoord.cc @@ -467,9 +467,13 @@ void BatchToolPanelCoordinator::panelChanged(const rtengine::ProcEvent& event, c crop->write (&pparams, &pparamsEdited); resize->update (pparams.crop.enabled, pparams.crop.w, pparams.crop.h, w, h); resize->write (&pparams, &pparamsEdited); + framing->update (pparams.crop.enabled, pparams.crop.w, pparams.crop.h, w, h); + framing->write (&pparams, &pparamsEdited); } else if (event == rtengine::EvCrop) { resize->update (pparams.crop.enabled, pparams.crop.w, pparams.crop.h); resize->write (&pparams, &pparamsEdited); + framing->update (pparams.crop.enabled, pparams.crop.w, pparams.crop.h, w, h); + framing->write (&pparams, &pparamsEdited); } } else { // Compensate rotation on flip diff --git a/rtgui/framing.cc b/rtgui/framing.cc new file mode 100644 index 000000000..fa38f91f6 --- /dev/null +++ b/rtgui/framing.cc @@ -0,0 +1,466 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2004-2010 Gabor Horvath + * + * RawTherapee is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RawTherapee is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RawTherapee. If not, see . + * + * 2024-2024 Daniel Gao + */ + +#include "framing.h" + +#include "aspectratios.h" +#include "resize.h" + +#include + +namespace { + +// Framing method combo box data +constexpr int INDEX_STANDARD = 0; +constexpr int INDEX_BBOX = 1; +constexpr int INDEX_FIXED = 2; +constexpr std::array FRAMING_METHODS = { + "TP_FRAMING_METHOD_STANDARD", + "TP_FRAMING_METHOD_BBOX", + "TP_FRAMING_METHOD_FIXED" +}; + +// Orientation combo box data +constexpr int INDEX_AS_IMAGE = 0; +constexpr int INDEX_LANDSCAPE = 1; +constexpr int INDEX_PORTRAIT = 2; +constexpr std::array ORIENTATION = { + "GENERAL_ASIMAGE", + "GENERAL_LANDSCAPE", + "GENERAL_PORTRAIT" +}; + +// Border sizing method combo box data +constexpr int INDEX_SIZE_RELATIVE = 0; +constexpr int INDEX_SIZE_ABSOLUTE = 1; +constexpr std::array BORDER_SIZE_METHODS = { + "TP_FRAMING_BORDER_SIZE_RELATIVE", + "TP_FRAMING_BORDER_SIZE_ABSOLUTE" +}; + +// Relative sizing basis combo box data +constexpr int INDEX_BASIS_AUTO = 0; +constexpr int INDEX_BASIS_WIDTH = 1; +constexpr int INDEX_BASIS_HEIGHT = 2; +constexpr int INDEX_BASIS_LONG = 3; +constexpr int INDEX_BASIS_SHORT = 4; +constexpr std::array BORDER_SIZE_BASIS = { + "TP_FRAMING_BASIS_AUTO", + "TP_FRAMING_BASIS_WIDTH", + "TP_FRAMING_BASIS_HEIGHT", + "TP_FRAMING_BASIS_LONG_SIDE", + "TP_FRAMING_BASIS_SHORT_SIDE" +}; + +constexpr int INITIAL_IMG_WIDTH = 800; +constexpr int INITIAL_IMG_HEIGHT = 600; + +constexpr int ROW_SPACING = 4; +constexpr float FRAME_LABEL_ALIGN_X = 0.025; +constexpr float FRAME_LABEL_ALIGN_Y = 0.5; + +Gtk::Label* createGridLabel(const char* text) +{ + Gtk::Label* label = Gtk::manage(new Gtk::Label(M(text))); + label->set_halign(Gtk::ALIGN_START); + return label; +} + +MySpinButton* createSpinButton() +{ + MySpinButton* button = Gtk::manage(new MySpinButton()); + button->set_width_chars(5); + button->set_digits(0); + button->set_increments(1, 100); + setExpandAlignProperties(button, false, false, Gtk::ALIGN_END, Gtk::ALIGN_CENTER); + return button; +} + +} // namespace + +const Glib::ustring Framing::TOOL_NAME = "framing"; + +class Framing::AspectRatios +{ +public: + static constexpr int INDEX_CURRENT = 0; + + AspectRatios() : + ratios{{M("GENERAL_CURRENT")}} + { + fillAspectRatios(ratios); + } + + void fillCombo(MyComboBoxText* combo) + { + for (const auto& aspectRatio : ratios) { + combo->append(aspectRatio.label); + } + combo->set_active(INDEX_CURRENT); + } + +private: + std::vector ratios; +}; + +Framing::DimensionGui::DimensionGui(Gtk::Box* parent, const char* text) +{ + box = Gtk::manage(new Gtk::Box()); + Gtk::Label* label = Gtk::manage(new Gtk::Label(M(text))); + setExpandAlignProperties(label, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); + value = createSpinButton(); + box->pack_start(*label); + box->pack_start(*value); + parent->pack_start(*box); +} + +void Framing::DimensionGui::connect(Framing& framing, CallbackFunc callback) +{ + connection = value->signal_value_changed().connect(sigc::mem_fun(framing, callback), true); +} + +Framing::Framing() : + FoldableToolPanel(this, TOOL_NAME, M("TP_FRAMING_LABEL"), false, true), + aspectRatioData(new AspectRatios), + imgWidth(INITIAL_IMG_WIDTH), + imgHeight(INITIAL_IMG_HEIGHT) +{ + setupFramingMethodGui(); + pack_start(*Gtk::manage(new Gtk::Separator(Gtk::ORIENTATION_HORIZONTAL))); + setupBorderSizeGui(); + pack_start(*Gtk::manage(new Gtk::Separator(Gtk::ORIENTATION_HORIZONTAL))); + setupBorderColorsGui(); +} + +Framing::~Framing() { + idleRegister.destroy(); +} + +void Framing::setupFramingMethodGui() +{ + Gtk::Grid* combos = Gtk::manage(new Gtk::Grid()); + combos->set_row_spacing(ROW_SPACING); + + framingMethod = Gtk::manage(new MyComboBoxText()); + for (auto label : FRAMING_METHODS) { + framingMethod->append(M(label)); + } + framingMethod->set_active(INDEX_STANDARD); + framingMethod->set_hexpand(); + framingMethod->set_halign(Gtk::ALIGN_FILL); + + combos->attach(*createGridLabel("TP_FRAMING_METHOD"), 0, 0); + combos->attach(*framingMethod, 1, 0); + + aspectRatioLabel = createGridLabel("TP_FRAMING_ASPECT_RATIO"); + aspectRatio = Gtk::manage(new MyComboBoxText()); + aspectRatioData->fillCombo(aspectRatio); + aspectRatio->set_hexpand(); + aspectRatio->set_halign(Gtk::ALIGN_FILL); + + combos->attach(*aspectRatioLabel, 0, 1); + combos->attach(*aspectRatio, 1, 1); + + orientationLabel = createGridLabel("TP_FRAMING_ORIENTATION"); + orientation = Gtk::manage(new MyComboBoxText()); + for (auto label : ORIENTATION) { + orientation->append(M(label)); + } + orientation->set_active(INDEX_AS_IMAGE); + orientation->set_hexpand(); + orientation->set_halign(Gtk::ALIGN_FILL); + + combos->attach(*orientationLabel, 0, 2); + combos->attach(*orientation, 1, 2); + pack_start(*combos); + + width = DimensionGui(this, "TP_FRAMING_FRAMED_WIDTH"); + width.setRange(Resize::MIN_SIZE, Resize::MAX_SCALE * imgWidth); + width.setValue(imgWidth); + height = DimensionGui(this, "TP_FRAMING_FRAMED_HEIGHT"); + height.setRange(Resize::MIN_SIZE, Resize::MAX_SCALE * imgHeight); + height.setValue(imgHeight); + + allowUpscaling = Gtk::manage(new Gtk::CheckButton(M("TP_FRAMING_ALLOW_UPSCALING"))); + pack_start(*allowUpscaling); + + updateFramingMethodGui(); + + framingMethodChanged = framingMethod->signal_changed().connect( + sigc::mem_fun(*this, &Framing::onFramingMethodChanged)); + aspectRatioChanged = aspectRatio->signal_changed().connect( + sigc::mem_fun(*this, &Framing::onAspectRatioChanged)); + orientationChanged = orientation->signal_changed().connect( + sigc::mem_fun(*this, &Framing::onOrientationChanged)); + width.connect(*this, &Framing::onWidthChanged); + height.connect(*this, &Framing::onHeightChanged); + allowUpscalingConnection = allowUpscaling->signal_toggled().connect( + sigc::mem_fun(*this, &Framing::onAllowUpscalingToggled)); +} + +void Framing::setupBorderSizeGui() +{ + Gtk::Grid* combos = Gtk::manage(new Gtk::Grid()); + combos->set_row_spacing(ROW_SPACING); + + borderSizeMethod = Gtk::manage(new MyComboBoxText()); + for (auto label : BORDER_SIZE_METHODS) { + borderSizeMethod->append(M(label)); + } + borderSizeMethod->set_active(INDEX_SIZE_RELATIVE); + borderSizeMethod->set_hexpand(); + borderSizeMethod->set_halign(Gtk::ALIGN_FILL); + + combos->attach(*createGridLabel("TP_FRAMING_BORDER_SIZE_METHOD"), 0, 0); + combos->attach(*borderSizeMethod, 1, 0); + + basisLabel = createGridLabel("TP_FRAMING_BASIS"); + basis = Gtk::manage(new MyComboBoxText()); + for (auto label : BORDER_SIZE_BASIS) { + basis->append(M(label)); + } + basis->set_active(INDEX_BASIS_AUTO); + basis->set_hexpand(); + basis->set_halign(Gtk::ALIGN_FILL); + + combos->attach(*basisLabel, 0, 1); + combos->attach(*basis, 1, 1); + + pack_start(*combos); + + relativeBorderSize = Gtk::manage(new Adjuster(M("TP_FRAMING_BORDER_SIZE"), 0, 1, 0.01, 0.1)); + pack_start(*relativeBorderSize); + + minSizeFrame = Gtk::manage(new Gtk::Frame()); + minSizeFrame->set_label_align(FRAME_LABEL_ALIGN_X, FRAME_LABEL_ALIGN_Y); + minSizeEnabled = Gtk::manage(new Gtk::CheckButton(M("TP_FRAMING_LIMIT_MINIMUM"))); + minSizeFrame->set_label_widget(*minSizeEnabled); + + minSizeFrameContent = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL)); + + minWidth = DimensionGui(minSizeFrameContent, "TP_FRAMING_MIN_WIDTH"); + minWidth.setRange(0, imgWidth); + minWidth.setValue(0); + minHeight = DimensionGui(minSizeFrameContent, "TP_FRAMING_MIN_HEIGHT"); + minHeight.setRange(0, imgHeight); + minHeight.setValue(0); + + minSizeFrame->add(*minSizeFrameContent); + pack_start(*minSizeFrame); + + absWidth = DimensionGui(this, "TP_FRAMING_ABSOLUTE_WIDTH"); + absWidth.setRange(0, imgWidth); + absWidth.setValue(0); + absHeight = DimensionGui(this, "TP_FRAMING_ABSOLUTE_HEIGHT"); + absHeight.setRange(0, imgHeight); + absHeight.setValue(0); + + updateBorderSizeGui(); + + borderSizeMethodChanged = borderSizeMethod->signal_changed().connect( + sigc::mem_fun(*this, &Framing::onBorderSizeMethodChanged)); + basisChanged = basis->signal_changed().connect( + sigc::mem_fun(*this, &Framing::onBasisChanged)); + relativeBorderSize->setAdjusterListener(this); + minSizeEnabledConnection = minSizeEnabled->signal_toggled().connect( + sigc::mem_fun(*this, &Framing::onMinSizeToggled)); + minWidth.connect(*this, &Framing::onMinWidthChanged); + minHeight.connect(*this, &Framing::onMinHeightChanged); + absWidth.connect(*this, &Framing::onAbsWidthChanged); + absHeight.connect(*this, &Framing::onAbsHeightChanged); +} + +void Framing::setupBorderColorsGui() +{ + Gtk::Frame* const frame = Gtk::manage(new Gtk::Frame()); + + Gtk::Label* const label = Gtk::manage(new Gtk::Label(M("TP_FRAMING_BORDER_COLOR"))); + frame->set_label_align(FRAME_LABEL_ALIGN_X, FRAME_LABEL_ALIGN_Y); + frame->set_label_widget(*label); + + Gtk::Box* const box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL)); + redAdj = Gtk::manage(new Adjuster(M("TP_FRAMING_RED"), 0, 255, 1, 255)); + box->add(*redAdj); + greenAdj = Gtk::manage(new Adjuster(M("TP_FRAMING_GREEN"), 0, 255, 1, 255)); + box->add(*greenAdj); + blueAdj = Gtk::manage(new Adjuster(M("TP_FRAMING_BLUE"), 0, 255, 1, 255)); + box->add(*blueAdj); + + frame->add(*box); + pack_start(*frame); + + redAdj->setAdjusterListener(this); + greenAdj->setAdjusterListener(this); + blueAdj->setAdjusterListener(this); +} + +void Framing::read(const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited) +{ +} + +void Framing::write(rtengine::procparams::ProcParams* pp, ParamsEdited* pedited) +{ +} + +void Framing::setDefaults(const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited) +{ +} + +void Framing::setBatchMode(bool batchMode) +{ + ToolPanel::setBatchMode(batchMode); +} + +void Framing::update(bool isCropped, int croppedWidth, int croppedHeight, + int originalWidth, int originalHeight) +{ + if (originalWidth && originalHeight) { + imgWidth = originalWidth; + imgHeight = originalHeight; + } + + setDimensions(); +} + +void Framing::setDimensions() +{ + idleRegister.add([this]() -> bool { + width.value->set_range(Resize::MIN_SIZE, Resize::MAX_SCALE * imgWidth); + height.value->set_range(Resize::MIN_SIZE, Resize::MAX_SCALE * imgHeight); + + return false; + }); +} + +void Framing::updateFramingMethodGui() +{ + if (batchMode) return; + + int activeRow = framingMethod->get_active_row_number(); + if (activeRow == INDEX_STANDARD) { + aspectRatioLabel->show(); + aspectRatio->show(); + orientationLabel->show(); + orientation->show(); + width.hide(); + height.hide(); + allowUpscaling->hide(); + } else if (activeRow == INDEX_BBOX) { + aspectRatioLabel->show(); + aspectRatio->show(); + orientationLabel->show(); + orientation->show(); + width.show(); + height.show(); + allowUpscaling->hide(); + } else if (activeRow == INDEX_FIXED) { + aspectRatioLabel->hide(); + aspectRatio->hide(); + orientationLabel->hide(); + orientation->hide(); + width.show(); + height.show(); + allowUpscaling->show(); + } +} + +void Framing::updateBorderSizeGui() +{ + if (batchMode) return; + + int activeRow = borderSizeMethod->get_active_row_number(); + if (activeRow == INDEX_SIZE_RELATIVE) { + basisLabel->show(); + basis->show(); + relativeBorderSize->show(); + minSizeFrame->show(); + absWidth.hide(); + absHeight.hide(); + } else if (activeRow == INDEX_SIZE_ABSOLUTE) { + basisLabel->hide(); + basis->hide(); + relativeBorderSize->hide(); + minSizeFrame->hide(); + absWidth.show(); + absHeight.show(); + } + + minSizeFrameContent->set_sensitive(minSizeEnabled->get_active()); +} + +void Framing::adjusterChanged(Adjuster* adj, double newVal) +{ +} + +void Framing::onFramingMethodChanged() +{ + updateFramingMethodGui(); +} + +void Framing::onAspectRatioChanged() +{ +} + +void Framing::onOrientationChanged() +{ +} + +void Framing::onWidthChanged() +{ +} + +void Framing::onHeightChanged() +{ +} + +void Framing::onAllowUpscalingToggled() +{ +} + +void Framing::onBorderSizeMethodChanged() +{ + updateBorderSizeGui(); +} + +void Framing::onBasisChanged() +{ +} + +void Framing::onMinSizeToggled() +{ + updateBorderSizeGui(); +} + +void Framing::onMinWidthChanged() +{ +} + +void Framing::onMinHeightChanged() +{ +} + +void Framing::onAbsWidthChanged() +{ +} + +void Framing::onAbsHeightChanged() +{ +} \ No newline at end of file diff --git a/rtgui/framing.h b/rtgui/framing.h new file mode 100644 index 000000000..90d8d85b9 --- /dev/null +++ b/rtgui/framing.h @@ -0,0 +1,141 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2004-2010 Gabor Horvath + * + * RawTherapee is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RawTherapee is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RawTherapee. If not, see . + * + * 2024-2024 Daniel Gao + */ + +#pragma once + +#include "adjuster.h" +#include "guiutils.h" +#include "toolpanel.h" + +#include + +class Framing final : + public ToolParamBlock, + public AdjusterListener, + public FoldableToolPanel +{ +public: + static const Glib::ustring TOOL_NAME; + + Framing(); + ~Framing(); + + void read(const rtengine::procparams::ProcParams* pp, + const ParamsEdited* pedited = nullptr) override; + void write(rtengine::procparams::ProcParams* pp, + ParamsEdited* pedited = nullptr) override; + void setDefaults(const rtengine::procparams::ProcParams* defParams, + const ParamsEdited* pedited = nullptr) override; + void setBatchMode(bool batchMode) override; + + void update(bool isCropped, int croppedWidth, int croppedHeight, + int originalWidth = 0, int originalHeight = 0); + + // AdjusterListener + void adjusterChanged(Adjuster* adj, double newVal) override; + + // Signal connections + void onFramingMethodChanged(); + void onAspectRatioChanged(); + void onOrientationChanged(); + void onWidthChanged(); + void onHeightChanged(); + void onAllowUpscalingToggled(); + void onBorderSizeMethodChanged(); + void onBasisChanged(); + void onMinSizeToggled(); + void onMinWidthChanged(); + void onMinHeightChanged(); + void onAbsWidthChanged(); + void onAbsHeightChanged(); + +private: + class AspectRatios; + + // Helper struct for repeated patterns + struct DimensionGui + { + using CallbackFunc = void(Framing::*)(); + + DimensionGui() = default; + DimensionGui(Gtk::Box* parent, const char* text); + + void setValue(int newValue) { value->set_value(newValue); } + void setRange(int min, int max) { value->set_range(min, max); } + void connect(Framing& framing, CallbackFunc callback); + + void show() { box->show(); } + void hide() { box->hide(); } + + Gtk::Box* box; + MySpinButton* value; + sigc::connection connection; + }; + + void setupFramingMethodGui(); + void setupBorderSizeGui(); + void setupBorderColorsGui(); + + void setDimensions(); + void updateFramingMethodGui(); + void updateBorderSizeGui(); + + // Framing method + MyComboBoxText* framingMethod; + sigc::connection framingMethodChanged; + Gtk::Label* aspectRatioLabel; + MyComboBoxText* aspectRatio; + sigc::connection aspectRatioChanged; + Gtk::Label* orientationLabel; + MyComboBoxText* orientation; + sigc::connection orientationChanged; + DimensionGui width; + DimensionGui height; + Gtk::CheckButton* allowUpscaling; + sigc::connection allowUpscalingConnection; + + // Border sizing + MyComboBoxText* borderSizeMethod; + sigc::connection borderSizeMethodChanged; + Gtk::Label* basisLabel; + MyComboBoxText* basis; + sigc::connection basisChanged; + Adjuster* relativeBorderSize; + Gtk::Frame* minSizeFrame; + Gtk::Box* minSizeFrameContent; + Gtk::CheckButton* minSizeEnabled; + sigc::connection minSizeEnabledConnection; + DimensionGui minWidth; + DimensionGui minHeight; + DimensionGui absWidth; + DimensionGui absHeight; + + // Border colors + Adjuster* redAdj; + Adjuster* greenAdj; + Adjuster* blueAdj; + + IdleRegister idleRegister; + std::unique_ptr aspectRatioData; + + int imgWidth; + int imgHeight; +}; \ No newline at end of file diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index f3cefe653..a610b147a 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -1307,6 +1307,29 @@ struct ResizeParamsEdited { bool allowUpscaling; }; +struct FramingParamsEdited { + bool enabled; + bool framingMethod; + bool aspectRatio; + bool orientation; + bool framedWidth; + bool framedHeight; + bool allowUpscaling; + + bool borderSizingMethod; + bool basis; + bool relativeBorderSize; + bool minSizeEnabled; + bool minWidth; + bool minHeight; + bool absWidth; + bool absHeight; + + bool borderRed; + bool borderGreen; + bool borderBlue; +}; + class SpotParamsEdited { public: diff --git a/rtgui/prsharpening.cc b/rtgui/prsharpening.cc index d3c936fa2..834e1f486 100644 --- a/rtgui/prsharpening.cc +++ b/rtgui/prsharpening.cc @@ -35,7 +35,7 @@ PrSharpening::PrSharpening () : FoldableToolPanel(this, TOOL_NAME, M("TP_PRSHARP milestones.push_back( GradientMilestone(0.0, 0.0, 0.0, 0.0) ); milestones.push_back( GradientMilestone(1.0, 1.0, 1.0, 1.0) ); - //setEnabledTooltipMarkup(M("TP_PRSHARPENING_TOOLTIP")); + setEnabledTooltipMarkup(M("TP_PRSHARPENING_TOOLTIP")); Gtk::Box* hb = Gtk::manage (new Gtk::Box ()); hb->show (); diff --git a/rtgui/resize.cc b/rtgui/resize.cc index de9f6b4d1..ca4221757 100644 --- a/rtgui/resize.cc +++ b/rtgui/resize.cc @@ -146,22 +146,22 @@ Resize::Resize () : FoldableToolPanel(this, TOOL_NAME, M("TP_RESIZE_LABEL"), fal w->set_digits (0); w->set_increments (1, 100); - w->set_range (32, MAX_SCALE * maxw); + w->set_range (MIN_SIZE, MAX_SCALE * maxw); w->set_value (800); // Doesn't seem to have any effect (overwritten in Resize::read) h->set_digits (0); h->set_increments (1, 100); - h->set_range (32, MAX_SCALE * maxh); + h->set_range (MIN_SIZE, MAX_SCALE * maxh); h->set_value (600); // Doesn't seem to have any effect (overwritten in Resize::read) le->set_digits (0); le->set_increments (1, 100); - le->set_range (32, MAX_SCALE * maxw); + le->set_range (MIN_SIZE, MAX_SCALE * maxw); le->set_value (900); se->set_digits (0); se->set_increments (1, 100); - se->set_range (32, MAX_SCALE * maxh); + se->set_range (MIN_SIZE, MAX_SCALE * maxh); se->set_value (900); wconn = w->signal_value_changed().connect ( sigc::mem_fun(*this, &Resize::entryWChanged), true); @@ -173,7 +173,6 @@ Resize::Resize () : FoldableToolPanel(this, TOOL_NAME, M("TP_RESIZE_LABEL"), fal sconn = spec->signal_changed().connect ( sigc::mem_fun(*this, &Resize::specChanged) ); getSubToolsContainer()->hide(); - getSubToolsContainer()->set_tooltip_markup (M("TP_PRSHARPENING_TOOLTIP")); show_all(); } diff --git a/rtgui/resize.h b/rtgui/resize.h index 674bbb34f..cd83b1418 100644 --- a/rtgui/resize.h +++ b/rtgui/resize.h @@ -22,7 +22,6 @@ #include "adjuster.h" #include "guiutils.h" -#include "guiutils.h" #include "toolpanel.h" class Resize final : @@ -33,6 +32,8 @@ class Resize final : { public: static const Glib::ustring TOOL_NAME; + static constexpr int MAX_SCALE = 16; // 16 to match the main preview max scale of 1600% + static constexpr int MIN_SIZE = 32; Resize (); ~Resize () override; @@ -85,6 +86,4 @@ private: sigc::connection sconn, aconn, wconn, hconn, leconn, seconn; bool wDirty, hDirty, leDirty, seDirty; IdleRegister idle_register; - - static constexpr int MAX_SCALE = 16; // 16 to match the main preview max scale of 1600% }; diff --git a/rtgui/toollocationpref.cc b/rtgui/toollocationpref.cc index 4352e1b07..d15f00de1 100644 --- a/rtgui/toollocationpref.cc +++ b/rtgui/toollocationpref.cc @@ -133,6 +133,8 @@ Glib::ustring getToolTitleKey(Tool tool) return "TP_RESIZE_LABEL"; case Tool::PR_SHARPENING: return "TP_PRSHARPENING_LABEL"; + case Tool::FRAMING: + return "TP_FRAMING_LABEL"; case Tool::CROP_TOOL: return "TP_CROP_LABEL"; case Tool::ICM: diff --git a/rtgui/toolpanelcoord.cc b/rtgui/toolpanelcoord.cc index 4dd1cca22..a02993ddc 100644 --- a/rtgui/toolpanelcoord.cc +++ b/rtgui/toolpanelcoord.cc @@ -164,6 +164,9 @@ const std::vector TRANSFORM_PANEL_TOOLS = { { .id = Tool::PR_SHARPENING, }, + { + .id = Tool::FRAMING, + }, }, }, { @@ -324,6 +327,7 @@ ToolPanelCoordinator::ToolPanelCoordinator (bool batch) : ipc (nullptr), favorit blackwhite = Gtk::manage(new BlackWhite()); resize = Gtk::manage(new Resize()); prsharpening = Gtk::manage(new PrSharpening()); + framing = Gtk::manage(new Framing()); crop = Gtk::manage(new Crop()); icm = Gtk::manage(new ICMPanel()); metadata = Gtk::manage(new MetaDataPanel()); @@ -638,6 +642,8 @@ std::string ToolPanelCoordinator::getToolName(Tool tool) return Resize::TOOL_NAME; case Tool::PR_SHARPENING: return PrSharpening::TOOL_NAME; + case Tool::FRAMING: + return Framing::TOOL_NAME; case Tool::CROP_TOOL: return Crop::TOOL_NAME; case Tool::ICM: @@ -1117,9 +1123,13 @@ void ToolPanelCoordinator::panelChanged(const rtengine::ProcEvent& event, const crop->write(params); resize->update(params->crop.enabled, params->crop.w, params->crop.h, ipc->getFullWidth(), ipc->getFullHeight()); resize->write(params); + framing->update(params->crop.enabled, params->crop.w, params->crop.h, ipc->getFullWidth(), ipc->getFullHeight()); + framing->write(params); } else if (event == rtengine::EvCrop) { resize->update(params->crop.enabled, params->crop.w, params->crop.h); resize->write(params); + framing->update(params->crop.enabled, params->crop.w, params->crop.h, ipc->getFullWidth(), ipc->getFullHeight()); + framing->write(params); } /* @@ -2001,6 +2011,8 @@ FoldableToolPanel *ToolPanelCoordinator::getFoldableToolPanel(Tool tool) const return resize; case Tool::PR_SHARPENING: return prsharpening; + case Tool::FRAMING: + return framing; case Tool::CROP_TOOL: return crop; case Tool::ICM: diff --git a/rtgui/toolpanelcoord.h b/rtgui/toolpanelcoord.h index 6801664f6..31044a9b0 100644 --- a/rtgui/toolpanelcoord.h +++ b/rtgui/toolpanelcoord.h @@ -45,6 +45,7 @@ #include "filmnegative.h" #include "filmsimulation.h" #include "flatfield.h" +#include "framing.h" #include "gradient.h" #include "guiutils.h" #include "hsvequalizer.h" @@ -131,6 +132,7 @@ protected: ChMixer* chmixer; BlackWhite* blackwhite; Resize* resize; + Framing* framing; PrSharpening* prsharpening; ICMPanel* icm; Crop* crop; @@ -282,6 +284,7 @@ public: BLACK_WHITE, RESIZE_TOOL, PR_SHARPENING, + FRAMING, CROP_TOOL, ICM, WAVELET,