mirror of
https://gitlab.freedesktop.org/wayland/weston.git
synced 2025-12-20 04:40:07 +01:00
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:
parent
1a9cf3bfb6
commit
6fe0cfc2b8
13 changed files with 526 additions and 14 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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 |=
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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 "???";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue