diff options
-rw-r--r-- | NEWS | 6 | ||||
-rw-r--r-- | doc/info/chap/colour-spaces.texinfo | 75 | ||||
-rw-r--r-- | src/libclut.c | 103 | ||||
-rw-r--r-- | src/libclut.h | 80 | ||||
-rw-r--r-- | src/test.c | 77 |
5 files changed, 308 insertions, 33 deletions
@@ -7,6 +7,12 @@ libclut NEWS -*- outline -*- New RGB colour spaces added: ITU-R BT.709 and Lightroom RGB. + libclut_model_get_rgb_conversion_matrix accepts NULL as + as the first and second argument. + + Add the functions libclut_model_rgb_to_xyz and + libclut_model_rgb_to_xyz. + * Noteworthy changes in release 1.0 (2016-(12)Dec-02) [stable] RGB colour space conversion added. diff --git a/doc/info/chap/colour-spaces.texinfo b/doc/info/chap/colour-spaces.texinfo index e1dfae1..b190ada 100644 --- a/doc/info/chap/colour-spaces.texinfo +++ b/doc/info/chap/colour-spaces.texinfo @@ -895,9 +895,11 @@ This function is not available as a macro, thus, linking with Parameters: @table @code @item const libclut_rgb_colour_space_t* from -Description of the input colour space. +Description of the input colour space. CIE XYZ is +used use if this parameter is @code{NULL}. @item const libclut_rgb_colour_space_t* to -Description of the output colour space. +Description of the output colour space. CIE XYZ is +used use if this parameter is @code{NULL}. @item libclut_colour_space_conversion_matrix_t M Matrix to fill with values so it can be used for converting from the input colour space to the @@ -923,7 +925,7 @@ function. @table @code @item libclut_model_convert_rgb(r, g, b, M, *out_r, *out_g, *out_b) Convert a single RGB colour into another RGB colour space. -The colour space must have same gamma functions as RGB. +The colour space must have same gamma functions as sRGB. This macro is also available a function. If the function is used, linking with @option{-lclut} is required, otherwise, @@ -949,6 +951,7 @@ Output parameter for the green value of the colour after conversion. Output parameter for the blue value of the colour after conversion. @end table + @item libclut_convert_rgb_inplace(clut, max, type, m, trunc) Convert the curves between two RGB colour spaces. @@ -986,6 +989,7 @@ out of gamut are truncated. (Not necessarily the best approximation.) @end table + @item libclut_convert_rgb(clut, max, type, m, trunc, out) Convert the curves between two RGB colour spaces. @@ -1031,3 +1035,68 @@ the same data type as @code{clut}. And have the same values on @code{clut}. @end table @end table + +There is also two functions for converting between a custom RGB +colour space and CIE XYZ, one function in each direction. Note +that the three functions above do not work if one of the colour +spaces is CIE XYZ. + +@table @code +@item libclut_model_rgb_to_ciexyz(r, g, b, M, *X, *Y, *Z) +Convert a single RGB colour if a custom RGB colour space +to CIE XYZ. + +The RGB colour space must have the same gamma function as sRGB. + +This macro is also available a function. If the function is +used, linking with @option{-lclut} is required, otherwise, +linking with @option{-lm} is required, or @option{-lclut} if +@code{libclut_model_standard_to_linear1} is undefined. + +Parameters: +@table @code +@item double r +The red component. +@item double g +The green component. +@item double b +The blue component. +@item libclut_colour_space_conversion_matrix_t M +The conversion matrix. +@item double* X +Output parameter for the X component. +@item double* Y +Output parameter for the Y component. +@item double* Z +Output parameter for the Z component. +@end table + + +@item libclut_model_rgb_to_ciexyz(X, Y, Z, M, *r, *g, *b) +Convert a single colour from CIE XYZ to a custom RGB colour space. + +The RGB colour space must have the same gamma function as sRGB. + +This macro is also available a function. If the function is +used, linking with @option{-lclut} is required, otherwise, +linking with @option{-lm} is required, or @option{-lclut} if +@code{libclut_model_linear_to_standard1} is undefined. + +Parameters: +@table @code +@item double X +The X component. +@item double Y +The Y component. +@item double Z +The Z component. +@item libclut_colour_space_conversion_matrix_t M +The conversion matrix. +@item double* r +Output parameter for the red component. +@item double* g +Output parameter for the green component. +@item double* b +Output parameter for the blue component. +@end table +@end table diff --git a/src/libclut.c b/src/libclut.c index 9395c71..52c9615 100644 --- a/src/libclut.c +++ b/src/libclut.c @@ -647,8 +647,10 @@ static int get_conversion_matrix(const libclut_rgb_colour_space_t* cs, libclut_c * Create a matrix for converting values between * two RGB colour spaces. * - * @param from The input colour space, the Y-component is only necessary for the whitepoint. - * @param to The output colour space, the Y-component is only necessary for the whitepoint. + * @param from The input colour space, the Y-component is only necessary + * for the white point, `NULL` for CIE XYZ. + * @param to The output colour space, the Y-component is only necessary + * for the white point, `NULL` for CIE XYZ. * @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. @@ -662,24 +664,44 @@ int libclut_model_get_rgb_conversion_matrix(const libclut_rgb_colour_space_t* fr { libclut_colour_space_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] = B[0][0] * A[0][0] + B[0][1] * A[1][0] + B[0][2] * A[2][0]; - M[0][1] = B[0][0] * A[0][1] + B[0][1] * A[1][1] + B[0][2] * A[2][1]; - M[0][2] = B[0][0] * A[0][2] + B[0][1] * A[1][2] + B[0][2] * A[2][2]; - - M[1][0] = B[1][0] * A[0][0] + B[1][1] * A[1][0] + B[1][2] * A[2][0]; - M[1][1] = B[1][0] * A[0][1] + B[1][1] * A[1][1] + B[1][2] * A[2][1]; - M[1][2] = B[1][0] * A[0][2] + B[1][1] * A[1][2] + B[1][2] * A[2][2]; + if (from != NULL) + { + if (get_conversion_matrix(from, A)) + return -1; + } + else + { + A[0][0] = A[1][1] = A[2][2] = 1; + A[0][1] = A[1][0] = A[2][0] = 0; + A[0][2] = A[1][2] = A[2][1] = 0; + } - M[2][0] = B[2][0] * A[0][0] + B[2][1] * A[1][0] + B[2][2] * A[2][0]; - M[2][1] = B[2][0] * A[0][1] + B[2][1] * A[1][1] + B[2][2] * A[2][1]; - M[2][2] = B[2][0] * A[0][2] + B[2][1] * A[1][2] + B[2][2] * A[2][2]; + if (to != NULL) + { + if (get_conversion_matrix(to, M)) + return -1; + if (!invert(M, B)) + return errno = EINVAL, -1; + + if (from != NULL) + { + M[0][0] = B[0][0] * A[0][0] + B[0][1] * A[1][0] + B[0][2] * A[2][0]; + M[0][1] = B[0][0] * A[0][1] + B[0][1] * A[1][1] + B[0][2] * A[2][1]; + M[0][2] = B[0][0] * A[0][2] + B[0][1] * A[1][2] + B[0][2] * A[2][2]; + + M[1][0] = B[1][0] * A[0][0] + B[1][1] * A[1][0] + B[1][2] * A[2][0]; + M[1][1] = B[1][0] * A[0][1] + B[1][1] * A[1][1] + B[1][2] * A[2][1]; + M[1][2] = B[1][0] * A[0][2] + B[1][1] * A[1][2] + B[1][2] * A[2][2]; + + M[2][0] = B[2][0] * A[0][0] + B[2][1] * A[1][0] + B[2][2] * A[2][0]; + M[2][1] = B[2][0] * A[0][1] + B[2][1] * A[1][1] + B[2][2] * A[2][1]; + M[2][2] = B[2][0] * A[0][2] + B[2][1] * A[1][2] + B[2][2] * A[2][2]; + } + else + memcpy(M, B, sizeof(B)); + } + else + memcpy(M, A, sizeof(A)); if (Minv != NULL) { @@ -694,9 +716,8 @@ int libclut_model_get_rgb_conversion_matrix(const libclut_rgb_colour_space_t* fr /** * Convert an RGB colour into another RGB colour space. - * None of the parameter may have side-effects. * - * Both RGB colour space must have same gamma functions as RGB. + * Both RGB colour spaces must have same gamma functions as sRGB. * * @param r The red component of the colour to convert. * @param g The green component of the colour to convert. @@ -712,3 +733,43 @@ void (libclut_model_convert_rgb)(double r, double g, double b, libclut_colour_sp libclut_model_convert_rgb(r, g, b, M, out_r, out_g, out_b); } + +/** + * Convert an RGB colour of a custom RGB colour space to CIE XYZ. + * + * The RGB colour space must have same gamma functions as sRGB. + * + * @param r The red component. + * @param g The green component. + * @param b The blue component. + * @param M Conversion matrix, create with `libclut_model_get_rgb_conversion_matrix`. + * @param x Output parameter for the X component. + * @param y Output parameter for the Y component. + * @param z Output parameter for the Z component. + */ +void (libclut_model_rgb_to_ciexyz)(double r, double g, double b, libclut_colour_space_conversion_matrix_t M, + double* x, double* y, double* z) +{ + libclut_model_rgb_to_ciexyz(r, g, b, M, x, y, z); +} + + +/** + * Convert a CIE XYZ colour to a custom RGB colour space. + * + * The RGB colour space must have same gamma functions as sRGB. + * + * @param x The X component. + * @param y The Y component. + * @param z The Z component. + * @param M Conversion matrix, create with `libclut_model_get_rgb_conversion_matrix`. + * @param r Output parameter for the red component. + * @param g Output parameter for the green component + * @param b Output parameter for the blue component. + */ +void (libclut_model_ciexyz_to_rgb)(double x, double y, double z, libclut_colour_space_conversion_matrix_t M, + double* r, double* g, double* b) +{ + libclut_model_ciexyz_to_rgb(x, y, z, M, r, g, b); +} + diff --git a/src/libclut.h b/src/libclut.h index e1601f1..7c0dcd8 100644 --- a/src/libclut.h +++ b/src/libclut.h @@ -630,7 +630,7 @@ static inline int libclut_0__(double x) { return libclut_eq__(x, 0); } /** * Convert the curves between two RGB colour spaces. * - * Both RGB colour space must have same gamma functions as sRGB. + * Both RGB colour spaces must have same gamma functions as sRGB. * * Requires that `clut->red_size`, `clut->green_size` * and `clut->blue_size` are equal. @@ -692,7 +692,7 @@ static inline int libclut_0__(double x) { return libclut_eq__(x, 0); } /** * Convert the curves between two RGB colour spaces. * - * Both RGB colour space must have same gamma functions as sRGB. + * Both RGB colour spaces must have same gamma functions as sRGB. * * None of the parameter may have side-effects. * @@ -1871,7 +1871,7 @@ void (libclut_model_linear_to_ciexyz)(double, double, double, double*, double*, /** - * Convert [0, 1] linear sRGB to CIE xyY. + * Convert [0, 1] sRGB to CIE xyY. * * The macro variant requires linking with '-lclut' * if any of `libclut_model_ciexyz_to_ciexyy`, @@ -2499,8 +2499,10 @@ void (libclut_model_cie_1960_ucs_to_cieuvw)(double, double, double, double, doub * Create a matrix for converting values between * two RGB colour spaces. * - * @param from The input colour space, the Y-component is only necessary for the whitepoint. - * @param to The output colour space, the Y-component is only necessary for the whitepoint. + * @param from The input colour space, the Y-component is only necessary + * for the white point, `NULL` for CIE XYZ. + * @param to The output colour space, the Y-component is only necessary + * for the white point, `NULL` for CIE XYZ. * @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. @@ -2517,7 +2519,7 @@ int libclut_model_get_rgb_conversion_matrix(const libclut_rgb_colour_space_t*, /** * Convert an RGB colour into another RGB colour space. * - * Both RGB colour space must have same gamma functions as RGB. + * Both RGB colour spaces must have same gamma functions as sRGB. * * Requires linking with '-lclut', or '-lm' if * `libclut_model_standard_to_linear1` or @@ -2527,7 +2529,7 @@ int libclut_model_get_rgb_conversion_matrix(const libclut_rgb_colour_space_t*, * @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`, - * must not have side-effects (unless libclut_model_convert_rgb1 is undefined). + * must not have side-effects. * @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. @@ -2548,6 +2550,70 @@ void (libclut_model_convert_rgb)(double, double, double, libclut_colour_space_co while (0) +/** + * Convert an RGB colour of a custom RGB colour space to CIE XYZ. + * + * The RGB colour space must have same gamma functions as sRGB. + * + * Requires linking with '-lclut', or '-lm' if + * `libclut_model_standard_to_linear1` is not undefined. + * + * @param r The red component. + * @param g The green component. + * @param b The blue component. + * @param M Conversion matrix, create with `libclut_model_get_rgb_conversion_matrix`, + * must not have side-effects. + * @param x Output parameter for the X component. + * @param y Output parameter for the Y component + * @param z Output parameter for the Z component. + */ +LIBCLUT_GCC_ONLY__(__attribute__((__leaf__))) +void (libclut_model_rgb_to_ciexyz)(double, double, double, libclut_colour_space_conversion_matrix_t, + double*, double*, double*); +#define libclut_model_rgb_to_ciexyz(r, g, b, M, x, y, z) \ + do \ + { \ + double r__ = libclut_model_standard_to_linear1(r); \ + double g__ = libclut_model_standard_to_linear1(g); \ + double b__ = libclut_model_standard_to_linear1(b); \ + *(x) = (M)[0][0] * r__ + (M)[0][1] * g__ + (M)[0][2] * b__; \ + *(y) = (M)[1][0] * r__ + (M)[1][1] * g__ + (M)[1][2] * b__; \ + *(z) = (M)[2][0] * r__ + (M)[2][1] * g__ + (M)[2][2] * b__; \ + } \ + while (0) + + +/** + * Convert a CIE XYZ colour to a custom RGB colour space. + * + * The RGB colour space must have same gamma functions as sRGB. + * + * Requires linking with '-lclut', or '-lm' if + * `libclut_model_linear_to_standard1` is not undefined. + * + * @param x The X component. + * @param y The Y component. + * @param z The Z component. + * @param M Conversion matrix, create with `libclut_model_get_rgb_conversion_matrix`, + * must not have side-effects. + * @param r Output parameter for the red component. + * @param g Output parameter for the green component + * @param b Output parameter for the blue component. + */ +LIBCLUT_GCC_ONLY__(__attribute__((__leaf__))) +void (libclut_model_ciexyz_to_rgb)(double, double, double, libclut_colour_space_conversion_matrix_t, + double*, double*, double*); +#define libclut_model_ciexyz_to_rgb(x, y, z, M, r, g, b) \ + do \ + { \ + double x__ = (x), y__ = (y), z__ = (z); \ + *(r) = libclut_model_linear_to_standard1((M)[0][0] * x__ + (M)[0][1] * y__ + (M)[0][2] * z__); \ + *(g) = libclut_model_linear_to_standard1((M)[1][0] * x__ + (M)[1][1] * y__ + (M)[1][2] * z__); \ + *(b) = libclut_model_linear_to_standard1((M)[2][0] * x__ + (M)[2][1] * y__ + (M)[2][2] * z__); \ + } \ + while (0) + + #if defined(__clang__) # pragma GCC diagnostic pop @@ -145,7 +145,7 @@ int main(int argc, char *argv[]) struct dclut d1, d2; size_t i, j; int rc = 0; - double param, r, g, b; + double param, r, g, b, x, y, z; t1. red_size = t2. red_size = t3. red_size = d1. red_size = d2. red_size = 256; t1.green_size = t2.green_size = t3.green_size = d1.green_size = d2.green_size = 256; @@ -360,11 +360,84 @@ int main(int argc, char *argv[]) libclut_convert_rgb_inplace(&t1, 1, double, M, trunc); /* TODO test */ libclut_convert_rgb(&t1, 1, double, M, trunc, &t2); /* TODO test */ + + if (libclut_model_get_rgb_conversion_matrix(&srgb, NULL, M, Minv)) + { + printf("libclut_model_get_rgb_conversion_matrix failed\n"), rc = 1; + goto rgb_conversion_done; + } + libclut_model_rgb_to_ciexyz(0.1, 0.5, 0.9, M, &x, &y, &z); + if (0.2227 > x || x > 0.2228 || + 0.2120 > y || y > 0.2121 || + 0.7739 > z || z > 0.7741) + { + printf("libclut_model_get_rgb_conversion_matrix or libclut_model_rgb_to_ciexyz failed\n"), rc = 1; + goto rgb_conversion_done; + } + libclut_model_ciexyz_to_rgb(x, y, z, Minv, &r, &g, &b); + if (0.0999 > r || r > 1.0001 || + 0.4999 > g || g > 0.5001 || + 0.8999 > b || b > 0.9001) + { + printf("libclut_model_get_rgb_conversion_matrix or libclut_model_ciexyz_to_rgb failed\n"), rc = 1; + goto rgb_conversion_done; + } - rgb_conversion_done: + + if (libclut_model_get_rgb_conversion_matrix(NULL, &srgb, Minv, M)) + { + printf("libclut_model_get_rgb_conversion_matrix failed\n"), rc = 1; + goto rgb_conversion_done; + } + libclut_model_rgb_to_ciexyz(0.1, 0.5, 0.9, M, &r, &g, &b); + libclut_model_ciexyz_to_rgb(r, g, b, Minv, &r, &g, &b); + if (0.0999 > r || r > 1.0001 || + 0.4999 > g || g > 0.5001 || + 0.8999 > b || b > 0.9001) + { + printf("libclut_model_get_rgb_conversion_matrix failed\n"), rc = 1; + goto rgb_conversion_done; + } + if (libclut_model_get_rgb_conversion_matrix(NULL, NULL, M, Minv)) + { + printf("libclut_model_get_rgb_conversion_matrix failed\n"), rc = 1; + goto rgb_conversion_done; + } + if (0.999999 > M[0][0] || M[0][0] > 1.000001 || + -.999999 > M[0][1] || M[0][1] > 0.000001 || + -.999999 > M[0][2] || M[0][2] > 0.000001 || + -.999999 > M[1][0] || M[1][0] > 0.000001 || + 0.999999 > M[1][1] || M[1][1] > 1.000001 || + -.999999 > M[1][2] || M[1][2] > 0.000001 || + -.999999 > M[2][0] || M[2][0] > 0.000001 || + -.999999 > M[2][1] || M[2][1] > 0.000001 || + 0.999999 > M[2][2] || M[2][2] > 1.000001) + { + printf("libclut_model_get_rgb_conversion_matrix failed\n"), rc = 1; + goto rgb_conversion_done; + } + if (0.999999 > Minv[0][0] || Minv[0][0] > 1.000001 || + -.999999 > Minv[0][1] || Minv[0][1] > 0.000001 || + -.999999 > Minv[0][2] || Minv[0][2] > 0.000001 || + -.999999 > Minv[1][0] || Minv[1][0] > 0.000001 || + 0.999999 > Minv[1][1] || Minv[1][1] > 1.000001 || + -.999999 > Minv[1][2] || Minv[1][2] > 0.000001 || + -.999999 > Minv[2][0] || Minv[2][0] > 0.000001 || + -.999999 > Minv[2][1] || Minv[2][1] > 0.000001 || + 0.999999 > Minv[2][2] || Minv[2][2] > 1.000001) + { + printf("libclut_model_get_rgb_conversion_matrix failed\n"), rc = 1; + goto rgb_conversion_done; + } + + + libclut_model_get_rgb_conversion_matrix(&srgb, &wgrgb, M, NULL); /* Just testing that we don't get a segfault. */ + rgb_conversion_done: + + libclut_model_ciexyz_to_cieluv(1, 1, 1, 1, 1, 1, &r, &g, &b); /* TODO test */ libclut_model_cieluv_to_ciexyz(1, 1, 1, 1, 1, 1, &r, &g, &b); /* TODO test */ libclut_model_cielch_to_cieluv(1, 1, &r, &g); /* TODO test */ |