/* babl - dynamically extendable universal pixel conversion library. * Copyright (C) 2017, Øyvind Kolås and others. * * babl-polynomial.c * Copyright (C) 2017 Ell * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, see * . */ #ifdef BABL_POLYNOMIAL_DEGREE BABL_POLYNOMIAL_DEGREE ( 0, __) BABL_POLYNOMIAL_DEGREE ( 1, 0) BABL_POLYNOMIAL_DEGREE ( 2, 1) BABL_POLYNOMIAL_DEGREE ( 3, 2) BABL_POLYNOMIAL_DEGREE ( 4, 3) BABL_POLYNOMIAL_DEGREE ( 5, 4) BABL_POLYNOMIAL_DEGREE ( 6, 5) BABL_POLYNOMIAL_DEGREE ( 7, 6) BABL_POLYNOMIAL_DEGREE ( 8, 7) BABL_POLYNOMIAL_DEGREE ( 9, 8) BABL_POLYNOMIAL_DEGREE (10, 9) BABL_POLYNOMIAL_DEGREE (11, 10) BABL_POLYNOMIAL_DEGREE (12, 11) BABL_POLYNOMIAL_DEGREE (13, 12) BABL_POLYNOMIAL_DEGREE (14, 13) BABL_POLYNOMIAL_DEGREE (15, 14) BABL_POLYNOMIAL_DEGREE (16, 15) BABL_POLYNOMIAL_DEGREE (17, 16) BABL_POLYNOMIAL_DEGREE (18, 17) BABL_POLYNOMIAL_DEGREE (19, 18) BABL_POLYNOMIAL_DEGREE (20, 19) BABL_POLYNOMIAL_DEGREE (21, 20) BABL_POLYNOMIAL_DEGREE (22, 21) #undef BABL_POLYNOMIAL_DEGREE #else #include "config.h" #include #include #include "babl-internal.h" #define BABL_BIG_POLYNOMIAL_MAX_DEGREE (2 * BABL_POLYNOMIAL_MAX_DEGREE + BABL_POLYNOMIAL_MAX_SCALE) #define EPSILON 1e-10 typedef struct { BablPolynomialEvalFunc eval; int degree; int scale; double coeff[BABL_BIG_POLYNOMIAL_MAX_DEGREE + 1]; } BablBigPolynomial; #define BABL_POLYNOMIAL_EVAL___(poly, x) 0.0 #define BABL_POLYNOMIAL_EVAL_0(poly, x) ( (poly)->coeff[0]) #define BABL_POLYNOMIAL_EVAL_1(poly, x) ( (poly)->coeff[1]) #define BABL_POLYNOMIAL_EVAL_2(poly, x) (BABL_POLYNOMIAL_EVAL_0 (poly, x) * (x) + (poly)->coeff[2]) #define BABL_POLYNOMIAL_EVAL_3(poly, x) (BABL_POLYNOMIAL_EVAL_1 (poly, x) * (x) + (poly)->coeff[3]) #define BABL_POLYNOMIAL_EVAL_4(poly, x) (BABL_POLYNOMIAL_EVAL_2 (poly, x) * (x) + (poly)->coeff[4]) #define BABL_POLYNOMIAL_EVAL_5(poly, x) (BABL_POLYNOMIAL_EVAL_3 (poly, x) * (x) + (poly)->coeff[5]) #define BABL_POLYNOMIAL_EVAL_6(poly, x) (BABL_POLYNOMIAL_EVAL_4 (poly, x) * (x) + (poly)->coeff[6]) #define BABL_POLYNOMIAL_EVAL_7(poly, x) (BABL_POLYNOMIAL_EVAL_5 (poly, x) * (x) + (poly)->coeff[7]) #define BABL_POLYNOMIAL_EVAL_8(poly, x) (BABL_POLYNOMIAL_EVAL_6 (poly, x) * (x) + (poly)->coeff[8]) #define BABL_POLYNOMIAL_EVAL_9(poly, x) (BABL_POLYNOMIAL_EVAL_7 (poly, x) * (x) + (poly)->coeff[9]) #define BABL_POLYNOMIAL_EVAL_10(poly, x) (BABL_POLYNOMIAL_EVAL_8 (poly, x) * (x) + (poly)->coeff[10]) #define BABL_POLYNOMIAL_EVAL_11(poly, x) (BABL_POLYNOMIAL_EVAL_9 (poly, x) * (x) + (poly)->coeff[11]) #define BABL_POLYNOMIAL_EVAL_12(poly, x) (BABL_POLYNOMIAL_EVAL_10 (poly, x) * (x) + (poly)->coeff[12]) #define BABL_POLYNOMIAL_EVAL_13(poly, x) (BABL_POLYNOMIAL_EVAL_11 (poly, x) * (x) + (poly)->coeff[13]) #define BABL_POLYNOMIAL_EVAL_14(poly, x) (BABL_POLYNOMIAL_EVAL_12 (poly, x) * (x) + (poly)->coeff[14]) #define BABL_POLYNOMIAL_EVAL_15(poly, x) (BABL_POLYNOMIAL_EVAL_13 (poly, x) * (x) + (poly)->coeff[15]) #define BABL_POLYNOMIAL_EVAL_16(poly, x) (BABL_POLYNOMIAL_EVAL_14 (poly, x) * (x) + (poly)->coeff[16]) #define BABL_POLYNOMIAL_EVAL_17(poly, x) (BABL_POLYNOMIAL_EVAL_15 (poly, x) * (x) + (poly)->coeff[17]) #define BABL_POLYNOMIAL_EVAL_18(poly, x) (BABL_POLYNOMIAL_EVAL_16 (poly, x) * (x) + (poly)->coeff[18]) #define BABL_POLYNOMIAL_EVAL_19(poly, x) (BABL_POLYNOMIAL_EVAL_17 (poly, x) * (x) + (poly)->coeff[19]) #define BABL_POLYNOMIAL_EVAL_20(poly, x) (BABL_POLYNOMIAL_EVAL_18 (poly, x) * (x) + (poly)->coeff[20]) #define BABL_POLYNOMIAL_EVAL_21(poly, x) (BABL_POLYNOMIAL_EVAL_19 (poly, x) * (x) + (poly)->coeff[21]) #define BABL_POLYNOMIAL_EVAL_22(poly, x) (BABL_POLYNOMIAL_EVAL_20 (poly, x) * (x) + (poly)->coeff[22]) #define BABL_POLYNOMIAL_DEGREE(i, i_1) \ static double \ babl_polynomial_eval_1_##i (const BablPolynomial *poly, \ double x) \ { \ /* quiet clang warnings */ \ const BablBigPolynomial *const big_poly = (const BablBigPolynomial *) poly;\ \ const double x2 = x * x; \ (void) x2; \ \ return BABL_POLYNOMIAL_EVAL_##i (big_poly, x2) + \ BABL_POLYNOMIAL_EVAL_##i_1 (big_poly, x2) * x; \ } #include "babl-polynomial.c" #define BABL_POLYNOMIAL_DEGREE(i, i_1) \ static double \ babl_polynomial_eval_2_##i (const BablPolynomial *poly, \ double x) \ { \ /* quiet clang warnings */ \ const BablBigPolynomial *const big_poly = (const BablBigPolynomial *) poly;\ \ return BABL_POLYNOMIAL_EVAL_##i (big_poly, x) + \ BABL_POLYNOMIAL_EVAL_##i_1 (big_poly, x) * sqrt (x); \ } #include "babl-polynomial.c" static const BablPolynomialEvalFunc babl_polynomial_eval_funcs[BABL_POLYNOMIAL_MAX_SCALE] [BABL_BIG_POLYNOMIAL_MAX_DEGREE + 1] = { { #define BABL_POLYNOMIAL_DEGREE(i, i_1) babl_polynomial_eval_1_##i, #include "babl-polynomial.c" }, { #define BABL_POLYNOMIAL_DEGREE(i, i_1) babl_polynomial_eval_2_##i, #include "babl-polynomial.c" } }; static void babl_polynomial_set_degree (BablPolynomial *poly, int degree, int scale) { babl_assert (degree >= BABL_POLYNOMIAL_MIN_DEGREE && degree <= BABL_BIG_POLYNOMIAL_MAX_DEGREE); babl_assert (scale >= BABL_POLYNOMIAL_MIN_SCALE && scale <= BABL_POLYNOMIAL_MAX_SCALE); poly->eval = babl_polynomial_eval_funcs[scale - 1][degree]; poly->degree = degree; poly->scale = scale; } static double babl_polynomial_get (const BablPolynomial *poly, int i) { return poly->coeff[poly->degree - i]; } static void babl_polynomial_set (BablPolynomial *poly, int i, double c) { poly->coeff[poly->degree - i] = c; } static void babl_polynomial_copy (BablPolynomial *poly, const BablPolynomial *rpoly) { poly->eval = rpoly->eval; poly->degree = rpoly->degree; poly->scale = rpoly->scale; memcpy (poly->coeff, rpoly->coeff, (rpoly->degree + 1) * sizeof (double)); } static void babl_polynomial_reset (BablPolynomial *poly, int scale) { babl_polynomial_set_degree (poly, 0, scale); babl_polynomial_set (poly, 0, 0.0); } static void babl_polynomial_shrink (BablPolynomial *poly) { int i; for (i = 0; i <= poly->degree; i++) { if (fabs (poly->coeff[i]) > EPSILON) break; } if (i == poly->degree + 1) { babl_polynomial_reset (poly, poly->scale); } else if (i > 0) { memmove (poly->coeff, &poly->coeff[i], (poly->degree - i + 1) * sizeof (double)); babl_polynomial_set_degree (poly, poly->degree - i, poly->scale); } } static void babl_polynomial_add (BablPolynomial *poly, const BablPolynomial *rpoly) { int i; babl_assert (poly->scale == rpoly->scale); if (poly->degree >= rpoly->degree) { for (i = 0; i <= rpoly->degree; i++) { babl_polynomial_set (poly, i, babl_polynomial_get (poly, i) + babl_polynomial_get (rpoly, i)); } } else { int orig_degree = poly->degree; babl_polynomial_set_degree (poly, rpoly->degree, poly->scale); for (i = 0; i <= orig_degree; i++) { babl_polynomial_set (poly, i, poly->coeff[orig_degree - i] + babl_polynomial_get (rpoly, i)); } for (; i <= rpoly->degree; i++) babl_polynomial_set (poly, i, babl_polynomial_get (rpoly, i)); } } static void babl_polynomial_sub (BablPolynomial *poly, const BablPolynomial *rpoly) { int i; babl_assert (poly->scale == rpoly->scale); if (poly->degree >= rpoly->degree) { for (i = 0; i <= rpoly->degree; i++) { babl_polynomial_set (poly, i, babl_polynomial_get (poly, i) - babl_polynomial_get (rpoly, i)); } } else { int orig_degree = poly->degree; babl_polynomial_set_degree (poly, rpoly->degree, poly->scale); for (i = 0; i <= orig_degree; i++) { babl_polynomial_set (poly, i, poly->coeff[orig_degree - i] - babl_polynomial_get (rpoly, i)); } for (; i <= rpoly->degree; i++) babl_polynomial_set (poly, i, -babl_polynomial_get (rpoly, i)); } } static void babl_polynomial_scalar_mul (BablPolynomial *poly, double a) { int i; for (i = 0; i <= poly->degree; i++) poly->coeff[i] *= a; } static void babl_polynomial_scalar_div (BablPolynomial *poly, double a) { int i; for (i = 0; i <= poly->degree; i++) poly->coeff[i] /= a; } static void babl_polynomial_mul_copy (BablPolynomial *poly, const BablPolynomial *poly1, const BablPolynomial *poly2) { int i; int j; babl_assert (poly1->scale == poly2->scale); babl_polynomial_set_degree (poly, poly1->degree + poly2->degree, poly1->scale); memset (poly->coeff, 0, (poly->degree + 1) * sizeof (double)); for (i = poly1->degree; i >= 0; i--) { for (j = poly2->degree; j >= 0; j--) { babl_polynomial_set (poly, i + j, babl_polynomial_get (poly, i + j) + babl_polynomial_get (poly1, i) * babl_polynomial_get (poly2, j)); } } } static void babl_polynomial_integrate (BablPolynomial *poly) { int i; babl_polynomial_set_degree (poly, poly->degree + poly->scale, poly->scale); for (i = 0; i <= poly->degree - poly->scale; i++) { poly->coeff[i] *= poly->scale; poly->coeff[i] /= poly->degree - i; } for (; i <= poly->degree; i++) poly->coeff[i] = 0.0; } static void babl_polynomial_gamma_integrate (BablPolynomial *poly, double gamma) { int i; babl_polynomial_set_degree (poly, poly->degree + poly->scale, poly->scale); gamma *= poly->scale; for (i = 0; i <= poly->degree - poly->scale; i++) { poly->coeff[i] *= poly->scale; poly->coeff[i] /= poly->degree - i + gamma; } for (; i <= poly->degree; i++) poly->coeff[i] = 0.0; } static double babl_polynomial_inner_product (const BablPolynomial *poly1, const BablPolynomial *poly2, double x0, double x1) { BablBigPolynomial temp; babl_polynomial_mul_copy ((BablPolynomial *) &temp, poly1, poly2); babl_polynomial_integrate ((BablPolynomial *) &temp); return babl_polynomial_eval ((BablPolynomial *) &temp, x1) - babl_polynomial_eval ((BablPolynomial *) &temp, x0); } static double babl_polynomial_gamma_inner_product (const BablPolynomial *poly, double gamma, double x0, double x1) { BablBigPolynomial temp; babl_polynomial_copy ((BablPolynomial *) &temp, poly); babl_polynomial_gamma_integrate ((BablPolynomial *) &temp, gamma); return babl_polynomial_eval ((BablPolynomial *) &temp, x1) * pow (x1, gamma) - babl_polynomial_eval ((BablPolynomial *) &temp, x0) * pow (x0, gamma); } static double babl_polynomial_norm (const BablPolynomial *poly, double x0, double x1) { return sqrt (babl_polynomial_inner_product (poly, poly, x0, x1)); } static void babl_polynomial_normalize (BablPolynomial *poly, double x0, double x1) { double norm; norm = babl_polynomial_norm (poly, x0, x1); if (norm > EPSILON) babl_polynomial_scalar_div (poly, norm); } static void babl_polynomial_project_copy (BablPolynomial *poly, const BablPolynomial *rpoly, const BablPolynomial *basis, int basis_n, double x0, double x1) { int i; babl_assert (basis_n > 0); babl_polynomial_reset (poly, basis[0].scale); for (i = 0; i < basis_n; i++) { BablPolynomial temp; babl_polynomial_copy (&temp, &basis[i]); babl_polynomial_scalar_mul (&temp, babl_polynomial_inner_product (&temp, rpoly, x0, x1)); babl_polynomial_add (poly, &temp); } } static void babl_polynomial_gamma_project_copy (BablPolynomial *poly, double gamma, const BablPolynomial *basis, int basis_n, double x0, double x1) { int i; babl_assert (basis_n > 0); babl_polynomial_reset (poly, basis[0].scale); for (i = 0; i < basis_n; i++) { BablPolynomial temp; babl_polynomial_copy (&temp, &basis[i]); babl_polynomial_scalar_mul (&temp, babl_polynomial_gamma_inner_product (&temp, gamma, x0, x1)); babl_polynomial_add (poly, &temp); } } static void babl_polynomial_gram_schmidt (BablPolynomial *basis, int basis_n, double x0, double x1) { int i; for (i = 0; i < basis_n; i++) { if (i > 0) { BablPolynomial temp; babl_polynomial_project_copy (&temp, &basis[i], basis, i, x0, x1); babl_polynomial_sub (&basis[i], &temp); } babl_polynomial_normalize (&basis[i], x0, x1); } } static void babl_polynomial_basis (BablPolynomial *basis, int basis_n, int scale) { int i; for (i = 0; i < basis_n; i++) { babl_polynomial_set_degree (&basis[i], i, scale); basis[i].coeff[0] = 1.0; memset (&basis[i].coeff[1], 0, i * sizeof (double)); } } static void babl_polynomial_orthonormal_basis (BablPolynomial *basis, int basis_n, double x0, double x1, int scale) { babl_polynomial_basis (basis, basis_n, scale); babl_polynomial_gram_schmidt (basis, basis_n, x0, x1); } void babl_polynomial_approximate_gamma (BablPolynomial *poly, double gamma, double x0, double x1, int degree, int scale) { BablPolynomial *basis; babl_assert (poly != NULL); babl_assert (gamma >= 0.0); babl_assert (x0 >= 0.0); babl_assert (x0 < x1); babl_assert (degree >= BABL_POLYNOMIAL_MIN_DEGREE && degree <= BABL_POLYNOMIAL_MAX_DEGREE); babl_assert (scale >= BABL_POLYNOMIAL_MIN_SCALE && scale <= BABL_POLYNOMIAL_MAX_SCALE); basis = alloca ((degree + 1) * sizeof (BablPolynomial)); babl_polynomial_orthonormal_basis (basis, degree + 1, x0, x1, scale); babl_polynomial_gamma_project_copy (poly, gamma, basis, degree + 1, x0, x1); babl_polynomial_shrink (poly); } #endif