1 /*
2  */
3 
4 /*
5 
6     Copyright (C) 2014 Ferrero Andrea
7 
8     This program is free software: you can redistribute it and/or modify
9     it under the terms of the GNU General Public License as published by
10     the Free Software Foundation, either version 3 of the License, or
11     (at your option) any later version.
12 
13     This program is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16     GNU General Public License for more details.
17 
18     You should have received a copy of the GNU General Public License
19     along with this program. If not, see <http://www.gnu.org/licenses/>.
20 
21 
22  */
23 
24 /*
25 
26     These files are distributed with PhotoFlow - http://aferrero2707.github.io/PhotoFlow/
27 
28  */
29 
30 #include <stdlib.h>
31 #include <vips/vips.h>
32 
33 #include "splinecurve.hh"
34 
SplineCurve()35 PF::SplineCurve::SplineCurve():
36   PF::Curve(),
37   ypp( NULL ),
38   ypp_size( 0 ),
39   circular( false ),
40   //needs_gamma_correction( false )
41   trc_type( PF::PF_TRC_STANDARD )
42 {
43   points_mutex = vips_g_mutex_new();
44 #ifdef SPLINE_USE_STDVEC
45   points.push_back( std::make_pair(float(0),float(0)) );
46   points.push_back( std::make_pair(float(1),float(1)) );
47 #else
48   points = new std::pair<float,float>[2];
49   points[0] = std::make_pair(float(0),float(0));
50   points[1] = std::make_pair(float(1),float(1));
51   npoints = 2;
52 #endif
53   update_spline();
54 }
55 
56 
57 
~SplineCurve()58 PF::SplineCurve::~SplineCurve()
59 {
60 }
61 
62 
63 
add_point(float x,float y)64 int PF::SplineCurve::add_point( float x, float y )
65 {
66   //if( (get_npoints()>0) && (x<=points[0].first) ) return -1;
67   //if( (get_npoints()>1) && (x >= points[get_npoints()-1].first) ) return -1;
68   //return -1;
69   lock();
70 #ifdef SPLINE_USE_STDVEC
71 #ifndef NDEBUG
72   std::cout<<"PF::SplineCurve::add_point( "<<x<<", "<<y<<" ): points.size()="<<points.size()<<std::endl;
73 #endif
74   for( unsigned int i = 0; i < points.size(); i++ ) {
75     if( points[i].first <= x ) continue;
76     points.insert( points.begin()+i, std::make_pair(x,y) );
77 #ifndef NDEBUG
78     std::cout<<"PF::SplineCurve::add_point( "<<x<<", "<<y<<" ): point added before "<<points[i].first<<std::endl;
79 #endif
80     update_spline();
81     unlock();
82     return i;
83   }
84   points.push_back( std::make_pair(x,y) );
85   update_spline();
86   unlock();
87   return( points.size()-1 );
88 #else
89   npoints += 1;
90   std::pair<float,float>* points_new = new std::pair<float,float>[npoints];
91 #ifndef NDEBUG
92   //std::cout<<"PF::SplineCurve::add_point( "<<x<<", "<<y<<" ): points.size()="<<points.size()<<std::endl;
93   std::cout<<"PF::SplineCurve::add_point( "<<x<<", "<<y<<" ): npoints="<<npoints<<std::endl;
94 #endif
95   //for( unsigned int i = 0; i < points.size(); i++ ) {
96   for( unsigned int i = 0; i < npoints-1; i++ ) {
97     if( points[i].first <= x ) {
98       points_new[i] = points[i];
99       continue;
100     }
101 #ifndef NDEBUG
102     std::cout<<"PF::SplineCurve::add_point( "<<x<<", "<<y<<" ): adding point before "<<points[i].first<<std::endl;
103 #endif
104     //points.insert( points.begin()+i, std::make_pair(x,y) );
105     for( size_t j = i; j < npoints-1; j++)
106       points_new[j+1] = points[j];
107     points_new[i] = std::make_pair(x,y);
108     delete[] points;
109     points = points_new;
110     update_spline();
111     unlock();
112     return i;
113   }
114   //points.push_back( std::make_pair(x,y) );
115   points_new[npoints-1] = std::make_pair(x,y);
116   delete[] points;
117   points = points_new;
118   update_spline();
119   unlock();
120   //return( points.size()-1 );
121   return -1;
122 #endif
123 }
124 
125 
remove_point(unsigned int id)126 bool PF::SplineCurve::remove_point( unsigned int id )
127 {
128   if( id == 0 ) return false;
129   if( id >= (get_npoints()-1) ) return false;
130 
131 #ifdef SPLINE_USE_STDVEC
132   points.erase( points.begin() + id );
133 #else
134   npoints -= 1;
135   std::pair<float,float>* points_new = new std::pair<float,float>[npoints];
136 
137   for( unsigned int i = 0; i < id; i++ ) {
138     points_new[i] = points[i];
139   }
140   for( size_t i = id; i < npoints; i++)
141     points_new[i] = points[i+1];
142   delete[] points;
143   points = points_new;
144 #endif
145   update_spline();
146   return true;
147 }
148 
149 
set_point(unsigned int id,float & x,float & y)150 bool PF::SplineCurve::set_point( unsigned int id, float& x, float& y )
151 {
152   //if( id >= points.size() ) return false;
153   if( id >= get_npoints() ) return false;
154 
155   if( x < 0 ) x = 0;
156   if( y < 0 ) y = 0;
157   if( x > 1 ) x = 1;
158   if( y > 1 ) y = 1;
159 
160   if( (id>0) && (x<=points[id-1].first) ) x = points[id].first;
161   //if( (id<(points.size()-1)) && (x>=points[id+1].first) ) x = points[id].first;
162   if( (id<(get_npoints()-1)) && (x>=points[id+1].first) ) x = points[id].first;
163   points[id] = std::make_pair( x, y );
164   if( is_circular() && get_npoints() > 1 ) {
165     if( x==0 && points[get_npoints()-1].first==1 ) {
166       points[get_npoints()-1].second = points[id].second;
167     }
168     if( x==1 && points[0].first==0 ) {
169       points[0].second = points[id].second;
170     }
171   }
172   update_spline();
173   return true;
174 }
175 
176 
update_spline()177 void PF::SplineCurve::update_spline()
178 {
179   //unsigned int N = points.size();
180   unsigned int N = get_npoints();
181   if( N < 2) return;
182 
183   if( is_circular() ) {
184     bool overlap = false;
185     if( points[0].first == (points[N-1].first-1.0f) ) overlap = true;
186     if( overlap && (N < 3) ) {
187       points2 = points;
188     } else {
189       points2.clear();
190       if( overlap ) {
191         points2.push_back( points[N-3] );
192         points2.push_back( points[N-2] );
193       } else {
194         points2.push_back( points[N-2] );
195         points2.push_back( points[N-1] );
196       }
197       points2[0].first = points2[0].first - 1.0f;
198       points2[1].first = points2[1].first - 1.0f;
199       size_t max = points.size()-1;
200       //if( overlap ) max -= 1;
201       for( size_t i = 0; i <= max; i++ ) {
202         points2.push_back( points[i] );
203       }
204       if( overlap ) {
205         points2.push_back( points[1] );
206         points2.push_back( points[2] );
207       } else {
208         points2.push_back( points[0] );
209         points2.push_back( points[1] );
210       }
211       points2[points2.size()-2].first += 1.0f;
212       points2[points2.size()-1].first += 1.0f;
213       N = points2.size();
214     }
215   } else {
216     points2 = points;
217     if( trc_type == PF::PF_TRC_LINEAR ) {
218       //std::cout<<"SplineCurve::update_spline(): p2l_trc="<<p2l_trc<<std::endl;
219       for( unsigned int i = 0; i < points2.size(); i++ ) {
220         points2[i].first = cmsEvalToneCurveFloat( p2l_trc, points2[i].first );
221         points2[i].second = cmsEvalToneCurveFloat( p2l_trc, points2[i].second );
222         //std::cout<<"  points["<<i<<"].first="<<points[i].first<<" -> points2["<<i<<"].first="<<points2[i].first<<std::endl;
223         //std::cout<<"  points["<<i<<"].second="<<points[i].second<<" -> points2["<<i<<"].second="<<points2[i].second<<std::endl;
224       }
225     }
226   }
227 
228   N = points2.size();
229 
230   double* u = new double[N-1];
231   if( ypp ) delete [] ypp;
232   ypp = new double [N];
233   ypp_size = N;
234   //std::cout<<"SplineCurve::update_spline(): ypp_size="<<ypp_size<<std::endl;
235 
236   ypp[0] = u[0] = 0.0;	/* set lower boundary condition to "natural" */
237 
238   for  (int i = 1; i < (int)N - 1; ++i) {
239     double sig = (points2[i].first - points2[i - 1].first) / (points2[i + 1].first - points2[i - 1].first);
240     double p = sig * ypp[i - 1] + 2.0;
241     ypp[i] = (sig - 1.0) / p;
242     u[i] = ((points2[i + 1].second - points2[i].second)
243 	    / (points2[i + 1].first - points2[i].first) - (points2[i].second - points2[i - 1].second) / (points2[i].first - points2[i - 1].first));
244     u[i] = (6.0 * u[i] / (points2[i + 1].first - points2[i - 1].first) - sig * u[i - 1]) / p;
245   }
246 
247   ypp[N - 1] = 0.0;
248   for (int k = (int)N - 2; k >= 0; --k)
249     ypp[k] = ypp[k] * ypp[k + 1] + u[k];
250 
251   delete [] u;
252 }
253 
254 
255 
256 #define CLIPD(a) ((a)>0.0?((a)<1.0?(a):1.0):0.0)
257 
get_value(float x)258 float PF::SplineCurve::get_value( float x )
259 {
260   //int size = points.size();
261   unsigned int N = points2.size();
262   //unsigned int N = get_npoints();
263   //if( is_circular() ) N += 2;
264   //std::cout<<"size = "<<size<<std::endl;
265   //return x;
266 
267   if( x <= points2[0].first ) return points2[0].second;
268   if( x >= points2[N-1].first ) return points2[N-1].second;
269 
270   //std::cout<<"N = "<<N<<std::endl;
271   // do a binary search for the right interval:
272   int k_lo = 0, k_hi = N - 1;
273   while (k_hi - k_lo > 1){
274     int k = (k_hi + k_lo) / 2;
275     //std::cout<<"  k = "<<k<<std::endl;
276     if (points2[k].first > x)
277       k_hi = k;
278     else
279       k_lo = k;
280   }
281 
282   std::pair<float,float> p_lo = points2[k_lo];
283   std::pair<float,float> p_hi = points2[k_hi];
284   double h = p_hi.first - p_lo.first;
285   //std::cout<<"points2.size()="<<points2.size()<<"  h="<<"points2["<<k_hi<<"].first - points2["<<k_lo<<"].first = "<<h<<std::endl;
286 //std::cout<<"  points2[k_hi].second="<<points2[k_hi].second<<"  points2[k_lo].second="<<points2[k_lo].second<<std::endl;
287   // linear
288   if( N == 2) {
289     float result = p_lo.second + (x - p_lo.first) * ( p_hi.second - p_lo.second ) / h;
290     //std::cout<<"result = "<<result<<std::endl;
291     return result;
292   // spline curve
293   } else { // if (kind==Spline) {
294     if( k_hi >= (int)ypp_size )
295       std::cout<<"k_lo="<<k_lo<<"  k_hi="<<k_hi<<"  ypp_size="<<ypp_size<<"  N="<<N<<std::endl;
296     double a = (points2[k_hi].first - x) / h;
297     double b = (x - points2[k_lo].first) / h;
298     double r1 = a*points2[k_lo].second;
299     double r2 = b*points2[k_hi].second;
300     double r3 = (a*a*a - a)*ypp[k_lo];
301     double r4 = (b*b*b - b)*ypp[k_hi];
302     double r = r1 + r2 +(r3+r4)*(h*h)/6.0;
303 /*
304     double r =
305         a*points2[k_lo].second +
306         b*points2[k_hi].second +
307         ((a*a*a - a)*ypp[k_lo] +
308             (b*b*b - b)*ypp[k_hi]) *
309             (h*h)/6.0;
310  */
311     return CLIPD(r);
312   }
313 }
314 
315 
get_delta(float x)316 float PF::SplineCurve::get_delta( float x )
317 {
318   return( get_value(x) - x );
319 }
320 
321 
get_values(std::vector<std::pair<float,float>> & vec)322 void PF::SplineCurve::get_values( std::vector< std::pair<float,float> >& vec )
323 {
324   for (unsigned int i=0; i<vec.size(); i++)
325     vec[i].second = get_value( vec[i].first );
326 }
327 
328 
get_deltas(std::vector<std::pair<float,float>> & vec)329 void PF::SplineCurve::get_deltas( std::vector< std::pair<float,float> >& vec )
330 {
331   for (unsigned int i=0; i<vec.size(); i++) {
332     float val = get_value( vec[i].first );
333     vec[i].second = val - vec[i].first;
334   }
335 }
336 
337