* 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
523 lines
13 KiB
C++
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;
|
|
}
|