/* * 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 . */ #include "vibrance.h" #include "curveeditor.h" #include "curveeditorgroup.h" #include "options.h" #include "../rtengine/color.h" using namespace rtengine; using namespace rtengine::procparams; const Glib::ustring Vibrance::TOOL_NAME = "vibrance"; Vibrance::Vibrance () : FoldableToolPanel(this, TOOL_NAME, M("TP_VIBRANCE_LABEL"), false, true) { std::vector 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)) ); saturated = Gtk::manage(new Adjuster (M("TP_VIBRANCE_SATURATED"), -100., 100., 1., 0.)); saturated->setAdjusterListener (this); saturated->set_sensitive(false); pack_start( *saturated, Gtk::PACK_SHRINK, 0); pastels = Gtk::manage(new Adjuster (M("TP_VIBRANCE_PASTELS"), -100., 100., 1., 0.)); pastels->setAdjusterListener (this); 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); 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(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); 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 ) { set_inconsistent (multiImage && !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); } setEnabled (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 (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 = getEnabled (); pp->vibrance.pastels = pastels->getIntValue(); pp->vibrance.saturated = pastSatTog->get_active() ? pp->vibrance.pastels : saturated->getIntValue (); pp->vibrance.psthreshold = psThreshold->getValue (); 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 = !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 && getEnabled ()) { listener->panelChanged (EvVibranceSkinTonesCurve, M("HISTORY_CUSTOMCURVE")); } } void Vibrance::enabledChanged () { if (listener) { if (get_inconsistent()) { listener->panelChanged (EvVibranceEnabled, M("GENERAL_UNCHANGED")); } if (getEnabled()) { 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 && getEnabled()) { 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 && getEnabled()) { 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 && getEnabled()) { 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 && getEnabled()) { 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, double newBottom, double newTop) { } void Vibrance::adjusterChanged(ThresholdAdjuster* a, double newBottomLeft, double newTopLeft, double newBottomRight, double newTopRight) { } void Vibrance::adjusterChanged(ThresholdAdjuster* a, int newBottom, int newTop) { if (listener && getEnabled()) { listener->panelChanged (EvVibrancePastSatThreshold, psThreshold->getHistoryString()); } } void Vibrance::adjusterChanged(ThresholdAdjuster* a, int newBottomLeft, int newTopLeft, int newBottomRight, int newTopRight) { } void Vibrance::adjusterChanged2(ThresholdAdjuster* a, int newBottomL, int newTopL, int newBottomR, int newTopR) { } 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 (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 Vibrance::getCurvePoints(ThresholdSelector* tAdjuster) const { std::vector points; double threshold, transitionWeighting; tAdjuster->getPositions(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; }