hsl: add our own implementations of rgb-to-hsl and hsl-to-rgb

* 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().
This commit is contained in:
Daniel Eklöf 2020-11-15 19:45:33 +01:00
parent 8e779b356e
commit e0297daa1f
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
6 changed files with 124 additions and 136 deletions

89
hsl.c Normal file
View file

@ -0,0 +1,89 @@
#include "hsl.h"
#include <math.h>
#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);
}

6
hsl.h Normal file
View file

@ -0,0 +1,6 @@
#pragma once
#include <stdint.h>
void rgb_to_hsl(uint32_t rgb, int *hue, int *sat, int *lum);
uint32_t hsl_to_rgb(int hue, int sat, int lum);

View file

@ -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',

View file

@ -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);
}

View file

@ -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);

37
sixel.c
View file

@ -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);
/*
* Sixels 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);