From 32d0bd3d0e04426809039038cb30fe65c400a14c Mon Sep 17 00:00:00 2001 From: Leandro Ribeiro Date: Thu, 26 Feb 2026 17:47:15 -0300 Subject: [PATCH] compositor: add grayscale output color effect This adds a new output color effect: grayscale. It takes RGB color as input and computes a gray pixel color using the luminance formula for linear sRGB: Y = 0.2126 * R + 0.7152 * G + 0.0722 * B Just like the other color effects we have, this only works for sRGB and are not enabled when color-management is on. Note: although the technique is designed to be applied in linear, it's costly to convert to linear and then back to electrical. As doing the conversion in electrical still gives a reasonable result, we do it this way. When we add support for color effects with color-management on, we'll apply the effect in linear. Signed-off-by: Leandro Ribeiro --- frontend/main.c | 3 +++ include/libweston/libweston.h | 4 +++ libweston/color.h | 1 + libweston/compositor.c | 21 +++++++++++++++ libweston/renderer-gl/fragment.glsl | 26 ++++++++++++++++++- libweston/renderer-gl/gl-renderer-internal.h | 1 + libweston/renderer-gl/gl-renderer.c | 6 +++++ .../gl-shader-config-color-transformation.c | 3 +++ libweston/renderer-gl/gl-shaders.c | 4 +++ man/weston.ini.man | 3 +++ 10 files changed, 71 insertions(+), 1 deletion(-) diff --git a/frontend/main.c b/frontend/main.c index e65c60bfa..e8c215cd5 100644 --- a/frontend/main.c +++ b/frontend/main.c @@ -2060,6 +2060,9 @@ wet_output_set_color_effect(struct weston_output *output, if (strcmp(color_effect, "inversion") == 0) { weston_output_color_effect_inversion(output); goto out; + } else if (strcmp(color_effect, "grayscale") == 0) { + weston_output_color_effect_grayscale(output); + goto out; } entry = weston_enum_map_find_name(cvd_correction_name_map, color_effect); diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 367def169..ce70148c2 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -140,6 +140,7 @@ enum weston_surface_protection_mode { enum weston_output_color_effect_type { WESTON_OUTPUT_COLOR_EFFECT_TYPE_INVERSION = 0, + WESTON_OUTPUT_COLOR_EFFECT_TYPE_GRAYSCALE, WESTON_OUTPUT_COLOR_EFFECT_TYPE_CVD_CORRECTION, }; @@ -2693,6 +2694,9 @@ weston_output_set_transform(struct weston_output *output, void weston_output_color_effect_inversion(struct weston_output *output); +void +weston_output_color_effect_grayscale(struct weston_output *output); + void weston_output_color_effect_cvd_correction(struct weston_output *output, enum weston_cvd_correction_type type); diff --git a/libweston/color.h b/libweston/color.h index f79f1b5cc..464aae333 100644 --- a/libweston/color.h +++ b/libweston/color.h @@ -436,6 +436,7 @@ struct weston_output_color_effect { union { /* color inversion: no parameters */ + /* color grayscale: no parameters */ struct weston_cvd_correction cvd; } u; }; diff --git a/libweston/compositor.c b/libweston/compositor.c index c10f8967e..6ed0901b5 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -7681,6 +7681,27 @@ weston_output_color_effect_inversion(struct weston_output *output) output->color_effect->type = WESTON_OUTPUT_COLOR_EFFECT_TYPE_INVERSION; } +/** Set output's color effect to grayscale. + * + * The color effect is an effect applied to the whole scenegraph. Note that in + * some cases this may force all the surfaces to be composed on the primary + * plane, i.e. offloading to overlay planes won't be possible. + * + * \param output The output to set the effect. + * + * \ingroup output + */ +WL_EXPORT void +weston_output_color_effect_grayscale(struct weston_output *output) +{ + struct weston_compositor *compositor = output->compositor; + + weston_assert_ptr_null(compositor, output->color_effect); + + output->color_effect = weston_output_color_effect_create(compositor); + output->color_effect->type = WESTON_OUTPUT_COLOR_EFFECT_TYPE_GRAYSCALE; +} + /** Set output's color effect as CVD correction * * The color effect is an effect applied to the whole scenegraph. Note that in diff --git a/libweston/renderer-gl/fragment.glsl b/libweston/renderer-gl/fragment.glsl index 5eb21136d..a0d8b90d0 100644 --- a/libweston/renderer-gl/fragment.glsl +++ b/libweston/renderer-gl/fragment.glsl @@ -44,7 +44,8 @@ /* enum gl_shader_color_effect */ #define SHADER_COLOR_EFFECT_NONE 0 #define SHADER_COLOR_EFFECT_INVERSION 1 -#define SHADER_COLOR_EFFECT_CVD_CORRECTION 2 +#define SHADER_COLOR_EFFECT_GRAYSCALE 2 +#define SHADER_COLOR_EFFECT_CVD_CORRECTION 3 /* enum gl_shader_color_curve */ #define SHADER_COLOR_CURVE_IDENTITY 0 @@ -451,11 +452,32 @@ color_pipeline(vec4 color) vec4 color_inversion(vec4 color) { + /** + * Ideally this should be done in linear space, but converting to linear + * and back is costly. Historically this also has been done in the + * electrical domain. Let's do in electrical, results are good enough. + */ color.rgb = 1.0 - color.rgb; return color; } +vec4 +color_grayscale(vec4 color) +{ + float gray; + + /** + * Ideally this should be done in linear space, but converting to linear + * and back is costly. Historically this also has been done in the + * electrical domain. Let's do in electrical, results are good enough. + */ + gray = dot(color.rgb, vec3(0.2126, 0.7152, 0.0722)); + color.rgb = vec3(gray); + + return color; +} + vec4 color_cvd_correction(vec4 color) { @@ -504,6 +526,8 @@ main() color = color_pipeline(color); else if (c_color_effect == SHADER_COLOR_EFFECT_INVERSION) color = color_inversion(color); + else if (c_color_effect == SHADER_COLOR_EFFECT_GRAYSCALE) + color = color_grayscale(color); else if (c_color_effect == SHADER_COLOR_EFFECT_CVD_CORRECTION) color = color_cvd_correction(color); diff --git a/libweston/renderer-gl/gl-renderer-internal.h b/libweston/renderer-gl/gl-renderer-internal.h index c966c5255..bc3845b3a 100644 --- a/libweston/renderer-gl/gl-renderer-internal.h +++ b/libweston/renderer-gl/gl-renderer-internal.h @@ -226,6 +226,7 @@ enum gl_shader_texture_variant { enum gl_shader_color_effect { SHADER_COLOR_EFFECT_NONE = 0, SHADER_COLOR_EFFECT_INVERSION, + SHADER_COLOR_EFFECT_GRAYSCALE, SHADER_COLOR_EFFECT_CVD_CORRECTION, }; diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 79bf93edd..91753b99e 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -2344,6 +2344,12 @@ apply_color_effect(struct gl_renderer *gr, struct weston_output *output, *b = 1.0f - *b; gl_log_paint_node(gr, "\t\tcolor effect: inversion\n"); return; + case WESTON_OUTPUT_COLOR_EFFECT_TYPE_GRAYSCALE: + *r = 0.2126f * (*r) + 0.7152f * (*g) + 0.0722f * (*b); + *g = *r; + *b = *r; + gl_log_paint_node(gr, "\t\tcolor effect: grayscale\n"); + return; case WESTON_OUTPUT_COLOR_EFFECT_TYPE_CVD_CORRECTION: /** * See weston_output_color_effect_cvd_correction() for more details. diff --git a/libweston/renderer-gl/gl-shader-config-color-transformation.c b/libweston/renderer-gl/gl-shader-config-color-transformation.c index 24a8dc88c..6c028ca14 100644 --- a/libweston/renderer-gl/gl-shader-config-color-transformation.c +++ b/libweston/renderer-gl/gl-shader-config-color-transformation.c @@ -563,6 +563,9 @@ gl_shader_config_set_color_effect(struct gl_renderer *gr, case WESTON_OUTPUT_COLOR_EFFECT_TYPE_INVERSION: sconf->req.color_effect = SHADER_COLOR_EFFECT_INVERSION; break; + case WESTON_OUTPUT_COLOR_EFFECT_TYPE_GRAYSCALE: + sconf->req.color_effect = SHADER_COLOR_EFFECT_GRAYSCALE; + break; case WESTON_OUTPUT_COLOR_EFFECT_TYPE_CVD_CORRECTION: sconf->req.color_effect = SHADER_COLOR_EFFECT_CVD_CORRECTION; sconf->color_effect.cvd.correction = effect->u.cvd.correction; diff --git a/libweston/renderer-gl/gl-shaders.c b/libweston/renderer-gl/gl-shaders.c index 2671a93d7..1eab6d89e 100644 --- a/libweston/renderer-gl/gl-shaders.c +++ b/libweston/renderer-gl/gl-shaders.c @@ -137,6 +137,7 @@ gl_shader_color_effect_to_string(enum gl_shader_color_effect kind) #define CASERET(x) case x: return #x; CASERET(SHADER_COLOR_EFFECT_NONE) CASERET(SHADER_COLOR_EFFECT_INVERSION) + CASERET(SHADER_COLOR_EFFECT_GRAYSCALE) CASERET(SHADER_COLOR_EFFECT_CVD_CORRECTION) #undef CASERET } @@ -890,6 +891,9 @@ gl_shader_load_config(struct gl_renderer *gr, case SHADER_COLOR_EFFECT_INVERSION: weston_log_scope_printf(gr->paint_node_scope, "\t\tcolor effect: inversion\n"); break; + case SHADER_COLOR_EFFECT_GRAYSCALE: + weston_log_scope_printf(gr->paint_node_scope, "\t\tcolor effect: grayscale\n"); + break; case SHADER_COLOR_EFFECT_CVD_CORRECTION: weston_assert_int_ne(gr->compositor, shader->cvd_correction_uniform, -1); weston_log_scope_printf(gr->paint_node_scope, "\t\tcolor effect: cvd - %s\n", diff --git a/man/weston.ini.man b/man/weston.ini.man index 283239b2f..2938b563b 100644 --- a/man/weston.ini.man +++ b/man/weston.ini.man @@ -702,6 +702,9 @@ The effect can be one of the following strings: .B inversion color inversion, i.e. the RGB complement .TP +.B grayscale +convert colors to shades of gray +.TP .B deuteranopia color correction (not simulation) for deuteranopia .TP