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 <leandro.ribeiro@collabora.com>
This commit is contained in:
Leandro Ribeiro 2026-02-26 17:47:15 -03:00
parent ed29f588e4
commit 32d0bd3d0e
10 changed files with 71 additions and 1 deletions

View file

@ -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);

View file

@ -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);

View file

@ -436,6 +436,7 @@ struct weston_output_color_effect {
union {
/* color inversion: no parameters */
/* color grayscale: no parameters */
struct weston_cvd_correction cvd;
} u;
};

View file

@ -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

View file

@ -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);

View file

@ -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,
};

View file

@ -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.

View file

@ -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;

View file

@ -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",

View file

@ -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