Various enhancement of the curves (see issue 968) :
- less memory required (512 KB less) - isIdentity() method has to be used by developers to let the user preselect a curve type without processing overhead - faster NURBS getVal(float t) method, by using a hash table
This commit is contained in:
@@ -38,8 +38,15 @@ namespace rtengine {
|
|||||||
x = 0;
|
x = 0;
|
||||||
y = 0;
|
y = 0;
|
||||||
ypp = 0;
|
ypp = 0;
|
||||||
|
hash = NULL;
|
||||||
|
hashSize = 1000; // has to be initiallised to the maximum value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Curve::~Curve () {
|
||||||
|
if (hash)
|
||||||
|
delete [] hash;
|
||||||
|
}
|
||||||
|
|
||||||
void Curve::AddPolygons ()
|
void Curve::AddPolygons ()
|
||||||
{
|
{
|
||||||
if (firstPointIncluded) {
|
if (firstPointIncluded) {
|
||||||
@@ -62,12 +69,71 @@ namespace rtengine {
|
|||||||
poly_y.push_back(y3);
|
poly_y.push_back(y3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Curve::fillHash() {
|
||||||
|
hash = new unsigned short int[hashSize+2];
|
||||||
|
|
||||||
|
unsigned int polyIter = 0;
|
||||||
|
double const increment = 1./hashSize;
|
||||||
|
double milestone = 0.;
|
||||||
|
|
||||||
|
for (unsigned int i=0; i<(hashSize+1); i++) {
|
||||||
|
while(poly_x[polyIter] <= milestone) ++polyIter;
|
||||||
|
hash[i] = polyIter-1;
|
||||||
|
milestone += increment;
|
||||||
|
}
|
||||||
|
hash[hashSize+1] = poly_x.size()-1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Debug output to file
|
||||||
|
FILE* f = fopen ("hash.txt", "wt");
|
||||||
|
for (int i=0; i<(hashSize+2); i++)
|
||||||
|
fprintf (f, "%d: %d > %.6f, %.6f\n", i, hash[i], poly_x[hash[i]], poly_y[hash[i]]);
|
||||||
|
fprintf (f, "\nppn: %d\npoly_x: %d\n", ppn, poly_x.size());
|
||||||
|
fclose (f);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
// Wikipedia sRGB: Unlike most other RGB color spaces, the sRGB gamma cannot be expressed as a single numerical value.
|
// Wikipedia sRGB: Unlike most other RGB color spaces, the sRGB gamma cannot be expressed as a single numerical value.
|
||||||
// The overall gamma is approximately 2.2, consisting of a linear (gamma 1.0) section near black, and a non-linear section elsewhere involving a 2.4 exponent
|
// The overall gamma is approximately 2.2, consisting of a linear (gamma 1.0) section near black, and a non-linear section elsewhere involving a 2.4 exponent
|
||||||
// and a gamma (slope of log output versus log input) changing from 1.0 through about 2.3.
|
// and a gamma (slope of log output versus log input) changing from 1.0 through about 2.3.
|
||||||
const double CurveFactory::sRGBGamma = 2.2;
|
const double CurveFactory::sRGBGamma = 2.2;
|
||||||
const double CurveFactory::sRGBGammaCurve = 2.4;
|
const double CurveFactory::sRGBGammaCurve = 2.4;
|
||||||
|
|
||||||
|
void fillCurveArray(DiagonalCurve* diagCurve, LUTf &outCurve, int skip, bool needed) {
|
||||||
|
if (needed) {
|
||||||
|
LUTf lutCurve (65536);
|
||||||
|
|
||||||
|
for (int i=0; i<=0xffff; i+= i<0xffff-skip ? skip : 1 ) {
|
||||||
|
// change to [0,1] range
|
||||||
|
double val = (double)i / 65535.0;
|
||||||
|
// apply custom/parametric/NURBS curve, if any
|
||||||
|
val = diagCurve->getVal (val);
|
||||||
|
// store result in a temporary array
|
||||||
|
lutCurve[i] = (val);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if skip>1, let apply linear interpolation in the skipped points of the curve
|
||||||
|
if (skip > 1) {
|
||||||
|
int prev = 0;
|
||||||
|
for (int i=1; i<=0xffff-skip; i++) {
|
||||||
|
if (i%skip==0) {
|
||||||
|
prev+=skip;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
lutCurve[i] = ( lutCurve[prev] * (skip - i%skip) + lutCurve[prev+skip] * (i%skip) ) / skip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i=0; i<=0xffff; i++) {
|
||||||
|
outCurve[i] = (65535.0 * lutCurve[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (int i=0; i<=0xffff; i++) {
|
||||||
|
outCurve[i] = (float)i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
void CurveFactory::complexsgnCurve (double saturation, bool satlimit, double satlimthresh, \
|
void CurveFactory::complexsgnCurve (double saturation, bool satlimit, double satlimthresh, \
|
||||||
@@ -76,118 +142,99 @@ namespace rtengine {
|
|||||||
|
|
||||||
//colormult = chroma_scale for Lab manipulations
|
//colormult = chroma_scale for Lab manipulations
|
||||||
|
|
||||||
|
//-----------------------------------------------------
|
||||||
|
|
||||||
|
bool needed;
|
||||||
|
DiagonalCurve* dCurve = NULL;
|
||||||
|
|
||||||
// check if contrast curve is needed
|
// check if contrast curve is needed
|
||||||
bool needsaturation = (saturation<-0.0001 || saturation>0.0001);
|
needed = (saturation<-0.0001 || saturation>0.0001);
|
||||||
|
|
||||||
// curve without contrast
|
|
||||||
LUTf dacurve (65536);
|
|
||||||
LUTf dbcurve (65536);
|
|
||||||
|
|
||||||
LUTf dscurve (65536);
|
// Filling the curve if needed
|
||||||
|
if (needed) {
|
||||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
||||||
std::vector<double> satcurvePoints;
|
//%%%%%%%%%%%%%%%%% Saturation curve's control points %%%%%%%%%%%%%%%%%
|
||||||
satcurvePoints.push_back((double)DCT_NURBS);
|
std::vector<double> satcurvePoints;
|
||||||
if (saturation>0) {
|
satcurvePoints.push_back((double)DCT_NURBS);
|
||||||
double satslope = (0.5+2*saturation/500.0)/(0.5-2*saturation/500.0);
|
if (saturation>0) {
|
||||||
double scale = (satlimthresh/100.1);
|
double satslope = (0.5+2*saturation/500.0)/(0.5-2*saturation/500.0);
|
||||||
if (!satlimit) scale=100/100.1;
|
double scale = (satlimthresh/100.1);
|
||||||
|
if (!satlimit) scale=100/100.1;
|
||||||
satcurvePoints.push_back(0); //black point. Value in [0 ; 1] range
|
|
||||||
satcurvePoints.push_back(0); //black point. Value in [0 ; 1] range
|
satcurvePoints.push_back(0); //black point. Value in [0 ; 1] range
|
||||||
|
satcurvePoints.push_back(0); //black point. Value in [0 ; 1] range
|
||||||
//if (satlimit) {
|
|
||||||
satcurvePoints.push_back(0.5-0.5*scale); //toe point
|
//if (satlimit) {
|
||||||
satcurvePoints.push_back(0.5-0.5*scale); //value at toe point
|
satcurvePoints.push_back(0.5-0.5*scale); //toe point
|
||||||
|
satcurvePoints.push_back(0.5-0.5*scale); //value at toe point
|
||||||
satcurvePoints.push_back(0.5-(0.5/satslope)*scale); //toe point
|
|
||||||
satcurvePoints.push_back(0.5-0.5*scale); //value at toe point
|
satcurvePoints.push_back(0.5-(0.5/satslope)*scale); //toe point
|
||||||
|
satcurvePoints.push_back(0.5-0.5*scale); //value at toe point
|
||||||
satcurvePoints.push_back(0.5+(0.5/satslope)*scale); //shoulder point
|
|
||||||
satcurvePoints.push_back(0.5+0.5*scale); //value at shoulder point
|
satcurvePoints.push_back(0.5+(0.5/satslope)*scale); //shoulder point
|
||||||
|
satcurvePoints.push_back(0.5+0.5*scale); //value at shoulder point
|
||||||
satcurvePoints.push_back(0.5+0.5*scale); //shoulder point
|
|
||||||
satcurvePoints.push_back(0.5+0.5*scale); //value at shoulder point
|
satcurvePoints.push_back(0.5+0.5*scale); //shoulder point
|
||||||
/*} else {
|
satcurvePoints.push_back(0.5+0.5*scale); //value at shoulder point
|
||||||
satcurvePoints.push_back(0.25+saturation/500.0); //toe point
|
/*} else {
|
||||||
satcurvePoints.push_back(0.25-saturation/500.0); //value at toe point
|
satcurvePoints.push_back(0.25+saturation/500.0); //toe point
|
||||||
|
satcurvePoints.push_back(0.25-saturation/500.0); //value at toe point
|
||||||
satcurvePoints.push_back(0.75-saturation/500.0); //shoulder point
|
|
||||||
satcurvePoints.push_back(0.75+saturation/500.0); //value at shoulder point
|
satcurvePoints.push_back(0.75-saturation/500.0); //shoulder point
|
||||||
}*/
|
satcurvePoints.push_back(0.75+saturation/500.0); //value at shoulder point
|
||||||
|
}*/
|
||||||
satcurvePoints.push_back(1); // white point
|
|
||||||
satcurvePoints.push_back(1); // value at white point
|
satcurvePoints.push_back(1); // white point
|
||||||
} else {
|
satcurvePoints.push_back(1); // value at white point
|
||||||
satcurvePoints.push_back(0);
|
} else {
|
||||||
satcurvePoints.push_back(-(saturation/200.0));
|
satcurvePoints.push_back(0);
|
||||||
|
satcurvePoints.push_back(-(saturation/200.0));
|
||||||
satcurvePoints.push_back(1);
|
|
||||||
satcurvePoints.push_back(1+saturation/200.0);
|
satcurvePoints.push_back(1);
|
||||||
|
satcurvePoints.push_back(1+saturation/200.0);
|
||||||
|
}
|
||||||
|
dCurve = new DiagonalCurve (satcurvePoints, CURVES_MIN_POLY_POINTS/skip);
|
||||||
|
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
|
fillCurveArray(dCurve, satCurve, skip, needed);
|
||||||
|
|
||||||
|
delete dCurve;
|
||||||
|
dCurve = NULL;
|
||||||
}
|
}
|
||||||
DiagonalCurve* satcurve = new DiagonalCurve (satcurvePoints, CURVES_MIN_POLY_POINTS/skip); // Actually, CURVES_MIN_POLY_POINTS = 1000,
|
else {
|
||||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
fillCurveArray(NULL, satCurve, skip, needed);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------
|
||||||
|
|
||||||
|
needed = false;
|
||||||
// create a curve if needed
|
// create a curve if needed
|
||||||
DiagonalCurve* tacurve = NULL;
|
if (acurvePoints.size()>0 && acurvePoints[0]!=0) {
|
||||||
if (acurvePoints.size()>0 && acurvePoints[0]!=0)
|
dCurve = new DiagonalCurve (acurvePoints, CURVES_MIN_POLY_POINTS/skip);
|
||||||
tacurve = new DiagonalCurve (acurvePoints, CURVES_MIN_POLY_POINTS/skip);
|
if (dCurve && !dCurve->isIdentity())
|
||||||
DiagonalCurve* tbcurve = NULL;
|
needed = true;
|
||||||
if (bcurvePoints.size()>0 && bcurvePoints[0]!=0)
|
|
||||||
tbcurve = new DiagonalCurve (bcurvePoints, CURVES_MIN_POLY_POINTS/skip);
|
|
||||||
|
|
||||||
for (int i=0; i<=0xffff; i+= i<0xffff-skip ? skip : 1 ) {
|
|
||||||
|
|
||||||
// change to [0,1] range
|
|
||||||
double aval = (double)i / 65535.0;
|
|
||||||
double bval = (double)i / 65535.0;
|
|
||||||
double sval = (double)i / 65535.0;
|
|
||||||
|
|
||||||
|
|
||||||
// apply saturation curve
|
|
||||||
if (needsaturation)
|
|
||||||
sval = satcurve->getVal (sval);
|
|
||||||
|
|
||||||
// apply custom/parametric/NURBS curve, if any
|
|
||||||
if (tacurve) {
|
|
||||||
aval = tacurve->getVal (aval);
|
|
||||||
}
|
|
||||||
// apply custom/parametric/NURBS curve, if any
|
|
||||||
if (tbcurve) {
|
|
||||||
bval = tbcurve->getVal (bval);
|
|
||||||
}
|
|
||||||
|
|
||||||
// store result in a temporary array
|
|
||||||
dacurve[i] = (aval);
|
|
||||||
dbcurve[i] = (bval);
|
|
||||||
dscurve[i] = (sval);
|
|
||||||
}
|
}
|
||||||
|
fillCurveArray(dCurve, aoutCurve, skip, needed);
|
||||||
delete tacurve;
|
if (dCurve) {
|
||||||
delete tbcurve;
|
delete dCurve;
|
||||||
|
dCurve = NULL;
|
||||||
// if skip>1, let apply linear interpolation in the skipped points of the curve
|
|
||||||
int prev = 0;
|
|
||||||
for (int i=1; i<=0xffff-skip; i++) {
|
|
||||||
if (i%skip==0) {
|
|
||||||
prev+=skip;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
dacurve[i] = ( dacurve[prev] * (skip - i%skip) + dacurve[prev+skip] * (i%skip) ) / skip;
|
|
||||||
dbcurve[i] = ( dbcurve[prev] * (skip - i%skip) + dbcurve[prev+skip] * (i%skip) ) / skip;
|
|
||||||
dscurve[i] = ( dscurve[prev] * (skip - i%skip) + dscurve[prev+skip] * (i%skip) ) / skip;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i=0; i<=0xffff; i++) {
|
|
||||||
aoutCurve[i] = (65535.0 * dacurve[i]);
|
|
||||||
boutCurve[i] = (65535.0 * dbcurve[i]);
|
|
||||||
satCurve[i] = (65535.0 * dscurve[i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
delete satcurve;
|
//-----------------------------------------------------
|
||||||
|
|
||||||
|
needed = false;
|
||||||
|
if (bcurvePoints.size()>0 && bcurvePoints[0]!=0) {
|
||||||
|
dCurve = new DiagonalCurve (bcurvePoints, CURVES_MIN_POLY_POINTS/skip);
|
||||||
|
if (dCurve && !dCurve->isIdentity())
|
||||||
|
needed = true;
|
||||||
|
}
|
||||||
|
fillCurveArray(dCurve, boutCurve, skip, needed);
|
||||||
|
if (dCurve) {
|
||||||
|
delete dCurve;
|
||||||
|
dCurve = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
@@ -223,17 +270,9 @@ namespace rtengine {
|
|||||||
// curve without contrast
|
// curve without contrast
|
||||||
LUTf dcurve(0x10000);
|
LUTf dcurve(0x10000);
|
||||||
|
|
||||||
// check if contrast curve is needed
|
|
||||||
bool needcontrast = contr>0.00001 || contr<-0.00001;
|
|
||||||
|
|
||||||
// check if inverse gamma is needed at the end
|
// check if inverse gamma is needed at the end
|
||||||
bool needigamma = igamma_ && gamma_>0;
|
bool needigamma = igamma_ && gamma_>0;
|
||||||
|
|
||||||
// create a curve if needed
|
|
||||||
DiagonalCurve* tcurve = NULL;
|
|
||||||
if (curvePoints.size()>0 && curvePoints[0]!=0)
|
|
||||||
tcurve = new DiagonalCurve (curvePoints, CURVES_MIN_POLY_POINTS/skip);
|
|
||||||
|
|
||||||
// clear array that stores histogram valid before applying the custom curve
|
// clear array that stores histogram valid before applying the custom curve
|
||||||
outBeforeCCurveHistogram.clear();
|
outBeforeCCurveHistogram.clear();
|
||||||
|
|
||||||
@@ -241,31 +280,37 @@ namespace rtengine {
|
|||||||
// tone curve base. a: slope (from exp.comp.), b: black, def_mul: max. x value (can be>1), hr,sr: highlight,shadow recovery
|
// tone curve base. a: slope (from exp.comp.), b: black, def_mul: max. x value (can be>1), hr,sr: highlight,shadow recovery
|
||||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
std::vector<double> brightcurvePoints;
|
DiagonalCurve* brightcurve = NULL;
|
||||||
brightcurvePoints.push_back((double)DCT_NURBS);
|
|
||||||
|
// check if brightness curve is needed
|
||||||
brightcurvePoints.push_back(0); //black point. Value in [0 ; 1] range
|
if (br>0.00001 || br<-0.00001) {
|
||||||
brightcurvePoints.push_back(0); //black point. Value in [0 ; 1] range
|
|
||||||
|
std::vector<double> brightcurvePoints;
|
||||||
if(br>0) {
|
brightcurvePoints.push_back((double)DCT_NURBS);
|
||||||
brightcurvePoints.push_back(0.1); //toe point
|
|
||||||
brightcurvePoints.push_back(0.1+br/150.0); //value at toe point
|
brightcurvePoints.push_back(0.); //black point. Value in [0 ; 1] range
|
||||||
|
brightcurvePoints.push_back(0.); //black point. Value in [0 ; 1] range
|
||||||
|
|
||||||
brightcurvePoints.push_back(0.7); //shoulder point
|
if(br>0) {
|
||||||
brightcurvePoints.push_back(MIN(1.0,0.7+br/300.0)); //value at shoulder point
|
brightcurvePoints.push_back(0.1); //toe point
|
||||||
} else {
|
brightcurvePoints.push_back(0.1+br/150.0); //value at toe point
|
||||||
brightcurvePoints.push_back(0.1-br/150.0); //toe point
|
|
||||||
brightcurvePoints.push_back(0.1); //value at toe point
|
brightcurvePoints.push_back(0.7); //shoulder point
|
||||||
|
brightcurvePoints.push_back(MIN(1.0,0.7+br/300.0)); //value at shoulder point
|
||||||
|
} else {
|
||||||
|
brightcurvePoints.push_back(0.1-br/150.0); //toe point
|
||||||
|
brightcurvePoints.push_back(0.1); //value at toe point
|
||||||
|
|
||||||
|
brightcurvePoints.push_back(MIN(1.0,0.7-br/300.0)); //shoulder point
|
||||||
|
brightcurvePoints.push_back(0.7); //value at shoulder point
|
||||||
|
}
|
||||||
|
brightcurvePoints.push_back(1.); // white point
|
||||||
|
brightcurvePoints.push_back(1.); // value at white point
|
||||||
|
|
||||||
brightcurvePoints.push_back(MIN(1.0,0.7-br/300.0)); //shoulder point
|
brightcurve = new DiagonalCurve (brightcurvePoints, CURVES_MIN_POLY_POINTS/skip);
|
||||||
brightcurvePoints.push_back(0.7); //value at shoulder point
|
|
||||||
}
|
}
|
||||||
brightcurvePoints.push_back(1); // white point
|
|
||||||
brightcurvePoints.push_back(1); // value at white point
|
|
||||||
|
|
||||||
DiagonalCurve* brightcurve = new DiagonalCurve (brightcurvePoints, CURVES_MIN_POLY_POINTS/skip); // Actually, CURVES_MIN_POLY_POINTS = 1000,
|
|
||||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
float exp_scale = a;
|
float exp_scale = a;
|
||||||
float scale = 65536.0;
|
float scale = 65536.0;
|
||||||
float comp = (MAX(0,ecomp) + 1.0)*hlcompr/100.0;
|
float comp = (MAX(0,ecomp) + 1.0)*hlcompr/100.0;
|
||||||
@@ -307,21 +352,27 @@ namespace rtengine {
|
|||||||
val = (double)i / 65535.0;
|
val = (double)i / 65535.0;
|
||||||
|
|
||||||
// gamma correction
|
// gamma correction
|
||||||
if (gamma_>1)
|
if (gamma_>1)
|
||||||
val = gamma (val, gamma_, start, slope, mul, add);
|
val = gamma (val, gamma_, start, slope, mul, add);
|
||||||
|
|
||||||
// apply brightness curve
|
// apply brightness curve
|
||||||
val = brightcurve->getVal (val);
|
if (brightcurve)
|
||||||
|
val = brightcurve->getVal (val); // TODO: getVal(double) is very slow! Optimize with a LUTf
|
||||||
|
|
||||||
// store result in a temporary array
|
// store result in a temporary array
|
||||||
dcurve[i] = CLIPD(val);
|
dcurve[i] = CLIPD(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (brightcurve)
|
||||||
|
delete brightcurve;
|
||||||
|
|
||||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
if (needcontrast) {
|
// check if contrast curve is needed
|
||||||
|
if (contr>0.00001 || contr<-0.00001) {
|
||||||
|
|
||||||
// compute mean luminance of the image with the curve applied
|
// compute mean luminance of the image with the curve applied
|
||||||
int sum = 0;
|
int sum = 0;
|
||||||
float avg = 0;
|
float avg = 0;
|
||||||
@@ -352,49 +403,64 @@ namespace rtengine {
|
|||||||
contrastcurvePoints.push_back(1); // white point
|
contrastcurvePoints.push_back(1); // white point
|
||||||
contrastcurvePoints.push_back(1); // value at white point
|
contrastcurvePoints.push_back(1); // value at white point
|
||||||
|
|
||||||
DiagonalCurve* contrastcurve = new DiagonalCurve (contrastcurvePoints, CURVES_MIN_POLY_POINTS/skip); // Actually, CURVES_MIN_POLY_POINTS = 1000,
|
DiagonalCurve* contrastcurve = new DiagonalCurve (contrastcurvePoints, CURVES_MIN_POLY_POINTS/skip);
|
||||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
// apply contrast enhancement
|
// apply contrast enhancement
|
||||||
for (int i=0; i<=0xffff; i++) {
|
for (int i=0; i<=0xffff; i++) {
|
||||||
dcurve[i] = contrastcurve->getVal (dcurve[i]);
|
dcurve[i] = contrastcurve->getVal (dcurve[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete contrastcurve;
|
delete contrastcurve;
|
||||||
}
|
}
|
||||||
|
|
||||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
|
// create a curve if needed
|
||||||
|
bool histNeeded = false;
|
||||||
|
DiagonalCurve* tcurve = NULL;
|
||||||
|
if (curvePoints.size()>0 && curvePoints[0]!=0) {
|
||||||
|
tcurve = new DiagonalCurve (curvePoints, CURVES_MIN_POLY_POINTS/skip);
|
||||||
|
if (outBeforeCCurveHistogram && histogramCropped)
|
||||||
|
histNeeded = true;
|
||||||
|
}
|
||||||
|
if (tcurve && tcurve->isIdentity()) {
|
||||||
|
delete tcurve;
|
||||||
|
tcurve = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i=0; i<=0xffff; i++) {
|
for (int i=0; i<=0xffff; i++) {
|
||||||
float val;
|
float val;
|
||||||
|
|
||||||
|
if (histNeeded) {
|
||||||
|
float fi=i;
|
||||||
|
float hval = dcurve[shCurve[hlCurve[i]*fi]*fi];
|
||||||
|
//if (needigamma)
|
||||||
|
// hval = igamma2 (hval);
|
||||||
|
int hi = (int)(255.0*(hval));
|
||||||
|
outBeforeCCurveHistogram[hi] += histogramCropped[i] ;
|
||||||
|
}
|
||||||
|
|
||||||
// apply custom/parametric/NURBS curve, if any
|
// apply custom/parametric/NURBS curve, if any
|
||||||
if (tcurve) {
|
if (tcurve) {
|
||||||
if (outBeforeCCurveHistogram && histogramCropped) {
|
val = tcurve->getVal (dcurve[i]); // TODO: getVal(double) is very slow! Optimize with a LUTf
|
||||||
float fi=i;
|
|
||||||
float hval = dcurve[shCurve[hlCurve[i]*fi]*fi];
|
|
||||||
//if (needigamma)
|
|
||||||
// hval = igamma2 (hval);
|
|
||||||
int hi = (int)(255.0*(hval));
|
|
||||||
outBeforeCCurveHistogram[hi] += histogramCropped[i] ;
|
|
||||||
}
|
|
||||||
val = tcurve->getVal (dcurve[i]);
|
|
||||||
} else {
|
} else {
|
||||||
val = (dcurve[i]);
|
val = (dcurve[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if inverse gamma is needed, do it (standard sRGB inverse gamma is applied)
|
// if inverse gamma is needed, do it (standard sRGB inverse gamma is applied)
|
||||||
if (needigamma)
|
if (needigamma)
|
||||||
val = igamma (val, gamma_, start, slope, mul, add);
|
val = igamma (val, gamma_, start, slope, mul, add);
|
||||||
|
|
||||||
outCurve[i] = (65535.0 * val);
|
outCurve[i] = (65535.0 * val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tcurve)
|
||||||
delete tcurve;
|
delete tcurve;
|
||||||
delete brightcurve;
|
|
||||||
/*if (outBeforeCCurveHistogram) {
|
/*if (outBeforeCCurveHistogram) {
|
||||||
for (int i=0; i<256; i++) printf("i= %d bchist= %d \n",i,outBeforeCCurveHistogram[i]);
|
for (int i=0; i<256; i++) printf("i= %d bchist= %d \n",i,outBeforeCCurveHistogram[i]);
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -409,15 +475,6 @@ namespace rtengine {
|
|||||||
// curve without contrast
|
// curve without contrast
|
||||||
LUTf dcurve(65536,0);
|
LUTf dcurve(65536,0);
|
||||||
|
|
||||||
// check if contrast curve is needed
|
|
||||||
bool needcontrast = contr>0.00001 || contr<-0.00001;
|
|
||||||
|
|
||||||
// create a curve if needed
|
|
||||||
DiagonalCurve* tcurve = NULL;
|
|
||||||
if (curvePoints.size()>0 && curvePoints[0]!=0)
|
|
||||||
tcurve = new DiagonalCurve (curvePoints, CURVES_MIN_POLY_POINTS/skip);
|
|
||||||
|
|
||||||
|
|
||||||
// clear array that stores histogram valid before applying the custom curve
|
// clear array that stores histogram valid before applying the custom curve
|
||||||
if (outBeforeCCurveHistogram)
|
if (outBeforeCCurveHistogram)
|
||||||
outBeforeCCurveHistogram.clear();
|
outBeforeCCurveHistogram.clear();
|
||||||
@@ -426,48 +483,62 @@ namespace rtengine {
|
|||||||
// tone curve base. a: slope (from exp.comp.), b: black, def_mul: max. x value (can be>1), hr,sr: highlight,shadow recovery
|
// tone curve base. a: slope (from exp.comp.), b: black, def_mul: max. x value (can be>1), hr,sr: highlight,shadow recovery
|
||||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
std::vector<double> brightcurvePoints;
|
// check if brightness curve is needed
|
||||||
brightcurvePoints.push_back((double)((CurveType)DCT_NURBS));
|
if (br>0.00001 || br<-0.00001) {
|
||||||
|
|
||||||
brightcurvePoints.push_back(0); // black point. Value in [0 ; 1] range
|
std::vector<double> brightcurvePoints;
|
||||||
brightcurvePoints.push_back(0); // black point. Value in [0 ; 1] range
|
brightcurvePoints.push_back((double)((CurveType)DCT_NURBS));
|
||||||
|
|
||||||
if (br>0) {
|
brightcurvePoints.push_back(0.); // black point. Value in [0 ; 1] range
|
||||||
brightcurvePoints.push_back(0.1); // toe point
|
brightcurvePoints.push_back(0.); // black point. Value in [0 ; 1] range
|
||||||
brightcurvePoints.push_back(0.1+br/150.0); //value at toe point
|
|
||||||
|
|
||||||
brightcurvePoints.push_back(0.7); // shoulder point
|
if (br>0) {
|
||||||
brightcurvePoints.push_back(MIN(1.0,0.7+br/300.0)); //value at shoulder point
|
brightcurvePoints.push_back(0.1); // toe point
|
||||||
} else {
|
brightcurvePoints.push_back(0.1+br/150.0); //value at toe point
|
||||||
brightcurvePoints.push_back(0.1-br/150.0); // toe point
|
|
||||||
brightcurvePoints.push_back(0.1); // value at toe point
|
brightcurvePoints.push_back(0.7); // shoulder point
|
||||||
|
brightcurvePoints.push_back(MIN(1.0,0.7+br/300.0)); //value at shoulder point
|
||||||
|
} else {
|
||||||
|
brightcurvePoints.push_back(0.1-br/150.0); // toe point
|
||||||
|
brightcurvePoints.push_back(0.1); // value at toe point
|
||||||
|
|
||||||
|
brightcurvePoints.push_back(MIN(1.0,0.7-br/300.0)); // shoulder point
|
||||||
|
brightcurvePoints.push_back(0.7); // value at shoulder point
|
||||||
|
}
|
||||||
|
brightcurvePoints.push_back(1.); // white point
|
||||||
|
brightcurvePoints.push_back(1.); // value at white point
|
||||||
|
|
||||||
brightcurvePoints.push_back(MIN(1.0,0.7-br/300.0)); // shoulder point
|
DiagonalCurve* brightcurve = new DiagonalCurve (brightcurvePoints, CURVES_MIN_POLY_POINTS/skip);
|
||||||
brightcurvePoints.push_back(0.7); // value at shoulder point
|
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
|
// Applying brightness curve
|
||||||
|
for (int i=0; i<32768; i++) { // L values range up to 32767, higher values are for highlight overflow
|
||||||
|
|
||||||
|
// change to [0,1] range
|
||||||
|
float val = (float)i / 32767.0;
|
||||||
|
|
||||||
|
// apply brightness curve
|
||||||
|
val = brightcurve->getVal (val);
|
||||||
|
|
||||||
|
// store result in a temporary array
|
||||||
|
dcurve[i] = CLIPD(val);
|
||||||
|
}
|
||||||
|
delete brightcurve;
|
||||||
}
|
}
|
||||||
brightcurvePoints.push_back(1); // white point
|
else {
|
||||||
brightcurvePoints.push_back(1); // value at white point
|
for (int i=0; i<32768; i++) { // L values range up to 32767, higher values are for highlight overflow
|
||||||
|
// set the identity curve in the temporary array
|
||||||
DiagonalCurve* brightcurve = new DiagonalCurve (brightcurvePoints, CURVES_MIN_POLY_POINTS/skip); // Actually, CURVES_MIN_POLY_POINTS = 1000,
|
dcurve[i] = (float)i / 32767.0;
|
||||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
}
|
||||||
|
|
||||||
for (int i=0; i<32768; i++) { // L values range up to 32767, higher values are for highlight overflow
|
|
||||||
|
|
||||||
// change to [0,1] range
|
|
||||||
float val = (float)i / 32767.0;
|
|
||||||
|
|
||||||
// apply brightness curve
|
|
||||||
val = brightcurve->getVal (val);
|
|
||||||
|
|
||||||
// store result in a temporary array
|
|
||||||
dcurve[i] = CLIPD(val);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
if (needcontrast) {
|
// check if contrast curve is needed
|
||||||
|
if (contr>0.00001 || contr<-0.00001) {
|
||||||
|
|
||||||
// compute mean luminance of the image with the curve applied
|
// compute mean luminance of the image with the curve applied
|
||||||
int sum = 0;
|
int sum = 0;
|
||||||
float avg = 0;
|
float avg = 0;
|
||||||
@@ -485,8 +556,8 @@ namespace rtengine {
|
|||||||
std::vector<double> contrastcurvePoints;
|
std::vector<double> contrastcurvePoints;
|
||||||
contrastcurvePoints.push_back((double)((CurveType)DCT_NURBS));
|
contrastcurvePoints.push_back((double)((CurveType)DCT_NURBS));
|
||||||
|
|
||||||
contrastcurvePoints.push_back(0); // black point. Value in [0 ; 1] range
|
contrastcurvePoints.push_back(0.); // black point. Value in [0 ; 1] range
|
||||||
contrastcurvePoints.push_back(0); // black point. Value in [0 ; 1] range
|
contrastcurvePoints.push_back(0.); // black point. Value in [0 ; 1] range
|
||||||
|
|
||||||
contrastcurvePoints.push_back(avg-avg*(0.6-contr/250.0)); // toe point
|
contrastcurvePoints.push_back(avg-avg*(0.6-contr/250.0)); // toe point
|
||||||
contrastcurvePoints.push_back(avg-avg*(0.6+contr/250.0)); // value at toe point
|
contrastcurvePoints.push_back(avg-avg*(0.6+contr/250.0)); // value at toe point
|
||||||
@@ -494,10 +565,10 @@ namespace rtengine {
|
|||||||
contrastcurvePoints.push_back(avg+(1-avg)*(0.6-contr/250.0)); // shoulder point
|
contrastcurvePoints.push_back(avg+(1-avg)*(0.6-contr/250.0)); // shoulder point
|
||||||
contrastcurvePoints.push_back(avg+(1-avg)*(0.6+contr/250.0)); // value at shoulder point
|
contrastcurvePoints.push_back(avg+(1-avg)*(0.6+contr/250.0)); // value at shoulder point
|
||||||
|
|
||||||
contrastcurvePoints.push_back(1); // white point
|
contrastcurvePoints.push_back(1.); // white point
|
||||||
contrastcurvePoints.push_back(1); // value at white point
|
contrastcurvePoints.push_back(1.); // value at white point
|
||||||
|
|
||||||
DiagonalCurve* contrastcurve = new DiagonalCurve (contrastcurvePoints, CURVES_MIN_POLY_POINTS/skip); // Actually, CURVES_MIN_POLY_POINTS = 1000,
|
DiagonalCurve* contrastcurve = new DiagonalCurve (contrastcurvePoints, CURVES_MIN_POLY_POINTS/skip);
|
||||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
// apply contrast enhancement
|
// apply contrast enhancement
|
||||||
@@ -508,39 +579,64 @@ namespace rtengine {
|
|||||||
delete contrastcurve;
|
delete contrastcurve;
|
||||||
}
|
}
|
||||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
for (int i=0; i<32768; i++) { // L values go up to 32767, last stop is for highlight overflow
|
// create a curve if needed
|
||||||
float val;
|
DiagonalCurve* tcurve = NULL;
|
||||||
|
bool histNeeded = false;
|
||||||
// apply custom/parametric/NURBS curve, if any
|
if (curvePoints.size()>0 && curvePoints[0]!=0) {
|
||||||
if (tcurve) {
|
tcurve = new DiagonalCurve (curvePoints, CURVES_MIN_POLY_POINTS/skip);
|
||||||
if (outBeforeCCurveHistogram) {
|
if (outBeforeCCurveHistogram && histogramCropped)
|
||||||
|
histNeeded = true;
|
||||||
|
}
|
||||||
|
if (tcurve && tcurve->isIdentity()) {
|
||||||
|
delete tcurve;
|
||||||
|
tcurve = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tcurve) {
|
||||||
|
// L values go up to 32767, last stop is for highlight overflow
|
||||||
|
for (int i=0; i<32768; i++) {
|
||||||
|
float val;
|
||||||
|
|
||||||
|
if (histNeeded) {
|
||||||
float hval = dcurve[i];
|
float hval = dcurve[i];
|
||||||
int hi = (int)(255.0*CLIPD(hval));
|
int hi = (int)(255.0*CLIPD(hval));
|
||||||
outBeforeCCurveHistogram[hi]+=histogramCropped[i] ;
|
outBeforeCCurveHistogram[hi]+=histogramCropped[i] ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// apply custom/parametric/NURBS curve, if any
|
||||||
val = tcurve->getVal (dcurve[i]);
|
val = tcurve->getVal (dcurve[i]);
|
||||||
} else {
|
|
||||||
val = (dcurve[i]);
|
outCurve[i] = (32767.0 * val);
|
||||||
}
|
}
|
||||||
|
|
||||||
outCurve[i] = (32767.0 * val);
|
|
||||||
}
|
}
|
||||||
for (int i=32768; i<65536; i++) outCurve[i]=i;
|
else {
|
||||||
|
// Skip the slow getval method if no curve is used (or an identity curve)
|
||||||
|
// L values go up to 32767, last stop is for highlight overflow
|
||||||
delete tcurve;
|
for (int i=0; i<32768; i++) {
|
||||||
delete brightcurve;
|
if (histNeeded) {
|
||||||
|
float hval = dcurve[i];
|
||||||
|
int hi = (int)(255.0*CLIPD(hval));
|
||||||
|
outBeforeCCurveHistogram[hi]+=histogramCropped[i] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
outCurve[i] = 32767.0*dcurve[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i=32768; i<65536; i++) outCurve[i]=(float)i;
|
||||||
|
|
||||||
|
if (tcurve)
|
||||||
|
delete tcurve;
|
||||||
|
|
||||||
/*if (outBeforeCCurveHistogram) {
|
/*if (outBeforeCCurveHistogram) {
|
||||||
for (int i=0; i<256; i++) printf("i= %d bchist= %d \n",i,outBeforeCCurveHistogram[i]);
|
for (int i=0; i<256; i++) printf("i= %d bchist= %d \n",i,outBeforeCCurveHistogram[i]);
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
LUTf CurveFactory::gammatab;
|
LUTf CurveFactory::gammatab;
|
||||||
LUTf CurveFactory::igammatab_srgb;
|
LUTf CurveFactory::igammatab_srgb;
|
||||||
|
@@ -35,7 +35,6 @@
|
|||||||
|
|
||||||
#define CLIPI(a) ((a)>0?((a)<65534?(a):65534):0)
|
#define CLIPI(a) ((a)>0?((a)<65534?(a):65534):0)
|
||||||
|
|
||||||
|
|
||||||
namespace rtengine {
|
namespace rtengine {
|
||||||
|
|
||||||
class CurveFactory {
|
class CurveFactory {
|
||||||
@@ -211,7 +210,10 @@ class Curve {
|
|||||||
double* y;
|
double* y;
|
||||||
std::vector<double> poly_x; // X points of the faceted curve
|
std::vector<double> poly_x; // X points of the faceted curve
|
||||||
std::vector<double> poly_y; // Y points of the faceted curve
|
std::vector<double> poly_y; // Y points of the faceted curve
|
||||||
double* ypp;
|
unsigned short int* hash;
|
||||||
|
unsigned int hashSize; // hash table's size, between [10, 100, 1000]
|
||||||
|
|
||||||
|
double* ypp;
|
||||||
|
|
||||||
// Fields for the elementary curve polygonisation
|
// Fields for the elementary curve polygonisation
|
||||||
double x1, y1, x2, y2, x3, y3;
|
double x1, y1, x2, y2, x3, y3;
|
||||||
@@ -225,12 +227,16 @@ class Curve {
|
|||||||
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 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); }
|
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:
|
public:
|
||||||
Curve ();
|
Curve ();
|
||||||
|
~Curve ();
|
||||||
void AddPolygons ();
|
void AddPolygons ();
|
||||||
virtual double getVal (double t) = 0;
|
virtual double getVal (double t) = 0;
|
||||||
virtual void getVal (const std::vector<double>& t, std::vector<double>& res) = 0;
|
virtual void getVal (const std::vector<double>& t, std::vector<double>& res) = 0;
|
||||||
|
|
||||||
|
virtual bool isIdentity () = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DiagonalCurve : public Curve {
|
class DiagonalCurve : public Curve {
|
||||||
@@ -238,6 +244,10 @@ class DiagonalCurve : public Curve {
|
|||||||
protected:
|
protected:
|
||||||
DiagonalCurveType kind;
|
DiagonalCurveType kind;
|
||||||
|
|
||||||
|
unsigned int minSearch; // a effacer!!!
|
||||||
|
unsigned int maxSearch; // a effacer!!!
|
||||||
|
unsigned int searchArray[21]; // a effacer!!!
|
||||||
|
|
||||||
void spline_cubic_set ();
|
void spline_cubic_set ();
|
||||||
void NURBS_set ();
|
void NURBS_set ();
|
||||||
|
|
||||||
@@ -245,8 +255,9 @@ class DiagonalCurve : public Curve {
|
|||||||
DiagonalCurve (const std::vector<double>& points, int ppn=CURVES_MIN_POLY_POINTS);
|
DiagonalCurve (const std::vector<double>& points, int ppn=CURVES_MIN_POLY_POINTS);
|
||||||
~DiagonalCurve ();
|
~DiagonalCurve ();
|
||||||
|
|
||||||
double getVal (double t);
|
double getVal (double t);
|
||||||
void getVal (const std::vector<double>& t, std::vector<double>& res);
|
void getVal (const std::vector<double>& t, std::vector<double>& res);
|
||||||
|
bool isIdentity () { return kind==DCT_Empty; };
|
||||||
};
|
};
|
||||||
|
|
||||||
class FlatCurve : public Curve {
|
class FlatCurve : public Curve {
|
||||||
@@ -264,8 +275,9 @@ class FlatCurve : public Curve {
|
|||||||
FlatCurve (const std::vector<double>& points, bool isPeriodic = true, int ppn=CURVES_MIN_POLY_POINTS);
|
FlatCurve (const std::vector<double>& points, bool isPeriodic = true, int ppn=CURVES_MIN_POLY_POINTS);
|
||||||
~FlatCurve ();
|
~FlatCurve ();
|
||||||
|
|
||||||
double getVal (double t);
|
double getVal (double t);
|
||||||
void getVal (const std::vector<double>& t, std::vector<double>& res);
|
void getVal (const std::vector<double>& t, std::vector<double>& res);
|
||||||
|
bool isIdentity () { return kind==FCT_Empty; };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -30,7 +30,11 @@ namespace rtengine {
|
|||||||
|
|
||||||
DiagonalCurve::DiagonalCurve (const std::vector<double>& p, int poly_pn) {
|
DiagonalCurve::DiagonalCurve (const std::vector<double>& p, int poly_pn) {
|
||||||
|
|
||||||
ppn = poly_pn;
|
ppn = poly_pn;
|
||||||
|
bool identity = true;
|
||||||
|
|
||||||
|
if (ppn < 500) hashSize = 100; // Arbitrary cut-off value
|
||||||
|
if (ppn < 50) hashSize = 10; // Arbitrary cut-off value
|
||||||
|
|
||||||
if (p.size()<3) {
|
if (p.size()<3) {
|
||||||
kind = DCT_Empty;
|
kind = DCT_Empty;
|
||||||
@@ -45,17 +49,28 @@ DiagonalCurve::DiagonalCurve (const std::vector<double>& p, int poly_pn) {
|
|||||||
for (int i=0; i<N; i++) {
|
for (int i=0; i<N; i++) {
|
||||||
x[i] = p[ix++];
|
x[i] = p[ix++];
|
||||||
y[i] = p[ix++];
|
y[i] = p[ix++];
|
||||||
|
if (x[i] != y[i])
|
||||||
|
identity = false;
|
||||||
|
}
|
||||||
|
if (x[0] != 0.0f || x[N-1] != 1.0f)
|
||||||
|
// Special (and very rare) case where all points are on the identity line but
|
||||||
|
// not reaching the limits
|
||||||
|
identity = false;
|
||||||
|
|
||||||
|
if (!identity) {
|
||||||
|
if (kind==DCT_Spline && N > 2)
|
||||||
|
spline_cubic_set ();
|
||||||
|
else if (kind==DCT_NURBS && N > 2) {
|
||||||
|
NURBS_set ();
|
||||||
|
fillHash();
|
||||||
|
}
|
||||||
|
else kind=DCT_Linear;
|
||||||
}
|
}
|
||||||
if (kind==DCT_Spline)
|
|
||||||
spline_cubic_set ();
|
|
||||||
else if (kind==DCT_NURBS && N > 2)
|
|
||||||
NURBS_set ();
|
|
||||||
else kind=DCT_Linear;
|
|
||||||
}
|
}
|
||||||
else if (kind==DCT_Parametric) {
|
else if (kind==DCT_Parametric) {
|
||||||
if (p.size()!=8 && p.size()!=9)
|
if ((p.size()==8 || p.size()==9) && (p.at(4)!=0.0f || p.at(5)!=0.0f || p.at(6)!=0.0f || p.at(7)!=0.0f)) {
|
||||||
kind = DCT_Empty;
|
identity = false;
|
||||||
else {
|
|
||||||
x = new double[9];
|
x = new double[9];
|
||||||
for (int i=0; i<4; i++)
|
for (int i=0; i<4; i++)
|
||||||
x[i] = p[i];
|
x[i] = p[i];
|
||||||
@@ -67,6 +82,9 @@ DiagonalCurve::DiagonalCurve (const std::vector<double>& p, int poly_pn) {
|
|||||||
x[8] = p[8]/100.0;
|
x[8] = p[8]/100.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (identity) {
|
||||||
|
kind = DCT_Empty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,7 +100,7 @@ DiagonalCurve::~DiagonalCurve () {
|
|||||||
void DiagonalCurve::spline_cubic_set () {
|
void DiagonalCurve::spline_cubic_set () {
|
||||||
|
|
||||||
double* u = new double[N-1];
|
double* u = new double[N-1];
|
||||||
delete [] ypp;
|
delete [] ypp; // TODO: why do we delete ypp here since it should not be allocated yet?
|
||||||
ypp = new double [N];
|
ypp = new double [N];
|
||||||
|
|
||||||
ypp[0] = u[0] = 0.0; /* set lower boundary condition to "natural" */
|
ypp[0] = u[0] = 0.0; /* set lower boundary condition to "natural" */
|
||||||
@@ -113,7 +131,7 @@ void DiagonalCurve::NURBS_set () {
|
|||||||
double total_length=0.;
|
double total_length=0.;
|
||||||
|
|
||||||
// Create the list of Bezier sub-curves
|
// Create the list of Bezier sub-curves
|
||||||
// NURBS_set is called if N > 2 only
|
// NURBS_set is called if N > 2 and non identity only
|
||||||
|
|
||||||
int j = 0;
|
int j = 0;
|
||||||
int k = 0;
|
int k = 0;
|
||||||
@@ -122,76 +140,84 @@ void DiagonalCurve::NURBS_set () {
|
|||||||
double dx;
|
double dx;
|
||||||
double dy;
|
double dy;
|
||||||
|
|
||||||
// first point (on the curve)
|
// first point (on the curve)
|
||||||
if (!i) {
|
if (!i) {
|
||||||
sc_x[j] = x[i];
|
sc_x[j] = x[i];
|
||||||
sc_y[j++] = y[i++];
|
sc_y[j++] = y[i++];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
sc_x[j] = (x[i-1] + x[i]) / 2.;
|
sc_x[j] = (x[i-1] + x[i]) / 2.;
|
||||||
sc_y[j++] = (y[i-1] + y[i]) / 2.;
|
sc_y[j++] = (y[i-1] + y[i]) / 2.;
|
||||||
}
|
}
|
||||||
|
|
||||||
// second point (control point)
|
// second point (control point)
|
||||||
sc_x[j] = x[i];
|
sc_x[j] = x[i];
|
||||||
sc_y[j] = y[i++];
|
sc_y[j] = y[i++];
|
||||||
|
|
||||||
dx = sc_x[j] - sc_x[j-1];
|
dx = sc_x[j] - sc_x[j-1];
|
||||||
dy = sc_y[j] - sc_y[j-1];
|
dy = sc_y[j] - sc_y[j-1];
|
||||||
length = sqrt(dx*dx + dy*dy);
|
length = sqrt(dx*dx + dy*dy);
|
||||||
j++;
|
j++;
|
||||||
|
|
||||||
// third point (on the curve)
|
// third point (on the curve)
|
||||||
if (i==N-1) {
|
if (i==N-1) {
|
||||||
sc_x[j] = x[i];
|
sc_x[j] = x[i];
|
||||||
sc_y[j] = y[i];
|
sc_y[j] = y[i];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
sc_x[j] = (x[i-1] + x[i]) / 2.;
|
sc_x[j] = (x[i-1] + x[i]) / 2.;
|
||||||
sc_y[j] = (y[i-1] + y[i]) / 2.;
|
sc_y[j] = (y[i-1] + y[i]) / 2.;
|
||||||
}
|
}
|
||||||
dx = sc_x[j] - sc_x[j-1];
|
dx = sc_x[j] - sc_x[j-1];
|
||||||
dy = sc_y[j] - sc_y[j-1];
|
dy = sc_y[j] - sc_y[j-1];
|
||||||
length += sqrt(dx*dx + dy*dy);
|
length += sqrt(dx*dx + dy*dy);
|
||||||
j++;
|
j++;
|
||||||
|
|
||||||
// Storing the length of all sub-curves and the total length (to have a better distribution
|
// Storing the length of all sub-curves and the total length (to have a better distribution
|
||||||
// of the points along the curve)
|
// of the points along the curve)
|
||||||
sc_length[k++] = length;
|
sc_length[k++] = length;
|
||||||
total_length += length;
|
total_length += length;
|
||||||
}
|
}
|
||||||
|
|
||||||
poly_x.clear();
|
poly_x.clear();
|
||||||
poly_y.clear();
|
poly_y.clear();
|
||||||
unsigned int sc_xsize=j-1;
|
unsigned int sc_xsize=j-1;
|
||||||
j = 0;
|
j = 0;
|
||||||
|
|
||||||
|
// adding the initial horizontal segment, if any
|
||||||
|
if (x[0] > 0.) {
|
||||||
|
poly_x.push_back(0.);
|
||||||
|
poly_y.push_back(y[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// adding the initial horizontal segment, if any
|
||||||
// create the polyline with the number of points adapted to the X range of the sub-curve
|
// 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) {
|
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 !!!
|
// 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);
|
nbr_points = (int)(((double)(ppn+N-2) * sc_length[i/3] )/ total_length);
|
||||||
if (nbr_points<0){
|
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]);
|
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);
|
printf("NURBS diagonal 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);
|
exit(0);
|
||||||
}
|
}
|
||||||
// increment along the curve, not along the X axis
|
// increment along the curve, not along the X axis
|
||||||
increment = 1.0 / (double)(nbr_points-1);
|
increment = 1.0 / (double)(nbr_points-1);
|
||||||
x1 = sc_x[i]; y1 = sc_y[i];
|
x1 = sc_x[i]; y1 = sc_y[i];
|
||||||
x2 = sc_x[i+1]; y2 = sc_y[i+1];
|
x2 = sc_x[i+1]; y2 = sc_y[i+1];
|
||||||
x3 = sc_x[i+2]; y3 = sc_y[i+2];
|
x3 = sc_x[i+2]; y3 = sc_y[i+2];
|
||||||
firstPointIncluded = !i;
|
firstPointIncluded = !i;
|
||||||
AddPolygons ();
|
AddPolygons ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// adding the final horizontal segment, always (see under)
|
||||||
|
poly_x.push_back(1.1); // 1.1 is a hack for optimization purpose of the getVal method (the last value has to be beyond the normal range)
|
||||||
|
poly_y.push_back(y[N-1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
double DiagonalCurve::getVal (double t) {
|
double DiagonalCurve::getVal (double t) {
|
||||||
|
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
|
|
||||||
case DCT_Empty :
|
|
||||||
return t;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DCT_Parametric : {
|
case DCT_Parametric : {
|
||||||
if (t<=1e-14)
|
if (t<=1e-14)
|
||||||
return 0.0;
|
return 0.0;
|
||||||
@@ -222,7 +248,7 @@ double DiagonalCurve::getVal (double t) {
|
|||||||
}
|
}
|
||||||
case DCT_Linear :
|
case DCT_Linear :
|
||||||
case DCT_Spline : {
|
case DCT_Spline : {
|
||||||
// values under and over the first and last point
|
// values under and over the first and last point
|
||||||
if (t>x[N-1])
|
if (t>x[N-1])
|
||||||
return y[N-1];
|
return y[N-1];
|
||||||
else if (t<x[0])
|
else if (t<x[0])
|
||||||
@@ -252,38 +278,46 @@ double DiagonalCurve::getVal (double t) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DCT_NURBS : {
|
case DCT_NURBS : {
|
||||||
// values under and over the first and last point
|
// get the hash table entry by rounding the value (previously multiplied by "hashSize")
|
||||||
if (t>x[N-1])
|
unsigned short int i = (unsigned short int)(t*hashSize);
|
||||||
return y[N-1];
|
|
||||||
else if (t<x[0])
|
|
||||||
return y[0];
|
|
||||||
else if (N == 2)
|
|
||||||
return y[0] + (t - x[0]) * ( y[1] - y[0] ) / (x[1] - x[0]);
|
|
||||||
|
|
||||||
// do a binary search for the right interval:
|
if (i > (hashSize+1)) {
|
||||||
int k_lo = 0, k_hi = poly_x.size() - 1;
|
//printf("\nOVERFLOW: hash #%d is used while seeking for value %.8f, corresponding polygon's point #%d (out of %d point) x value: %.8f\n\n", i, t, hash[i], poly_x.size(), poly_x[hash[i]]);
|
||||||
while (k_hi - k_lo > 1){
|
printf("\nOVERFLOW: hash #%d is used while seeking for value %.8f\n\n", i, t);
|
||||||
int k = (k_hi + k_lo) / 2;
|
return t;
|
||||||
if (poly_x[k] > t)
|
}
|
||||||
k_hi = k;
|
|
||||||
else
|
|
||||||
k_lo = k;
|
|
||||||
}
|
|
||||||
|
|
||||||
double h = poly_x[k_hi] - poly_x[k_lo];
|
unsigned int k_lo = 0;
|
||||||
return poly_y[k_lo] + (t - poly_x[k_lo]) * ( poly_y[k_hi] - poly_y[k_lo] ) / h;
|
unsigned int k_hi = 0;
|
||||||
break;
|
|
||||||
|
k_lo = hash[i];
|
||||||
|
k_hi = hash[i+1];
|
||||||
|
|
||||||
|
// do a binary search for the right interval :
|
||||||
|
while (k_hi - k_lo > 1){
|
||||||
|
unsigned int k = (k_hi + k_lo) / 2;
|
||||||
|
if (poly_x[k] > t)
|
||||||
|
k_hi = k;
|
||||||
|
else
|
||||||
|
k_lo = k;
|
||||||
|
}
|
||||||
|
if (k_lo == k_hi)
|
||||||
|
k_hi = k_lo+1;
|
||||||
|
|
||||||
|
double dx = poly_x[k_hi] - poly_x[k_lo];
|
||||||
|
double dy = poly_y[k_hi] - poly_y[k_lo];
|
||||||
|
return poly_y[k_lo] + (t - poly_x[k_lo]) * ( dy ) / dx;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
case DCT_Empty :
|
||||||
default:
|
default:
|
||||||
// all other (unknown) kind
|
// all other (unknown) kind
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DiagonalCurve::getVal (const std::vector<double>& t, std::vector<double>& res) {
|
void DiagonalCurve::getVal (const std::vector<double>& t, std::vector<double>& res) {
|
||||||
|
|
||||||
// TODO!!!! can be made much faster!!! Binary search of getVal(double) at each point can be avoided
|
|
||||||
|
|
||||||
res.resize (t.size());
|
res.resize (t.size());
|
||||||
for (unsigned int i=0; i<t.size(); i++)
|
for (unsigned int i=0; i<t.size(); i++)
|
||||||
res[i] = getVal(t[i]);
|
res[i] = getVal(t[i]);
|
||||||
|
@@ -38,10 +38,12 @@ FlatCurve::FlatCurve (const std::vector<double>& p, bool isPeriodic, int poly_pn
|
|||||||
kind = FCT_Empty;
|
kind = FCT_Empty;
|
||||||
periodic = isPeriodic;
|
periodic = isPeriodic;
|
||||||
|
|
||||||
|
bool identity = true;
|
||||||
|
|
||||||
if (p.size()>4) {
|
if (p.size()>4) {
|
||||||
kind = (FlatCurveType)p[0];
|
kind = (FlatCurveType)p[0];
|
||||||
if (kind==FCT_MinMaxCPoints) {
|
if (kind==FCT_MinMaxCPoints) {
|
||||||
int oneMorePoint = periodic ? 1:0;
|
int oneMorePoint = periodic ? 1:0;
|
||||||
N = (p.size()-1)/4;
|
N = (p.size()-1)/4;
|
||||||
x = new double[N+oneMorePoint];
|
x = new double[N+oneMorePoint];
|
||||||
y = new double[N+oneMorePoint];
|
y = new double[N+oneMorePoint];
|
||||||
@@ -53,22 +55,28 @@ FlatCurve::FlatCurve (const std::vector<double>& p, bool isPeriodic, int poly_pn
|
|||||||
y[i] = p[ix++];
|
y[i] = p[ix++];
|
||||||
leftTangent[i] = p[ix++];
|
leftTangent[i] = p[ix++];
|
||||||
rightTangent[i] = p[ix++];
|
rightTangent[i] = p[ix++];
|
||||||
|
if (y[i] != 0.5f)
|
||||||
|
identity = false;
|
||||||
}
|
}
|
||||||
// The first point is copied to the end of the point list, to handle the curve periodicity
|
// The first point is copied to the end of the point list, to handle the curve periodicity
|
||||||
if (periodic) {
|
if (periodic) {
|
||||||
x[N] = p[1]+1.0;
|
x[N] = p[1]+1.0;
|
||||||
y[N] = p[2];
|
y[N] = p[2];
|
||||||
leftTangent[N] = p[3];
|
leftTangent[N] = p[3];
|
||||||
rightTangent[N] = p[4];
|
rightTangent[N] = p[4];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
N--;
|
N--;
|
||||||
}
|
}
|
||||||
if (N > 0+(periodic?1:0) )
|
if (!identity && N > 0+(periodic?1:0) ) {
|
||||||
CtrlPoints_set ();
|
CtrlPoints_set ();
|
||||||
|
fillHash();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/*else if (kind==FCT_Parametric) {
|
/*else if (kind==FCT_Parametric) {
|
||||||
}*/
|
}*/
|
||||||
|
if (identity)
|
||||||
|
kind = FCT_Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,11 +283,11 @@ void FlatCurve::CtrlPoints_set () {
|
|||||||
}
|
}
|
||||||
// increment along the curve, not along the X axis
|
// increment along the curve, not along the X axis
|
||||||
increment = 1.0 / (double)(nbr_points-1);
|
increment = 1.0 / (double)(nbr_points-1);
|
||||||
x1 = sc_x[j]; y1 = sc_y[j++];
|
x1 = sc_x[j]; y1 = sc_y[j++];
|
||||||
x2 = sc_x[j]; y2 = sc_y[j++];
|
x2 = sc_x[j]; y2 = sc_y[j++];
|
||||||
x3 = sc_x[j]; y3 = sc_y[j++];
|
x3 = sc_x[j]; y3 = sc_y[j++];
|
||||||
AddPolygons ();
|
AddPolygons ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// adding an final horizontal line if necessary
|
// adding an final horizontal line if necessary
|
||||||
@@ -306,18 +314,16 @@ double FlatCurve::getVal (double t) {
|
|||||||
switch (kind) {
|
switch (kind) {
|
||||||
|
|
||||||
case FCT_MinMaxCPoints : {
|
case FCT_MinMaxCPoints : {
|
||||||
/* To be updated if we have to handle non periodic flat curves
|
/* To be updated if we have to handle non periodic flat curves
|
||||||
// values under and over the first and last point
|
// values under and over the first and last point
|
||||||
if (t>x[N-1])
|
if (t>x[N-1])
|
||||||
return y[N-1];
|
return y[N-1];
|
||||||
else if (t<x[0])
|
else if (t<x[0])
|
||||||
return y[0];
|
return y[0];
|
||||||
else if (N == 2)
|
|
||||||
return y[0] + (t - x[0]) * ( y[1] - y[0] ) / (x[1] - x[0]);
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// magic to handle curve periodicity : we look above the 1.0 bound for the value
|
// magic to handle curve periodicity : we look above the 1.0 bound for the value
|
||||||
if (t < poly_x[0]) t += 1.0;
|
if (t < poly_x[0]) t += 1.0;
|
||||||
|
|
||||||
// do a binary search for the right interval:
|
// do a binary search for the right interval:
|
||||||
int k_lo = 0, k_hi = poly_x.size() - 1;
|
int k_lo = 0, k_hi = poly_x.size() - 1;
|
||||||
@@ -337,16 +343,14 @@ double FlatCurve::getVal (double t) {
|
|||||||
break;
|
break;
|
||||||
}*/
|
}*/
|
||||||
case FCT_Empty :
|
case FCT_Empty :
|
||||||
case FCT_Linear :
|
case FCT_Linear : // Linear doesn't exist yet and is then considered as identity
|
||||||
default:
|
default:
|
||||||
return t;
|
return 0.5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlatCurve::getVal (const std::vector<double>& t, std::vector<double>& res) {
|
void FlatCurve::getVal (const std::vector<double>& t, std::vector<double>& res) {
|
||||||
|
|
||||||
// TODO!!!! can be made much faster!!! Binary search of getVal(double) at each point can be avoided
|
|
||||||
|
|
||||||
res.resize (t.size());
|
res.resize (t.size());
|
||||||
for (unsigned int i=0; i<t.size(); i++)
|
for (unsigned int i=0; i<t.size(); i++)
|
||||||
res[i] = getVal(t[i]);
|
res[i] = getVal(t[i]);
|
||||||
|
@@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
// For compatibility and simplicity reason, order shouldn't change, and must be identical to the order specified in the curveType widget
|
// For compatibility and simplicity reason, order shouldn't change, and must be identical to the order specified in the curveType widget
|
||||||
enum DiagonalCurveType {
|
enum DiagonalCurveType {
|
||||||
DCT_Empty = -1,
|
DCT_Empty = -1, // Also used for identity curves
|
||||||
DCT_Linear, // 0
|
DCT_Linear, // 0
|
||||||
DCT_Spline, // 1
|
DCT_Spline, // 1
|
||||||
DCT_Parametric, // 2
|
DCT_Parametric, // 2
|
||||||
|
@@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
// For compatibility and simplicity reason, order shouldn't change, and must be identical to the order specified in the curveType widget
|
// For compatibility and simplicity reason, order shouldn't change, and must be identical to the order specified in the curveType widget
|
||||||
enum FlatCurveType {
|
enum FlatCurveType {
|
||||||
FCT_Empty = -1,
|
FCT_Empty = -1, // Also used for identity curves
|
||||||
FCT_Linear, // 0
|
FCT_Linear, // 0
|
||||||
FCT_MinMaxCPoints, // 1
|
FCT_MinMaxCPoints, // 1
|
||||||
//FCT_Parametric, // 2
|
//FCT_Parametric, // 2
|
||||||
|
Reference in New Issue
Block a user