render: dim and brighten using linear rgb interpolation

Adds setting tweak.dim-amount, similar to bold-text-in-bright-amount.

Closes #2006
This commit is contained in:
Sam McCall 2025-03-22 20:11:23 +01:00 committed by Daniel Eklöf
parent 5f72f51ae8
commit 663c9082db
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
7 changed files with 51 additions and 66 deletions

View file

@ -64,6 +64,12 @@
### Changed
* UTF-8 error recovery now discards fewer bytes.
* Auto-calculated dimmed and brightened colors (e.g. when custom dim
colors has not configured) is now done by linear RGB interpolation,
rather than converting to HSL and adjusting the luminance
([#2006][2006]).
[2006]: https://codeberg.org/dnkl/foot/issues/2006
### Deprecated

View file

@ -2759,6 +2759,9 @@ parse_section_tweak(struct context *ctx)
else if (streq(key, "sixel"))
return value_to_bool(ctx, &conf->tweak.sixel);
else if (streq(key, "dim-amount"))
return value_to_float(ctx, &conf->dim.amount);
else if (streq(key, "bold-text-in-bright-amount"))
return value_to_float(ctx, &conf->bold_in_bright.amount);
@ -3288,6 +3291,7 @@ config_load(struct config *conf, const char *conf_path,
.resize_by_cells = true,
.resize_keep_grid = true,
.resize_delay_ms = 100,
.dim = { .amount = 1.5 },
.bold_in_bright = {
.enabled = false,
.palette_based = false,

View file

@ -155,6 +155,10 @@ struct config {
uint16_t resize_delay_ms;
struct {
float amount;
} dim;
struct {
bool enabled;
bool palette_based;

View file

@ -396,7 +396,7 @@ empty string to be set, but it must be quoted: *KEY=""*)
*bold-text-in-bright*
Semi-boolean. When enabled, bold text is rendered in a brighter
color (in addition to using a bold font). The color is brightened
by increasing its luminance.
by blending it with white.
If set to *palette-based*, rather than a simple *yes|true*, colors
matching one of the 8 regular palette colors will be brightened
@ -986,8 +986,8 @@ can configure the background transparency with the _alpha_ option.
an entry in the color palette. Applications emit them by combining
a color value, and a "dim" attribute.
By default, foot implements this by reducing the luminance of the
current color. This is a generic approach that applies to both
By default, foot implements this by blending the current color
with black. This is a generic approach that applies to both
colors from the 256-color palette, as well as 24-bit RGB colors.
You can change this behavior by setting the *dimN* options. When
@ -999,7 +999,7 @@ can configure the background transparency with the _alpha_ option.
the corresponding *regularN* color will be used.
If the current color does not match any known color, it is dimmed
by reducing the luminance (i.e. the same behavior as if the *dimN*
by blending with black (i.e. the same behavior as if the *dimN*
options are unconfigured). 24-bit RGB colors will typically fall
into this category.
@ -1940,6 +1940,9 @@ any of these options.
Boolean. When enabled, foot will process sixel images. Default:
_yes_
*dim-amount*
Amount by which dimmed text is darkened. Default: _1.5_.
*bold-text-in-bright-amount*
Amount by which bold fonts are brightened when
*bold-text-in-bright* is set to *yes* (the *palette-based* variant

35
hsl.c
View file

@ -2,41 +2,6 @@
#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)
{

1
hsl.h
View file

@ -2,5 +2,4 @@
#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

@ -34,7 +34,6 @@
#include "config.h"
#include "cursor-shape.h"
#include "grid.h"
#include "hsl.h"
#include "ime.h"
#include "quirks.h"
#include "search.h"
@ -271,13 +270,23 @@ color_hex_to_pixman(uint32_t color, bool srgb)
return color_hex_to_pixman_with_alpha(color, 0xffff, srgb);
}
static inline int i_lerp(int from, int to, float t) {
return from + (to - from) * t;
}
static inline uint32_t
color_decrease_luminance(uint32_t color)
color_blend_towards(uint32_t from, uint32_t to, float amount)
{
uint32_t alpha = color & 0xff000000;
int hue, sat, lum;
rgb_to_hsl(color, &hue, &sat, &lum);
return alpha | hsl_to_rgb(hue, sat, lum / 1.5);
if (unlikely(amount == 0))
return from;
float t = 1 - 1/amount;
uint32_t alpha = from & 0xff000000;
uint8_t r = i_lerp((from>>16)&0xff, (to>>16)&0xff, t);
uint8_t g = i_lerp((from>>8)&0xff, (to>>8)&0xff, t);
uint8_t b = i_lerp((from>>0)&0xff, (to>>0)&0xff, t);
return alpha | (r<<16) | (g<<8) | (b<<0);
}
static inline uint32_t
@ -286,25 +295,24 @@ color_dim(const struct terminal *term, uint32_t color)
const struct config *conf = term->conf;
const uint8_t custom_dim = conf->colors.use_custom.dim;
if (likely(custom_dim == 0))
return color_decrease_luminance(color);
if (unlikely(custom_dim != 0)) {
for (size_t i = 0; i < 8; i++) {
if (((custom_dim >> i) & 1) == 0)
continue;
for (size_t i = 0; i < 8; i++) {
if (((custom_dim >> i) & 1) == 0)
continue;
if (term->colors.table[0 + i] == color) {
/* "Regular" color, return the corresponding "dim" */
return conf->colors.dim[i];
}
if (term->colors.table[0 + i] == color) {
/* "Regular" color, return the corresponding "dim" */
return conf->colors.dim[i];
}
else if (term->colors.table[8 + i] == color) {
/* "Bright" color, return the corresponding "regular" */
return term->colors.table[i];
else if (term->colors.table[8 + i] == color) {
/* "Bright" color, return the corresponding "regular" */
return term->colors.table[i];
}
}
}
return color_decrease_luminance(color);
return color_blend_towards(color, 0x00000000, conf->dim.amount);
}
static inline uint32_t
@ -322,11 +330,7 @@ color_brighten(const struct terminal *term, uint32_t color)
return color;
}
int hue, sat, lum;
rgb_to_hsl(color, &hue, &sat, &lum);
lum = (int)roundf(lum * term->conf->bold_in_bright.amount);
return hsl_to_rgb(hue, sat, min(lum, 100));
return color_blend_towards(color, 0x00ffffff, term->conf->bold_in_bright.amount);
}
static void
@ -798,7 +802,7 @@ render_cell(struct terminal *term, pixman_image_t *pix,
_fg = color_brighten(term, _fg);
if (cell->attrs.blink && term->blink.state == BLINK_OFF)
_fg = color_decrease_luminance(_fg);
_fg = color_blend_towards(_fg, 0x00000000, term->conf->dim.amount);
const bool gamma_correct = render_do_linear_blending(term);
pixman_color_t fg = color_hex_to_pixman(_fg, gamma_correct);