color: introduce output color effects

Output color effects are applied to the whole output scenegraph. It
depends on color-management being disabled, as the color effects are
applied in sRGB content.

For now we added only a few accessibility options: color inversion,
deuteranopia, protanopia and tritanopia CVD correction.

Note that surfaces presented on outputs that contains a color effect
can't be used for direct scanout (i.e. bypass composition and offloading
to KMS overlay planes). The color effect is applied in our GL-renderer.

Signed-off-by: Leandro Ribeiro <leandro.ribeiro@collabora.com>
This commit is contained in:
Leandro Ribeiro 2025-08-07 17:04:14 -03:00
parent 1a9cf3bfb6
commit 6fe0cfc2b8
13 changed files with 526 additions and 14 deletions

View file

@ -1385,6 +1385,47 @@ wet_output_set_vrr_mode(struct weston_output *output,
return 0;
}
static const struct weston_enum_map cvd_correction_name_map[] = {
{ "deuteranopia", WESTON_CVD_CORRECTION_TYPE_DEUTERANOPIA },
{ "protanopia", WESTON_CVD_CORRECTION_TYPE_PROTANOPIA },
{ "tritanopia", WESTON_CVD_CORRECTION_TYPE_TRITANOPIA },
};
static int
wet_output_set_color_effect(struct weston_output *output,
struct weston_config_section *section)
{
struct wet_compositor *compositor = to_wet_compositor(output->compositor);
const struct weston_enum_map *entry;
char *color_effect = NULL;
bool ok = true;
weston_config_section_get_string(section, "color-effect", &color_effect, NULL);
if (!color_effect)
return 0;
if (compositor->use_color_manager) {
weston_log("Error: color effect can not be set for output %s, " \
"color-management is enabled\n", output->name);
goto out;
}
if (strcmp(color_effect, "inversion") == 0) {
weston_output_color_effect_inversion(output);
goto out;
}
entry = weston_enum_map_find_name(cvd_correction_name_map, color_effect);
if (entry)
weston_output_color_effect_cvd_correction(output, entry->value);
else
weston_log("Error: unknown color effect '%s'\n", color_effect);
out:
free(color_effect);
return ok ? 0 : -1;
}
static int
wet_output_set_color_profile(struct weston_output *output,
struct weston_config_section *section,
@ -1897,6 +1938,9 @@ wet_configure_windowed_output_from_config(struct weston_output *output,
if (wet_output_set_color_profile(output, section, NULL) < 0)
return -1;
if (wet_output_set_color_effect(output, section) < 0)
return -1;
if (api->output_set_size(output, width, height) < 0) {
weston_log("Cannot configure output \"%s\" using weston_windowed_output_api.\n",
output->name);
@ -2413,6 +2457,9 @@ drm_backend_output_configure(struct weston_output *output,
if (wet_output_set_color_profile(output, section, NULL) < 0)
return -1;
if (wet_output_set_color_effect(output, section) < 0)
return -1;
weston_config_section_get_string(section,
"gbm-format", &gbm_format, NULL);
@ -3099,6 +3146,9 @@ drm_backend_remoted_output_configure(struct weston_output *output,
if (wet_output_set_color_profile(output, section, NULL) < 0)
return -1;
if (wet_output_set_color_effect(output, section) < 0)
return -1;
weston_config_section_get_string(section, "gbm-format", &gbm_format,
NULL);
api->set_gbm_format(output, gbm_format);
@ -3259,6 +3309,9 @@ drm_backend_pipewire_output_configure(struct weston_output *output,
if (wet_output_set_color_profile(output, section, NULL) < 0)
return -1;
if (wet_output_set_color_effect(output, section) < 0)
return -1;
weston_config_section_get_string(section, "seat", &seat, "");
api->set_seat(output, seat);

View file

@ -131,6 +131,17 @@ enum weston_surface_protection_mode {
WESTON_SURFACE_PROTECTION_MODE_ENFORCED
};
enum weston_output_color_effect_type {
WESTON_OUTPUT_COLOR_EFFECT_TYPE_INVERSION = 0,
WESTON_OUTPUT_COLOR_EFFECT_TYPE_CVD_CORRECTION,
};
enum weston_cvd_correction_type {
WESTON_CVD_CORRECTION_TYPE_DEUTERANOPIA = 0,
WESTON_CVD_CORRECTION_TYPE_PROTANOPIA,
WESTON_CVD_CORRECTION_TYPE_TRITANOPIA,
};
/** Possible mode of an output
*
* \ingroup output
@ -448,6 +459,8 @@ struct weston_output {
struct weston_output_color_outcome *color_outcome;
uint64_t color_outcome_serial;
struct weston_output_color_effect *color_effect;
int (*enable)(struct weston_output *output);
int (*disable)(struct weston_output *output);
@ -2503,6 +2516,13 @@ void
weston_output_set_transform(struct weston_output *output,
uint32_t transform);
void
weston_output_color_effect_inversion(struct weston_output *output);
void
weston_output_color_effect_cvd_correction(struct weston_output *output,
enum weston_cvd_correction_type type);
bool
weston_output_set_color_profile(struct weston_output *output,
struct weston_color_profile *cprof);

View file

@ -175,6 +175,19 @@ weston_m3f_sub_m3f(struct weston_mat3f A, struct weston_mat3f B)
return R;
}
/** Element-wise scalar multiplication */
static inline struct weston_mat3f
weston_m3f_mul_scalar(struct weston_mat3f M, float scalar)
{
struct weston_mat3f R;
unsigned i;
for (i = 0; i < 3 * 3; i++)
R.colmaj[i] = scalar * M.colmaj[i];
return R;
}
bool
weston_m3f_invert(struct weston_mat3f *out, struct weston_mat3f M);

View file

@ -385,7 +385,8 @@ dmabuf_feedback_maybe_update(struct drm_device *device, struct weston_view *ev,
FAILURE_REASONS_NO_GBM |
FAILURE_REASONS_NO_COLOR_TRANSFORM |
FAILURE_REASONS_SOLID_SURFACE |
FAILURE_REASONS_OCCLUDED_BY_RENDERER)) {
FAILURE_REASONS_OCCLUDED_BY_RENDERER |
FAILURE_REASONS_OUTPUT_COLOR_EFFECT)) {
action_needed = ACTION_NEEDED_REMOVE_SCANOUT_TRANCHE;
/* Direct scanout may be possible if client re-allocates using the
* params from the scanout tranche. */
@ -913,6 +914,10 @@ drm_output_propose_state(struct weston_output *output_base,
pnode->try_view_on_plane_failure_reasons |=
FAILURE_REASONS_SOLID_SURFACE;
if (pnode->output->color_effect)
pnode->try_view_on_plane_failure_reasons |=
FAILURE_REASONS_OUTPUT_COLOR_EFFECT;
if (pnode->surf_xform.transform != NULL ||
!pnode->surf_xform.identity_pipeline)
pnode->try_view_on_plane_failure_reasons |=

View file

@ -419,6 +419,25 @@ struct weston_color_transform {
uint32_t len_lut3d, float *lut3d);
};
struct weston_cvd_correction {
enum weston_cvd_correction_type type;
struct weston_mat3f simulation;
struct weston_mat3f redistribution;
};
struct weston_output_color_effect {
struct weston_compositor *compositor;
struct wl_signal destroy_signal;
/** Which member of 'u' defines the effect. */
enum weston_output_color_effect_type type;
union {
/* color inversion: no parameters */
struct weston_cvd_correction cvd;
} u;
};
/**
* How content color needs to be transformed
*

View file

@ -7210,6 +7210,165 @@ out_error:
return false;
}
static void
weston_output_color_effect_destroy(struct weston_output_color_effect *effect)
{
if (!effect)
return;
wl_signal_emit(&effect->destroy_signal, effect);
free(effect);
}
static struct weston_output_color_effect *
weston_output_color_effect_create(struct weston_compositor *compositor)
{
struct weston_output_color_effect *effect;
effect = xzalloc(sizeof(*effect));
effect->compositor = compositor;
wl_signal_init(&effect->destroy_signal);
return effect;
}
/** Set output's color effect as color inversion
*
* 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_inversion(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_INVERSION;
}
/** Set output's color effect as CVD correction
*
* 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.
* \param type The color blidness correction type.
*
* \ingroup output
*/
WL_EXPORT void
weston_output_color_effect_cvd_correction(struct weston_output *output,
enum weston_cvd_correction_type type)
{
struct weston_compositor *compositor = output->compositor;
struct weston_cvd_correction *cvd;
float correction_factor = 0.7f; /* TODO: allow tweaking this from ini */
/**
* Color vision correction algorithms depend on color vision deficiency
* (CVD) simulation. The majority of FOSS applications uses CVD
* simulation and/or correction pipelines that expects content in sRGB
* color space.
*
* To get an idea of how CVD simulation works, "Designing for Color
* blindness" [1] is a good source. "Online Color Blindness Simulators"
* [2] is another good source, as it contains comparison between
* pipelines for simulating CVD, and you can upload images and check the
* results of each model. They also created Daltonlens-Python [3], which
* provides implementation of all famous methods.
*
* The most reliable method to perform CVD simulation was developed in
* Brettel et al. "Computerized simulation of color appearance for
* dichromats" [4]. But it is expensive for us to apply that for every
* frame, so we need something else.
*
* Many modern applications (as Firefox, see [5]) are using the CVD
* simulation matrices from Machado et al. "A Physiologically-based
* Model for Simulation of Color Vision Deficiency" [6], both because
* baked-in matrices pipelines are easy to implement and efficient, but
* also because they seem to produce nice results. Such matrices are
* usually applied in linear RGB, althought there are discussions [7]
* mentioning that authors didn't notice differences applying their
* simulation matrices directly to electrical. So we do that in
* electrical.
*
* After simulating the CVD, we need to apply the correction. We compute
* the "error" (original linear RGB minus simulation), and use the
* redistribution matrix to shift the lost content to the other color
* channels (depending on the CVD type). This shifted content is summed
* to the original, but we multiply it by a correction_factor first:
*
* color = original + correction_factor * (redistribution_matrix * error)
*
* We pre-multiply the redistribution matrix by this correction_factor
* here just to avoid carrying the coefficient.
*
* [1] https://mk.bcgsc.ca/colorblind/math.mhtml#projecthome
* [2] https://daltonlens.org/colorblindness-simulator
* [3] https://github.com/DaltonLens/DaltonLens-Python
* [4] https://vision.psychol.cam.ac.uk/jdmollon/papers/Dichromatsimulation.pdf
* [5] https://firefox-source-docs.mozilla.org/devtools-user/accessibility_inspector/simulation/index.html
* [6] https://www.inf.ufrgs.br/~oliveira/pubs_files/CVD_Simulation/CVD_Simulation.html
* [7] https://bugzilla.mozilla.org/show_bug.cgi?id=1655053#c22
*/
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_CVD_CORRECTION;
cvd = &output->color_effect->u.cvd;
cvd->type = type;
/**
* CVD simulation matrices from "A Physiologically-based Model for
* Simulation of Color Deficiency". The redistribution matrices are
* based on each type of CVD (i.e. the defective cone). We use the
* 100% severity matrices for the simulation.
*/
switch (cvd->type) {
case WESTON_CVD_CORRECTION_TYPE_DEUTERANOPIA:
cvd->simulation = WESTON_MAT3F( 0.367322, 0.860646, -0.227968,
0.280085, 0.672501, 0.047413,
-0.011820, 0.042940, 0.968881);
cvd->redistribution = WESTON_MAT3F(1.0, 0.5, 0.0, /* redistribute green */
0.0, 0.0, 0.0,
0.0, 0.5, 1.0);
cvd->redistribution = weston_m3f_mul_scalar(cvd->redistribution,
correction_factor);
return;
case WESTON_CVD_CORRECTION_TYPE_PROTANOPIA:
cvd->simulation = WESTON_MAT3F( 0.152286, 1.052583, -0.204868,
0.114503, 0.786281, 0.099216,
-0.003882, -0.048116, 1.051998);
cvd->redistribution = WESTON_MAT3F(0.0, 0.0, 0.0, /* redistribute red */
0.5, 1.0, 0.0,
0.5, 0.0, 1.0);
cvd->redistribution = weston_m3f_mul_scalar(cvd->redistribution,
correction_factor);
return;
case WESTON_CVD_CORRECTION_TYPE_TRITANOPIA:
cvd->simulation = WESTON_MAT3F( 1.255528, -0.076749, -0.178779,
-0.078411, 0.930809, 0.147602,
0.004733, 0.691367, 0.303900);
cvd->redistribution = WESTON_MAT3F(1.0, 0.0, 0.5, /* redistribute blue */
0.0, 1.0, 0.5,
0.0, 0.0, 0.0);
cvd->redistribution = weston_m3f_mul_scalar(cvd->redistribution,
correction_factor);
return;
}
weston_assert_not_reached(compositor, "unknown color correction type");
}
/** Removes output from compositor's list of enabled outputs
*
* \param output The weston_output object that is being removed.
@ -8041,6 +8200,7 @@ weston_output_get_destroy_listener(struct weston_output *output,
WL_EXPORT void
weston_output_release(struct weston_output *output)
{
struct weston_compositor *compositor = output->compositor;
struct weston_head *head, *tmp;
output->destroying = 1;
@ -8050,13 +8210,16 @@ weston_output_release(struct weston_output *output)
if (output->enabled)
weston_compositor_remove_output(output);
weston_assert_ptr_null(compositor, output->color_outcome);
/* We always have a color profile set, as weston_output_init() sets the
* output cprof to the stock sRGB one. */
assert(output->color_profile);
weston_color_profile_unref(output->color_profile);
output->color_profile = NULL;
assert(output->color_outcome == NULL);
weston_output_color_effect_destroy(output->color_effect);
output->color_effect = NULL;
pixman_region32_fini(&output->region);
wl_list_remove(&output->link);
@ -8958,6 +9121,7 @@ weston_plane_failure_reasons_to_str(enum try_view_on_plane_failure_reasons failu
case FAILURE_REASONS_NO_COLOR_TRANSFORM: return "no color transform";
case FAILURE_REASONS_SOLID_SURFACE: return "solid surface";
case FAILURE_REASONS_OCCLUDED_BY_RENDERER: return "occluded by renderer";
case FAILURE_REASONS_OUTPUT_COLOR_EFFECT: return "output contains color effect";
}
return "???";
}

View file

@ -675,6 +675,7 @@ enum try_view_on_plane_failure_reasons {
FAILURE_REASONS_NO_COLOR_TRANSFORM = 1 << 15,
FAILURE_REASONS_SOLID_SURFACE = 1 << 16,
FAILURE_REASONS_OCCLUDED_BY_RENDERER = 1 << 17,
FAILURE_REASONS_OUTPUT_COLOR_EFFECT = 1 << 18,
};
/**

View file

@ -41,6 +41,11 @@
#define SHADER_VARIANT_SOLID 5
#define SHADER_VARIANT_EXTERNAL 6
/* enum gl_shader_color_effect */
#define SHADER_COLOR_EFFECT_NONE 0
#define SHADER_COLOR_EFFECT_INVERSION 1
#define SHADER_COLOR_EFFECT_CVD_CORRECTION 2
/* enum gl_shader_color_curve */
#define SHADER_COLOR_CURVE_IDENTITY 0
#define SHADER_COLOR_CURVE_LUT_3x1D 1
@ -78,6 +83,7 @@ compile_const int c_variant = DEF_VARIANT;
compile_const int c_color_pre_curve = DEF_COLOR_PRE_CURVE;
compile_const int c_color_mapping = DEF_COLOR_MAPPING;
compile_const int c_color_post_curve = DEF_COLOR_POST_CURVE;
compile_const int c_color_effect = DEF_COLOR_EFFECT;
compile_const bool c_input_is_premult = DEF_INPUT_IS_PREMULT;
compile_const bool c_tint = DEF_TINT;
@ -86,6 +92,8 @@ compile_const bool c_need_color_pipeline =
c_color_pre_curve != SHADER_COLOR_CURVE_IDENTITY ||
c_color_mapping != SHADER_COLOR_MAPPING_IDENTITY ||
c_color_post_curve != SHADER_COLOR_CURVE_IDENTITY;
compile_const bool c_need_straight_alpha =
c_need_color_pipeline || c_color_effect != SHADER_COLOR_EFFECT_NONE;
vec4
yuva2rgba(vec4 yuva)
@ -161,6 +169,9 @@ uniform HIGHPRECISION vec2 color_mapping_lut_scale_offset;
uniform HIGHPRECISION mat3 color_mapping_matrix;
uniform HIGHPRECISION vec3 color_mapping_offset;
uniform HIGHPRECISION mat3 color_cvd_simulation;
uniform HIGHPRECISION mat3 color_cvd_redistribution;
/*
* 2D texture sampler abstracting away the lack of swizzles on OpenGL ES 2. This
* should only be used by code relying on swizzling. 'unit' is the texture unit
@ -459,14 +470,6 @@ color_mapping(vec3 color)
vec4
color_pipeline(vec4 color)
{
/* Ensure straight alpha */
if (c_input_is_premult) {
if (color.a == 0.0)
color.rgb = vec3(0, 0, 0);
else
color.rgb *= 1.0 / color.a;
}
color.rgb = color_curve(c_color_pre_curve, color_pre_curve_lut,
color_pre_curve_par, color.rgb);
color.rgb = color_mapping(color.rgb);
@ -476,6 +479,34 @@ color_pipeline(vec4 color)
return color;
}
vec4
color_inversion(vec4 color)
{
color.rgb = 1.0 - color.rgb;
return color;
}
vec4
color_cvd_correction(vec4 color)
{
vec3 original, error;
vec4 res;
/**
* See weston_output_color_effect_cvd_correction() for more details.
*/
original = color.rgb;
color.rgb = color_cvd_simulation * original;
error = original - color.rgb;
color.rgb = original + color_cvd_redistribution * error;
color.rgb = clamp(color.rgb, 0.0, 1.0);
return color;
}
vec4
wireframe()
{
@ -494,11 +525,24 @@ main()
/* Electrical (non-linear) RGBA values, may be premult or not */
color = sample_input_texture();
/* Ensure straight alpha for color pipeline and color effects */
if (c_input_is_premult && c_need_straight_alpha) {
if (color.a == 0.0)
color.rgb = vec3(0, 0, 0);
else
color.rgb *= 1.0 / color.a;
}
/* For now color management and color effects do not coexist */
if (c_need_color_pipeline)
color = color_pipeline(color); /* Produces straight alpha */
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_CVD_CORRECTION)
color = color_cvd_correction(color);
/* Ensure pre-multiplied for blending */
if (!c_input_is_premult || c_need_color_pipeline)
if (!c_input_is_premult || (c_input_is_premult && c_need_straight_alpha))
color.rgb *= color.a;
color *= view_alpha;

View file

@ -222,6 +222,13 @@ enum gl_shader_texture_variant {
SHADER_VARIANT_EXTERNAL,
};
/* Keep the following in sync with fragment.glsl. */
enum gl_shader_color_effect {
SHADER_COLOR_EFFECT_NONE = 0,
SHADER_COLOR_EFFECT_INVERSION,
SHADER_COLOR_EFFECT_CVD_CORRECTION,
};
/* Keep the following in sync with fragment.glsl. */
enum gl_shader_color_curve {
SHADER_COLOR_CURVE_IDENTITY = 0,
@ -289,6 +296,8 @@ struct gl_shader_requirements
bool tint:1;
bool wireframe:1;
unsigned color_effect:2; /* enum gl_shader_color_effect */
unsigned color_pre_curve:3; /* enum gl_shader_color_curve */
unsigned color_mapping:2; /* enum gl_shader_color_mapping */
unsigned color_post_curve:3; /* enum gl_shader_color_curve */
@ -297,7 +306,7 @@ struct gl_shader_requirements
* The total size of all bitfields plus pad_bits_ must fill up exactly
* how many bytes the compiler allocates for them together.
*/
unsigned pad_bits_:16;
unsigned pad_bits_:14;
};
static_assert(sizeof(struct gl_shader_requirements) ==
4 /* total bitfield size in bytes */,
@ -333,6 +342,13 @@ struct gl_shader;
struct weston_color_transform;
struct dmabuf_allocator;
union gl_shader_config_color_effect {
struct {
struct weston_mat3f simulation;
struct weston_mat3f redistribution;
} cvd_correction;
};
union gl_shader_config_color_curve {
struct {
GLuint tex;
@ -369,6 +385,8 @@ struct gl_shader_config {
GLuint wireframe_tex;
union gl_shader_config_color_effect color_effect;
union gl_shader_config_color_curve color_pre_curve;
union gl_shader_config_color_mapping color_mapping;
union gl_shader_config_color_curve color_post_curve;
@ -734,4 +752,9 @@ gl_shader_config_set_color_transform(struct gl_renderer *gr,
struct gl_shader_config *sconf,
struct weston_color_transform *xform);
bool
gl_shader_config_set_color_effect(struct gl_renderer *gr,
struct gl_shader_config *sconf,
struct weston_output_color_effect *effect);
#endif /* GL_RENDERER_INTERNAL_H */

View file

@ -1503,6 +1503,11 @@ gl_shader_config_init_for_paint_node(struct gl_shader_config *sconf,
return false;
}
if (!gl_shader_config_set_color_effect(gr, sconf, pnode->output->color_effect)) {
weston_log("GL-renderer: %s failed to generate a color effect.\n", __func__);
return false;
}
return true;
}

View file

@ -56,6 +56,13 @@ struct gl_renderer_color_transform {
struct gl_renderer_color_curve post_curve;
};
struct gl_renderer_color_effect {
struct weston_output_color_effect *owner;
struct wl_listener destroy_listener;
enum gl_shader_color_effect type;
union gl_shader_config_color_effect u;
};
static void
gl_renderer_color_curve_fini(struct gl_renderer_color_curve *gl_curve)
{
@ -466,3 +473,97 @@ gl_shader_config_set_color_transform(struct gl_renderer *gr,
return true;
}
static void
gl_renderer_color_effect_destroy(struct gl_renderer_color_effect *gl_effect)
{
wl_list_remove(&gl_effect->destroy_listener.link);
free(gl_effect);
}
static void
color_effect_destroy_handler(struct wl_listener *l, void *data)
{
struct gl_renderer_color_effect *gl_effect;
gl_effect = wl_container_of(l, gl_effect, destroy_listener);
weston_assert_ptr_eq(gl_effect->owner->compositor, gl_effect->owner, data);
gl_renderer_color_effect_destroy(gl_effect);
}
static struct gl_renderer_color_effect *
gl_renderer_color_effect_create(struct weston_output_color_effect *effect)
{
struct gl_renderer_color_effect *gl_effect;
gl_effect = zalloc(sizeof *gl_effect);
if (!gl_effect)
return NULL;
gl_effect->owner = effect;
gl_effect->destroy_listener.notify = color_effect_destroy_handler;
wl_signal_add(&effect->destroy_signal, &gl_effect->destroy_listener);
return gl_effect;
}
static struct gl_renderer_color_effect *
gl_renderer_color_effect_get(struct weston_output_color_effect *effect)
{
struct wl_listener *l;
l = wl_signal_get(&effect->destroy_signal,
color_effect_destroy_handler);
if (!l)
return NULL;
return container_of(l, struct gl_renderer_color_effect,
destroy_listener);
}
static const struct gl_renderer_color_effect *
gl_renderer_color_effect_from(struct weston_output_color_effect *effect)
{
struct gl_renderer_color_effect *gl_effect;
/* Cached effect */
gl_effect = gl_renderer_color_effect_get(effect);
if (gl_effect)
return gl_effect;
/* New effect */
return gl_renderer_color_effect_create(effect);
}
bool
gl_shader_config_set_color_effect(struct gl_renderer *gr,
struct gl_shader_config *sconf,
struct weston_output_color_effect *effect)
{
const struct gl_renderer_color_effect *gl_effect;
if (!effect) {
sconf->req.color_effect = SHADER_COLOR_EFFECT_NONE;
return true;
}
gl_effect = gl_renderer_color_effect_from(effect);
if (!gl_effect)
return false;
switch (effect->type) {
case WESTON_OUTPUT_COLOR_EFFECT_TYPE_INVERSION:
sconf->req.color_effect = SHADER_COLOR_EFFECT_INVERSION;
break;
case WESTON_OUTPUT_COLOR_EFFECT_TYPE_CVD_CORRECTION:
sconf->req.color_effect = SHADER_COLOR_EFFECT_CVD_CORRECTION;
sconf->color_effect.cvd_correction.simulation = effect->u.cvd.simulation;
sconf->color_effect.cvd_correction.redistribution = effect->u.cvd.redistribution;
break;
}
weston_assert_u32_ne(gr->compositor, sconf->req.color_effect,
SHADER_COLOR_EFFECT_NONE);
return true;
}

View file

@ -52,6 +52,11 @@
/* static const char fragment_shader[]; fragment.glsl */
#include "fragment-shader.h"
struct gl_shader_cvd_correction_uniforms {
GLint simulation_uniform;
GLint redistribution_uniform;
};
union gl_shader_color_curve_uniforms {
struct {
GLint tex_2d_uniform;
@ -90,6 +95,7 @@ struct gl_shader {
GLint view_alpha_uniform;
GLint color_uniform;
GLint tint_uniform;
struct gl_shader_cvd_correction_uniforms cvd;
union gl_shader_color_curve_uniforms color_pre_curve;
union gl_shader_color_mapping_uniforms color_mapping;
union gl_shader_color_curve_uniforms color_post_curve;
@ -126,6 +132,20 @@ gl_shader_texture_variant_to_string(enum gl_shader_texture_variant v)
return "!?!?"; /* never reached */
}
static const char *
gl_shader_color_effect_to_string(enum gl_shader_color_effect kind)
{
switch(kind) {
#define CASERET(x) case x: return #x;
CASERET(SHADER_COLOR_EFFECT_NONE)
CASERET(SHADER_COLOR_EFFECT_INVERSION)
CASERET(SHADER_COLOR_EFFECT_CVD_CORRECTION)
#undef CASERET
}
return "!?!?"; /* never reached */
}
static const char *
gl_shader_color_curve_to_string(enum gl_shader_color_curve kind)
{
@ -222,9 +242,10 @@ create_shader_description_string(const struct gl_shader_requirements *req)
int size;
char *str;
size = asprintf(&str, "%s %s %s %s %s %cinput_is_premult %ctint",
size = asprintf(&str, "%s %s %s %s %s %s %cinput_is_premult %ctint",
gl_shader_texcoord_input_to_string(req->texcoord_input),
gl_shader_texture_variant_to_string(req->variant),
gl_shader_color_effect_to_string(req->color_effect),
gl_shader_color_curve_to_string(req->color_pre_curve),
gl_shader_color_mapping_to_string(req->color_mapping),
gl_shader_color_curve_to_string(req->color_post_curve),
@ -266,6 +287,7 @@ create_fragment_shader_config_string(const struct gl_shader_requirements *req)
"#define DEF_COLOR_PRE_CURVE %s\n"
"#define DEF_COLOR_MAPPING %s\n"
"#define DEF_COLOR_POST_CURVE %s\n"
"#define DEF_COLOR_EFFECT %s\n"
"#define DEF_VARIANT %s\n",
ARRAY_LENGTH(((union weston_color_curve_parametric_chan_data){}).data),
req->tint ? "true" : "false",
@ -274,6 +296,7 @@ create_fragment_shader_config_string(const struct gl_shader_requirements *req)
gl_shader_color_curve_to_string(req->color_pre_curve),
gl_shader_color_mapping_to_string(req->color_mapping),
gl_shader_color_curve_to_string(req->color_post_curve),
gl_shader_color_effect_to_string(req->color_effect),
gl_shader_texture_variant_to_string(req->variant));
if (size < 0)
return NULL;
@ -443,6 +466,16 @@ gl_shader_create(struct gl_renderer *gr,
shader->tint_uniform = -1;
}
if (requirements->color_effect == SHADER_COLOR_EFFECT_CVD_CORRECTION) {
shader->cvd.simulation_uniform =
glGetUniformLocation(shader->program, "color_cvd_simulation");
shader->cvd.redistribution_uniform =
glGetUniformLocation(shader->program, "color_cvd_redistribution");
} else {
shader->cvd.simulation_uniform = -1;
shader->cvd.redistribution_uniform = -1;
}
get_curve_uniform_locations(gr, &shader->color_pre_curve,
requirements->color_pre_curve,
shader->program, "color_pre_curve");
@ -807,6 +840,15 @@ gl_shader_load_config(struct gl_renderer *gr,
gl_texture_parameters_flush(gr, &sconf->input_param[i]);
}
if (shader->cvd.simulation_uniform)
glUniformMatrix3fv(shader->cvd.simulation_uniform,
1, GL_FALSE,
sconf->color_effect.cvd_correction.simulation.colmaj);
if (shader->cvd.redistribution_uniform)
glUniformMatrix3fv(shader->cvd.redistribution_uniform,
1, GL_FALSE,
sconf->color_effect.cvd_correction.redistribution.colmaj);
/* Fixed texture unit for color_pre_curve LUT if it is available */
gl_shader_load_config_curve(gr->compositor, sconf->req.color_pre_curve,
&sconf->color_pre_curve, &shader->color_pre_curve,

View file

@ -586,6 +586,28 @@ is true, load the given ICC file as the output color profile. This works only
on DRM, headless, wayland, and x11 backends, and for remoting and pipewire
outputs.
.TP 7
.BI "color-effect=" name
Use the given color effect for the output. For now we only provide color effects
for color vision deficiencies (CVD). Only works when option
.B color-management
is false.
The effect can be one of the following strings:
.RS 11
.TP
.B inversion
color inversion
.TP
.B deuteranopia
color correction (not simulation) for deuteranopia
.TP
.B protanopia
color correction (not simulation) for protanopia
.TP
.B tritanopia
color correction (not simulation) for tritanopia
.RE
.TP 7
.BI "seat=" name
The logical seat name that this output should be associated with. If this
is set then the seat's input will be confined to the output that has the seat