diff options
Diffstat (limited to 'src/libclut.c')
-rw-r--r-- | src/libclut.c | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/src/libclut.c b/src/libclut.c index 8963a4d..93d0ac0 100644 --- a/src/libclut.c +++ b/src/libclut.c @@ -16,6 +16,8 @@ */ #include "libclut.h" +#include <errno.h> + /** @@ -199,3 +201,215 @@ void (libclut_model_cielab_to_ciexyz)(double L, double a, double b, double* X, d libclut_model_cielab_to_ciexyz(L, a, b, X, Y, Z); } + + +/** + * Divide all values in a row by a divisor. + * + * @param m The first part of the row. + * @param a The second part of the row. + * @param d The divisor. + */ +static void divrow(double m[3], double a[3], double d) +{ + m[0] /= d, m[1] /= d, m[2] /= d; + a[0] /= d, a[1] /= d, a[2] /= d; +} + +/** + * Subtract all values in a row by corresponding value + * in another row multiplied by a common value. + * + * @param a1 The first part of the minuend/difference row. + * @param a2 The second part of the minuend/difference row. + * @param b1 The first part of the subtrahend row. + * @param b2 The second part of the subtrahend row. + * @param m The multiplier. + */ +static void subrow(double a1[3], double a2[3], double b1[3], double b2[3], double m) +{ + a1[0] -= b1[0] * m, a1[1] -= b1[1] * m, a1[2] -= b1[2] * m; + a2[0] -= b2[0] * m, a2[1] -= b2[1] * m, a2[2] -= b2[2] * m; +} + + +/** + * Invert a matrix. + * + * @param M The matrix to invert, will be modified to an + * identity matrix, possibly with reordered rows. + * @param A The inversion of M (as input). + * @return 1 on success, 0 if the matrix is not invertible. + */ +static int invert(libclut_colourspace_conversion_matrix_t M, libclut_colourspace_conversion_matrix_t A) +{ + int r0 = 0, r1 = 1, r2 = 2, t, swapped = 0; + libclut_colourspace_conversion_matrix_t T; + + A[0][0] = A[1][1] = A[2][2] = 1; + A[0][1] = A[0][2] = A[1][0] = A[1][2] = A[2][0] = A[2][1] = 0; + + if (libclut_0__(M[r0][0])) + { + if (libclut_0__(M[r1][0])) + { + if (libclut_0__(M[r2][0])) + return 0; + t = r0, r0 = r2, r2 = t; + } + else + t = r0, r0 = r1, r1 = t; + swapped = 1; + } + + divrow(M[r0], A[r0], M[r0][0]); + subrow(M[r1], A[r1], M[r0], A[r0], M[r1][0]); + subrow(M[r2], A[r2], M[r0], A[r0], M[r2][0]); + + if (libclut_0__(M[r1][1])) + { + if (libclut_0__(M[r2][1])) + return 0; + t = r1, r1 = r2, r2 = t; + swapped = 1; + } + + divrow(M[r1], A[r1], M[r1][1]); + subrow(M[r2], A[r2], M[r1], A[r1], M[r2][1]); + + if (libclut_0__(M[r2][2])) + return 0; + + divrow(M[r2], A[r2], M[r2][2]); + + subrow(M[r1], A[r1], M[r2], A[r2], M[r1][2]); + subrow(M[r0], A[r0], M[r2], A[r2], M[r0][2]); + + subrow(M[r0], A[r0], M[r1], A[r1], M[r0][1]); + + if (swapped) + { + memcpy(T, A, sizeof(T)); + memcpy(A[0], T[r0], sizeof(*T)); + memcpy(A[1], T[r1], sizeof(*T)); + memcpy(A[2], T[r2], sizeof(*T)); + } + + return 1; +} + + +/** + * Create an RGB to CIE XYZ conversion matrix. + * + * @param cs The colour space. + * @param M The output matrix. + * @return Zero on success, -1 on error. + * + * @throws EINVAL The colourspace cannot be used. + */ +static int get_conversion_matrix(const libclut_rgb_colourspace_t* cs, libclut_colourspace_conversion_matrix_t M) +{ +#define XYY_TO_XYZ(x, y, Y, Xp, Yp, Zp) \ + (libclut_0__(Y)) ? \ + (*(Xp) = *(Zp) = 0, *(Yp) = (Y)) : \ + (*(Xp) = (x) * (Y) / (y), \ + *(Yp) = (Y), \ + *(Zp) = (1 - (x) - (y)) * (Y) / (y)) + + double Xr, Yr, Zr, Xg, Yg, Zg, Xb, Yb, Zb, Xw, Yw, Zw, Sr, Sg, Sb; + libclut_colourspace_conversion_matrix_t M2; + + XYY_TO_XYZ(cs->red_x, cs->red_y, 1, &Xr, &Yr, &Zr); + XYY_TO_XYZ(cs->green_x, cs->green_y, 1, &Xg, &Yg, &Zg); + XYY_TO_XYZ(cs->blue_x, cs->blue_y, 1, &Xb, &Yb, &Zb); + XYY_TO_XYZ(cs->white_x, cs->white_y, cs->white_Y, &Xw, &Yw, &Zw); + + M2[0][0] = Xr, M2[0][1] = Xg, M2[0][2] = Xb; + M2[1][0] = Yr, M2[1][1] = Yg, M2[1][2] = Yb; + M2[2][0] = Zr, M2[2][1] = Zg, M2[2][2] = Zb; + + if (!invert(M2, M)) + return errno = EINVAL, -1; + + Sr = M[0][0] * Xw + M[0][1] * Yw + M[0][2] * Zw; + Sg = M[1][0] * Xw + M[1][1] * Yw + M[1][2] * Zw; + Sb = M[2][0] * Xw + M[2][1] * Yw + M[2][2] * Zw; + + M[0][0] = Sr * Xr, M[0][1] = Sg * Xg, M[0][2] = Sb * Xb; + M[1][0] = Sr * Yr, M[1][1] = Sg * Yg, M[1][2] = Sb * Yb; + M[2][0] = Sr * Zr, M[2][1] = Sg * Zg, M[2][2] = Sb * Zb; + + return 0; + +#undef XYY_TO_XYZ +} + + +/** + * Create a matrix for converting values between + * two RGB colourspaces. + * + * @param from The input colourspace, the Y-component is only necessary for the whitepoint. + * @param to The output colourspace, the Y-component is only necessary for the whitepoint. + * @param M Output matrix for conversion from `from` to `to`. + * @param Minv Output matrix for conversion from `to` to `from`, may be `NULL`. + * @return Zero on success, -1 on error. + * + * @throws EINVAL The colourspace cannot be used. + */ +int libclut_model_get_rgb_conversion_matrix(const libclut_rgb_colourspace_t* from, + const libclut_rgb_colourspace_t* to, + libclut_colourspace_conversion_matrix_t M, + libclut_colourspace_conversion_matrix_t Minv) +{ + libclut_colourspace_conversion_matrix_t A, B; + + if (get_conversion_matrix(from, A)) + return -1; + if (get_conversion_matrix(to, M)) + return -1; + if (!invert(M, B)) + return errno = EINVAL, -1; + + M[0][0] = A[0][0] * B[0][0] + A[0][1] * B[1][0] + A[0][2] * B[2][0]; + M[0][1] = A[0][0] * B[0][1] + A[0][1] * B[1][1] + A[0][2] * B[2][1]; + M[0][2] = A[0][0] * B[0][2] + A[0][1] * B[1][2] + A[0][2] * B[2][2]; + + M[1][0] = A[1][0] * B[0][0] + A[1][1] * B[1][0] + A[1][2] * B[2][0]; + M[1][1] = A[1][0] * B[0][1] + A[1][1] * B[1][1] + A[1][2] * B[2][1]; + M[1][2] = A[1][0] * B[0][2] + A[1][1] * B[1][2] + A[1][2] * B[2][2]; + + M[2][0] = A[2][0] * B[0][0] + A[2][1] * B[1][0] + A[2][2] * B[2][0]; + M[2][1] = A[2][0] * B[0][1] + A[2][1] * B[1][1] + A[2][2] * B[2][1]; + M[2][2] = A[2][0] * B[0][2] + A[2][1] * B[1][2] + A[2][2] * B[2][2]; + + if (Minv != NULL) + { + memcpy(A, M, sizeof(A)); + if (!invert(A, Minv)) + return errno = EINVAL, -1; + } + + return 0; +} + + +/** + * Convert an RGB colour into another RGB colourspace. + * None of the parameter may have side-effects. + * + * @param r The red component of the colour to convert. + * @param g The green component of the colour to convert. + * @param b The blue component of the colour to convert. + * @param M Conversion matrix, create with `libclut_model_get_rgb_conversion_matrix`. + * @param out_r Output parameter for the new red component. + * @param out_g Output parameter for the new green component. + * @param out_b Output parameter for the new blue component. + */ +void (libclut_model_convert_rgb)(double r, double g, double b, libclut_colourspace_conversion_matrix_t M, + double *out_r, double *out_g, double *out_b) +{ + libclut_model_convert_rgb(r, g, b, M, out_r, out_g, out_b); +} + |