502 lines
13 KiB
C++
502 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) : CurveEditor::CurveEditor(text, static_cast<CurveEditorGroup*>(ceGroup), ceSubGroup)
|
|
{
|
|
|
|
curveType->addEntry("curve-linear-small.png", M("CURVEEDITOR_LINEAR")); // 0 Linear
|
|
curveType->addEntry("curve-spline-small.png", M("CURVEEDITOR_CUSTOM")); // 1 Spline
|
|
curveType->addEntry("curve-catmullrom-small.png", M("CURVEEDITOR_CATMULLROM")); // 4 CatmullRom
|
|
curveType->addEntry("curve-parametric-small.png", M("CURVEEDITOR_PARAMETRIC")); // 2 Parametric
|
|
curveType->addEntry("curve-nurbs-small.png", M("CURVEEDITOR_NURBS")); // 3 NURBS
|
|
static_cast<CurveTypePopUpButton *>(curveType)->setPosIndexMap({ 0, 1, 4, 2, 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.png", M("CURVEEDITOR_LINEAR")); // 0 Linear
|
|
curveType->addEntry("curve-controlpoints-small.png", 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;
|
|
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);
|
|
}
|
|
|
|
// 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;
|
|
}
|