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,6 +38,13 @@ namespace rtengine {
|
||||
x = 0;
|
||||
y = 0;
|
||||
ypp = 0;
|
||||
hash = NULL;
|
||||
hashSize = 1000; // has to be initiallised to the maximum value
|
||||
}
|
||||
|
||||
Curve::~Curve () {
|
||||
if (hash)
|
||||
delete [] hash;
|
||||
}
|
||||
|
||||
void Curve::AddPolygons ()
|
||||
@@ -62,12 +69,71 @@ namespace rtengine {
|
||||
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.
|
||||
// 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.
|
||||
const double CurveFactory::sRGBGamma = 2.2;
|
||||
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, \
|
||||
@@ -76,16 +142,18 @@ namespace rtengine {
|
||||
|
||||
//colormult = chroma_scale for Lab manipulations
|
||||
|
||||
//-----------------------------------------------------
|
||||
|
||||
bool needed;
|
||||
DiagonalCurve* dCurve = NULL;
|
||||
|
||||
// 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);
|
||||
// Filling the curve if needed
|
||||
if (needed) {
|
||||
|
||||
LUTf dscurve (65536);
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
//%%%%%%%%%%%%%%%%% Saturation curve's control points %%%%%%%%%%%%%%%%%
|
||||
std::vector<double> satcurvePoints;
|
||||
satcurvePoints.push_back((double)DCT_NURBS);
|
||||
if (saturation>0) {
|
||||
@@ -125,69 +193,48 @@ namespace rtengine {
|
||||
satcurvePoints.push_back(1);
|
||||
satcurvePoints.push_back(1+saturation/200.0);
|
||||
}
|
||||
DiagonalCurve* satcurve = new DiagonalCurve (satcurvePoints, CURVES_MIN_POLY_POINTS/skip); // Actually, CURVES_MIN_POLY_POINTS = 1000,
|
||||
dCurve = new DiagonalCurve (satcurvePoints, CURVES_MIN_POLY_POINTS/skip);
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
fillCurveArray(dCurve, satCurve, skip, needed);
|
||||
|
||||
delete dCurve;
|
||||
dCurve = NULL;
|
||||
}
|
||||
else {
|
||||
fillCurveArray(NULL, satCurve, skip, needed);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------
|
||||
|
||||
needed = false;
|
||||
// create a curve if needed
|
||||
DiagonalCurve* tacurve = NULL;
|
||||
if (acurvePoints.size()>0 && acurvePoints[0]!=0)
|
||||
tacurve = new DiagonalCurve (acurvePoints, CURVES_MIN_POLY_POINTS/skip);
|
||||
DiagonalCurve* tbcurve = NULL;
|
||||
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);
|
||||
if (acurvePoints.size()>0 && acurvePoints[0]!=0) {
|
||||
dCurve = new DiagonalCurve (acurvePoints, CURVES_MIN_POLY_POINTS/skip);
|
||||
if (dCurve && !dCurve->isIdentity())
|
||||
needed = true;
|
||||
}
|
||||
// apply custom/parametric/NURBS curve, if any
|
||||
if (tbcurve) {
|
||||
bval = tbcurve->getVal (bval);
|
||||
fillCurveArray(dCurve, aoutCurve, skip, needed);
|
||||
if (dCurve) {
|
||||
delete dCurve;
|
||||
dCurve = NULL;
|
||||
}
|
||||
|
||||
// store result in a temporary array
|
||||
dacurve[i] = (aval);
|
||||
dbcurve[i] = (bval);
|
||||
dscurve[i] = (sval);
|
||||
}
|
||||
//-----------------------------------------------------
|
||||
|
||||
delete tacurve;
|
||||
delete tbcurve;
|
||||
|
||||
// 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;
|
||||
needed = false;
|
||||
if (bcurvePoints.size()>0 && bcurvePoints[0]!=0) {
|
||||
dCurve = new DiagonalCurve (bcurvePoints, CURVES_MIN_POLY_POINTS/skip);
|
||||
if (dCurve && !dCurve->isIdentity())
|
||||
needed = true;
|
||||
}
|
||||
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;
|
||||
fillCurveArray(dCurve, boutCurve, skip, needed);
|
||||
if (dCurve) {
|
||||
delete dCurve;
|
||||
dCurve = NULL;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
@@ -223,17 +270,9 @@ namespace rtengine {
|
||||
// curve without contrast
|
||||
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
|
||||
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
|
||||
outBeforeCCurveHistogram.clear();
|
||||
|
||||
@@ -241,11 +280,16 @@ 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
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
DiagonalCurve* brightcurve = NULL;
|
||||
|
||||
// check if brightness curve is needed
|
||||
if (br>0.00001 || br<-0.00001) {
|
||||
|
||||
std::vector<double> brightcurvePoints;
|
||||
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
|
||||
brightcurvePoints.push_back(0.); //black point. Value in [0 ; 1] range
|
||||
brightcurvePoints.push_back(0.); //black point. Value in [0 ; 1] range
|
||||
|
||||
if(br>0) {
|
||||
brightcurvePoints.push_back(0.1); //toe point
|
||||
@@ -260,10 +304,11 @@ namespace rtengine {
|
||||
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(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,
|
||||
brightcurve = new DiagonalCurve (brightcurvePoints, CURVES_MIN_POLY_POINTS/skip);
|
||||
}
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
float exp_scale = a;
|
||||
@@ -311,17 +356,23 @@ namespace rtengine {
|
||||
val = gamma (val, gamma_, start, slope, mul, add);
|
||||
|
||||
// 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
|
||||
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
|
||||
int sum = 0;
|
||||
float avg = 0;
|
||||
@@ -352,23 +403,36 @@ namespace rtengine {
|
||||
contrastcurvePoints.push_back(1); // 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
|
||||
for (int i=0; i<=0xffff; i++) {
|
||||
dcurve[i] = contrastcurve->getVal (dcurve[i]);
|
||||
}
|
||||
|
||||
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++) {
|
||||
float val;
|
||||
|
||||
// apply custom/parametric/NURBS curve, if any
|
||||
if (tcurve) {
|
||||
if (outBeforeCCurveHistogram && histogramCropped) {
|
||||
if (histNeeded) {
|
||||
float fi=i;
|
||||
float hval = dcurve[shCurve[hlCurve[i]*fi]*fi];
|
||||
//if (needigamma)
|
||||
@@ -376,7 +440,10 @@ namespace rtengine {
|
||||
int hi = (int)(255.0*(hval));
|
||||
outBeforeCCurveHistogram[hi] += histogramCropped[i] ;
|
||||
}
|
||||
val = tcurve->getVal (dcurve[i]);
|
||||
|
||||
// apply custom/parametric/NURBS curve, if any
|
||||
if (tcurve) {
|
||||
val = tcurve->getVal (dcurve[i]); // TODO: getVal(double) is very slow! Optimize with a LUTf
|
||||
} else {
|
||||
val = (dcurve[i]);
|
||||
}
|
||||
@@ -388,13 +455,12 @@ namespace rtengine {
|
||||
outCurve[i] = (65535.0 * val);
|
||||
}
|
||||
|
||||
|
||||
if (tcurve)
|
||||
delete tcurve;
|
||||
delete brightcurve;
|
||||
|
||||
/*if (outBeforeCCurveHistogram) {
|
||||
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
|
||||
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
|
||||
if (outBeforeCCurveHistogram)
|
||||
outBeforeCCurveHistogram.clear();
|
||||
@@ -426,11 +483,14 @@ 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
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
// check if brightness curve is needed
|
||||
if (br>0.00001 || br<-0.00001) {
|
||||
|
||||
std::vector<double> brightcurvePoints;
|
||||
brightcurvePoints.push_back((double)((CurveType)DCT_NURBS));
|
||||
|
||||
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.); // black point. Value in [0 ; 1] range
|
||||
brightcurvePoints.push_back(0.); // black point. Value in [0 ; 1] range
|
||||
|
||||
if (br>0) {
|
||||
brightcurvePoints.push_back(0.1); // toe point
|
||||
@@ -445,12 +505,13 @@ namespace rtengine {
|
||||
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(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,
|
||||
DiagonalCurve* brightcurve = new DiagonalCurve (brightcurvePoints, CURVES_MIN_POLY_POINTS/skip);
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
// 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
|
||||
@@ -462,12 +523,22 @@ namespace rtengine {
|
||||
// store result in a temporary array
|
||||
dcurve[i] = CLIPD(val);
|
||||
}
|
||||
delete brightcurve;
|
||||
}
|
||||
else {
|
||||
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
|
||||
dcurve[i] = (float)i / 32767.0;
|
||||
}
|
||||
}
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
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
|
||||
int sum = 0;
|
||||
float avg = 0;
|
||||
@@ -485,8 +556,8 @@ namespace rtengine {
|
||||
std::vector<double> contrastcurvePoints;
|
||||
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)); // 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)); // value at shoulder point
|
||||
|
||||
contrastcurvePoints.push_back(1); // white point
|
||||
contrastcurvePoints.push_back(1); // value at white point
|
||||
contrastcurvePoints.push_back(1.); // 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
|
||||
@@ -509,32 +580,57 @@ namespace rtengine {
|
||||
}
|
||||
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
for (int i=0; i<32768; i++) { // L values go up to 32767, last stop is for highlight overflow
|
||||
// create a curve if needed
|
||||
DiagonalCurve* tcurve = NULL;
|
||||
bool histNeeded = false;
|
||||
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;
|
||||
}
|
||||
|
||||
if (tcurve) {
|
||||
// L values go up to 32767, last stop is for highlight overflow
|
||||
for (int i=0; i<32768; i++) {
|
||||
float val;
|
||||
|
||||
// apply custom/parametric/NURBS curve, if any
|
||||
if (tcurve) {
|
||||
if (outBeforeCCurveHistogram) {
|
||||
if (histNeeded) {
|
||||
float hval = dcurve[i];
|
||||
int hi = (int)(255.0*CLIPD(hval));
|
||||
outBeforeCCurveHistogram[hi]+=histogramCropped[i] ;
|
||||
}
|
||||
|
||||
// apply custom/parametric/NURBS curve, if any
|
||||
val = tcurve->getVal (dcurve[i]);
|
||||
} else {
|
||||
val = (dcurve[i]);
|
||||
}
|
||||
|
||||
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
|
||||
for (int i=0; i<32768; i++) {
|
||||
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;
|
||||
delete brightcurve;
|
||||
|
||||
/*if (outBeforeCCurveHistogram) {
|
||||
for (int i=0; i<256; i++) printf("i= %d bchist= %d \n",i,outBeforeCCurveHistogram[i]);
|
||||
}*/
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@@ -35,7 +35,6 @@
|
||||
|
||||
#define CLIPI(a) ((a)>0?((a)<65534?(a):65534):0)
|
||||
|
||||
|
||||
namespace rtengine {
|
||||
|
||||
class CurveFactory {
|
||||
@@ -211,6 +210,9 @@ class Curve {
|
||||
double* y;
|
||||
std::vector<double> poly_x; // X points of the faceted curve
|
||||
std::vector<double> poly_y; // Y points of the faceted curve
|
||||
unsigned short int* hash;
|
||||
unsigned int hashSize; // hash table's size, between [10, 100, 1000]
|
||||
|
||||
double* ypp;
|
||||
|
||||
// Fields for the elementary curve polygonisation
|
||||
@@ -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 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 ();
|
||||
~Curve ();
|
||||
void AddPolygons ();
|
||||
virtual double getVal (double t) = 0;
|
||||
virtual void getVal (const std::vector<double>& t, std::vector<double>& res) = 0;
|
||||
|
||||
virtual bool isIdentity () = 0;
|
||||
};
|
||||
|
||||
class DiagonalCurve : public Curve {
|
||||
@@ -238,6 +244,10 @@ 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 ();
|
||||
|
||||
@@ -247,6 +257,7 @@ class DiagonalCurve : public Curve {
|
||||
|
||||
double getVal (double t);
|
||||
void getVal (const std::vector<double>& t, std::vector<double>& res);
|
||||
bool isIdentity () { return kind==DCT_Empty; };
|
||||
};
|
||||
|
||||
class FlatCurve : public Curve {
|
||||
@@ -266,6 +277,7 @@ class FlatCurve : public Curve {
|
||||
|
||||
double getVal (double t);
|
||||
void getVal (const std::vector<double>& t, std::vector<double>& res);
|
||||
bool isIdentity () { return kind==FCT_Empty; };
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -31,6 +31,10 @@ namespace rtengine {
|
||||
DiagonalCurve::DiagonalCurve (const std::vector<double>& p, int 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) {
|
||||
kind = DCT_Empty;
|
||||
@@ -45,17 +49,28 @@ DiagonalCurve::DiagonalCurve (const std::vector<double>& p, int poly_pn) {
|
||||
for (int i=0; i<N; i++) {
|
||||
x[i] = p[ix++];
|
||||
y[i] = p[ix++];
|
||||
if (x[i] != y[i])
|
||||
identity = false;
|
||||
}
|
||||
if (kind==DCT_Spline)
|
||||
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)
|
||||
else if (kind==DCT_NURBS && N > 2) {
|
||||
NURBS_set ();
|
||||
fillHash();
|
||||
}
|
||||
else kind=DCT_Linear;
|
||||
}
|
||||
}
|
||||
else if (kind==DCT_Parametric) {
|
||||
if (p.size()!=8 && p.size()!=9)
|
||||
kind = DCT_Empty;
|
||||
else {
|
||||
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)) {
|
||||
identity = false;
|
||||
|
||||
x = new double[9];
|
||||
for (int i=0; i<4; 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;
|
||||
}
|
||||
}
|
||||
if (identity) {
|
||||
kind = DCT_Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +100,7 @@ DiagonalCurve::~DiagonalCurve () {
|
||||
void DiagonalCurve::spline_cubic_set () {
|
||||
|
||||
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[0] = u[0] = 0.0; /* set lower boundary condition to "natural" */
|
||||
@@ -113,7 +131,7 @@ void DiagonalCurve::NURBS_set () {
|
||||
double total_length=0.;
|
||||
|
||||
// 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 k = 0;
|
||||
@@ -165,13 +183,21 @@ void DiagonalCurve::NURBS_set () {
|
||||
poly_y.clear();
|
||||
unsigned int sc_xsize=j-1;
|
||||
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
|
||||
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);
|
||||
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);
|
||||
}
|
||||
// increment along the curve, not along the X axis
|
||||
@@ -182,16 +208,16 @@ void DiagonalCurve::NURBS_set () {
|
||||
firstPointIncluded = !i;
|
||||
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) {
|
||||
|
||||
switch (kind) {
|
||||
|
||||
case DCT_Empty :
|
||||
return t;
|
||||
break;
|
||||
|
||||
case DCT_Parametric : {
|
||||
if (t<=1e-14)
|
||||
return 0.0;
|
||||
@@ -252,28 +278,38 @@ double DiagonalCurve::getVal (double t) {
|
||||
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<x[0])
|
||||
return y[0];
|
||||
else if (N == 2)
|
||||
return y[0] + (t - x[0]) * ( y[1] - y[0] ) / (x[1] - x[0]);
|
||||
// get the hash table entry by rounding the value (previously multiplied by "hashSize")
|
||||
unsigned short int i = (unsigned short int)(t*hashSize);
|
||||
|
||||
// do a binary search for the right interval:
|
||||
int k_lo = 0, k_hi = poly_x.size() - 1;
|
||||
if (i > (hashSize+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]]);
|
||||
printf("\nOVERFLOW: hash #%d is used while seeking for value %.8f\n\n", i, t);
|
||||
return t;
|
||||
}
|
||||
|
||||
unsigned int k_lo = 0;
|
||||
unsigned int k_hi = 0;
|
||||
|
||||
k_lo = hash[i];
|
||||
k_hi = hash[i+1];
|
||||
|
||||
// do a binary search for the right interval :
|
||||
while (k_hi - k_lo > 1){
|
||||
int k = (k_hi + k_lo) / 2;
|
||||
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 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;
|
||||
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:
|
||||
// all other (unknown) kind
|
||||
return t;
|
||||
@@ -282,8 +318,6 @@ double DiagonalCurve::getVal (double t) {
|
||||
|
||||
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());
|
||||
for (unsigned int i=0; i<t.size(); i++)
|
||||
res[i] = getVal(t[i]);
|
||||
|
@@ -38,6 +38,8 @@ FlatCurve::FlatCurve (const std::vector<double>& p, bool isPeriodic, int poly_pn
|
||||
kind = FCT_Empty;
|
||||
periodic = isPeriodic;
|
||||
|
||||
bool identity = true;
|
||||
|
||||
if (p.size()>4) {
|
||||
kind = (FlatCurveType)p[0];
|
||||
if (kind==FCT_MinMaxCPoints) {
|
||||
@@ -53,6 +55,8 @@ FlatCurve::FlatCurve (const std::vector<double>& p, bool isPeriodic, int poly_pn
|
||||
y[i] = p[ix++];
|
||||
leftTangent[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
|
||||
if (periodic) {
|
||||
@@ -64,11 +68,15 @@ FlatCurve::FlatCurve (const std::vector<double>& p, bool isPeriodic, int poly_pn
|
||||
else {
|
||||
N--;
|
||||
}
|
||||
if (N > 0+(periodic?1:0) )
|
||||
if (!identity && N > 0+(periodic?1:0) ) {
|
||||
CtrlPoints_set ();
|
||||
fillHash();
|
||||
}
|
||||
}
|
||||
/*else if (kind==FCT_Parametric) {
|
||||
}*/
|
||||
if (identity)
|
||||
kind = FCT_Empty;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,8 +320,6 @@ double FlatCurve::getVal (double t) {
|
||||
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]);
|
||||
*/
|
||||
|
||||
// magic to handle curve periodicity : we look above the 1.0 bound for the value
|
||||
@@ -337,16 +343,14 @@ double FlatCurve::getVal (double t) {
|
||||
break;
|
||||
}*/
|
||||
case FCT_Empty :
|
||||
case FCT_Linear :
|
||||
case FCT_Linear : // Linear doesn't exist yet and is then considered as identity
|
||||
default:
|
||||
return t;
|
||||
return 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
for (unsigned int i=0; i<t.size(); 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
|
||||
enum DiagonalCurveType {
|
||||
DCT_Empty = -1,
|
||||
DCT_Empty = -1, // Also used for identity curves
|
||||
DCT_Linear, // 0
|
||||
DCT_Spline, // 1
|
||||
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
|
||||
enum FlatCurveType {
|
||||
FCT_Empty = -1,
|
||||
FCT_Empty = -1, // Also used for identity curves
|
||||
FCT_Linear, // 0
|
||||
FCT_MinMaxCPoints, // 1
|
||||
//FCT_Parametric, // 2
|
||||
|
Reference in New Issue
Block a user