mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-05 04:06:08 -05:00
render: gamma-correct blending
This implements gamma-correct blending, which mainly affects font rendering. The implementation requires compile-time availability of the new color-management protocol (available in wayland-protocols >= 1.41), and run-time support for the same in the compositor (specifically, the EXT_LINEAR TF function and sRGB primaries). How it works: all colors are decoded from sRGB to linear (using a lookup table, generated in the exact same way pixman generates it's internal conversion tables) before being used by pixman. The resulting image buffer is thus in decoded/linear format. We use the color-management protocol to inform the compositor of this, by tagging the wayland surfaces with the 'ext_linear' image attribute. Sixes: all colors are sRGB internally, and decoded to linear before being used in any sixels. Thus, the image buffers will contain linear colors. This is important, since otherwise there would be a decode/encode penalty every time a sixel is blended to the grid. Emojis: we require fcft >= 3.2, which adds support for sRGB decoding color glyphs. Meaning, the emoji pixman surfaces can be blended directly to the grid, just like sixels. Gamma-correct blending is enabled by default *when the compositor supports it*. There's a new option to explicitly enable/disable it: gamma-correct-blending=no|yes. If set to 'yes', and the compositor does not implement the required color-management features, warning logs are emitted. There's a loss of precision when storing linear pixels in 8-bit channels. For this reason, this patch also adds supports for 10-bit surfaces. For now, this is disabled by default since such surfaces only have 2 bits for alpha. It can be enabled with tweak.surface-bit-depth=10-bit. Perhaps, in the future, we can enable it by default if: * gamma-correct blending is enabled * the user has not enabled a transparent background
This commit is contained in:
parent
6d39f66eb7
commit
ccf625b991
20 changed files with 746 additions and 101 deletions
|
|
@ -72,6 +72,11 @@
|
|||
* `search-bindings.delete-to-start` and
|
||||
`search-bindings.delete-to-end` key bindings, defaulting to
|
||||
`Control+u` and `Control+k` respectively ([#1972][1972]).
|
||||
* Gamma-correct font rendering. Requires compositor support
|
||||
(`wp_color_management_v1`, and specifically, the `ext_linear`
|
||||
transfer function). Enabled by default when compositor support is
|
||||
available. Can be explicitly enabled or disabled with
|
||||
`gamma-correct-blending=no|yes`.
|
||||
|
||||
[1386]: https://codeberg.org/dnkl/foot/issues/1386
|
||||
[1872]: https://codeberg.org/dnkl/foot/issues/1872
|
||||
|
|
@ -87,6 +92,7 @@
|
|||
* Auto-detection of URLs (i.e. not OSC-8 based URLs) are now regex
|
||||
based.
|
||||
* Rename Tokyo Night Day theme to Tokyo Night Light and update colors.
|
||||
* fcft >= 3.2.0 is now required.
|
||||
|
||||
[1925]: https://codeberg.org/dnkl/foot/issues/1925
|
||||
|
||||
|
|
|
|||
3
client.c
3
client.c
|
|
@ -67,13 +67,14 @@ version_and_features(void)
|
|||
{
|
||||
static char buf[256];
|
||||
snprintf(buf, sizeof(buf),
|
||||
"version: %s %cpgo %cime %cgraphemes %ctoplevel-icon %csystem-bell %cassertions",
|
||||
"version: %s %cpgo %cime %cgraphemes %ctoplevel-icon %csystem-bell %ccolor-management %cassertions",
|
||||
FOOT_VERSION,
|
||||
feature_pgo() ? '+' : '-',
|
||||
feature_ime() ? '+' : '-',
|
||||
feature_graphemes() ? '+' : '-',
|
||||
feature_xdg_toplevel_icon() ? '+' : '-',
|
||||
feature_xdg_system_bell() ? '+' : '-',
|
||||
feature_wp_color_management() ? '+' : '-',
|
||||
feature_assertions() ? '+' : '-');
|
||||
return buf;
|
||||
}
|
||||
|
|
|
|||
38
config.c
38
config.c
|
|
@ -1107,6 +1107,28 @@ parse_section_main(struct context *ctx)
|
|||
return true;
|
||||
}
|
||||
|
||||
else if (streq(key, "gamma-correct-blending")) {
|
||||
bool gamma_correct;
|
||||
if (!value_to_bool(ctx, &gamma_correct))
|
||||
return false;
|
||||
|
||||
#if defined(HAVE_WP_COLOR_MANAGEMENT)
|
||||
conf->gamma_correct =
|
||||
gamma_correct
|
||||
? GAMMA_CORRECT_ENABLED
|
||||
: GAMMA_CORRECT_DISABLED;
|
||||
return true;
|
||||
#else
|
||||
if (gamma_correct) {
|
||||
LOG_CONTEXTUAL_WARN(
|
||||
"ignoring; foot was built without color-management support");
|
||||
}
|
||||
|
||||
conf->gamma_correct = GAMMA_CORRECT_DISABLED;
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
else {
|
||||
LOG_CONTEXTUAL_ERR("not a valid option: %s", key);
|
||||
return false;
|
||||
|
|
@ -2767,6 +2789,16 @@ parse_section_tweak(struct context *ctx)
|
|||
else if (streq(key, "bold-text-in-bright-amount"))
|
||||
return value_to_float(ctx, &conf->bold_in_bright.amount);
|
||||
|
||||
else if (streq(key, "surface-bit-depth")) {
|
||||
_Static_assert(sizeof(conf->tweak.surface_bit_depth) == sizeof(int),
|
||||
"enum is not 32-bit");
|
||||
|
||||
return value_to_enum(
|
||||
ctx,
|
||||
(const char *[]){"8-bit", "10-bit", NULL},
|
||||
(int *)&conf->tweak.surface_bit_depth);
|
||||
}
|
||||
|
||||
else {
|
||||
LOG_CONTEXTUAL_ERR("not a valid option: %s", key);
|
||||
return false;
|
||||
|
|
@ -3300,6 +3332,11 @@ config_load(struct config *conf, const char *conf_path,
|
|||
.underline_thickness = {.pt = 0., .px = -1},
|
||||
.strikeout_thickness = {.pt = 0., .px = -1},
|
||||
.dpi_aware = false,
|
||||
#if defined(HAVE_WP_COLOR_MANAGEMENT)
|
||||
.gamma_correct = GAMMA_CORRECT_AUTO,
|
||||
#else
|
||||
.gamma_correct = GAMMA_CORRECT_DISABLED,
|
||||
#endif
|
||||
.security = {
|
||||
.osc52 = OSC52_ENABLED,
|
||||
},
|
||||
|
|
@ -3408,6 +3445,7 @@ config_load(struct config *conf, const char *conf_path,
|
|||
.box_drawing_solid_shades = true,
|
||||
.font_monospace_warn = true,
|
||||
.sixel = true,
|
||||
.surface_bit_depth = 8,
|
||||
},
|
||||
|
||||
.touch = {
|
||||
|
|
|
|||
4
config.h
4
config.h
|
|
@ -164,6 +164,9 @@ struct config {
|
|||
enum { STARTUP_WINDOWED, STARTUP_MAXIMIZED, STARTUP_FULLSCREEN } startup_mode;
|
||||
|
||||
bool dpi_aware;
|
||||
enum {GAMMA_CORRECT_DISABLED,
|
||||
GAMMA_CORRECT_ENABLED,
|
||||
GAMMA_CORRECT_AUTO} gamma_correct;
|
||||
struct config_font_list fonts[4];
|
||||
struct font_size_adjustment font_size_adjustment;
|
||||
|
||||
|
|
@ -397,6 +400,7 @@ struct config {
|
|||
bool box_drawing_solid_shades;
|
||||
bool font_monospace_warn;
|
||||
bool sixel;
|
||||
enum { SHM_8_BIT, SHM_10_BIT } surface_bit_depth;
|
||||
} tweak;
|
||||
|
||||
struct {
|
||||
|
|
|
|||
|
|
@ -198,6 +198,35 @@ empty string to be set, but it must be quoted: *KEY=""*)
|
|||
|
||||
Default: _unset_
|
||||
|
||||
*gamma-correct-blending*
|
||||
Boolean. When enabled, foot will do gamma-correct blending in
|
||||
linear color space. This is how font glyphs are supposed to be
|
||||
rendered, but since nearly no applications or toolkits are doing
|
||||
it on Linux, the result may not look like you are used to.
|
||||
|
||||
Compared to the default (disabled), bright glyphs on a dark
|
||||
background will appear thicker, and dark glyphs on a light
|
||||
background will appear thinner.
|
||||
|
||||
Also be aware that many fonts have been developed on systems that
|
||||
do not do gamma-correct blending, and may therefore look thicker
|
||||
than intended when rendered with gamma-correct blending, since the
|
||||
font designer set the font weight based on incorrect rendering.
|
||||
|
||||
FreeType can limit the effect of the latter, with a technique
|
||||
called stem darkening. It is only available for CFF fonts
|
||||
(OpenType, .otf) and disabled by default (in FreeType). You can
|
||||
enable it by setting the environment variable
|
||||
*FREETYPE_PROPERTIES="cff:no-stem-darkening=0"* before starting
|
||||
foot.
|
||||
|
||||
You may also want to enable 10-bit image buffers when
|
||||
gamma-correct blending is enabled. Though probably only if you do
|
||||
not use a transparent background (with 10-bit buffers, you only
|
||||
get 2 bits alpha). See *tweak.surface-bit-depth*.
|
||||
|
||||
Default: enabled when compositor support is available
|
||||
|
||||
*box-drawings-uses-font-glyphs*
|
||||
Boolean. When disabled, foot generates box/line drawing characters
|
||||
itself. The are several advantages to doing this instead of using
|
||||
|
|
@ -1917,6 +1946,23 @@ any of these options.
|
|||
*bold-text-in-bright* is set to *yes* (the *palette-based* variant
|
||||
is not affected by this option). Default: _1.3_.
|
||||
|
||||
*surface-bit-depth*
|
||||
Selects which RGB bit depth to use for image buffers. One of
|
||||
*8-bit*, or *10-bit*.
|
||||
|
||||
The default, *8-bit*, uses 8 bits for all channels, alpha
|
||||
included. When *gamma-correct-blending* is disabled, this is the
|
||||
best option.
|
||||
|
||||
When *gamma-correct-blending* is enabled, you may want to enable
|
||||
10-bit surfaces, as that improves the color resolution. Be aware
|
||||
however, that in this mode, the alpha channel is only 2 bits
|
||||
instead of 8 bits. Thus, if you are using a transparent
|
||||
background, you may want to use the default, *8-bit*, even if you
|
||||
have gamma-correct blending enabled.
|
||||
|
||||
Default: _8-bit_
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
*foot*(1), *footclient*(1)
|
||||
|
|
|
|||
|
|
@ -55,3 +55,12 @@ static inline bool feature_xdg_system_bell(void)
|
|||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline bool feature_wp_color_management(void)
|
||||
{
|
||||
#if defined(HAVE_WP_COLOR_MANAGEMENT)
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
3
main.c
3
main.c
|
|
@ -51,13 +51,14 @@ version_and_features(void)
|
|||
{
|
||||
static char buf[256];
|
||||
snprintf(buf, sizeof(buf),
|
||||
"version: %s %cpgo %cime %cgraphemes %ctoplevel-icon %csystem-bell %cassertions",
|
||||
"version: %s %cpgo %cime %cgraphemes %ctoplevel-icon %csystem-bell %ccolor-management %cassertions",
|
||||
FOOT_VERSION,
|
||||
feature_pgo() ? '+' : '-',
|
||||
feature_ime() ? '+' : '-',
|
||||
feature_graphemes() ? '+' : '-',
|
||||
feature_xdg_toplevel_icon() ? '+' : '-',
|
||||
feature_xdg_system_bell() ? '+' : '-',
|
||||
feature_wp_color_management() ? '+' : '-',
|
||||
feature_assertions() ? '+' : '-');
|
||||
return buf;
|
||||
}
|
||||
|
|
|
|||
19
meson.build
19
meson.build
|
|
@ -146,7 +146,7 @@ if utf8proc.found()
|
|||
endif
|
||||
|
||||
tllist = dependency('tllist', version: '>=1.1.0', fallback: 'tllist')
|
||||
fcft = dependency('fcft', version: ['>=3.0.1', '<4.0.0'], fallback: 'fcft')
|
||||
fcft = dependency('fcft', version: ['>=3.2.0', '<4.0.0'], fallback: 'fcft')
|
||||
|
||||
wayland_protocols_datadir = wayland_protocols.get_variable('pkgdatadir')
|
||||
|
||||
|
|
@ -187,6 +187,13 @@ else
|
|||
xdg_system_bell = false
|
||||
endif
|
||||
|
||||
if wayland_protocols.version().version_compare('>=1.41')
|
||||
add_project_arguments('-DHAVE_WP_COLOR_MANAGEMENT', language: 'c')
|
||||
wl_proto_xml += [wayland_protocols_datadir / 'staging/color-management/color-management-v1.xml']
|
||||
wp_color_management = true
|
||||
else
|
||||
wp_color_management = false
|
||||
endif
|
||||
|
||||
foreach prot : wl_proto_xml
|
||||
wl_proto_headers += custom_target(
|
||||
|
|
@ -228,6 +235,13 @@ emoji_variation_sequences = custom_target(
|
|||
command: [python, generate_emoji_variation_sequences, '@INPUT@', '@OUTPUT@']
|
||||
)
|
||||
|
||||
generate_srgb_funcs = files('scripts/srgb.py')
|
||||
srgb_funcs = custom_target(
|
||||
'generate_srgb_funcs',
|
||||
output: ['srgb.c', 'srgb.h'],
|
||||
command: [python, generate_srgb_funcs, '@OUTPUT0@', '@OUTPUT1@']
|
||||
)
|
||||
|
||||
common = static_library(
|
||||
'common',
|
||||
'log.c', 'log.h',
|
||||
|
|
@ -260,7 +274,7 @@ vtlib = static_library(
|
|||
'osc.c', 'osc.h',
|
||||
'sixel.c', 'sixel.h',
|
||||
'vt.c', 'vt.h',
|
||||
builtin_terminfo, emoji_variation_sequences,
|
||||
builtin_terminfo, emoji_variation_sequences, srgb_funcs,
|
||||
wl_proto_src + wl_proto_headers,
|
||||
version,
|
||||
dependencies: [libepoll, pixman, fcft, tllist, wayland_client, xkb, utf8proc],
|
||||
|
|
@ -424,6 +438,7 @@ summary(
|
|||
'Grapheme clustering': utf8proc.found(),
|
||||
'Wayland: xdg-toplevel-icon-v1': xdg_toplevel_icon,
|
||||
'Wayland: xdg-system-bell-v1': xdg_system_bell,
|
||||
'Wayland: wp-color-management-v1': wp_color_management,
|
||||
'utmp backend': utmp_backend,
|
||||
'utmp helper default path': utmp_default_helper_path,
|
||||
'Build terminfo': tic.found(),
|
||||
|
|
|
|||
10
pgo/pgo.c
10
pgo/pgo.c
|
|
@ -128,6 +128,12 @@ render_worker_thread(void *_ctx)
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
render_do_linear_blending(const struct terminal *term)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
struct extraction_context *
|
||||
extract_begin(enum selection_kind kind, bool strip_trailing_empty)
|
||||
{
|
||||
|
|
@ -197,7 +203,9 @@ void shm_unref(struct buffer *buf) {}
|
|||
void shm_chain_free(struct buffer_chain *chain) {}
|
||||
|
||||
struct buffer_chain *
|
||||
shm_chain_new(struct wl_shm *shm, bool scrollable, size_t pix_instances)
|
||||
shm_chain_new(
|
||||
struct wayland *wayl, bool scrollable, size_t pix_instances,
|
||||
bool ten_bit_it_if_capable)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
|||
1
quirks.c
1
quirks.c
|
|
@ -86,6 +86,7 @@ is_sway(void)
|
|||
void
|
||||
quirk_sway_subsurface_unmap(struct terminal *term)
|
||||
{
|
||||
return;
|
||||
if (!is_sway())
|
||||
return;
|
||||
|
||||
|
|
|
|||
171
render.c
171
render.c
|
|
@ -44,6 +44,7 @@
|
|||
#include "selection.h"
|
||||
#include "shm.h"
|
||||
#include "sixel.h"
|
||||
#include "srgb.h"
|
||||
#include "url-mode.h"
|
||||
#include "util.h"
|
||||
#include "xmalloc.h"
|
||||
|
|
@ -232,22 +233,45 @@ attrs_to_font(const struct terminal *term, const struct attributes *attrs)
|
|||
return term->fonts[idx];
|
||||
}
|
||||
|
||||
static inline pixman_color_t
|
||||
color_hex_to_pixman_with_alpha(uint32_t color, uint16_t alpha)
|
||||
static pixman_color_t
|
||||
color_hex_to_pixman_srgb(uint32_t color, uint16_t alpha)
|
||||
{
|
||||
return (pixman_color_t){
|
||||
.red = ((color >> 16 & 0xff) | (color >> 8 & 0xff00)) * alpha / 0xffff,
|
||||
.green = ((color >> 8 & 0xff) | (color >> 0 & 0xff00)) * alpha / 0xffff,
|
||||
.blue = ((color >> 0 & 0xff) | (color << 8 & 0xff00)) * alpha / 0xffff,
|
||||
.alpha = alpha,
|
||||
.alpha = alpha, /* Consider alpha linear already? */
|
||||
.red = srgb_decode_8_to_16((color >> 16) & 0xff),
|
||||
.green = srgb_decode_8_to_16((color >> 8) & 0xff),
|
||||
.blue = srgb_decode_8_to_16((color >> 0) & 0xff),
|
||||
};
|
||||
}
|
||||
|
||||
static inline pixman_color_t
|
||||
color_hex_to_pixman(uint32_t color)
|
||||
color_hex_to_pixman_with_alpha(uint32_t color, uint16_t alpha, bool srgb)
|
||||
{
|
||||
pixman_color_t ret;
|
||||
|
||||
if (srgb)
|
||||
ret = color_hex_to_pixman_srgb(color, alpha);
|
||||
else {
|
||||
ret = (pixman_color_t){
|
||||
.red = ((color >> 16 & 0xff) | (color >> 8 & 0xff00)),
|
||||
.green = ((color >> 8 & 0xff) | (color >> 0 & 0xff00)),
|
||||
.blue = ((color >> 0 & 0xff) | (color << 8 & 0xff00)),
|
||||
.alpha = alpha,
|
||||
};
|
||||
}
|
||||
|
||||
ret.red = (uint32_t)ret.red * alpha / 0xffff;
|
||||
ret.green = (uint32_t)ret.green * alpha / 0xffff;
|
||||
ret.blue = (uint32_t)ret.blue * alpha / 0xffff;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline pixman_color_t
|
||||
color_hex_to_pixman(uint32_t color, bool srgb)
|
||||
{
|
||||
/* Count on the compiler optimizing this */
|
||||
return color_hex_to_pixman_with_alpha(color, 0xffff);
|
||||
return color_hex_to_pixman_with_alpha(color, 0xffff, srgb);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
|
|
@ -568,23 +592,24 @@ draw_strikeout(const struct terminal *term, pixman_image_t *pix,
|
|||
|
||||
static void
|
||||
cursor_colors_for_cell(const struct terminal *term, const struct cell *cell,
|
||||
const pixman_color_t *fg, const pixman_color_t *bg,
|
||||
pixman_color_t *cursor_color, pixman_color_t *text_color)
|
||||
const pixman_color_t *fg, const pixman_color_t *bg,
|
||||
pixman_color_t *cursor_color, pixman_color_t *text_color,
|
||||
bool gamma_correct)
|
||||
{
|
||||
if (term->colors.cursor_bg >> 31)
|
||||
*cursor_color = color_hex_to_pixman(term->colors.cursor_bg);
|
||||
*cursor_color = color_hex_to_pixman(term->colors.cursor_bg, gamma_correct);
|
||||
else
|
||||
*cursor_color = *fg;
|
||||
|
||||
if (term->colors.cursor_fg >> 31)
|
||||
*text_color = color_hex_to_pixman(term->colors.cursor_fg);
|
||||
*text_color = color_hex_to_pixman(term->colors.cursor_fg, gamma_correct);
|
||||
else {
|
||||
*text_color = *bg;
|
||||
|
||||
if (unlikely(text_color->alpha != 0xffff)) {
|
||||
/* The *only* color that can have transparency is the
|
||||
* default background color */
|
||||
*text_color = color_hex_to_pixman(term->colors.bg);
|
||||
*text_color = color_hex_to_pixman(term->colors.bg, gamma_correct);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -592,8 +617,8 @@ cursor_colors_for_cell(const struct terminal *term, const struct cell *cell,
|
|||
text_color->green == cursor_color->green &&
|
||||
text_color->blue == cursor_color->blue)
|
||||
{
|
||||
*text_color = color_hex_to_pixman(term->colors.bg);
|
||||
*cursor_color = color_hex_to_pixman(term->colors.fg);
|
||||
*text_color = color_hex_to_pixman(term->colors.bg, gamma_correct);
|
||||
*cursor_color = color_hex_to_pixman(term->colors.fg, gamma_correct);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -604,7 +629,8 @@ draw_cursor(const struct terminal *term, const struct cell *cell,
|
|||
{
|
||||
pixman_color_t cursor_color;
|
||||
pixman_color_t text_color;
|
||||
cursor_colors_for_cell(term, cell, fg, bg, &cursor_color, &text_color);
|
||||
cursor_colors_for_cell(term, cell, fg, bg, &cursor_color, &text_color,
|
||||
render_do_linear_blending(term));
|
||||
|
||||
if (unlikely(!term->kbd_focus)) {
|
||||
switch (term->conf->cursor.unfocused_style) {
|
||||
|
|
@ -656,8 +682,9 @@ draw_cursor(const struct terminal *term, const struct cell *cell,
|
|||
}
|
||||
|
||||
static int
|
||||
render_cell(struct terminal *term, pixman_image_t *pix, pixman_region32_t *damage,
|
||||
struct row *row, int row_no, int col, bool has_cursor)
|
||||
render_cell(struct terminal *term, pixman_image_t *pix,
|
||||
pixman_region32_t *damage, struct row *row, int row_no, int col,
|
||||
bool has_cursor)
|
||||
{
|
||||
struct cell *cell = &row->cells[col];
|
||||
if (cell->attrs.clean)
|
||||
|
|
@ -776,8 +803,9 @@ render_cell(struct terminal *term, pixman_image_t *pix, pixman_region32_t *damag
|
|||
if (cell->attrs.blink && term->blink.state == BLINK_OFF)
|
||||
_fg = color_decrease_luminance(_fg);
|
||||
|
||||
pixman_color_t fg = color_hex_to_pixman(_fg);
|
||||
pixman_color_t bg = color_hex_to_pixman_with_alpha(_bg, alpha);
|
||||
const bool gamma_correct = render_do_linear_blending(term);
|
||||
pixman_color_t fg = color_hex_to_pixman(_fg, gamma_correct);
|
||||
pixman_color_t bg = color_hex_to_pixman_with_alpha(_bg, alpha, gamma_correct);
|
||||
|
||||
struct fcft_font *font = attrs_to_font(term, &cell->attrs);
|
||||
const struct composed *composed = NULL;
|
||||
|
|
@ -987,7 +1015,7 @@ render_cell(struct terminal *term, pixman_image_t *pix, pixman_region32_t *damag
|
|||
if (i > 0 && glyph->x >= 0 && cell_cols == 1)
|
||||
g_x -= term->cell_width;
|
||||
|
||||
if (unlikely(pixman_image_get_format(glyph->pix) == PIXMAN_a8r8g8b8)) {
|
||||
if (unlikely(glyph->is_color_glyph)) {
|
||||
/* Glyph surface is a pre-rendered image (typically a color emoji...) */
|
||||
if (!(cell->attrs.blink && term->blink.state == BLINK_OFF)) {
|
||||
pixman_image_composite32(
|
||||
|
|
@ -1071,12 +1099,12 @@ render_cell(struct terminal *term, pixman_image_t *pix, pixman_region32_t *damag
|
|||
switch (range->underline.color_src) {
|
||||
case COLOR_BASE256:
|
||||
underline_color = color_hex_to_pixman(
|
||||
term->colors.table[range->underline.color]);
|
||||
term->colors.table[range->underline.color], gamma_correct);
|
||||
break;
|
||||
|
||||
case COLOR_RGB:
|
||||
underline_color =
|
||||
color_hex_to_pixman(range->underline.color);
|
||||
color_hex_to_pixman(range->underline.color, gamma_correct);
|
||||
break;
|
||||
|
||||
case COLOR_DEFAULT:
|
||||
|
|
@ -1105,8 +1133,8 @@ render_cell(struct terminal *term, pixman_image_t *pix, pixman_region32_t *damag
|
|||
pixman_color_t url_color = color_hex_to_pixman(
|
||||
term->conf->colors.use_custom.url
|
||||
? term->conf->colors.url
|
||||
: term->colors.table[3]
|
||||
);
|
||||
: term->colors.table[3],
|
||||
gamma_correct);
|
||||
draw_underline(term, pix, font, &url_color, x, y, cell_cols);
|
||||
}
|
||||
|
||||
|
|
@ -1119,8 +1147,9 @@ draw_cursor:
|
|||
}
|
||||
|
||||
static void
|
||||
render_row(struct terminal *term, pixman_image_t *pix, pixman_region32_t *damage,
|
||||
struct row *row, int row_no, int cursor_col)
|
||||
render_row(struct terminal *term, pixman_image_t *pix,
|
||||
pixman_region32_t *damage, struct row *row,
|
||||
int row_no, int cursor_col)
|
||||
{
|
||||
for (int col = term->cols - 1; col >= 0; col--)
|
||||
render_cell(term, pix, damage, row, row_no, col, cursor_col == col);
|
||||
|
|
@ -1130,7 +1159,7 @@ static void
|
|||
render_urgency(struct terminal *term, struct buffer *buf)
|
||||
{
|
||||
uint32_t red = term->colors.table[1];
|
||||
pixman_color_t bg = color_hex_to_pixman(red);
|
||||
pixman_color_t bg = color_hex_to_pixman(red, render_do_linear_blending(term));
|
||||
|
||||
int width = min(min(term->margins.left, term->margins.right),
|
||||
min(term->margins.top, term->margins.bottom));
|
||||
|
|
@ -1161,6 +1190,7 @@ render_margin(struct terminal *term, struct buffer *buf,
|
|||
const int bmargin = term->height - term->margins.bottom;
|
||||
const int line_count = end_line - start_line;
|
||||
|
||||
const bool gamma_correct = render_do_linear_blending(term);
|
||||
const uint32_t _bg = !term->reverse ? term->colors.bg : term->colors.fg;
|
||||
uint16_t alpha = term->colors.alpha;
|
||||
|
||||
|
|
@ -1169,7 +1199,7 @@ render_margin(struct terminal *term, struct buffer *buf,
|
|||
alpha = 0xffff;
|
||||
}
|
||||
|
||||
pixman_color_t bg = color_hex_to_pixman_with_alpha(_bg, alpha);
|
||||
pixman_color_t bg = color_hex_to_pixman_with_alpha(_bg, alpha, gamma_correct);
|
||||
|
||||
pixman_image_fill_rectangles(
|
||||
PIXMAN_OP_SRC, buf->pix[0], &bg, 4,
|
||||
|
|
@ -1596,8 +1626,7 @@ render_sixel(struct terminal *term, pixman_image_t *pix,
|
|||
|
||||
static void
|
||||
render_sixel_images(struct terminal *term, pixman_image_t *pix,
|
||||
pixman_region32_t *damage,
|
||||
const struct coord *cursor)
|
||||
pixman_region32_t *damage, const struct coord *cursor)
|
||||
{
|
||||
if (likely(tll_length(term->grid->sixel_images)) == 0)
|
||||
return;
|
||||
|
|
@ -1649,6 +1678,8 @@ render_ime_preedit_for_seat(struct terminal *term, struct seat *seat,
|
|||
if (unlikely(term->is_searching))
|
||||
return;
|
||||
|
||||
const bool gamma_correct = render_do_linear_blending(term);
|
||||
|
||||
/* Adjust cursor position to viewport */
|
||||
struct coord cursor;
|
||||
cursor = term->grid->cursor.point;
|
||||
|
|
@ -1753,12 +1784,12 @@ render_ime_preedit_for_seat(struct terminal *term, struct seat *seat,
|
|||
if (!seat->ime.preedit.cursor.hidden) {
|
||||
const struct cell *start_cell = &seat->ime.preedit.cells[0];
|
||||
|
||||
pixman_color_t fg = color_hex_to_pixman(term->colors.fg);
|
||||
pixman_color_t bg = color_hex_to_pixman(term->colors.bg);
|
||||
pixman_color_t fg = color_hex_to_pixman(term->colors.fg, gamma_correct);
|
||||
pixman_color_t bg = color_hex_to_pixman(term->colors.bg, gamma_correct);
|
||||
|
||||
pixman_color_t cursor_color, text_color;
|
||||
cursor_colors_for_cell(
|
||||
term, start_cell, &fg, &bg, &cursor_color, &text_color);
|
||||
term, start_cell, &fg, &bg, &cursor_color, &text_color, gamma_correct);
|
||||
|
||||
int x = term->margins.left + (col_idx + start) * term->cell_width;
|
||||
int y = term->margins.top + row_idx * term->cell_height;
|
||||
|
|
@ -1789,12 +1820,14 @@ render_ime_preedit_for_seat(struct terminal *term, struct seat *seat,
|
|||
row->cells[col_idx + i] = real_cells[i];
|
||||
free(real_cells);
|
||||
|
||||
const int damage_x = term->margins.left + col_idx * term->cell_width;
|
||||
const int damage_y = term->margins.top + row_idx * term->cell_height;
|
||||
const int damage_w = cells_used * term->cell_width;
|
||||
const int damage_h = term->cell_height;
|
||||
|
||||
wl_surface_damage_buffer(
|
||||
term->window->surface.surf,
|
||||
term->margins.left,
|
||||
term->margins.top + row_idx * term->cell_height,
|
||||
term->width - term->margins.left - term->margins.right,
|
||||
1 * term->cell_height);
|
||||
damage_x, damage_y, damage_w, damage_h);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -1916,7 +1949,7 @@ render_overlay(struct terminal *term)
|
|||
case OVERLAY_FLASH:
|
||||
color = color_hex_to_pixman_with_alpha(
|
||||
term->conf->colors.flash,
|
||||
term->conf->colors.flash_alpha);
|
||||
term->conf->colors.flash_alpha, render_do_linear_blending(term));
|
||||
break;
|
||||
|
||||
case OVERLAY_NONE:
|
||||
|
|
@ -2118,6 +2151,7 @@ render_worker_thread(void *_ctx)
|
|||
sem_wait(start);
|
||||
|
||||
struct buffer *buf = term->render.workers.buf;
|
||||
|
||||
bool frame_done = false;
|
||||
|
||||
/* Translate offset-relative cursor row to view-relative */
|
||||
|
|
@ -2138,8 +2172,6 @@ render_worker_thread(void *_ctx)
|
|||
|
||||
switch (row_no) {
|
||||
default: {
|
||||
xassert(buf != NULL);
|
||||
|
||||
struct row *row = grid_row_in_view(term->grid, row_no);
|
||||
int cursor_col = cursor.row == row_no ? cursor.col : -1;
|
||||
|
||||
|
|
@ -2259,13 +2291,14 @@ render_osd(struct terminal *term, const struct wayl_sub_surface *sub_surf,
|
|||
pixman_image_set_clip_region32(buf->pix[0], &clip);
|
||||
pixman_region32_fini(&clip);
|
||||
|
||||
const bool gamma_correct = render_do_linear_blending(term);
|
||||
uint16_t alpha = _bg >> 24 | (_bg >> 24 << 8);
|
||||
pixman_color_t bg = color_hex_to_pixman_with_alpha(_bg, alpha);
|
||||
pixman_color_t bg = color_hex_to_pixman_with_alpha(_bg, alpha, gamma_correct);
|
||||
pixman_image_fill_rectangles(
|
||||
PIXMAN_OP_SRC, buf->pix[0], &bg, 1,
|
||||
&(pixman_rectangle16_t){0, 0, buf->width, buf->height});
|
||||
|
||||
pixman_color_t fg = color_hex_to_pixman(_fg);
|
||||
pixman_color_t fg = color_hex_to_pixman(_fg, gamma_correct);
|
||||
const int x_ofs = term->font_x_ofs;
|
||||
|
||||
const size_t len = c32len(text);
|
||||
|
|
@ -2312,7 +2345,7 @@ render_osd(struct terminal *term, const struct wayl_sub_surface *sub_surf,
|
|||
for (size_t i = 0; i < glyph_count; i++) {
|
||||
const struct fcft_glyph *glyph = glyphs[i];
|
||||
|
||||
if (pixman_image_get_format(glyph->pix) == PIXMAN_a8r8g8b8) {
|
||||
if (unlikely(glyph->is_color_glyph)) {
|
||||
pixman_image_composite32(
|
||||
PIXMAN_OP_OVER, glyph->pix, NULL, buf->pix[0], 0, 0, 0, 0,
|
||||
x + x_ofs + glyph->x, y - glyph->y,
|
||||
|
|
@ -2399,8 +2432,11 @@ render_csd_border(struct terminal *term, enum csd_surface surf_idx,
|
|||
if (info->width == 0 || info->height == 0)
|
||||
return;
|
||||
|
||||
const bool gamma_correct = render_do_linear_blending(term);
|
||||
|
||||
{
|
||||
pixman_color_t color = color_hex_to_pixman_with_alpha(0, 0);
|
||||
/* Fully transparent - no need to do a color space transform */
|
||||
pixman_color_t color = color_hex_to_pixman_with_alpha(0, 0, gamma_correct);
|
||||
render_csd_part(term, surf->surf, buf, info->width, info->height, &color);
|
||||
}
|
||||
|
||||
|
|
@ -2461,7 +2497,8 @@ render_csd_border(struct terminal *term, enum csd_surface surf_idx,
|
|||
_color = color_dim(term, _color);
|
||||
|
||||
uint16_t alpha = _color >> 24 | (_color >> 24 << 8);
|
||||
pixman_color_t color = color_hex_to_pixman_with_alpha(_color, alpha);
|
||||
pixman_color_t color =
|
||||
color_hex_to_pixman_with_alpha(_color, alpha, gamma_correct);
|
||||
|
||||
pixman_image_fill_rectangles(
|
||||
PIXMAN_OP_SRC, buf->pix[0], &color, 1,
|
||||
|
|
@ -2472,8 +2509,9 @@ render_csd_border(struct terminal *term, enum csd_surface surf_idx,
|
|||
}
|
||||
|
||||
static pixman_color_t
|
||||
get_csd_button_fg_color(const struct config *conf)
|
||||
get_csd_button_fg_color(const struct terminal *term)
|
||||
{
|
||||
const struct config *conf = term->conf;
|
||||
uint32_t _color = conf->colors.bg;
|
||||
uint16_t alpha = 0xffff;
|
||||
|
||||
|
|
@ -2482,13 +2520,14 @@ get_csd_button_fg_color(const struct config *conf)
|
|||
alpha = _color >> 24 | (_color >> 24 << 8);
|
||||
}
|
||||
|
||||
return color_hex_to_pixman_with_alpha(_color, alpha);
|
||||
return color_hex_to_pixman_with_alpha(
|
||||
_color, alpha, render_do_linear_blending(term));
|
||||
}
|
||||
|
||||
static void
|
||||
render_csd_button_minimize(struct terminal *term, struct buffer *buf)
|
||||
{
|
||||
pixman_color_t color = get_csd_button_fg_color(term->conf);
|
||||
pixman_color_t color = get_csd_button_fg_color(term);
|
||||
pixman_image_t *src = pixman_image_create_solid_fill(&color);
|
||||
|
||||
const int max_height = buf->height / 3;
|
||||
|
|
@ -2516,7 +2555,7 @@ static void
|
|||
render_csd_button_maximize_maximized(
|
||||
struct terminal *term, struct buffer *buf)
|
||||
{
|
||||
pixman_color_t color = get_csd_button_fg_color(term->conf);
|
||||
pixman_color_t color = get_csd_button_fg_color(term);
|
||||
pixman_image_t *src = pixman_image_create_solid_fill(&color);
|
||||
|
||||
const int max_height = buf->height / 3;
|
||||
|
|
@ -2548,7 +2587,7 @@ static void
|
|||
render_csd_button_maximize_window(
|
||||
struct terminal *term, struct buffer *buf)
|
||||
{
|
||||
pixman_color_t color = get_csd_button_fg_color(term->conf);
|
||||
pixman_color_t color = get_csd_button_fg_color(term);
|
||||
pixman_image_t *src = pixman_image_create_solid_fill(&color);
|
||||
|
||||
const int max_height = buf->height / 3;
|
||||
|
|
@ -2588,7 +2627,7 @@ render_csd_button_maximize(struct terminal *term, struct buffer *buf)
|
|||
static void
|
||||
render_csd_button_close(struct terminal *term, struct buffer *buf)
|
||||
{
|
||||
pixman_color_t color = get_csd_button_fg_color(term->conf);
|
||||
pixman_color_t color = get_csd_button_fg_color(term);
|
||||
pixman_image_t *src = pixman_image_create_solid_fill(&color);
|
||||
|
||||
const int max_height = buf->height / 3;
|
||||
|
|
@ -2759,14 +2798,14 @@ render_csd_button(struct terminal *term, enum csd_surface surf_idx,
|
|||
if (!term->visual_focus)
|
||||
_color = color_dim(term, _color);
|
||||
|
||||
pixman_color_t color = color_hex_to_pixman_with_alpha(_color, alpha);
|
||||
const bool gamma_correct = render_do_linear_blending(term);
|
||||
pixman_color_t color = color_hex_to_pixman_with_alpha(_color, alpha, gamma_correct);
|
||||
render_csd_part(term, surf->surf, buf, info->width, info->height, &color);
|
||||
|
||||
switch (surf_idx) {
|
||||
case CSD_SURF_MINIMIZE: render_csd_button_minimize(term, buf); break;
|
||||
case CSD_SURF_MAXIMIZE: render_csd_button_maximize(term, buf); break;
|
||||
case CSD_SURF_CLOSE: render_csd_button_close(term, buf); break;
|
||||
break;
|
||||
|
||||
default:
|
||||
BUG("unhandled surface type: %u", (unsigned)surf_idx);
|
||||
|
|
@ -3618,6 +3657,7 @@ render_search_box(struct terminal *term)
|
|||
: term->conf->colors.use_custom.search_box_no_match;
|
||||
|
||||
/* Background - yellow on empty/match, red on mismatch (default) */
|
||||
const bool gamma_correct = render_do_linear_blending(term);
|
||||
const pixman_color_t color = color_hex_to_pixman(
|
||||
is_match
|
||||
? (custom_colors
|
||||
|
|
@ -3625,13 +3665,14 @@ render_search_box(struct terminal *term)
|
|||
: term->colors.table[3])
|
||||
: (custom_colors
|
||||
? term->conf->colors.search_box.no_match.bg
|
||||
: term->colors.table[1]));
|
||||
: term->colors.table[1]),
|
||||
gamma_correct);
|
||||
|
||||
pixman_image_fill_rectangles(
|
||||
PIXMAN_OP_SRC, buf->pix[0], &color,
|
||||
1, &(pixman_rectangle16_t){width - visible_width, 0, visible_width, height});
|
||||
|
||||
pixman_color_t transparent = color_hex_to_pixman_with_alpha(0, 0);
|
||||
pixman_color_t transparent = color_hex_to_pixman_with_alpha(0, 0, gamma_correct);
|
||||
pixman_image_fill_rectangles(
|
||||
PIXMAN_OP_SRC, buf->pix[0], &transparent,
|
||||
1, &(pixman_rectangle16_t){0, 0, width - visible_width, height});
|
||||
|
|
@ -3641,12 +3682,14 @@ render_search_box(struct terminal *term)
|
|||
const int x_ofs = term->font_x_ofs;
|
||||
int x = x_left;
|
||||
int y = margin;
|
||||
|
||||
pixman_color_t fg = color_hex_to_pixman(
|
||||
custom_colors
|
||||
? (is_match
|
||||
? term->conf->colors.search_box.match.fg
|
||||
: term->conf->colors.search_box.no_match.fg)
|
||||
: term->colors.table[0]);
|
||||
: term->colors.table[0],
|
||||
gamma_correct);
|
||||
|
||||
/* Move offset we start rendering at, to ensure the cursor is visible */
|
||||
for (size_t i = 0, cell_idx = 0; i <= term->search.cursor; cell_idx += widths[i], i++) {
|
||||
|
|
@ -3802,8 +3845,7 @@ render_search_box(struct terminal *term)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (unlikely(pixman_image_get_format(glyph->pix) == PIXMAN_a8r8g8b8)) {
|
||||
/* Glyph surface is a pre-rendered image (typically a color emoji...) */
|
||||
if (unlikely(glyph->is_color_glyph)) {
|
||||
pixman_image_composite32(
|
||||
PIXMAN_OP_OVER, glyph->pix, NULL, buf->pix[0], 0, 0, 0, 0,
|
||||
x + x_ofs + glyph->x, y + term->font_baseline - glyph->y,
|
||||
|
|
@ -5186,3 +5228,14 @@ render_xcursor_set(struct seat *seat, struct terminal *term,
|
|||
seat->pointer.xcursor_pending = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
render_do_linear_blending(const struct terminal *term)
|
||||
{
|
||||
#if defined(HAVE_WP_COLOR_MANAGEMENT)
|
||||
return term->conf->gamma_correct != GAMMA_CORRECT_DISABLED &&
|
||||
term->wl->color_management.img_description != NULL;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
2
render.h
2
render.h
|
|
@ -47,3 +47,5 @@ struct csd_data {
|
|||
};
|
||||
|
||||
struct csd_data get_csd_data(const struct terminal *term, enum csd_surface surf_idx);
|
||||
|
||||
bool render_do_linear_blending(const struct terminal *term);
|
||||
|
|
|
|||
90
scripts/srgb.py
Executable file
90
scripts/srgb.py
Executable file
|
|
@ -0,0 +1,90 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import math
|
||||
import sys
|
||||
|
||||
|
||||
def srgb_to_linear(f: float) -> float:
|
||||
assert(f >= 0 and f <= 1.0)
|
||||
|
||||
if f <= 0.04045:
|
||||
return f / 12.92
|
||||
|
||||
return math.pow((f + 0.055) / 1.055, 2.4)
|
||||
|
||||
|
||||
def linear_to_srgb(f: float) -> float:
|
||||
if f < 0.0031308:
|
||||
return f * 12.92
|
||||
|
||||
return 1.055 * math.pow(f, 1 / 2.4) - 0.055
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('c_output', type=argparse.FileType('w'))
|
||||
parser.add_argument('h_output', type=argparse.FileType('w'))
|
||||
opts = parser.parse_args()
|
||||
|
||||
linear_table: list[int] = []
|
||||
srgb_table: list[int] = []
|
||||
|
||||
for i in range(256):
|
||||
linear_table.append(int(srgb_to_linear(float(i) / 255) * 65535 + 0.5))
|
||||
|
||||
for i in range(4096):
|
||||
srgb_table.append(int(linear_to_srgb(float(i) / 4095) * 255 + 0.5))
|
||||
|
||||
for i in range(256):
|
||||
while True:
|
||||
linear = linear_table[i]
|
||||
srgb = srgb_table[linear >> 4]
|
||||
|
||||
if i == srgb:
|
||||
break
|
||||
|
||||
linear_table[i] += 1
|
||||
|
||||
|
||||
opts.h_output.write("#pragma once\n")
|
||||
opts.h_output.write("#include <stdint.h>\n")
|
||||
opts.h_output.write("\n")
|
||||
opts.h_output.write('/* 8-bit input, 16-bit output */\n')
|
||||
opts.h_output.write("extern const uint16_t srgb_decode_8_to_16_table[256];")
|
||||
|
||||
opts.h_output.write('\n')
|
||||
opts.h_output.write('static inline uint16_t\n')
|
||||
opts.h_output.write('srgb_decode_8_to_16(uint8_t v)\n')
|
||||
opts.h_output.write('{\n')
|
||||
opts.h_output.write(' return srgb_decode_8_to_16_table[v];\n')
|
||||
opts.h_output.write('}\n')
|
||||
|
||||
opts.h_output.write('\n')
|
||||
opts.h_output.write('/* 8-bit input, 8-bit output */\n')
|
||||
opts.h_output.write("extern const uint8_t srgb_decode_8_to_8_table[256];\n")
|
||||
|
||||
opts.h_output.write('\n')
|
||||
opts.h_output.write('static inline uint8_t\n')
|
||||
opts.h_output.write('srgb_decode_8_to_8(uint8_t v)\n')
|
||||
opts.h_output.write('{\n')
|
||||
opts.h_output.write(' return srgb_decode_8_to_8_table[v];\n')
|
||||
opts.h_output.write('}\n')
|
||||
|
||||
opts.c_output.write('#include "srgb.h"\n')
|
||||
opts.c_output.write('\n')
|
||||
|
||||
opts.c_output.write("const uint16_t srgb_decode_8_to_16_table[256] = {\n")
|
||||
for i in range(256):
|
||||
opts.c_output.write(f' {linear_table[i]},\n')
|
||||
opts.c_output.write('};\n')
|
||||
|
||||
opts.c_output.write("const uint8_t srgb_decode_8_to_8_table[256] = {\n")
|
||||
for i in range(256):
|
||||
opts.c_output.write(f' {linear_table[i] >> 8},\n')
|
||||
opts.c_output.write('};\n')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
80
shm.c
80
shm.c
|
|
@ -92,6 +92,12 @@ struct buffer_chain {
|
|||
struct wl_shm *shm;
|
||||
size_t pix_instances;
|
||||
bool scrollable;
|
||||
|
||||
pixman_format_code_t pixman_fmt_without_alpha;
|
||||
enum wl_shm_format shm_format_without_alpha;
|
||||
|
||||
pixman_format_code_t pixman_fmt_with_alpha;
|
||||
enum wl_shm_format shm_format_with_alpha;
|
||||
};
|
||||
|
||||
static tll(struct buffer_private *) deferred;
|
||||
|
|
@ -115,6 +121,7 @@ buffer_destroy_dont_close(struct buffer *buf)
|
|||
if (buf->pix[i] != NULL)
|
||||
pixman_image_unref(buf->pix[i]);
|
||||
}
|
||||
|
||||
if (buf->wl_buf != NULL)
|
||||
wl_buffer_destroy(buf->wl_buf);
|
||||
|
||||
|
|
@ -262,7 +269,9 @@ instantiate_offset(struct buffer_private *buf, off_t new_offset)
|
|||
wl_buf = wl_shm_pool_create_buffer(
|
||||
pool->wl_pool, new_offset,
|
||||
buf->public.width, buf->public.height, buf->public.stride,
|
||||
buf->with_alpha ? WL_SHM_FORMAT_ARGB8888 : WL_SHM_FORMAT_XRGB8888);
|
||||
buf->with_alpha
|
||||
? buf->chain->shm_format_with_alpha
|
||||
: buf->chain->shm_format_without_alpha);
|
||||
|
||||
if (wl_buf == NULL) {
|
||||
LOG_ERR("failed to create SHM buffer");
|
||||
|
|
@ -272,9 +281,12 @@ instantiate_offset(struct buffer_private *buf, off_t new_offset)
|
|||
/* One pixman image for each worker thread (do we really need multiple?) */
|
||||
for (size_t i = 0; i < buf->public.pix_instances; i++) {
|
||||
pix[i] = pixman_image_create_bits_no_clear(
|
||||
buf->with_alpha ? PIXMAN_a8r8g8b8 : PIXMAN_x8r8g8b8,
|
||||
buf->with_alpha
|
||||
? buf->chain->pixman_fmt_with_alpha
|
||||
: buf->chain->pixman_fmt_without_alpha,
|
||||
buf->public.width, buf->public.height,
|
||||
(uint32_t *)mmapped, buf->public.stride);
|
||||
|
||||
if (pix[i] == NULL) {
|
||||
LOG_ERR("failed to create pixman image");
|
||||
goto err;
|
||||
|
|
@ -959,14 +971,74 @@ shm_unref(struct buffer *_buf)
|
|||
}
|
||||
|
||||
struct buffer_chain *
|
||||
shm_chain_new(struct wl_shm *shm, bool scrollable, size_t pix_instances)
|
||||
shm_chain_new(struct wayland *wayl, bool scrollable, size_t pix_instances,
|
||||
bool ten_bit_if_capable)
|
||||
{
|
||||
pixman_format_code_t pixman_fmt_without_alpha = PIXMAN_x8r8g8b8;
|
||||
enum wl_shm_format shm_fmt_without_alpha = WL_SHM_FORMAT_XRGB8888;
|
||||
|
||||
pixman_format_code_t pixman_fmt_with_alpha = PIXMAN_a8r8g8b8;
|
||||
enum wl_shm_format shm_fmt_with_alpha = WL_SHM_FORMAT_ARGB8888;
|
||||
|
||||
static bool have_logged = false;
|
||||
|
||||
|
||||
if (ten_bit_if_capable) {
|
||||
if (wayl->shm_have_argb2101010 && wayl->shm_have_xrgb2101010) {
|
||||
pixman_fmt_without_alpha = PIXMAN_x2r10g10b10;
|
||||
shm_fmt_without_alpha = WL_SHM_FORMAT_XRGB2101010;
|
||||
|
||||
pixman_fmt_with_alpha = PIXMAN_a2r10g10b10;
|
||||
shm_fmt_with_alpha = WL_SHM_FORMAT_ARGB2101010;
|
||||
|
||||
if (!have_logged) {
|
||||
have_logged = true;
|
||||
LOG_INFO("using 10-bit RGB surfaces");
|
||||
}
|
||||
}
|
||||
|
||||
else if (wayl->shm_have_abgr2101010 && wayl->shm_have_xbgr2101010) {
|
||||
pixman_fmt_without_alpha = PIXMAN_x2b10g10r10;
|
||||
shm_fmt_without_alpha = WL_SHM_FORMAT_XBGR2101010;
|
||||
|
||||
pixman_fmt_with_alpha = PIXMAN_a2b10g10r10;
|
||||
shm_fmt_with_alpha = WL_SHM_FORMAT_ABGR2101010;
|
||||
|
||||
if (!have_logged) {
|
||||
have_logged = true;
|
||||
LOG_INFO("using 10-bit BGR surfaces");
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
if (!have_logged) {
|
||||
have_logged = true;
|
||||
|
||||
LOG_WARN(
|
||||
"10-bit surfaces requested, but compositor does not "
|
||||
"implement ARGB2101010+XRGB2101010, or "
|
||||
"ABGR2101010+XBGR2101010. Falling back to 8-bit surfaces");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!have_logged) {
|
||||
have_logged = true;
|
||||
LOG_INFO("using 8-bit RGB surfaces");
|
||||
}
|
||||
}
|
||||
|
||||
struct buffer_chain *chain = xmalloc(sizeof(*chain));
|
||||
*chain = (struct buffer_chain){
|
||||
.bufs = tll_init(),
|
||||
.shm = shm,
|
||||
.shm = wayl->shm,
|
||||
.pix_instances = pix_instances,
|
||||
.scrollable = scrollable,
|
||||
|
||||
.pixman_fmt_without_alpha = pixman_fmt_without_alpha,
|
||||
.shm_format_without_alpha = shm_fmt_without_alpha,
|
||||
|
||||
.pixman_fmt_with_alpha = pixman_fmt_with_alpha,
|
||||
.shm_format_with_alpha = shm_fmt_with_alpha,
|
||||
};
|
||||
return chain;
|
||||
}
|
||||
|
|
|
|||
5
shm.h
5
shm.h
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
#include <tllist.h>
|
||||
|
||||
#include "wayland.h"
|
||||
|
||||
struct damage;
|
||||
|
||||
struct buffer {
|
||||
|
|
@ -43,7 +45,8 @@ void shm_set_max_pool_size(off_t max_pool_size);
|
|||
|
||||
struct buffer_chain;
|
||||
struct buffer_chain *shm_chain_new(
|
||||
struct wl_shm *shm, bool scrollable, size_t pix_instances);
|
||||
struct wayland *wayl, bool scrollable, size_t pix_instances,
|
||||
bool ten_bit_it_if_capable);
|
||||
void shm_chain_free(struct buffer_chain *chain);
|
||||
|
||||
/*
|
||||
|
|
|
|||
97
sixel.c
97
sixel.c
|
|
@ -10,6 +10,7 @@
|
|||
#include "grid.h"
|
||||
#include "hsl.h"
|
||||
#include "render.h"
|
||||
#include "srgb.h"
|
||||
#include "util.h"
|
||||
#include "xmalloc.h"
|
||||
#include "xsnprintf.h"
|
||||
|
|
@ -19,6 +20,40 @@ static size_t count;
|
|||
static void sixel_put_generic(struct terminal *term, uint8_t c);
|
||||
static void sixel_put_ar_11(struct terminal *term, uint8_t c);
|
||||
|
||||
static uint32_t
|
||||
color_decode_srgb(const struct terminal *term, uint16_t r, uint16_t g, uint16_t b)
|
||||
{
|
||||
if (term->sixel.linear_blending) {
|
||||
if (term->sixel.use_10bit) {
|
||||
r = srgb_decode_8_to_16(r) >> 6;
|
||||
g = srgb_decode_8_to_16(g) >> 6;
|
||||
b = srgb_decode_8_to_16(b) >> 6;
|
||||
} else {
|
||||
r = srgb_decode_8_to_8(r);
|
||||
g = srgb_decode_8_to_8(g);
|
||||
b = srgb_decode_8_to_8(b);
|
||||
}
|
||||
} else {
|
||||
if (term->sixel.use_10bit) {
|
||||
r <<= 2;
|
||||
g <<= 2;
|
||||
b <<= 2;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t color;
|
||||
|
||||
if (term->sixel.use_10bit) {
|
||||
if (PIXMAN_FORMAT_TYPE(term->sixel.pixman_fmt) == PIXMAN_TYPE_ARGB)
|
||||
color = 0x3u << 30 | r << 20 | g << 10 | b;
|
||||
else
|
||||
color = 0x3u << 30 | b << 20 | g << 10 | r;
|
||||
} else
|
||||
color = 0xffu << 24 | r << 16 | g << 8 | b;
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
void
|
||||
sixel_fini(struct terminal *term)
|
||||
{
|
||||
|
|
@ -75,6 +110,23 @@ sixel_init(struct terminal *term, int p1, int p2, int p3)
|
|||
term->sixel.image.height = 0;
|
||||
term->sixel.image.alloc_height = 0;
|
||||
term->sixel.image.bottom_pixel = 0;
|
||||
term->sixel.linear_blending = render_do_linear_blending(term);
|
||||
term->sixel.pixman_fmt = PIXMAN_a8r8g8b8;
|
||||
|
||||
if (term->conf->tweak.surface_bit_depth == SHM_10_BIT) {
|
||||
if (term->wl->shm_have_argb2101010 && term->wl->shm_have_xrgb2101010) {
|
||||
term->sixel.use_10bit = true;
|
||||
term->sixel.pixman_fmt = PIXMAN_a2r10g10b10;
|
||||
}
|
||||
|
||||
else if (term->wl->shm_have_abgr2101010 && term->wl->shm_have_xbgr2101010) {
|
||||
term->sixel.use_10bit = true;
|
||||
term->sixel.pixman_fmt = PIXMAN_a2b10g10r10;
|
||||
}
|
||||
}
|
||||
|
||||
const size_t active_palette_entries = min(
|
||||
ALEN(term->conf->colors.sixel), term->sixel.palette_size);
|
||||
|
||||
if (term->sixel.use_private_palette) {
|
||||
xassert(term->sixel.private_palette == NULL);
|
||||
|
|
@ -83,11 +135,18 @@ sixel_init(struct terminal *term, int p1, int p2, int p3)
|
|||
|
||||
memcpy(
|
||||
term->sixel.private_palette, term->conf->colors.sixel,
|
||||
min(sizeof(term->conf->colors.sixel),
|
||||
term->sixel.palette_size * sizeof(term->sixel.private_palette[0])));
|
||||
active_palette_entries * sizeof(term->sixel.private_palette[0]));
|
||||
|
||||
if (term->sixel.linear_blending || term->sixel.use_10bit) {
|
||||
for (size_t i = 0; i < active_palette_entries; i++) {
|
||||
uint8_t r = (term->sixel.private_palette[i] >> 16) & 0xff;
|
||||
uint8_t g = (term->sixel.private_palette[i] >> 8) & 0xff;
|
||||
uint8_t b = (term->sixel.private_palette[i] >> 0) & 0xff;
|
||||
term->sixel.private_palette[i] = color_decode_srgb(term, r, g, b);
|
||||
}
|
||||
}
|
||||
|
||||
term->sixel.palette = term->sixel.private_palette;
|
||||
|
||||
} else {
|
||||
if (term->sixel.shared_palette == NULL) {
|
||||
term->sixel.shared_palette = xcalloc(
|
||||
|
|
@ -95,8 +154,16 @@ sixel_init(struct terminal *term, int p1, int p2, int p3)
|
|||
|
||||
memcpy(
|
||||
term->sixel.shared_palette, term->conf->colors.sixel,
|
||||
min(sizeof(term->conf->colors.sixel),
|
||||
term->sixel.palette_size * sizeof(term->sixel.shared_palette[0])));
|
||||
active_palette_entries * sizeof(term->sixel.shared_palette[0]));
|
||||
|
||||
if (term->sixel.linear_blending || term->sixel.use_10bit) {
|
||||
for (size_t i = 0; i < active_palette_entries; i++) {
|
||||
uint8_t r = (term->sixel.private_palette[i] >> 16) & 0xff;
|
||||
uint8_t g = (term->sixel.private_palette[i] >> 8) & 0xff;
|
||||
uint8_t b = (term->sixel.private_palette[i] >> 0) & 0xff;
|
||||
term->sixel.private_palette[i] = color_decode_srgb(term, r, g, b);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Shared palette - do *not* reset palette for new sixels */
|
||||
}
|
||||
|
|
@ -488,7 +555,7 @@ blend_new_image_over_old(const struct terminal *term,
|
|||
int stride = new_width * sizeof(uint32_t);
|
||||
uint32_t *new_data = xmalloc(stride * new_height);
|
||||
pixman_image_t *pix2 = pixman_image_create_bits_no_clear(
|
||||
PIXMAN_a8r8g8b8, new_width, new_height, new_data, stride);
|
||||
term->sixel.pixman_fmt, new_width, new_height, new_data, stride);
|
||||
|
||||
#if defined(_DEBUG)
|
||||
/* Fill new image with an easy-to-recognize color (green) */
|
||||
|
|
@ -651,8 +718,7 @@ sixel_overwrite(struct terminal *term, struct sixel *six,
|
|||
}
|
||||
|
||||
pixman_image_t *new_pix = pixman_image_create_bits_no_clear(
|
||||
PIXMAN_a8r8g8b8,
|
||||
new_width, new_height, new_data, new_width * sizeof(uint32_t));
|
||||
term->sixel.pixman_fmt, new_width, new_height, new_data, new_width * sizeof(uint32_t));
|
||||
|
||||
struct sixel new_six = {
|
||||
.pix = NULL,
|
||||
|
|
@ -948,7 +1014,7 @@ sixel_sync_cache(const struct terminal *term, struct sixel *six)
|
|||
|
||||
uint8_t *scaled_data = xmalloc(scaled_height * scaled_stride);
|
||||
pixman_image_t *scaled_pix = pixman_image_create_bits_no_clear(
|
||||
PIXMAN_a8r8g8b8, scaled_width, scaled_height,
|
||||
term->sixel.pixman_fmt, scaled_width, scaled_height,
|
||||
(uint32_t *)scaled_data, scaled_stride);
|
||||
|
||||
pixman_image_composite32(
|
||||
|
|
@ -1232,7 +1298,7 @@ sixel_unhook(struct terminal *term)
|
|||
image.pos.row, image.pos.row + image.rows);
|
||||
|
||||
image.original.pix = pixman_image_create_bits_no_clear(
|
||||
PIXMAN_a8r8g8b8, image.original.width, image.original.height,
|
||||
term->sixel.pixman_fmt, image.original.width, image.original.height,
|
||||
img_data, stride);
|
||||
|
||||
pixel_row_idx += height;
|
||||
|
|
@ -2006,15 +2072,14 @@ decgci(struct terminal *term, uint8_t c)
|
|||
}
|
||||
|
||||
case 2: { /* RGB */
|
||||
uint8_t r = 255 * min(c1, 100) / 100;
|
||||
uint8_t g = 255 * min(c2, 100) / 100;
|
||||
uint8_t b = 255 * min(c3, 100) / 100;
|
||||
uint16_t r = 255 * min(c1, 100) / 100;
|
||||
uint16_t g = 255 * min(c2, 100) / 100;
|
||||
uint16_t b = 255 * min(c3, 100) / 100;
|
||||
|
||||
LOG_DBG("setting palette #%d = RGB %hhu/%hhu/%hhu",
|
||||
LOG_DBG("setting palette #%d = RGB %hu/%hu/%hu",
|
||||
term->sixel.color_idx, r, g, b);
|
||||
|
||||
term->sixel.palette[term->sixel.color_idx] =
|
||||
0xffu << 24 | r << 16 | g << 8 | b;
|
||||
term->sixel.palette[term->sixel.color_idx] = color_decode_srgb(term, r, g, b);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
54
terminal.c
54
terminal.c
|
|
@ -991,6 +991,7 @@ struct font_load_data {
|
|||
const char **names;
|
||||
const char *attrs;
|
||||
|
||||
const struct fcft_font_options *options;
|
||||
struct fcft_font **font;
|
||||
};
|
||||
|
||||
|
|
@ -998,7 +999,8 @@ static int
|
|||
font_loader_thread(void *_data)
|
||||
{
|
||||
struct font_load_data *data = _data;
|
||||
*data->font = fcft_from_name(data->count, data->names, data->attrs);
|
||||
*data->font = fcft_from_name2(
|
||||
data->count, data->names, data->attrs, data->options);
|
||||
return *data->font != NULL;
|
||||
}
|
||||
|
||||
|
|
@ -1065,14 +1067,32 @@ reload_fonts(struct terminal *term, bool resize_grid)
|
|||
[1] = xstrjoin(dpi, !custom_bold ? ":weight=bold" : ""),
|
||||
[2] = xstrjoin(dpi, !custom_italic ? ":slant=italic" : ""),
|
||||
[3] = xstrjoin(dpi, !custom_bold_italic ? ":weight=bold:slant=italic" : ""),
|
||||
};
|
||||
};
|
||||
|
||||
struct fcft_font_options *options = fcft_font_options_create();
|
||||
|
||||
options->color_glyphs.format = PIXMAN_a8r8g8b8;
|
||||
options->color_glyphs.srgb_decode = render_do_linear_blending(term);
|
||||
|
||||
if (conf->tweak.surface_bit_depth == SHM_10_BIT) {
|
||||
if ((term->wl->shm_have_argb2101010 && term->wl->shm_have_xrgb2101010) ||
|
||||
(term->wl->shm_have_abgr2101010 && term->wl->shm_have_xbgr2101010))
|
||||
{
|
||||
/*
|
||||
* Use a high-res buffer type for emojis. We don't want to
|
||||
* use an a2r10g0b10 type of surface, since we need more
|
||||
* than 2 bits for alpha.
|
||||
*/
|
||||
options->color_glyphs.format = PIXMAN_rgba_float;
|
||||
}
|
||||
}
|
||||
|
||||
struct fcft_font *fonts[4];
|
||||
struct font_load_data data[4] = {
|
||||
{count_regular, names_regular, attrs[0], &fonts[0]},
|
||||
{count_bold, names_bold, attrs[1], &fonts[1]},
|
||||
{count_italic, names_italic, attrs[2], &fonts[2]},
|
||||
{count_bold_italic, names_bold_italic, attrs[3], &fonts[3]},
|
||||
{count_regular, names_regular, attrs[0], options, &fonts[0]},
|
||||
{count_bold, names_bold, attrs[1], options, &fonts[1]},
|
||||
{count_italic, names_italic, attrs[2], options, &fonts[2]},
|
||||
{count_bold_italic, names_bold_italic, attrs[3], options, &fonts[3]},
|
||||
};
|
||||
|
||||
thrd_t tids[4] = {0};
|
||||
|
|
@ -1097,6 +1117,8 @@ reload_fonts(struct terminal *term, bool resize_grid)
|
|||
success = false;
|
||||
}
|
||||
|
||||
fcft_font_options_destroy(options);
|
||||
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
for (size_t j = 0; j < counts[i]; j++)
|
||||
free(names[i][j]);
|
||||
|
|
@ -1237,6 +1259,8 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
|||
goto err;
|
||||
}
|
||||
|
||||
const bool ten_bit_surfaces = conf->tweak.surface_bit_depth == SHM_10_BIT;
|
||||
|
||||
/* Initialize configure-based terminal attributes */
|
||||
*term = (struct terminal) {
|
||||
.fdm = fdm,
|
||||
|
|
@ -1320,13 +1344,14 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
|||
.wl = wayl,
|
||||
.render = {
|
||||
.chains = {
|
||||
.grid = shm_chain_new(wayl->shm, true, 1 + conf->render_worker_count),
|
||||
.search = shm_chain_new(wayl->shm, false, 1),
|
||||
.scrollback_indicator = shm_chain_new(wayl->shm, false, 1),
|
||||
.render_timer = shm_chain_new(wayl->shm, false, 1),
|
||||
.url = shm_chain_new(wayl->shm, false, 1),
|
||||
.csd = shm_chain_new(wayl->shm, false, 1),
|
||||
.overlay = shm_chain_new(wayl->shm, false, 1),
|
||||
.grid = shm_chain_new(wayl, true, 1 + conf->render_worker_count,
|
||||
ten_bit_surfaces),
|
||||
.search = shm_chain_new(wayl, false, 1 ,ten_bit_surfaces),
|
||||
.scrollback_indicator = shm_chain_new(wayl, false, 1, ten_bit_surfaces),
|
||||
.render_timer = shm_chain_new(wayl, false, 1, ten_bit_surfaces),
|
||||
.url = shm_chain_new(wayl, false, 1, ten_bit_surfaces),
|
||||
.csd = shm_chain_new(wayl, false, 1, ten_bit_surfaces),
|
||||
.overlay = shm_chain_new(wayl, false, 1, ten_bit_surfaces),
|
||||
},
|
||||
.scrollback_lines = conf->scrollback.lines,
|
||||
.app_sync_updates.timer_fd = app_sync_updates_fd,
|
||||
|
|
@ -1468,6 +1493,9 @@ term_window_configured(struct terminal *term)
|
|||
if (!term->shutdown.in_progress) {
|
||||
xassert(term->window->is_configured);
|
||||
fdm_add(term->fdm, term->ptmx, EPOLLIN, &fdm_ptmx, term);
|
||||
|
||||
const bool gamma_correct = render_do_linear_blending(term);
|
||||
LOG_INFO("gamma-correct blending: %s", gamma_correct ? "enabled" : "disabled");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -777,6 +777,10 @@ struct terminal {
|
|||
|
||||
bool transparent_bg;
|
||||
|
||||
bool linear_blending;
|
||||
bool use_10bit;
|
||||
pixman_format_code_t pixman_fmt;
|
||||
|
||||
/* Application configurable */
|
||||
unsigned palette_size; /* Number of colors in palette */
|
||||
unsigned max_width; /* Maximum image width, in pixels */
|
||||
|
|
|
|||
182
wayland.c
182
wayland.c
|
|
@ -237,6 +237,15 @@ seat_destroy(struct seat *seat)
|
|||
static void
|
||||
shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
|
||||
{
|
||||
struct wayland *wayl = data;
|
||||
|
||||
switch (format) {
|
||||
case WL_SHM_FORMAT_XRGB2101010: wayl->shm_have_xrgb2101010 = 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_ABGR2101010: wayl->shm_have_abgr2101010 = true; break;
|
||||
}
|
||||
|
||||
#if defined(_DEBUG)
|
||||
bool have_description = false;
|
||||
|
||||
|
|
@ -666,6 +675,91 @@ static const struct wp_presentation_listener presentation_listener = {
|
|||
.clock_id = &clock_id,
|
||||
};
|
||||
|
||||
#if defined(HAVE_WP_COLOR_MANAGEMENT)
|
||||
|
||||
static void
|
||||
color_manager_create_image_description(struct wayland *wayl)
|
||||
{
|
||||
if (!wayl->color_management.have_feat_parametric)
|
||||
return;
|
||||
|
||||
if (!wayl->color_management.have_primaries_srgb)
|
||||
return;
|
||||
|
||||
if (!wayl->color_management.have_tf_ext_linear)
|
||||
return;
|
||||
|
||||
struct wp_image_description_creator_params_v1 *params =
|
||||
wp_color_manager_v1_create_parametric_creator(wayl->color_management.manager);
|
||||
|
||||
wp_image_description_creator_params_v1_set_tf_named(
|
||||
params, WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR);
|
||||
|
||||
wp_image_description_creator_params_v1_set_primaries_named(
|
||||
params, WP_COLOR_MANAGER_V1_PRIMARIES_SRGB);
|
||||
|
||||
wayl->color_management.img_description =
|
||||
wp_image_description_creator_params_v1_create(params);
|
||||
}
|
||||
|
||||
static void
|
||||
color_manager_supported_intent(void *data,
|
||||
struct wp_color_manager_v1 *wp_color_manager_v1,
|
||||
uint32_t render_intent)
|
||||
{
|
||||
struct wayland *wayl = data;
|
||||
if (render_intent == WP_COLOR_MANAGER_V1_RENDER_INTENT_PERCEPTUAL)
|
||||
wayl->color_management.have_intent_perceptual = true;
|
||||
}
|
||||
|
||||
static void
|
||||
color_manager_supported_feature(void *data,
|
||||
struct wp_color_manager_v1 *wp_color_manager_v1,
|
||||
uint32_t feature)
|
||||
{
|
||||
struct wayland *wayl = data;
|
||||
|
||||
if (feature == WP_COLOR_MANAGER_V1_FEATURE_PARAMETRIC)
|
||||
wayl->color_management.have_feat_parametric = true;
|
||||
}
|
||||
|
||||
static void
|
||||
color_manager_supported_tf_named(void *data,
|
||||
struct wp_color_manager_v1 *wp_color_manager_v1,
|
||||
uint32_t tf)
|
||||
{
|
||||
struct wayland *wayl = data;
|
||||
if (tf == WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR)
|
||||
wayl->color_management.have_tf_ext_linear = true;
|
||||
}
|
||||
|
||||
static void
|
||||
color_manager_supported_primaries_named(void *data,
|
||||
struct wp_color_manager_v1 *wp_color_manager_v1,
|
||||
uint32_t primaries)
|
||||
{
|
||||
struct wayland *wayl = data;
|
||||
if (primaries == WP_COLOR_MANAGER_V1_PRIMARIES_SRGB)
|
||||
wayl->color_management.have_primaries_srgb = true;
|
||||
}
|
||||
|
||||
static void
|
||||
color_manager_done(void *data,
|
||||
struct wp_color_manager_v1 *wp_color_manager_v1)
|
||||
{
|
||||
struct wayland *wayl = data;
|
||||
color_manager_create_image_description(wayl);
|
||||
}
|
||||
|
||||
static const struct wp_color_manager_v1_listener color_manager_listener = {
|
||||
.supported_intent = &color_manager_supported_intent,
|
||||
.supported_feature = &color_manager_supported_feature,
|
||||
.supported_primaries_named = &color_manager_supported_primaries_named,
|
||||
.supported_tf_named = &color_manager_supported_tf_named,
|
||||
.done = &color_manager_done,
|
||||
};
|
||||
#endif
|
||||
|
||||
static bool
|
||||
verify_iface_version(const char *iface, uint32_t version, uint32_t wanted)
|
||||
{
|
||||
|
|
@ -1385,6 +1479,20 @@ handle_global(void *data, struct wl_registry *registry,
|
|||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_WP_COLOR_MANAGEMENT)
|
||||
else if (streq(interface, wp_color_manager_v1_interface.name)) {
|
||||
const uint32_t required = 1;
|
||||
if (!verify_iface_version(interface, version, required))
|
||||
return;
|
||||
|
||||
wayl->color_management.manager = wl_registry_bind(
|
||||
wayl->registry, name, &wp_color_manager_v1_interface, required);
|
||||
|
||||
wp_color_manager_v1_add_listener(
|
||||
wayl->color_management.manager, &color_manager_listener, wayl);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
||||
else if (streq(interface, zwp_text_input_manager_v3_interface.name)) {
|
||||
const uint32_t required = 1;
|
||||
|
|
@ -1707,6 +1815,13 @@ wayl_destroy(struct wayland *wayl)
|
|||
zwp_text_input_manager_v3_destroy(wayl->text_input_manager);
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_WP_COLOR_MANAGEMENT)
|
||||
if (wayl->color_management.img_description != NULL)
|
||||
wp_image_description_v1_destroy(wayl->color_management.img_description);
|
||||
if (wayl->color_management.manager != NULL)
|
||||
wp_color_manager_v1_destroy(wayl->color_management.manager);
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_XDG_SYSTEM_BELL)
|
||||
if (wayl->system_bell != NULL)
|
||||
xdg_system_bell_v1_destroy(wayl->system_bell);
|
||||
|
|
@ -1847,6 +1962,38 @@ wayl_win_init(struct terminal *term, const char *token)
|
|||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_WP_COLOR_MANAGEMENT)
|
||||
if (term->conf->gamma_correct != GAMMA_CORRECT_DISABLED) {
|
||||
if (wayl->color_management.img_description != NULL) {
|
||||
xassert(wayl->color_management.manager != NULL);
|
||||
|
||||
win->surface.color_management = wp_color_manager_v1_get_surface(
|
||||
term->wl->color_management.manager, win->surface.surf);
|
||||
|
||||
wp_color_management_surface_v1_set_image_description(
|
||||
win->surface.color_management, wayl->color_management.img_description,
|
||||
WP_COLOR_MANAGER_V1_RENDER_INTENT_PERCEPTUAL);
|
||||
} else if (term->conf->gamma_correct == GAMMA_CORRECT_ENABLED) {
|
||||
if (wayl->color_management.manager == NULL) {
|
||||
LOG_WARN(
|
||||
"gamma-corrected-blending: disabling; "
|
||||
"compositor does not implement the color-management protocol");
|
||||
} else {
|
||||
LOG_WARN(
|
||||
"gamma-corrected-blending: disabling; "
|
||||
"compositor does not implement all required color-management features");
|
||||
LOG_WARN("use e.g. 'wayland-info' and verify the compositor implements:");
|
||||
LOG_WARN(" - feature: parametric");
|
||||
LOG_WARN(" - render intent: perceptual");
|
||||
LOG_WARN(" - TF: ext_linear");
|
||||
LOG_WARN(" - primaries: sRGB");
|
||||
}
|
||||
} else {
|
||||
/* "auto" - don't warn */
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (conf->csd.preferred == CONF_CSD_PREFER_NONE) {
|
||||
/* User specifically do *not* want decorations */
|
||||
win->csd_mode = CSD_NO;
|
||||
|
|
@ -1861,8 +2008,8 @@ wayl_win_init(struct terminal *term, const char *token)
|
|||
zxdg_toplevel_decoration_v1_set_mode(
|
||||
win->xdg_toplevel_decoration,
|
||||
(conf->csd.preferred == CONF_CSD_PREFER_SERVER
|
||||
? ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE
|
||||
: ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE));
|
||||
? ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE
|
||||
: ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE));
|
||||
|
||||
zxdg_toplevel_decoration_v1_add_listener(
|
||||
win->xdg_toplevel_decoration, &xdg_toplevel_decoration_listener, win);
|
||||
|
|
@ -1987,7 +2134,12 @@ wayl_win_destroy(struct wl_window *win)
|
|||
free(it->item);
|
||||
|
||||
tll_remove(win->xdg_tokens, it);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(HAVE_WP_COLOR_MANAGEMENT)
|
||||
if (win->surface.color_management != NULL)
|
||||
wp_color_management_surface_v1_destroy(win->surface.color_management);
|
||||
#endif
|
||||
|
||||
if (win->fractional_scale != NULL)
|
||||
wp_fractional_scale_v1_destroy(win->fractional_scale);
|
||||
|
|
@ -2308,6 +2460,7 @@ wayl_win_subsurface_new_with_custom_parent(
|
|||
struct wayland *wayl = win->term->wl;
|
||||
|
||||
surf->surface.surf = NULL;
|
||||
surf->surface.viewport = NULL;
|
||||
surf->sub = NULL;
|
||||
|
||||
struct wl_surface *main_surface
|
||||
|
|
@ -2318,6 +2471,22 @@ wayl_win_subsurface_new_with_custom_parent(
|
|||
return false;
|
||||
}
|
||||
|
||||
#if defined(HAVE_WP_COLOR_MANAGEMENT)
|
||||
surf->surface.color_management = NULL;
|
||||
if (win->term->conf->gamma_correct &&
|
||||
wayl->color_management.img_description != NULL)
|
||||
{
|
||||
xassert(wayl->color_management.manager != NULL);
|
||||
|
||||
surf->surface.color_management = wp_color_manager_v1_get_surface(
|
||||
wayl->color_management.manager, main_surface);
|
||||
|
||||
wp_color_management_surface_v1_set_image_description(
|
||||
surf->surface.color_management, wayl->color_management.img_description,
|
||||
WP_COLOR_MANAGER_V1_RENDER_INTENT_PERCEPTUAL);
|
||||
}
|
||||
#endif
|
||||
|
||||
struct wl_subsurface *sub = wl_subcompositor_get_subsurface(
|
||||
wayl->sub_compositor, main_surface, parent);
|
||||
|
||||
|
|
@ -2369,6 +2538,13 @@ wayl_win_subsurface_destroy(struct wayl_sub_surface *surf)
|
|||
if (surf == NULL)
|
||||
return;
|
||||
|
||||
#if defined(HAVE_WP_COLOR_MANAGEMENT)
|
||||
if (surf->surface.color_management != NULL) {
|
||||
wp_color_management_surface_v1_destroy(surf->surface.color_management);
|
||||
surf->surface.color_management = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (surf->surface.viewport != NULL) {
|
||||
wp_viewport_destroy(surf->surface.viewport);
|
||||
surf->surface.viewport = NULL;
|
||||
|
|
|
|||
23
wayland.h
23
wayland.h
|
|
@ -28,6 +28,10 @@
|
|||
#include <xdg-system-bell-v1.h>
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_WP_COLOR_MANAGEMENT)
|
||||
#include <color-management-v1.h>
|
||||
#endif
|
||||
|
||||
#include <fcft/fcft.h>
|
||||
#include <tllist.h>
|
||||
|
||||
|
|
@ -61,6 +65,9 @@ enum touch_state {
|
|||
struct wayl_surface {
|
||||
struct wl_surface *surf;
|
||||
struct wp_viewport *viewport;
|
||||
#if defined(HAVE_WP_COLOR_MANAGEMENT)
|
||||
struct wp_color_management_surface_v1 *color_management;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct wayl_sub_surface {
|
||||
|
|
@ -459,6 +466,17 @@ struct wayland {
|
|||
struct xdg_system_bell_v1 *system_bell;
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_WP_COLOR_MANAGEMENT)
|
||||
struct {
|
||||
struct wp_color_manager_v1 *manager;
|
||||
struct wp_image_description_v1 *img_description;
|
||||
bool have_intent_perceptual;
|
||||
bool have_feat_parametric;
|
||||
bool have_tf_ext_linear;
|
||||
bool have_primaries_srgb;
|
||||
} color_management;
|
||||
#endif
|
||||
|
||||
bool presentation_timings;
|
||||
struct wp_presentation *presentation;
|
||||
uint32_t presentation_clock_id;
|
||||
|
|
@ -474,6 +492,11 @@ struct wayland {
|
|||
|
||||
/* WL_SHM >= 2 */
|
||||
bool use_shm_release;
|
||||
|
||||
bool shm_have_argb2101010:1;
|
||||
bool shm_have_xrgb2101010:1;
|
||||
bool shm_have_abgr2101010:1;
|
||||
bool shm_have_xbgr2101010:1;
|
||||
};
|
||||
|
||||
struct wayland *wayl_init(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue