Patch from issue 1267: "New Threshold curve widget", used in USM Sharpening and Vibrance

This commit is contained in:
natureh
2012-06-19 23:01:25 +02:00
parent b5176679b8
commit a9286155e8
49 changed files with 5685 additions and 4202 deletions

View File

@@ -3,7 +3,7 @@ set (BASESOURCEFILES
editwindow.cc batchtoolpanelcoord.cc paramsedited.cc cropwindow.cc previewhandler.cc previewwindow.cc navigator.cc indclippedpanel.cc previewmodepanel.cc filterpanel.cc
exportpanel.cc cursormanager.cc rtwindow.cc renamedlg.cc recentbrowser.cc placesbrowser.cc filepanel.cc editorpanel.cc batchqueuepanel.cc
ilabel.cc thumbbrowserbase.cc adjuster.cc filebrowserentry.cc filebrowser.cc filethumbnailbuttonset.cc
cachemanager.cc cacheimagedata.cc shcselector.cc perspective.cc
cachemanager.cc cacheimagedata.cc shcselector.cc perspective.cc thresholdselector.cc thresholdadjuster.cc
clipboard.cc thumbimageupdater.cc bqentryupdater.cc lensgeom.cc
coarsepanel.cc cacorrection.cc hlrec.cc chmixer.cc
resize.cc icmpanel.cc crop.cc shadowshighlights.cc

View File

@@ -49,11 +49,10 @@
#define ADDSET_SHARPENMICRO_UNIFORMITY 39
#define ADDSET_VIBRANCE_PASTELS 40
#define ADDSET_VIBRANCE_SATURATED 41
#define ADDSET_VIBRANCE_PSTHRESHOLD 42
#define ADDSET_FREE_OUPUT_GAMMA 43
#define ADDSET_FREE_OUTPUT_SLOPE 44
#define ADDSET_FREE_OUPUT_GAMMA 42
#define ADDSET_FREE_OUTPUT_SLOPE 43
// When adding items, make sure to update ADDSET_PARAM_NUM
#define ADDSET_PARAM_NUM 45 // THIS IS USED AS A DELIMITER!!
#define ADDSET_PARAM_NUM 44 // THIS IS USED AS A DELIMITER!!
#endif

View File

@@ -243,11 +243,6 @@ void Adjuster::setAddMode(bool addM) {
}
}
void Adjuster::setAdjusterListener (AdjusterListener* alistener) {
adjusterListener = alistener;
}
void Adjuster::spinChanged () {
sliderChange.block (true);
@@ -312,24 +307,6 @@ void Adjuster::setValue (double a) {
afterReset = false;
}
// return the value trimmed to the limits at construction time
double Adjuster::getValue () {
return spin->get_value ();
}
// return the value trimmed to the limits at construction time
int Adjuster::getIntValue () {
return spin->get_value_as_int ();
}
// method only used by the history manager
Glib::ustring Adjuster::getTextValue () {
return spin->get_text ();
}
bool Adjuster::notifyListener () {
if (adjusterListener!=NULL && !blocked) {

View File

@@ -68,11 +68,16 @@ class Adjuster : public Gtk::VBox {
Adjuster (Glib::ustring label, double vmin, double vmax, double vstep, double vdefault, bool editedCheckBox=false);
Adjuster (Gtk::Image *imgIcon, double vmin, double vmax, double vstep, double vdefault, bool editedCheckBox=false);
virtual ~Adjuster ();
void setAdjusterListener (AdjusterListener* alistener);
void setAdjusterListener (AdjusterListener* alistener) { adjusterListener = alistener; }
// return the value trimmed to the limits at construction time
double getValue () { return spin->get_value (); }
// return the value trimmed to the limits at construction time
int getIntValue () { return spin->get_value_as_int (); }
// return the value trimmed to the limits at construction time,
// method only used by the history manager
Glib::ustring getTextValue () { return spin->get_text (); }
double getValue ();
int getIntValue ();
Glib::ustring getTextValue ();
void setValue (double a);
void setLimits (double vmin, double vmax, double vstep, double vdefault);
void setEnabled (bool enabled);

View File

@@ -147,7 +147,6 @@ void BatchToolPanelCoordinator::initSession () {
toneCurve->setAdjusterBehavior (options.baBehav[ADDSET_TC_EXPCOMP], options.baBehav[ADDSET_TC_HLCOMPAMOUNT],options.baBehav[ADDSET_TC_HLCOMPTHRESH], options.baBehav[ADDSET_TC_BRIGHTNESS], options.baBehav[ADDSET_TC_BLACKLEVEL],options.baBehav[ADDSET_TC_SHCOMP], options.baBehav[ADDSET_TC_CONTRAST], options.baBehav[ADDSET_TC_SATURATION]);
lcurve->setAdjusterBehavior (options.baBehav[ADDSET_LC_BRIGHTNESS], options.baBehav[ADDSET_LC_CONTRAST], options.baBehav[ADDSET_LC_SATURATION]);
whitebalance->setAdjusterBehavior (options.baBehav[ADDSET_WB_TEMPERATURE], options.baBehav[ADDSET_WB_GREEN]);
vibrance->setAdjusterBehavior (options.baBehav[ADDSET_VIBRANCE_PASTELS], options.baBehav[ADDSET_VIBRANCE_SATURATED], options.baBehav[ADDSET_VIBRANCE_PSTHRESHOLD]);
vignetting->setAdjusterBehavior (options.baBehav[ADDSET_VIGN_AMOUNT]);
rotate->setAdjusterBehavior (options.baBehav[ADDSET_ROTATE_DEGREE]);
distortion->setAdjusterBehavior (options.baBehav[ADDSET_DIST_AMOUNT]);

View File

@@ -196,4 +196,20 @@ public:
void switchTo(TOITypes type);
};
/**
* @brief Define a gradient milestone
*/
class GradientMilestone {
public:
double position;
double r;
double g;
double b;
double a;
GradientMilestone(double _p=0., double _r=0., double _g=0., double _b=0., double _a=0.) {
position = _p; r = _r; g = _g; b = _b; a = _a;
}
};
#endif

View File

@@ -36,7 +36,9 @@
#include <Shlobj.h>
#endif
// User's settings directory, including images' profiles if used
Glib::ustring Options::rtdir;
// User's cached datas' directory
Glib::ustring Options::cacheBaseDir;
Options options;
@@ -385,9 +387,8 @@ void Options::setDefaults () {
0, // ADDSET_SHARPENMICRO_UNIFORMITY
0, // ADDSET_VIBRANCE_PASTELS
0, // ADDSET_VIBRANCE_SATURATED
0, // ADDSET_VIBRANCE_PSTHRESHOLD
0, // ADDSET_FREE_OUPUT_GAMMA
0, // ADDSET_FREE_OUTPUT_SLOPE
0, // ADDSET_FREE_OUPUT_GAMMA
0, // ADDSET_FREE_OUTPUT_SLOPE
};
baBehav = std::vector<int> (babehav, babehav+ADDSET_PARAM_NUM);

View File

@@ -454,7 +454,15 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten
if (sharpening.enabled) toEdit.sharpening.enabled = mods.sharpening.enabled;
if (sharpening.radius) toEdit.sharpening.radius = mods.sharpening.radius;
if (sharpening.amount) toEdit.sharpening.amount = dontforceSet && options.baBehav[ADDSET_SHARP_AMOUNT] ? toEdit.sharpening.amount + mods.sharpening.amount : mods.sharpening.amount;
if (sharpening.threshold) toEdit.sharpening.threshold = mods.sharpening.threshold;
if (sharpening.threshold) toEdit.sharpening.threshold = mods.sharpening.threshold;
for (int i=0; i<3; i++) {
if (chmixer.red[i]) toEdit.chmixer.red[i] = dontforceSet && options.baBehav[ADDSET_CHMIXER] ? toEdit.chmixer.red[i] + mods.chmixer.red[i] : mods.chmixer.red[i];
if (chmixer.green[i]) toEdit.chmixer.green[i] = dontforceSet && options.baBehav[ADDSET_CHMIXER] ? toEdit.chmixer.green[i] + mods.chmixer.green[i] : mods.chmixer.green[i];
if (chmixer.blue[i]) toEdit.chmixer.blue[i] = dontforceSet && options.baBehav[ADDSET_CHMIXER] ? toEdit.chmixer.blue[i] + mods.chmixer.blue[i] : mods.chmixer.blue[i];
}
if (sharpening.edgesonly) toEdit.sharpening.edgesonly = mods.sharpening.edgesonly;
if (sharpening.edges_radius) toEdit.sharpening.edges_radius = mods.sharpening.edges_radius;
if (sharpening.edges_tolerance) toEdit.sharpening.edges_tolerance = mods.sharpening.edges_tolerance;
@@ -468,7 +476,7 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten
if (vibrance.enabled) toEdit.vibrance.enabled = mods.vibrance.enabled;
if (vibrance.pastels) toEdit.vibrance.pastels = dontforceSet && options.baBehav[ADDSET_VIBRANCE_PASTELS] ? toEdit.vibrance.pastels + mods.vibrance.pastels : mods.vibrance.pastels;
if (vibrance.saturated) toEdit.vibrance.saturated = dontforceSet && options.baBehav[ADDSET_VIBRANCE_SATURATED] ? toEdit.vibrance.saturated + mods.vibrance.saturated : mods.vibrance.saturated;
if (vibrance.psthreshold) toEdit.vibrance.psthreshold = dontforceSet && options.baBehav[ADDSET_VIBRANCE_PSTHRESHOLD] ? toEdit.vibrance.psthreshold + mods.vibrance.psthreshold : mods.vibrance.psthreshold;
if (vibrance.psthreshold) toEdit.vibrance.psthreshold = mods.vibrance.psthreshold;
if (vibrance.protectskins) toEdit.vibrance.protectskins = mods.vibrance.protectskins;
if (vibrance.avoidcolorshift) toEdit.vibrance.avoidcolorshift = mods.vibrance.avoidcolorshift;
if (vibrance.pastsattog) toEdit.vibrance.pastsattog = mods.vibrance.pastsattog;

View File

@@ -2,7 +2,7 @@
#define _PPVERSION_
// This number have to be incremented whenever the PP3 file format is modified
#define PPVERSION 301
#define PPVERSION 302
#define PPVERSION_AEXP 301 //value of PPVERSION when auto exposure algorithm was modified
#endif

View File

@@ -195,7 +195,6 @@ Gtk::Widget* Preferences::getBatchProcPanel () {
mi->set_value (behavColumns.label, M("TP_VIBRANCE_LABEL"));
appendBehavList (mi, M("TP_VIBRANCE_PASTELS"), ADDSET_VIBRANCE_PASTELS, false);
appendBehavList (mi, M("TP_VIBRANCE_SATURATED"), ADDSET_VIBRANCE_SATURATED, false);
appendBehavList (mi, M("TP_VIBRANCE_PSTHRESHOLD"), ADDSET_VIBRANCE_PSTHRESHOLD, false);
mi = behModel->append ();
mi->set_value (behavColumns.label, M("TP_GAMMA_OUTPUT"));

View File

@@ -26,6 +26,10 @@ using namespace rtengine::procparams;
Sharpening::Sharpening () : Gtk::VBox(), FoldableToolPanel(this) {
std::vector<GradientMilestone> milestones;
milestones.push_back( GradientMilestone(0.0, 0.0, 0.0, 0.0) );
milestones.push_back( GradientMilestone(1.0, 1.0, 1.0, 1.0) );
enabled = Gtk::manage (new Gtk::CheckButton (M("GENERAL_ENABLED")));
enabled->set_active (true);
pack_start(*enabled);
@@ -69,7 +73,8 @@ Sharpening::Sharpening () : Gtk::VBox(), FoldableToolPanel(this) {
Gtk::HSeparator *hsep6a = Gtk::manage (new Gtk::HSeparator());
amount = Gtk::manage (new Adjuster (M("TP_SHARPENING_AMOUNT"), 1, 1000, 1, 150));
radius = Gtk::manage (new Adjuster (M("TP_SHARPENING_RADIUS"), 0.3, 3, 0.01, 0.8));
threshold = Gtk::manage (new Adjuster (M("TP_SHARPENING_THRESHOLD"), 0, 16384, 50, 1));
threshold = Gtk::manage (new ThresholdAdjuster (M("TP_SHARPENING_THRESHOLD"), 0., 2000., 20., 80., 2000., 1200., 0, false));
threshold->setBgGradient(milestones);
pack_start(*hsep6a, Gtk::PACK_SHRINK, 2);
pack_start (*usm);
@@ -186,7 +191,7 @@ void Sharpening::read (const ProcParams* pp, const ParamsEdited* pedited) {
amount->setValue (pp->sharpening.amount);
radius->setValue (pp->sharpening.radius);
threshold->setValue (pp->sharpening.threshold);
threshold->setValue<int>(pp->sharpening.threshold);
eradius->setValue (pp->sharpening.edges_radius);
etolerance->setValue (pp->sharpening.edges_tolerance);
hcamount->setValue (pp->sharpening.halocontrol_amount);
@@ -221,7 +226,7 @@ void Sharpening::write (ProcParams* pp, ParamsEdited* pedited) {
pp->sharpening.amount = (int)amount->getValue();
pp->sharpening.enabled = enabled->get_active ();
pp->sharpening.radius = radius->getValue ();
pp->sharpening.threshold = (int)threshold->getValue ();
pp->sharpening.threshold = threshold->getValue<int> ();
pp->sharpening.edgesonly = edgesonly->get_active ();
pp->sharpening.edges_radius = eradius->getValue ();
pp->sharpening.edges_tolerance = (int)etolerance->getValue ();
@@ -259,7 +264,7 @@ void Sharpening::setDefaults (const ProcParams* defParams, const ParamsEdited* p
amount->setDefault (defParams->sharpening.amount);
radius->setDefault (defParams->sharpening.radius);
threshold->setDefault (defParams->sharpening.threshold);
threshold->setDefault<int> (defParams->sharpening.threshold);
eradius->setDefault (defParams->sharpening.edges_radius);
etolerance->setDefault (defParams->sharpening.edges_tolerance);
hcamount->setDefault (defParams->sharpening.halocontrol_amount);
@@ -310,8 +315,6 @@ void Sharpening::adjusterChanged (Adjuster* a, double newval) {
listener->panelChanged (EvShrAmount, costr);
else if (a==radius)
listener->panelChanged (EvShrRadius, costr);
else if (a==threshold)
listener->panelChanged (EvShrThresh, costr);
else if (a==eradius)
listener->panelChanged (EvShrEdgeRadius, costr);
else if (a==etolerance)
@@ -329,6 +332,13 @@ void Sharpening::adjusterChanged (Adjuster* a, double newval) {
}
}
//void Sharpening::adjusterChanged (ThresholdAdjuster* a, int newBottomLeft, int newTopLeft, int newBottomRight, int newTopRight) {
void Sharpening::adjusterChanged (ThresholdAdjuster* a, int newBottomLeft, int newTopLeft, int newBottomRight, int newTopRight) {
if (listener && enabled->get_active()) {
listener->panelChanged (EvShrThresh, threshold->getHistoryString());
}
}
void Sharpening::enabled_toggled () {
if (batchMode) {

View File

@@ -21,10 +21,10 @@
#include <gtkmm.h>
#include "adjuster.h"
#include "guiutils.h"
#include "thresholdadjuster.h"
#include "toolpanel.h"
class Sharpening : public Gtk::VBox, public AdjusterListener, public FoldableToolPanel {
class Sharpening : public Gtk::VBox, public ThresholdAdjusterListener, public AdjusterListener, public FoldableToolPanel {
protected:
MyComboBoxText* method;
@@ -37,7 +37,6 @@ class Sharpening : public Gtk::VBox, public AdjusterListener, public FoldableToo
Adjuster* radius;
Adjuster* amount;
Adjuster* threshold;
Adjuster* eradius;
Adjuster* etolerance;
Adjuster* hcamount;
@@ -45,6 +44,7 @@ class Sharpening : public Gtk::VBox, public AdjusterListener, public FoldableToo
Gtk::VBox* hcbin;
Gtk::VBox* edgebox;
Gtk::VBox* hcbox;
ThresholdAdjuster* threshold;
Gtk::CheckButton* enabled;
bool lastEnabled;
sigc::connection enaConn;
@@ -68,6 +68,7 @@ class Sharpening : public Gtk::VBox, public AdjusterListener, public FoldableToo
void setBatchMode (bool batchMode);
void adjusterChanged (Adjuster* a, double newval);
void adjusterChanged (ThresholdAdjuster* a, int newBottomLeft, int newTopLeft, int newBottomRight, int newTopRight);
void enabled_toggled ();
void edgesonly_toggled ();
void halocontrol_toggled ();

301
rtgui/thresholdadjuster.cc Normal file
View File

@@ -0,0 +1,301 @@
/*
* 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 "thresholdadjuster.h"
#include <sigc++/class_slot.h>
#include <cmath>
#include "multilangmgr.h"
#include "../rtengine/rtengine.h"
#include "options.h"
#include "guiutils.h"
#include "rtimage.h"
#define MIN_RESET_BUTTON_HEIGHT 17
ThresholdAdjuster::ThresholdAdjuster (Glib::ustring label, double minValue, double maxValue, double defBottom,
double defTop, unsigned int precision, bool startAtOne, bool editedCheckBox)
: tSelector(minValue, maxValue, defBottom, defTop, precision, startAtOne)
{
initialDefaultVal[ThresholdSelector::TS_BOTTOMLEFT] = defBottom;
initialDefaultVal[ThresholdSelector::TS_TOPLEFT] = defTop;
initialDefaultVal[ThresholdSelector::TS_BOTTOMRIGHT] = maxValue;
initialDefaultVal[ThresholdSelector::TS_TOPRIGHT] = maxValue;
initObject (label, editedCheckBox);
}
ThresholdAdjuster::ThresholdAdjuster (Glib::ustring label, double minValue, double maxValue,
double defBottomLeft, double defTopLeft, double defBottomRight, double defTopRight,
unsigned int precision, bool startAtOne, bool editedCheckBox)
: tSelector(minValue, maxValue, defBottomLeft, defTopLeft,
defBottomRight, defTopRight, precision, startAtOne)
{
initialDefaultVal[ThresholdSelector::TS_BOTTOMLEFT] = defBottomLeft;
initialDefaultVal[ThresholdSelector::TS_TOPLEFT] = defTopLeft;
initialDefaultVal[ThresholdSelector::TS_BOTTOMRIGHT] = defBottomRight;
initialDefaultVal[ThresholdSelector::TS_TOPRIGHT] = defTopRight;
initObject (label, editedCheckBox);
}
void ThresholdAdjuster::initObject (Glib::ustring label, bool editedcb) {
adjusterListener = NULL;
afterReset = false;
blocked = false;
addMode = false;
// TODO: let the user chose the default value of ThresholdAdjuster::delay, for slow machines
delay = options.adjusterDelay; // delay is no more static, so we can set the delay individually (useful for the RAW editor tab)
set_border_width (2);
hbox = Gtk::manage (new Gtk::HBox ());
this->label = Gtk::manage (new Gtk::Label (label, Gtk::ALIGN_LEFT));
if (editedcb) {
editedCheckBox = Gtk::manage (new Gtk::CheckButton ());
editedChange = editedCheckBox->signal_toggled().connect( sigc::mem_fun(*this, &ThresholdAdjuster::editedToggled) );
hbox->pack_start (*editedCheckBox);
}
else
editedCheckBox = NULL;
hbox->pack_start (*this->label);
reset = Gtk::manage (new Gtk::Button ());
reset->add (*Gtk::manage (new RTImage ("gtk-undo-ltr-small.png", "gtk-undo-rtl-small.png")));
reset->set_relief (Gtk::RELIEF_NONE);
reset->set_border_width (0);
reset->set_tooltip_text (M("ADJUSTER_RESET_TO_DEFAULT"));
hbox->pack_end (*reset, Gtk::PACK_SHRINK, 0);
reset->set_size_request (-1, this->label->get_height() > MIN_RESET_BUTTON_HEIGHT ? this->label->get_height(): MIN_RESET_BUTTON_HEIGHT);
pack_start (*hbox, false, false);
pack_start (tSelector, false, false);
editedState = defEditedState = Irrelevant;
selectorChange = tSelector.signal_value_changed().connect( sigc::mem_fun(*this, &ThresholdAdjuster::selectorChanged) );
reset->signal_button_release_event().connect_notify( sigc::mem_fun(*this, &ThresholdAdjuster::resetPressed) );
show_all ();
}
ThresholdAdjuster::~ThresholdAdjuster () {
selectorChange.disconnect();
delayConnection.block(true);
adjusterListener = NULL;
}
void ThresholdAdjuster::setDefault (double bottom, double top) {
selectorChange.block (true);
tSelector.setPositions(shapeValue(bottom), shapeValue(top));
selectorChange.block (false);
}
void ThresholdAdjuster::setDefault (double bottomLeft, double topLeft, double bottomRight, double topRight) {
selectorChange.block (true);
tSelector.setPositions(shapeValue(bottomLeft), shapeValue(topLeft), shapeValue(bottomRight), shapeValue(topRight));
selectorChange.block (false);
}
void ThresholdAdjuster::setDefaultEditedState (EditedState eState) {
defEditedState = eState;
}
void ThresholdAdjuster::resetPressed (GdkEventButton* event) {
if (editedState!=Irrelevant) {
editedState = defEditedState;
if (editedCheckBox) {
editedChange.block (true);
editedCheckBox->set_active (defEditedState==Edited);
editedChange.block (false);
}
refreshLabelStyle ();
}
afterReset = true;
if ((event != NULL) && (event->state & GDK_CONTROL_MASK) && (event->button == 1))
// CTRL pressed : resetting to current default value
tSelector.reset();
else
// no modifier key or addMode=true : resetting to initial default value
tSelector.setPositions(initialDefaultVal[ThresholdSelector::TS_BOTTOMLEFT],
initialDefaultVal[ThresholdSelector::TS_TOPLEFT],
initialDefaultVal[ThresholdSelector::TS_BOTTOMRIGHT],
initialDefaultVal[ThresholdSelector::TS_TOPRIGHT]);
}
double ThresholdAdjuster::shapeValue (double a) {
unsigned int digit = tSelector.getPrecision();
return round(a*pow(double(10), digit)) / pow(double(10), digit);
}
void ThresholdAdjuster::selectorChanged () {
if (delayConnection.connected())
delayConnection.disconnect ();
if (delay==0) {
if (adjusterListener && !blocked)
sendToListener ();
}
else
delayConnection = Glib::signal_timeout().connect (sigc::mem_fun(*this, &ThresholdAdjuster::notifyListener), delay);
if (!afterReset && editedState==UnEdited) {
editedState = Edited;
if (editedCheckBox) {
editedChange.block (true);
editedCheckBox->set_active (true);
editedChange.block (false);
}
refreshLabelStyle ();
}
afterReset = false;
}
void ThresholdAdjuster::setValue (double bottom, double top) {
selectorChange.block (true);
tSelector.setPositions(bottom, top);
selectorChange.block (false);
afterReset = false;
}
void ThresholdAdjuster::setValue (double bottomLeft, double topLeft, double bottomRight, double topRight) {
selectorChange.block (true);
tSelector.setPositions(bottomLeft, topLeft, bottomRight, topRight);
selectorChange.block (false);
afterReset = false;
}
void ThresholdAdjuster::getValue (Glib::ustring& bottom, Glib::ustring& top) {
tSelector.getPositions (bottom, top);
}
void ThresholdAdjuster::getValue (Glib::ustring& bottomLeft, Glib::ustring& topLeft, Glib::ustring& bottomRight, Glib::ustring& topRight) {
tSelector.getPositions (bottomLeft, topLeft, bottomRight, topRight);
}
bool ThresholdAdjuster::notifyListener () {
if (adjusterListener!=NULL && !blocked) {
GThreadLock lock;
sendToListener();
}
return false;
}
void ThresholdAdjuster::setEnabled (bool enabled) {
tSelector.set_sensitive (enabled);
}
void ThresholdAdjuster::setEditedState (EditedState eState) {
if (editedState!=eState) {
if (editedCheckBox) {
editedChange.block (true);
editedCheckBox->set_active (eState==Edited);
editedChange.block (false);
}
editedState = eState;
refreshLabelStyle ();
}
}
EditedState ThresholdAdjuster::getEditedState () {
if (editedState!=Irrelevant && editedCheckBox)
editedState = editedCheckBox->get_active () ? Edited : UnEdited;
return editedState;
}
void ThresholdAdjuster::showEditedCB () {
if (!editedCheckBox) {
editedCheckBox = Gtk::manage(new Gtk::CheckButton ());
hbox->pack_start (*editedCheckBox, Gtk::PACK_SHRINK, 2);
hbox->reorder_child (*editedCheckBox, 0);
editedChange = editedCheckBox->signal_toggled().connect( sigc::mem_fun(*this, &ThresholdAdjuster::editedToggled) );
}
}
void ThresholdAdjuster::refreshLabelStyle () {
/* Glib::RefPtr<Gtk::Style> style = label->get_style ();
Pango::FontDescription fd = style->get_font ();
fd.set_weight (editedState==Edited ? Pango::WEIGHT_BOLD : Pango::WEIGHT_NORMAL);
style->set_font (fd);
label->set_style (style);
label->queue_draw ();*/
}
void ThresholdAdjuster::editedToggled () {
if (adjusterListener && !blocked)
sendToListener ();
}
void ThresholdAdjuster::sendToListener () {
if (tSelector.getPrecision() > 0) {
// if precision is >0, then we assume that the listener is waiting for doubles
rtengine::procparams::Threshold<double> t = tSelector.getPositions<double>();
if (tSelector.isDouble())
adjusterListener->adjusterChanged (this, t.value[0], t.value[1], t.value[2], t.value[3]);
else
adjusterListener->adjusterChanged (this, t.value[0], t.value[1]);
}
else {
// if precision is equal to 0, then we assume that the listener is waiting for integers
rtengine::procparams::Threshold<int> t = tSelector.getPositions<int>();
if (tSelector.isDouble())
adjusterListener->adjusterChanged (this, t.value[0], t.value[1], t.value[2], t.value[3]);
else
adjusterListener->adjusterChanged (this, t.value[0], t.value[1]);
}
}
/* For better readability, this method create the history string of the parameter column,
* so that the parameters list can be read in a more logical way (i.e. corresponding
* to the startAtOne field)
*/
Glib::ustring ThresholdAdjuster::getHistoryString () {
if (tSelector.isDouble()) {
Glib::ustring bl, tl, br, tr;
tSelector.getPositions(bl, tl, br, tr);
return Glib::ustring::compose(tSelector.isStartAtOne()?"%2, %1, %3, %4":"%1, %2, %4, %3", bl, tl, br, tr);
}
else {
Glib::ustring b, t;
tSelector.getPositions(b, t);
return Glib::ustring::compose(tSelector.isStartAtOne()?"%2, %1":"%1, %2", b, t);
}
}

119
rtgui/thresholdadjuster.h Normal file
View File

@@ -0,0 +1,119 @@
/*
* 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/>.
*/
#ifndef _THRESHOLDADJUSTER_H_
#define _THRESHOLDADJUSTER_H_
#include <gtkmm.h>
#include "editedstate.h"
#include "guiutils.h"
#include "thresholdselector.h"
class ThresholdAdjuster;
/*
* TODO: Maybe we could just send back the history string instead of the individual values?
*/
class ThresholdAdjusterListener {
public:
// to be used by listener that has created a ThresholdAdjuster with with single threshold and precision > 0
virtual void adjusterChanged (ThresholdAdjuster* a, double newBottom, double newTop) {}
// to be used by listener that has created a ThresholdAdjuster with with double threshold and precision > 0
virtual void adjusterChanged (ThresholdAdjuster* a, double newBottomLeft, double newTopLeft, double newBottomRight, double newTopRight) {}
// to be used by listener that has created a ThresholdAdjuster with with single threshold and precision == 0
virtual void adjusterChanged (ThresholdAdjuster* a, int newBottom, int newTop) {}
// to be used by listener that has created a ThresholdAdjuster with with double threshold and precision == 0
virtual void adjusterChanged (ThresholdAdjuster* a, int newBottomLeft, int newTopLeft, int newBottomRight, int newTopRight) {}
};
class ThresholdAdjuster : public Gtk::VBox {
protected:
Gtk::HBox* hbox;
Gtk::Label* label;
ThresholdSelector tSelector;
//MySpinButton* spin;
Gtk::Button* reset;
ThresholdAdjusterListener* adjusterListener;
sigc::connection delayConnection;
//sigc::connection spinChange;
sigc::connection selectorChange;
sigc::connection editedChange;
bool listenerReady;
double initialDefaultVal[4]; // default value at construction time
EditedState editedState;
EditedState defEditedState;
Gtk::CheckButton* editedCheckBox;
bool afterReset;
bool blocked;
bool addMode;
int delay;
double shapeValue (double a);
void refreshLabelStyle ();
void initObject (Glib::ustring label, bool editedcb);
void sendToListener ();
public:
ThresholdAdjuster (Glib::ustring label, double minValue, double maxValue, double defBottom,
double defTop, unsigned int precision, bool startAtOne, bool editedCheckBox=false);
ThresholdAdjuster (Glib::ustring label, double minValue, double maxValue, double defBottomLeft,
double defTopLeft, double defBottomRight, double defTopRight, unsigned int precision,
bool startAtOne, bool editedCheckBox=false);
virtual ~ThresholdAdjuster ();
void setAdjusterListener (ThresholdAdjusterListener* alistener) { adjusterListener = alistener; }
template <typename T>
rtengine::procparams::Threshold<T> getValue () { return tSelector.getPositions<T>(); }
void getValue (double& bottom, double& top);
void getValue (double& bottomLeft, double& topLeft, double& bottomRight, double& topRight);
void getValue (int& bottom, int& top);
void getValue (int& bottomLeft, int& topLeft, int& bottomRight, int& topRight);
void getValue (Glib::ustring& bottom, Glib::ustring& top);
void getValue (Glib::ustring& bottomLeft, Glib::ustring& topLeft, Glib::ustring& bottomRight, Glib::ustring& topRight);
template <class T>
void setValue (const rtengine::procparams::Threshold<T> &tValues) {
tSelector.setPositions<T>(tValues);
}
void setValue (double bottom, double top);
void setValue (double bottomLeft, double topLeft, double bottomRight, double topRight);
void setEnabled (bool enabled);
template <typename T>
void setDefault (const rtengine::procparams::Threshold<T> &tresh) { tSelector.setDefaults<T>(tresh); }
void setDefault (double defBottom, double defTop);
void setDefault (double defBottomLeft, double defTopLeft, double defBottomRight, double defTopRight);
void setEditedState (EditedState eState);
EditedState getEditedState ();
void setDefaultEditedState (EditedState eState);
void showEditedCB ();
void block(bool isBlocked) { blocked = isBlocked; }
void setBgGradient (const std::vector<GradientMilestone> &milestones) { tSelector.setBgGradient (milestones); }
//void spinChanged ();
void selectorChanged ();
bool notifyListener ();
void resetPressed (GdkEventButton* event);
void editedToggled ();
Glib::ustring getHistoryString ();
};
#endif

570
rtgui/thresholdselector.cc Normal file
View File

@@ -0,0 +1,570 @@
/*
* 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 "thresholdselector.h"
#include "multilangmgr.h"
#include <cassert>
#include <iomanip>
#include "mycurve.h"
ThresholdSelector::ThresholdSelector(double minValue, double maxValue, double defBottom, double defTop, unsigned int precision, bool startAtOne) {
positions[TS_BOTTOMLEFT] = defPos[TS_BOTTOMLEFT] = defBottom;
positions[TS_TOPLEFT] = defPos[TS_TOPLEFT] = defTop;
positions[TS_BOTTOMRIGHT] = defPos[TS_BOTTOMRIGHT] = maxValue;
positions[TS_TOPRIGHT] = defPos[TS_TOPRIGHT] = maxValue;
this->precision = precision;
doubleThresh = false;
#ifndef NDEBUG
if (startAtOne) {
assert (defBottom >= defTop);
assert (defTop >= minValue);
assert (defBottom <= maxValue);
}
else {
assert (defTop >= defBottom);
assert (defBottom >= minValue);
assert (defTop <= maxValue);
}
#endif
initValues (minValue, maxValue, startAtOne);
}
ThresholdSelector::ThresholdSelector(double minValue, double maxValue, double defBottomLeft, double defTopLeft, double defBottomRight, double defTopRight, unsigned int precision, bool startAtOne) {
positions[TS_BOTTOMLEFT] = defPos[TS_BOTTOMLEFT] = defBottomLeft;
positions[TS_TOPLEFT] = defPos[TS_TOPLEFT] = defTopLeft;
positions[TS_BOTTOMRIGHT] = defPos[TS_BOTTOMRIGHT] = defBottomRight;
positions[TS_TOPRIGHT] = defPos[TS_TOPRIGHT] = defTopRight;
this->precision = precision;
doubleThresh = true;
#ifndef NDEBUG
if (startAtOne) {
assert (minValue <= defTopLeft);
assert (defTopLeft <= defBottomLeft);
assert (defBottomLeft <= defBottomRight);
assert (defBottomRight <= defTopRight);
assert (defTopRight <= maxValue);
}
else {
assert (minValue <= defBottomLeft);
assert (defBottomLeft <= defTopLeft);
assert (defTopLeft <= defTopRight);
assert (defTopRight <= defBottomRight);
assert (defBottomRight <= maxValue);
}
#endif
initValues (minValue, maxValue, startAtOne);
}
void ThresholdSelector::initValues (double minValue, double maxValue, bool startAtOne) {
assert(minValue <= maxValue);
initalEq1 = startAtOne;
minVal = minValue;
maxVal = maxValue;
oldLitCursor = litCursor = TS_UNDEFINED;
movedCursor = TS_UNDEFINED;
secondaryMovedCursor = TS_UNDEFINED;
set_size_request (-1, 20);
add_events(Gdk::LEAVE_NOTIFY_MASK);
set_name("ThresholdSelector");
set_can_focus(false);
set_app_paintable(true);
updateTooltip();
}
/*
* Set the position of the sliders without telling it to the listener
*/
void ThresholdSelector::setPositions (double bottom, double top) {
setPositions(bottom, top, maxVal, maxVal);
}
/*
* Set the position of the sliders without telling it to the listener
*/
void ThresholdSelector::setPositions (double bottomLeft, double topLeft, double bottomRight, double topRight) {
bool different = ( (positions[TS_TOPLEFT] != topLeft) || (positions[TS_TOPRIGHT] != topRight) ||
(positions[TS_BOTTOMLEFT] != bottomLeft) || (positions[TS_BOTTOMRIGHT] != bottomRight) );
positions[TS_BOTTOMLEFT] = bottomLeft;
positions[TS_TOPLEFT] = topLeft;
positions[TS_BOTTOMRIGHT] = bottomRight;
positions[TS_TOPRIGHT] = topRight;
if (different) {
sig_val_changed.emit();
updateTooltip();
queue_draw ();
}
}
void ThresholdSelector::setDefaults (double bottom, double top) {
setDefaults(bottom, top, maxVal, maxVal);
}
void ThresholdSelector::setDefaults (double bottomLeft, double topLeft, double bottomRight, double topRight) {
defPos[TS_BOTTOMLEFT] = bottomLeft;
defPos[TS_TOPLEFT] = topLeft;
if (doubleThresh) {
defPos[TS_BOTTOMRIGHT] = bottomRight;
defPos[TS_TOPRIGHT] = topRight;
}
}
void ThresholdSelector::getPositions (Glib::ustring& bottom, Glib::ustring& top) {
bottom = Glib::ustring::format(std::fixed, std::setprecision(precision),positions[TS_BOTTOMLEFT]);
top = Glib::ustring::format(std::fixed, std::setprecision(precision),positions[TS_TOPLEFT]);
}
void ThresholdSelector::getPositions (Glib::ustring& bottomLeft, Glib::ustring& topLeft, Glib::ustring& bottomRight, Glib::ustring& topRight) {
bottomLeft = Glib::ustring::format(std::fixed, std::setprecision(precision),positions[TS_BOTTOMLEFT]);
topLeft = Glib::ustring::format(std::fixed, std::setprecision(precision),positions[TS_TOPLEFT]);
bottomRight = Glib::ustring::format(std::fixed, std::setprecision(precision),positions[TS_BOTTOMRIGHT]);
topRight = Glib::ustring::format(std::fixed, std::setprecision(precision),positions[TS_TOPRIGHT]);
}
void ThresholdSelector::setBgGradient (const std::vector<GradientMilestone> &milestones) {
bgGradient.clear();
bgGradient = milestones;
}
void ThresholdSelector::on_realize() {
Gtk::DrawingArea::on_realize();
add_events(Gdk::EXPOSURE_MASK | Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK);
}
bool ThresholdSelector::on_expose_event(GdkEventExpose* event) {
Gdk::Color c;
Glib::RefPtr<Gdk::Window> win = get_window();
Cairo::RefPtr<Cairo::Context> cr = win->create_cairo_context();
double positions01[4];
int w = get_width ();
int h = get_height ();
wslider = std::max(int(h / 5), 10);
int hwslider = wslider/2;
int iw = w-wslider-2*hb; // inner width (excluding padding for tabs)
for (int i=0; i<4; i++) {
positions01[i] = to01(positions[i]);
}
Gtk::StateType state = !is_sensitive() ? Gtk::STATE_INSENSITIVE : Gtk::STATE_NORMAL;
Glib::RefPtr<Gtk::Style> style = get_style();
// set the box's colors
cr->set_line_width (1.0);
cr->set_line_cap(Cairo::LINE_CAP_BUTT);
if (is_sensitive() && bgGradient.size()>1) {
// gradient background
Cairo::RefPtr< Cairo::LinearGradient > bggradient = Cairo::LinearGradient::create (hwslider, 0, hwslider+iw, 0);
for (std::vector<GradientMilestone>::iterator i=bgGradient.begin(); i!=bgGradient.end(); i++) {
bggradient->add_color_stop_rgb (i->position, i->r, i->g, i->b);
}
cr->set_source (bggradient);
// draw the box's background
cr->rectangle (hb+hwslider-0.5, double(int(float(h)*1.5f/7.f))+0.5, iw+1, double(int(float(h)*4.f/7.f)));
cr->fill();
}
else if (is_sensitive()) {
// solid background
c = style->get_bg (state);
cr->set_source_rgb (c.get_red_p()*0.85, c.get_green_p()*0.85, c.get_blue_p()*0.85);
// draw the box's background
cr->rectangle (hb+hwslider-0.5, double(int(float(h)*1.5f/7.f))+0.5, iw+1, double(int(float(h)*4.f/7.f)));
cr->fill();
}
// draw curve
double yStart = initalEq1 ? double(int(float(h)*1.5f/7.f))+1.5 : double(int(float(h)*5.5f/7.f))-0.5;
double yEnd = initalEq1 ? double(int(float(h)*5.5f/7.f))-0.5 : double(int(float(h)*1.5f/7.f))+1.5;
ThreshCursorId p[4];
if (initalEq1) { p[0] = TS_TOPLEFT; p[1] = TS_BOTTOMLEFT; p[2] = TS_BOTTOMRIGHT; p[3] = TS_TOPRIGHT; }
else { p[0] = TS_BOTTOMLEFT; p[1] = TS_TOPLEFT; p[2] = TS_TOPRIGHT; p[3] = TS_BOTTOMRIGHT; }
if (positions[p[1]] > minVal)
cr->move_to (hb+hwslider, yStart);
else
cr->move_to (hb+hwslider, yEnd);
if (positions[p[0]] > minVal)
cr->line_to (hb+hwslider+iw*positions01[p[0]]+0.5, yStart);
if (positions[p[1]] > minVal)
cr->line_to (hb+hwslider+iw*positions01[p[1]]+0.5, yEnd);
cr->line_to (hb+hwslider+iw*positions01[p[2]]+0.5, yEnd);
if (doubleThresh && positions[p[2]] < maxVal) {
cr->line_to (hb+hwslider+iw*positions01[p[3]]+0.5, yStart);
if (positions[p[3]] < maxVal)
cr->line_to (hb+hwslider+iw+0.5, yStart);
}
if (is_sensitive() && bgGradient.size()>1) {
// draw surrounding curve
c = style->get_bg (state);
cr->set_source_rgb (c.get_red_p()*0.85, c.get_green_p()*0.85, c.get_blue_p()*0.85);
cr->set_line_width (5.0);
cr->stroke_preserve();
}
// draw curve
if (is_sensitive()) {
c = style->get_fg (movedCursor!=TS_UNDEFINED || litCursor!=TS_UNDEFINED ? Gtk::STATE_PRELIGHT : Gtk::STATE_ACTIVE);
cr->set_source_rgb (c.get_red_p(), c.get_green_p(), c.get_blue_p());
}
else {
c = style->get_bg (Gtk::STATE_INSENSITIVE);
cr->set_source_rgb (c.get_red_p()*0.7, c.get_green_p()*0.7, c.get_blue_p()*0.7);
}
cr->set_line_width (1.5);
cr->stroke ();
// draw the box's borders
cr->set_line_width (1.);
cr->rectangle (hb+hwslider-0.5, double(int(float(h)*1.5f/7.f))+0.5, iw+1, double(int(float(h)*4.f/7.f)));
c = style->get_bg (state);
cr->set_source_rgb (c.get_red_p()*0.7, c.get_green_p()*0.7, c.get_blue_p()*0.7);
cr->stroke ();
// draw sliders
//if (!(litCursor == TS_UNDEFINED && movedCursor == TS_UNDEFINED)) {
cr->set_line_width (1.);
for (int i=0; i<(doubleThresh?4:2); i++) {
double posX = hb+hwslider+iw*positions01[i]+0.5;
double arrowY = i==0 || i==2 ? h-(h*2.5/7.-0.5)-vb : h*2.5/7.-0.5+vb;
double baseY = i==0 || i==2 ? h-0.5-vb : 0.5+vb;
double centerY = (arrowY+baseY)/2.;
cr->move_to (posX, arrowY);
cr->line_to (posX+hwslider, centerY);
cr->line_to (posX+hwslider, baseY);
cr->line_to (posX-hwslider, baseY);
cr->line_to (posX-hwslider, centerY);
cr->close_path();
if (i==movedCursor) {
// moved (selected)
c = style->get_bg (Gtk::STATE_SELECTED);
cr->set_source_rgb (c.get_red_p(), c.get_green_p(), c.get_blue_p());
cr->fill_preserve ();
//c = style->get_dark (Gtk::STATE_SELECTED);
//cr->set_source_rgb (c.get_red_p(), c.get_green_p(), c.get_blue_p());
c = style->get_bg (state);
cr->set_source_rgb (c.get_red_p()*0.55, c.get_green_p()*0.55, c.get_blue_p()*0.55);
cr->stroke ();
}
else if (i==secondaryMovedCursor || (movedCursor==TS_UNDEFINED && i==litCursor)) {
// prelight
c = style->get_bg (Gtk::STATE_PRELIGHT);
cr->set_source_rgb (c.get_red_p(), c.get_green_p(), c.get_blue_p());
cr->fill_preserve ();
c = style->get_bg (state);
cr->set_source_rgb (c.get_red_p()*0.55, c.get_green_p()*0.55, c.get_blue_p()*0.55);
cr->stroke ();
}
else {
// normal
c = style->get_bg (is_sensitive() ? Gtk::STATE_ACTIVE : Gtk::STATE_INSENSITIVE);
cr->set_source_rgb (c.get_red_p(), c.get_green_p(), c.get_blue_p());
cr->fill_preserve ();
c = style->get_bg (state);
cr->set_source_rgb (c.get_red_p()*0.7, c.get_green_p()*0.7, c.get_blue_p()*0.7);
cr->stroke ();
}
}
//}
//printf("\n\n");
// draw text for the slider that is being moved
/*
* Original code from shcselector.cc
*
Glib::RefPtr<Pango::Context> context = get_pango_context () ;
cr->set_line_width (0.5);
if (litCursor != TS_UNDEFINED) {
int offset;
int layout_width, layout_height;
Glib::RefPtr<Pango::Layout> layout = create_pango_layout(Glib::ustring::format(std::setprecision(2), positions01[litCursor]));
layout->get_pixel_size(layout_width, layout_height);
offset = positions01[litCursor] > 0.5 ? -layout_width-1-wslider/2 : 1+wslider/2;
cr->move_to (w*positions01[litCursor]+offset-0.5, 0);
cr->set_source_rgb (bgnc.get_red_p(), bgnc.get_green_p(), bgnc.get_blue_p());
layout->add_to_cairo_context (cr);
cr->fill_preserve ();
cr->stroke ();
cr->move_to (w*positions01[litCursor]+offset+0.5, 1);
layout->add_to_cairo_context (cr);
cr->fill_preserve ();
cr->stroke ();
cr->set_source_rgb (fgnc.get_red_p(), fgnc.get_green_p(), fgnc.get_blue_p());
cr->move_to (w*positions01[litCursor]+offset, 0.5);
layout->add_to_cairo_context (cr);
cr->fill_preserve ();
cr->stroke ();
}*/
return true;
}
bool ThresholdSelector::on_button_press_event (GdkEventButton* event) {
if (event->button == 1) {
movedCursor = litCursor;
findSecondaryMovedCursor(event->state);
tmpX = event->x;
queue_draw ();
}
grab_focus();
return true;
}
bool ThresholdSelector::on_button_release_event (GdkEventButton* event) {
if (event->button == 1) {
findLitCursor(event->x, event->y);
movedCursor = TS_UNDEFINED;
secondaryMovedCursor = TS_UNDEFINED;
queue_draw ();
}
return true;
}
bool ThresholdSelector::on_leave_notify_event (GdkEventCrossing* event) {
if (movedCursor == TS_UNDEFINED) {
litCursor = TS_UNDEFINED;
oldLitCursor = TS_UNDEFINED;
queue_draw();
}
return true;
}
bool ThresholdSelector::on_motion_notify_event (GdkEventMotion* event) {
int w = get_width ();
findLitCursor(event->x, event->y);
if (movedCursor != TS_UNDEFINED) {
// user is moving a cursor or two
double minBound, maxBound;
findSecondaryMovedCursor(event->state);
// computing the boundaries
findBoundaries(minBound, maxBound);
double dX = ( (event->x-tmpX)*(maxVal-minVal) )/( w-2*hb );
// slow motion if CTRL is pressed
if (event->state & Gdk::CONTROL_MASK)
dX *= 0.05;
// get the new X value, inside bounds
double newX = positions[movedCursor] + dX;
if (newX > maxBound) newX = maxBound;
else if (newX < minBound) newX = minBound;
// compute the effective dX
dX = newX - positions[movedCursor];
// set the new position of the moved cursor
positions[movedCursor] = newX;
// apply the decay to the secondary moved cursor, if necessary
if (secondaryMovedCursor != TS_UNDEFINED) {
positions[secondaryMovedCursor] += dX;
}
// set the new reference value for the next move
tmpX = event->x;
// update the tooltip
updateTooltip();
sig_val_changed.emit();
queue_draw ();
}
else {
if (litCursor != oldLitCursor)
queue_draw ();
oldLitCursor = litCursor;
}
return true;
}
void ThresholdSelector::findLitCursor(int posX, int posY) {
int w = get_width ();
int h = get_height ();
litCursor = TS_UNDEFINED;
if (posY >=0 && posY <= h/2) {
if (posX > 0 && posX < w) {
litCursor = TS_TOPLEFT;
if (doubleThresh) {
double cursorX = (posX-hb)*(maxVal-minVal)/(w-2*hb)+minVal;
if (cursorX>positions[TS_TOPRIGHT] || abs(cursorX-positions[TS_TOPRIGHT]) < abs(cursorX-positions[TS_TOPLEFT]))
litCursor = TS_TOPRIGHT;
}
}
}
else if (posY > h/2 && posY < h) {
if (posX > 0 && posX < w) {
litCursor = TS_BOTTOMLEFT;
if (doubleThresh) {
double cursorX = (posX-hb)*(maxVal-minVal)/(w-2*hb)+minVal;
if (cursorX>positions[TS_BOTTOMRIGHT] || abs(cursorX-positions[TS_BOTTOMRIGHT]) < abs(cursorX-positions[TS_BOTTOMLEFT]))
litCursor = TS_BOTTOMRIGHT;
}
}
}
}
void ThresholdSelector::findBoundaries(double &min, double &max) {
switch (movedCursor) {
case (TS_BOTTOMLEFT):
if (initalEq1) {
min = secondaryMovedCursor == TS_UNDEFINED ? positions[TS_TOPLEFT] : minVal+(positions[TS_BOTTOMLEFT]-positions[TS_TOPLEFT]);
max = positions[TS_BOTTOMRIGHT];
}
else {
min = minVal;
max = secondaryMovedCursor == TS_UNDEFINED ? positions[TS_TOPLEFT] : positions[TS_TOPRIGHT]-(positions[TS_TOPLEFT]-positions[TS_BOTTOMLEFT]);
}
break;
case (TS_TOPLEFT):
if (initalEq1) {
min = minVal;
max = secondaryMovedCursor == TS_UNDEFINED ? positions[TS_BOTTOMLEFT] : positions[TS_BOTTOMRIGHT]-(positions[TS_BOTTOMLEFT]-positions[TS_TOPLEFT]);
}
else {
min = secondaryMovedCursor == TS_UNDEFINED ? positions[TS_BOTTOMLEFT] : minVal+(positions[TS_TOPLEFT]-positions[TS_BOTTOMLEFT]);
max = positions[TS_TOPRIGHT];
}
break;
case (TS_BOTTOMRIGHT):
if (initalEq1) {
min = positions[TS_BOTTOMLEFT];
max = secondaryMovedCursor == TS_UNDEFINED ? positions[TS_TOPRIGHT] : maxVal-(positions[TS_TOPRIGHT]-positions[TS_BOTTOMRIGHT]);
}
else {
min = secondaryMovedCursor == TS_UNDEFINED ? positions[TS_TOPRIGHT] : positions[TS_TOPLEFT]+(positions[TS_BOTTOMRIGHT]-positions[TS_TOPRIGHT]);
max = maxVal;
}
break;
case (TS_TOPRIGHT):
if (initalEq1) {
min = secondaryMovedCursor == TS_UNDEFINED ? positions[TS_BOTTOMRIGHT] : positions[TS_BOTTOMLEFT]+(positions[TS_TOPRIGHT]-positions[TS_BOTTOMRIGHT]);
max = maxVal;
}
else {
min = positions[TS_TOPLEFT];
max = secondaryMovedCursor == TS_UNDEFINED ? positions[TS_BOTTOMRIGHT] : maxVal-(positions[TS_BOTTOMRIGHT]-positions[TS_TOPRIGHT]);
}
break;
default:
min = minVal;
max = maxVal;
break;
}
}
void ThresholdSelector::findSecondaryMovedCursor(guint state) {
secondaryMovedCursor = TS_UNDEFINED;
if (!(state & Gdk::SHIFT_MASK)) {
switch (movedCursor) {
case (TS_BOTTOMLEFT):
secondaryMovedCursor = TS_TOPLEFT;
break;
case (TS_TOPLEFT):
secondaryMovedCursor = TS_BOTTOMLEFT;
break;
case (TS_BOTTOMRIGHT):
secondaryMovedCursor = TS_TOPRIGHT;
break;
case (TS_TOPRIGHT):
secondaryMovedCursor = TS_BOTTOMRIGHT;
break;
default:
secondaryMovedCursor = TS_UNDEFINED;
break;
}
}
}
void ThresholdSelector::styleChanged (const Glib::RefPtr<Gtk::Style>& style) {
queue_draw ();
}
void ThresholdSelector::reset () {
positions[0] = defPos[0];
positions[1] = defPos[1];
positions[2] = defPos[2];
positions[2] = defPos[3];
updateTooltip();
queue_draw ();
}
inline double ThresholdSelector::to01(double value) {
double rVal = (value-minVal)/(maxVal-minVal);
if (rVal < 0.) rVal = 0.;
else if (rVal > 1.) rVal = 1.;
return rVal;
}
void ThresholdSelector::updateTooltip() {
Glib::ustring tTip;
if (doubleThresh)
tTip = Glib::ustring::compose("<b>%1:</b> %2 <b>%3:</b> %4\n<b>%5:</b> %6 <b>%7:</b> %8\n%9",
M("THRESHOLDSELECTOR_TL"), Glib::ustring::format(std::fixed, std::setprecision(precision), positions[TS_TOPLEFT]),
M("THRESHOLDSELECTOR_TR"), Glib::ustring::format(std::fixed, std::setprecision(precision), positions[TS_TOPRIGHT]),
M("THRESHOLDSELECTOR_BL"), Glib::ustring::format(std::fixed, std::setprecision(precision), positions[TS_BOTTOMLEFT]),
M("THRESHOLDSELECTOR_BR"), Glib::ustring::format(std::fixed, std::setprecision(precision), positions[TS_BOTTOMRIGHT]),
M("THRESHOLDSELECTOR_HINT")
);
else
tTip = Glib::ustring::compose("<b>%1:</b> %2\n<b>%3:</b> %4\n%5",
M("THRESHOLDSELECTOR_T"), Glib::ustring::format(std::fixed, std::setprecision(precision), positions[TS_TOPLEFT]),
M("THRESHOLDSELECTOR_B"), Glib::ustring::format(std::fixed, std::setprecision(precision), positions[TS_BOTTOMLEFT]),
M("THRESHOLDSELECTOR_HINT")
);
set_tooltip_markup(tTip);
}
sigc::signal<void> ThresholdSelector::signal_value_changed() {
return sig_val_changed;
}

161
rtgui/thresholdselector.h Normal file
View File

@@ -0,0 +1,161 @@
/*
* 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/>.
*/
#ifndef _THRESHOLDSELECTOR_
#define _THRESHOLDSELECTOR_
#include "guiutils.h"
#include "../rtengine/procparams.h"
/*
* This widget will let you select a linearly variable threshold, creating a ramp up
* if you want to go from a null effect to a full effect
* 0-0-ramp up-1-1
* or a ramp down if you want the contrary
* 1-1-ramp down-0-0
*
* You can optionally create a double threshold
* 0-0-ramp up-1-1-ramp down-0-0
* or
* 1-1-ramp down-0-0-ramp up-1-1
*
* Please note that the values are related to the cursors, depending on their position
* on the graph. E.g. the "bottomLeft" value is related to the bottom left cursor.
*/
class ThresholdSelector : public Gtk::DrawingArea {
public:
enum ThreshCursorId {
TS_UNDEFINED=-1,
TS_BOTTOMLEFT,
TS_TOPLEFT,
TS_BOTTOMRIGHT,
TS_TOPRIGHT
};
protected:
sigc::signal<void> sig_val_changed;
Glib::RefPtr<Gdk::GC> gc_;
Glib::RefPtr<Gdk::Pixmap> backBuffer;
std::vector<GradientMilestone> bgGradient;
bool doubleThresh; // If true: there curve is a double threshold (0 to 1 to 0, or 1 to 0 to 1).
bool initalEq1; // If true: the curve start at 1 (top); if false: the curve start at 0 (bottom)
unsigned int precision; // Decimal number if this object has to handle "double" values
ThreshCursorId litCursor;
ThreshCursorId oldLitCursor;
double boundary1[2], boundary2[2];
double tmpX, tmpPos;
ThreshCursorId movedCursor, secondaryMovedCursor;
double minVal, maxVal;
double defPos[4];
double positions[4];
unsigned short wslider;
const static int hb = 3; // horizontal border
const static int vb = 2; // vertical border
void initValues (double minValue, double maxValue, bool startAtOne);
void findLitCursor(int posX, int posY);
void findSecondaryMovedCursor(guint state);
void findBoundaries(double &min, double &max);
double to01(double value);
void updateTooltip();
public:
sigc::signal<void> signal_value_changed();
ThresholdSelector(double minValue, double maxValue, double defBottom, double defTop, unsigned int precision, bool startAtOne);
ThresholdSelector(double minValue, double maxValue, double defBottomLeft, double defTopLeft, double defBottomRight, double defTopRight, unsigned int precision, bool startAtOne);
double shapeValue (double value) { return round(value*pow(double(10), precision)) / pow(double(10), precision); }
template <typename T>
void setDefaults (const rtengine::procparams::Threshold<T> &t) {
defPos[TS_BOTTOMLEFT] = double(t.value[0]); // should we use shapeValue() ?
defPos[TS_TOPLEFT] = double(t.value[1]);
if (doubleThresh) {
defPos[TS_BOTTOMRIGHT] = double(t.value[2]);
defPos[TS_TOPRIGHT] = double(t.value[3]);
}
}
void setDefaults (double bottom, double top);
void setDefaults (double bottomLeft, double topLeft, double bottomRight, double topRight);
template <typename T>
void setPositions (const rtengine::procparams::Threshold<T> &tValues) {
positions[TS_BOTTOMLEFT] = static_cast<double>(tValues.value[TS_BOTTOMLEFT]);
positions[TS_TOPLEFT] = static_cast<double>(tValues.value[TS_TOPLEFT]);
if (tValues.isDouble()) {
positions[TS_BOTTOMRIGHT] = static_cast<double>(tValues.value[TS_BOTTOMRIGHT]);
positions[TS_TOPRIGHT] = static_cast<double>(tValues.value[TS_TOPRIGHT]);
}
updateTooltip();
queue_draw();
}
void setPositions (double bottom, double top);
void setPositions (double bottomLeft, double topLeft, double bottomRight, double topRight);
template <typename T>
rtengine::procparams::Threshold<T> getPositions () {
if (doubleThresh) {
rtengine::procparams::Threshold<T> rThresh(
static_cast<T>(shapeValue(positions[TS_BOTTOMLEFT])),
static_cast<T>(shapeValue(positions[TS_TOPLEFT])),
static_cast<T>(shapeValue(positions[TS_BOTTOMRIGHT])),
static_cast<T>(shapeValue(positions[TS_TOPRIGHT])),
initalEq1
);
return rThresh;
}
else {
rtengine::procparams::Threshold<T> rThresh(
static_cast<T>(shapeValue(positions[TS_BOTTOMLEFT])),
static_cast<T>(shapeValue(positions[TS_TOPLEFT])),
initalEq1
);
return rThresh;
}
}
void getPositions (Glib::ustring& bottom, Glib::ustring& top);
void getPositions (Glib::ustring& bottomLeft, Glib::ustring& topLeft, Glib::ustring& bottomRight, Glib::ustring& topRight);
void setBgGradient (const std::vector<GradientMilestone> &milestones);
bool isStartAtOne() { return initalEq1; }
bool isDouble() { return doubleThresh; }
void on_realize ();
bool on_expose_event(GdkEventExpose* event);
bool on_button_press_event (GdkEventButton* event);
bool on_button_release_event (GdkEventButton* event);
bool on_motion_notify_event (GdkEventMotion* event);
bool on_leave_notify_event (GdkEventCrossing* event);
void styleChanged (const Glib::RefPtr<Gtk::Style>& style);
unsigned int getPrecision () { return precision; }
void reset ();
};
#endif

View File

@@ -327,6 +327,11 @@ void Thumbnail::setProcParams (const ProcParams& pp, ParamsEdited* pe, int whoCh
Glib::Mutex::Lock lock(mutex);
#endif
if (pparams.sharpening.threshold.isDouble() != pp.sharpening.threshold.isDouble())
printf("WARNING: Sharpening different!\n");
if (pparams.vibrance.psthreshold.isDouble() != pp.vibrance.psthreshold.isDouble())
printf("WARNING: Vibrance different!\n");
if (pparams!=pp)
cfs.recentlySaved = false;

View File

@@ -23,6 +23,7 @@ using namespace rtengine;
using namespace rtengine::procparams;
Vibrance::Vibrance () : Gtk::VBox(), FoldableToolPanel(this) {
enabled = Gtk::manage (new Gtk::CheckButton (M("GENERAL_ENABLED")));
enabled->set_active (false);
pack_start(*enabled, Gtk::PACK_SHRINK, 0);
@@ -38,7 +39,7 @@ Vibrance::Vibrance () : Gtk::VBox(), FoldableToolPanel(this) {
//if (saturated->delay < 1000) saturated->delay = 1000;
pack_start( *saturated, Gtk::PACK_SHRINK, 0);
psThreshold = Gtk::manage(new Adjuster (M("TP_VIBRANCE_PSTHRESHOLD"),0,100,5,75));
psThreshold = Gtk::manage (new ThresholdAdjuster (M("TP_VIBRANCE_PSTHRESHOLD"), 0., 100., 75., 75., 0, false));
psThreshold->setAdjusterListener (this);
psThreshold->set_sensitive(false);
//if (psThreshold->delay < 1000) psThreshold->delay = 1000;
@@ -98,7 +99,7 @@ void Vibrance::read(const ProcParams* pp, const ParamsEdited* pedited) {
lastPastSatTog = pp->vibrance.pastsattog;
pastels->setValue (pp->vibrance.pastels);
psThreshold->setValue (pp->vibrance.psthreshold);
psThreshold->setValue<int> (pp->vibrance.psthreshold);
if (lastPastSatTog) {
// Link both slider, so we set saturated and psThresholds unsensitive
@@ -120,7 +121,7 @@ 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->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 ();
@@ -249,12 +250,16 @@ void Vibrance::adjusterChanged (Adjuster* a, double newval) {
listener->panelChanged (EvVibrancePastels, value );
else if (a == saturated && !pastSatTog->get_active())
listener->panelChanged (EvVibranceSaturated, value );
else if (a == psThreshold){
listener->panelChanged (EvVibrancePastSatThreshold, 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);
@@ -267,7 +272,7 @@ void Vibrance::setBatchMode(bool 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);
psThreshold->setDefault<int> (defParams->vibrance.psthreshold);
if (pedited) {
pastels->setDefaultEditedState (pedited->vibrance.pastels ? Edited : UnEdited);
@@ -284,11 +289,9 @@ void Vibrance::setDefaults(const ProcParams* defParams, const ParamsEdited* pedi
void Vibrance::setAdjusterBehavior (bool pastelsadd, bool saturatedadd, bool psthreshdadd) {
pastels->setAddMode (pastelsadd);
saturated->setAddMode (saturatedadd);
psThreshold->setAddMode (psthreshdadd);
}
void Vibrance::trimValues (ProcParams* pp) {
pastels->trimValue (pp->vibrance.pastels);
saturated->trimValue (pp->vibrance.saturated);
psThreshold->trimValue (pp->vibrance.psthreshold);
}

View File

@@ -21,16 +21,16 @@
#include <gtkmm.h>
#include "adjuster.h"
//#include "guiutils.h"
#include "thresholdadjuster.h"
#include "toolpanel.h"
class Vibrance : public Gtk::VBox, public AdjusterListener, public FoldableToolPanel {
class Vibrance : public Gtk::VBox, public AdjusterListener, public ThresholdAdjusterListener, public FoldableToolPanel {
protected:
Gtk::CheckButton* enabled;
Adjuster* pastels;
Adjuster* saturated;
Adjuster* psThreshold;
ThresholdAdjuster* psThreshold;
Gtk::CheckButton* protectSkins;
Gtk::CheckButton* avoidColorShift;
Gtk::CheckButton* pastSatTog;
@@ -55,6 +55,7 @@ public:
void trimValues (rtengine::procparams::ProcParams* pp);
void setAdjusterBehavior (bool amountadd, bool passadd, bool psthreshdadd);
void adjusterChanged (Adjuster* a, double newval);
void adjusterChanged (ThresholdAdjuster* a, int newBottom, int newTop);
void enabled_toggled ();
void protectskins_toggled ();