From e0297daa1fcf90a911fd236971e1a2b433f7b134 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 15 Nov 2020 19:45:33 +0100 Subject: [PATCH] hsl: add our own implementations of rgb-to-hsl and hsl-to-rgb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * New function: rgb_to_hsl() * New function: hsl_to_rgb() * Replace XTerm’s hls_to_rgb() with our own, hsl_to_rgb(). * Ensure hue/lum/sat values are within range before calling hsl_to_rgb() Note that sixels’ use the following primary hues: * blue: 0° * red: 120 * green: 240° While “standard” HSL uses: * red: 0° * green: 120° * blue: 240° Thus, we need to adjust the sixel’s hue value before calling hsl_to_rgb(). --- hsl.c | 89 +++++++++++++++++++++++++++++++++++++++ hsl.h | 6 +++ meson.build | 2 +- sixel-hls.c | 117 ---------------------------------------------------- sixel-hls.h | 9 ---- sixel.c | 37 +++++++++++++---- 6 files changed, 124 insertions(+), 136 deletions(-) create mode 100644 hsl.c create mode 100644 hsl.h delete mode 100644 sixel-hls.c delete mode 100644 sixel-hls.h diff --git a/hsl.c b/hsl.c new file mode 100644 index 00000000..3ebe4beb --- /dev/null +++ b/hsl.c @@ -0,0 +1,89 @@ +#include "hsl.h" + +#include + +#include "util.h" + +void +rgb_to_hsl(uint32_t rgb, int *hue, int *sat, int *lum) +{ + double r = (double)((rgb >> 16) & 0xff) / 255.; + double g = (double)((rgb >> 8) & 0xff) / 255.; + double b = (double)((rgb >> 0) & 0xff) / 255.; + + double x_max = max(max(r, g), b); + double x_min = min(min(r, g), b); + double V = x_max; + + double C = x_max - x_min; + double L = (x_max + x_min) / 2.; + + *lum = 100 * L; + + if (C == 0.0) + *hue = 0; + else if (V == r) + *hue = 60. * (0. + (g - b) / C); + else if (V == g) + *hue = 60. * (2. + (b - r) / C); + else if (V == b) + *hue = 60. * (4. + (r - g) / C); + if (*hue < 0) + *hue += 360; + + double S = C == 0.0 + ? 0 + : C / (1. - fabs(2. * L - 1.)); + *sat = 100 * S; +} + +uint32_t +hsl_to_rgb(int hue, int sat, int lum) +{ + double L = lum / 100.0; + double S = sat / 100.0; + double C = (1. - fabs(2. * L - 1.)) * S; + + double X = C * (1. - fabs(fmod((double)hue / 60., 2.) - 1.)); + double m = L - C / 2.; + + double r, g, b; + if (hue >= 0 && hue <= 60) { + r = C; + g = X; + b = 0.; + } else if (hue >= 60 && hue <= 120) { + r = X; + g = C; + b = 0.; + } else if (hue >= 120 && hue <= 180) { + r = 0.; + g = C; + b = X; + } else if (hue >= 180 && hue <= 240) { + r = 0.; + g = X; + b = C; + } else if (hue >= 240 && hue <= 300) { + r = X; + g = 0.; + b = C; + } else if (hue >= 300 && hue <= 360) { + r = C; + g = 0.; + b = X; + } else { + r = 0.; + g = 0.; + b = 0.; + } + + r += m; + g += m; + b += m; + + return ( + (int)round(r * 255.) << 16 | + (int)round(g * 255.) << 8 | + (int)round(b * 255.) << 0); +} diff --git a/hsl.h b/hsl.h new file mode 100644 index 00000000..2a46c117 --- /dev/null +++ b/hsl.h @@ -0,0 +1,6 @@ +#pragma once + +#include + +void rgb_to_hsl(uint32_t rgb, int *hue, int *sat, int *lum); +uint32_t hsl_to_rgb(int hue, int sat, int lum); diff --git a/meson.build b/meson.build index d4d95708..c2fff8cc 100644 --- a/meson.build +++ b/meson.build @@ -111,6 +111,7 @@ executable( 'extract.c', 'extract.h', 'fdm.c', 'fdm.h', 'grid.c', 'grid.h', + 'hsl.c', 'hsl.h', 'input.c', 'input.h', 'log.c', 'log.h', 'macros.h', @@ -125,7 +126,6 @@ executable( 'server.c', 'server.h', 'shm.c', 'shm.h', 'sixel.c', 'sixel.h', - 'sixel-hls.c', 'sixel-hls.h', 'slave.c', 'slave.h', 'spawn.c', 'spawn.h', 'terminal.c', 'terminal.h', diff --git a/sixel-hls.c b/sixel-hls.c deleted file mode 100644 index 8b88b414..00000000 --- a/sixel-hls.c +++ /dev/null @@ -1,117 +0,0 @@ -// sixel.c (part of mintty) -// this function is derived from a part of graphics.c -// in Xterm pl#310 originally written by Ross Combs. -// -// Copyright 2013,2014 by Ross Combs -// -// All Rights Reserved -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -// IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY -// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// -// Except as contained in this notice, the name(s) of the above copyright -// holders shall not be used in advertising or otherwise to promote the -// sale, use or other dealings in this Software without prior written -// authorization. - -#include "sixel-hls.h" - -#define SIXEL_RGB(r, g, b) (((r) << 16) + ((g) << 8) + (b)) - -int -hls_to_rgb(int hue, int lum, int sat) -{ - double hs = (hue + 240) % 360; - double hv = hs / 360.0; - double lv = lum / 100.0; - double sv = sat / 100.0; - double c, x, m, c2; - double r1, g1, b1; - int r, g, b; - int hpi; - - if (sat == 0) { - r = g = b = lum * 255 / 100; - return SIXEL_RGB(r, g, b); - } - - if ((c2 = ((2.0 * lv) - 1.0)) < 0.0) { - c2 = -c2; - } - c = (1.0 - c2) * sv; - hpi = (int) (hv * 6.0); - x = (hpi & 1) ? c : 0.0; - m = lv - 0.5 * c; - - switch (hpi) { - case 0: - r1 = c; - g1 = x; - b1 = 0.0; - break; - case 1: - r1 = x; - g1 = c; - b1 = 0.0; - break; - case 2: - r1 = 0.0; - g1 = c; - b1 = x; - break; - case 3: - r1 = 0.0; - g1 = x; - b1 = c; - break; - case 4: - r1 = x; - g1 = 0.0; - b1 = c; - break; - case 5: - r1 = c; - g1 = 0.0; - b1 = x; - break; - default: - return SIXEL_RGB(255, 255, 255); - } - - r = (int) ((r1 + m) * 100.0 + 0.5); - g = (int) ((g1 + m) * 100.0 + 0.5); - b = (int) ((b1 + m) * 100.0 + 0.5); - - if (r < 0) { - r = 0; - } else if (r > 100) { - r = 100; - } - if (g < 0) { - g = 0; - } else if (g > 100) { - g = 100; - } - if (b < 0) { - b = 0; - } else if (b > 100) { - b = 100; - } - return SIXEL_RGB(r * 255 / 100, g * 255 / 100, b * 255 / 100); -} diff --git a/sixel-hls.h b/sixel-hls.h deleted file mode 100644 index 6d6b9f2f..00000000 --- a/sixel-hls.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -/* - * Primary color hues: - * blue: 0 degrees - * red: 120 degrees - * green: 240 degrees - */ -int hls_to_rgb(int hue, int lum, int sat); diff --git a/sixel.c b/sixel.c index 89acd638..68490dfa 100644 --- a/sixel.c +++ b/sixel.c @@ -7,7 +7,7 @@ #define LOG_ENABLE_DBG 0 #include "log.h" #include "render.h" -#include "sixel-hls.h" +#include "hsl.h" #include "util.h" #include "xmalloc.h" @@ -1052,23 +1052,42 @@ decgci(struct terminal *term, uint8_t c) if (nparams > 4) { unsigned format = term->sixel.params[1]; - unsigned c1 = term->sixel.params[2]; - unsigned c2 = term->sixel.params[3]; - unsigned c3 = term->sixel.params[4]; + int c1 = term->sixel.params[2]; + int c2 = term->sixel.params[3]; + int c3 = term->sixel.params[4]; switch (format) { case 1: { /* HLS */ - uint32_t rgb = hls_to_rgb(c1, c2, c3); + int hue = min(c1, 360); + int lum = min(c2, 100); + int sat = min(c3, 100); + + /* + * Sixel’s HLS use the following primary color hues: + * blue: 0° + * red: 120° + * green: 240° + * + * While “standard” HSL uses: + * red: 0° + * green: 120° + * blue: 240° + */ + hue = (hue + 240) % 360; + + uint32_t rgb = hsl_to_rgb(hue, sat, lum); + LOG_DBG("setting palette #%d = HLS %hhu/%hhu/%hhu (0x%06x)", - term->sixel.color_idx, c1, c2, c3, rgb); + term->sixel.color_idx, hue, lum, sat, rgb); + term->sixel.palette[term->sixel.color_idx] = rgb; break; } case 2: { /* RGB */ - uint8_t r = 255 * c1 / 100; - uint8_t g = 255 * c2 / 100; - uint8_t b = 255 * c3 / 100; + uint8_t r = 255 * min(c1, 100) / 100; + uint8_t g = 255 * min(c2, 100) / 100; + uint8_t b = 255 * min(c3, 100) / 100; LOG_DBG("setting palette #%d = RGB %hhu/%hhu/%hhu", term->sixel.color_idx, r, g, b);