1 /*
2    This file is part of darktable,
3    Copyright (C) 2011-2020 darktable developers.
4 
5    darktable is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation, either version 3 of the License, or
8    (at your option) any later version.
9 
10    darktable is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License
16    along with darktable.  If not, see <http://www.gnu.org/licenses/>.
17 
18    Part of this file is based on nikon_curve.c from UFraw
19    Copyright 2004-2008 by Shawn Freeman, Udi Fuchs
20 */
21 
22 #include "curve_tools.h"
23 #include <float.h>
24 #include <math.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 
28 #define EPSILON 2 * FLT_MIN
29 #define MAX_ITER 10
30 
31 static const int curvedata_anchors_max = 20;
32 
33 // declare some functions and so I can use the function pointer
34 float spline_cubic_val(int n, float t[], float tval, float y[], float ypp[]);
35 float catmull_rom_val(int n, float x[], float xval, float y[], float tangents[]);
36 
37 float *spline_cubic_set(int n, float t[], float y[]);
38 float *catmull_rom_set(int n, float x[], float y[]);
39 float *monotone_hermite_set(int n, float x[], float y[]);
40 
41 float (*spline_val[])(int, float[], float, float[], float[])
42     = { spline_cubic_val, catmull_rom_val, catmull_rom_val };
43 
44 float *(*spline_set[])(int, float[], float[]) = { spline_cubic_set, catmull_rom_set, monotone_hermite_set };
45 
46 /**********************************************************************
47 
48   Purpose:
49 
50     D3_NP_FS factors and solves a D3 system.
51 
52   Discussion:
53 
54     The D3 storage format is used for a tridiagonal matrix.
55     The superdiagonal is stored in entries (1,2:N), the diagonal in
56     entries (2,1:N), and the subdiagonal in (3,1:N-1).  Thus, the
57     original matrix is "collapsed" vertically into the array.
58 
59     This algorithm requires that each diagonal entry be nonzero.
60     It does not use pivoting, and so can fail on systems that
61     are actually nonsingular.
62 
63   Example:
64 
65     Here is how a D3 matrix of order 5 would be stored:
66 
67        *  A12 A23 A34 A45
68       A11 A22 A33 A44 A55
69       A21 A32 A43 A54  *
70 
71   Modified:
72 
73       07 January 2005    Shawn Freeman (pure C modifications)
74     15 November 2003    John Burkardt
75 
76   Author:
77 
78     John Burkardt
79 
80   Parameters:
81 
82     Input, int N, the order of the linear system.
83 
84     Input/output, float A[3*N].
85     On input, the nonzero diagonals of the linear system.
86     On output, the data in these vectors has been overwritten
87     by factorization information.
88 
89     Input, float B[N], the right hand side.
90 
91     Output, float D3_NP_FS[N], the solution of the linear system.
92     This is NULL if there was an error because one of the diagonal
93     entries was zero.
94 **********************************************************************/
d3_np_fs(int n,float a[],float b[])95 float *d3_np_fs(int n, float a[], float b[])
96 
97 {
98   if(n <= 0 || n > curvedata_anchors_max) return NULL;
99 
100   int i;
101   float *x;
102   float xmult;
103   //
104   //  Check.
105   //
106   for(i = 0; i < n; i++)
107   {
108     if(a[1 + i * 3] == 0.0E+00)
109     {
110       return NULL;
111     }
112   }
113   x = (float *)calloc(n, sizeof(float));
114   // nc_merror(x, "d3_np_fs");
115 
116   for(i = 0; i < n; i++)
117   {
118     x[i] = b[i];
119   }
120 
121   for(i = 1; i < n; i++)
122   {
123     xmult = a[2 + (i - 1) * 3] / a[1 + (i - 1) * 3];
124     a[1 + i * 3] = a[1 + i * 3] - xmult * a[0 + i * 3];
125     x[i] = x[i] - xmult * x[i - 1];
126   }
127 
128   x[n - 1] = x[n - 1] / a[1 + (n - 1) * 3];
129   for(i = n - 2; 0 <= i; i--)
130   {
131     x[i] = (x[i] - a[0 + (i + 1) * 3] * x[i + 1]) / a[1 + i * 3];
132   }
133 
134   return x;
135 }
136 
137 /**********************************************************************
138 
139   Purpose:
140 
141     SPLINE_CUBIC_SET computes the second derivatives of a piecewise cubic spline.
142 
143   Discussion:
144 
145     For data interpolation, the user must call SPLINE_SET to determine
146     the second derivative data, passing in the data to be interpolated,
147     and the desired boundary conditions.
148 
149     The data to be interpolated, plus the SPLINE_SET output, defines
150     the spline.  The user may then call SPLINE_VAL to evaluate the
151     spline at any point.
152 
153     The cubic spline is a piecewise cubic polynomial.  The intervals
154     are determined by the "knots" or abscissas of the data to be
155     interpolated.  The cubic spline has continuous first and second
156     derivatives over the entire interval of interpolation.
157 
158     For any point T in the interval T(IVAL), T(IVAL+1), the form of
159     the spline is
160 
161       SPL(T) = A(IVAL)
162              + B(IVAL) * ( T - T(IVAL) )
163              + C(IVAL) * ( T - T(IVAL) )**2
164              + D(IVAL) * ( T - T(IVAL) )**3
165 
166     If we assume that we know the values Y(*) and YPP(*), which represent
167     the values and second derivatives of the spline at each knot, then
168     the coefficients can be computed as:
169 
170       A(IVAL) = Y(IVAL)
171       B(IVAL) = ( Y(IVAL+1) - Y(IVAL) ) / ( T(IVAL+1) - T(IVAL) )
172         - ( YPP(IVAL+1) + 2 * YPP(IVAL) ) * ( T(IVAL+1) - T(IVAL) ) / 6
173       C(IVAL) = YPP(IVAL) / 2
174       D(IVAL) = ( YPP(IVAL+1) - YPP(IVAL) ) / ( 6 * ( T(IVAL+1) - T(IVAL) ) )
175 
176     Since the first derivative of the spline is
177 
178       SPL'(T) =     B(IVAL)
179               + 2 * C(IVAL) * ( T - T(IVAL) )
180               + 3 * D(IVAL) * ( T - T(IVAL) )**2,
181 
182     the requirement that the first derivative be continuous at interior
183     knot I results in a total of N-2 equations, of the form:
184 
185       B(IVAL-1) + 2 C(IVAL-1) * (T(IVAL)-T(IVAL-1))
186       + 3 * D(IVAL-1) * (T(IVAL) - T(IVAL-1))**2 = B(IVAL)
187 
188     or, setting H(IVAL) = T(IVAL+1) - T(IVAL)
189 
190       ( Y(IVAL) - Y(IVAL-1) ) / H(IVAL-1)
191       - ( YPP(IVAL) + 2 * YPP(IVAL-1) ) * H(IVAL-1) / 6
192       + YPP(IVAL-1) * H(IVAL-1)
193       + ( YPP(IVAL) - YPP(IVAL-1) ) * H(IVAL-1) / 2
194       =
195       ( Y(IVAL+1) - Y(IVAL) ) / H(IVAL)
196       - ( YPP(IVAL+1) + 2 * YPP(IVAL) ) * H(IVAL) / 6
197 
198     or
199 
200       YPP(IVAL-1) * H(IVAL-1) + 2 * YPP(IVAL) * ( H(IVAL-1) + H(IVAL) )
201       + YPP(IVAL) * H(IVAL)
202       =
203       6 * ( Y(IVAL+1) - Y(IVAL) ) / H(IVAL)
204       - 6 * ( Y(IVAL) - Y(IVAL-1) ) / H(IVAL-1)
205 
206     Boundary conditions must be applied at the first and last knots.
207     The resulting tridiagonal system can be solved for the YPP values.
208 
209   Modified:
210 
211       07 January 2005    Shawn Freeman (pure C modifications)
212     06 February 2004    John Burkardt
213 
214 
215   Author:
216 
217     John Burkardt
218 
219   Parameters:
220 
221     Input, int N, the number of data points.  N must be at least 2.
222     In the special case where N = 2 and IBCBEG = IBCEND = 0, the
223     spline will actually be linear.
224 
225     Input, float T[N], the knot values, that is, the points were data is
226     specified.  The knot values should be distinct, and increasing.
227 
228     Input, float Y[N], the data values to be interpolated.
229 
230     Input, int IBCBEG, left boundary condition flag:
231       0: the cubic spline should be a quadratic over the first interval;
232       1: the first derivative at the left endpoint should be YBCBEG;
233       2: the second derivative at the left endpoint should be YBCBEG.
234 
235     Input, float YBCBEG, the values to be used in the boundary
236     conditions if IBCBEG is equal to 1 or 2.
237 
238     Input, int IBCEND, right boundary condition flag:
239       0: the cubic spline should be a quadratic over the last interval;
240       1: the first derivative at the right endpoint should be YBCEND;
241       2: the second derivative at the right endpoint should be YBCEND.
242 
243     Input, float YBCEND, the values to be used in the boundary
244     conditions if IBCEND is equal to 1 or 2.
245 
246     Output, float SPLINE_CUBIC_SET[N], the second derivatives of the cubic spline.
247 **********************************************************************/
spline_cubic_set_internal(int n,float t[],float y[],int ibcbeg,float ybcbeg,int ibcend,float ybcend)248 static float *spline_cubic_set_internal(int n, float t[], float y[], int ibcbeg, float ybcbeg, int ibcend,
249                                         float ybcend)
250 {
251   float *a;
252   float *b;
253   int i;
254   float *ypp;
255   //
256   //  Check.
257   //
258   if(n <= 1)
259   {
260     // nc_message(NC_SET_ERROR, "spline_cubic_set() error: "
261     //    "The number of data points must be at least 2.\n");
262     return NULL;
263   }
264 
265   for(i = 0; i < n - 1; i++)
266   {
267     if(t[i + 1] <= t[i])
268     {
269       // nc_message(NC_SET_ERROR, "spline_cubic_set() error: "
270       //   "The knots must be strictly increasing, but "
271       //  "T(%u) = %e, T(%u) = %e\n",i,t[i],i+1,t[i+1]);
272       return NULL;
273     }
274   }
275   a = (float *)calloc(3 * n, sizeof(float));
276   // nc_merror(a, "spline_cubic_set");
277   b = (float *)calloc(n, sizeof(float));
278   // nc_merror(b, "spline_cubic_set");
279   //
280   //  Set up the first equation.
281   //
282   if(ibcbeg == 0)
283   {
284     b[0] = 0.0E+00;
285     a[1 + 0 * 3] = 1.0E+00;
286     a[0 + 1 * 3] = -1.0E+00;
287   }
288   else if(ibcbeg == 1)
289   {
290     b[0] = (y[1] - y[0]) / (t[1] - t[0]) - ybcbeg;
291     a[1 + 0 * 3] = (t[1] - t[0]) / 3.0E+00;
292     a[0 + 1 * 3] = (t[1] - t[0]) / 6.0E+00;
293   }
294   else if(ibcbeg == 2)
295   {
296     b[0] = ybcbeg;
297     a[1 + 0 * 3] = 1.0E+00;
298     a[0 + 1 * 3] = 0.0E+00;
299   }
300   else
301   {
302     // nc_message(NC_SET_ERROR, "spline_cubic_set() error: "
303     //   "IBCBEG must be 0, 1 or 2. The input value is %u.\n", ibcbeg);
304     free(a);
305     free(b);
306     return NULL;
307   }
308   //
309   //  Set up the intermediate equations.
310   //
311   for(i = 1; i < n - 1; i++)
312   {
313     b[i] = (y[i + 1] - y[i]) / (t[i + 1] - t[i]) - (y[i] - y[i - 1]) / (t[i] - t[i - 1]);
314     a[2 + (i - 1) * 3] = (t[i] - t[i - 1]) / 6.0E+00;
315     a[1 + i * 3] = (t[i + 1] - t[i - 1]) / 3.0E+00;
316     a[0 + (i + 1) * 3] = (t[i + 1] - t[i]) / 6.0E+00;
317   }
318   //
319   //  Set up the last equation.
320   //
321   if(ibcend == 0)
322   {
323     b[n - 1] = 0.0E+00;
324     a[2 + (n - 2) * 3] = -1.0E+00;
325     a[1 + (n - 1) * 3] = 1.0E+00;
326   }
327   else if(ibcend == 1)
328   {
329     b[n - 1] = ybcend - (y[n - 1] - y[n - 2]) / (t[n - 1] - t[n - 2]);
330     a[2 + (n - 2) * 3] = (t[n - 1] - t[n - 2]) / 6.0E+00;
331     a[1 + (n - 1) * 3] = (t[n - 1] - t[n - 2]) / 3.0E+00;
332   }
333   else if(ibcend == 2)
334   {
335     b[n - 1] = ybcend;
336     a[2 + (n - 2) * 3] = 0.0E+00;
337     a[1 + (n - 1) * 3] = 1.0E+00;
338   }
339   else
340   {
341     // nc_message(NC_SET_ERROR, "spline_cubic_set() error: "
342     //   "IBCEND must be 0, 1 or 2. The input value is %u", ibcend);
343     free(a);
344     free(b);
345     return NULL;
346   }
347   //
348   //  Solve the linear system.
349   //
350   if(n == 2 && ibcbeg == 0 && ibcend == 0)
351   {
352     ypp = (float *)calloc(2, sizeof(float));
353     // nc_merror(ypp, "spline_cubic_set");
354 
355     ypp[0] = 0.0E+00;
356     ypp[1] = 0.0E+00;
357   }
358   else
359   {
360     ypp = d3_np_fs(n, a, b);
361 
362     if(!ypp)
363     {
364       //  nc_message(NC_SET_ERROR, "spline_cubic_set() error: "
365       //     "The linear system could not be solved.\n");
366       free(a);
367       free(b);
368       return NULL;
369     }
370   }
371 
372   free(a);
373   free(b);
374   return ypp;
375 }
376 /************************************************************
377  *
378  * This is a convenience wrapper function around spline_cubic_set
379  *
380  ************************************************************/
spline_cubic_set(int n,float t[],float y[])381 float *spline_cubic_set(int n, float t[], float y[])
382 {
383   return spline_cubic_set_internal(n, t, y, 2, 0.0, 2, 0.0);
384 }
385 
386 /*************************************************************
387 * monotone_hermite_set:
388 *      calculates the tangents for a monotonic hermite spline curve.
389 *      see http://en.wikipedia.org/wiki/Monotone_cubic_interpolation
390 *
391 *  input:
392 *      n = number of control points
393 *      x = input x array
394 *      y = input y array
395 *  output:
396 *      pointer to array containing the tangents
397 *************************************************************/
monotone_hermite_set(int n,float x[],float y[])398 float *monotone_hermite_set(int n, float x[], float y[])
399 {
400   float *delta;
401   float *m;
402   int i;
403   if(n <= 1)
404   {
405     // nc_message(NC_SET_ERROR, "spline_cubic_set() error: "
406     //   "The number of data points must be at least 2.\n");
407     return NULL;
408   }
409 
410   for(i = 0; i < n - 1; i++)
411   {
412     if(x[i + 1] <= x[i])
413     {
414       // nc_message(NC_SET_ERROR, "spline_cubic_set() error: "
415       //   "The knots must be strictly increasing, but "
416       //  "T(%u) = %e, T(%u) = %e\n",i,x[i],i+1,x[i+1]);
417       return NULL;
418     }
419   }
420 
421   delta = (float *)calloc(n, sizeof(float));
422   // nc_merror(delta, "spline_cubic_set");
423   m = (float *)calloc(n + 1, sizeof(float));
424   // nc_merror(m, "spline_cubic_set");
425   // calculate the slopes
426   for(i = 0; i < n - 1; i++)
427   {
428     delta[i] = (y[i + 1] - y[i]) / (x[i + 1] - x[i]);
429   }
430   delta[n - 1] = delta[n - 2];
431 
432   m[0] = delta[0];
433   m[n - 1] = delta[n - 1];
434 
435   for(i = 1; i < n - 1; i++)
436   {
437     m[i] = (delta[i - 1] + delta[i]) * .5f;
438   }
439   for(i = 0; i < n; i++)
440   {
441     if(fabsf(delta[i]) < EPSILON)
442     {
443       m[i] = 0.0f;
444       m[i + 1] = 0.0f;
445     }
446     else
447     {
448       const float alpha = m[i] / delta[i];
449       const float beta = m[i + 1] / delta[i];
450       const float tau = alpha * alpha + beta * beta;
451       if(tau > 9.0f)
452       {
453         m[i] = 3.0f * alpha * delta[i] / sqrtf(tau);
454         m[i + 1] = 3.0f * beta * delta[i] / sqrtf(tau);
455       }
456     }
457   }
458   free(delta);
459   return m;
460 }
461 
462 /*************************************************************
463 * catmull_rom_set:
464 *      calculates the tangents for a catmull_rom spline
465 *      see http://en.wikipedia.org/wiki/Cubic_Hermite_spline
466 *
467 *
468 *  input:
469 *      n = number of control points
470 *      x = input x array
471 *      y = input y array
472 *  output:
473 *      pointer to array containing the tangents
474 *************************************************************/
catmull_rom_set(int n,float x[],float y[])475 float *catmull_rom_set(int n, float x[], float y[])
476 {
477   float *m;
478   int i;
479   if(n <= 1)
480   {
481     // nc_message(NC_SET_ERROR, "spline_cubic_set() error: "
482     //   "The number of data points must be at least 2.\n");
483     return NULL;
484   }
485 
486   for(i = 0; i < n - 1; i++)
487   {
488     if(x[i + 1] <= x[i])
489     {
490       // nc_message(NC_SET_ERROR, "spline_cubic_set() error: "
491       //   "The knots must be strictly increasing, but "
492       //  "T(%u) = %e, T(%u) = %e\n",i,x[i],i+1,x[i+1]);
493       return NULL;
494     }
495   }
496   // nc_merror(delta, "spline_cubic_set");
497   m = (float *)calloc(n, sizeof(float));
498   // nc_merror(m, "spline_cubic_set");
499 
500   // calculate the slopes
501   m[0] = (y[1] - y[0]) / (x[1] - x[0]);
502   for(i = 1; i < n - 1; i++)
503   {
504     m[i] = (y[i + 1] - y[i - 1]) / (x[i + 1] - x[i - 1]);
505   }
506   m[n - 1] = (y[n - 1] - y[n - 2]) / (x[n - 1] - x[n - 2]);
507 
508   return m;
509 }
510 
interpolate_set(int n,float x[],float y[],unsigned int type)511 float *interpolate_set(int n, float x[], float y[], unsigned int type)
512 {
513   return (*spline_set[type])(n, x, y);
514 }
515 
interpolate_val(int n,float x[],float xval,float y[],float tangents[],unsigned int type)516 float interpolate_val(int n, float x[], float xval, float y[], float tangents[], unsigned int type)
517 {
518   return (*spline_val[type])(n, x, xval, y, tangents);
519 }
520 
521 /*************************************************************
522  * catmull_rom_val:
523  *      piecewise catmull-rom interpolation
524  *
525  *      n = number of control points
526  *      x = input x array
527  *      xval = input value where to interpolate the data
528  *      y = input y array
529  *      tangent = input array of tangents
530  *  output:
531  *      interpolated value at xval
532  *
533  *************************************************************/
catmull_rom_val(int n,float x[],float xval,float y[],float tangents[])534 float catmull_rom_val(int n, float x[], float xval, float y[], float tangents[])
535 {
536   //
537   //  Determine the interval [ T(I), T(I+1) ] that contains TVAL.
538   //  Values below T[0] or above T[N-1] use extrapolation.
539   //
540   int ival = n - 2;
541 
542   for(int i = 0; i < n - 2; i++)
543   {
544     if(xval < x[i + 1])
545     {
546       ival = i;
547       break;
548     }
549   }
550 
551   const float m0 = tangents[ival];
552   const float m1 = tangents[ival + 1];
553   //
554   //  In the interval I, the polynomial is in terms of a normalized
555   //  coordinate between 0 and 1.
556   //
557   const float h = x[ival + 1] - x[ival];
558   const float dx = (xval - x[ival]) / h;
559   const float dx2 = dx * dx;
560   const float dx3 = dx * dx2;
561 
562   const float h00 = (2.0 * dx3) - (3.0 * dx2) + 1.0;
563   const float h10 = (1.0 * dx3) - (2.0 * dx2) + dx;
564   const float h01 = (-2.0 * dx3) + (3.0 * dx2);
565   const float h11 = (1.0 * dx3) - (1.0 * dx2);
566 
567   return (h00 * y[ival]) + (h10 * h * m0) + (h01 * y[ival + 1]) + (h11 * h * m1);
568 }
569 
570 
571 /**********************************************************************
572 
573   Purpose:
574 
575     SPLINE_CUBIC_VAL evaluates a piecewise cubic spline at a point.
576 
577   Discussion:
578 
579     SPLINE_CUBIC_SET must have already been called to define the values of YPP.
580 
581     For any point T in the interval T(IVAL), T(IVAL+1), the form of
582     the spline is
583 
584       SPL(T) = A
585              + B * ( T - T(IVAL) )
586              + C * ( T - T(IVAL) )**2
587              + D * ( T - T(IVAL) )**3
588 
589     Here:
590       A = Y(IVAL)
591       B = ( Y(IVAL+1) - Y(IVAL) ) / ( T(IVAL+1) - T(IVAL) )
592         - ( YPP(IVAL+1) + 2 * YPP(IVAL) ) * ( T(IVAL+1) - T(IVAL) ) / 6
593       C = YPP(IVAL) / 2
594       D = ( YPP(IVAL+1) - YPP(IVAL) ) / ( 6 * ( T(IVAL+1) - T(IVAL) ) )
595 
596   Modified:
597 
598       07 January 2005    Shawn Freeman (pure C modifications)
599     04 February 1999    John Burkardt
600 
601   Author:
602 
603     John Burkardt
604 
605   Parameters:
606 
607     Input, int n, the number of knots.
608 
609     Input, float Y[N], the data values at the knots.
610 
611     Input, float T[N], the knot values.
612 
613     Input, float TVAL, a point, typically between T[0] and T[N-1], at
614     which the spline is to be evalulated.  If TVAL lies outside
615     this range, extrapolation is used.
616 
617     Input, float Y[N], the data values at the knots.
618 
619     Input, float YPP[N], the second derivatives of the spline at
620     the knots.
621 
622 
623     Output, float SPLINE_VAL, the value of the spline at TVAL.
624 
625 **********************************************************************/
spline_cubic_val(int n,float t[],float tval,float y[],float ypp[])626 float spline_cubic_val(int n, float t[], float tval, float y[], float ypp[])
627 {
628   float dt;
629   float h;
630   int i;
631   int ival;
632   float yval;
633   //
634   //  Determine the interval [ T(I), T(I+1) ] that contains TVAL.
635   //  Values below T[0] or above T[N-1] use extrapolation.
636   //
637   ival = n - 2;
638 
639   for(i = 0; i < n - 1; i++)
640   {
641     if(tval < t[i + 1])
642     {
643       ival = i;
644       break;
645     }
646   }
647   //
648   //  In the interval I, the polynomial is in terms of a normalized
649   //  coordinate between 0 and 1.
650   //
651   dt = tval - t[ival];
652   h = t[ival + 1] - t[ival];
653 
654   yval = y[ival]
655          + dt * ((y[ival + 1] - y[ival]) / h - (ypp[ival + 1] / 6.0E+00 + ypp[ival] / 3.0E+00) * h
656                  + dt * (0.5E+00 * ypp[ival] + dt * ((ypp[ival + 1] - ypp[ival]) / (6.0E+00 * h))));
657 
658   // we really never need the derivatives so commented this out
659   /**ypval = ( y[ival+1] - y[ival] ) / h
660     - ( ypp[ival+1] / 6.0E+00 + ypp[ival] / 3.0E+00 ) * h
661     + dt * ( ypp[ival]
662     + dt * ( 0.5E+00 * ( ypp[ival+1] - ypp[ival] ) / h ) );
663 
664   *yppval = ypp[ival] + dt * ( ypp[ival+1] - ypp[ival] ) / h;*/
665 
666   return yval;
667 }
668 
669 
670 /*********************************************
671 CurveDataSample:
672     Samples from a spline curve constructed from
673     the Nikon data.
674 
675     curve   - Pointer to curve struct to hold the data.
676     sample  - Pointer to sample struct to hold the data.
677 **********************************************/
CurveDataSample(CurveData * curve,CurveSample * sample)678 int CurveDataSample(CurveData *curve, CurveSample *sample)
679 {
680   int i = 0, n;
681 
682   float x[20] = { 0 };
683   float y[20] = { 0 };
684   float *ypp;
685 
686   // The box points are what the anchor points are relative
687   // to so...
688 
689   float box_width = curve->m_max_x - curve->m_min_x;
690   float box_height = curve->m_max_y - curve->m_min_y;
691 
692   // build arrays for processing
693   if(curve->m_numAnchors == 0)
694   {
695     // just a straight line using box coordinates
696     x[0] = curve->m_min_x;
697     y[0] = curve->m_min_y;
698     x[1] = curve->m_max_x;
699     y[1] = curve->m_max_y;
700     n = 2;
701   }
702   else
703   {
704     for(i = 0; i < curve->m_numAnchors; i++)
705     {
706       x[i] = curve->m_anchors[i].x * box_width + curve->m_min_x;
707       y[i] = curve->m_anchors[i].y * box_height + curve->m_min_y;
708     }
709     n = curve->m_numAnchors;
710   }
711   int val;
712   float res = 1.0 / (float)(sample->m_samplingRes - 1);
713   int firstPointX = x[0] * (sample->m_samplingRes - 1);
714   int firstPointY = y[0] * (sample->m_outputRes - 1);
715   int lastPointX = x[n - 1] * (sample->m_samplingRes - 1);
716   int lastPointY = y[n - 1] * (sample->m_outputRes - 1);
717   int maxY = curve->m_max_y * (sample->m_outputRes - 1);
718   int minY = curve->m_min_y * (sample->m_outputRes - 1);
719   // returns an array of second derivatives used to calculate the spline curve.
720   // this is a malloc'd array that needs to be freed when done.
721   // The settings currently calculate the natural spline, which closely matches
722   // camera curve output in raw files.
723   ypp = interpolate_set(n, x, y, curve->m_spline_type);
724   if(ypp == NULL) return CT_ERROR;
725 
726   for(i = 0; i < (int)sample->m_samplingRes; i++)
727   {
728     // get the value of the curve at a point
729     // take into account that curves may not necessarily begin at x = 0.0
730     // nor end at x = 1.0
731 
732     // Before the first point and after the last point, take a strait line
733     if(i < firstPointX)
734     {
735       sample->m_Samples[i] = firstPointY;
736     }
737     else if(i > lastPointX)
738     {
739       sample->m_Samples[i] = lastPointY;
740     }
741     else
742     {
743       // within range, we can sample the curve
744       val = interpolate_val(n, x, i * res, y, ypp, curve->m_spline_type) * (sample->m_outputRes - 1) + 0.5;
745       if(val > maxY) val = maxY;
746       if(val < minY) val = minY;
747       sample->m_Samples[i] = val;
748     }
749   }
750 
751   free(ypp);
752   return CT_SUCCESS;
753 }
754 
755 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
756 // vim: shiftwidth=2 expandtab tabstop=2 cindent
757 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
758