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