diff options
-rw-r--r-- | src/convert.c | 154 | ||||
-rw-r--r-- | src/libcolour.c | 2 | ||||
-rw-r--r-- | src/test.c | 124 |
3 files changed, 225 insertions, 55 deletions
diff --git a/src/convert.c b/src/convert.c index 9a2c3d9..a998d66 100644 --- a/src/convert.c +++ b/src/convert.c @@ -53,6 +53,106 @@ static void ciexyz_to_rgb(const libcolour_ciexyz_t* restrict from, libcolour_rgb to->B = to->Minv[2][0] * X + to->Minv[2][1] * Y + to->Minv[2][2] * Z; } +#define REGULAR(s, t) ((t) <= (s)->transition ? (s)->slope * (t) : (1 + (s)->offset) * pow((t), 1 / (s)->gamma) - (s)->offset) +#define INVREGULAR(s, t) ((t) <= (s)->transitioninv ? (t) / (s)->slope : pow(((t) + (s)->offset) / (1 + (s)->offset), (s)->gamma)) + +static void rgb_encode(libcolour_rgb_t* restrict colour, const libcolour_rgb_t* restrict space) +{ + double r_sign = 1, g_sign = 1, b_sign = 1; + switch (space->encoding_type) { + case LIBCOLOUR_ENCODING_TYPE_LINEAR: + break; + case LIBCOLOUR_ENCODING_TYPE_SIMPLE: + case LIBCOLOUR_ENCODING_TYPE_REGULAR: + if (colour->R < 0) r_sign = -1, colour->R = -colour->R; + if (colour->G < 0) g_sign = -1, colour->G = -colour->G; + if (colour->B < 0) b_sign = -1, colour->B = -colour->B; + if (space->encoding_type == LIBCOLOUR_ENCODING_TYPE_SIMPLE) { + colour->R = pow(colour->R, 1 / space->gamma); + colour->G = pow(colour->G, 1 / space->gamma); + colour->B = pow(colour->B, 1 / space->gamma); + } else { + colour->R = REGULAR(space, colour->R); + colour->G = REGULAR(space, colour->G); + colour->B = REGULAR(space, colour->B); + } + colour->R *= r_sign; + colour->G *= g_sign; + colour->B *= b_sign; + break; + case LIBCOLOUR_ENCODING_TYPE_CUSTOM: + colour->R = (space->to_encoded_red)(colour->R); + colour->G = (space->to_encoded_green)(colour->G); + colour->B = (space->to_encoded_blue)(colour->B); + break; + default: + fprintf(stderr, "libcolour: invalid encoding type\n"); + abort(); + } +} + +static void rgb_decode(libcolour_rgb_t* restrict colour, const libcolour_rgb_t* restrict space) +{ + double r_sign = 1, g_sign = 1, b_sign = 1; + switch (space->encoding_type) { + case LIBCOLOUR_ENCODING_TYPE_LINEAR: + break; + case LIBCOLOUR_ENCODING_TYPE_SIMPLE: + case LIBCOLOUR_ENCODING_TYPE_REGULAR: + if (colour->R < 0) r_sign = -1, colour->R = -colour->R; + if (colour->G < 0) g_sign = -1, colour->G = -colour->G; + if (colour->B < 0) b_sign = -1, colour->B = -colour->B; + if (space->encoding_type == LIBCOLOUR_ENCODING_TYPE_SIMPLE) { + colour->R = pow(colour->R, space->gamma); + colour->G = pow(colour->G, space->gamma); + colour->B = pow(colour->B, space->gamma); + } else { + colour->R = INVREGULAR(space, colour->R); + colour->G = INVREGULAR(space, colour->G); + colour->B = INVREGULAR(space, colour->B); + } + colour->R *= r_sign; + colour->G *= g_sign; + colour->B *= b_sign; + break; + case LIBCOLOUR_ENCODING_TYPE_CUSTOM: + colour->R = (space->to_decoded_red)(colour->R); + colour->G = (space->to_decoded_green)(colour->G); + colour->B = (space->to_decoded_blue)(colour->B); + break; + default: + fprintf(stderr, "libcolour: invalid encoding type\n"); + abort(); + } +} + +#undef REGULAR +#undef INVREGULAR + +static int rgb_same_gamma(const libcolour_rgb_t* restrict a, const libcolour_rgb_t* restrict b) +{ + if (a->encoding_type != b->encoding_type) + return 0; + switch (a->encoding_type) { + case LIBCOLOUR_ENCODING_TYPE_SIMPLE: + return a->gamma == b->gamma; + case LIBCOLOUR_ENCODING_TYPE_REGULAR: + return a->gamma == b->gamma && + a->offset == b->offset && + a->slope == b->slope && + a->transition == b->transition; + case LIBCOLOUR_ENCODING_TYPE_CUSTOM: + return a->to_encoded_red == b->to_encoded_red && + a->to_encoded_green == b->to_encoded_green && + a->to_encoded_blue == b->to_encoded_blue && + a->to_decoded_red == b->to_decoded_red && + a->to_decoded_green == b->to_decoded_green && + a->to_decoded_blue == b->to_decoded_blue; + default: + return 1; + } +} + static void to_rgb(const libcolour_colour_t* restrict from, libcolour_rgb_t* restrict to) { int have_gamma = 0, with_gamma = to->with_gamma; @@ -64,7 +164,11 @@ static void to_rgb(const libcolour_colour_t* restrict from, libcolour_rgb_t* res case LIBCOLOUR_RGB: if (!memcmp(from->rgb.M, to->M, sizeof(double[3][3]))) { have_gamma = from->rgb.with_gamma; - *to = from->rgb; + to->R = from->rgb.R; + to->G = from->rgb.G; + to->B = from->rgb.B; + if (have_gamma && with_gamma && !rgb_same_gamma(&from->rgb, to)) + rgb_decode(to, &from->rgb), have_gamma = 0; break; } /* fall through */ @@ -77,51 +181,9 @@ static void to_rgb(const libcolour_colour_t* restrict from, libcolour_rgb_t* res if (have_gamma != with_gamma) { if (with_gamma) { - switch (to->encoding_type) { - case LIBCOLOUR_ENCODING_TYPE_LINEAR: - break; - case LIBCOLOUR_ENCODING_TYPE_SIMPLE: - to->R = pow(to->R, 1 / to->gamma); - to->G = pow(to->G, 1 / to->gamma); - to->B = pow(to->B, 1 / to->gamma); - break; - case LIBCOLOUR_ENCODING_TYPE_REGULAR: - to->R = to->R <= to->transition ? to->slope * to->R : (1 + to->offset) * pow(to->R, 1 / to->gamma) - to->offset; - to->G = to->G <= to->transition ? to->slope * to->G : (1 + to->offset) * pow(to->G, 1 / to->gamma) - to->offset; - to->B = to->B <= to->transition ? to->slope * to->B : (1 + to->offset) * pow(to->B, 1 / to->gamma) - to->offset; - break; - case LIBCOLOUR_ENCODING_TYPE_CUSTOM: - to->R = (to->to_encoded_red)(to->R); - to->G = (to->to_encoded_green)(to->G); - to->B = (to->to_encoded_blue)(to->B); - break; - default: - fprintf(stderr, "libcolour: invalid encoding type\n"); - abort(); - } + rgb_encode(to, to); } else { - switch (to->encoding_type) { - case LIBCOLOUR_ENCODING_TYPE_LINEAR: - break; - case LIBCOLOUR_ENCODING_TYPE_SIMPLE: - to->R = pow(to->R, to->gamma); - to->G = pow(to->G, to->gamma); - to->B = pow(to->B, to->gamma); - break; - case LIBCOLOUR_ENCODING_TYPE_REGULAR: - to->R = to->R <= to->transitioninv ? to->R * to->slope : pow((to->R + to->offset) / (1 + to->offset), to->gamma); - to->G = to->G <= to->transitioninv ? to->G * to->slope : pow((to->G + to->offset) / (1 + to->offset), to->gamma); - to->B = to->B <= to->transitioninv ? to->B * to->slope : pow((to->B + to->offset) / (1 + to->offset), to->gamma); - break; - case LIBCOLOUR_ENCODING_TYPE_CUSTOM: - to->R = (to->to_decoded_red)(to->R); - to->G = (to->to_decoded_green)(to->G); - to->B = (to->to_decoded_blue)(to->B); - break; - default: - fprintf(stderr, "libcolour: invalid encoding type\n"); - abort(); - } + rgb_decode(to, &from->rgb); } } } @@ -155,7 +217,7 @@ static inline double srgb_decode(double t) t = -t; sign = -1; } - t = t <= 0.040448236277380506 ? t / 12.92 : pow((t + 0.055) / 1.055, 2.4); + t = t <= 0.0031306684425217108 * 12.92 ? t / 12.92 : pow((t + 0.055) / 1.055, 2.4); return t * sign; } diff --git a/src/libcolour.c b/src/libcolour.c index 69014d3..867e280 100644 --- a/src/libcolour.c +++ b/src/libcolour.c @@ -44,7 +44,7 @@ double libcolour_srgb_decode(double t) t = -t; sign = -1; } - t = t <= 0.040448236277380506 ? t / 12.92 : pow((t + 0.055) / 1.055, 2.4); + t = t <= 0.0031306684425217108 * 12.92 ? t / 12.92 : pow((t + 0.055) / 1.055, 2.4); return t * sign; } @@ -18,6 +18,7 @@ #include <math.h> #include <stdio.h> +#include <string.h> #ifndef SKIP_CONVERT @@ -319,8 +320,10 @@ static int test_rgb_(enum libcolour_rgb_colour_space colour_space, const char* n int main(int argc, char* argv[]) { int r, rc = 0; - libcolour_colour_t c1, c2; + libcolour_colour_t c1, c2, c3, c4; double t1, t2; + size_t n; + char buf[sizeof(int) + sizeof(libcolour_rgb_t)]; #ifndef SKIP_CONVERT r = test_convert_nm_all(); @@ -436,9 +439,6 @@ int main(int argc, char* argv[]) 0.294559, 0.228864, 0.742689, 0.294559, 0.228864, 0.742689); /* - TODO test LIBCOLOUR_RGB_COLOUR_SPACE_CUSTOM_FROM_MEASUREMENTS - TODO test LIBCOLOUR_RGB_COLOUR_SPACE_CUSTOM_FROM_MATRIX - TODO test LIBCOLOUR_RGB_COLOUR_SPACE_CUSTOM_FROM_INV_MATRIX TODO test LIBCOLOUR_RGB_COLOUR_SPACE_DCI_P3_D65 TODO test LIBCOLOUR_RGB_COLOUR_SPACE_DCI_P3_THEATER TODO test LIBCOLOUR_RGB_COLOUR_SPACE_ITU_R_BT_601_625_LINE @@ -453,17 +453,125 @@ int main(int argc, char* argv[]) TODO test LIBCOLOUR_RGB_COLOUR_SPACE_ITU_R_BT_2100_OETF_HLG TODO test LIBCOLOUR_RGB_COLOUR_SPACE_SGI_RGB TODO test LIBCOLOUR_RGB_COLOUR_SPACE_SMPTE_240M_RGB + + TODO test LIBCOLOUR_RGB_COLOUR_SPACE_CUSTOM_FROM_MEASUREMENTS + TODO test LIBCOLOUR_RGB_COLOUR_SPACE_CUSTOM_FROM_MATRIX + TODO test LIBCOLOUR_RGB_COLOUR_SPACE_CUSTOM_FROM_INV_MATRIX */ /* TODO test transfer functions more rigorously */ + c1.model = c2.model = LIBCOLOUR_RGB; + c3.model = c4.model = LIBCOLOUR_SRGB; + c1.rgb.with_gamma = c3.srgb.with_gamma = 0; + c2.rgb.with_gamma = c4.srgb.with_gamma = 1; + if (libcolour_get_rgb_colour_space(&c1.rgb, LIBCOLOUR_RGB_COLOUR_SPACE_SRGB)) + goto fail; + if (libcolour_get_rgb_colour_space(&c2.rgb, LIBCOLOUR_RGB_COLOUR_SPACE_SRGB)) + goto fail; + for (t1 = 0; t1 <= 1; t1 += 0.001) { + c1.rgb.R = c3.srgb.R = t1 - 1; + c1.rgb.G = c3.srgb.G = t1; + c1.rgb.B = c3.srgb.B = t1 + 1; + if (libcolour_convert(&c1, &c2)) + goto fail; + if (libcolour_convert(&c3, &c4)) + goto fail; + if (!ftest(c2.rgb.R, c4.srgb.R, 0.00000000001) || + !ftest(c2.rgb.G, c4.srgb.G, 0.00000000001) || + !ftest(c2.rgb.B, c4.srgb.B, 0.00000000001) || + !ftest(libcolour_srgb_encode(c1.rgb.R), c2.srgb.R, 0.00000000001) || + !ftest(libcolour_srgb_encode(c1.rgb.G), c2.srgb.G, 0.00000000001) || + !ftest(libcolour_srgb_encode(c1.rgb.B), c2.srgb.B, 0.00000000001)) { + printf("libcolour_srgb_encode failed\n"), rc = 0; + goto colour_spaces_done; + } + if (libcolour_convert(&c2, &c1)) + goto fail; + if (libcolour_convert(&c4, &c3)) + goto fail; + if (!ftest(c1.rgb.R, c3.srgb.R, 0.00000000001) || + !ftest(c1.rgb.G, c3.srgb.G, 0.00000000001) || + !ftest(c1.rgb.B, c3.srgb.B, 0.00000000001) || + !ftest(libcolour_srgb_decode(c2.rgb.R), c1.srgb.R, 0.00000000001) || + !ftest(libcolour_srgb_decode(c2.rgb.G), c1.srgb.G, 0.00000000001) || + !ftest(libcolour_srgb_decode(c2.rgb.B), c1.srgb.B, 0.00000000001)) { + printf("%.30lf -> %.30lf\n%.30lf -> %.30lf\n%.30lf -> %.30lf\n", + c2.rgb.R, c1.rgb.R, c4.srgb.R, c3.srgb.R, c2.rgb.R, libcolour_srgb_decode(c2.rgb.R)); + printf("libcolour_srgb_decode failed\n"), rc = 0; + goto colour_spaces_done; + } + } + + c1.rgb.encoding_type = LIBCOLOUR_ENCODING_TYPE_SIMPLE; + c1.rgb.gamma = 2.2; + c1.rgb.with_gamma = 0; + c1.rgb.R = 0.25; + c1.rgb.G = 0.5; + c1.rgb.B = 0.75; + c4 = c3 = c2 = c1; + c2.rgb.with_gamma = c3.srgb.with_gamma = 1; + c3.rgb.gamma = c4.rgb.gamma = 1.8; + if (libcolour_convert(&c1, &c2)) + goto fail; + if (libcolour_convert(&c2, &c3)) + goto fail; + if (libcolour_convert(&c3, &c4)) + goto fail; + if (!ftest(c1.rgb.R, c4.srgb.R, 0.0000001) || + !ftest(c1.rgb.G, c4.srgb.G, 0.0000001) || + !ftest(c1.rgb.B, c4.srgb.B, 0.0000001)) { + printf("libcolour_convert failed to convert between two transfer functions\n"), rc = 0; + goto colour_spaces_done; + } + + if (libcolour_convert(&c1, &c4)) + goto fail; + if (!ftest(c1.rgb.R, c4.srgb.R, 0.0000001) || + !ftest(c1.rgb.G, c4.srgb.G, 0.0000001) || + !ftest(c1.rgb.B, c4.srgb.B, 0.0000001)) { + printf("libcolour_convert failed to convert when two different transfer functions are not applied\n"), rc = 0; + goto colour_spaces_done; + } + /* TODO test libcolour_convert with single conversions */ colour_spaces_done: - /* TODO test libcolour_srgb_encode */ - /* TODO test libcolour_srgb_decode */ - /* TODO test libcolour_marshal */ - /* TODO test libcolour_unmarshal */ + memset(&c1, 0, sizeof(c1)); + memset(&c2, 0, sizeof(c2)); + c1.model = LIBCOLOUR_RGB; + if (libcolour_get_rgb_colour_space(&c1.rgb, LIBCOLOUR_RGB_COLOUR_SPACE_ECI_RGB_V2)) + goto fail; + c3 = c1; + c1.rgb.to_encoded_red = NULL; + c1.rgb.to_encoded_green = NULL; + c1.rgb.to_encoded_blue = NULL; + c1.rgb.to_decoded_red = NULL; + c1.rgb.to_decoded_green = NULL; + c1.rgb.to_decoded_blue = NULL; + if (libcolour_marshal(&c1, NULL) > sizeof(buf)) { + printf("libcolour_marshal failed\n"), rc = 0; + goto marshal_done; + } + n = libcolour_marshal(&c1, buf); + if (n > sizeof(buf)) { + printf("libcolour_marshal failed\n"), rc = 0; + goto marshal_done; + } + if (libcolour_unmarshal(NULL, buf) != n) { + printf("libcolour_unmarshal failed\n"), rc = 0; + goto marshal_done; + } + if (libcolour_unmarshal(&c2, buf) != n) { + printf("libcolour_unmarshal failed\n"), rc = 0; + goto marshal_done; + } + if (memcmp(&c2, &c3, sizeof(c2))) { + printf("libcolour_(un)marshal failed\n"), rc = 0; + goto marshal_done; + } + marshal_done: + return rc; fail: perror(*argv); |