Files
rawTherapee/rtengine/diagonalcurves.cc
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

325 lines
10 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/>.
*/
#include <glib.h>
#include <glib/gstdio.h>
#include "curves.h"
#include <cmath>
#include <vector>
#include "mytime.h"
#include <cstring>
#define CLIPD(a) ((a)>0.0?((a)<1.0?(a):1.0):0.0)
namespace rtengine {
DiagonalCurve::DiagonalCurve (const std::vector<double>& p, int poly_pn) {
ppn = poly_pn > 65500 ? 65500 : poly_pn;
bool identity = true;
if (ppn < 500) hashSize = 100; // Arbitrary cut-off value, but mutliple of 10
if (ppn < 50) hashSize = 10; // Arbitrary cut-off value, but mutliple of 10
if (p.size()<3) {
kind = DCT_Empty;
}
else {
kind = (DiagonalCurveType)p[0];
if (kind==DCT_Linear || kind==DCT_Spline || kind==DCT_NURBS) {
N = (p.size()-1)/2;
x = new double[N];
y = new double[N];
int ix = 1;
for (int i=0; i<N; i++) {
x[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;
}
}
else if (kind==DCT_Parametric) {
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];
for (int i=4; i<8; i++)
x[i] = (p[i]+100.0)/200.0;
if (p.size()<9)
x[8] = 1.0;
else
x[8] = p[8]/100.0;
mc = -xlog(2.0)/xlog(x[2]);
double mbase = pfull (0.5, x[8], x[6], x[5]);
mfc = mbase<=1e-14 ? 0.0 : xexp(xlog(mbase)/mc); // value of the curve at the center point
msc = -xlog(2.0)/xlog(x[1]/x[2]);
mhc = -xlog(2.0)/xlog((x[3]-x[2])/(1-x[2]));
}
}
if (identity) {
kind = DCT_Empty;
}
}
}
DiagonalCurve::~DiagonalCurve () {
delete [] x;
delete [] y;
delete [] ypp;
poly_x.clear();
poly_y.clear();
}
void DiagonalCurve::spline_cubic_set () {
double* u = new double[N-1];
delete [] ypp; // 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" */
for (int i = 1; i < N - 1; ++i) {
double sig = (x[i] - x[i - 1]) / (x[i + 1] - x[i - 1]);
double p = sig * ypp[i - 1] + 2.0;
ypp[i] = (sig - 1.0) / p;
u[i] = ((y[i + 1] - y[i])
/ (x[i + 1] - x[i]) - (y[i] - y[i - 1]) / (x[i] - x[i - 1]));
u[i] = (6.0 * u[i] / (x[i + 1] - x[i - 1]) - sig * u[i - 1]) / p;
}
ypp[N - 1] = 0.0;
for (int k = N - 2; k >= 0; --k)
ypp[k] = ypp[k] * ypp[k + 1] + u[k];
delete [] u;
}
void DiagonalCurve::NURBS_set () {
int nbSubCurvesPoints = N + (N-3)*2;
std::vector<double> sc_x(nbSubCurvesPoints); // X sub-curve points ( XP0,XP1,XP2, XP2,XP3,XP4, ...)
std::vector<double> sc_y(nbSubCurvesPoints); // Y sub-curve points ( YP0,YP1,YP2, YP2,YP3,YP4, ...)
std::vector<double> sc_length(N+2); // Length of the subcurves
double total_length=0.;
// Create the list of Bezier sub-curves
// NURBS_set is called if N > 2 and non identity only
int j = 0;
int k = 0;
for (int i = 0; i < N-1;) {
double length;
double dx;
double dy;
// first point (on the curve)
if (!i) {
sc_x[j] = x[i];
sc_y[j++] = y[i++];
}
else {
sc_x[j] = (x[i-1] + x[i]) / 2.;
sc_y[j++] = (y[i-1] + y[i]) / 2.;
}
// second point (control point)
sc_x[j] = x[i];
sc_y[j] = y[i++];
dx = sc_x[j] - sc_x[j-1];
dy = sc_y[j] - sc_y[j-1];
length = sqrt(dx*dx + dy*dy);
j++;
// third point (on the curve)
if (i==N-1) {
sc_x[j] = x[i];
sc_y[j] = y[i];
}
else {
sc_x[j] = (x[i-1] + x[i]) / 2.;
sc_y[j] = (y[i-1] + y[i]) / 2.;
}
dx = sc_x[j] - sc_x[j-1];
dy = sc_y[j] - sc_y[j-1];
length += sqrt(dx*dx + dy*dy);
j++;
// Storing the length of all sub-curves and the total length (to have a better distribution
// of the points along the curve)
sc_length[k++] = length;
total_length += length;
}
poly_x.clear();
poly_y.clear();
unsigned int sc_xsize=j-1;
j = 0;
// 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(size_t it=0;it < sc_x.size(); it+=3) printf("sc_length[%zu/3]=%f \n",it,sc_length[it/3]);
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
increment = 1.0 / (double)(nbr_points-1);
x1 = sc_x[i]; y1 = sc_y[i];
x2 = sc_x[i+1]; y2 = sc_y[i+1];
x3 = sc_x[i+2]; y3 = sc_y[i+2];
firstPointIncluded = !i;
AddPolygons ();
}
// adding the final horizontal segment, always (see under)
poly_x.push_back(3.0); // 3.0 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) const {
switch (kind) {
case DCT_Parametric : {
if (t<=1e-14)
return 0.0;
double tv = xexp(mc*xlog(t));
double base = pfull (tv, x[8], x[6], x[5]);
double stretched = base<=1e-14 ? 0.0 : xexp(xlog(base)/mc);
if (t<x[2]) {
// add shadows effect:
double stv = xexp(msc*xlog(stretched/mfc));
double sbase = pfull (stv, x[8], x[7], 0.5);
return mfc*(sbase<=1e-14 ? 0.0 : xexp(xlog(sbase)/msc));
}
else {
// add highlights effect:
double htv = xexp(mhc*xlog((stretched-mfc)/(1-mfc)));
double hbase = pfull (htv, x[8], 0.5, x[4]);
return mfc + (1-mfc)*(hbase<=1e-14 ? 0.0 : xexp(xlog(hbase)/mhc));
}
break;
}
case DCT_Linear :
case DCT_Spline : {
// 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];
// do a binary search for the right interval:
int k_lo = 0, k_hi = N - 1;
while (k_hi - k_lo > 1){
int k = (k_hi + k_lo) / 2;
if (x[k] > t)
k_hi = k;
else
k_lo = k;
}
double h = x[k_hi] - x[k_lo];
// linear
if (kind==DCT_Linear)
return y[k_lo] + (t - x[k_lo]) * ( y[k_hi] - y[k_lo] ) / h;
// spline curve
else { // if (kind==Spline) {
double a = (x[k_hi] - t) / h;
double b = (t - x[k_lo]) / h;
double r = a*y[k_lo] + b*y[k_hi] + ((a*a*a - a)*ypp[k_lo] + (b*b*b - b)*ypp[k_hi]) * (h*h)/6.0;
return CLIPD(r);
}
break;
}
case DCT_NURBS : {
// get the hash table entry by rounding the value (previously multiplied by "hashSize")
unsigned short int i = (unsigned short int)(t*hashSize);
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.at(i), poly_x.size(), poly_x[hash.at(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.at(i).smallerValue;
k_hi = hash.at(i).higherValue;
// 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:
// all other (unknown) kind
return t;
}
}
void DiagonalCurve::getVal (const std::vector<double>& t, std::vector<double>& res) const {
res.resize (t.size());
for (unsigned int i=0; i<t.size(); i++)
res[i] = getVal(t[i]);
}
}