mirror of
https://github.com/elementary/gala.git
synced 2024-08-17 02:50:38 +03:00
Prototype colorblind correction filters (#1595)
This commit is contained in:
parent
d408d394db
commit
3bf28b0ddf
@ -22,5 +22,7 @@
|
|||||||
</gresource>
|
</gresource>
|
||||||
<gresource prefix="/io/elementary/desktop/gala">
|
<gresource prefix="/io/elementary/desktop/gala">
|
||||||
<file compressed="true">gala.css</file>
|
<file compressed="true">gala.css</file>
|
||||||
|
<file compressed="true">shaders/colorblindness-correction.vert</file>
|
||||||
|
<file compressed="true">shaders/monochrome.vert</file>
|
||||||
</gresource>
|
</gresource>
|
||||||
</gresources>
|
</gresources>
|
||||||
|
@ -394,4 +394,38 @@
|
|||||||
<description>The action that corresponds to performing a horizontal swipe gesture with four fingers</description>
|
<description>The action that corresponds to performing a horizontal swipe gesture with four fingers</description>
|
||||||
</key>
|
</key>
|
||||||
</schema>
|
</schema>
|
||||||
|
|
||||||
|
<enum id="GalaColorblindnessFilterType">
|
||||||
|
<value nick="none" value="0"/>
|
||||||
|
<value nick="protanopia" value="1"/>
|
||||||
|
<value nick="protanopia-high-contrast" value="2"/>
|
||||||
|
<value nick="deuteranopia" value="3"/>
|
||||||
|
<value nick="deuteranopia-high-contrast" value="4"/>
|
||||||
|
<value nick="tritanopia" value="5"/>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<schema path="/io/elementary/desktop/wm/accessibility/" id="io.elementary.desktop.wm.accessibility">
|
||||||
|
<key enum="GalaColorblindnessFilterType" name="colorblindness-correction-filter">
|
||||||
|
<default>"none"</default>
|
||||||
|
<summary>Colorblind correction filter.</summary>
|
||||||
|
<description></description>
|
||||||
|
</key>
|
||||||
|
<key type="d" name="colorblindness-correction-filter-strength">
|
||||||
|
<range min="0.0" max="1.0"/>
|
||||||
|
<default>1.0</default>
|
||||||
|
<summary>The strength of colorblindness correction filter.</summary>
|
||||||
|
<description></description>
|
||||||
|
</key>
|
||||||
|
<key type="b" name="enable-monochrome-filter">
|
||||||
|
<default>false</default>
|
||||||
|
<summary>Enable monochrome filter.</summary>
|
||||||
|
<description></description>
|
||||||
|
</key>
|
||||||
|
<key type="d" name="monochrome-filter-strength">
|
||||||
|
<range min="0.0" max="1.0"/>
|
||||||
|
<default>1.0</default>
|
||||||
|
<summary>The strength of monochrome filter.</summary>
|
||||||
|
<description></description>
|
||||||
|
</key>
|
||||||
|
</schema>
|
||||||
</schemalist>
|
</schemalist>
|
||||||
|
90
data/shaders/colorblindness-correction.vert
Normal file
90
data/shaders/colorblindness-correction.vert
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022-2023 GdH <G-dH@github.com>
|
||||||
|
* Copyright 2023 elementary, Inc. <https://elementary.io>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
uniform sampler2D tex;
|
||||||
|
uniform int COLORBLIND_MODE;
|
||||||
|
uniform float STRENGTH;
|
||||||
|
void main() {
|
||||||
|
vec4 c = texture2D(tex, cogl_tex_coord0_in.xy);
|
||||||
|
|
||||||
|
// RGB to LMS matrix
|
||||||
|
float L = (17.8824f * c.r) + (43.5161f * c.g) + (4.11935f * c.b);
|
||||||
|
float M = (3.45565f * c.r) + (27.1554f * c.g) + (3.86714f * c.b);
|
||||||
|
float S = (0.0299566f * c.r) + (0.184309f * c.g) + (1.46709f * c.b);
|
||||||
|
|
||||||
|
float l, m, s;
|
||||||
|
|
||||||
|
// Remove invisible colors
|
||||||
|
if ( COLORBLIND_MODE == 1 || COLORBLIND_MODE == 2) { // Protanopia - reds are greatly reduced
|
||||||
|
l = 0.0f * L + 2.02344f * M + -2.52581f * S;
|
||||||
|
m = 0.0f * L + 1.0f * M + 0.0f * S;
|
||||||
|
s = 0.0f * L + 0.0f * M + 1.0f * S;
|
||||||
|
} else if ( COLORBLIND_MODE == 3 || COLORBLIND_MODE == 4) { // Deuteranopia - greens are greatly reduced
|
||||||
|
l = 1.0f * L + 0.0f * M + 0.0f * S;
|
||||||
|
m = 0.494207f * L + 0.0f * M + 1.24827f * S;
|
||||||
|
s = 0.0f * L + 0.0f * M + 1.0f * S;
|
||||||
|
} else if ( COLORBLIND_MODE == 5 ) { // Tritanopia - blues are greatly reduced (1 of 10 000)
|
||||||
|
l = 1.0f * L + 0.0f * M + 0.0f * S;
|
||||||
|
m = 0.0f * L + 1.0f * M + 0.0f * S;
|
||||||
|
// GdH - trinatopia vector calculated by me, all public sources were off
|
||||||
|
s = -0.012491378299329402f * L + 0.07203451899279534f * M + 0.0f * S;
|
||||||
|
}
|
||||||
|
|
||||||
|
// LMS to RGB matrix conversion
|
||||||
|
vec4 error;
|
||||||
|
error.r = (0.0809444479f * l) + (-0.130504409f * m) + (0.116721066f * s);
|
||||||
|
error.g = (-0.0102485335f * l) + (0.0540193266f * m) + (-0.113614708f * s);
|
||||||
|
error.b = (-0.000365296938f * l) + (-0.00412161469f * m) + (0.693511405f * s);
|
||||||
|
// The error is what they see
|
||||||
|
|
||||||
|
// ratio between original and error colors allows adjusting filter for weaker forms of dichromacy
|
||||||
|
error = error * STRENGTH + c * (1.0 - STRENGTH);
|
||||||
|
error.a = 1.0;
|
||||||
|
|
||||||
|
// Isolate invisible colors to color vision deficiency (calculate error matrix)
|
||||||
|
error = (c - error);
|
||||||
|
|
||||||
|
// Shift colors
|
||||||
|
vec4 correction;
|
||||||
|
// protanopia / protanomaly corrections
|
||||||
|
if ( COLORBLIND_MODE == 1 ) {
|
||||||
|
//(kwin effect values)
|
||||||
|
correction.r = error.r * 0.56667 + error.g * 0.43333 + error.b * 0.00000;
|
||||||
|
correction.g = error.r * 0.55833 + error.g * 0.44267 + error.b * 0.00000;
|
||||||
|
correction.b = error.r * 0.00000 + error.g * 0.24167 + error.b * 0.75833;
|
||||||
|
// tries to mimic Android, GdH
|
||||||
|
//correction.r = error.r * -0.5 + error.g * -0.3 + error.b * 0.0;
|
||||||
|
//correction.g = error.r * 0.2 + error.g * 0.0 + error.b * 0.0;
|
||||||
|
//correction.b = error.r * 0.2 + error.g * 1.0 + error.b * 1.0;
|
||||||
|
// protanopia / protanomaly high contrast G-R corrections
|
||||||
|
} else if ( COLORBLIND_MODE == 2 ) {
|
||||||
|
correction.r = error.r * 2.56667 + error.g * 0.43333 + error.b * 0.00000;
|
||||||
|
correction.g = error.r * 1.55833 + error.g * 0.44267 + error.b * 0.00000;
|
||||||
|
correction.b = error.r * 0.00000 + error.g * 0.24167 + error.b * 0.75833;
|
||||||
|
// deuteranopia / deuteranomaly corrections (tries to mimic Android, GdH)
|
||||||
|
} else if ( COLORBLIND_MODE == 3 ) {
|
||||||
|
correction.r = error.r * -0.7 + error.g * 0.0 + error.b * 0.0;
|
||||||
|
correction.g = error.r * 0.5 + error.g * 1.0 + error.b * 0.0;
|
||||||
|
correction.b = error.r * -0.3 + error.g * 0.0 + error.b * 1.0;
|
||||||
|
// deuteranopia / deuteranomaly high contrast R-G corrections
|
||||||
|
} else if ( COLORBLIND_MODE == 4 ) {
|
||||||
|
correction.r = error.r * -1.5 + error.g * 1.5 + error.b * 0.0;
|
||||||
|
correction.g = error.r * -1.5 + error.g * 1.5 + error.b * 0.0;
|
||||||
|
correction.b = error.r * 1.5 + error.g * 0.0 + error.b * 0.0;
|
||||||
|
// tritanopia / tritanomaly corrections (GdH)
|
||||||
|
} else if ( COLORBLIND_MODE == 5 ) {
|
||||||
|
correction.r = error.r * 0.3 + error.g * 0.5 + error.b * 0.4;
|
||||||
|
correction.g = error.r * 0.5 + error.g * 0.7 + error.b * 0.3;
|
||||||
|
correction.b = error.r * 0.0 + error.g * 0.0 + error.b * 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add compensation to original values
|
||||||
|
correction = c + correction;
|
||||||
|
correction.a = c.a;
|
||||||
|
|
||||||
|
cogl_color_out = correction;
|
||||||
|
}
|
19
data/shaders/monochrome.vert
Normal file
19
data/shaders/monochrome.vert
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 elementary, Inc. <https://elementary.io>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
uniform sampler2D tex;
|
||||||
|
uniform float STRENGTH;
|
||||||
|
void main() {
|
||||||
|
vec2 uv = cogl_tex_coord0_in.xy;
|
||||||
|
vec4 sample = texture2D (tex, uv);
|
||||||
|
vec3 luminance = vec3 (0.2126, 0.7512, 0.0722);
|
||||||
|
float gray = luminance.r * sample.r + luminance.g * sample.g + luminance.b * sample.b;
|
||||||
|
cogl_color_out = vec4 (
|
||||||
|
sample.r * (1.0 - STRENGTH) + gray * STRENGTH,
|
||||||
|
sample.g * (1.0 - STRENGTH) + gray * STRENGTH,
|
||||||
|
sample.b * (1.0 - STRENGTH) + gray * STRENGTH,
|
||||||
|
sample.a
|
||||||
|
) ;
|
||||||
|
}
|
47
src/ColorFilters/ColorblindnessCorrectionEffect.vala
Normal file
47
src/ColorFilters/ColorblindnessCorrectionEffect.vala
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 elementary, Inc. <https://elementary.io>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class Gala.ColorblindnessCorrectionEffect : Clutter.ShaderEffect {
|
||||||
|
public const string EFFECT_NAME = "colorblindness-correction-filter";
|
||||||
|
|
||||||
|
private int _mode;
|
||||||
|
public int mode {
|
||||||
|
get { return _mode; }
|
||||||
|
construct set {
|
||||||
|
_mode = value;
|
||||||
|
set_uniform_value ("COLORBLIND_MODE", _mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private double _strength;
|
||||||
|
public double strength {
|
||||||
|
get { return _strength; }
|
||||||
|
construct set {
|
||||||
|
_strength = value;
|
||||||
|
|
||||||
|
set_uniform_value ("STRENGTH", value);
|
||||||
|
queue_repaint ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Used for fading in and out the effect, since you can't add transitions to effects.
|
||||||
|
*/
|
||||||
|
public Clutter.Actor? transition_actor { get; set; default = null; }
|
||||||
|
|
||||||
|
public ColorblindnessCorrectionEffect (int mode, double strength) {
|
||||||
|
Object (
|
||||||
|
shader_type: Clutter.ShaderType.FRAGMENT_SHADER,
|
||||||
|
mode: mode,
|
||||||
|
strength: strength
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
var bytes = GLib.resources_lookup_data ("/io/elementary/desktop/gala/shaders/colorblindness-correction.vert", GLib.ResourceLookupFlags.NONE);
|
||||||
|
set_shader_source ((string) bytes.get_data ());
|
||||||
|
} catch (Error e) {
|
||||||
|
critical ("Unable to load colorblindness-correction.vert: %s", e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
293
src/ColorFilters/FilterManager.vala
Normal file
293
src/ColorFilters/FilterManager.vala
Normal file
@ -0,0 +1,293 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 elementary, Inc. <https://elementary.io>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class Gala.FilterManager : Object {
|
||||||
|
private const string TRANSITION_NAME = "strength-transition";
|
||||||
|
private const int TRANSITION_DURATION = 500;
|
||||||
|
|
||||||
|
private static FilterManager instance;
|
||||||
|
private static GLib.Settings settings;
|
||||||
|
public WindowManager wm { get; construct; }
|
||||||
|
|
||||||
|
public static void init (WindowManager wm) {
|
||||||
|
if (instance != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
instance = new FilterManager (wm);
|
||||||
|
}
|
||||||
|
|
||||||
|
private FilterManager (WindowManager wm) {
|
||||||
|
Object (wm: wm);
|
||||||
|
}
|
||||||
|
|
||||||
|
static construct {
|
||||||
|
settings = new GLib.Settings ("io.elementary.desktop.wm.accessibility");
|
||||||
|
}
|
||||||
|
|
||||||
|
construct {
|
||||||
|
settings.changed["colorblindness-correction-filter"].connect (update_colorblindness_filter);
|
||||||
|
settings.changed["colorblindness-correction-filter-strength"].connect (update_colorblindness_strength);
|
||||||
|
settings.changed["enable-monochrome-filter"].connect (update_monochrome_filter);
|
||||||
|
settings.changed["monochrome-filter-strength"].connect (update_monochrome_strength);
|
||||||
|
|
||||||
|
load_filters ();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void load_filters () {
|
||||||
|
load_colorblindness_filter ();
|
||||||
|
load_monochrome_filter ();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void load_colorblindness_filter () {
|
||||||
|
// When gala launches and there is an effect active, it shouldn't be faded in
|
||||||
|
var filter_variant = settings.get_enum ("colorblindness-correction-filter");
|
||||||
|
var strength = settings.get_double ("colorblindness-correction-filter-strength");
|
||||||
|
if (filter_variant != 0 && strength > 0.0) {
|
||||||
|
wm.stage.add_effect_with_name (
|
||||||
|
ColorblindnessCorrectionEffect.EFFECT_NAME,
|
||||||
|
new ColorblindnessCorrectionEffect (filter_variant, strength)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update_colorblindness_filter () {
|
||||||
|
var filter_variant = settings.get_enum ("colorblindness-correction-filter");
|
||||||
|
var strength = settings.get_double ("colorblindness-correction-filter-strength");
|
||||||
|
|
||||||
|
// Fade out applied effects
|
||||||
|
foreach (unowned var _effect in wm.stage.get_effects ()) {
|
||||||
|
if (_effect is ColorblindnessCorrectionEffect) {
|
||||||
|
unowned var effect = (ColorblindnessCorrectionEffect) _effect;
|
||||||
|
|
||||||
|
if (effect.mode == filter_variant) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since you can't add a transition to an effect
|
||||||
|
// add it to a transition actor and bind one of its properties to the effect
|
||||||
|
|
||||||
|
// stop transition (if there is one in progress)
|
||||||
|
if (effect.transition_actor != null) {
|
||||||
|
effect.transition_actor.destroy ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a new transition
|
||||||
|
var transition = new Clutter.PropertyTransition ("scale_x") {
|
||||||
|
duration = TRANSITION_DURATION,
|
||||||
|
progress_mode = Clutter.AnimationMode.LINEAR,
|
||||||
|
remove_on_complete = true
|
||||||
|
};
|
||||||
|
transition.set_from_value (effect.strength);
|
||||||
|
transition.set_to_value (0.0);
|
||||||
|
|
||||||
|
// create a transition actor and bind its `scale_x` to effect's `strength`
|
||||||
|
effect.transition_actor = new Clutter.Actor () {
|
||||||
|
visible = false
|
||||||
|
};
|
||||||
|
wm.ui_group.add_child (effect.transition_actor);
|
||||||
|
effect.transition_actor.bind_property ("scale_x", effect, "strength");
|
||||||
|
|
||||||
|
transition.completed.connect (() => {
|
||||||
|
effect.transition_actor.destroy ();
|
||||||
|
wm.stage.remove_effect (effect);
|
||||||
|
});
|
||||||
|
|
||||||
|
effect.transition_actor.add_transition (TRANSITION_NAME, transition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply a new filter
|
||||||
|
if (filter_variant == 0 || strength == 0.0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var new_effect = new ColorblindnessCorrectionEffect (filter_variant, 0.0);
|
||||||
|
wm.stage.add_effect_with_name (ColorblindnessCorrectionEffect.EFFECT_NAME, new_effect);
|
||||||
|
|
||||||
|
// Transition new effect in the same way
|
||||||
|
var new_transition = new Clutter.PropertyTransition ("scale_x") {
|
||||||
|
duration = TRANSITION_DURATION,
|
||||||
|
progress_mode = Clutter.AnimationMode.LINEAR,
|
||||||
|
remove_on_complete = true
|
||||||
|
};
|
||||||
|
new_transition.set_from_value (0.0);
|
||||||
|
new_transition.set_to_value (strength);
|
||||||
|
|
||||||
|
new_effect.transition_actor = new Clutter.Actor () {
|
||||||
|
visible = false
|
||||||
|
};
|
||||||
|
wm.ui_group.add_child (new_effect.transition_actor);
|
||||||
|
new_effect.transition_actor.bind_property ("scale_x", new_effect, "strength");
|
||||||
|
|
||||||
|
new_transition.completed.connect (() => {
|
||||||
|
new_effect.transition_actor.destroy ();
|
||||||
|
});
|
||||||
|
|
||||||
|
new_effect.transition_actor.add_transition (TRANSITION_NAME, new_transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update_colorblindness_strength () {
|
||||||
|
var filter_variant = settings.get_enum ("colorblindness-correction-filter");
|
||||||
|
var strength = settings.get_double ("colorblindness-correction-filter-strength");
|
||||||
|
|
||||||
|
foreach (unowned var _effect in wm.stage.get_effects ()) {
|
||||||
|
if (_effect is ColorblindnessCorrectionEffect) {
|
||||||
|
unowned var effect = (ColorblindnessCorrectionEffect) _effect;
|
||||||
|
|
||||||
|
if (effect.mode != filter_variant) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop transition (if there is one in progress)
|
||||||
|
if (effect.transition_actor != null) {
|
||||||
|
effect.transition_actor.destroy ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a new transition
|
||||||
|
var transition = new Clutter.PropertyTransition ("scale_x") {
|
||||||
|
duration = TRANSITION_DURATION,
|
||||||
|
progress_mode = Clutter.AnimationMode.LINEAR,
|
||||||
|
remove_on_complete = true
|
||||||
|
};
|
||||||
|
transition.set_from_value (effect.strength);
|
||||||
|
transition.set_to_value (strength);
|
||||||
|
|
||||||
|
// create a transition actor and bind its `scale_x` to effect's `strength`
|
||||||
|
effect.transition_actor = new Clutter.Actor () {
|
||||||
|
visible = false
|
||||||
|
};
|
||||||
|
wm.ui_group.add_child (effect.transition_actor);
|
||||||
|
effect.transition_actor.bind_property ("scale_x", effect, "strength");
|
||||||
|
|
||||||
|
transition.completed.connect (() => {
|
||||||
|
effect.transition_actor.destroy ();
|
||||||
|
});
|
||||||
|
|
||||||
|
effect.transition_actor.add_transition (TRANSITION_NAME, transition);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void load_monochrome_filter () {
|
||||||
|
// When gala launches and there is an effect active, it shouldn't be faded in
|
||||||
|
var enable = settings.get_boolean ("enable-monochrome-filter");
|
||||||
|
var strength = settings.get_double ("monochrome-filter-strength");
|
||||||
|
if (enable && strength > 0.0) {
|
||||||
|
wm.stage.add_effect_with_name (
|
||||||
|
MonochromeEffect.EFFECT_NAME,
|
||||||
|
new MonochromeEffect (strength)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update_monochrome_filter () {
|
||||||
|
var enabled = settings.get_boolean ("enable-monochrome-filter");
|
||||||
|
var strength = settings.get_double ("monochrome-filter-strength");
|
||||||
|
unowned var effect = (MonochromeEffect) wm.stage.get_effect (MonochromeEffect.EFFECT_NAME);
|
||||||
|
|
||||||
|
if ((effect != null) == enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fade out applied effects
|
||||||
|
if (effect != null) {
|
||||||
|
// Since you can't add a transition to an effect
|
||||||
|
// add it to a transition actor and bind one of its properties to the effect
|
||||||
|
|
||||||
|
// stop transition (if there is one in progress)
|
||||||
|
if (effect.transition_actor != null) {
|
||||||
|
effect.transition_actor.destroy ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a new transition
|
||||||
|
var transition = new Clutter.PropertyTransition ("scale_x") {
|
||||||
|
duration = TRANSITION_DURATION,
|
||||||
|
progress_mode = Clutter.AnimationMode.LINEAR,
|
||||||
|
remove_on_complete = true
|
||||||
|
};
|
||||||
|
transition.set_from_value (effect.strength);
|
||||||
|
transition.set_to_value (0.0);
|
||||||
|
|
||||||
|
// create a transition actor and bind its `scale_x` to effect's `strength`
|
||||||
|
effect.transition_actor = new Clutter.Actor () {
|
||||||
|
visible = false
|
||||||
|
};
|
||||||
|
wm.ui_group.add_child (effect.transition_actor);
|
||||||
|
effect.transition_actor.bind_property ("scale_x", effect, "strength");
|
||||||
|
|
||||||
|
transition.completed.connect (() => {
|
||||||
|
effect.transition_actor.destroy ();
|
||||||
|
wm.stage.remove_effect (effect);
|
||||||
|
});
|
||||||
|
|
||||||
|
effect.transition_actor.add_transition (TRANSITION_NAME, transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply a new filter
|
||||||
|
if (!enabled || strength == 0.0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var new_effect = new MonochromeEffect (0.0);
|
||||||
|
wm.stage.add_effect_with_name (MonochromeEffect.EFFECT_NAME, new_effect);
|
||||||
|
|
||||||
|
// Transition new effect in the same way
|
||||||
|
var new_transition = new Clutter.PropertyTransition ("scale_x") {
|
||||||
|
duration = TRANSITION_DURATION,
|
||||||
|
progress_mode = Clutter.AnimationMode.LINEAR,
|
||||||
|
remove_on_complete = true
|
||||||
|
};
|
||||||
|
new_transition.set_from_value (0.0);
|
||||||
|
new_transition.set_to_value (strength);
|
||||||
|
|
||||||
|
new_effect.transition_actor = new Clutter.Actor () {
|
||||||
|
visible = false
|
||||||
|
};
|
||||||
|
wm.ui_group.add_child (new_effect.transition_actor);
|
||||||
|
new_effect.transition_actor.bind_property ("scale_x", new_effect, "strength");
|
||||||
|
|
||||||
|
new_transition.completed.connect (() => {
|
||||||
|
new_effect.transition_actor.destroy ();
|
||||||
|
});
|
||||||
|
|
||||||
|
new_effect.transition_actor.add_transition (TRANSITION_NAME, new_transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update_monochrome_strength () {
|
||||||
|
var strength = settings.get_double ("monochrome-filter-strength");
|
||||||
|
|
||||||
|
unowned var effect = (MonochromeEffect) wm.stage.get_effect (MonochromeEffect.EFFECT_NAME);
|
||||||
|
|
||||||
|
// stop transition (if there is one in progress)
|
||||||
|
if (effect.transition_actor != null) {
|
||||||
|
effect.transition_actor.destroy ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a new transition
|
||||||
|
var transition = new Clutter.PropertyTransition ("scale_x") {
|
||||||
|
duration = TRANSITION_DURATION,
|
||||||
|
progress_mode = Clutter.AnimationMode.LINEAR,
|
||||||
|
remove_on_complete = true
|
||||||
|
};
|
||||||
|
transition.set_from_value (effect.strength);
|
||||||
|
transition.set_to_value (strength);
|
||||||
|
|
||||||
|
// create a transition actor and bind its `scale_x` to effect's `strength`
|
||||||
|
effect.transition_actor = new Clutter.Actor () {
|
||||||
|
visible = false
|
||||||
|
};
|
||||||
|
wm.ui_group.add_child (effect.transition_actor);
|
||||||
|
effect.transition_actor.bind_property ("scale_x", effect, "strength");
|
||||||
|
|
||||||
|
transition.completed.connect (() => {
|
||||||
|
effect.transition_actor.destroy ();
|
||||||
|
});
|
||||||
|
|
||||||
|
effect.transition_actor.add_transition (TRANSITION_NAME, transition);
|
||||||
|
}
|
||||||
|
}
|
38
src/ColorFilters/MonochromeEffect.vala
Normal file
38
src/ColorFilters/MonochromeEffect.vala
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 elementary, Inc. <https://elementary.io>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class Gala.MonochromeEffect : Clutter.ShaderEffect {
|
||||||
|
public const string EFFECT_NAME = "monochrome-filter";
|
||||||
|
|
||||||
|
private double _strength;
|
||||||
|
public double strength {
|
||||||
|
get { return _strength; }
|
||||||
|
construct set {
|
||||||
|
_strength = value;
|
||||||
|
|
||||||
|
set_uniform_value ("STRENGTH", value);
|
||||||
|
queue_repaint ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Used for fading in and out the effect, since you can't add transitions to effects.
|
||||||
|
*/
|
||||||
|
public Clutter.Actor? transition_actor { get; set; default = null; }
|
||||||
|
|
||||||
|
public MonochromeEffect (double strength) {
|
||||||
|
Object (
|
||||||
|
shader_type: Clutter.ShaderType.FRAGMENT_SHADER,
|
||||||
|
strength: strength
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
var bytes = GLib.resources_lookup_data ("/io/elementary/desktop/gala/shaders/monochrome.vert", GLib.ResourceLookupFlags.NONE);
|
||||||
|
set_shader_source ((string) bytes.get_data ());
|
||||||
|
} catch (Error e) {
|
||||||
|
critical ("Unable to load monochrome.vert: %s", e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -242,6 +242,8 @@ namespace Gala {
|
|||||||
stage.remove_child (top_window_group);
|
stage.remove_child (top_window_group);
|
||||||
ui_group.add_child (top_window_group);
|
ui_group.add_child (top_window_group);
|
||||||
|
|
||||||
|
FilterManager.init (this);
|
||||||
|
|
||||||
/*keybindings*/
|
/*keybindings*/
|
||||||
var keybinding_settings = new GLib.Settings (Config.SCHEMA + ".keybindings");
|
var keybinding_settings = new GLib.Settings (Config.SCHEMA + ".keybindings");
|
||||||
|
|
||||||
|
@ -29,6 +29,9 @@ gala_bin_sources = files(
|
|||||||
'Background/BackgroundManager.vala',
|
'Background/BackgroundManager.vala',
|
||||||
'Background/BackgroundSource.vala',
|
'Background/BackgroundSource.vala',
|
||||||
'Background/SystemBackground.vala',
|
'Background/SystemBackground.vala',
|
||||||
|
'ColorFilters/ColorblindnessCorrectionEffect.vala',
|
||||||
|
'ColorFilters/FilterManager.vala',
|
||||||
|
'ColorFilters/MonochromeEffect.vala',
|
||||||
'Gestures/Gesture.vala',
|
'Gestures/Gesture.vala',
|
||||||
'Gestures/GestureSettings.vala',
|
'Gestures/GestureSettings.vala',
|
||||||
'Gestures/GestureTracker.vala',
|
'Gestures/GestureTracker.vala',
|
||||||
|
Loading…
Reference in New Issue
Block a user