diff --git a/src/bin/termiolink.c b/src/bin/termiolink.c index dba8d3c3..6f2b225f 100644 --- a/src/bin/termiolink.c +++ b/src/bin/termiolink.c @@ -904,11 +904,106 @@ _parse_one_css_alpha(struct ty_sb *sb, return EINA_TRUE; } +/* return hue between 0 and 1 */ +static Eina_Bool +_parse_one_hue(struct ty_sb *sb, + double *dp) +{ + char *endptr_double; + double d; + + if (!sb->len) + return EINA_FALSE; + + d = eina_convert_strtod_c(sb->buf, &endptr_double); + if (isnan(d) || endptr_double == sb->buf || d < 0) + return EINA_FALSE; + ty_sb_lskip(sb, endptr_double - sb->buf); + if (sbstartswith(sb, "turn")) + { + ty_sb_lskip(sb, strlen("turn")); + } + else if (sbstartswith(sb, "rad")) + { + ty_sb_lskip(sb, strlen("rad")); + d /= 2 * M_PI; + } + else if (sbstartswith(sb, "grad")) + { + ty_sb_lskip(sb, strlen("grad")); + d /= 400; + } + else if (sbstartswith(sb, "deg") || sb->buf[0] == ',' || sb->buf[0] == ' ') + { + if (sbstartswith(sb, "deg")) + ty_sb_lskip(sb, strlen("deg")); + d /= 360; + } + else + return EINA_FALSE; + d = d - (long) d; + if (d < 0) + d = 1 + d; + *dp = d; + return EINA_TRUE; +} + +static Eina_Bool +_parse_one_percent(struct ty_sb *sb, + double *dp) +{ + char *endptr_double; + double d; + + if (!sb->len) + return EINA_FALSE; + + d = eina_convert_strtod_c(sb->buf, &endptr_double); + if (isnan(d) || endptr_double == sb->buf || d < 0) + return EINA_FALSE; + ty_sb_lskip(sb, endptr_double - sb->buf); + if (!sb->len || sb->buf[0] != '%') return EINA_FALSE; + ty_sb_lskip(sb, 1); + if (d > 100.0) + return EINA_FALSE; + d /= 100.0; + *dp = d; + return EINA_TRUE; +} + + +/* From http://en.wikipedia.org/wiki/HSL_color_space */ +static Eina_Bool +_hsl_to_rgb(double hue, double saturation, double lightness, + uint8_t *rp, uint8_t *gp, uint8_t *bp) +{ + double a = saturation * MIN(lightness, 1.0 - lightness); + int n[3] = {0, 8, 4}; + double res[3] = {}; + int i; + + for (i = 0; i < 3; i++) + { + double k = fmod(n[i] + hue / 12., 12.); + double f = lightness - a * MAX(-1, MIN(MIN(k - 3, 9 - k), 1)); + if (f > 1 || f < 0) + return EINA_FALSE; + res[i] = f * 255.0; + } + + *rp = res[0]; + *gp = res[1]; + *bp = res[2]; + return EINA_TRUE; +} + static Eina_Bool _parse_css_hsl_color(struct ty_sb *sb, uint8_t *rp, uint8_t *gp, uint8_t *bp, uint8_t *ap) { uint8_t r = 0, g = 0, b = 0, a = 255; + double hue, saturation, lightness; + Eina_Bool is_functional; ty_sb_spaces_ltrim(sb); @@ -925,7 +1020,54 @@ _parse_css_hsl_color(struct ty_sb *sb, if (!sb->len) return EINA_FALSE; ty_sb_spaces_ltrim(sb); - /* TODO: boris */ + if (!_parse_one_hue(sb, &hue)) + return EINA_FALSE; + is_functional = (sb->len && sb->buf[0] == ','); + if (is_functional) + { + ty_sb_lskip(sb, 1); + } + ty_sb_spaces_ltrim(sb); + if (!sb->len) return EINA_FALSE; + + if (!_parse_one_percent(sb, &saturation)) + return EINA_FALSE; + ty_sb_spaces_ltrim(sb); + if (!sb->len) return EINA_FALSE; + if (is_functional != (sb->buf[0] == ',')) + return EINA_FALSE; + if (is_functional) + ty_sb_lskip(sb, 1); + ty_sb_spaces_ltrim(sb); + if (!sb->len) return EINA_FALSE; + + if (!_parse_one_percent(sb, &lightness)) + return EINA_FALSE; + ty_sb_spaces_ltrim(sb); + if (!sb->len) return EINA_FALSE; + if (sb->buf[0] == ')') + { + ty_sb_lskip(sb, 1); + a = 255; + } + else + { + if ((is_functional && (sb->buf[0] != ',')) || + (!is_functional && (sb->buf[0] != '/'))) + return EINA_FALSE; + if (!sb->len) return EINA_FALSE; + ty_sb_lskip(sb, 1); + ty_sb_spaces_ltrim(sb); + if (!sb->len) return EINA_FALSE; + if (!_parse_one_css_alpha(sb, &a)) + return EINA_FALSE; + ty_sb_spaces_ltrim(sb); + if (sb->buf[0] != ')') + return EINA_FALSE; + } + if (!_hsl_to_rgb(hue, saturation, lightness, &r, &g, &b)) + return EINA_FALSE; + *rp = r; *gp = g; *bp = b; @@ -1692,4 +1834,123 @@ tytest_color_parse_css_rgb(void) return 0; } + +int +tytest_color_parse_css_hsl(void) +{ + struct ty_sb sb = {}; + uint8_t r = 0, g = 0, b = 0, a = 0; + + + /* These examples all specify the same color: a lavender. */ + assert(TY_SB_ADD(&sb, "hsl(270,60%,70%)") == 0); + assert(_parse_css_hsl_color(&sb, &r, &g, &b, &a) == EINA_TRUE); + assert(r == 224 && g == 135 && b == 132 && a == 255); + ty_sb_free(&sb); + r = g = b = a = 0; + + assert(TY_SB_ADD(&sb, "hsl(270, 60%, 70%)") == 0); + assert(_parse_css_hsl_color(&sb, &r, &g, &b, &a) == EINA_TRUE); + assert(r == 224 && g == 135 && b == 132 && a == 255); + ty_sb_free(&sb); + r = g = b = a = 0; + + assert(TY_SB_ADD(&sb, "hsl(270 60% 70%)") == 0); + assert(_parse_css_hsl_color(&sb, &r, &g, &b, &a) == EINA_TRUE); + assert(r == 224 && g == 135 && b == 132 && a == 255); + ty_sb_free(&sb); + r = g = b = a = 0; + + assert(TY_SB_ADD(&sb, "hsl(270deg, 60%, 70%)") == 0); + assert(_parse_css_hsl_color(&sb, &r, &g, &b, &a) == EINA_TRUE); + assert(r == 224 && g == 135 && b == 132 && a == 255); + ty_sb_free(&sb); + r = g = b = a = 0; + + assert(TY_SB_ADD(&sb, "hsl(4.71239rad, 60%, 70%)") == 0); + assert(_parse_css_hsl_color(&sb, &r, &g, &b, &a) == EINA_TRUE); + assert(r == 224 && g == 135 && b == 132 && a == 255); + ty_sb_free(&sb); + r = g = b = a = 0; + + assert(TY_SB_ADD(&sb, "hsl(300grad, 60%, 70%)") == 0); + assert(_parse_css_hsl_color(&sb, &r, &g, &b, &a) == EINA_TRUE); + assert(r == 224 && g == 135 && b == 132 && a == 255); + ty_sb_free(&sb); + r = g = b = a = 0; + + assert(TY_SB_ADD(&sb, "hsl(.75turn, 60%, 70%)") == 0); + assert(_parse_css_hsl_color(&sb, &r, &g, &b, &a) == EINA_TRUE); + assert(r == 224 && g == 135 && b == 132 && a == 255); + ty_sb_free(&sb); + r = g = b = a = 0; + + + /* These examples all specify the same color: a lavender that is 15% opaque. */ + assert(TY_SB_ADD(&sb, "hsl(270, 60%, 50%, .15)") == 0); + assert(_parse_css_hsl_color(&sb, &r, &g, &b, &a) == EINA_TRUE); + assert(r == 204 && g == 55 && b == 51 && a == 38); + ty_sb_free(&sb); + r = g = b = a = 0; + + assert(TY_SB_ADD(&sb, "hsl(270, 60%, 50%, 15%)") == 0); + assert(_parse_css_hsl_color(&sb, &r, &g, &b, &a) == EINA_TRUE); + assert(r == 204 && g == 55 && b == 51 && a == 38); + ty_sb_free(&sb); + r = g = b = a = 0; + + assert(TY_SB_ADD(&sb, "hsl(270 60% 50% / .15)") == 0); + assert(_parse_css_hsl_color(&sb, &r, &g, &b, &a) == EINA_TRUE); + assert(r == 204 && g == 55 && b == 51 && a == 38); + ty_sb_free(&sb); + r = g = b = a = 0; + + assert(TY_SB_ADD(&sb, "hsl(270 60% 50% / 15%)") == 0); + assert(_parse_css_hsl_color(&sb, &r, &g, &b, &a) == EINA_TRUE); + assert(r == 204 && g == 55 && b == 51 && a == 38); + ty_sb_free(&sb); + r = g = b = a = 0; + + + /* Different shades */ + assert(TY_SB_ADD(&sb, "hsla(240, 100%, 50%, .05)") == 0); + assert(_parse_css_hsl_color(&sb, &r, &g, &b, &a) == EINA_TRUE); + assert(r == 255 && g == 7 && b == 0 && a == 13); + ty_sb_free(&sb); + r = g = b = a = 0; + + assert(TY_SB_ADD(&sb, "hsla(240, 100%, 50%, .4)") == 0); + assert(_parse_css_hsl_color(&sb, &r, &g, &b, &a) == EINA_TRUE); + assert(r == 255 && g == 7 && b == 0 && a == 102); + ty_sb_free(&sb); + r = g = b = a = 0; + + assert(TY_SB_ADD(&sb, "hsla(240, 100%, 50%, .7)") == 0); + assert(_parse_css_hsl_color(&sb, &r, &g, &b, &a) == EINA_TRUE); + assert(r == 255 && g == 7 && b == 0 && a == 179); + ty_sb_free(&sb); + r = g = b = a = 0; + + assert(TY_SB_ADD(&sb, "hsla(240, 100%, 50%, 1)") == 0); + assert(_parse_css_hsl_color(&sb, &r, &g, &b, &a) == EINA_TRUE); + assert(r == 255 && g == 7 && b == 0 && a == 255); + ty_sb_free(&sb); + r = g = b = a = 0; + + /* Whitespace syntax */ + assert(TY_SB_ADD(&sb, "hsla(240 100% 50% / .05)") == 0); + assert(_parse_css_hsl_color(&sb, &r, &g, &b, &a) == EINA_TRUE); + assert(r == 255 && g == 7 && b == 0 && a == 13); + ty_sb_free(&sb); + r = g = b = a = 0; + + /* Percentage value for alpha */ + assert(TY_SB_ADD(&sb, "hsla(240 100% 50% / 5%)") == 0); + assert(_parse_css_hsl_color(&sb, &r, &g, &b, &a) == EINA_TRUE); + assert(r == 255 && g == 7 && b == 0 && a == 13); + ty_sb_free(&sb); + r = g = b = a = 0; + + return 0; +} #endif diff --git a/src/bin/tytest.c b/src/bin/tytest.c index 50a826c8..b501ee59 100644 --- a/src/bin/tytest.c +++ b/src/bin/tytest.c @@ -39,6 +39,7 @@ static struct { { "color_parse_uint8", tytest_color_parse_uint8}, { "color_parse_edc", tytest_color_parse_edc}, { "color_parse_css_rgb", tytest_color_parse_css_rgb}, + { "color_parse_css_hsl", tytest_color_parse_css_hsl}, { NULL, NULL}, }; diff --git a/src/bin/unit_tests.h b/src/bin/unit_tests.h index 28947246..d429f7b8 100644 --- a/src/bin/unit_tests.h +++ b/src/bin/unit_tests.h @@ -16,5 +16,6 @@ int tytest_color_parse_sharp(void); int tytest_color_parse_uint8(void); int tytest_color_parse_edc(void); int tytest_color_parse_css_rgb(void); +int tytest_color_parse_css_hsl(void); #endif