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

656 lines
21 KiB
C++

/*
* This file is part of RawTherapee.
*
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
*
* RawTherapee is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RawTherapee is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RawTherapee. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __CURVES_H__
#define __CURVES_H__
#include <glibmm.h>
#include <map>
#include <string>
#include "rt_math.h"
#include "../rtgui/mycurve.h"
#include "../rtgui/myflatcurve.h"
#include "../rtgui/mydiagonalcurve.h"
#include "color.h"
#include "procparams.h"
#include "LUT.h"
#define CURVES_MIN_POLY_POINTS 1000
#include "rt_math.h"
#define CLIPI(a) ((a)>0?((a)<65534?(a):65534):0)
using namespace std;
namespace rtengine {
class ToneCurve;
class ColorAppearance;
class CurveFactory {
friend class Curve;
protected:
// functions calculating the parameters of the contrast curve based on the desired slope at the center
static double solve_upper (double m, double c, double deriv);
static double solve_lower (double m, double c, double deriv);
static double dupper (const double b, const double m, const double c);
static double dlower (const double b, const double m, const double c);
// basic convex function between (0,0) and (1,1). m1 and m2 controls the slope at the start and end point
static inline double basel (double x, double m1, double m2) {
if (x==0.0)
return 0.0;
double k = sqrt ((m1-1.0)*(m1-m2)/2) / (1.0-m2);
double l = (m1-m2) / (1.0-m2) + k;
double lx = xlog(x);
return m2*x + (1.0-m2)*(2.0 - xexp(k*lx))*xexp(l*lx);
}
// basic concave function between (0,0) and (1,1). m1 and m2 controls the slope at the start and end point
static inline double baseu (double x, double m1, double m2) {
return 1.0 - basel(1.0-x, m1, m2);
}
// convex curve between (0,0) and (1,1) with slope m at (0,0). hr controls the highlight recovery
static inline double cupper (double x, double m, double hr) {
if (hr>1.0)
return baseu (x, m, 2.0*(hr-1.0)/m);
double x1 = (1.0-hr)/m;
double x2 = x1 + hr;
if (x>=x2) return 1.0;
if (x<x1) return x*m;
return 1.0 - hr + hr*baseu((x-x1)/hr, m, 0);
}
// concave curve between (0,0) and (1,1) with slope m at (1,1). sr controls the shadow recovery
static inline double clower (double x, double m, double sr) {
return 1.0 - cupper(1.0-x, m, sr);
}
// convex curve between (0,0) and (1,1) with slope m at (0,0). hr controls the highlight recovery
static inline double cupper2 (double x, double m, double hr) {
double x1 = (1.0-hr)/m;
double x2 = x1 + hr;
if (x>=x2) return 1.0;
if (x<x1) return x*m;
return 1.0 - hr + hr*baseu((x-x1)/hr, m, 0.3*hr);
}
static inline double clower2 (double x, double m, double sr) {
//curve for b<0; starts with positive slope and then rolls over toward straight line to x=y=1
float x1 = sr/1.5 + 0.00001;
float y1 = 1-(1-x1)*m;
if (x>x1 || sr<0.001)
return 1-(1-x)*m;
else
return y1+m*(x-x1)-(1-m)*SQR(SQR(1-x/x1));
}
// tone curve base. a: slope (from exp.comp.), b: black point normalized by 65535,
// D: max. x value (can be>1), hr,sr: highlight,shadow recovery
static inline double basecurve (double x, double a, double b, double D, double hr, double sr) {
if (b<0) {
double m = 0.5;//midpoint
double slope = 1.0+b;//slope of straight line between (0,-b) and (1,1)
double y = -b+m*slope;//value at midpoint
if (x>m)
return y + (x - m)*slope;//value on straight line between (m,y) and (1,1)
else
return y*clower2(x/m, slope*m/y, 2.0-sr);
} else {
double slope = a/(1.0-b);
double m = a*D>1.0 ? b/a+(0.25)/slope : b+(1-b)/4;
double y = a*D>1.0 ? 0.25 : (m-b/a)*slope;
if (x<=m)
return b==0 ? x*slope : clower (x/m, slope*m/y, sr) * y;
else if (a*D>1.0)
return y+(1.0-y)*cupper2((x-m)/(D-m), slope*(D-m)/(1.0-y), hr);
else
return y+(x-m)*slope;
}
}
public:
const static double sRGBGamma; // standard average gamma
const static double sRGBGammaCurve; // 2.4 in the curve
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// accurately determine value from integer array with float as index
//linearly interpolate from ends of range if arg is out of bounds
static inline float interp(int *array,float f)
{
int index = CLIPI(floor(f));
float part = (float)((f)-index)*(float)(array[index+1]-array[index]);
return (float)array[index]+part;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// accurately determine value from float array with float as index
//linearly interpolate from ends of range if arg is out of bounds
static inline float flinterp(float *array,float f)
{
int index = CLIPI(floor(f));
float part = ((f)-(float)index)*(array[index+1]-array[index]);
return array[index]+part;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
static inline double centercontrast (double x, double b, double m);
// standard srgb gamma and its inverse
static inline double gamma2 (double x) {
return x <= 0.00304 ? x*12.92 : 1.055*exp(log(x)/sRGBGammaCurve)-0.055;
}
static inline double igamma2 (double x) {
return x <= 0.03928 ? x/12.92 : exp(log((x+0.055)/1.055)*sRGBGammaCurve);
}
// gamma function with adjustable parameters
static inline double gamma (double x, double gamma, double start, double slope, double mul, double add){
return (x <= start ? x*slope : exp(log(x)/gamma)*mul-add);
}
static inline double igamma (double x, double gamma, double start, double slope, double mul, double add){
return (x <= start*slope ? x/slope : exp(log((x+add)/mul)*gamma) );
}
static inline float hlcurve (const float exp_scale, const float comp, const float hlrange, float level)
{
if (comp>0.0) {
float val = level+(hlrange-65536.0);
float Y = val*exp_scale/hlrange;
float R = hlrange/(val*comp);
return log(1.0+Y*comp)*R;
} else {
return exp_scale;
}
}
public:
static void complexCurve (double ecomp, double black, double hlcompr, double hlcomprthresh, double shcompr, double br, double contr,
double gamma_, bool igamma_, procparams::ToneCurveParams::eTCModeId curveMode, const std::vector<double>& curvePoints, procparams::ToneCurveParams::eTCModeId curveMode2, const std::vector<double>& curvePoints2,
LUTu & histogram, LUTu & histogramCropped,
LUTf & hlCurve, LUTf & shCurve,LUTf & outCurve, LUTu & outBeforeCCurveHistogram, ToneCurve & outToneCurve, ToneCurve & outToneCurve2,
int skip=1);
static void curveBW (const std::vector<double>& curvePointsbw, const std::vector<double>& curvePointsbw2, LUTu & histogrambw, LUTu & outBeforeCCurveHistogrambw,
ToneCurve & customToneCurvebw1, ToneCurve & customToneCurvebw2, int skip);
static void curveCL ( bool & clcutili, const std::vector<double>& clcurvePoints, LUTf & clCurve, LUTu & histogramcl, LUTu & outBeforeCLurveHistogram, int skip);
static void complexsgnCurve ( bool & autili, bool & butili, bool & ccutili, bool & clcutili, double saturation, double rstprotection, const std::vector<double>& acurvePoints,
const std::vector<double>& bcurvePoints,const std::vector<double>& cccurvePoints,const std::vector<double>& lccurvePoints, LUTf & aoutCurve, LUTf & boutCurve, LUTf & satCurve, LUTf & lhskCurve,
LUTu & histogramC, LUTu & histogramLC, LUTu & outBeforeCCurveHistogram,LUTu & outBeforeLCurveHistogram,///for chroma
int skip=1);
static void complexLCurve (double br, double contr, const std::vector<double>& curvePoints, LUTu & histogram, LUTu & histogramCropped,
LUTf & outCurve, LUTu & outBeforeCCurveHistogram, int skip, bool & utili);
static void updatechroma (
const std::vector<double>& cccurvePoints,
LUTu & histogramC, LUTu & outBeforeCCurveHistogramC,//for chroma
int skip=1);
static void curveLightBrightColor (
procparams::ColorAppearanceParams::eTCModeId curveMode, const std::vector<double>& curvePoints,
procparams::ColorAppearanceParams::eTCModeId curveMode2, const std::vector<double>& curvePoints2,
procparams::ColorAppearanceParams::eCTCModeId curveMode3, const std::vector<double>& curvePoints3,
LUTu & histogram, LUTu & histogramCropped, LUTu & outBeforeCCurveHistogram,
LUTu & histogramC, LUTu & outBeforeCCurveHistogramC,
ColorAppearance & outColCurve1,
ColorAppearance & outColCurve2,
ColorAppearance & outColCurve3,
int skip=1);
static void RGBCurve (const std::vector<double>& curvePoints, LUTf & outCurve, int skip);
};
class Curve {
class HashEntry {
public:
unsigned short smallerValue;
unsigned short higherValue;
};
protected:
int N;
int ppn; // targeted polyline point number
double* x;
double* y;
// begin of variables used in Parametric curves only
double mc;
double mfc;
double msc;
double mhc;
// end of variables used in Parametric curves only
std::vector<double> poly_x; // X points of the faceted curve
std::vector<double> poly_y; // Y points of the faceted curve
std::vector<HashEntry> hash;
unsigned short hashSize; // hash table's size, between [10, 100, 1000]
double* ypp;
// 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; }
static inline double p10 (double x, double prot) { return x<=0.5 ? CurveFactory::cupper (x*2, 2.0, prot)/2.0 : 0.5 + CurveFactory::clower ((x-0.5)*2, 2.0, prot)/2.0; }
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); }
void fillHash();
public:
Curve ();
virtual ~Curve () {};
void AddPolygons ();
virtual double getVal (double t) const = 0;
virtual void getVal (const std::vector<double>& t, std::vector<double>& res) const = 0;
virtual bool isIdentity () const = 0;
};
class DiagonalCurve : public Curve {
protected:
DiagonalCurveType kind;
unsigned int minSearch; // a effacer!!!
unsigned int maxSearch; // a effacer!!!
unsigned int searchArray[21]; // a effacer!!!
void spline_cubic_set ();
void NURBS_set ();
public:
DiagonalCurve (const std::vector<double>& points, int ppn=CURVES_MIN_POLY_POINTS);
virtual ~DiagonalCurve ();
double getVal (double t) const;
void getVal (const std::vector<double>& t, std::vector<double>& res) const;
bool isIdentity () const { return kind==DCT_Empty; };
};
class FlatCurve : public Curve {
protected:
FlatCurveType kind;
double* leftTangent;
double* rightTangent;
double identityValue;
bool periodic;
void CtrlPoints_set ();
public:
FlatCurve (const std::vector<double>& points, bool isPeriodic = true, int ppn=CURVES_MIN_POLY_POINTS);
virtual ~FlatCurve ();
double getVal (double t) const;
void getVal (const std::vector<double>& t, std::vector<double>& res) const;
bool setIdentityValue (double iVal);
bool isIdentity () const { return kind==FCT_Empty; };
};
class ToneCurve {
public:
LUTf lutToneCurve; // 0xffff range
virtual ~ToneCurve() {};
void Reset();
void Set(Curve *pCurve);
operator bool (void) const { return lutToneCurve; }
};
class ColorAppearance {
public:
LUTf lutColCurve; // 0xffff range
virtual ~ColorAppearance() {};
void Reset();
void Set(Curve *pCurve);
operator bool (void) const { return lutColCurve; }
};
class Lightcurve : public ColorAppearance {
public:
void Apply(float& Li) const;
};
//lightness curve
inline void Lightcurve::Apply (float& Li) const {
assert (lutColCurve);
Li = lutColCurve[Li];
}
class Brightcurve : public ColorAppearance {
public:
void Apply(float& Br) const;
};
//brightness curve
inline void Brightcurve::Apply (float& Br) const {
assert (lutColCurve);
Br = lutColCurve[Br];
}
class Chromacurve : public ColorAppearance {
public:
void Apply(float& Cr) const;
};
//Chroma curve
inline void Chromacurve::Apply (float& Cr) const {
assert (lutColCurve);
Cr = lutColCurve[Cr];
}
class Saturcurve : public ColorAppearance {
public:
void Apply(float& Sa) const;
};
//Saturation curve
inline void Saturcurve::Apply (float& Sa) const {
assert (lutColCurve);
Sa = lutColCurve[Sa];
}
class Colorfcurve : public ColorAppearance {
public:
void Apply(float& Cf) const;
};
//Colorfullness curve
inline void Colorfcurve::Apply (float& Cf) const {
assert (lutColCurve);
Cf = lutColCurve[Cf];
}
class StandardToneCurve : public ToneCurve {
public:
void Apply(float& r, float& g, float& b) const;
};
class StandardToneCurvebw : public ToneCurve {
public:
void Apply(float& r, float& g, float& b) const;
};
class AdobeToneCurve : public ToneCurve {
private:
void RGBTone(float& r, float& g, float& b) const; // helper for tone curve
public:
void Apply(float& r, float& g, float& b) const;
};
class AdobeToneCurvebw : public ToneCurve {
private:
void RGBTone(float& r, float& g, float& b) const; // helper for tone curve
public:
void Apply(float& r, float& g, float& b) const;
};
class SatAndValueBlendingToneCurve : public ToneCurve {
public:
void Apply(float& r, float& g, float& b) const;
};
class SatAndValueBlendingToneCurvebw : public ToneCurve {
public:
void Apply(float& r, float& g, float& b) const;
};
class WeightedStdToneCurve : public ToneCurve {
private:
float Triangle(float refX, float refY, float X2) const;
public:
void Apply(float& r, float& g, float& b) const;
};
class WeightedStdToneCurvebw : public ToneCurve {
private:
float Triangle(float refX, float refY, float X2) const;
public:
void Apply(float& r, float& g, float& b) const;
};
// Standard tone curve
inline void StandardToneCurve::Apply (float& r, float& g, float& b) const {
assert (lutToneCurve);
r = lutToneCurve[r];
g = lutToneCurve[g];
b = lutToneCurve[b];
}
// Standard tone curve
inline void StandardToneCurvebw::Apply (float& r, float& g, float& b) const {
assert (lutToneCurve);
r = lutToneCurve[r];
g = lutToneCurve[g];
b = lutToneCurve[b];
}
// Tone curve according to Adobe's reference implementation
// values in 0xffff space
// inlined to make sure there will be no cache flush when used
inline void AdobeToneCurve::Apply (float& r, float& g, float& b) const {
assert (lutToneCurve);
if (r >= g) {
if (g > b) RGBTone (r, g, b); // Case 1: r >= g > b
else if (b > r) RGBTone (b, r, g); // Case 2: b > r >= g
else if (b > g) RGBTone (r, b, g); // Case 3: r >= b > g
else { // Case 4: r >= g == b
r = lutToneCurve[r];
g = lutToneCurve[g];
b = g;
}
}
else {
if (r >= b) RGBTone (g, r, b); // Case 5: g > r >= b
else if (b > g) RGBTone (b, g, r); // Case 6: b > g > r
else RGBTone (g, b, r); // Case 7: g >= b > r
}
}
inline void AdobeToneCurvebw::Apply (float& r, float& g, float& b) const {
assert (lutToneCurve);
if (r >= g) {
if (g > b) RGBTone (r, g, b); // Case 1: r >= g > b
else if (b > r) RGBTone (b, r, g); // Case 2: b > r >= g
else if (b > g) RGBTone (r, b, g); // Case 3: r >= b > g
else { // Case 4: r >= g == b
r = lutToneCurve[r];
g = lutToneCurve[g];
b = g;
}
}
else {
if (r >= b) RGBTone (g, r, b); // Case 5: g > r >= b
else if (b > g) RGBTone (b, g, r); // Case 6: b > g > r
else RGBTone (g, b, r); // Case 7: g >= b > r
}
}
inline void AdobeToneCurve::RGBTone (float& r, float& g, float& b) const {
float rold=r,gold=g,bold=b;
r = lutToneCurve[rold];
b = lutToneCurve[bold];
g = b + ((r - b) * (gold - bold) / (rold - bold));
}
inline void AdobeToneCurvebw::RGBTone (float& r, float& g, float& b) const {
float rold=r,gold=g,bold=b;
r = lutToneCurve[rold];
b = lutToneCurve[bold];
g = b + ((r - b) * (gold - bold) / (rold - bold));
}
inline float WeightedStdToneCurve::Triangle(float a, float a1, float b) const {
if (a != b) {
float b1;
float a2 = a1 - a;
if (b < a) { b1 = b + a2 * b / a ; }
else { b1 = b + a2 * (65535.f-b) / (65535.f-a); }
return b1;
}
return a1;
}
inline float WeightedStdToneCurvebw::Triangle(float a, float a1, float b) const {
if (a != b) {
float b1;
float a2 = a1 - a;
if (b < a) { b1 = b + a2 * b / a ; }
else { b1 = b + a2 * (65535.f-b) / (65535.f-a); }
return b1;
}
return a1;
}
// Tone curve modifying the value channel only, preserving hue and saturation
// values in 0xffff space
inline void WeightedStdToneCurve::Apply (float& r, float& g, float& b) const {
assert (lutToneCurve);
float r1 = lutToneCurve[r];
float g1 = Triangle(r, r1, g);
float b1 = Triangle(r, r1, b);
float g2 = lutToneCurve[g];
float r2 = Triangle(g, g2, r);
float b2 = Triangle(g, g2, b);
float b3 = lutToneCurve[b];
float r3 = Triangle(b, b3, r);
float g3 = Triangle(b, b3, g);
r = CLIP<float>( r1*0.50f + r2*0.25f + r3*0.25f);
g = CLIP<float>(g1*0.25f + g2*0.50f + g3*0.25f);
b = CLIP<float>(b1*0.25f + b2*0.25f + b3*0.50f);
}
inline void WeightedStdToneCurvebw::Apply (float& r, float& g, float& b) const {
assert (lutToneCurve);
float r1 = lutToneCurve[r];
float g1 = Triangle(r, r1, g);
float b1 = Triangle(r, r1, b);
float g2 = lutToneCurve[g];
float r2 = Triangle(g, g2, r);
float b2 = Triangle(g, g2, b);
float b3 = lutToneCurve[b];
float r3 = Triangle(b, b3, r);
float g3 = Triangle(b, b3, g);
r = CLIP<float>( r1*0.50f + r2*0.25f + r3*0.25f);
g = CLIP<float>(g1*0.25f + g2*0.50f + g3*0.25f);
b = CLIP<float>(b1*0.25f + b2*0.25f + b3*0.50f);
}
// Tone curve modifying the value channel only, preserving hue and saturation
// values in 0xffff space
inline void SatAndValueBlendingToneCurve::Apply (float& r, float& g, float& b) const {
assert (lutToneCurve);
float h, s, v;
float lum = (r+g+b)/3.f;
//float lum = Color::rgbLuminance(r, g, b);
float newLum = lutToneCurve[lum];
if (newLum == lum)
return;
bool increase = newLum > lum;
Color::rgb2hsv(r, g, b, h, s, v);
if (increase) {
// Linearly targeting Value = 1 and Saturation = 0
float coef = (newLum-lum)/(65535.f-lum);
float dV = (1.f-v)*coef;
s *= 1.f-coef;
Color::hsv2rgb(h, s, v+dV, r, g, b);
}
else {
// Linearly targeting Value = 0
float coef = (lum-newLum)/lum ;
float dV = v*coef;
Color::hsv2rgb(h, s, v-dV, r, g, b);
}
}
inline void SatAndValueBlendingToneCurvebw::Apply (float& r, float& g, float& b) const {
assert (lutToneCurve);
float h, s, v;
float lum = (r+g+b)/3.f;
//float lum = Color::rgbLuminance(r, g, b);
float newLum = lutToneCurve[lum];
if (newLum == lum)
return;
bool increase = newLum > lum;
Color::rgb2hsv(r, g, b, h, s, v);
if (increase) {
// Linearly targeting Value = 1 and Saturation = 0
float coef = (newLum-lum)/(65535.f-lum);
float dV = (1.f-v)*coef;
s *= 1.f-coef;
Color::hsv2rgb(h, s, v+dV, r, g, b);
}
else {
// Linearly targeting Value = 0
float coef = (lum-newLum)/lum ;
float dV = v*coef;
Color::hsv2rgb(h, s, v-dV, r, g, b);
}
}
}
#undef CLIPI
#endif