rawTherapee/rtgui/curveeditor.cc
Desmis 16db6186dd
Generalized Hyperbolic Stretch in Selective Editing (#7210)
* GHS GUI first step

* GHS GUI first step

* GUI step 3

* Hide show ghsmethod

* Siril functions for ghs

* Init ghs in iplocallab

* ghs step 2

* ghs step 4

* Comment copyright Pixlnsight 2021

* Smooth highlights and tooltips

* Enabled saturation and luminance ghs mode

* First tooltip

* Ghs tooltips

* Remove wrong packstart inverssh

* Change labels and tooltips

* Disabled mask in global and other improvments

* Comment code

* appimage and windows yml ghs

* Change tooltip

* Ghsmethod hue and change tolltips

* Change tooltip

* Inverse Stretch step 1

* Inverse GHS

* GHS tooltips

* Change tooltips

* Change tooltip

* Linear black point

* Small change to blackpoint

* Change tooltip

* Clean and comment code

* forgotten GHS highlight attenuation msg in history

* Comment code with Transformation equations

* Change tooltip

* Wrong default value balck point

* Comment code iplocallab

* Ghs curve step 1

* GHS curve step 2

* Show GHS setting in a Curve Box

* Tooltip for ghs S curve

* Disabled warning in compilation simpleprocess

* Simplified code part 1

* Simplified code GHS graph part 2

* Improve black point with negatives values

* Improve BP and tooltip

* Listener enable only with GHS

* White point for GHS

* Change label and tooltip

* Improve behavior white point and black point

* Link sliders ghs_SP ghs_LP ghs_HP and change code to avoid balck screen

* hide unused button in diagonal curve GHS

* prevents the GHS representation in S from displaying artifacts if ghs-d=0

* Change tooltips

* Improvment and tooltips

* Forgotten tooltip

* Improve GUI GHS S curve - change tooltips

* Set transition gray areas in S curve GHS with values of the RT-spot

* Change label GHS

* setlogscale blackpoint and symmetry

* Set recursive reference enable by default in controlspotpanel

* Change lastlocalCurvesDir Dirghs in locallabtools and options - change labels

* Added in converttonormal ghsMode.. not sure if this is useful

* DIY to make GHS curve work without the choices

* Change RGB calculation with luminance function working profile

* 5 modes GHS method

* Label to show datas clipped Black point and White point

* Various change white point tooltips

* Bad behavior wp bp labels

* Small change to improccordinator call to ghschanged ghsbwchanged

* Set log scale ghs_D

* Hide Graduated filter if GHS enable

* Luminance Lab in ghsmethod

* Lab slope factor step 1

* Slope and Chromaticity GHS improvments

* Fixed bad sqrt line 17477 iplocallab

* Workaround linear GHS - re-enable Graduated filer GHS

* Change limits slope lab factor

* Ghs chromaticity Lab (Lch)

* Improve ghs chromaticity

* Change labels and tooltips Lab chroma

* Slope Lab to 100

* Noise and saturation RGB

* Saturation RGB standard and labels

* Change histogram and navigator panel without gamma when using working profile

* Remove gray in GHS curve

* Local contrast a minima

* Regularization stretch

* Improve Graduated Filter in all cases GHS Color and Light etc.

* Improves nlmeans to reduce noise after GHS

* Change to GF - tooltip Nlmeans

* Added oW oH tW tH etc.

* Added call GF

* tX tY for previewProps

* Comment code GF

* Improve local contrast ghs

* Change norm to norm2

* Improve GUI mode complexity and Lab GHS

* Show values BP WP in GUI

* Labgrid ghs step 1

* Labgrid for simulation GHS - step 2

* More points for Labgrid ghs

* Clean and comment code

* Fixed crash in inverse GHS white point - set to 10 points for GSH simulation

* Change to black point in inverse GHS

* Intilialize simulation with nothing if new spot

* Remove curve GHS - optimize code simulation - improve GUI

* set ghs default - fixed crash is case HP SP LP

* Fixed crash - I hope in inverse GHS

* Simplify WP and BP limits to avoid crash in inverse GHS

* Clean code with ghscurve - ghsshape

* Change tooltips

* Change to D - GUI - comment code

* Simulation with 4 more points

* Best simulation with point 0.05 and 0.95

* Clean code - change for crsah in Inverse GHS

* Show values WP and BP
2024-11-13 08:23:41 +01:00

523 lines
13 KiB
C++

/*
* This file is part of RawTherapee.
*
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
*
* 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.
*
* 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 <https://www.gnu.org/licenses/>.
*/
#include "curveeditor.h"
#include "curveeditorgroup.h"
#include <fstream>
#include <string>
#include "guiutils.h"
#include "multilangmgr.h"
#include "popuptogglebutton.h"
#include "../rtengine/LUT.h"
#include <cstring>
namespace {
class CurveTypePopUpButton: public PopUpToggleButton {
public:
CurveTypePopUpButton(const Glib::ustring &label=""):
PopUpToggleButton(label) {}
void setPosIndexMap(const std::vector<int> &pmap)
{
posidxmap_ = pmap;
}
protected:
int posToIndex(int pos) const override
{
if (pos < 0 || size_t(pos) >= posidxmap_.size()) {
return pos;
}
return posidxmap_[pos];
}
int indexToPos(int index) const override
{
if (index < 0 || size_t(index) >= posidxmap_.size()) {
return index;
}
for (int i = 0, n = int(posidxmap_.size()); i < n; ++i) {
if (posidxmap_[i] == index) {
return i;
}
}
return -1;
}
private:
std::vector<int> posidxmap_;
};
} // namespace
bool CurveEditor::reset()
{
return subGroup->curveReset(this);
}
DiagonalCurveEditor::DiagonalCurveEditor (Glib::ustring text, CurveEditorGroup* ceGroup, CurveEditorSubGroup* ceSubGroup, int typ) : CurveEditor::CurveEditor(text, static_cast<CurveEditorGroup*>(ceGroup), ceSubGroup)
{
if(typ == 0) {
curveType->addEntry("curve-linear-small", M("CURVEEDITOR_LINEAR")); // 0 Linear
curveType->addEntry("curve-spline-small", M("CURVEEDITOR_CUSTOM")); // 1 Spline
curveType->addEntry("curve-catmullrom-small", M("CURVEEDITOR_CATMULLROM")); // 4 CatmullRom
curveType->addEntry("curve-parametric-small", M("CURVEEDITOR_PARAMETRIC")); // 2 Parametric
curveType->addEntry("curve-nurbs-small", M("CURVEEDITOR_NURBS")); // 3 NURBS
} else if(typ == 1) {
curveType->addEntry("curve-nurbs-small", M("CURVEEDITOR_NURBS")); // 3 NURBS
}
if(typ == 0) {
static_cast<CurveTypePopUpButton *>(curveType)->setPosIndexMap({ 0, 1, 4, 2, 3 });
} else if (typ == 1) {
static_cast<CurveTypePopUpButton *>(curveType)->setPosIndexMap({3});
}
curveType->setSelected(DCT_Linear);
curveType->show();
rangeLabels[0] = M("CURVEEDITOR_SHADOWS");
rangeLabels[1] = M("CURVEEDITOR_DARKS");
rangeLabels[2] = M("CURVEEDITOR_LIGHTS");
rangeLabels[3] = M("CURVEEDITOR_HIGHLIGHTS");
rangeMilestones[0] = 0.25;
rangeMilestones[1] = 0.50;
rangeMilestones[2] = 0.75;
}
std::vector<double> DiagonalCurveEditor::getCurve ()
{
std::vector<double> curve;
switch (selected) {
case (DCT_Spline):
return curve = customCurveEd;
case (DCT_Parametric):
return curve = paramCurveEd;
case (DCT_NURBS):
return curve = NURBSCurveEd;
case (DCT_CatumullRom):
return curve = catmullRomCurveEd;
default:
// returning Linear or Unchanged
curve.push_back((double)(selected));
return curve;
}
}
void DiagonalCurveEditor::setResetCurve(DiagonalCurveType cType, const std::vector<double> &resetCurve)
{
switch (cType) {
case (DCT_NURBS):
if (resetCurve.size() && DiagonalCurveType(resetCurve.at(0)) == cType) {
NURBSResetCurve = resetCurve;
}
break;
case (DCT_Parametric):
if (resetCurve.size() && DiagonalCurveType(resetCurve.at(0)) == cType) {
paramResetCurve = resetCurve;
}
break;
case (DCT_Spline):
if (resetCurve.size() && DiagonalCurveType(resetCurve.at(0)) == cType) {
customResetCurve = resetCurve;
}
break;
case (DCT_CatumullRom):
if (resetCurve.size() && DiagonalCurveType(resetCurve.at(0)) == cType) {
catmullRomResetCurve = resetCurve;
}
break;
default:
break;
}
}
void DiagonalCurveEditor::setRangeLabels(Glib::ustring r1, Glib::ustring r2, Glib::ustring r3, Glib::ustring r4)
{
rangeLabels[0] = r1;
rangeLabels[1] = r2;
rangeLabels[2] = r3;
rangeLabels[3] = r4;
}
void DiagonalCurveEditor::getRangeLabels(Glib::ustring &r1, Glib::ustring &r2, Glib::ustring &r3, Glib::ustring &r4)
{
r1 = rangeLabels[0];
r2 = rangeLabels[1];
r3 = rangeLabels[2];
r4 = rangeLabels[3];
}
/*
* Admittedly that this method is called just after the instantiation of this class, we set the shcselector's default values
*/
void DiagonalCurveEditor::setRangeDefaultMilestones(double m1, double m2, double m3)
{
rangeMilestones[0] = m1;
rangeMilestones[1] = m2;
rangeMilestones[2] = m3;
paramCurveEd.at(1) = m1;
paramCurveEd.at(2) = m2;
paramCurveEd.at(3) = m3;
}
void DiagonalCurveEditor::getRangeDefaultMilestones(double &m1, double &m2, double &m3)
{
m1 = rangeMilestones[0];
m2 = rangeMilestones[1];
m3 = rangeMilestones[2];
}
FlatCurveEditor::FlatCurveEditor (Glib::ustring text, CurveEditorGroup* ceGroup, CurveEditorSubGroup* ceSubGroup, bool isPeriodic) : CurveEditor::CurveEditor(text, static_cast<CurveEditorGroup*>(ceGroup), ceSubGroup)
{
periodic = isPeriodic;
identityValue = 0.5;
// Order set in the same order than "enum FlatCurveType". Shouldn't change, for compatibility reason
curveType->addEntry("curve-flat-small", M("CURVEEDITOR_LINEAR")); // 0 Linear
curveType->addEntry("curve-controlpoints-small", M("CURVEEDITOR_MINMAXCPOINTS")); // 1 Min/Max ControlPoints
curveType->setSelected(FCT_Linear);
curveType->show();
}
std::vector<double> FlatCurveEditor::getCurve ()
{
std::vector<double> curve;
switch (selected) {
//case (Parametric):
// return curve = paramCurveEd;
case (FCT_MinMaxCPoints):
return curve = controlPointsCurveEd;
default:
// returning Linear or Unchanged
curve.push_back((double)(selected));
return curve;
}
}
void FlatCurveEditor::setResetCurve(FlatCurveType cType, const std::vector<double> &resetCurve)
{
switch (cType) {
case (FCT_MinMaxCPoints):
if (resetCurve.size() && FlatCurveType(resetCurve.at(0)) == cType) {
controlPointsResetCurve = resetCurve;
}
break;
default:
break;
}
}
/*
* CurveEditor (CurveEditorGroup* ceGroup, Glib::ustring text)
*
* parameters:
* ceGroup = NULL or the address of the Widget that will receive the CurveTypeToggleButton
* text = (optional) label of the curve, displayed in the CurveTypeToggleButton, next to the image
*/
CurveEditor::CurveEditor (Glib::ustring text, CurveEditorGroup* ceGroup, CurveEditorSubGroup* ceSubGroup) : EditSubscriber(ET_PIPETTE)
{
bgHistValid = false;
locallabRef = 0.0;
remoteDrag = false;
selected = DCT_Linear;
bottomBarCP = nullptr;
bottomBarCId = 0;
leftBarCP = nullptr;
leftBarCId = 0;
curveCP = nullptr;
curveCId = 0;
relatedWidget = nullptr;
expandRelatedWidget = true;
group = ceGroup;
subGroup = ceSubGroup;
if (group && text.size()) {
curveType = new CurveTypePopUpButton(text + ":");
} else {
curveType = new CurveTypePopUpButton();
}
curveType->set_tooltip_text(M("CURVEEDITOR_TYPE"));
// TODO: Does this signal have to be blocked when on curve type change ?
curveType->signal_toggled().connect ( sigc::mem_fun(*this, &CurveEditor::curveTypeToggled) );
typeconn = curveType->signal_item_selected().connect (sigc::mem_fun(*this, &CurveEditor::typeSelectionChanged) );
}
void CurveEditor::setCurve (const std::vector<double>& p)
{
tempCurve = p;
group->setCurveExternal(this, p);
}
CurveEditor::~CurveEditor ()
{
delete curveType;
}
void CurveEditor::typeSelectionChanged (int n)
{
group->typeSelectionChanged(this, n);
}
void CurveEditor::curveTypeToggled()
{
group->curveTypeToggled(this);
}
bool CurveEditor::isUnChanged ()
{
return curveType->getSelected() == subGroup->getValUnchanged();
}
void CurveEditor::setUnChanged (bool uc)
{
group->setUnChanged(uc, this);
}
/*
* Update the backgrounds histograms
*/
void CurveEditor::updateBackgroundHistogram(const LUTu& hist)
{
// Copy the histogram in the curve editor cache
if (hist) {
histogram = hist;
bgHistValid = true;
} else {
bgHistValid = false;
}
// Then call the curve editor group to eventually update the histogram
subGroup->updateBackgroundHistogram(this);
}
/*
* Update Locallab reference value displayed in the background
*/
void CurveEditor::updateLocallabBackground(double ref)
{
// Copy Locallab reference value in the curve editor cache
locallabRef = ref;
// Then call the curve editor group to eventually update the histogram
subGroup->updateLocallabBackground(this);
}
// Open up the curve if it has modifications and it's not already opened
// Returns: true if curve was non linear and opened
bool CurveEditor::openIfNonlinear()
{
bool nonLinear = tempCurve.size() && (tempCurve[0] > subGroup->getValLinear()) && (tempCurve[0] < subGroup->getValUnchanged());
if (nonLinear && !curveType->get_active()) {
// Will trigger the signal_clicked event doing the display
curveType->set_active( true );
}
return nonLinear;
}
// Handles markup tooltips
void CurveEditor::setTooltip(Glib::ustring ttip)
{
curveType->set_tooltip_text(ttip.empty() ?
Glib::ustring::compose("<b>%1</b> ", M("CURVEEDITOR_TYPE")) :
Glib::ustring::compose("%1\n<b>%2</b>", ttip, M("CURVEEDITOR_TYPE")));
}
void CurveEditor::setLeftBarColorProvider(ColorProvider* cp, int callerId)
{
leftBarCP = cp;
leftBarCId = callerId;
}
void CurveEditor::setBottomBarColorProvider(ColorProvider* cp, int callerId)
{
bottomBarCP = cp;
bottomBarCId = callerId;
}
void CurveEditor::setLeftBarBgGradient (const std::vector<GradientMilestone> &milestones)
{
leftBarBgGradient = milestones;
}
void CurveEditor::setBottomBarBgGradient (const std::vector<GradientMilestone> &milestones)
{
bottomBarBgGradient = milestones;
}
void CurveEditor::refresh ()
{
subGroup->refresh(this);
}
void CurveEditor::setCurveColorProvider(ColorProvider* cp, int callerId)
{
curveCP = cp;
curveCId = callerId;
}
ColorProvider* CurveEditor::getLeftBarColorProvider()
{
return leftBarCP;
}
ColorProvider* CurveEditor::getBottomBarColorProvider()
{
return bottomBarCP;
}
ColorProvider* CurveEditor::getCurveColorProvider()
{
return curveCP;
}
int CurveEditor::getLeftBarCallerId()
{
return leftBarCId;
}
int CurveEditor::getBottomBarCallerId()
{
return bottomBarCId;
}
int CurveEditor::getCurveCallerId()
{
return curveCId;
}
std::vector<GradientMilestone> CurveEditor::getBottomBarBgGradient () const
{
return bottomBarBgGradient;
}
std::vector<GradientMilestone> CurveEditor::getLeftBarBgGradient () const
{
return leftBarBgGradient;
}
sigc::signal<void> CurveEditor::signal_curvegraph_enter()
{
return sig_curvegraph_enter;
}
sigc::signal<void> CurveEditor::signal_curvegraph_leave()
{
return sig_curvegraph_leave;
}
sigc::signal<void> CurveEditor::signal_curvepoint_click()
{
return sig_curvepoint_click;
}
sigc::signal<void> CurveEditor::signal_curvepoint_release()
{
return sig_curvepoint_release;
}
void CurveEditor::switchOffEditMode ()
{
if (EditSubscriber::getEditID() != EUID_None) {
// switching off the toggle button
if (group->displayedCurve == this) {
subGroup->editModeSwitchedOff();
}
}
EditSubscriber::switchOffEditMode(); // disconnect
}
bool CurveEditor::mouseOver(int modifierKey)
{
EditDataProvider* provider = getEditProvider();
subGroup->pipetteMouseOver(provider, modifierKey);
subGroup->refresh(this);
return true; // return true will ask the preview to be redrawn, for the cursor
}
bool CurveEditor::button1Pressed(int modifierKey)
{
EditDataProvider* provider = getEditProvider();
if (provider->getObject()) {
remoteDrag = subGroup->pipetteButton1Pressed(provider, modifierKey);
}
if (remoteDrag) {
action = EditSubscriber::Action::DRAGGING;
}
subGroup->refresh(this);
return true;
}
bool CurveEditor::button1Released()
{
EditDataProvider* provider = getEditProvider();
subGroup->pipetteButton1Released(provider);
remoteDrag = false;
subGroup->refresh(this);
return true;
}
bool CurveEditor::drag1(int modifierKey)
{
EditDataProvider* provider = getEditProvider();
subGroup->pipetteDrag(provider, modifierKey);
subGroup->refresh(this);
return false;
}
CursorShape CurveEditor::getCursor(int objectID, int xPos, int yPos) const
{
if (remoteDrag) {
return CSResizeHeight;
}
return CSHandOpen;
}