diff --git a/rtdata/images/curveType-controlPoints.png b/rtdata/images/curveType-controlPoints.png new file mode 100644 index 000000000..c32da3a05 Binary files /dev/null and b/rtdata/images/curveType-controlPoints.png differ diff --git a/rtdata/images/curveType-flatLinear.png b/rtdata/images/curveType-flatLinear.png new file mode 100644 index 000000000..3a746c534 Binary files /dev/null and b/rtdata/images/curveType-flatLinear.png differ diff --git a/rtdata/languages/Francais b/rtdata/languages/Francais index 390d5092c..82c19fb81 100644 --- a/rtdata/languages/Francais +++ b/rtdata/languages/Francais @@ -18,6 +18,7 @@ CURVEEDITOR_HIGHLIGHTS;Hautes lumières CURVEEDITOR_LIGHTS;Zones claires CURVEEDITOR_LINEAR;Linéaire CURVEEDITOR_LOADDLGLABEL;Charger la courbe... +CURVEEDITOR_MINMAXCPOINTS;Points de contrôle minima/maxima CURVEEDITOR_NURBS;Cage de contrôle CURVEEDITOR_PARAMETRIC;Paramétrique CURVEEDITOR_SAVEDLGLABEL;Enregistrer la courbe... @@ -499,8 +500,8 @@ PREFERENCES_INTENT_RELATIVE;Colorimétrie relative PREFERENCES_INTENT_SATURATION;Saturation PREFERENCES_LIVETHUMBNAILS;Vignettes "Live" (plus lent) PREFERENCES_MONITORICC;Profil du moniteur -PREFERENCES_MULTITAB;Mode éditeurs multiple -PREFERENCES_MULTITABDUALMON;Mode éditeurs multiple, si possible sur un second moniteur +PREFERENCES_MULTITAB;Éditeurs multiple +PREFERENCES_MULTITABDUALMON;Éditeurs multiple, si possible sur un second moniteur PREFERENCES_OUTDIR;Dossier de sortie PREFERENCES_OUTDIRFOLDER;Dossier de sauvegarde PREFERENCES_OUTDIRFOLDERHINT;Place les images traitées dans le dossier selectionné @@ -531,8 +532,8 @@ PREFERENCES_SHOWBASICEXIF;Voir les infos EXIF basiques PREFERENCES_SHOWDATETIME;Voir la date et l'heure PREFERENCES_SHOWONLYRAW;Voir seulement les fichiers RAW PREFERENCES_SHTHRESHOLD;Seuil pour le dépassement de domaine inférieur -PREFERENCES_SINGLETAB;Mode éditeur unique -PREFERENCES_SINGLETABVERTAB;Mode éditeur unique, onglets verticaux +PREFERENCES_SINGLETAB;Éditeur unique +PREFERENCES_SINGLETABVERTAB;Éditeur unique, onglets verticaux PREFERENCES_STARTUPIMDIR;Répertoire Image au démarrage PREFERENCES_TAB_BROWSER;Navigateur de fichiers PREFERENCES_TAB_COLORMGR;Gestion des couleurs @@ -691,11 +692,11 @@ TP_HSVEQUALIZER6;Bleu TP_HSVEQUALIZER7;Violet TP_HSVEQUALIZER8;Magenta TP_HSVEQUALIZER_CHANNEL;Canal -TP_HSVEQUALIZER_HUE;Teinte +TP_HSVEQUALIZER_HUE;T TP_HSVEQUALIZER_LABEL;Égaliseur TSV TP_HSVEQUALIZER_NEUTRAL;Neutre -TP_HSVEQUALIZER_SAT;Saturation -TP_HSVEQUALIZER_VAL;Valeur +TP_HSVEQUALIZER_SAT;S +TP_HSVEQUALIZER_VAL;V TP_ICM_FILEDLGFILTERANY;Tous les fichiers TP_ICM_FILEDLGFILTERICM;Fichiers de profil ICC TP_ICM_GAMMABEFOREINPUT;Appliquer le Gamma du profil diff --git a/rtdata/languages/default b/rtdata/languages/default index c79efe882..4feba3d7f 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -17,6 +17,7 @@ CURVEEDITOR_HIGHLIGHTS;Highlights CURVEEDITOR_LIGHTS;Lights CURVEEDITOR_LINEAR;Linear CURVEEDITOR_LOADDLGLABEL;Load Curve... +CURVEEDITOR_MINMAXCPOINTS;Minima/Maxima control points CURVEEDITOR_NURBS;Control cage CURVEEDITOR_PARAMETRIC;Parametric CURVEEDITOR_SAVEDLGLABEL;Save Curve... @@ -706,11 +707,11 @@ TP_HSVEQUALIZER6;Blue TP_HSVEQUALIZER7;Purple TP_HSVEQUALIZER8;Magenta TP_HSVEQUALIZER_CHANNEL;HSV Channel -TP_HSVEQUALIZER_HUE;Hue +TP_HSVEQUALIZER_HUE;H TP_HSVEQUALIZER_LABEL;HSV Equalizer TP_HSVEQUALIZER_NEUTRAL;Neutral -TP_HSVEQUALIZER_SAT;Saturation -TP_HSVEQUALIZER_VAL;Value +TP_HSVEQUALIZER_SAT;S +TP_HSVEQUALIZER_VAL;V TP_ICM_FILEDLGFILTERANY;Any files TP_ICM_FILEDLGFILTERICM;ICC Profile Files TP_ICM_GAMMABEFOREINPUT;Profile applies Gamma diff --git a/rtengine/CMakeLists.txt b/rtengine/CMakeLists.txt index 8422de6d9..e32cf101e 100644 --- a/rtengine/CMakeLists.txt +++ b/rtengine/CMakeLists.txt @@ -7,8 +7,8 @@ link_directories (${CMAKE_CURRENT_SOURCE_DIR}/../rtexif ${EXTRA_LIBDIR} ${GTHREA ${GOBJECT_LIBRARY_DIRS} ${GLIB2_LIBRARY_DIRS} ${GLIBMM_LIBRARY_DIRS} ${IPTCDATA_LIBRARY_DIRS} ${LCMS_LIBRARY_DIRS}) -set (RTENGINESOURCEFILES colortemp.cc curves.cc dcraw.cc iccstore.cc dfmanager.cc rawimage.cc - image8.cc image16.cc imagedata.cc imageio.cc improcfun.cc init.cc dcrop.cc +set (RTENGINESOURCEFILES colortemp.cc curves.cc flatcurves.cc diagonalcurves.cc dcraw.cc iccstore.cc + dfmanager.cc rawimage.cc image8.cc image16.cc imagedata.cc imageio.cc improcfun.cc init.cc dcrop.cc loadinitial.cc procparams.cc rawimagesource.cc shmap.cc simpleprocess.cc refreshmap.cc stdimagesource.cc myfile.cc iccjpeg.c hlmultipliers.cc improccoordinator.cc processingjob.cc rtthumbnail.cc utils.cc labimage.cc slicer.cc diff --git a/rtengine/curves.cc b/rtengine/curves.cc index b38663b71..be5d21987 100644 --- a/rtengine/curves.cc +++ b/rtengine/curves.cc @@ -30,275 +30,32 @@ namespace rtengine { -Curve::Curve (const std::vector& p, int poly_pn) : x(NULL), y(NULL), ypp(NULL) { - - ppn = poly_pn; - - if (p.size()<3) { - kind = Empty; - } - else { - kind = (CurveType)p[0]; - if (kind==Linear || kind==Spline || kind==NURBS) { - N = (p.size()-1)/2; - x = new double[N]; - y = new double[N]; - int ix = 1; - for (int i=0; i 2) - NURBS_set (); - else kind=Linear; - } - else if (kind==Parametric) { - if (p.size()!=8 && p.size()!=9) - kind = Empty; - else { - x = new double[9]; - for (int i=0; i<4; i++) - x[i] = p[i]; - for (int i=4; i<8; i++) - x[i] = (p[i]+100.0)/200.0; - if (p.size()<9) - x[8] = 1.0; - else - x[8] = p[8]/100.0; - } - } - } +Curve::Curve () { + x = 0; + y = 0; + ypp = 0; } -Curve::~Curve () { +void Curve::AddPolygons () +{ + if (firstPointIncluded) { + poly_x.push_back(x1); + poly_y.push_back(y1); + } + for (int k=1; k<(nbr_points-1); k++) { + double t = k*increment; + double t2 = t*t; + double tr = 1.-t; + double tr2 = tr*tr; + double tr2t = tr*2*t; - delete [] x; - delete [] y; - delete [] ypp; - poly_x.clear(); - poly_y.clear(); -} - -void Curve::spline_cubic_set () { - - double* u = new double[N-1]; - delete [] ypp; - ypp = new double [N]; - - ypp[0] = u[0] = 0.0; /* set lower boundary condition to "natural" */ - - for (int i = 1; i < N - 1; ++i) { - double sig = (x[i] - x[i - 1]) / (x[i + 1] - x[i - 1]); - double p = sig * ypp[i - 1] + 2.0; - ypp[i] = (sig - 1.0) / p; - u[i] = ((y[i + 1] - y[i]) - / (x[i + 1] - x[i]) - (y[i] - y[i - 1]) / (x[i] - x[i - 1])); - u[i] = (6.0 * u[i] / (x[i + 1] - x[i - 1]) - sig * u[i - 1]) / p; - } - - ypp[N - 1] = 0.0; - for (int k = N - 2; k >= 0; --k) - ypp[k] = ypp[k] * ypp[k + 1] + u[k]; - - delete [] u; -} - -void Curve::NURBS_set () { - - int nbSubCurvesPoints = N + (N-3)*2; - - std::vector sc_x(nbSubCurvesPoints); // X sub-curve points ( XP0,XP1,XP2, XP2,XP3,XP4, ...) - std::vector sc_y(nbSubCurvesPoints); // Y sub-curve points ( YP0,YP1,YP2, YP2,YP3,YP4, ...) - std::vector sc_length(N+2); // Length of the subcurves - double total_length=0.; - - // Create the list of Bezier sub-curves - // NURBS_set is called if N > 2 only - - int j = 0; - int k = 0; - for (int i = 0; i < N-1;) { - double length; - double dx; - double dy; - - // first point (on the curve) - if (!i) { - sc_x[j] = x[i]; - sc_y[j++] = y[i++]; - } - else { - sc_x[j] = (x[i-1] + x[i]) / 2.; - sc_y[j++] = (y[i-1] + y[i]) / 2.; - } - - // second point (control point) - sc_x[j] = x[i]; - sc_y[j] = y[i++]; - length = sqrt(pow(sc_x[j] - sc_x[j-1],2) + pow(sc_y[j] - sc_y[j-1],2)); - j++; - - // third point (on the curve) - if (i==N-1) { - sc_x[j] = x[i]; - sc_y[j] = y[i]; - } - else { - sc_x[j] = (x[i-1] + x[i]) / 2.; - sc_y[j] = (y[i-1] + y[i]) / 2.; - } - dx = sc_x[j] - sc_x[j-1]; - dy = sc_y[j] - sc_y[j-1]; - length += sqrt(dx*dx + dy*dy); - j++; - - // Storing the length of all sub-curves and the total length (to have a better distribution - // of the points along the curve) - sc_length[k++] = length; - total_length += length; - } - - poly_x.clear(); - poly_y.clear(); - unsigned int sc_xsize=j-1; - j = 0; - // create the polyline with the number of points adapted to the X range of the sub-curve - for (unsigned int i=0; i < sc_xsize /*sc_x.size()*/; i+=3) { - // TODO: Speeding-up the interface by caching the polyline, instead of rebuilding it at each action on sliders !!! - int nbr_points = (int)(((double)(ppn+N-2) * sc_length[i/3] )/ total_length); - if (nbr_points<0){ - for(int it=0;it < sc_x.size(); it+=3) printf("sc_length[%d/3]=%f \n",it,sc_length[it/3]); - printf("NURBS: error detected!\n i=%d nbr_points=%d ppn=%d N=%d sc_length[i/3]=%f total_length=%f",i,nbr_points,ppn,N,sc_length[i/3],total_length); - exit(0); - } - // increment along the curve, not along the X axis - double increment = 1.0 / (double)(nbr_points-1); - if (!i) { - poly_x.push_back( sc_x[i]); - poly_y.push_back(sc_y[i]); - } - for (k=1; k<(nbr_points-1); k++) { - double t = k*increment; - double t2 = t*t; - double tr = 1.-t; - double tr2 = tr*tr; - double tr2t = tr*2*t; - - // adding a point to the polyline - poly_x.push_back( tr2*sc_x[i] + tr2t*sc_x[i+1] + t2*sc_x[i+2]); - poly_y.push_back( tr2*sc_y[i] + tr2t*sc_y[i+1] + t2*sc_y[i+2]); - } - // adding the last point of the sub-curve - poly_x.push_back( sc_x[i+2]); - poly_y.push_back(sc_y[i+2]); - } -} - -double Curve::getVal (double t) { - - switch (kind) { - - case Empty : - return t; - break; - - case Parametric : { - if (t<=1e-14) - return 0.0; - double c = -log(2.0)/log(x[2]); - double tv = exp(c*log(t)); - double base = pfull (tv, x[8], x[6], x[5]); - double stretched = base<=1e-14 ? 0.0 : exp(log(base)/c); - - base = pfull (0.5, x[8], x[6], x[5]); - double fc = base<=1e-14 ? 0.0 : exp(log(base)/c); // value of the curve at the center point - if (tx[N-1]) - return y[N-1]; - else if (t 1){ - int k = (k_hi + k_lo) / 2; - if (x[k] > t) - k_hi = k; - else - k_lo = k; - } - - double h = x[k_hi] - x[k_lo]; - // linear - if (kind==Linear) - return y[k_lo] + (t - x[k_lo]) * ( y[k_hi] - y[k_lo] ) / h; - // spline curve - else { // if (kind==Spline) { - double a = (x[k_hi] - t) / h; - double b = (t - x[k_lo]) / h; - double r = a*y[k_lo] + b*y[k_hi] + ((a*a*a - a)*ypp[k_lo] + (b*b*b - b)*ypp[k_hi]) * (h*h)/6.0; - return CLIPD(r); - } - break; - } - case NURBS : { - // values under and over the first and last point - if (t>x[N-1]) - return y[N-1]; - else if (t 1){ - int k = (k_hi + k_lo) / 2; - if (poly_x[k] > t) - k_hi = k; - else - k_lo = k; - } - - double h = poly_x[k_hi] - poly_x[k_lo]; - return poly_y[k_lo] + (t - poly_x[k_lo]) * ( poly_y[k_hi] - poly_y[k_lo] ) / h; - break; - } - default: - // all other (unknown) kind - return t; - } -} - -void Curve::getVal (const std::vector& t, std::vector& res) { - -// TODO!!!! can be made much faster!!! Binary search of getVal(double) at each point can be avoided - - res.resize (t.size()); - for (unsigned int i=0; i satcurvePoints; - satcurvePoints.push_back((double)((CurveType)NURBS)); + satcurvePoints.push_back((double)DCT_NURBS); if (saturation>0) { double satslope = (0.5+2*saturation/500.0)/(0.5-2*saturation/500.0); double scale = (satlimthresh/100.1); @@ -352,14 +109,14 @@ void CurveFactory::complexsgnCurve (double saturation, bool satlimit, double sat satcurvePoints.push_back(1); satcurvePoints.push_back(1+saturation/200.0); } - Curve* satcurve = NULL; - satcurve = new Curve (satcurvePoints, CURVES_MIN_POLY_POINTS/skip); // Actually, CURVES_MIN_POLY_POINTS = 1000, + DiagonalCurve* satcurve = NULL; + satcurve = new DiagonalCurve (satcurvePoints, CURVES_MIN_POLY_POINTS/skip); // Actually, CURVES_MIN_POLY_POINTS = 1000, //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // create a curve if needed - Curve* tcurve = NULL; + DiagonalCurve* tcurve = NULL; if (curvePoints.size()>0 && curvePoints[0]!=0) - tcurve = new Curve (curvePoints, CURVES_MIN_POLY_POINTS/skip); + tcurve = new DiagonalCurve (curvePoints, CURVES_MIN_POLY_POINTS/skip); for (int i=0; i<=0xffff; i+= i<0xffff-skip ? skip : 1 ) { @@ -432,9 +189,9 @@ void CurveFactory::complexsgnCurve (double saturation, bool satlimit, double sat bool needigamma = igamma_ && gamma_>0; // create a curve if needed - Curve* tcurve = NULL; + DiagonalCurve* tcurve = NULL; if (curvePoints.size()>0 && curvePoints[0]!=0) - tcurve = new Curve (curvePoints, CURVES_MIN_POLY_POINTS/skip); + tcurve = new DiagonalCurve (curvePoints, CURVES_MIN_POLY_POINTS/skip); // clear array that stores histogram valid before applying the custom curve if (outBeforeCCurveHistogram) @@ -445,7 +202,7 @@ void CurveFactory::complexsgnCurve (double saturation, bool satlimit, double sat //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% std::vector brightcurvePoints; - brightcurvePoints.push_back((double)((CurveType)NURBS)); + brightcurvePoints.push_back((double)DCT_NURBS); brightcurvePoints.push_back(0); //black point. Value in [0 ; 1] range brightcurvePoints.push_back(0); //black point. Value in [0 ; 1] range @@ -466,8 +223,8 @@ void CurveFactory::complexsgnCurve (double saturation, bool satlimit, double sat brightcurvePoints.push_back(1); // white point brightcurvePoints.push_back(1); // value at white point - Curve* brightcurve = NULL; - brightcurve = new Curve (brightcurvePoints, CURVES_MIN_POLY_POINTS/skip); // Actually, CURVES_MIN_POLY_POINTS = 1000, + DiagonalCurve* brightcurve = NULL; + brightcurve = new DiagonalCurve (brightcurvePoints, CURVES_MIN_POLY_POINTS/skip); // Actually, CURVES_MIN_POLY_POINTS = 1000, //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% float exp_scale = a*def_mul ; @@ -545,7 +302,7 @@ void CurveFactory::complexsgnCurve (double saturation, bool satlimit, double sat //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% std::vector contrastcurvePoints; - contrastcurvePoints.push_back((double)((CurveType)NURBS)); + contrastcurvePoints.push_back((double)DCT_NURBS); contrastcurvePoints.push_back(0); //black point. Value in [0 ; 1] range contrastcurvePoints.push_back(0); //black point. Value in [0 ; 1] range @@ -559,8 +316,8 @@ void CurveFactory::complexsgnCurve (double saturation, bool satlimit, double sat contrastcurvePoints.push_back(1); // white point contrastcurvePoints.push_back(1); // value at white point - Curve* contrastcurve = NULL; - contrastcurve = new Curve (contrastcurvePoints, CURVES_MIN_POLY_POINTS/skip); // Actually, CURVES_MIN_POLY_POINTS = 1000, + DiagonalCurve* contrastcurve = NULL; + contrastcurve = new DiagonalCurve (contrastcurvePoints, CURVES_MIN_POLY_POINTS/skip); // Actually, CURVES_MIN_POLY_POINTS = 1000, //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // apply contrast enhancement diff --git a/rtengine/curves.h b/rtengine/curves.h index be6deb949..14e14f04b 100644 --- a/rtengine/curves.h +++ b/rtengine/curves.h @@ -24,6 +24,8 @@ #include #include #include +#include +#include #define CURVES_MIN_POLY_POINTS 1000 @@ -175,11 +177,13 @@ class Curve { std::vector poly_x; // X points of the faceted curve std::vector poly_y; // Y points of the faceted curve double* ypp; - CurveType kind; - protected: - void spline_cubic_set (); - void NURBS_set (); + // Fields for the elementary curve polygonisation + double x1, y1, x2, y2, x3, y3; + bool firstPointIncluded; + double increment; + int nbr_points; + static inline double p00 (double x, double prot) { return CurveFactory::clower (x, 2.0, prot); } static inline double p11 (double x, double prot) { return CurveFactory::cupper (x, 2.0, prot); } static inline double p01 (double x, double prot) { return x<=0.5 ? CurveFactory::clower (x*2, 2.0, prot)/2.0 : 0.5 + CurveFactory::cupper ((x-0.5)*2, 2.0, prot)/2.0; } @@ -187,11 +191,44 @@ class Curve { static inline double pfull (double x, double prot, double sh, double hl) { return (1-sh)*(1-hl)*p00(x,prot) + sh*hl*p11(x,prot) + (1-sh)*hl*p01(x,prot) + sh*(1-hl)*p10(x,prot); } public: + Curve (); + void AddPolygons (); + virtual double getVal (double t) = 0; + virtual void getVal (const std::vector& t, std::vector& res) = 0; - Curve (const std::vector& points, int ppn=CURVES_MIN_POLY_POINTS); - ~Curve (); +}; - double getVal (double x); +class DiagonalCurve : public Curve { + + protected: + DiagonalCurveType kind; + + void spline_cubic_set (); + void NURBS_set (); + + public: + DiagonalCurve (const std::vector& points, int ppn=CURVES_MIN_POLY_POINTS); + ~DiagonalCurve (); + + double getVal (double t); + void getVal (const std::vector& t, std::vector& res); +}; + +class FlatCurve : public Curve { + + protected: + FlatCurveType kind; + double* leftTangent; + double* rightTangent; + + void CtrlPoints_set (); + + public: + + FlatCurve (const std::vector& points, int ppn=CURVES_MIN_POLY_POINTS); + ~FlatCurve (); + + double getVal (double t); void getVal (const std::vector& t, std::vector& res); }; } diff --git a/rtengine/diagonalcurves.cc b/rtengine/diagonalcurves.cc new file mode 100644 index 000000000..f07b8f0a7 --- /dev/null +++ b/rtengine/diagonalcurves.cc @@ -0,0 +1,292 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +#define CLIPD(a) ((a)>0.0?((a)<1.0?(a):1.0):0.0) + +namespace rtengine { + +DiagonalCurve::DiagonalCurve (const std::vector& p, int poly_pn) { + + ppn = poly_pn; + + if (p.size()<3) { + kind = DCT_Empty; + } + else { + kind = (DiagonalCurveType)p[0]; + if (kind==DCT_Linear || kind==DCT_Spline || kind==DCT_NURBS) { + N = (p.size()-1)/2; + x = new double[N]; + y = new double[N]; + int ix = 1; + for (int i=0; i 2) + NURBS_set (); + else kind=DCT_Linear; + } + else if (kind==DCT_Parametric) { + if (p.size()!=8 && p.size()!=9) + kind = DCT_Empty; + else { + x = new double[9]; + for (int i=0; i<4; i++) + x[i] = p[i]; + for (int i=4; i<8; i++) + x[i] = (p[i]+100.0)/200.0; + if (p.size()<9) + x[8] = 1.0; + else + x[8] = p[8]/100.0; + } + } + } +} + +DiagonalCurve::~DiagonalCurve () { + + delete [] x; + delete [] y; + delete [] ypp; + poly_x.clear(); + poly_y.clear(); +} + +void DiagonalCurve::spline_cubic_set () { + + double* u = new double[N-1]; + delete [] ypp; + ypp = new double [N]; + + ypp[0] = u[0] = 0.0; /* set lower boundary condition to "natural" */ + + for (int i = 1; i < N - 1; ++i) { + double sig = (x[i] - x[i - 1]) / (x[i + 1] - x[i - 1]); + double p = sig * ypp[i - 1] + 2.0; + ypp[i] = (sig - 1.0) / p; + u[i] = ((y[i + 1] - y[i]) + / (x[i + 1] - x[i]) - (y[i] - y[i - 1]) / (x[i] - x[i - 1])); + u[i] = (6.0 * u[i] / (x[i + 1] - x[i - 1]) - sig * u[i - 1]) / p; + } + + ypp[N - 1] = 0.0; + for (int k = N - 2; k >= 0; --k) + ypp[k] = ypp[k] * ypp[k + 1] + u[k]; + + delete [] u; +} + +void DiagonalCurve::NURBS_set () { + + int nbSubCurvesPoints = N + (N-3)*2; + + std::vector sc_x(nbSubCurvesPoints); // X sub-curve points ( XP0,XP1,XP2, XP2,XP3,XP4, ...) + std::vector sc_y(nbSubCurvesPoints); // Y sub-curve points ( YP0,YP1,YP2, YP2,YP3,YP4, ...) + std::vector sc_length(N+2); // Length of the subcurves + double total_length=0.; + + // Create the list of Bezier sub-curves + // NURBS_set is called if N > 2 only + + int j = 0; + int k = 0; + for (int i = 0; i < N-1;) { + double length; + double dx; + double dy; + + // first point (on the curve) + if (!i) { + sc_x[j] = x[i]; + sc_y[j++] = y[i++]; + } + else { + sc_x[j] = (x[i-1] + x[i]) / 2.; + sc_y[j++] = (y[i-1] + y[i]) / 2.; + } + + // second point (control point) + sc_x[j] = x[i]; + sc_y[j] = y[i++]; + + dx = sc_x[j] - sc_x[j-1]; + dy = sc_y[j] - sc_y[j-1]; + length = sqrt(dx*dx + dy*dy); + j++; + + // third point (on the curve) + if (i==N-1) { + sc_x[j] = x[i]; + sc_y[j] = y[i]; + } + else { + sc_x[j] = (x[i-1] + x[i]) / 2.; + sc_y[j] = (y[i-1] + y[i]) / 2.; + } + dx = sc_x[j] - sc_x[j-1]; + dy = sc_y[j] - sc_y[j-1]; + length += sqrt(dx*dx + dy*dy); + j++; + + // Storing the length of all sub-curves and the total length (to have a better distribution + // of the points along the curve) + sc_length[k++] = length; + total_length += length; + } + + poly_x.clear(); + poly_y.clear(); + unsigned int sc_xsize=j-1; + j = 0; + // create the polyline with the number of points adapted to the X range of the sub-curve + for (unsigned int i=0; i < sc_xsize /*sc_x.size()*/; i+=3) { + // TODO: Speeding-up the interface by caching the polyline, instead of rebuilding it at each action on sliders !!! + nbr_points = (int)(((double)(ppn+N-2) * sc_length[i/3] )/ total_length); + if (nbr_points<0){ + for(int it=0;it < sc_x.size(); it+=3) printf("sc_length[%d/3]=%f \n",it,sc_length[it/3]); + printf("NURBS: error detected!\n i=%d nbr_points=%d ppn=%d N=%d sc_length[i/3]=%f total_length=%f",i,nbr_points,ppn,N,sc_length[i/3],total_length); + exit(0); + } + // increment along the curve, not along the X axis + increment = 1.0 / (double)(nbr_points-1); + x1 = sc_x[i]; y1 = sc_y[i]; + x2 = sc_x[i+1]; y2 = sc_y[i+1]; + x3 = sc_x[i+2]; y3 = sc_y[i+2]; + firstPointIncluded = !i; + AddPolygons (); + } +} + +double DiagonalCurve::getVal (double t) { + + switch (kind) { + + case DCT_Empty : + return t; + break; + + case DCT_Parametric : { + if (t<=1e-14) + return 0.0; + double c = -log(2.0)/log(x[2]); + double tv = exp(c*log(t)); + double base = pfull (tv, x[8], x[6], x[5]); + double stretched = base<=1e-14 ? 0.0 : exp(log(base)/c); + + base = pfull (0.5, x[8], x[6], x[5]); + double fc = base<=1e-14 ? 0.0 : exp(log(base)/c); // value of the curve at the center point + if (tx[N-1]) + return y[N-1]; + else if (t 1){ + int k = (k_hi + k_lo) / 2; + if (x[k] > t) + k_hi = k; + else + k_lo = k; + } + + double h = x[k_hi] - x[k_lo]; + // linear + if (kind==DCT_Linear) + return y[k_lo] + (t - x[k_lo]) * ( y[k_hi] - y[k_lo] ) / h; + // spline curve + else { // if (kind==Spline) { + double a = (x[k_hi] - t) / h; + double b = (t - x[k_lo]) / h; + double r = a*y[k_lo] + b*y[k_hi] + ((a*a*a - a)*ypp[k_lo] + (b*b*b - b)*ypp[k_hi]) * (h*h)/6.0; + return CLIPD(r); + } + break; + } + case DCT_NURBS : { + // values under and over the first and last point + if (t>x[N-1]) + return y[N-1]; + else if (t 1){ + int k = (k_hi + k_lo) / 2; + if (poly_x[k] > t) + k_hi = k; + else + k_lo = k; + } + + double h = poly_x[k_hi] - poly_x[k_lo]; + return poly_y[k_lo] + (t - poly_x[k_lo]) * ( poly_y[k_hi] - poly_y[k_lo] ) / h; + break; + } + default: + // all other (unknown) kind + return t; + } +} + +void DiagonalCurve::getVal (const std::vector& t, std::vector& res) { + +// TODO!!!! can be made much faster!!! Binary search of getVal(double) at each point can be avoided + + res.resize (t.size()); + for (unsigned int i=0; i + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace rtengine { + +FlatCurve::FlatCurve (const std::vector& p, int poly_pn) : leftTangent(NULL), rightTangent(NULL) { + + ppn = poly_pn; + poly_x.clear(); + poly_y.clear(); + + kind = FCT_Empty; + + if (p.size()>4) { + kind = (FlatCurveType)p[0]; + if (kind==FCT_MinMaxCPoints) { + N = (p.size()-1)/4; + x = new double[N+1]; + y = new double[N+1]; + leftTangent = new double[N+1]; + rightTangent = new double[N+1]; + int ix = 1; + for (int i=0; i 1) + CtrlPoints_set (); + } + /*else if (kind==FCT_Parametric) { + }*/ + } +} + +FlatCurve::~FlatCurve () { + + delete [] x; + delete [] y; + delete [] leftTangent; + delete [] rightTangent; + delete [] ypp; + poly_x.clear(); + poly_y.clear(); +} + +void FlatCurve::CtrlPoints_set () { + + int nbSubCurvesPoints = N*6; + + std::vector sc_x(nbSubCurvesPoints); // X sub-curve points ( XP0,XP1,XP2, XP2,XP3,XP4, ...) + std::vector sc_y(nbSubCurvesPoints); // Y sub-curve points ( YP0,YP1,YP2, YP2,YP3,YP4, ...) + std::vector sc_length(N*2); // Length of the subcurves + std::vector sc_isLinear(N*2); // true if the subcurve is linear + double total_length=0.; + + // Create the list of Bezier sub-curves + // CtrlPoints_set is called if N > 1 + + unsigned int j = 0; + unsigned int k = 0; + + for (int i = 0; i < N;) { + double length; + double dx; + double dy; + double xp1, xp2, yp2, xp3; + bool startLinear, endLinear; + + startLinear = (rightTangent[i] == 0.) || (y[i] == y[i+1]); + endLinear = (leftTangent [i+1] == 0.) || (y[i] == y[i+1]); + + if (startLinear && endLinear) { + // line shape + sc_x[j] = x[i]; + sc_y[j++] = y[i]; + sc_x[j] = x[i+1]; + sc_y[j] = y[i+1]; + sc_isLinear[k] = true; + i++; + + dx = sc_x[j] - sc_x[j-1]; + dy = sc_y[j] - sc_y[j-1]; + length = sqrt(dx*dx + dy*dy); + j++; + + // Storing the length of all sub-curves and the total length (to have a better distribution + // of the points along the curve) + sc_length[k++] = length; + total_length += length; + } + else { + if (startLinear) { + xp1 = x[i]; + } + else { + //xp1 = (xp4 - xp0) * rightTangent0 + xp0; + xp1 = (x[i+1] - x[i]) * rightTangent[i] + x[i]; + } + if (endLinear) { + xp3 = x[i+1]; + } + else { + //xp3 = (xp0 - xp4]) * leftTangent4 + xp4; + xp3 = (x[i] - x[i+1]) * leftTangent[i+1] + x[i+1]; + } + + xp2 = (xp1 + xp3) / 2.0; + yp2 = (y[i] + y[i+1]) / 2.0; + + if (rightTangent[i]+leftTangent[i+1] > 1.0) { // also means that start and end are not linear + xp1 = xp3 = xp2; + } + + if (startLinear) { + // Point 0, 2 + sc_x[j] = x[i]; + sc_y[j++] = y[i]; + sc_x[j] = xp2; + sc_y[j] = yp2; + sc_isLinear[k] = true; + + dx = sc_x[j] - sc_x[j-1]; + dy = sc_y[j] - sc_y[j-1]; + length = sqrt(dx*dx + dy*dy); + j++; + + // Storing the length of all sub-curves and the total length (to have a better distribution + // of the points along the curve) + sc_length[k++] = length; + total_length += length; + } + else { + // Point 0, 1, 2 + sc_x[j] = x[i]; + sc_y[j++] = y[i]; + sc_x[j] = xp1; + sc_y[j] = y[i]; + + dx = sc_x[j] - sc_x[j-1]; + dy = sc_y[j] - sc_y[j-1]; + length = sqrt(dx*dx + dy*dy); + j++; + + sc_x[j] = xp2; + sc_y[j] = yp2; + sc_isLinear[k] = false; + + dx = sc_x[j] - sc_x[j-1]; + dy = sc_y[j] - sc_y[j-1]; + length += sqrt(dx*dx + dy*dy); + j++; + + // Storing the length of all sub-curves and the total length (to have a better distribution + // of the points along the curve) + sc_length[k++] = length; + total_length += length; + } + if (endLinear) { + // Point 2, 4 + sc_x[j] = xp2; + sc_y[j++] = yp2; + sc_x[j] = x[i+1]; + sc_y[j] = y[i+1]; + sc_isLinear[k] = true; + + dx = sc_x[j] - sc_x[j-1]; + dy = sc_y[j] - sc_y[j-1]; + length = sqrt(dx*dx + dy*dy); + j++; + + // Storing the length of all sub-curves and the total length (to have a better distribution + // of the points along the curve) + sc_length[k++] = length; + total_length += length; + } + else { + // Point 2, 3, 4 + sc_x[j] = xp2; + sc_y[j++] = yp2; + sc_x[j] = xp3; + sc_y[j] = y[i+1]; + + dx = sc_x[j] - sc_x[j-1]; + dy = sc_y[j] - sc_y[j-1]; + length = sqrt(dx*dx + dy*dy); + j++; + + sc_x[j] = x[i+1]; + sc_y[j] = y[i+1]; + sc_isLinear[k] = false; + + dx = sc_x[j] - sc_x[j-1]; + dy = sc_y[j] - sc_y[j-1]; + length += sqrt(dx*dx + dy*dy); + j++; + + // Storing the length of all sub-curves and the total length (to have a better distribution + // of the points along the curve) + sc_length[k++] = length; + total_length += length; + } + i++; + } + } + + poly_x.clear(); + poly_y.clear(); + j = 0; + + // very first point of the curve + poly_x.push_back(sc_x[j]); + poly_y.push_back(sc_y[j]); + + firstPointIncluded = false; + + // create the polyline with the number of points adapted to the X range of the sub-curve + for (unsigned int i=0; i < k; i++) { + if (sc_isLinear[i]) { + j++; // skip the first point + poly_x.push_back(sc_x[j]); + poly_y.push_back(sc_y[j++]); + } + else { + nbr_points = (int)(((double)(ppn) * sc_length[i] )/ total_length); + if (nbr_points<0){ + for(unsigned int it=0;it < sc_x.size(); it+=3) printf("sc_length[%d/3]=%f \n",it,sc_length[it/3]); + printf("Flat curve: error detected!\n i=%d nbr_points=%d ppn=%d N=%d sc_length[i/3]=%f total_length=%f",i,nbr_points,ppn,N,sc_length[i/3],total_length); + exit(0); + } + // increment along the curve, not along the X axis + increment = 1.0 / (double)(nbr_points-1); + x1 = sc_x[j]; y1 = sc_y[j++]; + x2 = sc_x[j]; y2 = sc_y[j++]; + x3 = sc_x[j]; y3 = sc_y[j++]; + AddPolygons (); + } + } + + /* + // Checking the values + Glib::ustring fname = "Curve.xyz"; // TopSolid'Design "plot" file format + std::ofstream f (fname.c_str()); + f << "$" << std::endl;; + for (unsigned int iter = 0; iter < poly_x.size(); iter++) { + f << poly_x[iter] << ", " << poly_y[iter] << ", 0." << std::endl;; + } + f << "$" << std::endl;; + f.close (); + */ +} + +double FlatCurve::getVal (double t) { + + switch (kind) { + + case FCT_MinMaxCPoints : { + /* To be updated if we have to handle non periodic flat curves + // values under and over the first and last point + if (t>x[N-1]) + return y[N-1]; + else if (t 1){ + int k = (k_hi + k_lo) / 2; + if (poly_x[k] > t) + k_hi = k; + else + k_lo = k; + } + + double h = poly_x[k_hi] - poly_x[k_lo]; + return poly_y[k_lo] + (t - poly_x[k_lo]) * ( poly_y[k_hi] - poly_y[k_lo] ) / h; + break; + } + /*case Parametric : { + break; + }*/ + case FCT_Empty : + case FCT_Linear : + default: + return t; + } +} + +void FlatCurve::getVal (const std::vector& t, std::vector& res) { + +// TODO!!!! can be made much faster!!! Binary search of getVal(double) at each point can be avoided + + res.resize (t.size()); + for (unsigned int i=0; i #include #include +#include #ifdef _OPENMP #include @@ -294,16 +295,28 @@ void ImProcFunctions::rgbProc (Image16* working, LabImage* lab, float* hltonecur int tH = working->height; int r, g, b; float h, s, v; - float satparam,valparam; - int hue, hueband, hueres, nbrband; double pi = M_PI; + FlatCurve* hCurve; + FlatCurve* sCurve; + FlatCurve* vCurve; float* cossq = new float [8093]; for (int i=0; i<8093; i++) cossq[i] = SQR(cos(pi*(float)i/16384)); - -#pragma omp parallel for private(r, g, b,factor,mapval,h,s,v,hue,hueband,hueres,nbrband,satparam,valparam) if (multiThread) + FlatCurveType hCurveType = (FlatCurveType)params->hsvequalizer.hcurve.at(0); + FlatCurveType sCurveType = (FlatCurveType)params->hsvequalizer.scurve.at(0); + FlatCurveType vCurveType = (FlatCurveType)params->hsvequalizer.vcurve.at(0); + bool hCurveEnabled = hCurveType > FCT_Linear; + bool sCurveEnabled = sCurveType > FCT_Linear; + bool vCurveEnabled = vCurveType > FCT_Linear; + + // TODO: We should create a 'skip' value like for CurveFactory::complexsgnCurve (rtengine/curves.cc) + if (hCurveEnabled) hCurve = new FlatCurve(params->hsvequalizer.hcurve); + if (sCurveEnabled) sCurve = new FlatCurve(params->hsvequalizer.scurve); + if (vCurveEnabled) vCurve = new FlatCurve(params->hsvequalizer.vcurve); + +#pragma omp parallel for private(r, g, b,factor,mapval,h,s,v) if (multiThread) for (int i=0; i0.5 || params->hsvequalizer.enabled) { + if (abs(sat)>0.5 || hCurveEnabled || sCurveEnabled || vCurveEnabled) { rgb2hsv(r,g,b,h,s,v); if (sat > 0.5) { s = (1-(float)sat/100)*s+(float)sat/100*(1-SQR(SQR(1-s))); @@ -372,44 +385,51 @@ void ImProcFunctions::rgbProc (Image16* working, LabImage* lab, float* hltonecur s *= 1+(float)sat/100; } //HSV equalizer - if (params->hsvequalizer.enabled) { - hue = (int)(65535*h); - hueres = hue & 8091;//location of hue within a band - hueband = (hue-hueres) >> 13;//divides hue range into 8 bands - nbrband = (hueband+1)&7; - - //shift hue - h = fmod(h + 0.0025*(params->hsvequalizer.hue[hueband] * cossq[hueres] + params->hsvequalizer.hue[nbrband] * (1-cossq[hueres])),1); - if (h<0) h +=1; - hue = (int)(65535*h); - hueres = hue & 8091;//location of hue within a band - hueband = (hue-hueres) >> 13;//divides hue range into 8 bands - nbrband = (hueband+1)&7; - - //change saturation - satparam = 0.01*(params->hsvequalizer.sat[hueband] * cossq[hueres] + params->hsvequalizer.sat[nbrband] * (1-cossq[hueres])); + if (hCurveEnabled) { + h = (hCurve->getVal((double)h) - 0.5) * 2 + h; + if (h > 1.0) + h -= 1.0; + else if (h < 0.0) + h += 1.0; + } + if (sCurveEnabled) { + //shift saturation + float satparam = (sCurve->getVal((double)h)-0.5) * 2; if (satparam > 0.00001) { s = (1-satparam)*s+satparam*(1-SQR(1-s)); } else { if (satparam < -0.00001) - s *= 1+satparam; + s *= 1+satparam; } - - //change value - valparam = 0.005*(params->hsvequalizer.val[hueband] * cossq[hueres] + params->hsvequalizer.val[nbrband] * (1-cossq[hueres])); + + /*s = sCurve->getVal((double)s); + if (s > 1.0) + s -= 1.0; + else if (s < 0.0) + s += 1.0;*/ + } + if (vCurveEnabled) { + //shift value + float valparam = vCurve->getVal((double)h)-0.5; valparam *= (1-SQR(SQR(1-s))); if (valparam > 0.00001) { v = (1-valparam)*v+valparam*(1-SQR(1-v)); } else { if (valparam < -0.00001) - v *= (1+valparam); + v *= (1+valparam); } + + /*v = vCurve->getVal((double)v); + if (v > 1.0) + v -= 1.0; + else if (v < 0.0) + v += 1.0;*/ } hsv2rgb(h,s,v,r,g,b); } //hsv2rgb(h,s,v,r,g,b); - + int x = (toxyz[0][0] * r + toxyz[1][0] * g + toxyz[2][0] * b) >> 15; int y = (toxyz[0][1] * r + toxyz[1][1] * g + toxyz[2][1] * b) >> 15; int z = (toxyz[0][2] * r + toxyz[1][2] * g + toxyz[2][2] * b) >> 15; @@ -425,6 +445,9 @@ void ImProcFunctions::rgbProc (Image16* working, LabImage* lab, float* hltonecur } } + if (hCurveEnabled) delete hCurve; + if (sCurveEnabled) delete sCurve; + if (vCurveEnabled) delete vCurve; delete [] cossq; //delete [] my_tonecurve; } @@ -677,60 +700,5 @@ void ImProcFunctions::getAutoExp (unsigned int* histogram, int histcompr, doubl if (br>10) br=10; } -//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -void ImProcFunctions::rgb2hsv (int r, int g, int b, float &h, float &s, float &v) { - - double var_R = r / 65535.0; - double var_G = g / 65535.0; - double var_B = b / 65535.0; - - double var_Min = MIN(MIN(var_R,var_G),var_B); - double var_Max = MAX(MAX(var_R,var_G),var_B); - double del_Max = var_Max - var_Min; - v = var_Max; - if (fabs(del_Max)<0.00001) { - h = 0; - s = 0; - } - else { - s = del_Max/var_Max; - - if ( var_R == var_Max ) h = (var_G - var_B)/del_Max; - else if ( var_G == var_Max ) h = 2.0 + (var_B - var_R)/del_Max; - else if ( var_B == var_Max ) h = 4.0 + (var_R - var_G)/del_Max; - h /= 6.0; - - if ( h < 0 ) h += 1; - if ( h > 1 ) h -= 1; - } -} - -//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -void ImProcFunctions::hsv2rgb (float h, float s, float v, int &r, int &g, int &b) { - - float h1 = h*6; // sector 0 to 5 - int i = floor( h1 ); - float f = h1 - i; // fractional part of h - - float p = v * ( 1 - s ); - float q = v * ( 1 - s * f ); - float t = v * ( 1 - s * ( 1 - f ) ); - - float r1,g1,b1; - - if (i==0) {r1 = v; g1 = t; b1 = p;} - if (i==1) {r1 = q; g1 = v; b1 = p;} - if (i==2) {r1 = p; g1 = v; b1 = t;} - if (i==3) {r1 = p; g1 = q; b1 = v;} - if (i==4) {r1 = t; g1 = p; b1 = v;} - if (i==5) {r1 = v; g1 = p; b1 = q;} - - r = (int)((r1)*65535); - g = (int)((g1)*65535); - b = (int)((b1)*65535); -} - } diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index 7c1934e85..293e51810 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -121,9 +121,6 @@ class ImProcFunctions { bool transCoord (int W, int H, std::vector &src, std::vector &red, std::vector &green, std::vector &blue, double ascaleDef = -1); void getAutoExp (unsigned int* histogram, int histcompr, double expcomp, double clip, double& br, int& bl); double getTransformAutoFill (int oW, int oH); - - void rgb2hsv (int r, int g, int b, float &h, float &s, float &v); - void hsv2rgb (float h, float s, float v, int &r, int &g, int &b); }; } #endif diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 7905fd87c..62947b3f4 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -24,6 +24,7 @@ #include #include #include +#include #include @@ -200,13 +201,12 @@ void ProcParams::setDefaults () { dirpyrequalizer.mult[i] = 1.0; } dirpyrequalizer.mult[4] = 0.0; - hsvequalizer.enabled = false; - for(int i = 0; i < 8; i ++) - { - hsvequalizer.sat[i] = 0; - hsvequalizer.val[i] = 0; - hsvequalizer.hue[i] = 0; - } + hsvequalizer.hcurve.clear (); + hsvequalizer.hcurve.push_back (FCT_Linear); + hsvequalizer.scurve.clear (); + hsvequalizer.scurve.push_back (FCT_Linear); + hsvequalizer.vcurve.clear (); + hsvequalizer.vcurve.push_back (FCT_Linear); raw.df_autoselect = false; raw.cared = 0; raw.cablue = 0; @@ -219,10 +219,10 @@ void ProcParams::setDefaults () { raw.dcb_iterations=2; raw.dcb_enhance=false; //exposition -raw.expos=1.0; -raw.preser=0.0; -//raw.expos_correc=false; -// expos + raw.expos=1.0; + raw.preser=0.0; + //raw.expos_correc=false; + // expos exif.clear (); iptc.clear (); @@ -417,27 +417,15 @@ int ProcParams::save (Glib::ustring fname) const { keyFile.set_double("Directional Pyramid Equalizer", ss.str(), dirpyrequalizer.mult[i]); } - // save hsv equalizer parameters - keyFile.set_boolean ("HSV Equalizer", "Enabled", hsvequalizer.enabled); - keyFile.set_string ("HSV Equalizer", "Channel", hsvequalizer.hsvchannel); - for(int i = 0; i < 8; i++) - { - std::stringstream ss; - ss << "Sat" << i; - keyFile.set_double("HSV Equalizer", ss.str(), hsvequalizer.sat[i]); - } - for(int i = 0; i < 8; i++) - { - std::stringstream ss; - ss << "Val" << i; - keyFile.set_double("HSV Equalizer", ss.str(), hsvequalizer.val[i]); - } - for(int i = 0; i < 8; i++) - { - std::stringstream ss; - ss << "Hue" << i; - keyFile.set_double("HSV Equalizer", ss.str(), hsvequalizer.hue[i]); - } + // save hsv equalizer parameters + //keyFile.set_boolean ("HSV Equalizer", "Enabled", hsvequalizer.enabled); + //keyFile.set_string ("HSV Equalizer", "Channel", hsvequalizer.hsvchannel); + Glib::ArrayHandle hcurve = hsvequalizer.hcurve; + Glib::ArrayHandle scurve = hsvequalizer.scurve; + Glib::ArrayHandle vcurve = hsvequalizer.vcurve; + keyFile.set_double_list("HSV Equalizer", "HCurve", hcurve); + keyFile.set_double_list("HSV Equalizer", "SCurve", scurve); + keyFile.set_double_list("HSV Equalizer", "VCurve", vcurve); // save RAW parameters keyFile.set_string ("RAW", "DarkFrame", raw.dark_frame ); @@ -742,27 +730,13 @@ if (keyFile.has_group ("Directional Pyramid Equalizer")) { if(keyFile.has_key ("Directional Pyramid Equalizer", ss.str())) dirpyrequalizer.mult[i] = keyFile.get_double ("Directional Pyramid Equalizer", ss.str()); } } - - // load wavelet equalizer parameters + + // load HSV equalizer parameters if (keyFile.has_group ("HSV Equalizer")) { - if (keyFile.has_key ("HSV Equalizer", "Enabled")) hsvequalizer.enabled = keyFile.get_boolean ("HSV Equalizer", "Enabled"); - for(int i = 0; i < 8; i ++) - { - std::stringstream ss; - ss << "Sat" << i; - if(keyFile.has_key ("HSV Equalizer", ss.str())) hsvequalizer.sat[i] = keyFile.get_double ("HSV Equalizer", ss.str()); - } - for(int i = 0; i < 8; i ++) - { - std::stringstream ss; - ss << "Val" << i; - if(keyFile.has_key ("HSV Equalizer", ss.str())) hsvequalizer.val[i] = keyFile.get_double ("HSV Equalizer", ss.str()); - } - for(int i = 0; i < 8; i ++) - { - std::stringstream ss; - ss << "Hue" << i; - if(keyFile.has_key ("HSV Equalizer", ss.str())) hsvequalizer.hue[i] = keyFile.get_double ("HSV Equalizer", ss.str()); + if (ppVersion>=300) { + if (keyFile.has_key ("HSV Equalizer", "HCurve")) hsvequalizer.hcurve = keyFile.get_double_list ("HSV Equalizer", "HCurve"); + if (keyFile.has_key ("HSV Equalizer", "SCurve")) hsvequalizer.scurve = keyFile.get_double_list ("HSV Equalizer", "SCurve"); + if (keyFile.has_key ("HSV Equalizer", "VCurve")) hsvequalizer.vcurve = keyFile.get_double_list ("HSV Equalizer", "VCurve"); } } @@ -841,17 +815,6 @@ bool operator==(const DirPyrEqualizerParams & a, const DirPyrEqualizerParams & b return true; } -bool operator==(const HSVEqualizerParams & a, const HSVEqualizerParams & b) { - if(a.enabled != b.enabled) - return false; - - for(int i = 0; i < 8; i++) { - if(a.sat[i] != b.sat[i] && a.val[i] != b.val[i] && a.hue[i] != b.hue[i]) - return false; - } - return true; -} - bool operator==(const ExifPair& a, const ExifPair& b) { return a.field == b.field && a.value == b.value; @@ -984,8 +947,10 @@ bool ProcParams::operator== (const ProcParams& other) { && icm.working == other.icm.working && icm.output == other.icm.output && equalizer == other.equalizer - && dirpyrequalizer == other.dirpyrequalizer - && hsvequalizer == other.hsvequalizer + && dirpyrequalizer == other.dirpyrequalizer + && hsvequalizer.hcurve == other.hsvequalizer.hcurve + && hsvequalizer.scurve == other.hsvequalizer.scurve + && hsvequalizer.vcurve == other.hsvequalizer.vcurve && exif==other.exif && iptc==other.iptc && raw.expos==other.raw.expos // exposi diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 02fbd4381..d7ad4d609 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -370,16 +370,16 @@ class DirPyrEqualizerParams { }; /** - * Wavelet equalizer params + * HSV equalizer params */ class HSVEqualizerParams { public: - bool enabled; - Glib::ustring hsvchannel; - int sat[8]; - int val[8]; - int hue[8]; + //bool enabled; + //Glib::ustring hsvchannel; + std::vector hcurve; + std::vector scurve; + std::vector vcurve; }; /** diff --git a/rtengine/utils.cc b/rtengine/utils.cc index cea88e7fa..a4d4da0ba 100644 --- a/rtengine/utils.cc +++ b/rtengine/utils.cc @@ -17,9 +17,16 @@ * along with RawTherapee. If not, see . */ #include +#include #include #include +#undef MAX +#undef MIN + +#define MAX(a,b) ((a)<(b)?(b):(a)) +#define MIN(a,b) ((a)>(b)?(b):(a)) + namespace rtengine { void bilinearInterp (const unsigned char* src, int sw, int sh, unsigned char* dst, int dw, int dh) { @@ -139,6 +146,77 @@ void vflip (unsigned char* img, int w, int h) { delete [] flipped; } +void rgb2hsv (int r, int g, int b, float &h, float &s, float &v) { + + double var_R = r / 65535.0; + double var_G = g / 65535.0; + double var_B = b / 65535.0; + + double var_Min = MIN(MIN(var_R,var_G),var_B); + double var_Max = MAX(MAX(var_R,var_G),var_B); + double del_Max = var_Max - var_Min; + v = var_Max; + if (fabs(del_Max)<0.00001) { + h = 0; + s = 0; + } + else { + s = del_Max/var_Max; + + if ( var_R == var_Max ) h = (var_G - var_B)/del_Max; + else if ( var_G == var_Max ) h = 2.0 + (var_B - var_R)/del_Max; + else if ( var_B == var_Max ) h = 4.0 + (var_R - var_G)/del_Max; + h /= 6.0; + + if ( h < 0 ) h += 1; + if ( h > 1 ) h -= 1; + } +} + +void hsv2rgb (float h, float s, float v, int &r, int &g, int &b) { + + float h1 = h*6; // sector 0 to 5 + int i = floor( h1 ); + float f = h1 - i; // fractional part of h + + float p = v * ( 1 - s ); + float q = v * ( 1 - s * f ); + float t = v * ( 1 - s * ( 1 - f ) ); + + float r1,g1,b1; + + if (i==0) {r1 = v; g1 = t; b1 = p;} + if (i==1) {r1 = q; g1 = v; b1 = p;} + if (i==2) {r1 = p; g1 = v; b1 = t;} + if (i==3) {r1 = p; g1 = q; b1 = v;} + if (i==4) {r1 = t; g1 = p; b1 = v;} + if (i==5) {r1 = v; g1 = p; b1 = q;} + + r = (int)((r1)*65535); + g = (int)((g1)*65535); + b = (int)((b1)*65535); +} + +// The same function but set float values intead if int +// Function copied for speed concerns +void hsv2rgb (float h, float s, float v, float &r, float &g, float &b) { + + float h1 = h*6; // sector 0 to 5 + int i = floor( h1 ); + float f = h1 - i; // fractional part of h + + float p = v * ( 1 - s ); + float q = v * ( 1 - s * f ); + float t = v * ( 1 - s * ( 1 - f ) ); + + if (i==0) {r = v; g = t; b = p;} + if (i==1) {r = q; g = v; b = p;} + if (i==2) {r = p; g = v; b = t;} + if (i==3) {r = p; g = q; b = v;} + if (i==4) {r = t; g = p; b = v;} + if (i==5) {r = v; g = p; b = q;} +} + } diff --git a/rtengine/utils.h b/rtengine/utils.h index d33c8dc76..58c633591 100644 --- a/rtengine/utils.h +++ b/rtengine/utils.h @@ -26,6 +26,9 @@ void nearestInterp (const unsigned char* src, int sw, int sh, unsigned char* dst void rotate (unsigned char* img, int& w, int& h, int deg); void hflip (unsigned char* img, int w, int h); void vflip (unsigned char* img, int w, int h); +void rgb2hsv (int r, int g, int b, float &h, float &s, float &v); +void hsv2rgb (float h, float s, float v, int &r, int &g, int &b); +void hsv2rgb (float h, float s, float v, float &r, float &g, float &b); } #endif diff --git a/rtgui/CMakeLists.txt b/rtgui/CMakeLists.txt index 75ab8387f..b2ae816e3 100644 --- a/rtgui/CMakeLists.txt +++ b/rtgui/CMakeLists.txt @@ -12,12 +12,13 @@ set (BASESOURCEFILES exifpanel.cc toolpanel.cc sharpening.cc whitebalance.cc vignetting.cc rotate.cc distortion.cc - crophandler.cc curveeditorgroup.cc curveeditor.cc dirbrowser.cc + crophandler.cc dirbrowser.cc + curveeditor.cc curveeditorgroup.cc diagonalcurveeditorsubgroup.cc flatcurveeditorsubgroup.cc filecatalog.cc previewloader.cc histogrampanel.cc history.cc imagearea.cc imageareapanel.cc iptcpanel.cc labcurve.cc lumadenoise.cc main.cc - multilangmgr.cc mycurve.cc options.cc + multilangmgr.cc mycurve.cc myflatcurve.cc mydiagonalcurve.cc options.cc preferences.cc profilepanel.cc safegtk.cc saveasdlg.cc saveformatpanel.cc splash.cc thumbnail.cc tonecurve.cc toolbar.cc diff --git a/rtgui/colorprovider.h b/rtgui/colorprovider.h new file mode 100644 index 000000000..d4b44aa79 --- /dev/null +++ b/rtgui/colorprovider.h @@ -0,0 +1,33 @@ +/* + * 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 . + */ +#ifndef _COLORPROVIDER_ +#define _COLORPROVIDER_ + +class ColorProvider { + + public: + double red; + double green; + double blue; + + virtual void colorForValue (double valX, double valY) {} + +}; + +#endif diff --git a/rtgui/cursormanager.cc b/rtgui/cursormanager.cc index d79255a67..dd6a754e6 100644 --- a/rtgui/cursormanager.cc +++ b/rtgui/cursormanager.cc @@ -30,6 +30,8 @@ void CursorManager::init (Glib::RefPtr mainWin) { cCropMove = new Gdk::Cursor (Gdk::FLEUR); cCropMoving = new Gdk::Cursor (Gdk::HAND2); cCropSelection = new Gdk::Cursor (Gdk::CROSSHAIR); + cLeftTanMove = new Gdk::Cursor (Gdk::SB_LEFT_ARROW); + cRightTanMove = new Gdk::Cursor (Gdk::SB_RIGHT_ARROW); cAdd = new Gdk::Cursor (Gdk::PLUS); Glib::RefPtr hand = safe_create_from_file(argv0+"/images/openhand22.png"); @@ -50,7 +52,7 @@ void CursorManager::setCursor (Glib::RefPtr window, CursorShape sha if (shape==CSArrow) // set_cursor without any arguments to select system default - window->set_cursor (); + window->set_cursor (); else if (shape==CSOpenHand) window->set_cursor (*cHand); else if (shape==CSClosedHand) @@ -67,6 +69,10 @@ void CursorManager::setCursor (Glib::RefPtr window, CursorShape sha window->set_cursor (*cWB); else if (shape==CSCropSelect) window->set_cursor (*cCropSelection); + else if (shape==CSMoveLeft) + window->set_cursor (*cLeftTanMove); + else if (shape==CSMoveRight) + window->set_cursor (*cRightTanMove); else if (shape==CSStraighten) window->set_cursor (*cCropSelection); else if (shape==CSPlus) diff --git a/rtgui/cursormanager.h b/rtgui/cursormanager.h index d1bc6c05f..25038cd6d 100644 --- a/rtgui/cursormanager.h +++ b/rtgui/cursormanager.h @@ -21,7 +21,7 @@ #include -enum CursorShape {CSArrow, CSOpenHand, CSClosedHand, CSMove, CSResizeWidth, CSResizeHeight, CSResizeDiagonal, CSSpotWB, CSCropSelect, CSStraighten, CSPlus, CSEmpty}; +enum CursorShape {CSArrow, CSOpenHand, CSClosedHand, CSMove, CSMoveLeft, CSMoveRight, CSResizeWidth, CSResizeHeight, CSResizeDiagonal, CSSpotWB, CSCropSelect, CSStraighten, CSPlus, CSEmpty}; class CursorManager { @@ -31,6 +31,8 @@ class CursorManager { Gdk::Cursor* cResizeDiag; Gdk::Cursor* cCropMove; Gdk::Cursor* cCropMoving; + Gdk::Cursor* cLeftTanMove; + Gdk::Cursor* cRightTanMove; Gdk::Cursor* cNormal; Gdk::Cursor* cCropSelection; Gdk::Cursor* cAdd; diff --git a/rtgui/curveeditor.cc b/rtgui/curveeditor.cc index 6d584e2fb..09c7b0cd7 100644 --- a/rtgui/curveeditor.cc +++ b/rtgui/curveeditor.cc @@ -25,6 +25,58 @@ extern Glib::ustring argv0; +DiagonalCurveEditor::DiagonalCurveEditor (Glib::ustring text, CurveEditorGroup* ceGroup, CurveEditorSubGroup* ceSubGroup) : CurveEditor::CurveEditor(text, (CurveEditorGroup*) ceGroup, ceSubGroup) { + + // Order set in the same order than "enum DiagonalCurveType". Shouldn't change, for compatibility reason + curveType->addEntry(argv0+"/images/curveType-linear.png", M("CURVEEDITOR_LINEAR")); // 0 Linear + curveType->addEntry(argv0+"/images/curveType-spline.png", M("CURVEEDITOR_CUSTOM")); // 1 Spline + curveType->addEntry(argv0+"/images/curveType-parametric.png", M("CURVEEDITOR_PARAMETRIC")); // 2 Parametric + curveType->addEntry(argv0+"/images/curveType-NURBS.png", M("CURVEEDITOR_NURBS")); // 3 NURBS + curveType->setSelected(DCT_Linear); + curveType->show(); +} + +std::vector DiagonalCurveEditor::getCurve () { + std::vector curve; + + switch (selected) { + case (DCT_Spline): + return curve = customCurveEd; + case (DCT_Parametric): + return curve = paramCurveEd; + case (DCT_NURBS): + return curve = NURBSCurveEd; + default: + // returning Linear or Unchanged + curve.push_back((double)(selected)); + return curve; + } +} + +FlatCurveEditor::FlatCurveEditor (Glib::ustring text, CurveEditorGroup* ceGroup, CurveEditorSubGroup* ceSubGroup) : CurveEditor::CurveEditor(text, (CurveEditorGroup*) ceGroup, ceSubGroup) { + + // Order set in the same order than "enum FlatCurveType". Shouldn't change, for compatibility reason + curveType->addEntry(argv0+"/images/curveType-flatLinear.png", M("CURVEEDITOR_LINEAR")); // 0 Linear + curveType->addEntry(argv0+"/images/curveType-controlPoints.png", M("CURVEEDITOR_MINMAXCPOINTS")); // 1 Min/Max ControlPoints + curveType->setSelected(FCT_Linear); + curveType->show(); +} + +std::vector FlatCurveEditor::getCurve () { + std::vector 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; + } +} + /* * CurveEditor (CurveEditorGroup* ceGroup, Glib::ustring text) * @@ -32,32 +84,25 @@ extern Glib::ustring argv0; * 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) { +CurveEditor::CurveEditor (Glib::ustring text, CurveEditorGroup* ceGroup, CurveEditorSubGroup* ceSubGroup) { bgHistValid = false; - selected = Linear; + selected = DCT_Linear; histogram = new unsigned int[256]; // histogram values group = ceGroup; + subGroup = ceSubGroup; if (group && text.size()) curveType = Gtk::manage (new PopUpToggleButton(text + ":")); else curveType = Gtk::manage (new PopUpToggleButton()); - // Order set in the same order than "enum CurveType". Shouldn't change, for compatibility reason - curveType->addEntry(argv0+"/images/curveType-linear.png", M("CURVEEDITOR_LINEAR")); // 0 Linear - curveType->addEntry(argv0+"/images/curveType-spline.png", M("CURVEEDITOR_CUSTOM")); // 1 Spline - curveType->addEntry(argv0+"/images/curveType-parametric.png", M("CURVEEDITOR_PARAMETRIC")); // 2 Parametric - curveType->addEntry(argv0+"/images/curveType-NURBS.png", M("CURVEEDITOR_NURBS")); // 3 NURBS - curveType->setSelected(Linear); 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_changed().connect (sigc::mem_fun(*this, &CurveEditor::typeSelectionChanged) ); - - curveType->show(); } CurveEditor::~CurveEditor () { @@ -65,29 +110,11 @@ CurveEditor::~CurveEditor () { delete [] histogram; } - void CurveEditor::setCurve (const std::vector& p) { tempCurve = p; group->setCurveExternal(this, p); } -std::vector CurveEditor::getCurve () { - std::vector curve; - - switch (selected) { - case (Spline): - return curve = customCurveEd; - case (Parametric): - return curve = paramCurveEd; - case (NURBS): - return curve = NURBSCurveEd; - default: - // returning Linear or Unchanged - curve.push_back((double)(selected)); - return curve; - } -} - void CurveEditor::typeSelectionChanged (int n) { group->typeSelectionChanged(this, n); } @@ -97,7 +124,7 @@ void CurveEditor::curveTypeToggled() { } bool CurveEditor::isUnChanged () { - return curveType->getSelected()==Unchanged; + return curveType->getSelected()==subGroup->getValUnchanged(); } void CurveEditor::setUnChanged (bool uc) { @@ -117,5 +144,5 @@ void CurveEditor::updateBackgroundHistogram (unsigned int* hist) { bgHistValid = false; // Then call the curve editor group to eventually update the histogram - group->updateBackgroundHistogram (this); + subGroup->updateBackgroundHistogram (this); } diff --git a/rtgui/curveeditor.h b/rtgui/curveeditor.h index 6f0ef6e9a..66c6c999a 100644 --- a/rtgui/curveeditor.h +++ b/rtgui/curveeditor.h @@ -19,12 +19,16 @@ #ifndef _CURVEEDITOR_ #define _CURVEEDITOR_ -#include #include -#include -#include class CurveEditorGroup; +class CurveEditorSubGroup; + + +/* + *********************** Curve Editor *********************** + */ + /* * This class is an interface between RT and the curve editor group ; it handles the methods @@ -32,40 +36,83 @@ class CurveEditorGroup; */ class CurveEditor { -private: - - /* - * The curve editor contains only one widget (the curve type button) to receive the signals - * but it's co-handled by the CurveEditorGroup too - */ - - // reflects the buttonType active selection ; used as a pre-'selectionChange' reminder value - CurveType selected; - - PopUpToggleButton* curveType; - unsigned int* histogram; // histogram values - bool bgHistValid; - - CurveEditorGroup* group; - std::vector tempCurve; - std::vector customCurveEd; - std::vector paramCurveEd; - std::vector NURBSCurveEd; - sigc::connection typeconn; - -public: - friend class CurveEditorGroup; - CurveEditor (Glib::ustring text, CurveEditorGroup* ceGroup); - ~CurveEditor (); - void typeSelectionChanged (int n); - void curveTypeToggled(); - void setCurve (const std::vector& p); - std::vector getCurve (); - bool isUnChanged (); - void setUnChanged (bool uc); - void updateBackgroundHistogram (unsigned int* hist); + friend class CurveEditorSubGroup; + friend class DiagonalCurveEditorSubGroup; + friend class FlatCurveEditorSubGroup; + friend class DiagonalCurveEditor; + friend class FlatCurveEditor; + + protected: + + /* + * The curve editor contains only one widget (the curve type button) to receive the signals + * but it's co-handled by the CurveEditorGroup too + */ + + PopUpToggleButton* curveType; + unsigned int* histogram; // histogram values + bool bgHistValid; + + int selected; + + CurveEditorGroup* group; + CurveEditorSubGroup* subGroup; + + std::vector tempCurve; + sigc::connection typeconn; + + public: + + CurveEditor (Glib::ustring text, CurveEditorGroup* ceGroup, CurveEditorSubGroup* ceSubGroup); + ~CurveEditor (); + void typeSelectionChanged (int n); + void curveTypeToggled(); + bool isUnChanged (); + void setUnChanged (bool uc); + void updateBackgroundHistogram (unsigned int* hist); + void setCurve (const std::vector& p); + virtual std::vector getCurve () = 0; }; +/* + ******************** Diagonal Curve Editor ******************** + */ + + +class DiagonalCurveEditor : public CurveEditor { + + friend class DiagonalCurveEditorSubGroup; + + protected: + // reflects the buttonType active selection ; used as a pre-'selectionChange' reminder value + std::vector customCurveEd; + std::vector paramCurveEd; + std::vector NURBSCurveEd; + + public: + DiagonalCurveEditor (Glib::ustring text, CurveEditorGroup* ceGroup, CurveEditorSubGroup* ceSubGroup); + std::vector getCurve (); +}; + + +/* + ********************** Flat Curve Editor ********************** + */ + + +class FlatCurveEditor : public CurveEditor { + + friend class FlatCurveEditorSubGroup; + + protected: + // reflects the buttonType active selection ; used as a pre-'selectionChange' reminder value + std::vector controlPointsCurveEd; + + public: + FlatCurveEditor (Glib::ustring text, CurveEditorGroup* ceGroup, CurveEditorSubGroup* ceSubGroup); + std::vector getCurve (); +}; + #endif diff --git a/rtgui/curveeditorgroup.cc b/rtgui/curveeditorgroup.cc index 848a6ada1..4e094d63b 100644 --- a/rtgui/curveeditorgroup.cc +++ b/rtgui/curveeditorgroup.cc @@ -18,144 +18,24 @@ * * Class created by Jean-Christophe FRISCH, aka 'Hombre' */ + +#include #include +#include +#include #include extern Glib::ustring argv0; -CurveEditorGroup::CurveEditorGroup (Glib::ustring groupLabel) : cl(NULL), activeParamControl(-1) { +CurveEditorGroup::CurveEditorGroup (Glib::ustring groupLabel) : cl(NULL), cp(NULL) { curveEditors.clear(); displayedCurve = 0; numberOfPackedCurve = 0; + flatSubGroup = 0; + diagonalSubGroup = 0; // We set the label to the one provided as parameter, even if it's an empty string curveGroupLabel = Gtk::manage (new Gtk::Label (groupLabel+":", Gtk::ALIGN_LEFT)); - - // custom curve - customCurveBox = new Gtk::HBox (); - Gtk::HBox* tmpa = Gtk::manage (new Gtk::HBox ()); - customCurve = Gtk::manage (new MyCurve ()); - //Gtk::AspectFrame* af = Gtk::manage (new Gtk::AspectFrame ("",Gtk::ALIGN_CENTER,Gtk::ALIGN_CENTER,1,false)); - //af->add (*customCurve); - customCurve->set_size_request (GRAPH_SIZE+2*RADIUS, GRAPH_SIZE+2*RADIUS); - customCurve->setType (Spline); - //customCurve->set_tooltip_text (M("CURVEEDITOR_TOOLTIPMOVESPEED")); - tmpa->pack_start (*customCurve, true, false, 4); - customCurveBox->pack_start (*tmpa, true, true,4); - - Gtk::VBox* custombbox = Gtk::manage (new Gtk::VBox ()); - saveCustom = Gtk::manage (new Gtk::Button ()); - saveCustom->add (*Gtk::manage (new Gtk::Image (Gtk::StockID("gtk-save"), Gtk::ICON_SIZE_BUTTON))); - loadCustom = Gtk::manage (new Gtk::Button ()); - loadCustom->add (*Gtk::manage (new Gtk::Image (Gtk::StockID("gtk-open"), Gtk::ICON_SIZE_BUTTON))); - - custombbox->pack_end (*saveCustom, Gtk::PACK_SHRINK, 4); - custombbox->pack_end (*loadCustom, Gtk::PACK_SHRINK, 4); - - customCurveBox->pack_end (*custombbox, Gtk::PACK_SHRINK, 0); - customCurveBox->show_all (); - - saveCustom->signal_clicked().connect( sigc::mem_fun(*this, &CurveEditorGroup::savePressed) ); - loadCustom->signal_clicked().connect( sigc::mem_fun(*this, &CurveEditorGroup::loadPressed) ); - saveCustom->set_tooltip_text (M("CURVEEDITOR_TOOLTIPSAVE")); - loadCustom->set_tooltip_text (M("CURVEEDITOR_TOOLTIPLOAD")); - - // NURBS curve - NURBSCurveBox = new Gtk::HBox (); - Gtk::HBox* tmpb = Gtk::manage (new Gtk::HBox ()); - NURBSCurve = Gtk::manage (new MyCurve ()); - //Gtk::AspectFrame* af = Gtk::manage (new Gtk::AspectFrame ("",Gtk::ALIGN_CENTER,Gtk::ALIGN_CENTER,1,false)); - //af->add (*customCurve); - NURBSCurve->set_size_request (GRAPH_SIZE+2*RADIUS, GRAPH_SIZE+2*RADIUS); - NURBSCurve->setType (NURBS); - //customCurve->set_tooltip_text (M("CURVEEDITOR_TOOLTIPMOVESPEED")); - tmpb->pack_start (*NURBSCurve, true, false, 4); - NURBSCurveBox->pack_start (*tmpb, true, true,4); - - Gtk::VBox* NURBSbbox = Gtk::manage (new Gtk::VBox ()); - saveNURBS = Gtk::manage (new Gtk::Button ()); - saveNURBS->add (*Gtk::manage (new Gtk::Image (Gtk::StockID("gtk-save"), Gtk::ICON_SIZE_BUTTON))); - loadNURBS = Gtk::manage (new Gtk::Button ()); - loadNURBS->add (*Gtk::manage (new Gtk::Image (Gtk::StockID("gtk-open"), Gtk::ICON_SIZE_BUTTON))); - - NURBSbbox->pack_end (*saveNURBS, Gtk::PACK_SHRINK, 4); - NURBSbbox->pack_end (*loadNURBS, Gtk::PACK_SHRINK, 4); - - NURBSCurveBox->pack_end (*NURBSbbox, Gtk::PACK_SHRINK, 0); - NURBSCurveBox->show_all (); - - saveNURBS->signal_clicked().connect( sigc::mem_fun(*this, &CurveEditorGroup::savePressed) ); - loadNURBS->signal_clicked().connect( sigc::mem_fun(*this, &CurveEditorGroup::loadPressed) ); - saveNURBS->set_tooltip_text (M("CURVEEDITOR_TOOLTIPSAVE")); - loadNURBS->set_tooltip_text (M("CURVEEDITOR_TOOLTIPLOAD")); - - // parametric curve - paramCurveBox = new Gtk::VBox (); - paramCurve = Gtk::manage (new MyCurve ()); - Gtk::Table* paramctab = Gtk::manage (new Gtk::Table (2,1)); - //Gtk::AspectFrame* afp = Gtk::manage (new Gtk::AspectFrame ("",Gtk::ALIGN_CENTER,Gtk::ALIGN_CENTER,1,false)); - //afp->add (*paramCurve); - paramCurve->set_size_request (GRAPH_SIZE+2*RADIUS, GRAPH_SIZE+2*RADIUS); - paramCurve->setType (Parametric); - shcSelector = Gtk::manage (new SHCSelector ()); - shcSelector->set_size_request (GRAPH_SIZE, 20); - - paramctab->attach (*paramCurve, 0, 1, 0, 1, Gtk::FILL, Gtk::SHRINK, 4, 4); - paramctab->attach (*shcSelector, 0, 1, 1, 2, Gtk::FILL, Gtk::SHRINK, RADIUS+4, 0); - - Gtk::HBox* tmpc = Gtk::manage (new Gtk::HBox ()); - tmpc->pack_start (*paramctab, true, false); - - paramCurveBox->pack_start (*tmpc, true, true); - - highlights = Gtk::manage (new Adjuster (M("CURVEEDITOR_HIGHLIGHTS"), -100, 100, 1, 0)); - lights = Gtk::manage (new Adjuster (M("CURVEEDITOR_LIGHTS"), -100, 100, 1, 0)); - darks = Gtk::manage (new Adjuster (M("CURVEEDITOR_DARKS"), -100, 100, 1, 0)); - shadows = Gtk::manage (new Adjuster (M("CURVEEDITOR_SHADOWS"), -100, 100, 1, 0)); - - Gtk::EventBox* evhighlights = Gtk::manage (new Gtk::EventBox ()); - Gtk::EventBox* evlights = Gtk::manage (new Gtk::EventBox ()); - Gtk::EventBox* evdarks = Gtk::manage (new Gtk::EventBox ()); - Gtk::EventBox* evshadows = Gtk::manage (new Gtk::EventBox ()); - - evhighlights->add (*highlights); - evlights->add (*lights); - evdarks->add (*darks); - evshadows->add (*shadows); - - paramCurveBox->pack_start (*Gtk::manage (new Gtk::HSeparator ())); - paramCurveBox->pack_start (*evhighlights); - paramCurveBox->pack_start (*evlights); - paramCurveBox->pack_start (*evdarks); - paramCurveBox->pack_start (*evshadows); - paramCurveBox->show_all (); - - customCurveBox->reference (); - paramCurveBox->reference (); - - customCurve->setCurveListener (this); - NURBSCurve->setCurveListener (this); - paramCurve->setCurveListener (this); - shcSelector->setSHCListener (this); - - highlights->setAdjusterListener (this); - lights->setAdjusterListener (this); - darks->setAdjusterListener (this); - shadows->setAdjusterListener (this); - - evhighlights->set_events(Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK); - evlights->set_events(Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK); - evdarks->set_events(Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK); - evshadows->set_events(Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK); - evhighlights->signal_enter_notify_event().connect (sigc::bind(sigc::mem_fun(*this, &CurveEditorGroup::adjusterEntered), 4)); - evlights->signal_enter_notify_event().connect (sigc::bind(sigc::mem_fun(*this, &CurveEditorGroup::adjusterEntered), 5)); - evdarks->signal_enter_notify_event().connect (sigc::bind(sigc::mem_fun(*this, &CurveEditorGroup::adjusterEntered), 6)); - evshadows->signal_enter_notify_event().connect (sigc::bind(sigc::mem_fun(*this, &CurveEditorGroup::adjusterEntered), 7)); - evhighlights->signal_leave_notify_event().connect (sigc::bind(sigc::mem_fun(*this, &CurveEditorGroup::adjusterLeft), 4)); - evlights->signal_leave_notify_event().connect (sigc::bind(sigc::mem_fun(*this, &CurveEditorGroup::adjusterLeft), 5)); - evdarks->signal_leave_notify_event().connect (sigc::bind(sigc::mem_fun(*this, &CurveEditorGroup::adjusterLeft), 6)); - evshadows->signal_leave_notify_event().connect (sigc::bind(sigc::mem_fun(*this, &CurveEditorGroup::adjusterLeft), 7)); - } CurveEditorGroup::~CurveEditorGroup() { @@ -163,9 +43,8 @@ CurveEditorGroup::~CurveEditorGroup() { { delete *i; } - delete customCurveBox; - delete paramCurveBox; - delete NURBSCurveBox; + delete flatSubGroup; + delete diagonalSubGroup; } void CurveEditorGroup::hideCurrentCurve() { @@ -177,17 +56,22 @@ void CurveEditorGroup::hideCurrentCurve() { /* * Add a new curve to the curves list */ -CurveEditor* CurveEditorGroup::addCurve(Glib::ustring curveLabel) { - CurveEditor* newCE = new CurveEditor(curveLabel, this); - - // Initialization of the new curve - storeCurveValues(newCE, getCurveFromGUI(Spline)); - storeCurveValues(newCE, getCurveFromGUI(Parametric)); - storeCurveValues(newCE, getCurveFromGUI(NURBS)); - - // We add it to the curve editor list - curveEditors.push_back(newCE); - return newCE; +CurveEditor* CurveEditorGroup::addCurve(CurveType cType, Glib::ustring curveLabel) { + switch (cType) { + case (CT_Diagonal): + if (!diagonalSubGroup) { + diagonalSubGroup = new DiagonalCurveEditorSubGroup(this); + } + return (CurveEditor*)diagonalSubGroup->addCurve(curveLabel); + case (CT_Flat): + if (!flatSubGroup) { + flatSubGroup = new FlatCurveEditorSubGroup(this); + } + return (CurveEditor*)flatSubGroup->addCurve(curveLabel); + default: + return (CurveEditor*)NULL; + break; + } } /* @@ -233,6 +117,12 @@ void CurveEditorGroup::newLine() { void CurveEditorGroup::curveListComplete() { newLine(); + // Set the color provider + if (cp) { + if (flatSubGroup) flatSubGroup->setColorProvider(cp); + if (diagonalSubGroup) diagonalSubGroup->setColorProvider(cp); + } + // We check the length of the label ; if it contains only one char (':'), we set it to the right default string if (curveGroupLabel->get_label().size()==1) curveGroupLabel->set_label(M(curveEditors.size() > 1 ? "CURVEEDITOR_CURVES" : "CURVEEDITOR_CURVE") + ":"); @@ -247,11 +137,11 @@ void CurveEditorGroup::curveListComplete() { */ void CurveEditorGroup::typeSelectionChanged (CurveEditor* ce, int n) { // Same type : do nothing - if (ce==displayedCurve && (CurveType)n==ce->selected) + if (ce==displayedCurve && n==(int)ce->selected) return; - if ((CurveType)(n)selected = (CurveType)n; + if (nsubGroup->valUnchanged) + ce->selected = n; // The user selected a new type from a toggled off button if (ce!=displayedCurve) @@ -260,11 +150,11 @@ void CurveEditorGroup::typeSelectionChanged (CurveEditor* ce, int n) { // If the button was not pressed before if (!ce->curveType->get_active()) { - storeDisplayedCurve(); + ce->subGroup->storeDisplayedCurve(); // We set it pressed : it will emit the toggle on signal and update the GUI - ce->curveType->set_active( n>Linear && ncurveType->set_active( n>ce->subGroup->valLinear && nsubGroup->valUnchanged ); + if (n==ce->subGroup->valLinear || n==ce->subGroup->valUnchanged) { + // Since we do not activate the curve when the user switch the toggled off button to 'Linear', we have to // to call the curve listener manually, because 'curveChanged' uses displayedCurve... if (cl) { if (cl->isMulti()) @@ -278,7 +168,7 @@ void CurveEditorGroup::typeSelectionChanged (CurveEditor* ce, int n) { } else { // The button is already pressed so we switch the GUI ourselves - switchGUI(); + ce->subGroup->switchGUI(); curveChanged (); } } @@ -297,21 +187,21 @@ void CurveEditorGroup::curveTypeToggled(CurveEditor* ce) { displayedCurve = ce; - if (ce->curveType->getSelected()==Unchanged) { + if (ce->curveType->getSelected()==ce->subGroup->valUnchanged) { curveRestored = true; ce->curveType->setSelected(ce->selected); } // then show this CurveEditor int ct = ce->curveType->getSelected(); - if (ct < Unchanged) - restoreDisplayedHistogram(); + if (ct < ce->subGroup->valUnchanged) + ce->subGroup->restoreDisplayedHistogram(); } else { // The button is now released, so we have to hide this CurveEditor displayedCurve = 0; } - switchGUI(); + ce->subGroup->switchGUI(); if (curveRestored) curveChanged (); @@ -319,49 +209,88 @@ void CurveEditorGroup::curveTypeToggled(CurveEditor* ce) { } /* - * Switch the editor widgets to the currently edited curve + * Update the GUI if the given curveEditor is currently displayed */ -void CurveEditorGroup::switchGUI() { +void CurveEditorGroup::updateGUI (CurveEditor* ce) { + if (!ce) { + return; + } - removeEditor(); + // we update the curve type button to the corresponding curve type, only if it is not currently set to 'Unchanged' + if (ce->curveType->getSelected()subGroup->valUnchanged) + ce->curveType->setSelected(ce->selected); + // if not displayed or "unchanged" is selected, do not change gui + if (ce==displayedCurve && ce->curveType->getSelected()subGroup->valUnchanged) { + ce->subGroup->switchGUI(); + } +} + +/* + * Called from the outside to set the curve type & values + */ +void CurveEditorGroup::setCurveExternal (CurveEditor* ce, const std::vector& c) { + if (c.size()) { + ce->subGroup->storeCurveValues(ce, c); // The new curve is saved in the CurveEditor + (ce)->selected = c[0]; // We set the selected curve type in the CurveEditor to the one of the specified curve + } + updateGUI((CurveEditor*)ce); // And we update the GUI if necessary +} + +/* + * Listener called when the user has modified the curve + */ +void CurveEditorGroup::curveChanged () { + + displayedCurve->subGroup->storeDisplayedCurve(); + if (cl) { + if (cl->isMulti()) + cl->curveChanged (displayedCurve); + else + cl->curveChanged (); + } +} + +/* + * Call back method when the reset button is pressed : + * reset the currently toggled on curve editor + */ +void CurveEditorGroup::curveResetPressed () { if (displayedCurve) { - - // Initializing GUI values + repacking the appropriated widget - //displayedCurve->typeconn.block(true); - - switch((CurveType)(displayedCurve->curveType->getSelected())) { - case (Spline): - customCurve->setPoints (displayedCurve->customCurveEd); - pack_start (*customCurveBox); - break; - case (Parametric): - paramCurve->setPoints (displayedCurve->paramCurveEd); - shcSelector->setPositions ( - displayedCurve->paramCurveEd.at(1), - displayedCurve->paramCurveEd.at(2), - displayedCurve->paramCurveEd.at(3) - ); - highlights->setValue (displayedCurve->paramCurveEd.at(4)); - lights->setValue (displayedCurve->paramCurveEd.at(5)); - darks->setValue (displayedCurve->paramCurveEd.at(6)); - shadows->setValue (displayedCurve->paramCurveEd.at(7)); - pack_start (*paramCurveBox); - break; - case (NURBS): - NURBSCurve->setPoints (displayedCurve->NURBSCurveEd); - pack_start (*NURBSCurveBox); - break; - default: // (Linear, Unchanged) - // ... do nothing - break; + if (displayedCurve->subGroup->curveReset(displayedCurve->selected)) { + curveChanged(); } - - //displayedCurve->typeconn.block(false); } } -void CurveEditorGroup::savePressed () { +void CurveEditorGroup::setBatchMode (bool batchMode) { + for (std::vector::iterator i = curveEditors.begin(); i != curveEditors.end(); ++i) { + (*i)->curveType->addEntry(argv0+"/images/curveType-unchanged.png", M("GENERAL_UNCHANGED")); + (*i)->curveType->show(); + } +} + +void CurveEditorGroup::setUnChanged (bool uc, CurveEditor* ce) { + if (uc) { + // the user selected several thumbnails, so we hide the editors and set the curveEditor selection to 'Unchanged' + //ce->typeconn.block(true); + // we hide the editor widgets + hideCurrentCurve(); + // the curve type selected option is set to unchanged + ce->curveType->setSelected(ce->subGroup->valUnchanged); + //ce->typeconn.block(false); + } + else { + // we want it to use back the 'CurveEditor::setCurve' memorized in CurveEditor::tempCurve + //ce->typeconn.block(true); + // we switch back the curve type selected option to the one of the used curve + ce->curveType->setSelected(ce->selected); + updateGUI (ce); + //ce->typeconn.block(false); + } +} + +Glib::ustring CurveEditorSubGroup::outputFile () { Gtk::FileChooserDialog dialog(M("CURVEEDITOR_SAVEDLGLABEL"), Gtk::FILE_CHOOSER_ACTION_SAVE); // if (options.multiUser) @@ -384,52 +313,37 @@ void CurveEditorGroup::savePressed () { dialog.set_do_overwrite_confirmation (true); - int result = dialog.run(); + Glib::ustring fname; + do { + int result = dialog.run(); - if (result==Gtk::RESPONSE_OK) { + fname = dialog.get_filename(); - std::string fname = dialog.get_filename(); + if (result==Gtk::RESPONSE_OK) { - if (getExtension (fname)!="rtc") - fname = fname + ".rtc"; + if (getExtension (fname)!="rtc") + fname = fname + ".rtc"; - if (Glib::file_test (fname, Glib::FILE_TEST_EXISTS)) { - Glib::ustring msg_ = Glib::ustring("") + fname + ": " + M("MAIN_MSG_ALREADYEXISTS") + "\n" + M("MAIN_MSG_QOVERWRITE") + ""; - Gtk::MessageDialog msgd (msg_, true, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_YES_NO, true); - int response = msgd.run (); - if (response==Gtk::RESPONSE_NO) - return; - } + if (Glib::file_test (fname, Glib::FILE_TEST_EXISTS)) { + Glib::ustring msg_ = Glib::ustring("") + fname + ": " + M("MAIN_MSG_ALREADYEXISTS") + "\n" + M("MAIN_MSG_QOVERWRITE") + ""; + Gtk::MessageDialog msgd (msg_, true, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_YES_NO, true); + int response = msgd.run (); + if (response==Gtk::RESPONSE_YES) + break; + } + else + break; + } + else { + fname = ""; + break; + } + } while (1); - std::ofstream f (fname.c_str()); - std::vector p = customCurve->getPoints (); - - switch (displayedCurve->selected) { - case Spline: // custom - p = customCurve->getPoints (); - break; - case NURBS: // NURBS - p = NURBSCurve->getPoints (); - break; - default: - break; - } - - int ix = 0; - if (p[ix]==(double)(Linear)) - f << "Linear\n"; - else if (p[ix]==(double)(Spline)) - f << "Spline\n"; - else if (p[ix]==(double)(NURBS)) - f << "NURBS\n"; - ix++; - for (unsigned int i=0; i p; - std::string s; - f >> s; - if (s=="Linear") - p.push_back ((double)(Linear)); - else if (s=="Spline") - p.push_back ((double)(Spline)); - else if (s=="NURBS") - p.push_back ((double)(NURBS)); - else return; - double x; - while (f) { - f >> x; - if (f) - p.push_back (x); - } - if (p[0] == (double)(Spline)) { - customCurve->setPoints (p); - customCurve->queue_draw (); - customCurve->notifyListener (); - } - else if (p[0] == (double)(NURBS)) { - NURBSCurve->setPoints (p); - NURBSCurve->queue_draw (); - NURBSCurve->notifyListener (); - } - } + fname = dialog.get_filename(); + if (Glib::file_test (fname, Glib::FILE_TEST_EXISTS)) + return fname; } -} - -/* - * Store the curves of the currently displayed type from the widgets to the CurveEditor object - */ -void CurveEditorGroup::storeDisplayedCurve() { - if (displayedCurve) { - switch (displayedCurve->selected) { - case (Spline): - storeCurveValues(displayedCurve, getCurveFromGUI(Spline)); - break; - case (Parametric): - storeCurveValues(displayedCurve, getCurveFromGUI(Parametric)); - break; - case (NURBS): - storeCurveValues(displayedCurve, getCurveFromGUI(NURBS)); - break; - default: - break; - } - } -} - -/* - * Restore the histogram to all types from the CurveEditor object to the widgets - */ -void CurveEditorGroup::restoreDisplayedHistogram() { - if (displayedCurve) { - paramCurve->updateBackgroundHistogram (displayedCurve->bgHistValid ? displayedCurve->histogram : NULL); - customCurve->updateBackgroundHistogram (displayedCurve->bgHistValid ? displayedCurve->histogram : NULL); - NURBSCurve->updateBackgroundHistogram (displayedCurve->bgHistValid ? displayedCurve->histogram : NULL); - } - -} - -void CurveEditorGroup::storeCurveValues (CurveEditor* ce, const std::vector& p) { - if (p.size()) { - CurveType t = (CurveType)p[0]; - for (int i=0; i<(int)p.size(); i++) - - switch (t) { - case (Spline): - ce->customCurveEd = p; - break; - case (Parametric): - ce->paramCurveEd = p; - break; - case (NURBS): - ce->NURBSCurveEd = p; - break; - default: - break; - } - } -} - -/* - * Update the GUI if the given curveEditor is currently displayed - */ -void CurveEditorGroup::updateGUI (CurveEditor* ce) { - if (!ce) { - return; - } - - // we update the curve type button to the corresponding curve type, only if it is not currently set to 'Unchanged' - if (ce->curveType->getSelected()curveType->setSelected(ce->selected); - - // if not displayed or "unchanged" is selected, do not change gui - if (ce==displayedCurve && ce->curveType->getSelected()& c) { - if (c.size()) { - storeCurveValues(ce, c); // The new curve is saved in the CurveEditor - ce->selected = (CurveType)(c[0]); // We set the selected curve type in the CurveEditor to the one of the specified curve - } - updateGUI(ce); // And we update the GUI if necessary -} - -/* - * Called to update the parametric curve graph with new slider values - */ -const std::vector CurveEditorGroup::getCurveFromGUI (CurveType type) { - switch (type) { - case (Parametric): { - std::vector lcurve (8); - lcurve[0] = (double)(Parametric); - shcSelector->getPositions (lcurve[1], lcurve[2], lcurve[3]); - lcurve[4] = highlights->getValue (); - lcurve[5] = lights->getValue (); - lcurve[6] = darks->getValue (); - lcurve[7] = shadows->getValue (); - return lcurve; - } - case (Spline): - return customCurve->getPoints (); - case (NURBS): - return NURBSCurve->getPoints (); - default: { - // linear and other solutions - std::vector lcurve (1); - lcurve[0] = (double)(Linear); - return lcurve; - } - } -} - -/* - * Unlink the tree editor widgets from their parent box to hide them - */ -void CurveEditorGroup::removeEditor () { - removeIfThere (this, customCurveBox, false); - removeIfThere (this, paramCurveBox, false); - removeIfThere (this, NURBSCurveBox, false); -} - -/* - * Listener called when the user has modified the curve - */ -void CurveEditorGroup::curveChanged () { - - storeDisplayedCurve(); - if (cl) { - if (cl->isMulti()) - cl->curveChanged (displayedCurve); - else - cl->curveChanged (); - } -} - -/* - * Listener - */ -void CurveEditorGroup::shcChanged () { - - paramCurve->setPoints (getCurveFromGUI(Parametric)); - storeDisplayedCurve(); - if (cl->isMulti()) - cl->curveChanged (displayedCurve); - else - cl->curveChanged (); -} - -/* - * Listener - */ -void CurveEditorGroup::adjusterChanged (Adjuster* a, double newval) { - - paramCurve->setPoints (getCurveFromGUI(Parametric)); - storeDisplayedCurve(); - if (cl->isMulti()) - cl->curveChanged (displayedCurve); - else - cl->curveChanged (); -} - -/* - * Listener called when the mouse is over a parametric curve's slider - */ -bool CurveEditorGroup::adjusterEntered (GdkEventCrossing* ev, int ac) { - - if (ev->detail != GDK_NOTIFY_INFERIOR) { - activeParamControl = ac; - paramCurve->setActiveParam (activeParamControl); - } - return true; -} - -/* - * Listener called when the mouse left the parametric curve's slider - */ -bool CurveEditorGroup::adjusterLeft (GdkEventCrossing* ev, int ac) { - - if (ev->detail != GDK_NOTIFY_INFERIOR) { - activeParamControl = -1; - paramCurve->setActiveParam (activeParamControl); - } - return true; -} - -/* - * Call back method when the reset button is pressed : - * reset the currently toggled on curve editor - */ -void CurveEditorGroup::curveResetPressed() { - if (displayedCurve) { - switch (displayedCurve->selected) { - case (NURBS) : // = Control cage - NURBSCurve->reset (); - curveChanged (); - break; - case (Spline) : // = Custom - customCurve->reset (); - curveChanged (); - break; - case (Parametric) : - highlights->resetPressed(); - lights->resetPressed(); - darks->resetPressed(); - shadows->resetPressed(); - shcSelector->reset(); - paramCurve->reset (); - curveChanged (); - break; - default: - break; - } - } -} - -void CurveEditorGroup::setBatchMode (bool batchMode) { - for (std::vector::iterator i = curveEditors.begin(); i != curveEditors.end(); ++i) { - (*i)->curveType->addEntry(argv0+"/images/curveType-unchanged.png", M("GENERAL_UNCHANGED")); - (*i)->curveType->show(); - } -} - -void CurveEditorGroup::setUnChanged (bool uc, CurveEditor* ce) { - if (uc) { - // the user selected several thumbnails, so we hide the editors and set the curveEditor selection to 'Unchanged' - //ce->typeconn.block(true); - // we hide the editor widgets - hideCurrentCurve(); - // the curve type selected option is set to unchanged - ce->curveType->setSelected(Unchanged); - //ce->typeconn.block(false); - } - else { - // we want it to use back the 'CurveEditor::setCurve' memorized in CurveEditor::tempCurve - //ce->typeconn.block(true); - // we switch back the curve type selected option to the one of the used curve - ce->curveType->setSelected(ce->selected); - updateGUI (ce); - //ce->typeconn.block(false); - } -} - -void CurveEditorGroup::updateBackgroundHistogram (CurveEditor* ce) { - if (ce==displayedCurve) { - paramCurve->updateBackgroundHistogram (ce->bgHistValid ? ce->histogram : NULL); - customCurve->updateBackgroundHistogram (ce->bgHistValid ? ce->histogram : NULL); - NURBSCurve->updateBackgroundHistogram (ce->bgHistValid ? ce->histogram : NULL); - } + fname = ""; + return fname; } diff --git a/rtgui/curveeditorgroup.h b/rtgui/curveeditorgroup.h index 19a361932..97a582e7c 100644 --- a/rtgui/curveeditorgroup.h +++ b/rtgui/curveeditorgroup.h @@ -24,9 +24,14 @@ #include #include #include +#include +#include #include #include -#include + +class CurveEditor; +class DiagonalCurveEditorSubGroup; +class FlatCurveEditorSubGroup; /* * This class handle the curve widgets, shared between any number curve @@ -34,75 +39,76 @@ * - to start a new line of curve button, use the 'newLine' method * - if you add more than one curve, you must add a "CurveEditor* ce" parameter to your listener */ -class CurveEditorGroup : public Gtk::VBox, public CurveListener, public SHCListener, public AdjusterListener { +class CurveEditorGroup : public Gtk::VBox, public CurveListener { -private: + friend class CurveEditor; + friend class CurveEditorSubGroup; + friend class DiagonalCurveEditorSubGroup; + friend class FlatCurveEditorSubGroup; + +protected: Gtk::Label* curveGroupLabel; - Gtk::Button* curve_reset; - Gtk::HBox* customCurveBox; - Gtk::VBox* paramCurveBox; - Gtk::HBox* NURBSCurveBox; - - MyCurve* customCurve; - MyCurve* paramCurve; - MyCurve* NURBSCurve; - - SHCSelector* shcSelector; - Adjuster* highlights; - Adjuster* lights; - Adjuster* darks; - Adjuster* shadows; - - Gtk::Button* saveCustom; - Gtk::Button* loadCustom; - Gtk::Button* saveNURBS; - Gtk::Button* loadNURBS; - - CurveListener* cl; - - CurveType curveTypeIx; - unsigned int numberOfPackedCurve; - std::vector curveEditors; CurveEditor* displayedCurve; + FlatCurveEditorSubGroup* flatSubGroup; + DiagonalCurveEditorSubGroup* diagonalSubGroup; - int activeParamControl; + CurveListener* cl; + ColorProvider* cp; - void curveResetPressed (); - void curveTypeToggled (); - void typeSelectionChanged (CurveEditor* ce, int n); - void curveTypeToggled (CurveEditor* ce); - void savePressed (); - void loadPressed (); - void hideCurrentCurve (); - void setUnChanged (bool uc, CurveEditor* ce); - void storeDisplayedCurve (); - void restoreDisplayedHistogram(); - void storeCurveValues (CurveEditor* ce, const std::vector& p); - void typeSelectionChanged (int n); - void switchGUI(); - void updateGUI (CurveEditor* ce); - void removeEditor (); - void curveChanged (); - void shcChanged (); - void adjusterChanged (Adjuster* a, double newval); - bool adjusterEntered (GdkEventCrossing* ev, int ac); - bool adjusterLeft (GdkEventCrossing* ev, int ac); - const std::vector getCurveFromGUI (CurveType type); - void updateBackgroundHistogram (CurveEditor* ce); + unsigned int numberOfPackedCurve; public: - friend class CurveEditor; CurveEditorGroup(Glib::ustring groupLabel = ""); ~CurveEditorGroup(); - CurveEditor* addCurve(Glib::ustring curveLabel = ""); void newLine(); void curveListComplete(); void setBatchMode (bool batchMode); void setCurveExternal (CurveEditor* ce, const std::vector& c); - //void on_realize (); void setCurveListener (CurveListener* l) { cl = l; } + void setColorProvider (ColorProvider* p) { cp = p; } + CurveEditor* getDisplayedCurve () { return displayedCurve; } + //void on_realize (); + CurveEditor* addCurve(CurveType cType, Glib::ustring curveLabel); + +protected: + //void curveTypeToggled (); + void curveTypeToggled (CurveEditor* ce); + //void typeSelectionChanged (int n); + void typeSelectionChanged (CurveEditor* ce, int n); + void hideCurrentCurve (); + void updateGUI (CurveEditor* ce); + void curveResetPressed (); + void curveChanged (); + void setUnChanged (bool uc, CurveEditor* ce); +}; + +class CurveEditorSubGroup { + + friend class CurveEditorGroup; + +protected: + int valLinear; + int valUnchanged; + CurveEditorGroup *parent; + +public: + int getValUnchanged() { return valUnchanged; } + virtual void updateBackgroundHistogram (CurveEditor* ce) {} + virtual void setColorProvider (ColorProvider* p) = 0; + +protected: + Glib::ustring outputFile (); + Glib::ustring inputFile (); + + virtual bool curveReset (int cType) = 0; // Reset a curve editor, return TRUE if successful (curve changed) + virtual void storeCurveValues (CurveEditor* ce, const std::vector& p) = 0; + virtual void storeDisplayedCurve () = 0; + virtual void restoreDisplayedHistogram() {}; + virtual void switchGUI() = 0; + virtual void removeEditor () = 0; + virtual const std::vector getCurveFromGUI (int type) = 0; }; #endif diff --git a/rtgui/diagonalcurveeditorsubgroup.cc b/rtgui/diagonalcurveeditorsubgroup.cc new file mode 100644 index 000000000..111ff93e5 --- /dev/null +++ b/rtgui/diagonalcurveeditorsubgroup.cc @@ -0,0 +1,474 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DiagonalCurveEditorSubGroup::DiagonalCurveEditorSubGroup (CurveEditorGroup* prt) { + + valLinear = (int)DCT_Linear; + valUnchanged = (int)DCT_Unchanged; + parent = prt; + + activeParamControl = -1; + + // custom curve + customCurveBox = new Gtk::HBox (); + Gtk::HBox* tmpa = Gtk::manage (new Gtk::HBox ()); + customCurve = Gtk::manage (new MyDiagonalCurve ()); + //Gtk::AspectFrame* af = Gtk::manage (new Gtk::AspectFrame ("",Gtk::ALIGN_CENTER,Gtk::ALIGN_CENTER,1,false)); + customCurve->set_size_request (GRAPH_SIZE+2*RADIUS, GRAPH_SIZE+2*RADIUS); + customCurve->setType (DCT_Spline); + //customCurve->set_tooltip_text (M("CURVEEDITOR_TOOLTIPMOVESPEED")); + tmpa->pack_start (*customCurve, true, false, 4); + customCurveBox->pack_start (*tmpa, true, true,4); + + Gtk::VBox* custombbox = Gtk::manage (new Gtk::VBox ()); + saveCustom = Gtk::manage (new Gtk::Button ()); + saveCustom->add (*Gtk::manage (new Gtk::Image (Gtk::StockID("gtk-save"), Gtk::ICON_SIZE_BUTTON))); + loadCustom = Gtk::manage (new Gtk::Button ()); + loadCustom->add (*Gtk::manage (new Gtk::Image (Gtk::StockID("gtk-open"), Gtk::ICON_SIZE_BUTTON))); + + custombbox->pack_end (*saveCustom, Gtk::PACK_SHRINK, 4); + custombbox->pack_end (*loadCustom, Gtk::PACK_SHRINK, 4); + + customCurveBox->pack_end (*custombbox, Gtk::PACK_SHRINK, 0); + customCurveBox->show_all (); + + saveCustom->signal_clicked().connect( sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::savePressed) ); + loadCustom->signal_clicked().connect( sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::loadPressed) ); + saveCustom->set_tooltip_text (M("CURVEEDITOR_TOOLTIPSAVE")); + loadCustom->set_tooltip_text (M("CURVEEDITOR_TOOLTIPLOAD")); + + // NURBS curve + NURBSCurveBox = new Gtk::HBox (); + Gtk::HBox* tmpb = Gtk::manage (new Gtk::HBox ()); + NURBSCurve = Gtk::manage (new MyDiagonalCurve ()); + //Gtk::AspectFrame* af = Gtk::manage (new Gtk::AspectFrame ("",Gtk::ALIGN_CENTER,Gtk::ALIGN_CENTER,1,false)); + //af->add (*customCurve); + NURBSCurve->set_size_request (GRAPH_SIZE+2*RADIUS, GRAPH_SIZE+2*RADIUS); + NURBSCurve->setType (DCT_NURBS); + //customCurve->set_tooltip_text (M("CURVEEDITOR_TOOLTIPMOVESPEED")); + tmpb->pack_start (*NURBSCurve, true, false, 4); + NURBSCurveBox->pack_start (*tmpb, true, true,4); + + Gtk::VBox* NURBSbbox = Gtk::manage (new Gtk::VBox ()); + saveNURBS = Gtk::manage (new Gtk::Button ()); + saveNURBS->add (*Gtk::manage (new Gtk::Image (Gtk::StockID("gtk-save"), Gtk::ICON_SIZE_BUTTON))); + loadNURBS = Gtk::manage (new Gtk::Button ()); + loadNURBS->add (*Gtk::manage (new Gtk::Image (Gtk::StockID("gtk-open"), Gtk::ICON_SIZE_BUTTON))); + + NURBSbbox->pack_end (*saveNURBS, Gtk::PACK_SHRINK, 4); + NURBSbbox->pack_end (*loadNURBS, Gtk::PACK_SHRINK, 4); + + NURBSCurveBox->pack_end (*NURBSbbox, Gtk::PACK_SHRINK, 0); + NURBSCurveBox->show_all (); + + saveNURBS->signal_clicked().connect( sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::savePressed) ); + loadNURBS->signal_clicked().connect( sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::loadPressed) ); + saveNURBS->set_tooltip_text (M("CURVEEDITOR_TOOLTIPSAVE")); + loadNURBS->set_tooltip_text (M("CURVEEDITOR_TOOLTIPLOAD")); + + // parametric curve + paramCurveBox = new Gtk::VBox (); + paramCurve = Gtk::manage (new MyDiagonalCurve ()); + Gtk::Table* paramctab = Gtk::manage (new Gtk::Table (2,1)); + //Gtk::AspectFrame* afp = Gtk::manage (new Gtk::AspectFrame ("",Gtk::ALIGN_CENTER,Gtk::ALIGN_CENTER,1,false)); + paramCurve->set_size_request (GRAPH_SIZE+2*RADIUS, GRAPH_SIZE+2*RADIUS); + paramCurve->setType (DCT_Parametric); + shcSelector = Gtk::manage (new SHCSelector ()); + shcSelector->set_size_request (GRAPH_SIZE, 20); + + paramctab->attach (*paramCurve, 0, 1, 0, 1, Gtk::FILL, Gtk::SHRINK, 4, 4); + paramctab->attach (*shcSelector, 0, 1, 1, 2, Gtk::FILL, Gtk::SHRINK, RADIUS+4, 0); + + Gtk::HBox* tmpc = Gtk::manage (new Gtk::HBox ()); + tmpc->pack_start (*paramctab, true, false); + + paramCurveBox->pack_start (*tmpc, true, true); + + highlights = Gtk::manage (new Adjuster (M("CURVEEDITOR_HIGHLIGHTS"), -100, 100, 1, 0)); + lights = Gtk::manage (new Adjuster (M("CURVEEDITOR_LIGHTS"), -100, 100, 1, 0)); + darks = Gtk::manage (new Adjuster (M("CURVEEDITOR_DARKS"), -100, 100, 1, 0)); + shadows = Gtk::manage (new Adjuster (M("CURVEEDITOR_SHADOWS"), -100, 100, 1, 0)); + + Gtk::EventBox* evhighlights = Gtk::manage (new Gtk::EventBox ()); + Gtk::EventBox* evlights = Gtk::manage (new Gtk::EventBox ()); + Gtk::EventBox* evdarks = Gtk::manage (new Gtk::EventBox ()); + Gtk::EventBox* evshadows = Gtk::manage (new Gtk::EventBox ()); + + evhighlights->add (*highlights); + evlights->add (*lights); + evdarks->add (*darks); + evshadows->add (*shadows); + + paramCurveBox->pack_start (*Gtk::manage (new Gtk::HSeparator ())); + paramCurveBox->pack_start (*evhighlights); + paramCurveBox->pack_start (*evlights); + paramCurveBox->pack_start (*evdarks); + paramCurveBox->pack_start (*evshadows); + paramCurveBox->show_all (); + + customCurveBox->reference (); + paramCurveBox->reference (); + + customCurve->setCurveListener (parent); // Send the message directly to the parent + NURBSCurve->setCurveListener (parent); + paramCurve->setCurveListener (parent); + shcSelector->setSHCListener (this); + + highlights->setAdjusterListener (this); + lights->setAdjusterListener (this); + darks->setAdjusterListener (this); + shadows->setAdjusterListener (this); + + evhighlights->set_events(Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK); + evlights->set_events(Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK); + evdarks->set_events(Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK); + evshadows->set_events(Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK); + evhighlights->signal_enter_notify_event().connect (sigc::bind(sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::adjusterEntered), 4)); + evlights->signal_enter_notify_event().connect (sigc::bind(sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::adjusterEntered), 5)); + evdarks->signal_enter_notify_event().connect (sigc::bind(sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::adjusterEntered), 6)); + evshadows->signal_enter_notify_event().connect (sigc::bind(sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::adjusterEntered), 7)); + evhighlights->signal_leave_notify_event().connect (sigc::bind(sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::adjusterLeft), 4)); + evlights->signal_leave_notify_event().connect (sigc::bind(sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::adjusterLeft), 5)); + evdarks->signal_leave_notify_event().connect (sigc::bind(sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::adjusterLeft), 6)); + evshadows->signal_leave_notify_event().connect (sigc::bind(sigc::mem_fun(*this, &DiagonalCurveEditorSubGroup::adjusterLeft), 7)); +} + +DiagonalCurveEditorSubGroup::~DiagonalCurveEditorSubGroup() { + delete customCurveBox; + delete paramCurveBox; + delete NURBSCurveBox; +} + +/* + * Add a new curve to the curves list + */ +DiagonalCurveEditor* DiagonalCurveEditorSubGroup::addCurve(Glib::ustring curveLabel) { + DiagonalCurveEditor* newCE = new DiagonalCurveEditor(curveLabel, parent, this); + + // Initialization of the new curve + storeCurveValues(newCE, getCurveFromGUI(DCT_Spline)); + storeCurveValues(newCE, getCurveFromGUI(DCT_Parametric)); + storeCurveValues(newCE, getCurveFromGUI(DCT_NURBS)); + + // We add it to the curve editor list + parent->curveEditors.push_back(newCE); + return newCE; +} + +/* + * Switch the editor widgets to the currently edited curve + */ +void DiagonalCurveEditorSubGroup::switchGUI() { + + removeEditor(); + + DiagonalCurveEditor* dCurve = (DiagonalCurveEditor*)(parent->displayedCurve); + + if (dCurve) { + + // Initializing GUI values + repacking the appropriated widget + //dCurve->typeconn.block(true); + + + switch((DiagonalCurveType)(dCurve->curveType->getSelected())) { + case (DCT_Spline): + customCurve->setPoints (dCurve->customCurveEd); + parent->pack_start (*customCurveBox); + break; + case (DCT_Parametric): + paramCurve->setPoints (dCurve->paramCurveEd); + shcSelector->setPositions ( + dCurve->paramCurveEd.at(1), + dCurve->paramCurveEd.at(2), + dCurve->paramCurveEd.at(3) + ); + highlights->setValue (dCurve->paramCurveEd.at(4)); + lights->setValue (dCurve->paramCurveEd.at(5)); + darks->setValue (dCurve->paramCurveEd.at(6)); + shadows->setValue (dCurve->paramCurveEd.at(7)); + parent->pack_start (*paramCurveBox); + break; + case (DCT_NURBS): + NURBSCurve->setPoints (dCurve->NURBSCurveEd); + parent->pack_start (*NURBSCurveBox); + break; + default: // (DCT_Linear, DCT_Unchanged) + // ... do nothing + break; + } + + //dCurve->typeconn.block(false); + } +} + +void DiagonalCurveEditorSubGroup::savePressed () { + + Glib::ustring fname = outputFile(); + if (fname.size()) { + std::ofstream f (fname.c_str()); + std::vector p; + //std::vector p = customCurve->getPoints (); + + switch (parent->displayedCurve->selected) { + case DCT_Spline: // custom + p = customCurve->getPoints (); + break; + case DCT_NURBS: // NURBS + p = NURBSCurve->getPoints (); + break; + default: + break; + } + + int ix = 0; + if (p[ix]==(double)(DCT_Linear)) + f << "Linear\n"; + else if (p[ix]==(double)(DCT_Spline)) + f << "Spline\n"; + else if (p[ix]==(double)(DCT_NURBS)) + f << "NURBS\n"; + ix++; + for (unsigned int i=0; i p; + std::string s; + f >> s; + if (s=="Linear") + p.push_back ((double)(DCT_Linear)); + else if (s=="Spline") + p.push_back ((double)(DCT_Spline)); + else if (s=="NURBS") + p.push_back ((double)(DCT_NURBS)); + else return; + double x; + while (f) { + f >> x; + if (f) + p.push_back (x); + } + if (p[0] == (double)(DCT_Spline)) { + customCurve->setPoints (p); + customCurve->queue_draw (); + customCurve->notifyListener (); + } + else if (p[0] == (double)(DCT_NURBS)) { + NURBSCurve->setPoints (p); + NURBSCurve->queue_draw (); + NURBSCurve->notifyListener (); + } + } + } +} + +/* + * Store the curves of the currently displayed type from the widgets to the CurveEditor object + */ +void DiagonalCurveEditorSubGroup::storeDisplayedCurve() { + if (parent->displayedCurve) { + switch (parent->displayedCurve->selected) { + case (DCT_Spline): + storeCurveValues(parent->displayedCurve, getCurveFromGUI(DCT_Spline)); + break; + case (DCT_Parametric): + storeCurveValues(parent->displayedCurve, getCurveFromGUI(DCT_Parametric)); + break; + case (DCT_NURBS): + storeCurveValues(parent->displayedCurve, getCurveFromGUI(DCT_NURBS)); + break; + default: + break; + } + } +} + +/* + * Restore the histogram to all types from the CurveEditor object to the widgets + */ +void DiagonalCurveEditorSubGroup::restoreDisplayedHistogram() { + if (parent->displayedCurve) { + paramCurve->updateBackgroundHistogram (parent->displayedCurve->bgHistValid ? parent->displayedCurve->histogram : NULL); + customCurve->updateBackgroundHistogram (parent->displayedCurve->bgHistValid ? parent->displayedCurve->histogram : NULL); + NURBSCurve->updateBackgroundHistogram (parent->displayedCurve->bgHistValid ? parent->displayedCurve->histogram : NULL); + } + +} + +void DiagonalCurveEditorSubGroup::storeCurveValues (CurveEditor* ce, const std::vector& p) { + if (p.size()) { + DiagonalCurveType t = (DiagonalCurveType)p[0]; + for (int i=0; i<(int)p.size(); i++) + + switch (t) { + case (DCT_Spline): + ((DiagonalCurveEditor*)ce)->customCurveEd = p; + break; + case (DCT_Parametric): + ((DiagonalCurveEditor*)ce)->paramCurveEd = p; + break; + case (DCT_NURBS): + ((DiagonalCurveEditor*)ce)->NURBSCurveEd = p; + break; + default: + break; + } + } +} + +/* + * Called to update the parametric curve graph with new slider values + */ +const std::vector DiagonalCurveEditorSubGroup::getCurveFromGUI (int type) { + switch ((DiagonalCurveType)type) { + case (DCT_Parametric): { + std::vector lcurve (8); + lcurve[0] = (double)(DCT_Parametric); + shcSelector->getPositions (lcurve[1], lcurve[2], lcurve[3]); + lcurve[4] = highlights->getValue (); + lcurve[5] = lights->getValue (); + lcurve[6] = darks->getValue (); + lcurve[7] = shadows->getValue (); + return lcurve; + } + case (DCT_Spline): + return customCurve->getPoints (); + case (DCT_NURBS): + return NURBSCurve->getPoints (); + default: { + // linear and other solutions + std::vector lcurve (1); + lcurve[0] = (double)(DCT_Linear); + return lcurve; + } + } +} + +/* + * Unlink the tree editor widgets from their parent box to hide them + */ +void DiagonalCurveEditorSubGroup::removeEditor () { + removeIfThere (parent, customCurveBox, false); + removeIfThere (parent, paramCurveBox, false); + removeIfThere (parent, NURBSCurveBox, false); +} + +bool DiagonalCurveEditorSubGroup::curveReset(int cType) { + switch ((DiagonalCurveType) cType) { + case (DCT_NURBS) : // = Control cage + NURBSCurve->reset (); + return true; + break; + case (DCT_Spline) : // = Custom + customCurve->reset (); + return true; + break; + case (DCT_Parametric) : + highlights->resetPressed(); + lights->resetPressed(); + darks->resetPressed(); + shadows->resetPressed(); + shcSelector->reset(); + paramCurve->reset (); + return true; + break; + default: + return false; + break; + } +} + +void DiagonalCurveEditorSubGroup::setColorProvider (ColorProvider* p) { + customCurve->setColorProvider(p); + paramCurve->setColorProvider(p); + NURBSCurve->setColorProvider(p); +} + +/* + * Listener + */ +void DiagonalCurveEditorSubGroup::shcChanged () { + + paramCurve->setPoints (getCurveFromGUI(DCT_Parametric)); + storeDisplayedCurve(); + parent->curveChanged (); +} + +/* + * Listener + */ +void DiagonalCurveEditorSubGroup::adjusterChanged (Adjuster* a, double newval) { + + paramCurve->setPoints (getCurveFromGUI(DCT_Parametric)); + storeDisplayedCurve(); + parent->curveChanged (); +} + +/* + * Listener called when the mouse is over a parametric curve's slider + */ +bool DiagonalCurveEditorSubGroup::adjusterEntered (GdkEventCrossing* ev, int ac) { + + if (ev->detail != GDK_NOTIFY_INFERIOR) { + activeParamControl = ac; + paramCurve->setActiveParam (activeParamControl); + } + return true; +} + +/* + * Listener called when the mouse left the parametric curve's slider + */ +bool DiagonalCurveEditorSubGroup::adjusterLeft (GdkEventCrossing* ev, int ac) { + + if (ev->detail != GDK_NOTIFY_INFERIOR) { + activeParamControl = -1; + paramCurve->setActiveParam (activeParamControl); + } + return true; +} + +void DiagonalCurveEditorSubGroup::updateBackgroundHistogram (CurveEditor* ce) { + if (ce==parent->displayedCurve) { + paramCurve->updateBackgroundHistogram (ce->bgHistValid ? ce->histogram : NULL); + customCurve->updateBackgroundHistogram (ce->bgHistValid ? ce->histogram : NULL); + NURBSCurve->updateBackgroundHistogram (ce->bgHistValid ? ce->histogram : NULL); + } +} diff --git a/rtgui/diagonalcurveeditorsubgroup.h b/rtgui/diagonalcurveeditorsubgroup.h new file mode 100644 index 000000000..755a9a0e3 --- /dev/null +++ b/rtgui/diagonalcurveeditorsubgroup.h @@ -0,0 +1,77 @@ +/* + * 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 . + */ +#ifndef _DIAGONALCURVEEDITORSUBGROUP_ +#define _DIAGONALCURVEEDITORSUBGROUP_ + +#include +#include + +class DiagonalCurveEditor; + +class DiagonalCurveEditorSubGroup : public CurveEditorSubGroup, public SHCListener, public AdjusterListener { + + friend class DiagonalCurveEditor; + +private: + Gtk::HBox* customCurveBox; + Gtk::VBox* paramCurveBox; + Gtk::HBox* NURBSCurveBox; + + MyDiagonalCurve* customCurve; + MyDiagonalCurve* paramCurve; + MyDiagonalCurve* NURBSCurve; + + SHCSelector* shcSelector; + Adjuster* highlights; + Adjuster* lights; + Adjuster* darks; + Adjuster* shadows; + + Gtk::Button* saveCustom; + Gtk::Button* loadCustom; + Gtk::Button* saveNURBS; + Gtk::Button* loadNURBS; + + int activeParamControl; + +public: + DiagonalCurveEditorSubGroup(CurveEditorGroup* prt); + ~DiagonalCurveEditorSubGroup(); + + DiagonalCurveEditor* addCurve(Glib::ustring curveLabel = ""); + virtual void updateBackgroundHistogram (CurveEditor* ce); + virtual void setColorProvider (ColorProvider* p); + +private: + void storeCurveValues (CurveEditor* ce, const std::vector& p); + void storeDisplayedCurve (); + void restoreDisplayedHistogram (); + void savePressed (); + void loadPressed (); + void switchGUI(); + bool curveReset (int cType); + void removeEditor (); + const std::vector getCurveFromGUI (int type); + void shcChanged (); + void adjusterChanged (Adjuster* a, double newval); + bool adjusterEntered (GdkEventCrossing* ev, int ac); + bool adjusterLeft (GdkEventCrossing* ev, int ac); +}; + +#endif diff --git a/rtgui/flatcurveeditorsubgroup.cc b/rtgui/flatcurveeditorsubgroup.cc new file mode 100644 index 000000000..d59bb2f4e --- /dev/null +++ b/rtgui/flatcurveeditorsubgroup.cc @@ -0,0 +1,272 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +FlatCurveEditorSubGroup::FlatCurveEditorSubGroup (CurveEditorGroup* prt) { + + valLinear = (int)FCT_Linear; + valUnchanged = (int)FCT_Unchanged; + parent = prt; + + // ControlPoints curve + CPointsCurveBox = new Gtk::HBox (); + //Gtk::HBox* tmpb = Gtk::manage (new Gtk::HBox ()); + CPointsCurve = Gtk::manage (new MyFlatCurve ()); + //Gtk::AspectFrame* af = Gtk::manage (new Gtk::AspectFrame ("",Gtk::ALIGN_CENTER,Gtk::ALIGN_CENTER,1,false)); + //CPointsCurve->set_size_request (GRAPH_SIZE+2*RADIUS+1, GRAPH_SIZE+2*RADIUS+1); + CPointsCurve->setType (FCT_MinMaxCPoints); + //tmpb->pack_start (*CPointsCurve, Gtk::PACK_EXPAND_WIDGET, 4); + CPointsCurveBox->pack_start (*CPointsCurve, Gtk::PACK_EXPAND_WIDGET,4); + //CPointsCurveBox->pack_start (*tmpb, Gtk::PACK_EXPAND_WIDGET,4); + + Gtk::VBox* CPointsbbox = Gtk::manage (new Gtk::VBox ()); + saveCPoints = Gtk::manage (new Gtk::Button ()); + saveCPoints->add (*Gtk::manage (new Gtk::Image (Gtk::StockID("gtk-save"), Gtk::ICON_SIZE_BUTTON))); + loadCPoints = Gtk::manage (new Gtk::Button ()); + loadCPoints->add (*Gtk::manage (new Gtk::Image (Gtk::StockID("gtk-open"), Gtk::ICON_SIZE_BUTTON))); + + CPointsbbox->pack_end (*saveCPoints, Gtk::PACK_SHRINK, 4); + CPointsbbox->pack_end (*loadCPoints, Gtk::PACK_SHRINK, 4); + + CPointsCurveBox->pack_end (*CPointsbbox, Gtk::PACK_SHRINK, 0); + CPointsCurveBox->show_all (); + + saveCPoints->signal_clicked().connect( sigc::mem_fun(*this, &FlatCurveEditorSubGroup::savePressed) ); + loadCPoints->signal_clicked().connect( sigc::mem_fun(*this, &FlatCurveEditorSubGroup::loadPressed) ); + saveCPoints->set_tooltip_text (M("CURVEEDITOR_TOOLTIPSAVE")); + loadCPoints->set_tooltip_text (M("CURVEEDITOR_TOOLTIPLOAD")); + + CPointsCurve->setCurveListener (parent); // Send the message directly to the parent +} + +FlatCurveEditorSubGroup::~FlatCurveEditorSubGroup() { + delete CPointsCurveBox; +} + +/* + * Add a new curve to the curves list + */ +FlatCurveEditor* FlatCurveEditorSubGroup::addCurve(Glib::ustring curveLabel) { + FlatCurveEditor* newCE = new FlatCurveEditor(curveLabel, parent, this); + + // Initialization of the new curve + storeCurveValues(newCE, getCurveFromGUI(FCT_MinMaxCPoints)); + + // We add it to the curve editor list + parent->curveEditors.push_back(newCE); + return newCE; +} + +/* + * Switch the editor widgets to the currently edited curve + */ +void FlatCurveEditorSubGroup::switchGUI() { + + removeEditor(); + + FlatCurveEditor* dCurve = (FlatCurveEditor*)(parent->displayedCurve); + + if (dCurve) { + + // Initializing GUI values + repacking the appropriated widget + //dCurve->typeconn.block(true); + + switch((FlatCurveType)(dCurve->curveType->getSelected())) { + case (FCT_MinMaxCPoints): + CPointsCurve->setPoints (dCurve->controlPointsCurveEd); + parent->pack_start (*CPointsCurveBox); + break; + default: // (DCT_Linear, DCT_Unchanged) + // ... do nothing + break; + } + + //dCurve->typeconn.block(false); + } +} + +void FlatCurveEditorSubGroup::savePressed () { + + Glib::ustring fname = outputFile(); + if (fname.size()) { + std::ofstream f (fname.c_str()); + std::vector p; + //std::vector p = customCurve->getPoints (); + + switch (parent->displayedCurve->selected) { + case FCT_MinMaxCPoints: // Control points + p = CPointsCurve->getPoints (); + break; + default: + break; + } + + int ix = 0; + if (p[ix]==(double)(FCT_Linear)) + f << "Linear\n"; + else if (p[ix]==(double)(FCT_MinMaxCPoints)) + f << "ControlPoints\n"; + ix++; + for (unsigned int i=0; i p; + std::string s; + f >> s; + if (s=="Linear") + p.push_back ((double)(FCT_Linear)); + else if (s=="ControlPoints") + p.push_back ((double)(FCT_MinMaxCPoints)); + else return; + double x; + while (f) { + f >> x; + if (f) + p.push_back (x); + } + if (p[0] == (double)(FCT_MinMaxCPoints)) { + CPointsCurve->setPoints (p); + CPointsCurve->queue_draw (); + CPointsCurve->notifyListener (); + } + } + } +} + +/* + * Store the curves of the currently displayed type from the widgets to the CurveEditor object + */ +void FlatCurveEditorSubGroup::storeDisplayedCurve() { + if (parent->displayedCurve) { + switch (parent->displayedCurve->selected) { + /*case (FCT_Parametric): + storeCurveValues(parent->displayedCurve, getCurveFromGUI(FCT_Parametric)); + break;*/ + case (FCT_MinMaxCPoints): + storeCurveValues(parent->displayedCurve, getCurveFromGUI(FCT_MinMaxCPoints)); + break; + default: + break; + } + } +} + +/* + * Restore the histogram to all types from the CurveEditor object to the widgets + */ +void FlatCurveEditorSubGroup::restoreDisplayedHistogram() { + if (parent->displayedCurve) { + //paramCurve->updateBackgroundHistogram (parent->displayedCurve->bgHistValid ? parent->displayedCurve->histogram : NULL); + CPointsCurve->updateBackgroundHistogram (parent->displayedCurve->bgHistValid ? parent->displayedCurve->histogram : NULL); + } + +} + +void FlatCurveEditorSubGroup::storeCurveValues (CurveEditor* ce, const std::vector& p) { + if (p.size()) { + FlatCurveType t = (FlatCurveType)p[0]; + for (int i=0; i<(int)p.size(); i++) + + switch (t) { + case (FCT_MinMaxCPoints): + ((FlatCurveEditor*)ce)->controlPointsCurveEd = p; + break; + default: + break; + } + } +} + +/* + * Called to update the parametric curve graph with new slider values + */ +const std::vector FlatCurveEditorSubGroup::getCurveFromGUI (int type) { + switch ((FlatCurveType)type) { + case (FCT_MinMaxCPoints): + return CPointsCurve->getPoints (); + default: { + // linear and other solutions + std::vector lcurve (1); + lcurve[0] = (double)(FCT_Linear); + return lcurve; + } + } +} + +/* + * Unlink the tree editor widgets from their parent box to hide them + */ +void FlatCurveEditorSubGroup::removeEditor () { + removeIfThere (parent, CPointsCurveBox, false); +} + +bool FlatCurveEditorSubGroup::curveReset(int cType) { + switch ((FlatCurveType) cType) { + case (FCT_MinMaxCPoints) : // = Control cage + CPointsCurve->reset (); + return true; + break; + /*case (FCT_Parametric) : + highlights->resetPressed(); + lights->resetPressed(); + darks->resetPressed(); + shadows->resetPressed(); + shcSelector->reset(); + paramCurve->reset (); + return true; + break;*/ + default: + return false; + break; + } +} + +void FlatCurveEditorSubGroup::setColorProvider (ColorProvider* p) { + CPointsCurve->setColorProvider(p); +} + +/*void FlatCurveEditorSubGroup::updateBackgroundHistogram (CurveEditor* ce) { + CurveEditor* fce = (CurveEditor*)ce; + if (fce==displayedCurve) { + paramCurve->updateBackgroundHistogram (fce->bgHistValid ? fce->histogram : NULL); + customCurve->updateBackgroundHistogram (fce->bgHistValid ? fce->histogram : NULL); + NURBSCurve->updateBackgroundHistogram (fce->bgHistValid ? fce->histogram : NULL); + } +}*/ diff --git a/rtgui/flatcurveeditorsubgroup.h b/rtgui/flatcurveeditorsubgroup.h new file mode 100644 index 000000000..62d6b2c62 --- /dev/null +++ b/rtgui/flatcurveeditorsubgroup.h @@ -0,0 +1,59 @@ +/* + * 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 . + */ +#ifndef _FLATCURVEEDITORSUBGROUP_ +#define _FLATCURVEEDITORSUBGROUP_ + +#include +#include + +class FlatCurveEditor; + +class FlatCurveEditorSubGroup: public CurveEditorSubGroup { + + friend class FlatCurveEditor; + +private: + Gtk::HBox* CPointsCurveBox; + + MyFlatCurve* CPointsCurve; + + Gtk::Button* saveCPoints; + Gtk::Button* loadCPoints; + +public: + FlatCurveEditorSubGroup(CurveEditorGroup* prt); + ~FlatCurveEditorSubGroup(); + + FlatCurveEditor* addCurve(Glib::ustring curveLabel = ""); + //virtual void updateBackgroundHistogram (CurveEditor* ce); + virtual void setColorProvider (ColorProvider* p); + +private: + void storeCurveValues (CurveEditor* ce, const std::vector& p); + void storeDisplayedCurve (); + void restoreDisplayedHistogram (); + void savePressed (); + void loadPressed (); + void switchGUI(); + bool curveReset (int cType); + void removeEditor (); + const std::vector getCurveFromGUI (int type); +}; + +#endif diff --git a/rtgui/hsvequalizer.cc b/rtgui/hsvequalizer.cc index 7d53eaabd..15eb35fd4 100644 --- a/rtgui/hsvequalizer.cc +++ b/rtgui/hsvequalizer.cc @@ -18,6 +18,7 @@ */ #include +#include using namespace rtengine; using namespace rtengine::procparams; @@ -26,196 +27,61 @@ using namespace rtengine::procparams; HSVEqualizer::HSVEqualizer () : Gtk::VBox(), FoldableToolPanel(this) { - enabled = Gtk::manage (new Gtk::CheckButton (M("GENERAL_ENABLED"))); - enabled->set_active (true); - pack_start(*enabled); - enaConn = enabled->signal_toggled().connect( sigc::mem_fun(*this, &HSVEqualizer::enabledToggled) ); - - Gtk::HSeparator *hsvsepa = Gtk::manage (new Gtk::HSeparator()); - pack_start(*hsvsepa, Gtk::PACK_SHRINK, 2); - hsvsepa->show (); - - //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - Gtk::HBox* hb = Gtk::manage (new Gtk::HBox ()); - hb->set_border_width (4); - hb->show (); - Gtk::Label* hsvselect = Gtk::manage (new Gtk::Label (M("TP_HSVEQUALIZER_CHANNEL")+":")); - hsvselect->show (); - hsvchannel = Gtk::manage (new Gtk::ComboBoxText ()); - hsvchannel->append_text (M("TP_HSVEQUALIZER_SAT")); - hsvchannel->append_text (M("TP_HSVEQUALIZER_VAL")); - hsvchannel->append_text (M("TP_HSVEQUALIZER_HUE")); - hsvchannel->show (); - hb->pack_start(*hsvselect, Gtk::PACK_SHRINK, 4); - hb->pack_start(*hsvchannel); - - Gtk::Button * neutralButton = Gtk::manage (new Gtk::Button(M("TP_HSVEQUALIZER_NEUTRAL"))); - hb->pack_start(*neutralButton, Gtk::PACK_SHRINK, 2); - neutralPressedConn = neutralButton->signal_pressed().connect( sigc::mem_fun(*this, &HSVEqualizer::neutralPressed)); - - - pack_start (*hb); - - //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - /* - Gtk::HBox * buttonBox17 = Gtk::manage (new Gtk::HBox()); - pack_start(*buttonBox17, Gtk::PACK_SHRINK, 2); - - Gtk::Button * neutralButton = Gtk::manage (new Gtk::Button(M("TP_HSVEQUALIZER_NEUTRAL"))); - buttonBox17->pack_start(*neutralButton, Gtk::PACK_SHRINK, 2); - neutralPressedConn = neutralButton->signal_pressed().connect( sigc::mem_fun(*this, &HSVEqualizer::neutralPressed)); - - buttonBox17->show(); - */ - //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - Gtk::HSeparator *hsvsepb = Gtk::manage (new Gtk::HSeparator()); - pack_start(*hsvsepb, Gtk::PACK_SHRINK, 2); - hsvsepb->show (); - - satbox = new Gtk::VBox (); - sat[0] = new Adjuster (M("TP_HSVEQUALIZER1"), -100, 100, 1, 0); - sat[1] = new Adjuster (M("TP_HSVEQUALIZER2"), -100, 100, 1, 0); - sat[2] = new Adjuster (M("TP_HSVEQUALIZER3"), -100, 100, 1, 0); - sat[3] = new Adjuster (M("TP_HSVEQUALIZER4"), -100, 100, 1, 0); - sat[4] = new Adjuster (M("TP_HSVEQUALIZER5"), -100, 100, 1, 0); - sat[5] = new Adjuster (M("TP_HSVEQUALIZER6"), -100, 100, 1, 0); - sat[6] = new Adjuster (M("TP_HSVEQUALIZER7"), -100, 100, 1, 0); - sat[7] = new Adjuster (M("TP_HSVEQUALIZER8"), -100, 100, 1, 0); - for(int i = 0; i < 8; i++) - { - sat[i]->setAdjusterListener(this); - satbox->pack_start(*sat[i]); - } - - //show_all_children (); - satbox->show (); - - //%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - valbox = new Gtk::VBox (); - val[0] = new Adjuster (M("TP_HSVEQUALIZER1"), -100, 100, 1, 0); - val[1] = new Adjuster (M("TP_HSVEQUALIZER2"), -100, 100, 1, 0); - val[2] = new Adjuster (M("TP_HSVEQUALIZER3"), -100, 100, 1, 0); - val[3] = new Adjuster (M("TP_HSVEQUALIZER4"), -100, 100, 1, 0); - val[4] = new Adjuster (M("TP_HSVEQUALIZER5"), -100, 100, 1, 0); - val[5] = new Adjuster (M("TP_HSVEQUALIZER6"), -100, 100, 1, 0); - val[6] = new Adjuster (M("TP_HSVEQUALIZER7"), -100, 100, 1, 0); - val[7] = new Adjuster (M("TP_HSVEQUALIZER8"), -100, 100, 1, 0); - for(int i = 0; i < 8; i++) - { - val[i]->setAdjusterListener(this); - valbox->pack_start(*val[i]); - } - - //show_all_children (); - valbox->show (); - //%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - huebox = new Gtk::VBox (); - - - hue[0] = new Adjuster (M("TP_HSVEQUALIZER1"), -100, 100, 1, 0); - hue[1] = new Adjuster (M("TP_HSVEQUALIZER2"), -100, 100, 1, 0); - hue[2] = new Adjuster (M("TP_HSVEQUALIZER3"), -100, 100, 1, 0); - hue[3] = new Adjuster (M("TP_HSVEQUALIZER4"), -100, 100, 1, 0); - hue[4] = new Adjuster (M("TP_HSVEQUALIZER5"), -100, 100, 1, 0); - hue[5] = new Adjuster (M("TP_HSVEQUALIZER6"), -100, 100, 1, 0); - hue[6] = new Adjuster (M("TP_HSVEQUALIZER7"), -100, 100, 1, 0); - hue[7] = new Adjuster (M("TP_HSVEQUALIZER8"), -100, 100, 1, 0); - for(int i = 0; i < 8; i++) - { - hue[i]->setAdjusterListener(this); - huebox->pack_start(*hue[i]); - } - - //huebox->show_all_children (); - huebox->show (); - //%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - huebox->reference (); - valbox->reference (); - satbox->reference (); - - //enaConn = enabled->signal_toggled().connect( sigc::mem_fun(*this, &HSVEqualizer::enabled_toggled) ); - hsvchannel->signal_changed().connect( sigc::mem_fun(*this, &HSVEqualizer::hsvchannelChanged) ); + curveEditorG = new CurveEditorGroup (M("TP_HSVEQUALIZER_CHANNEL")); + curveEditorG->setCurveListener (this); + curveEditorG->setColorProvider (this); + + hshape = (FlatCurveEditor*)curveEditorG->addCurve(CT_Flat, M("TP_HSVEQUALIZER_HUE")); + sshape = (FlatCurveEditor*)curveEditorG->addCurve(CT_Flat, M("TP_HSVEQUALIZER_SAT")); + vshape = (FlatCurveEditor*)curveEditorG->addCurve(CT_Flat, M("TP_HSVEQUALIZER_VAL")); + + // This will add the reset button at the end of the curveType buttons + curveEditorG->curveListComplete(); + + pack_start (*curveEditorG, Gtk::PACK_SHRINK, 4); + + //curveEditorG->show(); } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% HSVEqualizer::~HSVEqualizer () { - for (int i=0;i<8;i++) - { - delete hue[i]; - delete val[i]; - delete sat[i]; - } + delete curveEditorG; } void HSVEqualizer::read (const ProcParams* pp, const ParamsEdited* pedited) { - + disableListener (); - + if (pedited) { - for (int i=0; i<8; i++) { - sat[i]->setEditedState (pedited->hsvequalizer.sat[i] ? Edited : UnEdited); - val[i]->setEditedState (pedited->hsvequalizer.val[i] ? Edited : UnEdited); - hue[i]->setEditedState (pedited->hsvequalizer.hue[i] ? Edited : UnEdited); - } - enabled->set_inconsistent (!pedited->hsvequalizer.enabled); + hshape->setUnChanged (!pedited->hsvequalizer.hcurve); + sshape->setUnChanged (!pedited->hsvequalizer.scurve); + vshape->setUnChanged (!pedited->hsvequalizer.vcurve); } - - enaConn.block (true); - enabled->set_active (pp->hsvequalizer.enabled); - enaConn.block (false); - lastEnabled = pp->hsvequalizer.enabled; - - for (int i=0; i<8; i++) { - sat[i]->setValue (pp->hsvequalizer.sat[i]); - val[i]->setValue (pp->hsvequalizer.val[i]); - hue[i]->setValue (pp->hsvequalizer.hue[i]); - } - - if (pedited && !pedited->hsvequalizer.hsvchannel) - hsvchannel->set_active (3); - else if (pp->hsvequalizer.hsvchannel=="sat") - hsvchannel->set_active (0); - else if (pp->hsvequalizer.hsvchannel=="val") - hsvchannel->set_active (1); - else if (pp->hsvequalizer.hsvchannel=="hue") - hsvchannel->set_active (2); + + hshape->setCurve (pp->hsvequalizer.hcurve); + sshape->setCurve (pp->hsvequalizer.scurve); + vshape->setCurve (pp->hsvequalizer.vcurve); enableListener (); } void HSVEqualizer::write (ProcParams* pp, ParamsEdited* pedited) { - pp->hsvequalizer.enabled = enabled->get_active (); - for (int i=0; i<8; i++) { - pp->hsvequalizer.sat[i] = sat[i]->getValue(); - pp->hsvequalizer.val[i] = val[i]->getValue(); - pp->hsvequalizer.hue[i] = hue[i]->getValue(); - } - - if (hsvchannel->get_active_row_number()==0) - pp->hsvequalizer.hsvchannel = "sat"; - else if (hsvchannel->get_active_row_number()==1) - pp->hsvequalizer.hsvchannel = "val"; - else if (hsvchannel->get_active_row_number()==2) - pp->hsvequalizer.hsvchannel = "hue"; - + pp->hsvequalizer.hcurve = hshape->getCurve (); + pp->hsvequalizer.scurve = sshape->getCurve (); + pp->hsvequalizer.vcurve = vshape->getCurve (); + if (pedited) { - pedited->hsvequalizer.enabled = !enabled->get_inconsistent();//from dirpyreq - for (int i=0; i<8; i++) { - pedited->hsvequalizer.sat[i] = sat[i]->getEditedState (); - pedited->hsvequalizer.val[i] = val[i]->getEditedState (); - pedited->hsvequalizer.hue[i] = hue[i]->getEditedState (); - } + + pedited->hsvequalizer.hcurve = !hshape->isUnChanged (); + pedited->hsvequalizer.scurve = !sshape->isUnChanged (); + pedited->hsvequalizer.vcurve = !vshape->isUnChanged (); } } +/* void HSVEqualizer::setDefaults (const ProcParams* defParams, const ParamsEdited* pedited) { for (int i = 0; i < 8; i++) { @@ -239,7 +105,27 @@ void HSVEqualizer::setDefaults (const ProcParams* defParams, const ParamsEdited* } } } +*/ +/* + * Curve listener + * + * If more than one curve has been added, the curve listener is automatically + * set to 'multi=true', and send a pointer of the modified curve in a parameter + */ +void HSVEqualizer::curveChanged (CurveEditor* ce) { + + if (listener) { + if (ce == hshape) + listener->panelChanged (EvHSVEqualizerH, M("HISTORY_CUSTOMCURVE")); + if (ce == sshape) + listener->panelChanged (EvHSVEqualizerS, M("HISTORY_CUSTOMCURVE")); + if (ce == vshape) + listener->panelChanged (EvHSVEqualizerV, M("HISTORY_CUSTOMCURVE")); + } +} + +/* void HSVEqualizer::adjusterChanged (Adjuster* a, double newval) { if (listener && enabled->get_active()) { @@ -289,75 +175,47 @@ void HSVEqualizer::adjusterChanged (Adjuster* a, double newval) { //listener->panelChanged (EvHSVEqualizer, ss.str()); } } +*/ -void HSVEqualizer::enabledToggled () { - - 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 (); +void HSVEqualizer::colorForValue (double valX, double valY) { + + float r, g, b; + + CurveEditor* ce = curveEditorG->getDisplayedCurve(); + + if (ce == hshape) { // Hue = f(Hue) + + float h = (float)((valY - 0.5) * 2. + valX); + if (h > 1.0) + h -= 1.0; + else if (h < 0.0) + h += 1.0; + hsv2rgb(h, (float)0.5, (float)0.5, r, g, b); + red = (double)r; + green = (double)g; + blue = (double)b; } - - if (listener) { - if (enabled->get_active ()) - listener->panelChanged (EvHSVEqEnabled, M("GENERAL_ENABLED")); - else - listener->panelChanged (EvHSVEqEnabled, M("GENERAL_DISABLED")); + else if (ce == sshape) { // Saturation = f(Hue) + hsv2rgb((float)valX, (float)valY, (float)0.5, r, g, b); + red = (double)r; + green = (double)g; + blue = (double)b; + } + else if (ce == vshape) { // Value = f(Hue) + hsv2rgb((float)valX, (float)0.5, (float)valY, r, g, b); + red = (double)r; + green = (double)g; + blue = (double)b; + } + else { + printf("Error: no curve displayed!\n"); } -} - -void HSVEqualizer::hsvchannelChanged () { - - removeIfThere (this, satbox, false); - removeIfThere (this, valbox, false); - removeIfThere (this, huebox, false); - - if (hsvchannel->get_active_row_number()==0) - pack_start (*satbox); - else if (hsvchannel->get_active_row_number()==1) - pack_start (*valbox); - else if (hsvchannel->get_active_row_number()==2) - pack_start (*huebox); - } void HSVEqualizer::setBatchMode (bool batchMode) { - ToolPanel::setBatchMode (batchMode); - - for (int i = 0; i < 8; i++) { - sat[i]->showEditedCB(); - val[i]->showEditedCB(); - hue[i]->showEditedCB(); - } - - hsvchannel->append_text (M("GENERAL_UNCHANGED")); -} + ToolPanel::setBatchMode (batchMode); -void HSVEqualizer::neutralPressed () { - if (hsvchannel->get_active_row_number()==0) - for (int i = 0; i < 8; i++) { - sat[i]->setValue(0); - adjusterChanged(sat[i], 0); - } - else if (hsvchannel->get_active_row_number()==1) - for (int i = 0; i < 8; i++) { - val[i]->setValue(0); - adjusterChanged(val[i], 0); - } - else if (hsvchannel->get_active_row_number()==2) - for (int i = 0; i < 8; i++) { - hue[i]->setValue(0); - adjusterChanged(hue[i], 0); - } - - + curveEditorG->setBatchMode (batchMode); } diff --git a/rtgui/hsvequalizer.h b/rtgui/hsvequalizer.h index 8e01610e5..40a4251d3 100644 --- a/rtgui/hsvequalizer.h +++ b/rtgui/hsvequalizer.h @@ -24,28 +24,23 @@ #include #include #include +#include +#include +#include -class HSVEqualizer : public Gtk::VBox, public AdjusterListener, public FoldableToolPanel +class HSVEqualizer : public Gtk::VBox, public AdjusterListener, public FoldableToolPanel, public CurveListener, public ColorProvider { protected: Gtk::CheckButton * enabled; Gtk::ComboBoxText* hsvchannel; - - Gtk::VBox* satbox; - Gtk::VBox* valbox; - Gtk::VBox* huebox; - Adjuster* sat[8]; - Adjuster* val[8]; - Adjuster* hue[8]; - - sigc::connection enaConn; - sigc::connection neutralPressedConn; - - bool lastEnabled; + CurveEditorGroup* curveEditorG; + FlatCurveEditor* hshape; + FlatCurveEditor* sshape; + FlatCurveEditor* vshape; public: @@ -54,15 +49,12 @@ public: void read (const rtengine::procparams::ProcParams* pp, const ParamsEdited* pedited=NULL); void write (rtengine::procparams::ProcParams* pp, ParamsEdited* pedited=NULL); - void setDefaults (const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited=NULL); + void curveChanged (CurveEditor* ce); + //void setDefaults (const rtengine::procparams::ProcParams* defParams, const ParamsEdited* pedited=NULL); void setBatchMode (bool batchMode); + virtual void colorForValue (double valX, double valY); - void adjusterChanged (Adjuster* a, double newval); - void enabledToggled (); - void hsvchannelChanged (); - - void neutralPressed (); - + //void adjusterChanged (Adjuster* a, double newval); }; #endif diff --git a/rtgui/labcurve.cc b/rtgui/labcurve.cc index 80046903f..cc847decc 100644 --- a/rtgui/labcurve.cc +++ b/rtgui/labcurve.cc @@ -69,10 +69,11 @@ LCurve::LCurve () : Gtk::VBox(), FoldableToolPanel(this), brAdd(false), contrAdd curveEditorG = new CurveEditorGroup (); curveEditorG->setCurveListener (this); + curveEditorG->setColorProvider (this); - lshape = curveEditorG->addCurve("L"); - ashape = curveEditorG->addCurve("a"); - bshape = curveEditorG->addCurve("b"); + lshape = (DiagonalCurveEditor*)curveEditorG->addCurve(CT_Diagonal, "L"); + ashape = (DiagonalCurveEditor*)curveEditorG->addCurve(CT_Diagonal, "a"); + bshape = (DiagonalCurveEditor*)curveEditorG->addCurve(CT_Diagonal, "b"); // This will add the reset button at the end of the curveType buttons curveEditorG->curveListComplete(); @@ -305,6 +306,32 @@ void LCurve::adjusterChanged (Adjuster* a, double newval) { }*/ +void LCurve::colorForValue (double valX, double valY) { + + CurveEditor* ce = curveEditorG->getDisplayedCurve(); + + if (ce == lshape) { // L = f(L) + red = (double)valY; + green = (double)valY; + blue = (double)valY; + } + else if (ce == ashape) { // a = f(a) + // TODO: To be implemented + red = (double)valY; + green = (double)valY; + blue = (double)valY; + } + else if (ce == bshape) { // b = f(b) + red = (double)valY; + green = (double)valY; + blue = (double)valY; + } + else { + printf("Error: no curve displayed!\n"); + } + +} + void LCurve::setBatchMode (bool batchMode) { ToolPanel::setBatchMode (batchMode); diff --git a/rtgui/labcurve.h b/rtgui/labcurve.h index 9ab9ffe1f..68efe3c7a 100644 --- a/rtgui/labcurve.h +++ b/rtgui/labcurve.h @@ -23,9 +23,10 @@ #include #include #include -#include +#include +#include -class LCurve : public Gtk::VBox, public AdjusterListener, public FoldableToolPanel, public CurveListener { +class LCurve : public Gtk::VBox, public AdjusterListener, public FoldableToolPanel, public CurveListener, public ColorProvider { protected: Gtk::ComboBoxText* channel; @@ -34,9 +35,9 @@ class LCurve : public Gtk::VBox, public AdjusterListener, public FoldableToolPan Adjuster* brightness; Adjuster* contrast; Adjuster* saturation; - CurveEditor* lshape; - CurveEditor* ashape; - CurveEditor* bshape; + DiagonalCurveEditor* lshape; + DiagonalCurveEditor* ashape; + DiagonalCurveEditor* bshape; //%%%%%%%%%%%%%%%% Gtk::CheckButton* avoidclip; @@ -65,6 +66,8 @@ class LCurve : public Gtk::VBox, public AdjusterListener, public FoldableToolPan void avoidclip_toggled (); void enablelimiter_toggled (); void updateCurveBackgroundHistogram (unsigned* hist); + + virtual void colorForValue (double valX, double valY); }; #endif diff --git a/rtgui/lcurve.cc b/rtgui/lcurve.cc index ba5f3c27e..b4246d0b3 100644 --- a/rtgui/lcurve.cc +++ b/rtgui/lcurve.cc @@ -40,7 +40,7 @@ LCurve::LCurve () : Gtk::VBox(), FoldableToolPanel(this), brAdd(false), contrAdd hsep3->show (); pack_start (*hsep3); - shape = Gtk::manage (new CurveEditor ()); + shape = Gtk::manage (new DiagonalCurveEditor ()); shape->show (); shape->setCurveListener (this); diff --git a/rtgui/lcurve.h b/rtgui/lcurve.h index 1a553e195..1cf353eb6 100644 --- a/rtgui/lcurve.h +++ b/rtgui/lcurve.h @@ -23,6 +23,7 @@ #include #include #include +#include #include class LCurve : public Gtk::VBox, public AdjusterListener, public FoldableToolPanel, public CurveListener { @@ -30,7 +31,7 @@ class LCurve : public Gtk::VBox, public AdjusterListener, public FoldableToolPan protected: Adjuster* brightness; Adjuster* contrast; - CurveEditor* shape; + DiagonalCurveEditor* shape; bool brAdd, contrAdd; public: diff --git a/rtgui/mycurve.cc b/rtgui/mycurve.cc index 1ceedec4a..e91a75856 100644 --- a/rtgui/mycurve.cc +++ b/rtgui/mycurve.cc @@ -1,695 +1,56 @@ -/* - * 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 -#include -#include -#include - -MyCurve::MyCurve () : listener(NULL), activeParam(-1), bghistvalid(false) { - - cursor_type = CSArrow; - curve.type = Spline; - innerWidth = get_allocation().get_width() - RADIUS * 2; - innerHeight = get_allocation().get_height() - RADIUS * 2; - prevInnerHeight = innerHeight; - grab_point = -1; - lit_point = -1; - buttonPressed = false; - - bghist = new unsigned int[256]; - - set_extension_events(Gdk::EXTENSION_EVENTS_ALL); - add_events(Gdk::EXPOSURE_MASK | Gdk::POINTER_MOTION_MASK | Gdk::POINTER_MOTION_HINT_MASK | Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::BUTTON1_MOTION_MASK); - signal_event().connect( sigc::mem_fun(*this, &MyCurve::handleEvents) ); - - curve.x.push_back(0.); - curve.y.push_back(0.); - curve.x.push_back(1.); - curve.y.push_back(1.); - curve.type = Spline; - - mcih = new MyCurveIdleHelper; - mcih->myCurve = this; - mcih->destroyed = false; - mcih->pending = 0; -} - -MyCurve::~MyCurve () { - - if (mcih->pending) - mcih->destroyed = true; - else - delete mcih; - - delete [] bghist; -} - -std::vector MyCurve::get_vector (int veclen) { - - std::vector vector; - vector.resize (veclen); - - if (curve.type != Parametric) { - // count active points: - double prev =- 1.0; - int active = 0; - int firstact = -1; - for (int i = 0; i < (int)curve.x.size(); ++i) - if (curve.x[i] > prev) { - if (firstact < 0) - firstact = i; - prev = curve.x[i]; - ++active; - } - // handle degenerate case: - if (active < 2) { - double ry; - if (active > 0) - ry = curve.y[firstact]; - else - ry = 0.0; - if (ry < 0.0) ry = 0.0; - if (ry > 1.0) ry = 1.0; - for (int x = 0; x < veclen; ++x) - vector[x] = ry; - return vector; - } - } - - // calculate remaining points - std::vector curveDescr = getPoints (); - rtengine::Curve* rtcurve = new rtengine::Curve (curveDescr, veclen*1.5); - std::vector t; - t.resize (veclen); - for (int i = 0; i < veclen; i++) - t[i] = (double) i / (veclen - 1.0); - rtcurve->getVal (t, vector); - delete rtcurve; - return vector; -} - -void MyCurve::interpolate () { - - prevInnerHeight = innerHeight; - point.resize (innerWidth); - std::vector vector = get_vector (innerWidth); - prevInnerHeight = innerHeight; - for (int i = 0; i < innerWidth; ++i) - point[i] = Gdk::Point (RADIUS + i, RADIUS + innerHeight - (int)((innerHeight-1) * vector[i] + 0.5)); - upoint.clear (); - lpoint.clear (); - - if (curve.type==Parametric && activeParam>0) { - double tmp = curve.x[activeParam-1]; - if (activeParam>=4) { - upoint.resize(innerWidth); - lpoint.resize(innerWidth); - curve.x[activeParam-1] = 100; - vector = get_vector (innerWidth); - for (int i = 0; i < innerWidth; ++i) - upoint[i] = Gdk::Point (RADIUS + i, RADIUS + innerHeight - (int)((innerHeight-1) * vector[i] + 0.5)); - curve.x[activeParam-1] = -100; - vector = get_vector (innerWidth); - for (int i = 0; i < innerWidth; ++i) - lpoint[i] = Gdk::Point (RADIUS + i, RADIUS + innerHeight - (int)((innerHeight-1) * vector[i] + 0.5)); - curve.x[activeParam-1] = tmp; - } - } -} - -void MyCurve::draw (int handle) { - if (!pixmap) - return; - - // re-calculate curve if dimensions changed - if (prevInnerHeight != innerHeight || (int)point.size() != innerWidth) - interpolate (); - - Gtk::StateType state = Gtk::STATE_NORMAL; - if (!is_sensitive()) - state = Gtk::STATE_INSENSITIVE; - - Glib::RefPtr style = get_style (); - Cairo::RefPtr cr = pixmap->create_cairo_context(); - - // bounding rectangle - Gdk::Color c = style->get_bg (state); - cr->set_source_rgb (c.get_red_p(), c.get_green_p(), c.get_blue_p()); - cr->rectangle (0, 0, innerWidth + RADIUS*2, innerHeight + RADIUS*2); - cr->fill (); - - // histogram in the background - if (bghistvalid) { - // find highest bin - unsigned int histheight = 0; - for (int i=0; i<256; i++) - if (bghist[i]>histheight) - histheight = bghist[i]; - // draw histogram - cr->set_line_width (1.0); - double stepSize = (innerWidth-1) / 256.0; - cr->move_to (RADIUS, innerHeight-1+RADIUS); - cr->set_source_rgb (0.75, 0.75, 0.75); - for (int i=0; i<256; i++) { - double val = bghist[i] * (double)(innerHeight-2) / (double)histheight; - if (val>innerHeight-1) - val = innerHeight-1; - if (i>0) - cr->line_to (i*stepSize+RADIUS, innerHeight-1+RADIUS-val); - } - cr->line_to (innerWidth-1+RADIUS, innerHeight-1+RADIUS); - cr->fill (); - } - - // draw the grid lines: - cr->set_line_width (1.0); - c = style->get_dark (state); - cr->set_source_rgb (c.get_red_p(), c.get_green_p(), c.get_blue_p()); - cr->set_antialias (Cairo::ANTIALIAS_NONE); - for (int i = 0; i < 5; i++) { // + 0.5 to align well with f(x)=x so it will cut through the center - cr->move_to (RADIUS, MAX(0,i * (innerHeight + 0.5) / 4) + RADIUS); - cr->line_to (innerWidth + RADIUS, MAX(0,i * (innerHeight + 0.5) / 4) + RADIUS); - cr->move_to (MAX(0,i * innerWidth / 4) + RADIUS, RADIUS); - cr->line_to (MAX(0,i * innerWidth / 4) + RADIUS, innerHeight + RADIUS); - } - cr->stroke (); - - // draw f(x)=x line - cr->set_source_rgb (c.get_red_p(), c.get_green_p(), c.get_blue_p()); - std::valarray ds (1); - ds[0] = 4; - cr->set_dash (ds, 0); - cr->move_to (RADIUS, innerHeight + RADIUS); - cr->line_to (innerWidth + RADIUS, RADIUS); - cr->stroke (); - cr->unset_dash (); - - cr->set_antialias (Cairo::ANTIALIAS_SUBPIXEL); - cr->set_line_width (1.0); - - // draw upper and lower bounds - if (curve.type==Parametric && activeParam>0 && lpoint.size()>1 && upoint.size()>1) { - cr->set_source_rgba (0.0, 0.0, 0.0, 0.15); - cr->move_to (upoint[0].get_x(), upoint[0].get_y()); - for (int i=1; i<(int)upoint.size(); i++) - cr->line_to (upoint[i].get_x(), upoint[i].get_y()); - cr->line_to (lpoint[lpoint.size()-1].get_x(), lpoint[lpoint.size()-1].get_y()); - for (int i=(int)lpoint.size()-2; i>=0; i--) - cr->line_to (lpoint[i].get_x(), lpoint[i].get_y()); - cr->line_to (upoint[0].get_x(), upoint[0].get_y()); - cr->fill (); - } - - // draw the cage of the NURBS curve - if (curve.type==NURBS) { - std::valarray ch_ds (1); - ch_ds[0] = 2; - cr->set_dash (ch_ds, 0); - cr->set_source_rgb (0.0, 0.0, 0.0); - std::vector points = getPoints(); - for (int i = 1; i < (int)points.size(); ) { - double x = ((innerWidth-1) * points[i++] + 0.5)+RADIUS; // project (curve.x[i], 0, 1, innerWidth); - double y = innerHeight - ((innerHeight-1) * points[i++] + 0.5)+RADIUS; // project (curve.y[i], 0, 1, innerHeight); - if (i==3) - cr->move_to (x, y); - else - cr->line_to (x, y); - } - cr->stroke (); - cr->unset_dash (); - } - - // draw curve - cr->set_source_rgb (0.0, 0.0, 0.0); - cr->move_to (point[0].get_x(), point[0].get_y()); - for (int i=1; i<(int)point.size(); i++) - cr->line_to (point[i].get_x(), point[i].get_y()); - cr->stroke (); - - // draw bullets - if (curve.type!=Parametric) - for (int i = 0; i < (int)curve.x.size(); ++i) { - cr->set_source_rgb ((i == handle ? 1.0 : 0.0), 0.0, 0.0); - double x = ((innerWidth-1) * curve.x[i] + 0.5)+RADIUS; // project (curve.x[i], 0, 1, innerWidth); - double y = innerHeight - ((innerHeight-1) * curve.y[i] + 0.5)+RADIUS; // project (curve.y[i], 0, 1, innerHeight); - - cr->arc (x, y, RADIUS+0.5, 0, 2*M_PI); - cr->fill (); - } - - get_window()->draw_drawable (style->get_fg_gc (state), pixmap, 0, 0, 0, 0, innerWidth + RADIUS * 2, innerHeight + RADIUS * 2); -} - -bool MyCurve::handleEvents (GdkEvent* event) { - - CursorShape new_type = cursor_type; - int src, dst; - std::vector::iterator itx, ity; - - bool retval = false; - int num = (int)curve.x.size(); - - /* innerWidth and innerHeight are the size of the graph */ - innerWidth = get_allocation().get_width() - RADIUS * 2; - innerHeight = get_allocation().get_height() - RADIUS * 2; - - double minDistanceX = (double)(MIN_DISTANCE) / (double)(innerWidth-1); - double minDistanceY = (double)(MIN_DISTANCE) / (double)(innerHeight-1); - - if ((innerWidth < 0) || (innerHeight < 0)) - return false; - - switch (event->type) { - case Gdk::CONFIGURE: - if (pixmap) - pixmap.clear (); - retval = true; - break; - - case Gdk::EXPOSE: - if (!pixmap) { - pixmap = Gdk::Pixmap::create (get_window(), get_allocation().get_width(), get_allocation().get_height()); - interpolate (); - } - draw (lit_point); - retval = true; - break; - - case Gdk::BUTTON_PRESS: - if (curve.type!=Parametric) { - if (event->button.button == 1) { - buttonPressed = true; - add_modal_grab (); - - // get the pointer position - getCursorPosition(event); - findClosestPoint(); - - new_type = CSMove; - if (distanceX > minDistanceX) { - /* insert a new control point */ - if (num > 0) { - if (clampedX > curve.x[closest_point]) - ++closest_point; - } - itx = curve.x.begin(); - ity = curve.y.begin(); - for (int i=0; ibutton.button == 1) { - buttonPressed = false; - /* get the pointer position */ - getCursorPosition(event); - findClosestPoint(); - - remove_modal_grab (); - int previous_lit_point = lit_point; - /* delete inactive points: */ - itx = curve.x.begin(); - ity = curve.y.begin(); - for (src = dst = 0; src < num; ++src) - if (curve.x[src] >= 0.0) { - curve.x[dst] = curve.x[src]; - curve.y[dst] = curve.y[src]; - ++dst; - ++itx; - ++ity; - } - if (dst < src) { - curve.x.erase (itx, curve.x.end()); - curve.y.erase (ity, curve.y.end()); - if (!curve.x.size()) { - curve.x.push_back (0); - curve.y.push_back (0); - interpolate (); - draw (lit_point); - } - } - if (distanceX <= minDistanceX) { - new_type = CSMove; - lit_point = closest_point; - } - else { - new_type = CSPlus; - lit_point = -1; - } - if (lit_point != previous_lit_point) - draw (lit_point); - grab_point = -1; - retval = true; - notifyListener (); - } - } - break; - - case Gdk::LEAVE_NOTIFY: - // Pointer can LEAVE even when dragging the point, so we don't modify the cursor in this case - // The cursor will have to LEAVE another time after the drag... - if (!buttonPressed) - if (grab_point == -1) { - new_type = CSArrow; - lit_point = -1; - draw (lit_point); - } - retval = true; - break; - - case Gdk::MOTION_NOTIFY: - if (curve.type == Linear || curve.type == Spline || curve.type == NURBS) { - // get the pointer position - getCursorPosition(event); - - if (grab_point == -1) { - // there's no point currently being moved - int previous_lit_point = lit_point; - findClosestPoint(); - if (distanceX <= minDistanceX) { - new_type = CSMove; - lit_point = closest_point; - } - else { - new_type = CSPlus; - lit_point = -1; - } - if (lit_point != previous_lit_point) - draw (lit_point); - } - else { - // a point is being moved - - // bounds of the grabbed point - double leftBound = (grab_point == 0 ) ? 0. : curve.x[grab_point-1]; - double rightBound = (grab_point == num-1) ? 1. : curve.x[grab_point+1]; - double const bottomBound = 0.; - double const topBound = 1.; - - double leftDeletionBound = leftBound - minDistanceX; - double rightDeletionBound = rightBound + minDistanceX; - double bottomDeletionBound = bottomBound - minDistanceY; - double topDeletionBound = topBound + minDistanceY; - - // we memorize the previous position of the point, for optimization purpose - double prevPosX = curve.x[grab_point]; - double prevPosY = curve.y[grab_point]; - - // we memorize the previous position of the point, for optimization purpose - ugpX += deltaX; - ugpY += deltaY; - - // handling limitations along X axis - if (ugpX >= rightDeletionBound && (grab_point > 0 && grab_point < (num-1))) { - curve.x[grab_point] = -1.; - } - else if (ugpX <= leftDeletionBound && (grab_point > 0 && grab_point < (num-1))) { - curve.x[grab_point] = -1.; - } - else - // nextPosX is in bounds - curve.x[grab_point] = CLAMP(ugpX, leftBound, rightBound); - - // Handling limitations along Y axis - if (ugpY >= topDeletionBound && grab_point != 0 && grab_point != num-1) { - curve.x[grab_point] = -1.; - } - else if (ugpY <= bottomDeletionBound && grab_point != 0 && grab_point != num-1) { - curve.x[grab_point] = -1.; - } - else - // nextPosY is in the bounds - curve.y[grab_point] = CLAMP(ugpY, 0.0, 1.0); - - if (curve.x[grab_point] != prevPosX || curve.y[grab_point] != prevPosY) { - // we recalculate the curve only if we have to - interpolate (); - draw (lit_point); - notifyListener (); - } - } - } - - retval = true; - break; - - default: - break; - } - if (new_type != cursor_type) { - cursor_type = new_type; - cursorManager.setCursor(cursor_type); - } - return retval; -} - -void MyCurve::getCursorPosition(GdkEvent* event) { - int tx, ty; - int prevCursorX, prevCursorY; - double incrementX = 1. / (double)(innerWidth-1); - double incrementY = 1. / (double)(innerHeight-1); - - // getting the cursor position - switch (event->type) { - case (Gdk::MOTION_NOTIFY) : - if (event->motion.is_hint) { - get_window()->get_pointer (tx, ty, mod_type); - } - else { - tx = (int)event->button.x; - ty = (int)event->button.y; - mod_type = (Gdk::ModifierType)event->button.state; - } - break; - case (Gdk::BUTTON_PRESS) : - case (Gdk::BUTTON_RELEASE) : - tx = (int)event->button.x; - ty = (int)event->button.y; - mod_type = (Gdk::ModifierType)event->button.state; - break; - default : - // The cursor position is not available - return; - break; - } - - if (grab_point != -1) { - prevCursorX = cursorX; - prevCursorY = cursorY; - } - cursorX = tx - RADIUS; - cursorY = (innerHeight-1) - (ty - RADIUS); - - // update deltaX/Y if the user drags a point - if (grab_point != -1) { - // set the dragging factor - int control_key = mod_type & GDK_CONTROL_MASK; - int shift_key = mod_type & GDK_SHIFT_MASK; - - // the increment get smaller if modifier key are used - if (control_key && shift_key) { incrementX *= 0.01; incrementY *= 0.01; } - else if (shift_key) { incrementX *= 0.07; incrementY *= 0.07; } - else if (control_key) { incrementX *= 0.25; incrementY *= 0.25; } - - deltaX = (double)(cursorX - prevCursorX) * incrementX; - deltaY = (double)(cursorY - prevCursorY) * incrementY; - } - // otherwise set the position of the new point (modifier keys has no effect here) - else { - double tempCursorX = cursorX * incrementX; - double tempCursorY = cursorY * incrementY; - clampedX = CLAMP (tempCursorX, 0., 1.); // X position of the pointer from the origin of the graph - clampedY = CLAMP (tempCursorY, 0., 1.); // Y position of the pointer from the origin of the graph - } - -} - -void MyCurve::findClosestPoint() { - distanceX = 10.0; distanceY = 10.0; - closest_point = -1; - - if (curve.type!=Parametric) { - for (int i = 0; i < (int)curve.x.size(); i++) { - double dX = curve.x[i] - clampedX; - double dY = curve.y[i] - clampedY; - double currDistX = dX < 0. ? -dX : dX; //abs (dX); - double currDistY = dY < 0. ? -dY : dY; //abs (dY); - if (currDistX < distanceX) { - distanceX = currDistX; - distanceY = currDistY; - closest_point = i; - } - else if (currDistX == distanceX && currDistY < distanceY) { - // there is more than 1 point for that X coordinate, we select the closest point to the cursor - distanceY = currDistY; - closest_point = i; - } - } - } -} - -std::vector MyCurve::getPoints () { - std::vector result; - if (curve.type==Parametric) { - result.push_back ((double)(Parametric)); - for (int i=0; i<(int)curve.x.size(); i++) { - result.push_back (curve.x[i]); - } - } - else { - // the first value gives the type of the curve - if (curve.type==Linear) - result.push_back ((double)(Linear)); - else if (curve.type==Spline) - result.push_back ((double)(Spline)); - else if (curve.type==NURBS) - result.push_back ((double)(NURBS)); - // then we push all the points coordinate - for (int i=0; i<(int)curve.x.size(); i++) { - if (curve.x[i]>=0) { - result.push_back (curve.x[i]); - result.push_back (curve.y[i]); - } - } - } - return result; -} - -void MyCurve::setPoints (const std::vector& p) { - int ix = 0; - CurveType t = (CurveType)p[ix++]; - curve.type = t; - if (t==Parametric) { - curve.x.clear (); - curve.y.clear (); - for (int i=1; i<(int)p.size(); i++) - curve.x.push_back (p[ix++]); - } - else { - curve.x.clear (); - curve.y.clear (); - for (int i=0; i<(int)p.size()/2; i++) { - curve.x.push_back (p[ix++]); - curve.y.push_back (p[ix++]); - } - activeParam = -1; - } - pixmap.clear (); - queue_draw (); -} - -void MyCurve::setType (CurveType t) { - - curve.type = t; - pixmap.clear (); -} - -void MyCurve::notifyListener () { - - if (listener) - listener->curveChanged (); -} - -void MyCurve::setActiveParam (int ac) { - - activeParam = ac; - pixmap.clear (); - queue_draw (); -} - -int mchistupdate (void* data) { - - gdk_threads_enter (); - - MyCurveIdleHelper* mcih = (MyCurveIdleHelper*)data; - - if (mcih->destroyed) { - if (mcih->pending == 1) - delete mcih; - else - mcih->pending--; - gdk_threads_leave (); - return 0; - } - - mcih->myCurve->pixmap.clear (); - mcih->myCurve->queue_draw (); - - mcih->pending--; - gdk_threads_leave (); - return 0; -} - -void MyCurve::updateBackgroundHistogram (unsigned int* hist) { - - if (hist!=NULL) { - memcpy (bghist, hist, 256*sizeof(unsigned int)); - bghistvalid = true; - } - else - bghistvalid = false; - - mcih->pending++; - g_idle_add (mchistupdate, mcih); - -} - -void MyCurve::reset() { - innerWidth = get_allocation().get_width() - RADIUS * 2; - innerHeight = get_allocation().get_height() - RADIUS * 2; - - switch (curve.type) { - case Spline : - case NURBS : - curve.x.clear(); - curve.y.clear(); - curve.x.push_back(0.); - curve.y.push_back(0.); - curve.x.push_back(1.); - curve.y.push_back(1.); - grab_point = -1; - lit_point = -1; - interpolate (); - break; - case Parametric : - // Nothing to do (?) - default: - break; - } - draw(-1); -} +/* + * 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 +#include +#include +#include + +MyCurve::MyCurve () : listener(NULL) { + + cursor_type = CSArrow; + innerWidth = get_allocation().get_width() - RADIUS * 2; + innerHeight = get_allocation().get_height() - RADIUS * 2; + prevInnerHeight = innerHeight; + buttonPressed = false; + snapTo = ST_None; + colorProvider = NULL; + sized = false; + + set_extension_events(Gdk::EXTENSION_EVENTS_ALL); + add_events(Gdk::EXPOSURE_MASK | Gdk::POINTER_MOTION_MASK | Gdk::POINTER_MOTION_HINT_MASK | Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::BUTTON1_MOTION_MASK); + + mcih = new MyCurveIdleHelper; + mcih->myCurve = this; + mcih->destroyed = false; + mcih->pending = 0; +} + +MyCurve::~MyCurve () { + + if (mcih->pending) + mcih->destroyed = true; + else + delete mcih; +} + +void MyCurve::notifyListener () { + + if (listener) + listener->curveChanged (); +} diff --git a/rtgui/mycurve.h b/rtgui/mycurve.h index 1cd07adc4..02167af52 100644 --- a/rtgui/mycurve.h +++ b/rtgui/mycurve.h @@ -23,91 +23,73 @@ #include #include #include +#include -#define RADIUS 3 /* radius of the control points. Assuming that the center of the spot is in the center of the pixel, the real RADIUS will be this value +0.5 */ -#define MIN_DISTANCE 8 /* min distance between control points */ +#define RADIUS 3 /* radius of the control points */ +#define SQUARE 2 /* half length of the square shape of the tangent handles */ +#define MIN_DISTANCE 5 /* min distance between control points */ #define GRAPH_SIZE 200 /* size of the curve editor graphic */ // For compatibility and simplicity reason, order shouldn't change, and must be identical to the order specified in the curveType widget enum CurveType { - Empty = -1, - Linear, // 0 - Spline, // 1 - Parametric, // 2 - NURBS, // 3 - // Insert new curve type above this line - Unchanged // Must remain the last of the enum + CT_Flat, + CT_Diagonal }; -class CurveDescr { - - public: - CurveType type; - std::vector x, y; // in case of parametric curves the curve parameters are stored in vector x. In other cases these vectors store the coordinates of the bullets. -}; - -class MyCurve; -struct MyCurveIdleHelper { - MyCurve* myCurve; - bool destroyed; - int pending; +enum SnapToType { + ST_None, + ST_Identity, // Point snapped to the identity curve + ST_Neighbors // Point snapped to the neighbor points }; +class MyCurveIdleHelper; class MyCurve : public Gtk::DrawingArea { - friend int mchistupdate (void* data); + friend class MyCurveIdleHelper; - protected: - CurveListener* listener; - CurveDescr curve; - CursorShape cursor_type; - Glib::RefPtr pixmap; - int innerWidth; // inner width of the editor, allocated by the system - int innerHeight; // inner height of the editor, allocated by the system - int prevInnerHeight;// previous inner height of the editor - int grab_point; // the point that the user is moving - int closest_point; // the point that is the closest from the cursor - int lit_point; // the point that is lit when the cursor is near it - //int last; + protected: + CurveListener* listener; + ColorProvider* colorProvider; + CursorShape cursor_type; + Glib::RefPtr pixmap; + int innerWidth; // inner width of the editor, allocated by the system + int innerHeight; // inner height of the editor, allocated by the system + int prevInnerHeight;// previous inner height of the editor Gdk::ModifierType mod_type; - int cursorX; // X coordinate in the graph of the cursor - int cursorY; // Y coordinate in the graph of the cursor - double clampedX; // clamped grabbed point X coordinates in the [0;1] range - double clampedY; // clamped grabbed point Y coordinates in the [0;1] range - double deltaX; // signed X distance of the cursor between two consecutive MOTION_NOTIFY - double deltaY; // signed Y distance of the cursor between two consecutive MOTION_NOTIFY - double distanceX; // X distance from the cursor to the closest point - double distanceY; // Y distance from the cursor to the closest point - double ugpX; // unclamped grabbed point X coordinate in the graph - double ugpY; // unclamped grabbed point Y coordinate in the graph - std::vector point; - std::vector upoint; - std::vector lpoint; - int activeParam; - unsigned int* bghist; // histogram values - bool bghistvalid; - bool buttonPressed; - MyCurveIdleHelper* mcih; + int cursorX; // X coordinate in the graph of the cursor + int cursorY; // Y coordinate in the graph of the cursor + std::vector point; + std::vector upoint; + std::vector lpoint; + bool buttonPressed; + enum SnapToType snapTo; + MyCurveIdleHelper* mcih; + bool sized; - void draw (int handle); - void interpolate (); - void getCursorPosition(GdkEvent* event); - void findClosestPoint(); - std::vector get_vector (int veclen); + public: + MyCurve (); + ~MyCurve (); + + void setCurveListener (CurveListener* cl) { listener = cl; } + void setColorProvider (ColorProvider* cp) { colorProvider = cp; } + void notifyListener (); + void updateBackgroundHistogram (unsigned int* hist) {return;} ; + virtual std::vector getPoints () = 0; + virtual void setPoints (const std::vector& p) = 0; + virtual bool handleEvents (GdkEvent* event) = 0; + virtual void reset () = 0; + protected: + virtual std::vector get_vector (int veclen) = 0; +}; + +class MyCurveIdleHelper { + public: + MyCurve* myCurve; + bool destroyed; + int pending; + + void clearPixmap () { myCurve->pixmap.clear (); } - public: - MyCurve (); - ~MyCurve (); - - void setCurveListener (CurveListener* cl) { listener = cl; } - std::vector getPoints (); - void setPoints (const std::vector& p); - void setType (CurveType t); - bool handleEvents (GdkEvent* event); - void notifyListener (); - void setActiveParam (int ac); - void updateBackgroundHistogram (unsigned int* hist); - void reset (); }; #endif diff --git a/rtgui/mydiagonalcurve.cc b/rtgui/mydiagonalcurve.cc new file mode 100644 index 000000000..913793ae9 --- /dev/null +++ b/rtgui/mydiagonalcurve.cc @@ -0,0 +1,678 @@ +/* + * 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 +#include +#include +#include + +MyDiagonalCurve::MyDiagonalCurve () : activeParam(-1), bghistvalid(false) { + + innerWidth = get_allocation().get_width() - RADIUS * 2; + innerHeight = get_allocation().get_height() - RADIUS * 2; + prevInnerHeight = innerHeight; + grab_point = -1; + lit_point = -1; + buttonPressed = false; + + bghist = new unsigned int[256]; + + signal_event().connect( sigc::mem_fun(*this, &MyDiagonalCurve::handleEvents) ); + + curve.type = DCT_Spline; + + curve.x.push_back(0.); + curve.y.push_back(0.); + curve.x.push_back(1.); + curve.y.push_back(1.); +} + +MyDiagonalCurve::~MyDiagonalCurve () { + + delete [] bghist; +} + +std::vector MyDiagonalCurve::get_vector (int veclen) { + + std::vector vector; + vector.resize (veclen); + + if (curve.type != DCT_Parametric) { + // count active points: + double prev =- 1.0; + int active = 0; + int firstact = -1; + for (int i = 0; i < (int)curve.x.size(); ++i) + if (curve.x[i] > prev) { + if (firstact < 0) + firstact = i; + prev = curve.x[i]; + ++active; + } + // handle degenerate case: + if (active < 2) { + double ry; + if (active > 0) + ry = curve.y[firstact]; + else + ry = 0.0; + if (ry < 0.0) ry = 0.0; + if (ry > 1.0) ry = 1.0; + for (int x = 0; x < veclen; ++x) + vector[x] = ry; + return vector; + } + } + + // calculate remaining points + std::vector curveDescr = getPoints (); + rtengine::DiagonalCurve* rtcurve = new rtengine::DiagonalCurve (curveDescr, veclen*1.5); + std::vector t; + t.resize (veclen); + for (int i = 0; i < veclen; i++) + t[i] = (double) i / (veclen - 1.0); + rtcurve->getVal (t, vector); + delete rtcurve; + return vector; +} + +void MyDiagonalCurve::interpolate () { + + prevInnerHeight = innerHeight; + point.resize (innerWidth); + std::vector vector = get_vector (innerWidth); + prevInnerHeight = innerHeight; + for (int i = 0; i < innerWidth; ++i) + point[i] = Gdk::Point (RADIUS + i, RADIUS + innerHeight - (int)((innerHeight-1) * vector[i] + 0.5)); + upoint.clear (); + lpoint.clear (); + + if (curve.type==DCT_Parametric && activeParam>0) { + double tmp = curve.x[activeParam-1]; + if (activeParam>=4) { + upoint.resize(innerWidth); + lpoint.resize(innerWidth); + curve.x[activeParam-1] = 100; + vector = get_vector (innerWidth); + for (int i = 0; i < innerWidth; ++i) + upoint[i] = Gdk::Point (RADIUS + i, RADIUS + innerHeight - (int)((innerHeight-1) * vector[i] + 0.5)); + curve.x[activeParam-1] = -100; + vector = get_vector (innerWidth); + for (int i = 0; i < innerWidth; ++i) + lpoint[i] = Gdk::Point (RADIUS + i, RADIUS + innerHeight - (int)((innerHeight-1) * vector[i] + 0.5)); + curve.x[activeParam-1] = tmp; + } + } +} + +void MyDiagonalCurve::draw (int handle) { + if (!pixmap) + return; + + // re-calculate curve if dimensions changed + if (prevInnerHeight != innerHeight || (int)point.size() != innerWidth) + interpolate (); + + Gtk::StateType state = Gtk::STATE_NORMAL; + if (!is_sensitive()) + state = Gtk::STATE_INSENSITIVE; + + Glib::RefPtr style = get_style (); + Cairo::RefPtr cr = pixmap->create_cairo_context(); + + // bounding rectangle + Gdk::Color c = style->get_bg (state); + cr->set_source_rgb (c.get_red_p(), c.get_green_p(), c.get_blue_p()); + cr->rectangle (0, 0, innerWidth + RADIUS*2, innerHeight + RADIUS*2); + cr->fill (); + + // histogram in the background + if (bghistvalid) { + // find highest bin + unsigned int histheight = 0; + for (int i=0; i<256; i++) + if (bghist[i]>histheight) + histheight = bghist[i]; + // draw histogram + cr->set_line_width (1.0); + double stepSize = (innerWidth-1) / 256.0; + cr->move_to (RADIUS, innerHeight-1+RADIUS); + cr->set_source_rgb (0.75, 0.75, 0.75); + for (int i=0; i<256; i++) { + double val = bghist[i] * (double)(innerHeight-2) / (double)histheight; + if (val>innerHeight-1) + val = innerHeight-1; + if (i>0) + cr->line_to (i*stepSize+RADIUS, innerHeight-1+RADIUS-val); + } + cr->line_to (innerWidth-1+RADIUS, innerHeight-1+RADIUS); + cr->fill (); + } + + // draw the grid lines: + cr->set_line_width (1.0); + c = style->get_dark (state); + cr->set_source_rgb (c.get_red_p(), c.get_green_p(), c.get_blue_p()); + cr->set_antialias (Cairo::ANTIALIAS_NONE); + for (int i = 0; i < 5; i++) { // + 0.5 to align well with f(x)=x so it will cut through the center + cr->move_to (RADIUS, MAX(0,i * (innerHeight + 0.5) / 4) + RADIUS); + cr->line_to (innerWidth + RADIUS, MAX(0,i * (innerHeight + 0.5) / 4) + RADIUS); + cr->move_to (MAX(0,i * innerWidth / 4) + RADIUS, RADIUS); + cr->line_to (MAX(0,i * innerWidth / 4) + RADIUS, innerHeight + RADIUS); + } + cr->stroke (); + + // draw f(x)=x line + cr->set_source_rgb (c.get_red_p(), c.get_green_p(), c.get_blue_p()); + std::valarray ds (1); + ds[0] = 4; + cr->set_dash (ds, 0); + cr->move_to (RADIUS, innerHeight + RADIUS); + cr->line_to (innerWidth + RADIUS, RADIUS); + cr->stroke (); + cr->unset_dash (); + + cr->set_antialias (Cairo::ANTIALIAS_SUBPIXEL); + cr->set_line_width (1.0); + + // draw upper and lower bounds + if (curve.type==DCT_Parametric && activeParam>0 && lpoint.size()>1 && upoint.size()>1) { + cr->set_source_rgba (0.0, 0.0, 0.0, 0.15); + cr->move_to (upoint[0].get_x(), upoint[0].get_y()); + for (int i=1; i<(int)upoint.size(); i++) + cr->line_to (upoint[i].get_x(), upoint[i].get_y()); + cr->line_to (lpoint[lpoint.size()-1].get_x(), lpoint[lpoint.size()-1].get_y()); + for (int i=(int)lpoint.size()-2; i>=0; i--) + cr->line_to (lpoint[i].get_x(), lpoint[i].get_y()); + cr->line_to (upoint[0].get_x(), upoint[0].get_y()); + cr->fill (); + } + + // draw the cage of the NURBS curve + if (curve.type==DCT_NURBS) { + std::valarray ch_ds (1); + ch_ds[0] = 2; + cr->set_dash (ch_ds, 0); + cr->set_source_rgb (0.0, 0.0, 0.0); + std::vector points = getPoints(); + for (int i = 1; i < (int)points.size(); ) { + double x = ((innerWidth-1) * points[i++] + 0.5)+RADIUS; // project (curve.x[i], 0, 1, innerWidth); + double y = innerHeight - ((innerHeight-1) * points[i++] + 0.5)+RADIUS; // project (curve.y[i], 0, 1, innerHeight); + if (i==3) + cr->move_to (x, y); + else + cr->line_to (x, y); + } + cr->stroke (); + cr->unset_dash (); + } + + // draw curve + cr->set_source_rgb (0.0, 0.0, 0.0); + cr->move_to (point[0].get_x(), point[0].get_y()); + for (int i=1; i<(int)point.size(); i++) + cr->line_to (point[i].get_x(), point[i].get_y()); + cr->stroke (); + + // draw bullets + if (curve.type!=DCT_Parametric) + for (int i = 0; i < (int)curve.x.size(); ++i) { + cr->set_source_rgb ((i == handle ? 1.0 : 0.0), 0.0, 0.0); + double x = ((innerWidth-1) * curve.x[i] + 0.5)+RADIUS; // project (curve.x[i], 0, 1, innerWidth); + double y = innerHeight - ((innerHeight-1) * curve.y[i] + 0.5)+RADIUS; // project (curve.y[i], 0, 1, innerHeight); + + cr->arc (x, y, RADIUS+0.5, 0, 2*M_PI); + cr->fill (); + } + + get_window()->draw_drawable (style->get_fg_gc (state), pixmap, 0, 0, 0, 0, innerWidth + RADIUS * 2, innerHeight + RADIUS * 2); +} + +bool MyDiagonalCurve::handleEvents (GdkEvent* event) { + + CursorShape new_type = cursor_type; + int src, dst; + std::vector::iterator itx, ity; + + bool retval = false; + int num = (int)curve.x.size(); + + /* innerWidth and innerHeight are the size of the graph */ + innerWidth = get_allocation().get_width() - RADIUS * 2; + innerHeight = get_allocation().get_height() - RADIUS * 2; + + double minDistanceX = (double)(MIN_DISTANCE) / (double)(innerWidth-1); + double minDistanceY = (double)(MIN_DISTANCE) / (double)(innerHeight-1); + + if ((innerWidth < 0) || (innerHeight < 0)) + return false; + + switch (event->type) { + case Gdk::CONFIGURE: + if (pixmap) + pixmap.clear (); + retval = true; + break; + + case Gdk::EXPOSE: + if (!pixmap) { + pixmap = Gdk::Pixmap::create (get_window(), get_allocation().get_width(), get_allocation().get_height()); + interpolate (); + } + draw (lit_point); + retval = true; + break; + + case Gdk::BUTTON_PRESS: + if (curve.type!=DCT_Parametric) { + if (event->button.button == 1) { + buttonPressed = true; + add_modal_grab (); + + // get the pointer position + getCursorPosition(event); + findClosestPoint(); + + new_type = CSMove; + if (distanceX > minDistanceX) { + /* insert a new control point */ + if (num > 0) { + if (clampedX > curve.x[closest_point]) + ++closest_point; + } + itx = curve.x.begin(); + ity = curve.y.begin(); + for (int i=0; ibutton.button == 1) { + buttonPressed = false; + /* get the pointer position */ + getCursorPosition(event); + findClosestPoint(); + + remove_modal_grab (); + int previous_lit_point = lit_point; + /* delete inactive points: */ + itx = curve.x.begin(); + ity = curve.y.begin(); + for (src = dst = 0; src < num; ++src) + if (curve.x[src] >= 0.0) { + curve.x[dst] = curve.x[src]; + curve.y[dst] = curve.y[src]; + ++dst; + ++itx; + ++ity; + } + if (dst < src) { + curve.x.erase (itx, curve.x.end()); + curve.y.erase (ity, curve.y.end()); + if (!curve.x.size()) { + curve.x.push_back (0); + curve.y.push_back (0); + interpolate (); + draw (lit_point); + } + } + if (distanceX <= minDistanceX) { + new_type = CSMove; + lit_point = closest_point; + } + else { + new_type = CSPlus; + lit_point = -1; + } + if (lit_point != previous_lit_point) + draw (lit_point); + grab_point = -1; + retval = true; + notifyListener (); + } + } + break; + + case Gdk::LEAVE_NOTIFY: + // Pointer can LEAVE even when dragging the point, so we don't modify the cursor in this case + // The cursor will have to LEAVE another time after the drag... + if (!buttonPressed) + if (grab_point == -1) { + new_type = CSArrow; + lit_point = -1; + draw (lit_point); + } + retval = true; + break; + + case Gdk::MOTION_NOTIFY: + if (curve.type == DCT_Linear || curve.type == DCT_Spline || curve.type == DCT_NURBS) { + // get the pointer position + getCursorPosition(event); + + if (grab_point == -1) { + // there's no point currently being moved + int previous_lit_point = lit_point; + findClosestPoint(); + if (distanceX <= minDistanceX) { + new_type = CSMove; + lit_point = closest_point; + } + else { + new_type = CSPlus; + lit_point = -1; + } + if (lit_point != previous_lit_point) + draw (lit_point); + } + else { + // a point is being moved + + // bounds of the grabbed point + double leftBound = (grab_point == 0 ) ? 0. : curve.x[grab_point-1]; + double rightBound = (grab_point == num-1) ? 1. : curve.x[grab_point+1]; + double const bottomBound = 0.; + double const topBound = 1.; + + double leftDeletionBound = leftBound - minDistanceX; + double rightDeletionBound = rightBound + minDistanceX; + double bottomDeletionBound = bottomBound - minDistanceY; + double topDeletionBound = topBound + minDistanceY; + + // we memorize the previous position of the point, for optimization purpose + double prevPosX = curve.x[grab_point]; + double prevPosY = curve.y[grab_point]; + + // we memorize the previous position of the point, for optimization purpose + ugpX += deltaX; + ugpY += deltaY; + + // handling limitations along X axis + if (ugpX >= rightDeletionBound && (grab_point > 0 && grab_point < (num-1))) { + curve.x[grab_point] = -1.; + } + else if (ugpX <= leftDeletionBound && (grab_point > 0 && grab_point < (num-1))) { + curve.x[grab_point] = -1.; + } + else + // nextPosX is in bounds + curve.x[grab_point] = CLAMP(ugpX, leftBound, rightBound); + + // Handling limitations along Y axis + if (ugpY >= topDeletionBound && grab_point != 0 && grab_point != num-1) { + curve.x[grab_point] = -1.; + } + else if (ugpY <= bottomDeletionBound && grab_point != 0 && grab_point != num-1) { + curve.x[grab_point] = -1.; + } + else + // nextPosY is in the bounds + curve.y[grab_point] = CLAMP(ugpY, 0.0, 1.0); + + if (curve.x[grab_point] != prevPosX || curve.y[grab_point] != prevPosY) { + // we recalculate the curve only if we have to + interpolate (); + draw (lit_point); + notifyListener (); + } + } + } + + retval = true; + break; + + default: + break; + } + if (new_type != cursor_type) { + cursor_type = new_type; + cursorManager.setCursor(cursor_type); + } + return retval; +} + +void MyDiagonalCurve::getCursorPosition(GdkEvent* event) { + int tx, ty; + int prevCursorX, prevCursorY; + double incrementX = 1. / (double)(innerWidth-1); + double incrementY = 1. / (double)(innerHeight-1); + + // getting the cursor position + switch (event->type) { + case (Gdk::MOTION_NOTIFY) : + if (event->motion.is_hint) { + get_window()->get_pointer (tx, ty, mod_type); + } + else { + tx = (int)event->button.x; + ty = (int)event->button.y; + mod_type = (Gdk::ModifierType)event->button.state; + } + break; + case (Gdk::BUTTON_PRESS) : + case (Gdk::BUTTON_RELEASE) : + tx = (int)event->button.x; + ty = (int)event->button.y; + mod_type = (Gdk::ModifierType)event->button.state; + break; + default : + // The cursor position is not available + return; + break; + } + + if (grab_point != -1) { + prevCursorX = cursorX; + prevCursorY = cursorY; + } + cursorX = tx - RADIUS; + cursorY = (innerHeight-1) - (ty - RADIUS); + + snapTo = ST_None; + + // update deltaX/Y if the user drags a point + if (grab_point != -1) { + // set the dragging factor + int control_key = mod_type & GDK_CONTROL_MASK; + int shift_key = mod_type & GDK_SHIFT_MASK; + + // the increment get smaller if modifier key are used, and "snap to" may be enabled + if (control_key && shift_key) { snapTo = ST_Neighbors; } + else if (control_key) { snapTo = ST_Identity; } + else if (shift_key) { incrementX *= 0.04; incrementY *= 0.04; } + + deltaX = (double)(cursorX - prevCursorX) * incrementX; + deltaY = (double)(cursorY - prevCursorY) * incrementY; + } + // otherwise set the position of the new point (modifier keys has no effect here) + else { + double tempCursorX = cursorX * incrementX; + double tempCursorY = cursorY * incrementY; + clampedX = CLAMP (tempCursorX, 0., 1.); // X position of the pointer from the origin of the graph + clampedY = CLAMP (tempCursorY, 0., 1.); // Y position of the pointer from the origin of the graph + } + +} + +void MyDiagonalCurve::findClosestPoint() { + distanceX = 10.0; distanceY = 10.0; + closest_point = -1; + + if (curve.type!=DCT_Parametric) { + for (int i = 0; i < (int)curve.x.size(); i++) { + double dX = curve.x[i] - clampedX; + double dY = curve.y[i] - clampedY; + double currDistX = dX < 0. ? -dX : dX; //abs (dX); + double currDistY = dY < 0. ? -dY : dY; //abs (dY); + if (currDistX < distanceX) { + distanceX = currDistX; + distanceY = currDistY; + closest_point = i; + } + else if (currDistX == distanceX && currDistY < distanceY) { + // there is more than 1 point for that X coordinate, we select the closest point to the cursor + distanceY = currDistY; + closest_point = i; + } + } + } +} + +std::vector MyDiagonalCurve::getPoints () { + std::vector result; + if (curve.type==DCT_Parametric) { + result.push_back ((double)(DCT_Parametric)); + for (int i=0; i<(int)curve.x.size(); i++) { + result.push_back (curve.x[i]); + } + } + else { + // the first value gives the type of the curve + if (curve.type==DCT_Linear) + result.push_back ((double)(DCT_Linear)); + else if (curve.type==DCT_Spline) + result.push_back ((double)(DCT_Spline)); + else if (curve.type==DCT_NURBS) + result.push_back ((double)(DCT_NURBS)); + // then we push all the points coordinate + for (int i=0; i<(int)curve.x.size(); i++) { + if (curve.x[i]>=0) { + result.push_back (curve.x[i]); + result.push_back (curve.y[i]); + } + } + } + return result; +} + +void MyDiagonalCurve::setPoints (const std::vector& p) { + int ix = 0; + DiagonalCurveType t = (DiagonalCurveType)p[ix++]; + curve.type = t; + if (t==DCT_Parametric) { + curve.x.clear (); + curve.y.clear (); + for (int i=1; i<(int)p.size(); i++) + curve.x.push_back (p[ix++]); + } + else { + curve.x.clear (); + curve.y.clear (); + for (int i=0; i<(int)p.size()/2; i++) { + curve.x.push_back (p[ix++]); + curve.y.push_back (p[ix++]); + } + activeParam = -1; + } + pixmap.clear (); + queue_draw (); +} + +void MyDiagonalCurve::setType (DiagonalCurveType t) { + + curve.type = t; + pixmap.clear (); +} + +void MyDiagonalCurve::setActiveParam (int ac) { + + activeParam = ac; + pixmap.clear (); + queue_draw (); +} + +int diagonalmchistupdate (void* data) { + + gdk_threads_enter (); + + MyCurveIdleHelper* mcih = (MyCurveIdleHelper*)data; + + if (mcih->destroyed) { + if (mcih->pending == 1) + delete mcih; + else + mcih->pending--; + gdk_threads_leave (); + return 0; + } + + mcih->clearPixmap (); + mcih->myCurve->queue_draw (); + + mcih->pending--; + gdk_threads_leave (); + return 0; +} + +void MyDiagonalCurve::updateBackgroundHistogram (unsigned int* hist) { + + if (hist!=NULL) { + memcpy (bghist, hist, 256*sizeof(unsigned int)); + bghistvalid = true; + } + else + bghistvalid = false; + + mcih->pending++; + g_idle_add (diagonalmchistupdate, mcih); + +} + +void MyDiagonalCurve::reset() { + innerWidth = get_allocation().get_width() - RADIUS * 2; + innerHeight = get_allocation().get_height() - RADIUS * 2; + + switch (curve.type) { + case DCT_Spline : + case DCT_NURBS : + curve.x.clear(); + curve.y.clear(); + curve.x.push_back(0.); + curve.y.push_back(0.); + curve.x.push_back(1.); + curve.y.push_back(1.); + grab_point = -1; + lit_point = -1; + interpolate (); + break; + case DCT_Parametric : + // Nothing to do (?) + default: + break; + } + draw(-1); +} diff --git a/rtgui/mydiagonalcurve.h b/rtgui/mydiagonalcurve.h new file mode 100644 index 000000000..3a7764b31 --- /dev/null +++ b/rtgui/mydiagonalcurve.h @@ -0,0 +1,83 @@ +/* + * 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 . + */ +#ifndef _MYDIAGONALCURVE_ +#define _MYDIAGONALCURVE_ + +#include +#include +#include +#include +#include + +// For compatibility and simplicity reason, order shouldn't change, and must be identical to the order specified in the curveType widget +enum DiagonalCurveType { + DCT_Empty = -1, + DCT_Linear, // 0 + DCT_Spline, // 1 + DCT_Parametric, // 2 + DCT_NURBS, // 3 + // Insert new curve type above this line + DCT_Unchanged // Must remain the last of the enum +}; + +class DiagonalCurveDescr { + + public: + DiagonalCurveType type; + std::vector x, y; // in case of parametric curves the curve parameters are stored in vector x. In other cases these vectors store the coordinates of the bullets. +}; + +class MyDiagonalCurve : public MyCurve { + + protected: + DiagonalCurveDescr curve; + int grab_point; // the point that the user is moving + int closest_point; // the point that is the closest from the cursor + int lit_point; // the point that is lit when the cursor is near it + double clampedX; // clamped grabbed point X coordinates in the [0;1] range + double clampedY; // clamped grabbed point Y coordinates in the [0;1] range + double deltaX; // signed X distance of the cursor between two consecutive MOTION_NOTIFY + double deltaY; // signed Y distance of the cursor between two consecutive MOTION_NOTIFY + double distanceX; // X distance from the cursor to the closest point + double distanceY; // Y distance from the cursor to the closest point + double ugpX; // unclamped grabbed point X coordinate in the graph + double ugpY; // unclamped grabbed point Y coordinate in the graph + int activeParam; + unsigned int* bghist; // histogram values + bool bghistvalid; + void draw (int handle); + void interpolate (); + void getCursorPosition(GdkEvent* event); + void findClosestPoint(); + std::vector get_vector (int veclen); + + public: + MyDiagonalCurve (); + ~MyDiagonalCurve (); + std::vector getPoints (); + void setPoints (const std::vector& p); + void setType (DiagonalCurveType t); + bool handleEvents (GdkEvent* event); + void setActiveParam (int ac); + void reset (); + void updateBackgroundHistogram (unsigned int* hist); + +}; + +#endif diff --git a/rtgui/myflatcurve.cc b/rtgui/myflatcurve.cc new file mode 100644 index 000000000..24d49bf33 --- /dev/null +++ b/rtgui/myflatcurve.cc @@ -0,0 +1,1296 @@ +/* + * 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 +#include +#include +#include + +MyFlatCurve::MyFlatCurve () { + + innerWidth = get_allocation().get_width() - RADIUS * 2; + innerHeight = get_allocation().get_height() - RADIUS * 2; + prevInnerHeight = innerHeight; + lit_point = -1; + closest_point = 0; + buttonPressed = false; + editedHandle = FCT_EditedHandle_None; + area = FCT_Area_None; + tanHandlesDisplayed = false; + periodic = true; + + //bghist = new unsigned int[256]; + + signal_event().connect( sigc::mem_fun(*this, &MyFlatCurve::handleEvents) ); + + // By default, we create a curve with 8 control points + curve.type = FCT_MinMaxCPoints; + + defaultCurve(); +} + +/*MyFlatCurve::~MyFlatCurve () { +}*/ + +std::vector MyFlatCurve::get_vector (int veclen) { + + // Create the output variable + std::vector convertedValues; + + // Get the curve control points + std::vector curveDescr = getPoints (); + rtengine::FlatCurve* rtcurve = new rtengine::FlatCurve (curveDescr, veclen*1.5 > 5000 ? 5000 : veclen*1.5); + + // Create the sample values that will be converted + std::vector samples; + samples.resize (veclen); + for (int i = 0; i < veclen; i++) + samples[i] = (double) i / (veclen - 1.0); + + // Converting the values + rtcurve->getVal (samples, convertedValues); + + // Cleanup and return + delete rtcurve; + return convertedValues; +} + +void MyFlatCurve::interpolate () { + + prevInnerHeight = innerHeight; + point.resize (innerWidth+1); + std::vector vector = get_vector (innerWidth+1); + for (int i = 0; i <= innerWidth; ++i) + point[i] = Gdk::Point ((double)RADIUS+0.5 + i, (double)RADIUS+0.5 + (double)innerHeight*(1.-vector[i])); + upoint.clear (); + lpoint.clear (); + + /*if (curve.type==FCT_Parametric && activeParam>0) { + double tmp = curve.x[activeParam-1]; + if (activeParam>=4) { + upoint.resize(innerWidth); + lpoint.resize(innerWidth); + curve.x[activeParam-1] = 100; + vector = get_vector (innerWidth); + for (int i = 0; i < innerWidth; ++i) + upoint[i] = Gdk::Point (RADIUS + i, RADIUS + innerHeight - (int)((innerHeight-1) * vector[i] + 0.5)); + curve.x[activeParam-1] = -100; + vector = get_vector (innerWidth); + for (int i = 0; i < innerWidth; ++i) + lpoint[i] = Gdk::Point (RADIUS + i, RADIUS + innerHeight - (int)((innerHeight-1) * vector[i] + 0.5)); + curve.x[activeParam-1] = tmp; + } + }*/ +} + +void MyFlatCurve::draw () { + if (!pixmap) + return; + + // re-calculate curve if dimensions changed + if (prevInnerHeight != innerHeight || (int)point.size() != (innerWidth+1)) { + interpolate (); + + } + + Gtk::StateType state = Gtk::STATE_NORMAL; + if (!is_sensitive()) + state = Gtk::STATE_INSENSITIVE; + + Glib::RefPtr style = get_style (); + Cairo::RefPtr cr = pixmap->create_cairo_context(); + + // bounding rectangle + Gdk::Color c = style->get_bg (state); + cr->set_source_rgb (c.get_red_p(), c.get_green_p(), c.get_blue_p()); + cr->rectangle (0, 0, innerWidth+RADIUS*2+1.5, innerHeight+RADIUS*2+1.5); + cr->fill (); + + // histogram in the background + /*if (bghistvalid) { + // find highest bin + unsigned int histheight = 0; + for (int i=0; i<256; i++) + if (bghist[i]>histheight) + histheight = bghist[i]; + // draw histogram + cr->set_line_width (1.0); + double stepSize = (innerWidth-1) / 256.0; + cr->move_to (RADIUS, innerHeight-1+RADIUS); + cr->set_source_rgb (0.75, 0.75, 0.75); + for (int i=0; i<256; i++) { + double val = bghist[i] * (double)(innerHeight-2) / (double)histheight; + if (val>innerHeight-1) + val = innerHeight-1; + if (i>0) + cr->line_to (i*stepSize+RADIUS, innerHeight-1+RADIUS-val); + } + cr->line_to (innerWidth-1+RADIUS, innerHeight-1+RADIUS); + cr->fill (); + }*/ + + cr->set_line_cap(Cairo::LINE_CAP_SQUARE); + + // draw the grid lines: + cr->set_line_width (1.0); + cr->set_antialias (Cairo::ANTIALIAS_NONE); + c = style->get_dark (state); + cr->set_source_rgb (c.get_red_p(), c.get_green_p(), c.get_blue_p()); + + double x0 = (double)RADIUS-0.5; + double x1 = (double)RADIUS-0.5 + (double)innerWidth + 2.; + double y0 = (double)RADIUS-0.5; + double y1 = (double)RADIUS-0.5 + (double)innerHeight + 2.; + for (int i = 0; i < 5; i++) { + double currX = (double)RADIUS-0.5 + (double)i*((double)innerWidth + 2.)/4.; + double currY = (double)RADIUS-0.5 + (double)i*((double)innerHeight + 2.)/4.; + cr->move_to (x0, currY); + cr->line_to (x1, currY); + cr->move_to (currX, y0); + cr->line_to (currX, y1); + } + cr->stroke (); + + // draw f(x)=0.5 line + cr->set_source_rgb (c.get_red_p(), c.get_green_p(), c.get_blue_p()); + std::valarray ds (1); + ds[0] = 4; + cr->set_dash (ds, 0); + cr->move_to ((double)RADIUS+0.5 , (double)RADIUS+0.5 + (double)innerHeight/2.); + cr->line_to ((double)RADIUS+0.5 + (double)innerWidth/2., (double)RADIUS+0.5 + (double)innerHeight/2.); + cr->stroke (); + + cr->set_antialias (Cairo::ANTIALIAS_SUBPIXEL); + + cr->unset_dash (); + cr->set_line_width (1.0); + + // draw the color feedback of the control points + if (colorProvider) { + + //if (curve.type!=FCT_Parametric) + for (int i=0; i<(int)curve.x.size(); ++i) { + + if (curve.x[i] != -1.) { + + cr->set_line_width (1.0); + colorProvider->colorForValue(curve.x[i], 0.5); + cr->set_source_rgb (colorProvider->red, colorProvider->green, colorProvider->blue); + + double x = (double)RADIUS+0.5 + innerWidth*curve.x[i]; + + if (i == lit_point && editedHandle&(FCT_EditedHandle_CPointUD|FCT_EditedHandle_CPoint|FCT_EditedHandle_CPointX)) { + cr->set_line_width (2.0); + } + cr->move_to (x, (double)RADIUS+0.5); + cr->line_to (x, (double)RADIUS+0.5 + innerHeight); + cr->stroke (); + cr->set_line_width (1.0); + + // draw the lit_point's horizontal line + if (i == lit_point) { + + if (area&(FCT_Area_H|FCT_Area_V|FCT_Area_Point) || editedHandle==FCT_EditedHandle_CPointUD) { + + if (editedHandle&(FCT_EditedHandle_CPointUD|FCT_EditedHandle_CPoint|FCT_EditedHandle_CPointY)) { + cr->set_line_width (2.0); + } + + colorProvider->colorForValue(curve.x[i], curve.y[i]); + cr->set_source_rgb (colorProvider->red, colorProvider->green, colorProvider->blue); + + double y = (double)RADIUS+0.5 + (double)innerHeight*(1.-curve.y[lit_point]); + cr->move_to ( RADIUS, y); + cr->line_to ((double)RADIUS+0.5 + (double)innerWidth, y); + cr->stroke (); + } + } + } + } + // endif + cr->set_line_width (1.0); + } + else { + cr->set_source_rgb (0.5, 0.0, 0.0); + + if (area==(FCT_Area_H|FCT_Area_V|FCT_Area_Point) || editedHandle==FCT_EditedHandle_CPointUD) { + double position; + + // draw the lit_point's vertical line + if (editedHandle==(FCT_EditedHandle_CPointUD|FCT_EditedHandle_CPoint|FCT_EditedHandle_CPointY)) { + cr->set_line_width (2.0); + } + position = (double)RADIUS+0.5 + (double)innerWidth*curve.x[lit_point]; + cr->move_to (position, (double)RADIUS+0.5); + cr->line_to (position, (double)RADIUS+0.5 + (double)innerHeight); + cr->stroke (); + cr->set_line_width (1.0); + + // draw the lit_point's horizontal line + if (editedHandle==(FCT_EditedHandle_CPointUD|FCT_EditedHandle_CPoint|FCT_EditedHandle_CPointY)) { + cr->set_line_width (2.0); + } + position = (double)RADIUS+0.5 + (double)innerHeight*(1.-curve.y[lit_point]); + cr->move_to ((double)RADIUS+0.5 , position); + cr->line_to ((double)RADIUS+0.5 + (double)innerWidth, position); + cr->stroke (); + cr->set_line_width (1.0); + } + } + + double lineMinLength = 1. / innerWidth * SQUARE * 0.9; + if (lit_point!=-1 && getHandles(lit_point) && curve.x[lit_point]!=-1.) { + double x = (double)RADIUS+0.5 + (double)innerWidth * curve.x[lit_point]; + double y = (double)RADIUS+0.5 + (double)innerHeight * (1.-curve.y[lit_point]); + double x2; + double square; + bool crossingTheFrame; + + // left handle is yellow + cr->set_source_rgb (1.0, 1.0, 0.0); + + // draw tangential vectors + + crossingTheFrame = false; + // We display the line only if it's longer than the handle knot half-size... + if (leftTanX < -0.00001) { + leftTanX += 1.0; + crossingTheFrame = true; + } + x2 = (double)RADIUS+0.5 + (double)innerWidth * leftTanX; + if (curve.x[lit_point] - leftTanX > lineMinLength || crossingTheFrame) { + // The left tangential vector reappear on the right side + // draw the line + cr->move_to (x, y); + if (crossingTheFrame) { + cr->line_to ((double)RADIUS+0.5, y); + cr->stroke (); + cr->move_to ((double)RADIUS+0.5 + (double)innerWidth, y); + } + cr->line_to (x2, y); + cr->stroke (); + } + // draw tangential knot + square = area == FCT_Area_LeftTan ? SQUARE*2. : SQUARE; + cr->move_to(x2-square, y+square); + cr->line_to(x2+square, y+square); + cr->line_to(x2+square, y-square); + cr->line_to(x2-square, y-square); + cr->line_to(x2-square, y+square); + cr->fill(); + + // right handle is blue + cr->set_source_rgb (0.0, 0.0, 1.0); + + // draw tangential vectors + + crossingTheFrame = false; + // We display the line only if it's longer than the handle knot half-size... + if (rightTanX > 1.00001) { + rightTanX -= 1.0; + crossingTheFrame = true; + } + x2 = (double)RADIUS+0.5 + (double)innerWidth * rightTanX; + if (rightTanX - curve.x[lit_point] > lineMinLength || crossingTheFrame) { + // The left tangential vector reappear on the right side + // draw the line + cr->move_to (x, y); + if (crossingTheFrame) { + cr->line_to ((double)RADIUS+0.5 + (double)innerWidth, y); + cr->stroke (); + cr->move_to ((double)RADIUS+0.5, y); + } + cr->line_to (x2, y); + cr->stroke (); + } + // draw tangential knot + square = area == FCT_Area_RightTan ? SQUARE*2. : SQUARE; + cr->move_to(x2-square, y+square); + cr->line_to(x2+square, y+square); + cr->line_to(x2+square, y-square); + cr->line_to(x2-square, y-square); + cr->line_to(x2-square, y+square); + cr->fill(); + } + + // draw curve + cr->set_source_rgb (0.0, 0.0, 0.0); + cr->move_to (point[0].get_x(), point[0].get_y()); + for (int i=1; i<(int)point.size(); i++) + cr->line_to (point[i].get_x(), point[i].get_y()); + cr->stroke (); + + // draw bullets + //if (curve.type!=FCT_Parametric) + for (int i = 0; i < (int)curve.x.size(); ++i) { + if (curve.x[i] != -1.) { + if (i == lit_point) { + if (colorProvider) { + colorProvider->colorForValue(curve.x[i], curve.y[i]); + cr->set_source_rgb (colorProvider->red, colorProvider->green, colorProvider->blue); + } + else + cr->set_source_rgb (1.0, 0.0, 0.0); + } + else if (curve.y[i] == 0.5) + cr->set_source_rgb (0.0, 0.5, 0.0); + else + cr->set_source_rgb (0.0, 0.0, 0.0); + double x = (double)RADIUS+0.5 + (double)innerWidth * curve.x[i]; // project (curve.x[i], 0, 1, innerWidth); + double y = (double)RADIUS+0.5 + (double)innerHeight * (1.-curve.y[i]); // project (curve.y[i], 0, 1, innerHeight); + + cr->arc (x, y, (double)RADIUS, 0, 2*M_PI); + cr->fill (); + } + } + // endif + + // draw the left and right tangent handles + if (tanHandlesDisplayed) { + double top, bottom, left, right; + double halfSquareSizeX, halfSquareSizeY; + + // LEFT handle + halfSquareSizeX = minDistanceX/2.; + halfSquareSizeY = minDistanceY/2.; + //halfSquareSizeX = area == FCT_Area_LeftTan ? minDistanceX : minDistanceX/2.; + //halfSquareSizeY = area == FCT_Area_LeftTan ? minDistanceY : minDistanceY/2.; + top = leftTanHandle.centerY + halfSquareSizeY; + bottom = leftTanHandle.centerY - halfSquareSizeY; + left = leftTanHandle.centerX - halfSquareSizeX; + right = leftTanHandle.centerX + halfSquareSizeX; + + // yellow + cr->set_source_rgb (1.0, 1.0, 0.0); + cr->move_to((double)RADIUS+0.5 + (double)innerWidth * left, (double)RADIUS+0.5 + (double)innerHeight * (1.-top)); + cr->line_to((double)RADIUS+0.5 + (double)innerWidth * right, (double)RADIUS+0.5 + (double)innerHeight * (1.-top)); + cr->line_to((double)RADIUS+0.5 + (double)innerWidth * right, (double)RADIUS+0.5 + (double)innerHeight * (1.-bottom)); + cr->line_to((double)RADIUS+0.5 + (double)innerWidth * left, (double)RADIUS+0.5 + (double)innerHeight * (1.-bottom)); + cr->line_to((double)RADIUS+0.5 + (double)innerWidth * left, (double)RADIUS+0.5 + (double)innerHeight * (1.-top)); + cr->fill(); + + // RIGHT handle + //halfSquareSizeX = area == FCT_Area_RightTan ? minDistanceX : minDistanceX/2.; + //halfSquareSizeY = area == FCT_Area_RightTan ? minDistanceY : minDistanceY/2.; + top = rightTanHandle.centerY + halfSquareSizeY; + bottom = rightTanHandle.centerY - halfSquareSizeY; + left = rightTanHandle.centerX - halfSquareSizeX; + right = rightTanHandle.centerX + halfSquareSizeX; + + // blue + cr->set_source_rgb (0.0, 0.0, 1.0); + cr->move_to((double)RADIUS+0.5 + (double)innerWidth * left, (double)RADIUS+0.5 + (double)innerHeight * (1.-top)); + cr->line_to((double)RADIUS+0.5 + (double)innerWidth * right, (double)RADIUS+0.5 + (double)innerHeight * (1.-top)); + cr->line_to((double)RADIUS+0.5 + (double)innerWidth * right, (double)RADIUS+0.5 + (double)innerHeight * (1.-bottom)); + cr->line_to((double)RADIUS+0.5 + (double)innerWidth * left, (double)RADIUS+0.5 + (double)innerHeight * (1.-bottom)); + cr->line_to((double)RADIUS+0.5 + (double)innerWidth * left, (double)RADIUS+0.5 + (double)innerHeight * (1.-top)); + cr->fill(); + } + + get_window()->draw_drawable (style->get_fg_gc (state), pixmap, 0, 0, 0, 0, innerWidth + RADIUS * 2 + 1, innerHeight + RADIUS * 2 + 1); +} + +/* + * Return the X1, X2, Y position of the tangential handles. + */ +bool MyFlatCurve::getHandles(int n) { + int N = curve.x.size(); + double prevX, nextX; + double prevY, nextY; + double prevTan, nextTan; + double x, y, leftTan, rightTan; + + if (n == -1) return false; + + x = curve.x[n]; + y = curve.y[n]; + leftTan = curve.leftTangent[n]; + rightTan = curve.rightTangent[n]; + + if (!n) { + // first point, the left handle is then computed with the last point's right handle + prevX = curve.x[N-1]-1.0; + prevY = curve.y[N-1]; + prevTan = curve.rightTangent[N-1]; + + nextX = curve.x[n+1]; + nextY = curve.y[n+1]; + nextTan = curve.leftTangent[n+1]; + } + else if (n == N-1) { + // last point, the right handle is then computed with the first point's left handle + prevX = curve.x[n-1]; + prevY = curve.y[n-1]; + prevTan = curve.rightTangent[n-1]; + + nextX = curve.x[0]+1.0; + nextY = curve.y[0]; + nextTan = curve.leftTangent[0]; + } + else { + // last point, the right handle is then computed with the first point's left handle + prevX = curve.x[n-1]; + prevY = curve.y[n-1]; + prevTan = curve.rightTangent[n-1]; + + nextX = curve.x[n+1]; + nextY = curve.y[n+1]; + nextTan = curve.leftTangent[n+1]; + } + + if (leftTan == 0.0) leftTanX = x; + else if (leftTan == 1.0) leftTanX = prevX; + else { + leftTanX = (prevX - x) * leftTan + x; + } + + if (rightTan == 0.0) rightTanX = x; + else if (rightTan == 1.0) rightTanX = nextX; + else { + rightTanX = (nextX - x) * rightTan + x; + } + return true; +} + + +bool MyFlatCurve::handleEvents (GdkEvent* event) { + + CursorShape new_type = cursor_type; + int src, dst; + std::vector::iterator itx, ity, itlt, itrt; + + bool retval = false; + int num = (int)curve.x.size(); + + /* innerWidth and innerHeight are the size of the graph */ + innerWidth = get_allocation().get_width() - 2*RADIUS - 1; + innerHeight = get_allocation().get_height() - 2*RADIUS - 1; + + minDistanceX = (double)(MIN_DISTANCE) / (double)innerWidth; + minDistanceY = (double)(MIN_DISTANCE) / (double)innerHeight; + + if ((innerWidth < 0) || (innerHeight < 0)) + return false; + + switch (event->type) { + case Gdk::CONFIGURE: { + GdkEventConfigure* cEvent = (GdkEventConfigure*)event; + printf("MyFlatCurve::handleEvents - CONFIGURE (%d, %d, %d, %d)\n", cEvent->x, cEvent->y, cEvent->width, cEvent->height); + if (!sized) { + int size = get_allocation().get_width(); + set_size_request(size, size); + sized = true; + } + if (pixmap) + pixmap.clear (); + retval = true; + break; + } + case Gdk::EXPOSE: + if (!sized) { + set_size_request(GRAPH_SIZE + RADIUS + 1, GRAPH_SIZE + RADIUS + 1); + } + sized = false; + printf("MyFlatCurve::handleEvents - EXPOSE\n"); + if (!pixmap) { + pixmap = Gdk::Pixmap::create (get_window(), get_allocation().get_width(), get_allocation().get_height()); + interpolate (); + } + draw (); + retval = true; + break; + + case Gdk::BUTTON_PRESS: + //if (curve.type!=FCT_Parametric) { + if (event->button.button == 1) { + buttonPressed = true; + add_modal_grab (); + + // get the pointer position + getCursorPosition(event); + getMouseOverArea(); + + // hide the tangent handles + tanHandlesDisplayed = false; + + // Action on BUTTON_PRESS and no edited point + switch (area) { + + case (FCT_Area_Insertion): + new_type = CSMove; + + /* insert a new control point */ + if (num > 0) { + if (clampedX > curve.x[closest_point]) + ++closest_point; + } + itx = curve.x.begin(); + ity = curve.y.begin(); + itlt = curve.leftTangent.begin(); + itrt = curve.rightTangent.begin(); + for (int i=0; ibutton.button == 1) { + buttonPressed = false; + enum MouseOverAreas prevArea = area; + remove_modal_grab (); + + int previous_lit_point = lit_point; + + // Removing any deleted point if we were previously modifying the point position + if (editedHandle & (FCT_EditedHandle_CPoint|FCT_EditedHandle_CPointX|FCT_EditedHandle_CPointY)) { + /* delete inactive points: */ + itx = curve.x.begin(); + ity = curve.y.begin(); + itlt = curve.leftTangent.begin(); + itrt = curve.rightTangent.begin(); + for (src = dst = 0; src < num; ++src) + if (curve.x[src] >= 0.0) { + curve.x[dst] = curve.x[src]; + curve.y[dst] = curve.y[src]; + curve.leftTangent[dst] = curve.leftTangent[src]; + curve.rightTangent[dst] = curve.rightTangent[src]; + ++dst; + ++itx; + ++ity; + ++itlt; + ++itrt; + } + if (dst < src) { + + // curve cleanup + curve.x.erase (itx, curve.x.end()); + curve.y.erase (ity, curve.y.end()); + curve.leftTangent.erase (itlt, curve.leftTangent.end()); + curve.rightTangent.erase (itrt, curve.rightTangent.end()); + if (!curve.x.size()) { + curve.x.push_back (0.5); + curve.y.push_back (0.5); + curve.leftTangent.push_back (0.3); + curve.rightTangent.push_back (0.3); + interpolate (); + } + } + } + + editedHandle = FCT_EditedHandle_None; + lit_point = -1; + + // get the pointer position + getCursorPosition(event); + getMouseOverArea(); + + switch (area) { + + case (FCT_Area_Insertion): + new_type = CSArrow; + break; + + case (FCT_Area_Point): + new_type = CSMove; + break; + + case (FCT_Area_H): + new_type = CSResizeHeight; + break; + + case (FCT_Area_V): + new_type = CSMove; + break; + + case (FCT_Area_LeftTan): + new_type = CSEmpty; + break; + + case (FCT_Area_RightTan): + new_type = CSEmpty; + break; + + default: + break; + } + + if ((lit_point != previous_lit_point) || (prevArea != area)) + draw (); + retval = true; + //notifyListener (); + } + //} + break; + + case Gdk::MOTION_NOTIFY: + if (curve.type == FCT_Linear || curve.type == FCT_MinMaxCPoints) { + int previous_lit_point = lit_point; + enum MouseOverAreas prevArea = area; + // get the pointer position + getCursorPosition(event); + getMouseOverArea(); + + if (editedHandle == FCT_EditedHandle_CPointUD) { + double dX = deltaX; + double dY = deltaY; + if (dX < 0.) dX = -dX; + if (dY < 0.) dY = -dY; + if (dX > dY) { + editedHandle = FCT_EditedHandle_CPointX; + area = FCT_Area_V; + new_type = CSResizeWidth; + } + else { + editedHandle = FCT_EditedHandle_CPointY; + area = FCT_Area_H; + new_type = CSResizeHeight; + } + } + + switch (editedHandle) { + + case (FCT_EditedHandle_None): { + + if ((lit_point != -1 && previous_lit_point != lit_point) && area & (FCT_Area_V|FCT_Area_Point)) { + + bool sameSide = false; + + // display the handles + tanHandlesDisplayed = true; + + if (curve.x[lit_point] < 3.*minDistanceX) { + // lit_point too near of the left border -> both handles are displayed on the right of the vertical line + rightTanHandle.centerX = leftTanHandle.centerX = curve.x[lit_point] + 2.*minDistanceX; + sameSide = true; + } + else if (curve.x[lit_point] > 1.-3.*minDistanceX) { + // lit_point too near of the left border -> both handles are displayed on the right of the vertical line + rightTanHandle.centerX = leftTanHandle.centerX = curve.x[lit_point] - 2.*minDistanceX; + sameSide = true; + } + else { + leftTanHandle.centerX = curve.x[lit_point] - 2.*minDistanceX; + rightTanHandle.centerX = curve.x[lit_point] + 2.*minDistanceX; + } + if (sameSide) { + if (clampedY > 1.-2.*minDistanceY) { + // lit_point too near of the top border + leftTanHandle.centerY = 1. - minDistanceY; + rightTanHandle.centerY = 1. - 3.*minDistanceY; + } + else if (clampedY < 2.*minDistanceY) { + // lit_point too near of the bottom border + leftTanHandle.centerY = 3.*minDistanceY; + rightTanHandle.centerY = minDistanceY; + } + else { + leftTanHandle.centerY = clampedY + minDistanceY; + rightTanHandle.centerY = clampedY - minDistanceY; + } + } + else { + if (clampedY > 1.-minDistanceY) { + rightTanHandle.centerY = leftTanHandle.centerY = 1. - minDistanceY; + } + else if (clampedY < minDistanceY) { + rightTanHandle.centerY = leftTanHandle.centerY = minDistanceY; + } + else { + rightTanHandle.centerY = leftTanHandle.centerY = clampedY; + } + } + } + else if (lit_point == -1) { + tanHandlesDisplayed = false; + } + + + switch (area) { + + case (FCT_Area_Insertion): + new_type = CSPlus; + break; + case (FCT_Area_Point): + //new_type = CSMove; + //break; + case (FCT_Area_V): + new_type = CSMove; + break; + case (FCT_Area_H): + new_type = CSResizeHeight; + break; + case (FCT_Area_LeftTan): + new_type = CSMoveLeft; + break; + case (FCT_Area_RightTan): + new_type = CSMoveRight; + break; + case (FCT_Area_None): + default: + new_type = CSArrow; + break; + } + + if ((lit_point != previous_lit_point) || (prevArea != area)) + draw (); + break; + } + + case (FCT_EditedHandle_CPoint): + movePoint(true, true); + break; + + case (FCT_EditedHandle_CPointX): + movePoint(true, false); + break; + + case (FCT_EditedHandle_CPointY): + movePoint(false, true); + break; + + case (FCT_EditedHandle_LeftTan): { + double prevValue = ugpX; + + ugpX -= deltaX*3; + ugpX = CLAMP(ugpX, 0., 1.); + curve.leftTangent[lit_point] = ugpX; + + if (ugpX != prevValue) { + interpolate (); + draw (); + notifyListener (); + } + break; + } + + case (FCT_EditedHandle_RightTan): { + double prevValue = ugpX; + + ugpX += deltaX*3; + ugpX = CLAMP(ugpX, 0., 1.); + curve.rightTangent[lit_point] = ugpX; + + if (ugpX != prevValue) { + interpolate (); + draw (); + notifyListener (); + } + break; + } + + // already process before the "switch" instruction + //case (FCT_EditedHandle_CPointUD): + + default: + break; + } + } + + retval = true; + break; + + case Gdk::LEAVE_NOTIFY: + // Pointer can LEAVE even when dragging the point, so we don't modify the cursor in this case + // The cursor will have to LEAVE another time after the drag... + if (editedHandle == FCT_EditedHandle_None) { + new_type = CSArrow; + lit_point = -1; + tanHandlesDisplayed = false; + draw (); + } + retval = true; + break; + + default: + break; + } + if (new_type != cursor_type) { + cursor_type = new_type; + cursorManager.setCursor(cursor_type); + } + return retval; +} + +void MyFlatCurve::movePoint(bool moveX, bool moveY) { + + // bounds of the grabbed point + double leftBound; + double rightBound; + double const bottomBound = 0.; + double const topBound = 1.; + + // we memorize the previous position of the point, for optimization purpose + double prevPosX = curve.x[lit_point]; + double prevPosY = curve.y[lit_point]; + + int nbPoints = (int)curve.x.size(); + + // left and right bound rely on curve periodicity + leftBound = (lit_point == 0 ) ? (periodic ? curve.x[nbPoints-1]-1. : 0.) : curve.x[lit_point-1]; + rightBound = (lit_point == nbPoints-1) ? (periodic ? curve.x[0]+1. : 1.) : curve.x[lit_point+1]; + + double leftDeletionBound = leftBound - minDistanceX; + double rightDeletionBound = rightBound + minDistanceX; + double bottomDeletionBound = bottomBound - minDistanceY; + double topDeletionBound = topBound + minDistanceY; + + if (moveX) { + // we memorize the previous position of the point, for optimization purpose + ugpX += deltaX; + + // handling periodicity (the first and last point can reappear at the other side of the X range) + if (periodic) { + if (lit_point==0 && ugpX<0.) { + // the first point has to be placed at the tail of the point list + std::vector::iterator itx, ity, itlt, itrt; + + ugpX += 1.; + leftBound += 1.; + rightBound += 1.; + leftDeletionBound += 1.; + rightDeletionBound += 1.; + + // adding a copy of the first point to the tail of the list + curve.x.push_back(curve.x[0]); + curve.y.push_back(curve.y[0]); + curve.leftTangent.push_back(curve.leftTangent[0]); + curve.rightTangent.push_back(curve.rightTangent[0]); + + // deleting the first point + itx = curve.x.begin(); + ity = curve.y.begin(); + itlt = curve.leftTangent.begin(); + itrt = curve.rightTangent.begin(); + + curve.x.erase(itx); + curve.y.erase(ity); + curve.leftTangent.erase(itlt); + curve.rightTangent.erase(itrt); + + lit_point = nbPoints-1; + } + else if (lit_point==(nbPoints-1) && ugpX>1.) { + // the last point has to be placed at the head of the point list + std::vector::iterator itx, ity, itlt, itrt; + + ugpX -= 1.; + leftBound -= 1.; + rightBound -= 1.; + leftDeletionBound -= 1.; + rightDeletionBound -= 1.; + + // adding a copy of the last point to the head of the list + itx = curve.x.begin(); + ity = curve.y.begin(); + itlt = curve.leftTangent.begin(); + itrt = curve.rightTangent.begin(); + + curve.x.insert(itx,0); + curve.y.insert(ity,0); + curve.leftTangent.insert(itlt,0); + curve.rightTangent.insert(itrt,0); + + curve.x[0] = curve.x[nbPoints]; + curve.y[0] = curve.y[nbPoints]; + curve.leftTangent[0] = curve.leftTangent[nbPoints]; + curve.rightTangent[0] = curve.rightTangent[nbPoints]; + + // deleting the last point + curve.x.pop_back(); + curve.y.pop_back(); + curve.leftTangent.pop_back(); + curve.rightTangent.pop_back(); + + lit_point = 0; + } + } + + // handling limitations along X axis + if (ugpX >= rightDeletionBound && nbPoints>2) { + curve.x[lit_point] = -1.; + } + else if (ugpX <= leftDeletionBound && nbPoints>2) { + curve.x[lit_point] = -1.; + } + else + // nextPosX is in bounds + curve.x[lit_point] = CLAMP(ugpX, leftBound, rightBound); + } + + if (moveY) { + // we memorize the previous position of the point, for optimization purpose + ugpY += deltaY; + + // Handling limitations along Y axis + if (ugpY >= topDeletionBound && nbPoints>2) { + if (curve.x[lit_point] != -1.) { + deletedPointX = curve.x[lit_point]; + curve.x[lit_point] = -1.; + curve.y[lit_point] = ugpY; // This is only to force the redraw of the curve + } + } + else if (ugpY <= bottomDeletionBound && nbPoints>2) { + if (curve.x[lit_point] != -1.) { + deletedPointX = curve.x[lit_point]; + curve.x[lit_point] = -1.; + curve.y[lit_point] = ugpY; // This is only to force the redraw of the curve + } + } + else { + // nextPosY is in the bounds + curve.y[lit_point] = CLAMP(ugpY, 0.0, 1.0); + if (!moveX && curve.x[lit_point] == -1.) { + // bring back the X value of the point if it reappear + curve.x[lit_point] = deletedPointX; + } + } + } + + if (curve.x[lit_point] != prevPosX || curve.y[lit_point] != prevPosY) { + // we recalculate the curve only if we have to + interpolate (); + draw (); + notifyListener (); + } +} + +// Set datas relative to cursor position +void MyFlatCurve::getCursorPosition(GdkEvent* event) { + int tx, ty; + int prevCursorX, prevCursorY; + double incrementX = 1. / (double)innerWidth; + double incrementY = 1. / (double)innerHeight; + + switch (event->type) { + case (Gdk::MOTION_NOTIFY) : + if (event->motion.is_hint) { + get_window()->get_pointer (tx, ty, mod_type); + } + else { + tx = (int)event->button.x; + ty = (int)event->button.y; + mod_type = (Gdk::ModifierType)event->button.state; + } + break; + case (Gdk::BUTTON_PRESS) : + case (Gdk::BUTTON_RELEASE) : + tx = (int)event->button.x; + ty = (int)event->button.y; + mod_type = (Gdk::ModifierType)event->button.state; + break; + default : + // The cursor position is not available + return; + break; + } + + if (editedHandle != FCT_EditedHandle_None) { + prevCursorX = cursorX; + prevCursorY = cursorY; + } + cursorX = tx - RADIUS; + cursorY = innerHeight - (ty - RADIUS); + + preciseCursorX = cursorX * incrementX; + preciseCursorY = cursorY * incrementY; + + snapTo = ST_None; + + // update deltaX/Y if the user drags a point + if (editedHandle != FCT_EditedHandle_None) { + // set the dragging factor + int control_key = mod_type & GDK_CONTROL_MASK; + int shift_key = mod_type & GDK_SHIFT_MASK; + + // the increment get smaller if modifier key are used, and "snap to" may be enabled + if (control_key && shift_key) { snapTo = ST_Neighbors; } + else if (control_key) { snapTo = ST_Identity; } + else if (shift_key) { incrementX *= 0.04; incrementY *= 0.04; } + + deltaX = (double)(cursorX - prevCursorX) * incrementX; + deltaY = (double)(cursorY - prevCursorY) * incrementY; + } + // otherwise set the position of the new point (modifier keys has no effect here) + else { + clampedX = CLAMP (preciseCursorX, 0., 1.); // X position of the pointer from the origin of the graph + clampedY = CLAMP (preciseCursorY, 0., 1.); // Y position of the pointer from the origin of the graph + } + +} + +// Find out the active area under the cursor +void MyFlatCurve::getMouseOverArea () { + + // When dragging an element, editedHandle keep its value + if (editedHandle == FCT_EditedHandle_None) { + + double minDist = 1000; // used to find out the point pointed by the cursor (over it) + double minDistX = 1000; // used to find out the closest point + double dX, dY; + double absDX; + double dist; + bool aboveVLine = false; + + // NB: this function assume that the graph's shape is a square + + //if (curve.type!=Parametric) { + + // we first check first if the cursor is still over a tangent handle + /*if (area = (FCT_Area_LeftTan|FCT_Area_RightTan)) { + if (still over a tangent handle) { + + } + area = FCT_Area_None; + return; + }*/ + + // Check if the cursor is over a tangent handle + if (tanHandlesDisplayed) { + + if (preciseCursorX>=(leftTanHandle.centerX-minDistanceX) && preciseCursorX<=(leftTanHandle.centerX+minDistanceX+0.00001) + && preciseCursorY>=(leftTanHandle.centerY-minDistanceY) && preciseCursorY<=(leftTanHandle.centerY+minDistanceY)) { + area = FCT_Area_LeftTan; + return; + } + if (preciseCursorX>=(rightTanHandle.centerX-minDistanceX-0.00001) && preciseCursorX<=(rightTanHandle.centerX+minDistanceX) + && preciseCursorY>=(rightTanHandle.centerY-minDistanceY ) && preciseCursorY<=(rightTanHandle.centerY+minDistanceY)) { + area = FCT_Area_RightTan; + return; + } + } + + area = FCT_Area_None; + closest_point = 0; + lit_point = -1; + + for (int i = 0; i < (int)curve.x.size(); i++) { + if (curve.x[i] != -1) { + dX = curve.x[i] - preciseCursorX; + absDX = dX>0 ? dX : -dX; + if (absDX < minDistX) { + minDistX = absDX; + closest_point = i; + lit_point = i; + } + if (absDX <= minDistanceX) { + aboveVLine = true; + dY = curve.y[i] - preciseCursorY; + dist = sqrt(dX*dX + dY*dY); + if (dist < minDist) { + minDist = dist; + } + } + } + } + if (minDist <= minDistanceX) { + // the cursor is over the point + area = FCT_Area_Point; + } + else if (aboveVLine) { + area = FCT_Area_V; + } + else { + // Check if the cursor is in an insertion area + lit_point = -1; + if (preciseCursorX>=0.0 && preciseCursorX<=1.0 && preciseCursorY>=0.0 && preciseCursorY<=1.0) + area = FCT_Area_Insertion; + } + } +} + +std::vector MyFlatCurve::getPoints () { + std::vector result; + /*if (curve.type==FCT_Parametric) { + result.push_back ((double)(Parametric)); + for (int i=0; i<(int)curve.x.size(); i++) { + result.push_back (curve.x[i]); + } + } + else {*/ + // the first value gives the type of the curve + if (curve.type==FCT_Linear) + result.push_back ((double)(FCT_Linear)); + else if (curve.type==FCT_MinMaxCPoints) + result.push_back ((double)(FCT_MinMaxCPoints)); + // then we push all the points coordinate + for (int i=0; i<(int)curve.x.size(); i++) { + if (curve.x[i]>=0) { + result.push_back (curve.x[i]); + result.push_back (curve.y[i]); + result.push_back (curve.leftTangent[i]); + result.push_back (curve.rightTangent[i]); + } + } + //} + return result; +} + +void MyFlatCurve::setPoints (const std::vector& p) { + int ix = 0; + FlatCurveType t = (FlatCurveType)p[ix++]; + curve.type = t; + if (t==FCT_MinMaxCPoints) { + curve.x.clear (); + curve.y.clear (); + curve.leftTangent.clear(); + curve.rightTangent.clear(); + for (int i=0; i<(int)p.size()/4; i++) { + curve.x.push_back (p[ix++]); + curve.y.push_back (p[ix++]); + curve.leftTangent.push_back (p[ix++]); + curve.rightTangent.push_back (p[ix++]); + } + } + pixmap.clear (); + queue_draw (); +} + +void MyFlatCurve::setType (FlatCurveType t) { + + curve.type = t; + pixmap.clear (); +} + +/*int flatmchistupdate (void* data) { + + gdk_threads_enter (); + + MyFlatCurveIdleHelper* mcih = (MyFlatCurveIdleHelper*)data; + + if (mcih->destroyed) { + if (mcih->pending == 1) + delete mcih; + else + mcih->pending--; + gdk_threads_leave (); + return 0; + } + + mcih->clearPixmap (); + mcih->myCurve->queue_draw (); + + mcih->pending--; + gdk_threads_leave (); + return 0; +}*/ + +/*void MyFlatCurve::updateBackgroundHistogram (unsigned int* hist) { + + if (hist!=NULL) { + memcpy (bghist, hist, 256*sizeof(unsigned int)); + bghistvalid = true; + } + else + bghistvalid = false; + + mcih->pending++; + g_idle_add (flatmchistupdate, mcih); + +}*/ + +void MyFlatCurve::reset() { + innerWidth = get_allocation().get_width() - RADIUS * 2; + innerHeight = get_allocation().get_height() - RADIUS * 2; + + switch (curve.type) { + case FCT_MinMaxCPoints : + defaultCurve(); + lit_point = -1; + interpolate (); + break; + //case Parametric : + // Nothing to do (?) + default: + break; + } + draw(); +} + +void MyFlatCurve::defaultCurve () { + + curve.x.clear(); + curve.y.clear(); + curve.leftTangent.clear(); + curve.rightTangent.clear(); + + // Point for RGBCMY colors + for (int i=0; i<6; i++) { + curve.x.push_back((1./6.)*i); + curve.y.push_back(0.5); + curve.leftTangent.push_back(0.35); + curve.rightTangent.push_back(0.35); + } + +} diff --git a/rtgui/myflatcurve.h b/rtgui/myflatcurve.h new file mode 100644 index 000000000..5d5de7621 --- /dev/null +++ b/rtgui/myflatcurve.h @@ -0,0 +1,135 @@ +/* + * 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 . + */ +#ifndef _MYFLATCURVE_ +#define _MYFLATCURVE_ + +#include +#include +#include +#include +#include + +// For compatibility and simplicity reason, order shouldn't change, and must be identical to the order specified in the curveType widget +enum FlatCurveType { + FCT_Empty = -1, + FCT_Linear, // 0 + FCT_MinMaxCPoints, // 1 + //FCT_Parametric, // 2 + // Insert new curve type above this line + FCT_Unchanged // Must remain the last of the enum +}; + +enum MouseOverAreas { + FCT_Area_None = 1<<0, // over a zone that don't have any + FCT_Area_Insertion = 1<<1, // where the user can insert a new point + FCT_Area_Point = 1<<2, // over an existing point + FCT_Area_H = 1<<3, // cursor is on the horizontal line going through the edited control point + FCT_Area_V = 1<<4, // cursor is on the vertical line going through the edited control point + FCT_Area_LeftTan = 1<<5, // in the left tangent edition area + FCT_Area_RightTan = 1<<6 // in the right tangent edition area +}; + +enum EditedHandle { + FCT_EditedHandle_None = 1<<0, + FCT_EditedHandle_CPointUD = 1<<1, // UD stands for Unknown Direction + FCT_EditedHandle_CPoint = 1<<2, + FCT_EditedHandle_CPointX = 1<<3, + FCT_EditedHandle_CPointY = 1<<4, + FCT_EditedHandle_LeftTan = 1<<5, + FCT_EditedHandle_RightTan = 1<<6 +}; + +class FlatCurveDescr { + + public: + FlatCurveType type; + std::vector x, // Range: [0.0 - 1.0] + y, // Range: [0.0 - 1.0], default value = 0.5 + leftTangent, // Range: [0.0 - 1.0], where 1.0 = distance from previous to this point + rightTangent; // Range: [0.0 - 1.0], where 1.0 = distance from this to next point +}; + +class RectArea { + + public: + double top; + double left; + double bottom; + double right; +}; + +class HandlePosition { + + public: + double centerX; + double centerY; +}; + +class MyFlatCurve : public MyCurve { + + protected: + FlatCurveDescr curve; + int closest_point; // the point that is the closest from the cursor + int lit_point; // the point that is lit when the cursor is near it + double clampedX; // clamped grabbed point X coordinates in the [0;1] range + double clampedY; // clamped grabbed point Y coordinates in the [0;1] range + double deltaX; // signed X distance of the cursor between two consecutive MOTION_NOTIFY + double deltaY; // signed Y distance of the cursor between two consecutive MOTION_NOTIFY + double distanceX; // X distance from the cursor to the closest point + double distanceY; // Y distance from the cursor to the closest point + double ugpX; // unclamped grabbed point X coordinate in the graph + double ugpY; // unclamped grabbed point Y coordinate in the graph + double leftTanX; // X position of the left tangent handle + double rightTanX; // X position of the right tangent handle + double preciseCursorX; // X coordinate in the graph of the cursor, as a double value + double preciseCursorY; // Y coordinate in the graph of the cursor, as a double value + double minDistanceX; // X minimal distance before point suppression + double minDistanceY; // Y minimal distance before point suppression + double deletedPointX; // Backup of the X value of the edited point, when deleted while being dragged + //RectArea leftTanHandle; // XY coordinate if the upper left and bottom right corner of the left tangent handle + //RectArea rightTanHandle; // XY coordinate if the upper left and bottom right corner of the right tangent handle + HandlePosition leftTanHandle; // XY coordinate if the upper left and bottom right corner of the left tangent handle + HandlePosition rightTanHandle; // XY coordinate if the upper left and bottom right corner of the right tangent handle + bool tanHandlesDisplayed; // True if the tangent handles are displayed + bool periodic; // Flat curves are periodic by default + enum EditedHandle editedHandle; + bool draggingElement; + enum MouseOverAreas area; + + void draw (); + void movePoint(bool moveX, bool moveY); + void defaultCurve (); + void interpolate (); + void getCursorPosition(GdkEvent* event); + void getMouseOverArea (); + bool getHandles(int n); + std::vector get_vector (int veclen); + + public: + MyFlatCurve (); + //~MyFlatCurve (); + std::vector getPoints (); + void setPoints (const std::vector& p); + void setType (FlatCurveType t); + bool handleEvents (GdkEvent* event); + void reset (); + //void updateBackgroundHistogram (unsigned int* hist); +}; + +#endif diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 820ed8e5a..23f0fc6aa 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -157,18 +157,15 @@ void ParamsEdited::set (bool v) { raw.linenoise = v; equalizer.enabled = v; dirpyrequalizer.enabled = v; - hsvequalizer.enabled = v; for(int i = 0; i < 8; i++) { equalizer.c[i] = v; } for(int i = 0; i < 5; i++) { dirpyrequalizer.mult[i] = v; } - for(int i = 0; i < 8; i++) { - hsvequalizer.sat[i] = v; - hsvequalizer.val[i] = v; - hsvequalizer.hue[i] = v; - } + hsvequalizer.hcurve = v; + hsvequalizer.scurve = v; + hsvequalizer.vcurve = v; exif.clear (); iptc.clear (); } @@ -323,12 +320,9 @@ void ParamsEdited::initFrom (const std::vector for(int i = 0; i < 8; i++) { dirpyrequalizer.mult[i] = dirpyrequalizer.mult[i] && p.dirpyrequalizer.mult[i] == other.dirpyrequalizer.mult[i]; } - hsvequalizer.enabled = hsvequalizer.enabled && p.hsvequalizer.enabled == other.hsvequalizer.enabled; - for(int i = 0; i < 8; i++) { - hsvequalizer.sat[i] = hsvequalizer.sat[i] && p.hsvequalizer.sat[i] == other.hsvequalizer.sat[i]; - hsvequalizer.val[i] = hsvequalizer.val[i] && p.hsvequalizer.val[i] == other.hsvequalizer.val[i]; - hsvequalizer.hue[i] = hsvequalizer.hue[i] && p.hsvequalizer.hue[i] == other.hsvequalizer.hue[i]; - } + hsvequalizer.hcurve = hsvequalizer.hcurve && p.hsvequalizer.hcurve == other.hsvequalizer.hcurve; + hsvequalizer.scurve = hsvequalizer.scurve && p.hsvequalizer.scurve == other.hsvequalizer.scurve; + hsvequalizer.vcurve = hsvequalizer.vcurve && p.hsvequalizer.vcurve == other.hsvequalizer.vcurve; // exif = exif && p.exif==other.exif // iptc = other.iptc; } @@ -474,12 +468,10 @@ void ParamsEdited::combine (rtengine::procparams::ProcParams& toEdit, const rten for(int i = 0; i < 5; i++) { if(dirpyrequalizer.mult[i]) toEdit.dirpyrequalizer.mult[i] = mods.dirpyrequalizer.mult[i]; } - if (hsvequalizer.enabled) toEdit.hsvequalizer.enabled = mods.hsvequalizer.enabled; - for(int i = 0; i < 8; i++) { - if(hsvequalizer.sat[i]) toEdit.hsvequalizer.sat[i] = mods.hsvequalizer.sat[i]; - if(hsvequalizer.val[i]) toEdit.hsvequalizer.val[i] = mods.hsvequalizer.val[i]; - if(hsvequalizer.hue[i]) toEdit.hsvequalizer.hue[i] = mods.hsvequalizer.hue[i]; - } + if (hsvequalizer.hcurve) toEdit.hsvequalizer.hcurve = mods.hsvequalizer.hcurve; + if (hsvequalizer.scurve) toEdit.hsvequalizer.scurve = mods.hsvequalizer.scurve; + if (hsvequalizer.vcurve) toEdit.hsvequalizer.vcurve = mods.hsvequalizer.vcurve; + // if (exif) toEdit.exif==mo.exif = mods.exif==other.exif; // if (iptc;) toEdit.iptc==other.iptc; = mods.iptc==other.iptc;; } diff --git a/rtgui/paramsedited.h b/rtgui/paramsedited.h index 200befbee..f23f4e459 100644 --- a/rtgui/paramsedited.h +++ b/rtgui/paramsedited.h @@ -269,11 +269,9 @@ public: class HSVEqualizerParamsEdited { public: - bool enabled; - bool sat[8]; - bool val[8]; - bool hue[8]; - int hsvchannel; + bool hcurve; + bool scurve; + bool vcurve; }; class RAWParamsEdited { @@ -321,11 +319,9 @@ class ParamsEdited { ColorShiftParamsEdited colorShift; LumaDenoiseParamsEdited lumaDenoise; ColorDenoiseParamsEdited colorDenoise; - DefringeParamsEdited defringe; DirPyrDenoiseParamsEdited dirpyrDenoise; ImpulseDenoiseParamsEdited impulseDenoise; - SHParamsEdited sh; CropParamsEdited crop; CoarseTransformParamsEdited coarse; diff --git a/rtgui/tonecurve.cc b/rtgui/tonecurve.cc index 3134455f7..8bebb3a9b 100644 --- a/rtgui/tonecurve.cc +++ b/rtgui/tonecurve.cc @@ -78,7 +78,7 @@ ToneCurve::ToneCurve () : Gtk::VBox(), FoldableToolPanel(this), expAdd(false),hl curveEditorG = new CurveEditorGroup (M("TP_EXPOSURE_CURVEEDITOR")); curveEditorG->setCurveListener (this); - shape = curveEditorG->addCurve(); + shape = (DiagonalCurveEditor*)curveEditorG->addCurve(CT_Diagonal, ""); // This will add the reset button at the end of the curveType buttons curveEditorG->curveListComplete(); @@ -95,7 +95,7 @@ ToneCurve::ToneCurve () : Gtk::VBox(), FoldableToolPanel(this), expAdd(false),hl hlcomprthresh->setAdjusterListener (this); shcompr->setAdjusterListener (this); contrast->setAdjusterListener (this); - saturation->setAdjusterListener (this); + saturation->setAdjusterListener (this); } void ToneCurve::read (const ProcParams* pp, const ParamsEdited* pedited) { @@ -394,6 +394,6 @@ void ToneCurve::setAdjusterBehavior (bool expadd, bool hlcompadd, bool hlcompthr } void ToneCurve::updateCurveBackgroundHistogram (unsigned* hist) { - + shape->updateBackgroundHistogram (hist); } diff --git a/rtgui/tonecurve.h b/rtgui/tonecurve.h index 006c49f14..5c79b295c 100644 --- a/rtgui/tonecurve.h +++ b/rtgui/tonecurve.h @@ -44,7 +44,7 @@ class ToneCurve : public Gtk::VBox, public AdjusterListener, public FoldableTool bool expAdd,hlcompAdd,hlcompthreshAdd, blackAdd, shcompAdd, brAdd, contrAdd, satAdd, clipDirty, lastAuto; sigc::connection autoconn; CurveEditorGroup* curveEditorG; - CurveEditor* shape; + DiagonalCurveEditor* shape; double nextBr; int nextBl; diff --git a/tools/FlatCurveType.svg b/tools/FlatCurveType.svg new file mode 100644 index 000000000..28df8abc8 --- /dev/null +++ b/tools/FlatCurveType.svg @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + +