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