rawTherapee/rtgui/vibrance.cc
Hombre 8b2eac9a3d Pipette and "On Preview Widgets" branch. See issue 227
The pipette part is already working quite nice but need to be finished. The widgets part needs more work...
2014-01-21 23:37:36 +01:00

390 lines
14 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.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "vibrance.h"
#include "../rtengine/color.h"
#include <iomanip>
using namespace rtengine;
using namespace rtengine::procparams;
Vibrance::Vibrance () : Gtk::VBox(), FoldableToolPanel(this) {
set_border_width(4);
std::vector<GradientMilestone> milestones;
float R, G, B;
// -0.1 rad < Hue < 1.6 rad
Color::hsv2rgb01(0.92f, 0.45f, 0.6f, R, G, B);
milestones.push_back( GradientMilestone(0.0, double(R), double(G), double(B)) );
Color::hsv2rgb01(0.14056f, 0.45f, 0.6f, R, G, B);
milestones.push_back( GradientMilestone(1.0, double(R), double(G), double(B)) );
enabled = Gtk::manage (new Gtk::CheckButton (M("GENERAL_ENABLED")));
enabled->set_active (false);
pack_start(*enabled, Gtk::PACK_SHRINK, 0);
pack_start (*Gtk::manage (new Gtk::HSeparator()));
saturated = Gtk::manage(new Adjuster (M("TP_VIBRANCE_SATURATED"),-100.,100.,1.,0.));
saturated->setAdjusterListener (this);
saturated->set_sensitive(false);
//if (saturated->delay < 1000) saturated->delay = 1000;
pack_start( *saturated, Gtk::PACK_SHRINK, 0);
pastels = Gtk::manage(new Adjuster (M("TP_VIBRANCE_PASTELS"),-100.,100.,1.,0.));
pastels->setAdjusterListener (this);
//if (pastels->delay < 1000) pastels->delay = 1000;
pack_start( *pastels, Gtk::PACK_SHRINK, 0);
psThreshold = Gtk::manage (new ThresholdAdjuster (M("TP_VIBRANCE_PSTHRESHOLD"), -100., 100., 0., M("TP_VIBRANCE_PSTHRESHOLD_WEIGTHING"), 0, 0., 100., 75., M("TP_VIBRANCE_PSTHRESHOLD_SATTHRESH"), 0, this, false));
psThreshold->setAdjusterListener (this);
psThreshold->set_tooltip_markup(M("TP_VIBRANCE_PSTHRESHOLD_TOOLTIP"));
psThreshold->set_sensitive(false);
//if (psThreshold->delay < 1000) psThreshold->delay = 1000;
pack_start( *psThreshold, Gtk::PACK_SHRINK, 0);
protectSkins = Gtk::manage (new Gtk::CheckButton (M("TP_VIBRANCE_PROTECTSKINS")));
protectSkins->set_active (true);
pack_start(*protectSkins, Gtk::PACK_SHRINK, 0);
avoidColorShift = Gtk::manage (new Gtk::CheckButton (M("TP_VIBRANCE_AVOIDCOLORSHIFT")));
avoidColorShift->set_active (true);
pack_start(*avoidColorShift, Gtk::PACK_SHRINK, 0);
pastSatTog = Gtk::manage (new Gtk::CheckButton (M("TP_VIBRANCE_PASTSATTOG")));
pastSatTog->set_active (true);
pack_start(*pastSatTog, Gtk::PACK_SHRINK, 0);
curveEditorGG = new CurveEditorGroup (options.lastVibranceCurvesDir, M("TP_VIBRANCE_CURVEEDITOR_SKINTONES_LABEL"));
curveEditorGG->setCurveListener (this);
skinTonesCurve = static_cast<DiagonalCurveEditor*>(curveEditorGG->addCurve(CT_Diagonal, M("TP_VIBRANCE_CURVEEDITOR_SKINTONES")));
skinTonesCurve->setTooltip(M("TP_VIBRANCE_CURVEEDITOR_SKINTONES_TOOLTIP"));
skinTonesCurve->setBottomBarBgGradient(milestones);
skinTonesCurve->setLeftBarBgGradient(milestones);
skinTonesCurve->setRangeLabels(
M("TP_VIBRANCE_CURVEEDITOR_SKINTONES_RANGE1"), M("TP_VIBRANCE_CURVEEDITOR_SKINTONES_RANGE2"),
M("TP_VIBRANCE_CURVEEDITOR_SKINTONES_RANGE3"), M("TP_VIBRANCE_CURVEEDITOR_SKINTONES_RANGE4")
);
skinTonesCurve->setRangeDefaultMilestones(0.1, 0.4, 0.85);
curveEditorGG->curveListComplete();
pack_start (*curveEditorGG, Gtk::PACK_SHRINK, 4);
show ();
enaconn = enabled->signal_toggled().connect( sigc::mem_fun(*this, &Vibrance::enabled_toggled) );
pskinsconn = protectSkins->signal_toggled().connect( sigc::mem_fun(*this, &Vibrance::protectskins_toggled) );
ashiftconn = avoidColorShift->signal_toggled().connect( sigc::mem_fun(*this, &Vibrance::avoidcolorshift_toggled) );
pastsattogconn = pastSatTog->signal_toggled().connect( sigc::mem_fun(*this, &Vibrance::pastsattog_toggled) );
}
Vibrance::~Vibrance () {
delete curveEditorGG;
}
void Vibrance::read(const ProcParams* pp, const ParamsEdited* pedited) {
disableListener ();
if(pedited ){
enabled->set_inconsistent (!pedited->vibrance.enabled);
pastels->setEditedState (pedited->vibrance.pastels ? Edited : UnEdited);
saturated->setEditedState (pedited->vibrance.saturated ? Edited : UnEdited);
psThreshold->setEditedState (pedited->vibrance.psthreshold ? Edited : UnEdited);
protectSkins->set_inconsistent (!pedited->vibrance.protectskins);
avoidColorShift->set_inconsistent (!pedited->vibrance.avoidcolorshift);
pastSatTog->set_inconsistent (!pedited->vibrance.pastsattog);
skinTonesCurve->setUnChanged (!pedited->vibrance.skintonescurve);
}
enaconn.block (true);
enabled->set_active (pp->vibrance.enabled);
enaconn.block (false);
lastEnabled = pp->vibrance.enabled;
pskinsconn.block (true);
protectSkins->set_active (pp->vibrance.protectskins);
pskinsconn.block (false);
lastProtectSkins = pp->vibrance.protectskins;
ashiftconn.block (true);
avoidColorShift->set_active (pp->vibrance.avoidcolorshift);
ashiftconn.block (false);
lastAvoidColorShift = pp->vibrance.avoidcolorshift;
pastsattogconn.block (true);
pastSatTog->set_active (pp->vibrance.pastsattog);
pastsattogconn.block (false);
lastPastSatTog = pp->vibrance.pastsattog;
pastels->setValue (pp->vibrance.pastels);
psThreshold->setValue<int> (pp->vibrance.psthreshold);
if (lastPastSatTog) {
// Link both slider, so we set saturated and psThresholds unsensitive
psThreshold->set_sensitive(false);
saturated->set_sensitive(false);
saturated->setValue (pp->vibrance.pastels); // Pastels and Saturated are linked
}
else {
// Separate sliders, so we set saturated and psThresholds sensitive again
psThreshold->set_sensitive(true);
saturated->set_sensitive(true);
saturated->setValue (pp->vibrance.saturated); // Pastels and Saturated are separate
}
skinTonesCurve->setCurve (pp->vibrance.skintonescurve);
enableListener ();
}
void Vibrance::autoOpenCurve () {
skinTonesCurve->openIfNonlinear();
}
void Vibrance::write( ProcParams* pp, ParamsEdited* pedited) {
pp->vibrance.enabled = enabled->get_active ();
pp->vibrance.pastels = pastels->getIntValue();
pp->vibrance.saturated = pastSatTog->get_active() ? pp->vibrance.pastels : saturated->getIntValue ();
pp->vibrance.psthreshold = psThreshold->getValue<int> ();
pp->vibrance.protectskins = protectSkins->get_active ();
pp->vibrance.avoidcolorshift = avoidColorShift->get_active ();
pp->vibrance.pastsattog = pastSatTog->get_active ();
pp->vibrance.skintonescurve = skinTonesCurve->getCurve ();
if (pedited) {
pedited->vibrance.enabled = !enabled->get_inconsistent();
pedited->vibrance.pastels = pastels->getEditedState ();
pedited->vibrance.saturated = saturated->getEditedState ();
pedited->vibrance.psthreshold = psThreshold->getEditedState ();
pedited->vibrance.protectskins = !protectSkins->get_inconsistent();
pedited->vibrance.avoidcolorshift = !avoidColorShift->get_inconsistent();
pedited->vibrance.pastsattog = !pastSatTog->get_inconsistent();
pedited->vibrance.skintonescurve = !skinTonesCurve->isUnChanged ();
}
}
void Vibrance::curveChanged () {
if (listener && enabled->get_active()) listener->panelChanged (EvVibranceSkinTonesCurve, M("HISTORY_CUSTOMCURVE"));
}
void Vibrance::enabled_toggled () {
if (batchMode) {
if (enabled->get_inconsistent()) {
enabled->set_inconsistent (false);
enaconn.block (true);
enabled->set_active (false);
enaconn.block (false);
}
else if (lastEnabled)
enabled->set_inconsistent (true);
lastEnabled = enabled->get_active ();
}
if (listener) {
if (enabled->get_active ())
listener->panelChanged (EvVibranceEnabled, M("GENERAL_ENABLED"));
else
listener->panelChanged (EvVibranceEnabled, M("GENERAL_DISABLED"));
}
}
void Vibrance::protectskins_toggled () {
if (batchMode) {
if (protectSkins->get_inconsistent()) {
protectSkins->set_inconsistent (false);
pskinsconn.block (true);
protectSkins->set_active (false);
pskinsconn.block (false);
}
else if (lastProtectSkins)
protectSkins->set_inconsistent (true);
lastProtectSkins = protectSkins->get_active ();
}
if (listener && enabled->get_active()) {
if (protectSkins->get_active ())
listener->panelChanged (EvVibranceProtectSkins, M("GENERAL_ENABLED"));
else
listener->panelChanged (EvVibranceProtectSkins, M("GENERAL_DISABLED"));
}
}
void Vibrance::avoidcolorshift_toggled () {
if (batchMode) {
if (avoidColorShift->get_inconsistent()) {
avoidColorShift->set_inconsistent (false);
ashiftconn.block (true);
avoidColorShift->set_active (false);
ashiftconn.block (false);
}
else if (lastAvoidColorShift)
avoidColorShift->set_inconsistent (true);
lastAvoidColorShift = avoidColorShift->get_active ();
}
if (listener && enabled->get_active()) {
if (avoidColorShift->get_active ())
listener->panelChanged (EvVibranceAvoidColorShift, M("GENERAL_ENABLED"));
else
listener->panelChanged (EvVibranceAvoidColorShift, M("GENERAL_DISABLED"));
}
}
void Vibrance::pastsattog_toggled () {
if (batchMode) {
if (pastSatTog->get_inconsistent()) {
pastSatTog->set_inconsistent (false);
pastsattogconn.block (true);
pastSatTog->set_active (false);
pastsattogconn.block (false);
}
else if (lastPastSatTog)
pastSatTog->set_inconsistent (true);
lastPastSatTog = pastSatTog->get_active ();
}
if (pastSatTog->get_active()) {
// Link both slider, so we set saturated and psThresholds unsensitive
psThreshold->set_sensitive(false);
saturated->set_sensitive(false);
saturated->setValue (pastels->getValue()); // Pastels and Saturated are linked
}
else {
// Separate sliders, so we set saturated and psThresholds sensitive again
psThreshold->set_sensitive(true);
saturated->set_sensitive(true);
}
if (listener && enabled->get_active()) {
if (pastSatTog->get_active ()) {
listener->panelChanged (EvVibrancePastSatTog, M("GENERAL_ENABLED"));
}
else
listener->panelChanged (EvVibrancePastSatTog, M("GENERAL_DISABLED"));
}
}
void Vibrance::adjusterChanged (Adjuster* a, double newval) {
if (a == pastels && pastSatTog->get_active())
saturated->setValue (newval);
if (listener && enabled->get_active()) {
Glib::ustring value = a->getTextValue();
if (a == pastels )
listener->panelChanged (EvVibrancePastels, value );
else if (a == saturated && !pastSatTog->get_active())
listener->panelChanged (EvVibranceSaturated, value );
}
}
void Vibrance::adjusterChanged (ThresholdAdjuster* a, int newBottom, int newTop) {
if (listener && enabled->get_active()) {
listener->panelChanged (EvVibrancePastSatThreshold, psThreshold->getHistoryString());
}
}
void Vibrance::setBatchMode(bool batchMode) {
ToolPanel::setBatchMode (batchMode);
pastels->showEditedCB ();
saturated->showEditedCB ();
psThreshold->showEditedCB ();
curveEditorGG->setBatchMode (batchMode);
}
void Vibrance::setDefaults(const ProcParams* defParams, const ParamsEdited* pedited) {
pastels->setDefault (defParams->vibrance.pastels);
saturated->setDefault (defParams->vibrance.saturated);
psThreshold->setDefault<int> (defParams->vibrance.psthreshold);
if (pedited) {
pastels->setDefaultEditedState (pedited->vibrance.pastels ? Edited : UnEdited);
saturated->setDefaultEditedState (pedited->vibrance.saturated ? Edited : UnEdited);
psThreshold->setDefaultEditedState (pedited->vibrance.psthreshold ? Edited : UnEdited);
} else {
pastels->setDefaultEditedState (Irrelevant);
saturated->setDefaultEditedState (Irrelevant);
psThreshold->setDefaultEditedState (Irrelevant);
}
}
void Vibrance::setAdjusterBehavior (bool pastelsadd, bool saturatedadd) {
pastels->setAddMode (pastelsadd);
saturated->setAddMode (saturatedadd);
}
void Vibrance::trimValues (ProcParams* pp) {
pastels->trimValue (pp->vibrance.pastels);
saturated->trimValue (pp->vibrance.saturated);
}
std::vector<double> Vibrance::getCurvePoints(ThresholdSelector* tAdjuster) const {
std::vector<double> points;
double threshold, transitionWeighting;
tAdjuster->getPositions<double>(transitionWeighting, threshold); // ( range -100;+100, range 0;+100 )
transitionWeighting /= 100.; // range -1., +1.
threshold /= 100.; // range 0., +1.
// Initial point
points.push_back(0.); points.push_back(0.);
double p2 = 3.0*threshold/4.0; // same one than in ipvibrance.cc
double s0 = threshold + (1.0-threshold)/4.0; // same one than in ipvibrance.cc
// point at the beginning of the first linear transition
points.push_back(p2); points.push_back(0.);
// Y value of the chroma mean point, calculated to get a straight line between p2 and s0
double chromaMean = (threshold/4.0) / (s0-p2);
// move chromaMean up or down depending on transitionWeighting
if (transitionWeighting > 0.0) {
// positive values -> give more weight to Saturated
chromaMean = (1.0-chromaMean) * transitionWeighting + chromaMean;
}
else if (transitionWeighting < 0.0) {
// negative values -> give more weight to Pastels
chromaMean = chromaMean * transitionWeighting + chromaMean;
}
// point at the location of the Top cursor, at the end of the first linear transition and the beginning of the second one
points.push_back(threshold); points.push_back(chromaMean);
if (threshold < 1.0) {
// point at the end of the second linear transition
points.push_back(s0); points.push_back(1.0);
// end point
points.push_back(1.0); points.push_back(1.0);
}
return points;
}