From 745426eff227ff556105147a4802540617decd7b Mon Sep 17 00:00:00 2001
From: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com>
Date: Thu, 10 Mar 2022 16:45:50 -0800
Subject: [PATCH] Adds a colorblind accessability testing tool (#65217)
* Adds a colorblind accessability testing tool
I keep finding myself worrying about if things I create will be parsable
for colorblind people. So I've made a debug tool for approximating
different extreme forms of colorblindness.
It's very very much a hack. We can't do the proper correction required
to actually deal directly with long medium and short wavelengths of
light, so we need to rely on approximations. Part of that means say,
bright things being brighter then they ought to be. S not how people
actually experience things, but it's not something we can do anything
about in byond.
Anyway uh, it works by taking color matrixes, and using the plane master
grouping system floyd added to apply them to most all parts of the game
you would want to color correct.
There's some slight fragility here, but I couldn't think of a better way
of handling it.
We also need to deal with planes that have BLEND_MULTIPLY as their
blendmode, since that fucks up the filter. I've come up with a hack for
it, since I wanted to avoid breaking anything.
Oh and since I want it to apply to huds too I added plane masters to
represent them. I think that's about it.
Co-authored-by: Mothblocks <35135081+Mothblocks@users.noreply.github.com>
---
code/__DEFINES/layers.dm | 1 +
code/_onclick/hud/rendering/plane_master.dm | 8 ++
.../hud/rendering/plane_master_controller.dm | 26 ++++++
code/modules/admin/admin_verbs.dm | 1 +
code/modules/admin/holder2.dm | 1 +
code/modules/admin/verbs/color_blind_test.dm | 73 ++++++++++++++++
code/modules/admin/verbs/debug.dm | 9 ++
tgstation.dme | 1 +
.../tgui/interfaces/ColorBlindTester.js | 85 +++++++++++++++++++
9 files changed, 205 insertions(+)
create mode 100644 code/modules/admin/verbs/color_blind_test.dm
create mode 100644 tgui/packages/tgui/interfaces/ColorBlindTester.js
diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm
index 7ce97c59206..4ce1c8b10f0 100644
--- a/code/__DEFINES/layers.dm
+++ b/code/__DEFINES/layers.dm
@@ -213,3 +213,4 @@
///Plane master controller keys
#define PLANE_MASTERS_GAME "plane_masters_game"
+#define PLANE_MASTERS_COLORBLIND "plane_masters_colorblind"
diff --git a/code/_onclick/hud/rendering/plane_master.dm b/code/_onclick/hud/rendering/plane_master.dm
index 288a8b060f1..bb5883b9b02 100644
--- a/code/_onclick/hud/rendering/plane_master.dm
+++ b/code/_onclick/hud/rendering/plane_master.dm
@@ -269,3 +269,11 @@
render_target = FIELD_OF_VISION_BLOCKER_RENDER_TARGET
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
render_relay_plane = null
+
+/atom/movable/screen/plane_master/hud
+ name = "HUD plane"
+ plane = HUD_PLANE
+
+/atom/movable/screen/plane_master/above_hud
+ name = "above HUD plane"
+ plane = ABOVE_HUD_PLANE
diff --git a/code/_onclick/hud/rendering/plane_master_controller.dm b/code/_onclick/hud/rendering/plane_master_controller.dm
index 8d0c98cbd90..faae549bc70 100644
--- a/code/_onclick/hud/rendering/plane_master_controller.dm
+++ b/code/_onclick/hud/rendering/plane_master_controller.dm
@@ -95,4 +95,30 @@ INITIALIZE_IMMEDIATE(/atom/movable/plane_master_controller)
LIGHTING_PLANE,
)
+/// Controller of all planes we're ok with changing with colorblind logic
+/atom/movable/plane_master_controller/colorblind
+ name = PLANE_MASTERS_COLORBLIND
+ controlled_planes = list(
+ PLANE_SPACE_PARALLAX,
+ GRAVITY_PULSE_PLANE,
+ FLOOR_PLANE,
+ GAME_PLANE,
+ GAME_PLANE_FOV_HIDDEN,
+ GAME_PLANE_UPPER,
+ GAME_PLANE_UPPER_FOV_HIDDEN,
+ ABOVE_GAME_PLANE,
+ MASSIVE_OBJ_PLANE,
+ GHOST_PLANE,
+ POINT_PLANE,
+ RAD_TEXT_PLANE,
+ LIGHTING_PLANE,
+ O_LIGHTING_VISUAL_PLANE,
+ ABOVE_LIGHTING_PLANE,
+ CAMERA_STATIC_PLANE,
+ ATMOS_GROUP_PLANE,
+ FULLSCREEN_PLANE,
+ RUNECHAT_PLANE,
+ HUD_PLANE,
+ ABOVE_HUD_PLANE,
+ )
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 54f02bb4378..603b92095e2 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -162,6 +162,7 @@ GLOBAL_PROTECT(admin_verbs_debug)
/client/proc/set_dynex_scale,
/client/proc/cmd_display_del_log,
/client/proc/outfit_manager,
+ /client/proc/open_colorblind_test,
/client/proc/generate_wikichem_list,
/client/proc/modify_goals,
/client/proc/debug_huds,
diff --git a/code/modules/admin/holder2.dm b/code/modules/admin/holder2.dm
index 1766af12483..f5b1bcfd848 100644
--- a/code/modules/admin/holder2.dm
+++ b/code/modules/admin/holder2.dm
@@ -32,6 +32,7 @@ GLOBAL_PROTECT(href_token)
var/deadmined
var/datum/filter_editor/filteriffic
+ var/datum/colorblind_tester/color_test = new
/// Whether or not the user tried to connect, but was blocked by 2FA
var/blocked_by_2fa = FALSE
diff --git a/code/modules/admin/verbs/color_blind_test.dm b/code/modules/admin/verbs/color_blind_test.dm
new file mode 100644
index 00000000000..5640149cd0a
--- /dev/null
+++ b/code/modules/admin/verbs/color_blind_test.dm
@@ -0,0 +1,73 @@
+/// Used to test the game for issues with different types of color blindness
+/// WARNING ASSHOLE: Because we can only apply matrixes, and can't preform gamma correction
+/// https://web.archive.org/web/20220227030606/https://ixora.io/projects/colorblindness/color-blindness-simulation-research/
+/// The results of this tool aren't perfect. It's way better then nothing, but IT IS NOT A PROPER SIMULATION
+/// Please do not make us look like assholes by assuming it is. Thanks.
+/datum/colorblind_tester
+ /// List of simulated blindness -> matrix to use
+ /// Most of these matrixes are based off https://web.archive.org/web/20220227030606/https://ixora.io/projects/colorblindness/color-blindness-simulation-research/
+ /// AGAIN, THESE ARE NOT PERFECT BECAUSE WE CANNOT COMPUTE GAMMA CORRECTION, AND CONVERT SRGB TO LINEAR RGB
+ /// Do not assume this is absolute
+ var/list/color_matrixes = list(
+ "Protanopia" = list(0.56,0.43,0,0, 0.55,0.44,0,0, 0,0.24,0.75,0, 0,0,0,1, 0,0,0,0),
+ "Deuteranopia" = list(0.62,0.37,0,0, 0.70,0.30,0,0, 0,0.30,0.70,0, 0,0,0,1, 0,0,0,0),
+ "Tritanopia" = list(0.95,0.5,0,0, 0,0.43,0.56,0, 0,0.47,0.52,0, 0,0,0,1, 0,0,0,0),
+ "Achromatopsia" = list(0.33,0.33,0.33,0, 0.33,0.33,0.33,0, 0.33,0.33,0.33,0, 0,0,0,1, 0,0,0,0),
+ )
+ var/list/descriptions = list(
+ "Protanopia" = "No long wavelength cones, ends up not being able to see red light. Troubles with blue/green and red/green",
+ "Deuteranopia" = "No medium wavelength cones. Because the red and green parts of light nearly overlap in this space, trouble is mostly with red/green",
+ "Tritanopia" = "No short wavelength cones, so trouble with blue/green and yellow/violet. Aggressively rare, and equally hard to simulate",
+ "Achromatopsia" = "No cones at all, which leads to something close to monochromatic vision"
+ )
+ var/selected_type = ""
+
+/datum/colorblind_tester/ui_state(mob/user)
+ return GLOB.admin_state
+
+/datum/colorblind_tester/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "ColorBlindTester")
+ ui.open()
+
+/datum/colorblind_tester/ui_data()
+ var/list/data = list()
+ data["details"] = descriptions
+ data["selected"] = selected_type
+ return data
+
+/datum/colorblind_tester/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ . = ..()
+ if(.)
+ return
+
+ var/datum/hud/our_hud = ui.user?.hud_used
+ if(!our_hud) // Nothing to act on
+ return
+
+ switch(action)
+ if("set_matrix")
+ set_selected_type(params["name"], our_hud)
+ return TRUE
+ if("clear_matrix")
+ set_selected_type("", our_hud)
+ return TRUE
+
+/datum/colorblind_tester/proc/set_selected_type(selected, datum/hud/remove_from)
+ var/atom/movable/plane_master_controller/colorblind_plane = remove_from.plane_master_controllers[PLANE_MASTERS_COLORBLIND]
+ // This is dumb, but well
+ // The parralax plane has a blend mode of 4, or BLEND_MULTIPLY
+ // It's like that so it is properly masked by darkness and such
+ // The problem is blend modes apply to filters like this too
+ // So I need to manually set and reset its blendmode to allow for proper shading of the background
+ // Sorry...
+ var/atom/movable/screen/plane_master/parralax = colorblind_plane.controlled_planes["[PLANE_SPACE_PARALLAX]"]
+ if(selected_type)
+ colorblind_plane.remove_filter(selected_type)
+ parralax.blend_mode = initial(parralax.blend_mode)
+ selected_type = selected
+ if(selected_type)
+ var/list/matrix = color_matrixes[selected_type]
+ colorblind_plane.add_filter(selected_type, 0, color_matrix_filter(matrix))
+ parralax.blend_mode = BLEND_DEFAULT
diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm
index 8369e4d03ce..4e192d6a9fb 100644
--- a/code/modules/admin/verbs/debug.dm
+++ b/code/modules/admin/verbs/debug.dm
@@ -601,6 +601,15 @@ But you can call procs that are of type /mob/living/carbon/human/proc/ for that
usr << browse(replacetext(SSatoms.InitLog(), "\n", "
"), "window=initlog")
+/client/proc/open_colorblind_test()
+ set category = "Debug"
+ set name = "Colorblind Testing"
+ set desc = "Change your view to a budget version of colorblindness to test for usability"
+
+ if(!holder)
+ return
+ holder.color_test.ui_interact(mob)
+
/client/proc/debug_huds(i as num)
set category = "Debug"
set name = "Debug HUDs"
diff --git a/tgstation.dme b/tgstation.dme
index c1d3e689e33..c3391106d98 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -1860,6 +1860,7 @@
#include "code\modules\admin\verbs\borgpanel.dm"
#include "code\modules\admin\verbs\BrokenInhands.dm"
#include "code\modules\admin\verbs\cinematic.dm"
+#include "code\modules\admin\verbs\color_blind_test.dm"
#include "code\modules\admin\verbs\commandreport.dm"
#include "code\modules\admin\verbs\deadsay.dm"
#include "code\modules\admin\verbs\debug.dm"
diff --git a/tgui/packages/tgui/interfaces/ColorBlindTester.js b/tgui/packages/tgui/interfaces/ColorBlindTester.js
new file mode 100644
index 00000000000..76a1faac965
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/ColorBlindTester.js
@@ -0,0 +1,85 @@
+import { useBackend } from '../backend';
+import { Box, Button, NoticeBox, Section } from '../components';
+import { Window } from '../layouts';
+
+export const ColorBlindTester = (props, context) => {
+ const { act, data } = useBackend(context);
+ const { details } = data;
+ return (
+
+
+
+
+ HEY FUCKOS, these filters are based off VERY OLD and VERY FLAWED
+ matrixes.
+
+
+ There is NO GOOD WAY to do proper color blind simulation in
+ BYOND, because we have no way to extract the gamma of a pixel
+ without iterating all pixels on the screen, which we need to do to
+ properly correct for the human eye.
+
+
+ Because of this, this simulation is very imperfect. You will notice
+ things are much more bright then they should be. This is a direct
+ result of not being able to correct for gamma.
+
+
+ This tool exists so we have at least some form of baseline for
+ accessability, it is nowhere near gospel.
+
+
+ If I find you being a dick to someone over this I will clobber you
+ with a crowbar
+
+
+
+ {Object.keys(details).map((category) => (
+
+ ))}
+
+
+
+ );
+};
+
+const ColorBlindCategory = (props, context) => {
+ const { act, data } = useBackend(context);
+ const { category } = props;
+ const { details, selected } = data;
+ if (category !== selected) {
+ return (
+ act("set_matrix", {
+ name: category,
+ })} />
+ )}>
+ {details[category]}
+
+ );
+ }
+ return (
+ act("clear_matrix")}
+ />
+ )}>
+ {details[category]}
+
+ );
+};