mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-13 04:27:47 -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-start` and
|
||||||
`search-bindings.delete-to-end` key bindings, defaulting to
|
`search-bindings.delete-to-end` key bindings, defaulting to
|
||||||
`Control+u` and `Control+k` respectively ([#1972][1972]).
|
`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
|
[1386]: https://codeberg.org/dnkl/foot/issues/1386
|
||||||
[1872]: https://codeberg.org/dnkl/foot/issues/1872
|
[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
|
* Auto-detection of URLs (i.e. not OSC-8 based URLs) are now regex
|
||||||
based.
|
based.
|
||||||
* Rename Tokyo Night Day theme to Tokyo Night Light and update colors.
|
* 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
|
[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];
|
static char buf[256];
|
||||||
snprintf(buf, sizeof(buf),
|
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,
|
FOOT_VERSION,
|
||||||
feature_pgo() ? '+' : '-',
|
feature_pgo() ? '+' : '-',
|
||||||
feature_ime() ? '+' : '-',
|
feature_ime() ? '+' : '-',
|
||||||
feature_graphemes() ? '+' : '-',
|
feature_graphemes() ? '+' : '-',
|
||||||
feature_xdg_toplevel_icon() ? '+' : '-',
|
feature_xdg_toplevel_icon() ? '+' : '-',
|
||||||
feature_xdg_system_bell() ? '+' : '-',
|
feature_xdg_system_bell() ? '+' : '-',
|
||||||
|
feature_wp_color_management() ? '+' : '-',
|
||||||
feature_assertions() ? '+' : '-');
|
feature_assertions() ? '+' : '-');
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
38
config.c
38
config.c
|
|
@ -1107,6 +1107,28 @@ parse_section_main(struct context *ctx)
|
||||||
return true;
|
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 {
|
else {
|
||||||
LOG_CONTEXTUAL_ERR("not a valid option: %s", key);
|
LOG_CONTEXTUAL_ERR("not a valid option: %s", key);
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -2767,6 +2789,16 @@ parse_section_tweak(struct context *ctx)
|
||||||
else if (streq(key, "bold-text-in-bright-amount"))
|
else if (streq(key, "bold-text-in-bright-amount"))
|
||||||
return value_to_float(ctx, &conf->bold_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 {
|
else {
|
||||||
LOG_CONTEXTUAL_ERR("not a valid option: %s", key);
|
LOG_CONTEXTUAL_ERR("not a valid option: %s", key);
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -3300,6 +3332,11 @@ config_load(struct config *conf, const char *conf_path,
|
||||||
.underline_thickness = {.pt = 0., .px = -1},
|
.underline_thickness = {.pt = 0., .px = -1},
|
||||||
.strikeout_thickness = {.pt = 0., .px = -1},
|
.strikeout_thickness = {.pt = 0., .px = -1},
|
||||||
.dpi_aware = false,
|
.dpi_aware = false,
|
||||||
|
#if defined(HAVE_WP_COLOR_MANAGEMENT)
|
||||||
|
.gamma_correct = GAMMA_CORRECT_AUTO,
|
||||||
|
#else
|
||||||
|
.gamma_correct = GAMMA_CORRECT_DISABLED,
|
||||||
|
#endif
|
||||||
.security = {
|
.security = {
|
||||||
.osc52 = OSC52_ENABLED,
|
.osc52 = OSC52_ENABLED,
|
||||||
},
|
},
|
||||||
|
|
@ -3408,6 +3445,7 @@ config_load(struct config *conf, const char *conf_path,
|
||||||
.box_drawing_solid_shades = true,
|
.box_drawing_solid_shades = true,
|
||||||
.font_monospace_warn = true,
|
.font_monospace_warn = true,
|
||||||
.sixel = true,
|
.sixel = true,
|
||||||
|
.surface_bit_depth = 8,
|
||||||
},
|
},
|
||||||
|
|
||||||
.touch = {
|
.touch = {
|
||||||
|
|
|
||||||
4
config.h
4
config.h
|
|
@ -164,6 +164,9 @@ struct config {
|
||||||
enum { STARTUP_WINDOWED, STARTUP_MAXIMIZED, STARTUP_FULLSCREEN } startup_mode;
|
enum { STARTUP_WINDOWED, STARTUP_MAXIMIZED, STARTUP_FULLSCREEN } startup_mode;
|
||||||
|
|
||||||
bool dpi_aware;
|
bool dpi_aware;
|
||||||
|
enum {GAMMA_CORRECT_DISABLED,
|
||||||
|
GAMMA_CORRECT_ENABLED,
|
||||||
|
GAMMA_CORRECT_AUTO} gamma_correct;
|
||||||
struct config_font_list fonts[4];
|
struct config_font_list fonts[4];
|
||||||
struct font_size_adjustment font_size_adjustment;
|
struct font_size_adjustment font_size_adjustment;
|
||||||
|
|
||||||
|
|
@ -397,6 +400,7 @@ struct config {
|
||||||
bool box_drawing_solid_shades;
|
bool box_drawing_solid_shades;
|
||||||
bool font_monospace_warn;
|
bool font_monospace_warn;
|
||||||
bool sixel;
|
bool sixel;
|
||||||
|
enum { SHM_8_BIT, SHM_10_BIT } surface_bit_depth;
|
||||||
} tweak;
|
} tweak;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
|
|
||||||
|
|
@ -198,6 +198,35 @@ empty string to be set, but it must be quoted: *KEY=""*)
|
||||||
|
|
||||||
Default: _unset_
|
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*
|
*box-drawings-uses-font-glyphs*
|
||||||
Boolean. When disabled, foot generates box/line drawing characters
|
Boolean. When disabled, foot generates box/line drawing characters
|
||||||
itself. The are several advantages to doing this instead of using
|
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
|
*bold-text-in-bright* is set to *yes* (the *palette-based* variant
|
||||||
is not affected by this option). Default: _1.3_.
|
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
|
# SEE ALSO
|
||||||
|
|
||||||
*foot*(1), *footclient*(1)
|
*foot*(1), *footclient*(1)
|
||||||
|
|
|
||||||
|
|
@ -55,3 +55,12 @@ static inline bool feature_xdg_system_bell(void)
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#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];
|
static char buf[256];
|
||||||
snprintf(buf, sizeof(buf),
|
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,
|
FOOT_VERSION,
|
||||||
feature_pgo() ? '+' : '-',
|
feature_pgo() ? '+' : '-',
|
||||||
feature_ime() ? '+' : '-',
|
feature_ime() ? '+' : '-',
|
||||||
feature_graphemes() ? '+' : '-',
|
feature_graphemes() ? '+' : '-',
|
||||||
feature_xdg_toplevel_icon() ? '+' : '-',
|
feature_xdg_toplevel_icon() ? '+' : '-',
|
||||||
feature_xdg_system_bell() ? '+' : '-',
|
feature_xdg_system_bell() ? '+' : '-',
|
||||||
|
feature_wp_color_management() ? '+' : '-',
|
||||||
feature_assertions() ? '+' : '-');
|
feature_assertions() ? '+' : '-');
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
19
meson.build
19
meson.build
|
|
@ -146,7 +146,7 @@ if utf8proc.found()
|
||||||
endif
|
endif
|
||||||
|
|
||||||
tllist = dependency('tllist', version: '>=1.1.0', fallback: 'tllist')
|
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')
|
wayland_protocols_datadir = wayland_protocols.get_variable('pkgdatadir')
|
||||||
|
|
||||||
|
|
@ -187,6 +187,13 @@ else
|
||||||
xdg_system_bell = false
|
xdg_system_bell = false
|
||||||
endif
|
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
|
foreach prot : wl_proto_xml
|
||||||
wl_proto_headers += custom_target(
|
wl_proto_headers += custom_target(
|
||||||
|
|
@ -228,6 +235,13 @@ emoji_variation_sequences = custom_target(
|
||||||
command: [python, generate_emoji_variation_sequences, '@INPUT@', '@OUTPUT@']
|
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 = static_library(
|
||||||
'common',
|
'common',
|
||||||
'log.c', 'log.h',
|
'log.c', 'log.h',
|
||||||
|
|
@ -260,7 +274,7 @@ vtlib = static_library(
|
||||||
'osc.c', 'osc.h',
|
'osc.c', 'osc.h',
|
||||||
'sixel.c', 'sixel.h',
|
'sixel.c', 'sixel.h',
|
||||||
'vt.c', 'vt.h',
|
'vt.c', 'vt.h',
|
||||||
builtin_terminfo, emoji_variation_sequences,
|
builtin_terminfo, emoji_variation_sequences, srgb_funcs,
|
||||||
wl_proto_src + wl_proto_headers,
|
wl_proto_src + wl_proto_headers,
|
||||||
version,
|
version,
|
||||||
dependencies: [libepoll, pixman, fcft, tllist, wayland_client, xkb, utf8proc],
|
dependencies: [libepoll, pixman, fcft, tllist, wayland_client, xkb, utf8proc],
|
||||||
|
|
@ -424,6 +438,7 @@ summary(
|
||||||
'Grapheme clustering': utf8proc.found(),
|
'Grapheme clustering': utf8proc.found(),
|
||||||
'Wayland: xdg-toplevel-icon-v1': xdg_toplevel_icon,
|
'Wayland: xdg-toplevel-icon-v1': xdg_toplevel_icon,
|
||||||
'Wayland: xdg-system-bell-v1': xdg_system_bell,
|
'Wayland: xdg-system-bell-v1': xdg_system_bell,
|
||||||
|
'Wayland: wp-color-management-v1': wp_color_management,
|
||||||
'utmp backend': utmp_backend,
|
'utmp backend': utmp_backend,
|
||||||
'utmp helper default path': utmp_default_helper_path,
|
'utmp helper default path': utmp_default_helper_path,
|
||||||
'Build terminfo': tic.found(),
|
'Build terminfo': tic.found(),
|
||||||
|
|
|
||||||
10
pgo/pgo.c
10
pgo/pgo.c
|
|
@ -128,6 +128,12 @@ render_worker_thread(void *_ctx)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
render_do_linear_blending(const struct terminal *term)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
struct extraction_context *
|
struct extraction_context *
|
||||||
extract_begin(enum selection_kind kind, bool strip_trailing_empty)
|
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) {}
|
void shm_chain_free(struct buffer_chain *chain) {}
|
||||||
|
|
||||||
struct buffer_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;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1
quirks.c
1
quirks.c
|
|
@ -86,6 +86,7 @@ is_sway(void)
|
||||||
void
|
void
|
||||||
quirk_sway_subsurface_unmap(struct terminal *term)
|
quirk_sway_subsurface_unmap(struct terminal *term)
|
||||||
{
|
{
|
||||||
|
return;
|
||||||
if (!is_sway())
|
if (!is_sway())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
||||||
171
render.c
171
render.c
|
|
@ -44,6 +44,7 @@
|
||||||
#include "selection.h"
|
#include "selection.h"
|
||||||
#include "shm.h"
|
#include "shm.h"
|
||||||
#include "sixel.h"
|
#include "sixel.h"
|
||||||
|
#include "srgb.h"
|
||||||
#include "url-mode.h"
|
#include "url-mode.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "xmalloc.h"
|
#include "xmalloc.h"
|
||||||
|
|
@ -232,22 +233,45 @@ attrs_to_font(const struct terminal *term, const struct attributes *attrs)
|
||||||
return term->fonts[idx];
|
return term->fonts[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline pixman_color_t
|
static pixman_color_t
|
||||||
color_hex_to_pixman_with_alpha(uint32_t color, uint16_t alpha)
|
color_hex_to_pixman_srgb(uint32_t color, uint16_t alpha)
|
||||||
{
|
{
|
||||||
return (pixman_color_t){
|
return (pixman_color_t){
|
||||||
.red = ((color >> 16 & 0xff) | (color >> 8 & 0xff00)) * alpha / 0xffff,
|
.alpha = alpha, /* Consider alpha linear already? */
|
||||||
.green = ((color >> 8 & 0xff) | (color >> 0 & 0xff00)) * alpha / 0xffff,
|
.red = srgb_decode_8_to_16((color >> 16) & 0xff),
|
||||||
.blue = ((color >> 0 & 0xff) | (color << 8 & 0xff00)) * alpha / 0xffff,
|
.green = srgb_decode_8_to_16((color >> 8) & 0xff),
|
||||||
.alpha = alpha,
|
.blue = srgb_decode_8_to_16((color >> 0) & 0xff),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline pixman_color_t
|
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 */
|
/* 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
|
static inline uint32_t
|
||||||
|
|
@ -568,23 +592,24 @@ draw_strikeout(const struct terminal *term, pixman_image_t *pix,
|
||||||
|
|
||||||
static void
|
static void
|
||||||
cursor_colors_for_cell(const struct terminal *term, const struct cell *cell,
|
cursor_colors_for_cell(const struct terminal *term, const struct cell *cell,
|
||||||
const pixman_color_t *fg, const pixman_color_t *bg,
|
const pixman_color_t *fg, const pixman_color_t *bg,
|
||||||
pixman_color_t *cursor_color, pixman_color_t *text_color)
|
pixman_color_t *cursor_color, pixman_color_t *text_color,
|
||||||
|
bool gamma_correct)
|
||||||
{
|
{
|
||||||
if (term->colors.cursor_bg >> 31)
|
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
|
else
|
||||||
*cursor_color = *fg;
|
*cursor_color = *fg;
|
||||||
|
|
||||||
if (term->colors.cursor_fg >> 31)
|
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 {
|
else {
|
||||||
*text_color = *bg;
|
*text_color = *bg;
|
||||||
|
|
||||||
if (unlikely(text_color->alpha != 0xffff)) {
|
if (unlikely(text_color->alpha != 0xffff)) {
|
||||||
/* The *only* color that can have transparency is the
|
/* The *only* color that can have transparency is the
|
||||||
* default background color */
|
* 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->green == cursor_color->green &&
|
||||||
text_color->blue == cursor_color->blue)
|
text_color->blue == cursor_color->blue)
|
||||||
{
|
{
|
||||||
*text_color = color_hex_to_pixman(term->colors.bg);
|
*text_color = color_hex_to_pixman(term->colors.bg, gamma_correct);
|
||||||
*cursor_color = color_hex_to_pixman(term->colors.fg);
|
*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 cursor_color;
|
||||||
pixman_color_t text_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)) {
|
if (unlikely(!term->kbd_focus)) {
|
||||||
switch (term->conf->cursor.unfocused_style) {
|
switch (term->conf->cursor.unfocused_style) {
|
||||||
|
|
@ -656,8 +682,9 @@ draw_cursor(const struct terminal *term, const struct cell *cell,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
render_cell(struct terminal *term, pixman_image_t *pix, pixman_region32_t *damage,
|
render_cell(struct terminal *term, pixman_image_t *pix,
|
||||||
struct row *row, int row_no, int col, bool has_cursor)
|
pixman_region32_t *damage, struct row *row, int row_no, int col,
|
||||||
|
bool has_cursor)
|
||||||
{
|
{
|
||||||
struct cell *cell = &row->cells[col];
|
struct cell *cell = &row->cells[col];
|
||||||
if (cell->attrs.clean)
|
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)
|
if (cell->attrs.blink && term->blink.state == BLINK_OFF)
|
||||||
_fg = color_decrease_luminance(_fg);
|
_fg = color_decrease_luminance(_fg);
|
||||||
|
|
||||||
pixman_color_t fg = color_hex_to_pixman(_fg);
|
const bool gamma_correct = render_do_linear_blending(term);
|
||||||
pixman_color_t bg = color_hex_to_pixman_with_alpha(_bg, alpha);
|
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);
|
struct fcft_font *font = attrs_to_font(term, &cell->attrs);
|
||||||
const struct composed *composed = NULL;
|
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)
|
if (i > 0 && glyph->x >= 0 && cell_cols == 1)
|
||||||
g_x -= term->cell_width;
|
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...) */
|
/* Glyph surface is a pre-rendered image (typically a color emoji...) */
|
||||||
if (!(cell->attrs.blink && term->blink.state == BLINK_OFF)) {
|
if (!(cell->attrs.blink && term->blink.state == BLINK_OFF)) {
|
||||||
pixman_image_composite32(
|
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) {
|
switch (range->underline.color_src) {
|
||||||
case COLOR_BASE256:
|
case COLOR_BASE256:
|
||||||
underline_color = color_hex_to_pixman(
|
underline_color = color_hex_to_pixman(
|
||||||
term->colors.table[range->underline.color]);
|
term->colors.table[range->underline.color], gamma_correct);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case COLOR_RGB:
|
case COLOR_RGB:
|
||||||
underline_color =
|
underline_color =
|
||||||
color_hex_to_pixman(range->underline.color);
|
color_hex_to_pixman(range->underline.color, gamma_correct);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case COLOR_DEFAULT:
|
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(
|
pixman_color_t url_color = color_hex_to_pixman(
|
||||||
term->conf->colors.use_custom.url
|
term->conf->colors.use_custom.url
|
||||||
? term->conf->colors.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);
|
draw_underline(term, pix, font, &url_color, x, y, cell_cols);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1119,8 +1147,9 @@ draw_cursor:
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
render_row(struct terminal *term, pixman_image_t *pix, pixman_region32_t *damage,
|
render_row(struct terminal *term, pixman_image_t *pix,
|
||||||
struct row *row, int row_no, int cursor_col)
|
pixman_region32_t *damage, struct row *row,
|
||||||
|
int row_no, int cursor_col)
|
||||||
{
|
{
|
||||||
for (int col = term->cols - 1; col >= 0; col--)
|
for (int col = term->cols - 1; col >= 0; col--)
|
||||||
render_cell(term, pix, damage, row, row_no, col, cursor_col == 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)
|
render_urgency(struct terminal *term, struct buffer *buf)
|
||||||
{
|
{
|
||||||
uint32_t red = term->colors.table[1];
|
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),
|
int width = min(min(term->margins.left, term->margins.right),
|
||||||
min(term->margins.top, term->margins.bottom));
|
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 bmargin = term->height - term->margins.bottom;
|
||||||
const int line_count = end_line - start_line;
|
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;
|
const uint32_t _bg = !term->reverse ? term->colors.bg : term->colors.fg;
|
||||||
uint16_t alpha = term->colors.alpha;
|
uint16_t alpha = term->colors.alpha;
|
||||||
|
|
||||||
|
|
@ -1169,7 +1199,7 @@ render_margin(struct terminal *term, struct buffer *buf,
|
||||||
alpha = 0xffff;
|
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_image_fill_rectangles(
|
||||||
PIXMAN_OP_SRC, buf->pix[0], &bg, 4,
|
PIXMAN_OP_SRC, buf->pix[0], &bg, 4,
|
||||||
|
|
@ -1596,8 +1626,7 @@ render_sixel(struct terminal *term, pixman_image_t *pix,
|
||||||
|
|
||||||
static void
|
static void
|
||||||
render_sixel_images(struct terminal *term, pixman_image_t *pix,
|
render_sixel_images(struct terminal *term, pixman_image_t *pix,
|
||||||
pixman_region32_t *damage,
|
pixman_region32_t *damage, const struct coord *cursor)
|
||||||
const struct coord *cursor)
|
|
||||||
{
|
{
|
||||||
if (likely(tll_length(term->grid->sixel_images)) == 0)
|
if (likely(tll_length(term->grid->sixel_images)) == 0)
|
||||||
return;
|
return;
|
||||||
|
|
@ -1649,6 +1678,8 @@ render_ime_preedit_for_seat(struct terminal *term, struct seat *seat,
|
||||||
if (unlikely(term->is_searching))
|
if (unlikely(term->is_searching))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
const bool gamma_correct = render_do_linear_blending(term);
|
||||||
|
|
||||||
/* Adjust cursor position to viewport */
|
/* Adjust cursor position to viewport */
|
||||||
struct coord cursor;
|
struct coord cursor;
|
||||||
cursor = term->grid->cursor.point;
|
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) {
|
if (!seat->ime.preedit.cursor.hidden) {
|
||||||
const struct cell *start_cell = &seat->ime.preedit.cells[0];
|
const struct cell *start_cell = &seat->ime.preedit.cells[0];
|
||||||
|
|
||||||
pixman_color_t fg = color_hex_to_pixman(term->colors.fg);
|
pixman_color_t fg = color_hex_to_pixman(term->colors.fg, gamma_correct);
|
||||||
pixman_color_t bg = color_hex_to_pixman(term->colors.bg);
|
pixman_color_t bg = color_hex_to_pixman(term->colors.bg, gamma_correct);
|
||||||
|
|
||||||
pixman_color_t cursor_color, text_color;
|
pixman_color_t cursor_color, text_color;
|
||||||
cursor_colors_for_cell(
|
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 x = term->margins.left + (col_idx + start) * term->cell_width;
|
||||||
int y = term->margins.top + row_idx * term->cell_height;
|
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];
|
row->cells[col_idx + i] = real_cells[i];
|
||||||
free(real_cells);
|
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(
|
wl_surface_damage_buffer(
|
||||||
term->window->surface.surf,
|
term->window->surface.surf,
|
||||||
term->margins.left,
|
damage_x, damage_y, damage_w, damage_h);
|
||||||
term->margins.top + row_idx * term->cell_height,
|
|
||||||
term->width - term->margins.left - term->margins.right,
|
|
||||||
1 * term->cell_height);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -1916,7 +1949,7 @@ render_overlay(struct terminal *term)
|
||||||
case OVERLAY_FLASH:
|
case OVERLAY_FLASH:
|
||||||
color = color_hex_to_pixman_with_alpha(
|
color = color_hex_to_pixman_with_alpha(
|
||||||
term->conf->colors.flash,
|
term->conf->colors.flash,
|
||||||
term->conf->colors.flash_alpha);
|
term->conf->colors.flash_alpha, render_do_linear_blending(term));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OVERLAY_NONE:
|
case OVERLAY_NONE:
|
||||||
|
|
@ -2118,6 +2151,7 @@ render_worker_thread(void *_ctx)
|
||||||
sem_wait(start);
|
sem_wait(start);
|
||||||
|
|
||||||
struct buffer *buf = term->render.workers.buf;
|
struct buffer *buf = term->render.workers.buf;
|
||||||
|
|
||||||
bool frame_done = false;
|
bool frame_done = false;
|
||||||
|
|
||||||
/* Translate offset-relative cursor row to view-relative */
|
/* Translate offset-relative cursor row to view-relative */
|
||||||
|
|
@ -2138,8 +2172,6 @@ render_worker_thread(void *_ctx)
|
||||||
|
|
||||||
switch (row_no) {
|
switch (row_no) {
|
||||||
default: {
|
default: {
|
||||||
xassert(buf != NULL);
|
|
||||||
|
|
||||||
struct row *row = grid_row_in_view(term->grid, row_no);
|
struct row *row = grid_row_in_view(term->grid, row_no);
|
||||||
int cursor_col = cursor.row == row_no ? cursor.col : -1;
|
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_image_set_clip_region32(buf->pix[0], &clip);
|
||||||
pixman_region32_fini(&clip);
|
pixman_region32_fini(&clip);
|
||||||
|
|
||||||
|
const bool gamma_correct = render_do_linear_blending(term);
|
||||||
uint16_t alpha = _bg >> 24 | (_bg >> 24 << 8);
|
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_image_fill_rectangles(
|
||||||
PIXMAN_OP_SRC, buf->pix[0], &bg, 1,
|
PIXMAN_OP_SRC, buf->pix[0], &bg, 1,
|
||||||
&(pixman_rectangle16_t){0, 0, buf->width, buf->height});
|
&(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 int x_ofs = term->font_x_ofs;
|
||||||
|
|
||||||
const size_t len = c32len(text);
|
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++) {
|
for (size_t i = 0; i < glyph_count; i++) {
|
||||||
const struct fcft_glyph *glyph = glyphs[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_image_composite32(
|
||||||
PIXMAN_OP_OVER, glyph->pix, NULL, buf->pix[0], 0, 0, 0, 0,
|
PIXMAN_OP_OVER, glyph->pix, NULL, buf->pix[0], 0, 0, 0, 0,
|
||||||
x + x_ofs + glyph->x, y - glyph->y,
|
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)
|
if (info->width == 0 || info->height == 0)
|
||||||
return;
|
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);
|
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);
|
_color = color_dim(term, _color);
|
||||||
|
|
||||||
uint16_t alpha = _color >> 24 | (_color >> 24 << 8);
|
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_image_fill_rectangles(
|
||||||
PIXMAN_OP_SRC, buf->pix[0], &color, 1,
|
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
|
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;
|
uint32_t _color = conf->colors.bg;
|
||||||
uint16_t alpha = 0xffff;
|
uint16_t alpha = 0xffff;
|
||||||
|
|
||||||
|
|
@ -2482,13 +2520,14 @@ get_csd_button_fg_color(const struct config *conf)
|
||||||
alpha = _color >> 24 | (_color >> 24 << 8);
|
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
|
static void
|
||||||
render_csd_button_minimize(struct terminal *term, struct buffer *buf)
|
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);
|
pixman_image_t *src = pixman_image_create_solid_fill(&color);
|
||||||
|
|
||||||
const int max_height = buf->height / 3;
|
const int max_height = buf->height / 3;
|
||||||
|
|
@ -2516,7 +2555,7 @@ static void
|
||||||
render_csd_button_maximize_maximized(
|
render_csd_button_maximize_maximized(
|
||||||
struct terminal *term, struct buffer *buf)
|
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);
|
pixman_image_t *src = pixman_image_create_solid_fill(&color);
|
||||||
|
|
||||||
const int max_height = buf->height / 3;
|
const int max_height = buf->height / 3;
|
||||||
|
|
@ -2548,7 +2587,7 @@ static void
|
||||||
render_csd_button_maximize_window(
|
render_csd_button_maximize_window(
|
||||||
struct terminal *term, struct buffer *buf)
|
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);
|
pixman_image_t *src = pixman_image_create_solid_fill(&color);
|
||||||
|
|
||||||
const int max_height = buf->height / 3;
|
const int max_height = buf->height / 3;
|
||||||
|
|
@ -2588,7 +2627,7 @@ render_csd_button_maximize(struct terminal *term, struct buffer *buf)
|
||||||
static void
|
static void
|
||||||
render_csd_button_close(struct terminal *term, struct buffer *buf)
|
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);
|
pixman_image_t *src = pixman_image_create_solid_fill(&color);
|
||||||
|
|
||||||
const int max_height = buf->height / 3;
|
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)
|
if (!term->visual_focus)
|
||||||
_color = color_dim(term, _color);
|
_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);
|
render_csd_part(term, surf->surf, buf, info->width, info->height, &color);
|
||||||
|
|
||||||
switch (surf_idx) {
|
switch (surf_idx) {
|
||||||
case CSD_SURF_MINIMIZE: render_csd_button_minimize(term, buf); break;
|
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_MAXIMIZE: render_csd_button_maximize(term, buf); break;
|
||||||
case CSD_SURF_CLOSE: render_csd_button_close(term, buf); break;
|
case CSD_SURF_CLOSE: render_csd_button_close(term, buf); break;
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
BUG("unhandled surface type: %u", (unsigned)surf_idx);
|
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;
|
: term->conf->colors.use_custom.search_box_no_match;
|
||||||
|
|
||||||
/* Background - yellow on empty/match, red on mismatch (default) */
|
/* 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(
|
const pixman_color_t color = color_hex_to_pixman(
|
||||||
is_match
|
is_match
|
||||||
? (custom_colors
|
? (custom_colors
|
||||||
|
|
@ -3625,13 +3665,14 @@ render_search_box(struct terminal *term)
|
||||||
: term->colors.table[3])
|
: term->colors.table[3])
|
||||||
: (custom_colors
|
: (custom_colors
|
||||||
? term->conf->colors.search_box.no_match.bg
|
? term->conf->colors.search_box.no_match.bg
|
||||||
: term->colors.table[1]));
|
: term->colors.table[1]),
|
||||||
|
gamma_correct);
|
||||||
|
|
||||||
pixman_image_fill_rectangles(
|
pixman_image_fill_rectangles(
|
||||||
PIXMAN_OP_SRC, buf->pix[0], &color,
|
PIXMAN_OP_SRC, buf->pix[0], &color,
|
||||||
1, &(pixman_rectangle16_t){width - visible_width, 0, visible_width, height});
|
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_image_fill_rectangles(
|
||||||
PIXMAN_OP_SRC, buf->pix[0], &transparent,
|
PIXMAN_OP_SRC, buf->pix[0], &transparent,
|
||||||
1, &(pixman_rectangle16_t){0, 0, width - visible_width, height});
|
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;
|
const int x_ofs = term->font_x_ofs;
|
||||||
int x = x_left;
|
int x = x_left;
|
||||||
int y = margin;
|
int y = margin;
|
||||||
|
|
||||||
pixman_color_t fg = color_hex_to_pixman(
|
pixman_color_t fg = color_hex_to_pixman(
|
||||||
custom_colors
|
custom_colors
|
||||||
? (is_match
|
? (is_match
|
||||||
? term->conf->colors.search_box.match.fg
|
? term->conf->colors.search_box.match.fg
|
||||||
: term->conf->colors.search_box.no_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 */
|
/* 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++) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
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...) */
|
|
||||||
pixman_image_composite32(
|
pixman_image_composite32(
|
||||||
PIXMAN_OP_OVER, glyph->pix, NULL, buf->pix[0], 0, 0, 0, 0,
|
PIXMAN_OP_OVER, glyph->pix, NULL, buf->pix[0], 0, 0, 0, 0,
|
||||||
x + x_ofs + glyph->x, y + term->font_baseline - glyph->y,
|
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;
|
seat->pointer.xcursor_pending = true;
|
||||||
return 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);
|
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;
|
struct wl_shm *shm;
|
||||||
size_t pix_instances;
|
size_t pix_instances;
|
||||||
bool scrollable;
|
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;
|
static tll(struct buffer_private *) deferred;
|
||||||
|
|
@ -115,6 +121,7 @@ buffer_destroy_dont_close(struct buffer *buf)
|
||||||
if (buf->pix[i] != NULL)
|
if (buf->pix[i] != NULL)
|
||||||
pixman_image_unref(buf->pix[i]);
|
pixman_image_unref(buf->pix[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buf->wl_buf != NULL)
|
if (buf->wl_buf != NULL)
|
||||||
wl_buffer_destroy(buf->wl_buf);
|
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(
|
wl_buf = wl_shm_pool_create_buffer(
|
||||||
pool->wl_pool, new_offset,
|
pool->wl_pool, new_offset,
|
||||||
buf->public.width, buf->public.height, buf->public.stride,
|
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) {
|
if (wl_buf == NULL) {
|
||||||
LOG_ERR("failed to create SHM buffer");
|
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?) */
|
/* One pixman image for each worker thread (do we really need multiple?) */
|
||||||
for (size_t i = 0; i < buf->public.pix_instances; i++) {
|
for (size_t i = 0; i < buf->public.pix_instances; i++) {
|
||||||
pix[i] = pixman_image_create_bits_no_clear(
|
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,
|
buf->public.width, buf->public.height,
|
||||||
(uint32_t *)mmapped, buf->public.stride);
|
(uint32_t *)mmapped, buf->public.stride);
|
||||||
|
|
||||||
if (pix[i] == NULL) {
|
if (pix[i] == NULL) {
|
||||||
LOG_ERR("failed to create pixman image");
|
LOG_ERR("failed to create pixman image");
|
||||||
goto err;
|
goto err;
|
||||||
|
|
@ -959,14 +971,74 @@ shm_unref(struct buffer *_buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct buffer_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_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));
|
struct buffer_chain *chain = xmalloc(sizeof(*chain));
|
||||||
*chain = (struct buffer_chain){
|
*chain = (struct buffer_chain){
|
||||||
.bufs = tll_init(),
|
.bufs = tll_init(),
|
||||||
.shm = shm,
|
.shm = wayl->shm,
|
||||||
.pix_instances = pix_instances,
|
.pix_instances = pix_instances,
|
||||||
.scrollable = scrollable,
|
.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;
|
return chain;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
5
shm.h
5
shm.h
|
|
@ -9,6 +9,8 @@
|
||||||
|
|
||||||
#include <tllist.h>
|
#include <tllist.h>
|
||||||
|
|
||||||
|
#include "wayland.h"
|
||||||
|
|
||||||
struct damage;
|
struct damage;
|
||||||
|
|
||||||
struct buffer {
|
struct buffer {
|
||||||
|
|
@ -43,7 +45,8 @@ void shm_set_max_pool_size(off_t max_pool_size);
|
||||||
|
|
||||||
struct buffer_chain;
|
struct buffer_chain;
|
||||||
struct buffer_chain *shm_chain_new(
|
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);
|
void shm_chain_free(struct buffer_chain *chain);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
97
sixel.c
97
sixel.c
|
|
@ -10,6 +10,7 @@
|
||||||
#include "grid.h"
|
#include "grid.h"
|
||||||
#include "hsl.h"
|
#include "hsl.h"
|
||||||
#include "render.h"
|
#include "render.h"
|
||||||
|
#include "srgb.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "xmalloc.h"
|
#include "xmalloc.h"
|
||||||
#include "xsnprintf.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_generic(struct terminal *term, uint8_t c);
|
||||||
static void sixel_put_ar_11(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
|
void
|
||||||
sixel_fini(struct terminal *term)
|
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.height = 0;
|
||||||
term->sixel.image.alloc_height = 0;
|
term->sixel.image.alloc_height = 0;
|
||||||
term->sixel.image.bottom_pixel = 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) {
|
if (term->sixel.use_private_palette) {
|
||||||
xassert(term->sixel.private_palette == NULL);
|
xassert(term->sixel.private_palette == NULL);
|
||||||
|
|
@ -83,11 +135,18 @@ sixel_init(struct terminal *term, int p1, int p2, int p3)
|
||||||
|
|
||||||
memcpy(
|
memcpy(
|
||||||
term->sixel.private_palette, term->conf->colors.sixel,
|
term->sixel.private_palette, term->conf->colors.sixel,
|
||||||
min(sizeof(term->conf->colors.sixel),
|
active_palette_entries * sizeof(term->sixel.private_palette[0]));
|
||||||
term->sixel.palette_size * 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;
|
term->sixel.palette = term->sixel.private_palette;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (term->sixel.shared_palette == NULL) {
|
if (term->sixel.shared_palette == NULL) {
|
||||||
term->sixel.shared_palette = xcalloc(
|
term->sixel.shared_palette = xcalloc(
|
||||||
|
|
@ -95,8 +154,16 @@ sixel_init(struct terminal *term, int p1, int p2, int p3)
|
||||||
|
|
||||||
memcpy(
|
memcpy(
|
||||||
term->sixel.shared_palette, term->conf->colors.sixel,
|
term->sixel.shared_palette, term->conf->colors.sixel,
|
||||||
min(sizeof(term->conf->colors.sixel),
|
active_palette_entries * sizeof(term->sixel.shared_palette[0]));
|
||||||
term->sixel.palette_size * 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 {
|
} else {
|
||||||
/* Shared palette - do *not* reset palette for new sixels */
|
/* 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);
|
int stride = new_width * sizeof(uint32_t);
|
||||||
uint32_t *new_data = xmalloc(stride * new_height);
|
uint32_t *new_data = xmalloc(stride * new_height);
|
||||||
pixman_image_t *pix2 = pixman_image_create_bits_no_clear(
|
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)
|
#if defined(_DEBUG)
|
||||||
/* Fill new image with an easy-to-recognize color (green) */
|
/* 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_image_t *new_pix = pixman_image_create_bits_no_clear(
|
||||||
PIXMAN_a8r8g8b8,
|
term->sixel.pixman_fmt, new_width, new_height, new_data, new_width * sizeof(uint32_t));
|
||||||
new_width, new_height, new_data, new_width * sizeof(uint32_t));
|
|
||||||
|
|
||||||
struct sixel new_six = {
|
struct sixel new_six = {
|
||||||
.pix = NULL,
|
.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);
|
uint8_t *scaled_data = xmalloc(scaled_height * scaled_stride);
|
||||||
pixman_image_t *scaled_pix = pixman_image_create_bits_no_clear(
|
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);
|
(uint32_t *)scaled_data, scaled_stride);
|
||||||
|
|
||||||
pixman_image_composite32(
|
pixman_image_composite32(
|
||||||
|
|
@ -1232,7 +1298,7 @@ sixel_unhook(struct terminal *term)
|
||||||
image.pos.row, image.pos.row + image.rows);
|
image.pos.row, image.pos.row + image.rows);
|
||||||
|
|
||||||
image.original.pix = pixman_image_create_bits_no_clear(
|
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);
|
img_data, stride);
|
||||||
|
|
||||||
pixel_row_idx += height;
|
pixel_row_idx += height;
|
||||||
|
|
@ -2006,15 +2072,14 @@ decgci(struct terminal *term, uint8_t c)
|
||||||
}
|
}
|
||||||
|
|
||||||
case 2: { /* RGB */
|
case 2: { /* RGB */
|
||||||
uint8_t r = 255 * min(c1, 100) / 100;
|
uint16_t r = 255 * min(c1, 100) / 100;
|
||||||
uint8_t g = 255 * min(c2, 100) / 100;
|
uint16_t g = 255 * min(c2, 100) / 100;
|
||||||
uint8_t b = 255 * min(c3, 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.color_idx, r, g, b);
|
||||||
|
|
||||||
term->sixel.palette[term->sixel.color_idx] =
|
term->sixel.palette[term->sixel.color_idx] = color_decode_srgb(term, r, g, b);
|
||||||
0xffu << 24 | r << 16 | g << 8 | b;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
54
terminal.c
54
terminal.c
|
|
@ -991,6 +991,7 @@ struct font_load_data {
|
||||||
const char **names;
|
const char **names;
|
||||||
const char *attrs;
|
const char *attrs;
|
||||||
|
|
||||||
|
const struct fcft_font_options *options;
|
||||||
struct fcft_font **font;
|
struct fcft_font **font;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -998,7 +999,8 @@ static int
|
||||||
font_loader_thread(void *_data)
|
font_loader_thread(void *_data)
|
||||||
{
|
{
|
||||||
struct font_load_data *data = _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;
|
return *data->font != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1065,14 +1067,32 @@ reload_fonts(struct terminal *term, bool resize_grid)
|
||||||
[1] = xstrjoin(dpi, !custom_bold ? ":weight=bold" : ""),
|
[1] = xstrjoin(dpi, !custom_bold ? ":weight=bold" : ""),
|
||||||
[2] = xstrjoin(dpi, !custom_italic ? ":slant=italic" : ""),
|
[2] = xstrjoin(dpi, !custom_italic ? ":slant=italic" : ""),
|
||||||
[3] = xstrjoin(dpi, !custom_bold_italic ? ":weight=bold: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 fcft_font *fonts[4];
|
||||||
struct font_load_data data[4] = {
|
struct font_load_data data[4] = {
|
||||||
{count_regular, names_regular, attrs[0], &fonts[0]},
|
{count_regular, names_regular, attrs[0], options, &fonts[0]},
|
||||||
{count_bold, names_bold, attrs[1], &fonts[1]},
|
{count_bold, names_bold, attrs[1], options, &fonts[1]},
|
||||||
{count_italic, names_italic, attrs[2], &fonts[2]},
|
{count_italic, names_italic, attrs[2], options, &fonts[2]},
|
||||||
{count_bold_italic, names_bold_italic, attrs[3], &fonts[3]},
|
{count_bold_italic, names_bold_italic, attrs[3], options, &fonts[3]},
|
||||||
};
|
};
|
||||||
|
|
||||||
thrd_t tids[4] = {0};
|
thrd_t tids[4] = {0};
|
||||||
|
|
@ -1097,6 +1117,8 @@ reload_fonts(struct terminal *term, bool resize_grid)
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fcft_font_options_destroy(options);
|
||||||
|
|
||||||
for (size_t i = 0; i < 4; i++) {
|
for (size_t i = 0; i < 4; i++) {
|
||||||
for (size_t j = 0; j < counts[i]; j++)
|
for (size_t j = 0; j < counts[i]; j++)
|
||||||
free(names[i][j]);
|
free(names[i][j]);
|
||||||
|
|
@ -1237,6 +1259,8 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bool ten_bit_surfaces = conf->tweak.surface_bit_depth == SHM_10_BIT;
|
||||||
|
|
||||||
/* Initialize configure-based terminal attributes */
|
/* Initialize configure-based terminal attributes */
|
||||||
*term = (struct terminal) {
|
*term = (struct terminal) {
|
||||||
.fdm = fdm,
|
.fdm = fdm,
|
||||||
|
|
@ -1320,13 +1344,14 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
||||||
.wl = wayl,
|
.wl = wayl,
|
||||||
.render = {
|
.render = {
|
||||||
.chains = {
|
.chains = {
|
||||||
.grid = shm_chain_new(wayl->shm, true, 1 + conf->render_worker_count),
|
.grid = shm_chain_new(wayl, true, 1 + conf->render_worker_count,
|
||||||
.search = shm_chain_new(wayl->shm, false, 1),
|
ten_bit_surfaces),
|
||||||
.scrollback_indicator = shm_chain_new(wayl->shm, false, 1),
|
.search = shm_chain_new(wayl, false, 1 ,ten_bit_surfaces),
|
||||||
.render_timer = shm_chain_new(wayl->shm, false, 1),
|
.scrollback_indicator = shm_chain_new(wayl, false, 1, ten_bit_surfaces),
|
||||||
.url = shm_chain_new(wayl->shm, false, 1),
|
.render_timer = shm_chain_new(wayl, false, 1, ten_bit_surfaces),
|
||||||
.csd = shm_chain_new(wayl->shm, false, 1),
|
.url = shm_chain_new(wayl, false, 1, ten_bit_surfaces),
|
||||||
.overlay = shm_chain_new(wayl->shm, false, 1),
|
.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,
|
.scrollback_lines = conf->scrollback.lines,
|
||||||
.app_sync_updates.timer_fd = app_sync_updates_fd,
|
.app_sync_updates.timer_fd = app_sync_updates_fd,
|
||||||
|
|
@ -1468,6 +1493,9 @@ term_window_configured(struct terminal *term)
|
||||||
if (!term->shutdown.in_progress) {
|
if (!term->shutdown.in_progress) {
|
||||||
xassert(term->window->is_configured);
|
xassert(term->window->is_configured);
|
||||||
fdm_add(term->fdm, term->ptmx, EPOLLIN, &fdm_ptmx, term);
|
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 transparent_bg;
|
||||||
|
|
||||||
|
bool linear_blending;
|
||||||
|
bool use_10bit;
|
||||||
|
pixman_format_code_t pixman_fmt;
|
||||||
|
|
||||||
/* Application configurable */
|
/* Application configurable */
|
||||||
unsigned palette_size; /* Number of colors in palette */
|
unsigned palette_size; /* Number of colors in palette */
|
||||||
unsigned max_width; /* Maximum image width, in pixels */
|
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
|
static void
|
||||||
shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
|
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)
|
#if defined(_DEBUG)
|
||||||
bool have_description = false;
|
bool have_description = false;
|
||||||
|
|
||||||
|
|
@ -666,6 +675,91 @@ static const struct wp_presentation_listener presentation_listener = {
|
||||||
.clock_id = &clock_id,
|
.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
|
static bool
|
||||||
verify_iface_version(const char *iface, uint32_t version, uint32_t wanted)
|
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
|
#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
|
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
||||||
else if (streq(interface, zwp_text_input_manager_v3_interface.name)) {
|
else if (streq(interface, zwp_text_input_manager_v3_interface.name)) {
|
||||||
const uint32_t required = 1;
|
const uint32_t required = 1;
|
||||||
|
|
@ -1707,6 +1815,13 @@ wayl_destroy(struct wayland *wayl)
|
||||||
zwp_text_input_manager_v3_destroy(wayl->text_input_manager);
|
zwp_text_input_manager_v3_destroy(wayl->text_input_manager);
|
||||||
#endif
|
#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 defined(HAVE_XDG_SYSTEM_BELL)
|
||||||
if (wayl->system_bell != NULL)
|
if (wayl->system_bell != NULL)
|
||||||
xdg_system_bell_v1_destroy(wayl->system_bell);
|
xdg_system_bell_v1_destroy(wayl->system_bell);
|
||||||
|
|
@ -1847,6 +1962,38 @@ wayl_win_init(struct terminal *term, const char *token)
|
||||||
}
|
}
|
||||||
#endif
|
#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) {
|
if (conf->csd.preferred == CONF_CSD_PREFER_NONE) {
|
||||||
/* User specifically do *not* want decorations */
|
/* User specifically do *not* want decorations */
|
||||||
win->csd_mode = CSD_NO;
|
win->csd_mode = CSD_NO;
|
||||||
|
|
@ -1861,8 +2008,8 @@ wayl_win_init(struct terminal *term, const char *token)
|
||||||
zxdg_toplevel_decoration_v1_set_mode(
|
zxdg_toplevel_decoration_v1_set_mode(
|
||||||
win->xdg_toplevel_decoration,
|
win->xdg_toplevel_decoration,
|
||||||
(conf->csd.preferred == CONF_CSD_PREFER_SERVER
|
(conf->csd.preferred == CONF_CSD_PREFER_SERVER
|
||||||
? ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE
|
? ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE
|
||||||
: ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE));
|
: ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE));
|
||||||
|
|
||||||
zxdg_toplevel_decoration_v1_add_listener(
|
zxdg_toplevel_decoration_v1_add_listener(
|
||||||
win->xdg_toplevel_decoration, &xdg_toplevel_decoration_listener, win);
|
win->xdg_toplevel_decoration, &xdg_toplevel_decoration_listener, win);
|
||||||
|
|
@ -1987,7 +2134,12 @@ wayl_win_destroy(struct wl_window *win)
|
||||||
free(it->item);
|
free(it->item);
|
||||||
|
|
||||||
tll_remove(win->xdg_tokens, it);
|
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)
|
if (win->fractional_scale != NULL)
|
||||||
wp_fractional_scale_v1_destroy(win->fractional_scale);
|
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;
|
struct wayland *wayl = win->term->wl;
|
||||||
|
|
||||||
surf->surface.surf = NULL;
|
surf->surface.surf = NULL;
|
||||||
|
surf->surface.viewport = NULL;
|
||||||
surf->sub = NULL;
|
surf->sub = NULL;
|
||||||
|
|
||||||
struct wl_surface *main_surface
|
struct wl_surface *main_surface
|
||||||
|
|
@ -2318,6 +2471,22 @@ wayl_win_subsurface_new_with_custom_parent(
|
||||||
return false;
|
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(
|
struct wl_subsurface *sub = wl_subcompositor_get_subsurface(
|
||||||
wayl->sub_compositor, main_surface, parent);
|
wayl->sub_compositor, main_surface, parent);
|
||||||
|
|
||||||
|
|
@ -2369,6 +2538,13 @@ wayl_win_subsurface_destroy(struct wayl_sub_surface *surf)
|
||||||
if (surf == NULL)
|
if (surf == NULL)
|
||||||
return;
|
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) {
|
if (surf->surface.viewport != NULL) {
|
||||||
wp_viewport_destroy(surf->surface.viewport);
|
wp_viewport_destroy(surf->surface.viewport);
|
||||||
surf->surface.viewport = NULL;
|
surf->surface.viewport = NULL;
|
||||||
|
|
|
||||||
23
wayland.h
23
wayland.h
|
|
@ -28,6 +28,10 @@
|
||||||
#include <xdg-system-bell-v1.h>
|
#include <xdg-system-bell-v1.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(HAVE_WP_COLOR_MANAGEMENT)
|
||||||
|
#include <color-management-v1.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <fcft/fcft.h>
|
#include <fcft/fcft.h>
|
||||||
#include <tllist.h>
|
#include <tllist.h>
|
||||||
|
|
||||||
|
|
@ -61,6 +65,9 @@ enum touch_state {
|
||||||
struct wayl_surface {
|
struct wayl_surface {
|
||||||
struct wl_surface *surf;
|
struct wl_surface *surf;
|
||||||
struct wp_viewport *viewport;
|
struct wp_viewport *viewport;
|
||||||
|
#if defined(HAVE_WP_COLOR_MANAGEMENT)
|
||||||
|
struct wp_color_management_surface_v1 *color_management;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wayl_sub_surface {
|
struct wayl_sub_surface {
|
||||||
|
|
@ -459,6 +466,17 @@ struct wayland {
|
||||||
struct xdg_system_bell_v1 *system_bell;
|
struct xdg_system_bell_v1 *system_bell;
|
||||||
#endif
|
#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;
|
bool presentation_timings;
|
||||||
struct wp_presentation *presentation;
|
struct wp_presentation *presentation;
|
||||||
uint32_t presentation_clock_id;
|
uint32_t presentation_clock_id;
|
||||||
|
|
@ -474,6 +492,11 @@ struct wayland {
|
||||||
|
|
||||||
/* WL_SHM >= 2 */
|
/* WL_SHM >= 2 */
|
||||||
bool use_shm_release;
|
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(
|
struct wayland *wayl_init(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue