config: tweak.surface-bit-depth: add support for 16-bit surfaces

This adds supports for 16-bit surfaces, using the new
PIXMAN_a16b16g16r16 buffer format. This maps to
WL_SHM_FORMAT_ABGR16161616 (little-endian).

Use the new 16-bit surfaces by default, when
gamma-correct-blending=yes.
This commit is contained in:
Daniel Eklöf 2025-05-01 09:37:47 +02:00
parent 7354b94f73
commit 970e13db8d
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
10 changed files with 101 additions and 25 deletions

View file

@ -87,6 +87,9 @@
- solarized - solarized
* `regex-copy`/`show-urls-copy` will copy and paste the selected text if the hint * `regex-copy`/`show-urls-copy` will copy and paste the selected text if the hint
is completed with an uppercase character ([#1975][1975]). is completed with an uppercase character ([#1975][1975]).
* `16-bit` to `tweak.surface-bit-depth`. Makes foot use 16-bit image
buffers. They provide the necessary color precision required by
`gamma-correct-blending=yes`.
[2025]: https://codeberg.org/dnkl/foot/issues/2025 [2025]: https://codeberg.org/dnkl/foot/issues/2025
[1975]: https://codeberg.org/dnkl/foot/issues/1975 [1975]: https://codeberg.org/dnkl/foot/issues/1975
@ -98,6 +101,8 @@
* OSC-11 without an alpha value will now restore the configured * OSC-11 without an alpha value will now restore the configured
(i.e. from `foot.ini`) alpha, rather than keeping whatever the (i.e. from `foot.ini`) alpha, rather than keeping whatever the
current alpha value is, unchanged. current alpha value is, unchanged.
* `gamma-correct-blending=yes` now defaults to `16-bit` image buffers,
instead of `10-bit`.
### Deprecated ### Deprecated

View file

@ -2809,12 +2809,19 @@ parse_section_tweak(struct context *ctx)
else if (streq(key, "surface-bit-depth")) { else if (streq(key, "surface-bit-depth")) {
_Static_assert(sizeof(conf->tweak.surface_bit_depth) == sizeof(int), _Static_assert(sizeof(conf->tweak.surface_bit_depth) == sizeof(int),
"enum is not 32-bit"); "enum is not 32-bit");
#if defined(HAVE_PIXMAN_RGBA_16)
return value_to_enum(
ctx,
(const char *[]){"auto", "8-bit", "10-bit", "16-bit", NULL},
(int *)&conf->tweak.surface_bit_depth);
#else
return value_to_enum( return value_to_enum(
ctx, ctx,
(const char *[]){"auto", "8-bit", "10-bit", NULL}, (const char *[]){"auto", "8-bit", "10-bit", NULL},
(int *)&conf->tweak.surface_bit_depth); (int *)&conf->tweak.surface_bit_depth);
#endif
} }
else { else {

View file

@ -198,7 +198,8 @@ enum which_color_theme {
enum shm_bit_depth { enum shm_bit_depth {
SHM_BITS_AUTO, SHM_BITS_AUTO,
SHM_BITS_8, SHM_BITS_8,
SHM_BITS_10 SHM_BITS_10,
SHM_BITS_16,
}; };
struct config { struct config {

View file

@ -207,7 +207,7 @@ empty string to be set, but it must be quoted: *KEY=""*)
Compared to the default (disabled), bright glyphs on a dark Compared to the default (disabled), bright glyphs on a dark
background will appear thicker, and dark glyphs on a light background will appear thicker, and dark glyphs on a light
background will appear thinner. background will appear thinner.
FreeType can limit the effect of the latter, with a technique FreeType can limit the effect of the latter, with a technique
called stem darkening. It is only available for CFF fonts called stem darkening. It is only available for CFF fonts
(OpenType, .otf) and disabled by default (in FreeType). You can (OpenType, .otf) and disabled by default (in FreeType). You can
@ -221,12 +221,13 @@ empty string to be set, but it must be quoted: *KEY=""*)
font designer set the font weight based on incorrect rendering. font designer set the font weight based on incorrect rendering.
In order to represent colors faithfully, higher precision image In order to represent colors faithfully, higher precision image
buffers are required. By default, foot will use 10-bit color buffers are required. By default, foot will use either 16-bit, or
channels, if available, when gamma-correct blending is 10-bit color channels, depending on availability, when
enabled. However, the high precision buffers are slow; if you want gamma-correct blending is enabled. However, the high precision
to use gamma-correct blending, but prefer speed (throughput and buffers are slow; if you want to use gamma-correct blending, but
input latency) over accurate colors, you can force 8-bit color prefer speed (throughput and input latency) over accurate colors,
channels by setting *tweak.surface-bit-depth=8-bit*. you can force 8-bit color channels by setting
*tweak.surface-bit-depth=8-bit*.
Default: _no_. Default: _no_.
@ -2023,7 +2024,7 @@ any of these options.
*surface-bit-depth* *surface-bit-depth*
Selects which RGB bit depth to use for image buffers. One of Selects which RGB bit depth to use for image buffers. One of
*auto*, *8-bit*, or *10-bit*. *auto*, *8-bit*, *10-bit* or *16-bit*.
*auto* chooses bit depth depending on other settings, and *auto* chooses bit depth depending on other settings, and
availability. availability.
@ -2033,12 +2034,14 @@ any of these options.
*10-bit* uses 10 bits for each RGB channel, and 2 bits for the *10-bit* uses 10 bits for each RGB channel, and 2 bits for the
alpha channel. Thus, it provides higher precision color channels, alpha channel. Thus, it provides higher precision color channels,
but a lower precision alpha channel. It is the default when but a lower precision alpha channel.
*gamma-correct-blending=yes*, if supported by the compositor.
Note that *10-bit* is much slower than *8-bit*; if you want to use *16-bit* 16 bits for each color channel, alpha included. If
gamma-correct blending, and if you prefer speed (throughput and available, this is the default when *gamma-correct-blending=yes*.
input latency) over accurate colors, you can set
Note that both *10-bit* and *16-bit* are much slower than *8-bit*;
if you want to use gamma-correct blending, and if you prefer speed
(throughput and input latency) over accurate colors, you can set
*surface-bit-depth=8-bit* explicitly. *surface-bit-depth=8-bit* explicitly.
Default: _auto_ Default: _auto_

View file

@ -145,6 +145,10 @@ if utf8proc.found()
add_project_arguments('-DFOOT_GRAPHEME_CLUSTERING=1', language: 'c') add_project_arguments('-DFOOT_GRAPHEME_CLUSTERING=1', language: 'c')
endif endif
if pixman.version().version_compare('>=0.46.0')
add_project_arguments('-DHAVE_PIXMAN_RGBA_16', language: 'c')
endif
tllist = dependency('tllist', version: '>=1.1.0', fallback: 'tllist') tllist = dependency('tllist', version: '>=1.1.0', fallback: 'tllist')
fcft = dependency('fcft', version: ['>=3.3.1', '<4.0.0'], fallback: 'fcft') fcft = dependency('fcft', version: ['>=3.3.1', '<4.0.0'], fallback: 'fcft')

53
shm.c
View file

@ -338,7 +338,10 @@ get_new_buffers(struct buffer_chain *chain, size_t count,
size_t total_size = 0; size_t total_size = 0;
for (size_t i = 0; i < count; i++) { for (size_t i = 0; i < count; i++) {
stride[i] = stride_for_format_and_width( stride[i] = stride_for_format_and_width(
with_alpha ? PIXMAN_a8r8g8b8 : PIXMAN_x8r8g8b8, widths[i]); with_alpha
? chain->pixman_fmt_with_alpha
: chain->pixman_fmt_without_alpha,
widths[i]);
sizes[i] = stride[i] * heights[i]; sizes[i] = stride[i] * heights[i];
total_size += sizes[i]; total_size += sizes[i];
} }
@ -981,8 +984,38 @@ shm_chain_new(struct wayland *wayl, bool scrollable, size_t pix_instances,
enum wl_shm_format shm_fmt_with_alpha = WL_SHM_FORMAT_ARGB8888; enum wl_shm_format shm_fmt_with_alpha = WL_SHM_FORMAT_ARGB8888;
static bool have_logged = false; static bool have_logged = false;
static bool have_logged_10_fallback = false;
if (desired_bit_depth == SHM_BITS_10) { #if defined(HAVE_PIXMAN_RGBA_16)
static bool have_logged_16_fallback = false;
if (desired_bit_depth == SHM_BITS_16) {
if (wayl->shm_have_abgr161616 && wayl->shm_have_xbgr161616) {
pixman_fmt_without_alpha = PIXMAN_a16b16g16r16;
shm_fmt_without_alpha = WL_SHM_FORMAT_XBGR16161616;
pixman_fmt_without_alpha = PIXMAN_a16b16g16r16;
shm_fmt_with_alpha = WL_SHM_FORMAT_ABGR16161616;
if (!have_logged) {
have_logged = true;
LOG_INFO("using 16-bit BGR surfaces");
}
} else {
if (!have_logged_16_fallback) {
have_logged_16_fallback = true;
LOG_WARN(
"16-bit surfaces requested, but compositor does not "
"implement ABGR161616+XBGR161616");
}
}
}
#endif
if (desired_bit_depth >= SHM_BITS_10 &&
pixman_fmt_with_alpha == PIXMAN_a8r8g8b8)
{
if (wayl->shm_have_argb2101010 && wayl->shm_have_xrgb2101010) { if (wayl->shm_have_argb2101010 && wayl->shm_have_xrgb2101010) {
pixman_fmt_without_alpha = PIXMAN_x2r10g10b10; pixman_fmt_without_alpha = PIXMAN_x2r10g10b10;
shm_fmt_without_alpha = WL_SHM_FORMAT_XRGB2101010; shm_fmt_without_alpha = WL_SHM_FORMAT_XRGB2101010;
@ -1010,13 +1043,13 @@ shm_chain_new(struct wayland *wayl, bool scrollable, size_t pix_instances,
} }
else { else {
if (!have_logged) { if (!have_logged_10_fallback) {
have_logged = true; have_logged_10_fallback = true;
LOG_WARN( LOG_WARN(
"10-bit surfaces requested, but compositor does not " "10-bit surfaces requested, but compositor does not "
"implement ARGB2101010+XRGB2101010, or " "implement ARGB2101010+XRGB2101010, or "
"ABGR2101010+XBGR2101010. Falling back to 8-bit surfaces"); "ABGR2101010+XBGR2101010");
} }
} }
} else { } else {
@ -1063,7 +1096,11 @@ shm_chain_bit_depth(const struct buffer_chain *chain)
{ {
const pixman_format_code_t fmt = chain->pixman_fmt_with_alpha; const pixman_format_code_t fmt = chain->pixman_fmt_with_alpha;
return (fmt == PIXMAN_a2r10g10b10 || fmt == PIXMAN_a2b10g10r10) return fmt == PIXMAN_a8r8g8b8
? SHM_BITS_10 ? SHM_BITS_8
: SHM_BITS_8; #if defined(HAVE_PIXMAN_RGBA_16)
: fmt == PIXMAN_a16b16g16r16
? SHM_BITS_16
#endif
: SHM_BITS_10;
} }

13
sixel.c
View file

@ -113,7 +113,18 @@ sixel_init(struct terminal *term, int p1, int p2, int p3)
term->sixel.linear_blending = wayl_do_linear_blending(term->wl, term->conf); term->sixel.linear_blending = wayl_do_linear_blending(term->wl, term->conf);
term->sixel.pixman_fmt = PIXMAN_a8r8g8b8; term->sixel.pixman_fmt = PIXMAN_a8r8g8b8;
if (term->conf->tweak.surface_bit_depth == SHM_BITS_10) { /*
* Use higher-precision sixel surfaces if we're using
* higher-precision window surfaces.
*
* This is to a) get more accurate colors when doing gamma-correct
* blending, and b) use the same pixman format as the main
* surfaces, for (hopefully) better performance.
*
* For now, don't support 16-bit surfaces (too much sixel logic
* that assumes 32-bit pixels).
*/
if (shm_chain_bit_depth(term->render.chains.grid) >= SHM_BITS_10) {
if (term->wl->shm_have_argb2101010 && term->wl->shm_have_xrgb2101010) { if (term->wl->shm_have_argb2101010 && term->wl->shm_have_xrgb2101010) {
term->sixel.use_10bit = true; term->sixel.use_10bit = true;
term->sixel.pixman_fmt = PIXMAN_a2r10g10b10; term->sixel.pixman_fmt = PIXMAN_a2r10g10b10;

View file

@ -1082,7 +1082,11 @@ reload_fonts(struct terminal *term, bool resize_grid)
* an a2r10g0b10 type of surface, since we need more than 2 * an a2r10g0b10 type of surface, since we need more than 2
* bits for alpha. * bits for alpha.
*/ */
#if defined(HAVE_PIXMAN_RGBA_16)
options->color_glyphs.format = PIXMAN_a16b16g16r16;
#else
options->color_glyphs.format = PIXMAN_rgba_float; options->color_glyphs.format = PIXMAN_rgba_float;
#endif
} }
struct fcft_font *fonts[4]; struct fcft_font *fonts[4];
@ -1259,7 +1263,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
const enum shm_bit_depth desired_bit_depth = const enum shm_bit_depth desired_bit_depth =
conf->tweak.surface_bit_depth == SHM_BITS_AUTO conf->tweak.surface_bit_depth == SHM_BITS_AUTO
? wayl_do_linear_blending(wayl, conf) ? SHM_BITS_10 : SHM_BITS_8 ? wayl_do_linear_blending(wayl, conf) ? SHM_BITS_16 : SHM_BITS_8
: conf->tweak.surface_bit_depth; : conf->tweak.surface_bit_depth;
const struct color_theme *theme = NULL; const struct color_theme *theme = NULL;

View file

@ -244,6 +244,8 @@ shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
case WL_SHM_FORMAT_ARGB2101010: wayl->shm_have_argb2101010 = true; break; case WL_SHM_FORMAT_ARGB2101010: wayl->shm_have_argb2101010 = true; break;
case WL_SHM_FORMAT_XBGR2101010: wayl->shm_have_xbgr2101010 = true; break; case WL_SHM_FORMAT_XBGR2101010: wayl->shm_have_xbgr2101010 = true; break;
case WL_SHM_FORMAT_ABGR2101010: wayl->shm_have_abgr2101010 = true; break; case WL_SHM_FORMAT_ABGR2101010: wayl->shm_have_abgr2101010 = true; break;
case WL_SHM_FORMAT_XBGR16161616: wayl->shm_have_xbgr161616 = true; break;
case WL_SHM_FORMAT_ABGR16161616: wayl->shm_have_abgr161616 = true; break;
} }
#if defined(_DEBUG) #if defined(_DEBUG)

View file

@ -496,6 +496,8 @@ struct wayland {
bool shm_have_xrgb2101010:1; bool shm_have_xrgb2101010:1;
bool shm_have_abgr2101010:1; bool shm_have_abgr2101010:1;
bool shm_have_xbgr2101010:1; bool shm_have_xbgr2101010:1;
bool shm_have_abgr161616:1;
bool shm_have_xbgr161616:1;
}; };
struct wayland *wayl_init( struct wayland *wayl_init(