plugins: add colorblindness correction effect
Colorblindness correction shader with adjustable intensity. Can correct for: - Protanopia (Greatly reduced reds) - Deuteranopia (Greatly reduced greens) - Tritanopia (Greatly reduced blues) FEATURE: 474470 FIXED-IN: 6.0master
parent
8d3b4a21c8
commit
b80741d531
@ -0,0 +1,36 @@
|
||||
# SPDX-FileCopyrightText: 2023 Fushan Wen <qydwhotmail@gmail.com>
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
kwin_add_builtin_effect(colorblindnesscorrection
|
||||
colorblindnesscorrection.cpp
|
||||
colorblindnesscorrection.qrc
|
||||
main.cpp
|
||||
)
|
||||
kconfig_add_kcfg_files(colorblindnesscorrection colorblindnesscorrection_settings.kcfgc GENERATE_MOC)
|
||||
target_link_libraries(colorblindnesscorrection PRIVATE
|
||||
kwineffects
|
||||
kwinglutils
|
||||
|
||||
KF6::ConfigCore
|
||||
KF6::ConfigGui
|
||||
)
|
||||
|
||||
# Config
|
||||
if (NOT KWIN_BUILD_KCMS)
|
||||
return()
|
||||
endif()
|
||||
|
||||
kcmutils_add_qml_kcm(kwin_colorblindnesscorrection_config SOURCES colorblindnesscorrection_config.cpp INSTALL_NAMESPACE "kwin/effects/configs" DISABLE_DESKTOP_FILE_GENERATION)
|
||||
kcmutils_generate_module_data(kwin_colorblindnesscorrection_config
|
||||
MODULE_DATA_HEADER colorblindnesscorrection_settingsdata.h
|
||||
MODULE_DATA_CLASS_NAME ColorBlindnessCorrectionSettingsData
|
||||
SETTINGS_HEADERS colorblindnesscorrection_settings.h
|
||||
SETTINGS_CLASSES ColorBlindnessCorrectionSettings
|
||||
)
|
||||
kconfig_add_kcfg_files(kwin_colorblindnesscorrection_config colorblindnesscorrection_settings.kcfgc GENERATE_MOC)
|
||||
target_link_libraries(kwin_colorblindnesscorrection_config
|
||||
KF6::ConfigCore
|
||||
KF6::KCMUtils
|
||||
KF6::KCMUtilsQuick
|
||||
KWinEffectsInterface
|
||||
)
|
@ -0,0 +1,136 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2023 Fushan Wen <qydwhotmail@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "colorblindnesscorrection.h"
|
||||
|
||||
#include <KSharedConfig>
|
||||
|
||||
#include "libkwineffects/glshader.h"
|
||||
#include "libkwineffects/kwineffects.h"
|
||||
|
||||
#include "colorblindnesscorrection_settings.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(KWIN_COLORBLINDNESS_CORRECTION, "kwin_effect_colorblindnesscorrection", QtWarningMsg)
|
||||
|
||||
static void ensureResources()
|
||||
{
|
||||
// Must initialize resources manually because the effect is a static lib.
|
||||
Q_INIT_RESOURCE(colorblindnesscorrection);
|
||||
}
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
ColorBlindnessCorrectionEffect::ColorBlindnessCorrectionEffect()
|
||||
: OffscreenEffect()
|
||||
, m_mode(static_cast<Mode>(ColorBlindnessCorrectionSettings().mode()))
|
||||
{
|
||||
loadData();
|
||||
}
|
||||
|
||||
ColorBlindnessCorrectionEffect::~ColorBlindnessCorrectionEffect()
|
||||
{
|
||||
}
|
||||
|
||||
bool ColorBlindnessCorrectionEffect::supported()
|
||||
{
|
||||
return effects->isOpenGLCompositing();
|
||||
}
|
||||
|
||||
void ColorBlindnessCorrectionEffect::loadData()
|
||||
{
|
||||
ensureResources();
|
||||
|
||||
QString fragPath;
|
||||
switch (m_mode) {
|
||||
case Deuteranopia:
|
||||
fragPath = QStringLiteral(":/effects/colorblindnesscorrection/shaders/Deutranopia.frag");
|
||||
break;
|
||||
case Tritanopia:
|
||||
fragPath = QStringLiteral(":/effects/colorblindnesscorrection/shaders/Tritanopia.frag");
|
||||
break;
|
||||
case Protanopia: // Most common, use it as fallback
|
||||
default:
|
||||
fragPath = QStringLiteral(":/effects/colorblindnesscorrection/shaders/Protanopia.frag");
|
||||
break;
|
||||
}
|
||||
|
||||
m_shader = ShaderManager::instance()->generateShaderFromFile(ShaderTrait::MapTexture, QString(), fragPath);
|
||||
|
||||
if (!m_shader->isValid()) {
|
||||
qCCritical(KWIN_COLORBLINDNESS_CORRECTION) << "Failed to load the shader!";
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto windows = effects->stackingOrder(); EffectWindow * w : windows) {
|
||||
correctColor(w);
|
||||
}
|
||||
effects->addRepaintFull();
|
||||
|
||||
connect(effects, &EffectsHandler::windowDeleted, this, &ColorBlindnessCorrectionEffect::slotWindowDeleted);
|
||||
connect(effects, &EffectsHandler::windowAdded, this, &ColorBlindnessCorrectionEffect::correctColor);
|
||||
}
|
||||
|
||||
void ColorBlindnessCorrectionEffect::correctColor(KWin::EffectWindow *w)
|
||||
{
|
||||
if (m_windows.contains(w)) {
|
||||
return;
|
||||
}
|
||||
|
||||
redirect(w);
|
||||
setShader(w, m_shader.get());
|
||||
m_windows.insert(w);
|
||||
}
|
||||
|
||||
void ColorBlindnessCorrectionEffect::slotWindowDeleted(EffectWindow *w)
|
||||
{
|
||||
if (auto it = m_windows.find(w); it != m_windows.end()) {
|
||||
m_windows.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
bool ColorBlindnessCorrectionEffect::isActive() const
|
||||
{
|
||||
return !m_windows.empty();
|
||||
}
|
||||
|
||||
bool ColorBlindnessCorrectionEffect::provides(Feature f)
|
||||
{
|
||||
return f == Contrast;
|
||||
}
|
||||
|
||||
void ColorBlindnessCorrectionEffect::reconfigure(ReconfigureFlags flags)
|
||||
{
|
||||
if (flags != Effect::ReconfigureAll) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto newMode = static_cast<Mode>(ColorBlindnessCorrectionSettings().mode());
|
||||
if (m_mode == newMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_mode = newMode;
|
||||
|
||||
disconnect(effects, &EffectsHandler::windowDeleted, this, &ColorBlindnessCorrectionEffect::slotWindowDeleted);
|
||||
disconnect(effects, &EffectsHandler::windowAdded, this, &ColorBlindnessCorrectionEffect::correctColor);
|
||||
|
||||
for (EffectWindow *w : m_windows) {
|
||||
unredirect(w);
|
||||
}
|
||||
m_windows.clear();
|
||||
|
||||
loadData();
|
||||
}
|
||||
|
||||
int ColorBlindnessCorrectionEffect::requestedEffectChainPosition() const
|
||||
{
|
||||
return 98;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#include "moc_colorblindnesscorrection.cpp"
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2023 Fushan Wen <qydwhotmail@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
#include "libkwineffects/glshadermanager.h"
|
||||
#include "libkwineffects/kwinoffscreeneffect.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
/**
|
||||
* The color filter supports protanopia, deuteranopia and tritanopia.
|
||||
*/
|
||||
class ColorBlindnessCorrectionEffect : public OffscreenEffect
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum Mode {
|
||||
Protanopia = 0, //<Greatly reduced reds
|
||||
Deuteranopia, //<Greatly reduced greens
|
||||
Tritanopia, //<Greatly reduced blues
|
||||
};
|
||||
|
||||
explicit ColorBlindnessCorrectionEffect();
|
||||
~ColorBlindnessCorrectionEffect() override;
|
||||
|
||||
bool isActive() const override;
|
||||
bool provides(Feature) override;
|
||||
void reconfigure(ReconfigureFlags flags) override;
|
||||
int requestedEffectChainPosition() const override;
|
||||
|
||||
static bool supported();
|
||||
|
||||
public Q_SLOTS:
|
||||
void slotWindowDeleted(KWin::EffectWindow *w);
|
||||
|
||||
private Q_SLOTS:
|
||||
void correctColor(KWin::EffectWindow *w);
|
||||
|
||||
private:
|
||||
void loadData();
|
||||
|
||||
Mode m_mode = Protanopia;
|
||||
|
||||
std::unordered_set<KWin::EffectWindow *> m_windows;
|
||||
std::unique_ptr<GLShader> m_shader;
|
||||
};
|
||||
|
||||
} // namespace
|
@ -0,0 +1,10 @@
|
||||
<!DOCTYPE RCC><RCC version="1.0">
|
||||
<qresource prefix="/effects/colorblindnesscorrection/">
|
||||
<file>shaders/Deutranopia.frag</file>
|
||||
<file>shaders/Deutranopia_core.frag</file>
|
||||
<file>shaders/Protanopia.frag</file>
|
||||
<file>shaders/Protanopia_core.frag</file>
|
||||
<file>shaders/Tritanopia.frag</file>
|
||||
<file>shaders/Tritanopia_core.frag</file>
|
||||
</qresource>
|
||||
</RCC>
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2023 Fushan Wen <qydwhotmail@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "colorblindnesscorrection_config.h"
|
||||
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusMessage>
|
||||
#include <QDBusPendingCall>
|
||||
|
||||
#include <KPluginFactory>
|
||||
|
||||
#include "colorblindnesscorrection_settings.h"
|
||||
#include "colorblindnesscorrection_settingsdata.h"
|
||||
#include "kwineffects_interface.h"
|
||||
|
||||
K_PLUGIN_CLASS_WITH_JSON(KWin::ColorBlindnessCorrectionEffectConfig, "kwin_colorblindnesscorrection_config.json")
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
ColorBlindnessCorrectionEffectConfig::ColorBlindnessCorrectionEffectConfig(QObject *parent, const KPluginMetaData &metaData)
|
||||
: KQuickManagedConfigModule(parent, metaData)
|
||||
, m_data(new ColorBlindnessCorrectionSettingsData(this))
|
||||
{
|
||||
qmlRegisterUncreatableType<ColorBlindnessCorrectionSettings>("org.kde.plasma.kwin.colorblindnesscorrectioneffect.kcm",
|
||||
1,
|
||||
0,
|
||||
"ColorBlindnessCorrectionSettings",
|
||||
QStringLiteral("Only for enums"));
|
||||
|
||||
setButtons(Apply | Default);
|
||||
}
|
||||
|
||||
ColorBlindnessCorrectionEffectConfig::~ColorBlindnessCorrectionEffectConfig()
|
||||
{
|
||||
}
|
||||
|
||||
ColorBlindnessCorrectionSettings *ColorBlindnessCorrectionEffectConfig::settings() const
|
||||
{
|
||||
return m_data->settings();
|
||||
}
|
||||
|
||||
void ColorBlindnessCorrectionEffectConfig::save()
|
||||
{
|
||||
KQuickManagedConfigModule::save();
|
||||
|
||||
OrgKdeKwinEffectsInterface interface(QStringLiteral("org.kde.KWin"),
|
||||
QStringLiteral("/Effects"),
|
||||
QDBusConnection::sessionBus());
|
||||
interface.reconfigureEffect(QStringLiteral("colorblindnesscorrection"));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#include "colorblindnesscorrection_config.moc"
|
||||
#include "moc_colorblindnesscorrection_config.cpp"
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2023 Fushan Wen <qydwhotmail@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <KQuickManagedConfigModule>
|
||||
|
||||
class ColorBlindnessCorrectionSettings;
|
||||
class ColorBlindnessCorrectionSettingsData;
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class ColorBlindnessCorrectionEffectConfig : public KQuickManagedConfigModule
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(ColorBlindnessCorrectionSettings *settings READ settings CONSTANT)
|
||||
|
||||
public:
|
||||
explicit ColorBlindnessCorrectionEffectConfig(QObject *parent, const KPluginMetaData &metaData);
|
||||
~ColorBlindnessCorrectionEffectConfig() override;
|
||||
|
||||
ColorBlindnessCorrectionSettings *settings() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
void save() override;
|
||||
|
||||
private:
|
||||
ColorBlindnessCorrectionSettingsData *m_data;
|
||||
|
||||
}; // namespace
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
# SPDX-FileCopyrightText: 2023 Fushan Wen <qydwhotmail@gmail.com>
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
-->
|
||||
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
|
||||
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
|
||||
<kcfgfile name="kwinrc" />
|
||||
<group name="Effect-colorblindnesscorrection">
|
||||
<entry name="Mode" type="UInt">
|
||||
<default>0</default>
|
||||
</entry>
|
||||
</group>
|
||||
</kcfg>
|
@ -0,0 +1,9 @@
|
||||
# SPDX-FileCopyrightText: 2023 Fushan Wen <qydwhotmail@gmail.com>
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
File=colorblindnesscorrection_settings.kcfg
|
||||
ClassName=ColorBlindnessCorrectionSettings
|
||||
Mutators=true
|
||||
DefaultValueGetters=true
|
||||
GenerateProperties=true
|
||||
ParentInConstructor=true
|
@ -0,0 +1,6 @@
|
||||
{
|
||||
"KPlugin": {
|
||||
"License": "GPL-2.0+",
|
||||
"Name": "Colorblindness Correction"
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
# SPDX-FileCopyrightText: None
|
||||
# SPDX-License-Identifier: CC0-1.0
|
@ -0,0 +1,16 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2023 Fushan Wen <qydwhotmail@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "colorblindnesscorrection.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
KWIN_EFFECT_FACTORY_SUPPORTED(ColorBlindnessCorrectionEffect, "metadata.json.stripped", return ColorBlindnessCorrectionEffect::supported();)
|
||||
|
||||
} // namespace KWin
|
||||
|
||||
#include "main.moc"
|
@ -0,0 +1,10 @@
|
||||
{
|
||||
"KPlugin": {
|
||||
"Category": "Accessibility",
|
||||
"Description": "Enhances color perception for color blindness",
|
||||
"EnabledByDefault": false,
|
||||
"License": "GPL-2.0+",
|
||||
"Name": "Colorblindness Correction"
|
||||
},
|
||||
"X-KDE-ConfigModule": "kwin_colorblindnesscorrection_config"
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
# SPDX-FileCopyrightText: None
|
||||
# SPDX-License-Identifier: CC0-1.0
|
@ -0,0 +1,40 @@
|
||||
// SPDX-FileCopyrightText: None
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
uniform sampler2D sampler;
|
||||
uniform vec4 modulation;
|
||||
uniform float saturation;
|
||||
varying vec2 texcoord0;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex = texture2D(sampler, texcoord0);
|
||||
|
||||
if (saturation != 1.0) {
|
||||
vec3 desaturated = tex.rgb * vec3( 0.30, 0.59, 0.11 );
|
||||
desaturated = vec3(dot( desaturated, tex.rgb ));
|
||||
tex.rgb = tex.rgb * vec3(saturation) + desaturated * vec3(1.0 - saturation);
|
||||
}
|
||||
|
||||
float L = (17.8824 * tex.r) + (43.5161 * tex.g) + (4.11935 * tex.b);
|
||||
float M = (3.45565 * tex.r) + (27.1554 * tex.g) + (3.86714 * tex.b);
|
||||
float S = (0.0299566 * tex.r) + (0.184309 * tex.g) + (1.46709 * tex.b);
|
||||
|
||||
// Deuteranopia
|
||||
float l = 1.0 * L + 0.0 * M + 0.0 * S;
|
||||
float m = 0.494207 * L + 0.0 * M + 1.24827 * S;
|
||||
float s = 0.0 * L + 0.0 * M + 1.0 * S;
|
||||
|
||||
vec4 error;
|
||||
error.r = (0.0809444479 * l) + (-0.130504409 * m) + (0.116721066 * s);
|
||||
error.g = (-0.0102485335 * l) + (0.0540193266 * m) + (-0.113614708 * s);
|
||||
error.b = (-0.000365296938 * l) + (-0.00412161469 * m) + (0.693511405 * s);
|
||||
error.a = 1.0;
|
||||
vec4 diff = tex - error;
|
||||
vec4 correction;
|
||||
correction.r = 0.0;
|
||||
correction.g = (diff.r * 0.7) + (diff.g * 1.0);
|
||||
correction.b = (diff.r * 0.7) + (diff.b * 1.0);
|
||||
correction = tex + correction;
|
||||
|
||||
gl_FragColor = correction * modulation;
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
#version 140
|
||||
// SPDX-FileCopyrightText: None
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
uniform sampler2D sampler;
|
||||
uniform vec4 modulation;
|
||||
uniform float saturation;
|
||||
|
||||
in vec2 texcoord0;
|
||||
out vec4 fragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex = texture2D(sampler, texcoord0);
|
||||
|
||||
if (saturation != 1.0) {
|
||||
vec3 desaturated = tex.rgb * vec3( 0.30, 0.59, 0.11 );
|
||||
desaturated = vec3(dot( desaturated, tex.rgb ));
|
||||
tex.rgb = tex.rgb * vec3(saturation) + desaturated * vec3(1.0 - saturation);
|
||||
}
|
||||
|
||||
float L = (17.8824 * tex.r) + (43.5161 * tex.g) + (4.11935 * tex.b);
|
||||
float M = (3.45565 * tex.r) + (27.1554 * tex.g) + (3.86714 * tex.b);
|
||||
float S = (0.0299566 * tex.r) + (0.184309 * tex.g) + (1.46709 * tex.b);
|
||||
|
||||
// Deuteranopia
|
||||
float l = 1.0 * L + 0.0 * M + 0.0 * S;
|
||||
float m = 0.494207 * L + 0.0 * M + 1.24827 * S;
|
||||
float s = 0.0 * L + 0.0 * M + 1.0 * S;
|
||||
|
||||
vec4 error;
|
||||
error.r = (0.0809444479 * l) + (-0.130504409 * m) + (0.116721066 * s);
|
||||
error.g = (-0.0102485335 * l) + (0.0540193266 * m) + (-0.113614708 * s);
|
||||
error.b = (-0.000365296938 * l) + (-0.00412161469 * m) + (0.693511405 * s);
|
||||
error.a = 1.0;
|
||||
vec4 diff = tex - error;
|
||||
vec4 correction;
|
||||
correction.r = 0.0;
|
||||
correction.g = (diff.r * 0.7) + (diff.g * 1.0);
|
||||
correction.b = (diff.r * 0.7) + (diff.b * 1.0);
|
||||
correction = tex + correction;
|
||||
|
||||
fragColor = correction * modulation;
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
// SPDX-FileCopyrightText: None
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
uniform sampler2D sampler;
|
||||
uniform vec4 modulation;
|
||||
uniform float saturation;
|
||||
varying vec2 texcoord0;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex = texture2D(sampler, texcoord0);
|
||||
|
||||
if (saturation != 1.0) {
|
||||
vec3 desaturated = tex.rgb * vec3( 0.30, 0.59, 0.11 );
|
||||
desaturated = vec3(dot( desaturated, tex.rgb ));
|
||||
tex.rgb = tex.rgb * vec3(saturation) + desaturated * vec3(1.0 - saturation);
|
||||
}
|
||||
|
||||
float L = (17.8824 * tex.r) + (43.5161 * tex.g) + (4.11935 * tex.b);
|
||||
float M = (3.45565 * tex.r) + (27.1554 * tex.g) + (3.86714 * tex.b);
|
||||
float S = (0.0299566 * tex.r) + (0.184309 * tex.g) + (1.46709 * tex.b);
|
||||
|
||||
// Protanopia
|
||||
float l = 0.0 * L + 2.02344 * M + -2.52581 * S;
|
||||
float m = 0.0 * L + 1.0 * M + 0.0 * S;
|
||||
float s = 0.0 * L + 0.0 * M + 1.0 * S;
|
||||
|
||||
vec4 error;
|
||||
error.r = (0.0809444479 * l) + (-0.130504409 * m) + (0.116721066 * s);
|
||||
error.g = (-0.0102485335 * l) + (0.0540193266 * m) + (-0.113614708 * s);
|
||||
error.b = (-0.000365296938 * l) + (-0.00412161469 * m) + (0.693511405 * s);
|
||||
error.a = 1.0;
|
||||
vec4 diff = tex - error;
|
||||
vec4 correction;
|
||||
correction.r = 0.0;
|
||||
correction.g = (diff.r * 0.7) + (diff.g * 1.0);
|
||||
correction.b = (diff.r * 0.7) + (diff.b * 1.0);
|
||||
correction = tex + correction;
|
||||
|
||||
gl_FragColor = correction * modulation;
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
#version 140
|
||||
// SPDX-FileCopyrightText: None
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
uniform sampler2D sampler;
|
||||
uniform vec4 modulation;
|
||||
uniform float saturation;
|
||||
|
||||
in vec2 texcoord0;
|
||||
out vec4 fragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex = texture2D(sampler, texcoord0);
|
||||
|
||||
if (saturation != 1.0) {
|
||||
vec3 desaturated = tex.rgb * vec3( 0.30, 0.59, 0.11 );
|
||||
desaturated = vec3(dot( desaturated, tex.rgb ));
|
||||
tex.rgb = tex.rgb * vec3(saturation) + desaturated * vec3(1.0 - saturation);
|
||||
}
|
||||
|
||||
float L = (17.8824 * tex.r) + (43.5161 * tex.g) + (4.11935 * tex.b);
|
||||
float M = (3.45565 * tex.r) + (27.1554 * tex.g) + (3.86714 * tex.b);
|
||||
float S = (0.0299566 * tex.r) + (0.184309 * tex.g) + (1.46709 * tex.b);
|
||||
|
||||
// Protanopia
|
||||
float l = 0.0 * L + 2.02344 * M + -2.52581 * S;
|
||||
float m = 0.0 * L + 1.0 * M + 0.0 * S;
|
||||
float s = 0.0 * L + 0.0 * M + 1.0 * S;
|
||||
|
||||
vec4 error;
|
||||
error.r = (0.0809444479 * l) + (-0.130504409 * m) + (0.116721066 * s);
|
||||
error.g = (-0.0102485335 * l) + (0.0540193266 * m) + (-0.113614708 * s);
|
||||
error.b = (-0.000365296938 * l) + (-0.00412161469 * m) + (0.693511405 * s);
|
||||
error.a = 1.0;
|
||||
vec4 diff = tex - error;
|
||||
vec4 correction;
|
||||
correction.r = 0.0;
|
||||
correction.g = (diff.r * 0.7) + (diff.g * 1.0);
|
||||
correction.b = (diff.r * 0.7) + (diff.b * 1.0);
|
||||
correction = tex + correction;
|
||||
|
||||
fragColor = correction * modulation;
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
ColorBlindness correction shader with adjustable intensity. Can correct for:
|
||||
* Protanopia (Greatly reduced reds)
|
||||
* Deuteranopia (Greatly reduced greens)
|
||||
* Tritanopia (Greatly reduced blues)
|
||||
|
||||
The correction algorithm is taken from http://www.daltonize.org/search/label/Daltonize
|
||||
|
||||
This shader is released under the CC0 license. Feel free to use, improve and change this shader and consider sharing the modified result.
|
@ -0,0 +1,43 @@
|
||||
// SPDX-FileCopyrightText: None
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
uniform sampler2D sampler;
|
||||
uniform vec4 modulation;
|
||||
uniform float saturation;
|
||||
varying vec2 texcoord0;
|
||||
|
||||
in vec2 texcoord0;
|
||||
out vec4 fragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex = texture2D(sampler, texcoord0);
|
||||
|
||||
if (saturation != 1.0) {
|
||||
vec3 desaturated = tex.rgb * vec3( 0.30, 0.59, 0.11 );
|
||||
desaturated = vec3(dot( desaturated, tex.rgb ));
|
||||
tex.rgb = tex.rgb * vec3(saturation) + desaturated * vec3(1.0 - saturation);
|
||||
}
|
||||
|
||||
float L = (17.8824 * tex.r) + (43.5161 * tex.g) + (4.11935 * tex.b);
|
||||
float M = (3.45565 * tex.r) + (27.1554 * tex.g) + (3.86714 * tex.b);
|
||||
float S = (0.0299566 * tex.r) + (0.184309 * tex.g) + (1.46709 * tex.b);
|
||||
|
||||
// Tritanopia
|
||||
float l = 1.0 * L + 0.0 * M + 0.0 * S;
|
||||
float m = 0.0 * L + 1.0 * M + 0.0 * S;
|
||||
float s = -0.395913 * L + 0.801109 * M + 0.0 * S;
|
||||
|
||||
vec4 error;
|
||||
error.r = (0.0809444479 * l) + (-0.130504409 * m) + (0.116721066 * s);
|
||||
error.g = (-0.0102485335 * l) + (0.0540193266 * m) + (-0.113614708 * s);
|
||||
error.b = (-0.000365296938 * l) + (-0.00412161469 * m) + (0.693511405 * s);
|
||||
error.a = 1.0;
|
||||
vec4 diff = tex - error;
|
||||
vec4 correction;
|
||||
correction.r = 0.0;
|
||||
correction.g = (diff.r * 0.7) + (diff.g * 1.0);
|
||||
correction.b = (diff.r * 0.7) + (diff.b * 1.0);
|
||||
correction = tex + correction;
|
||||
|
||||
gl_FragColor = correction * modulation;
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
#version 140
|
||||
// SPDX-FileCopyrightText: None
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
uniform sampler2D sampler;
|
||||
uniform vec4 modulation;
|
||||
uniform float saturation;
|
||||
|
||||
in vec2 texcoord0;
|
||||
out vec4 fragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex = texture2D(sampler, texcoord0);
|
||||
|
||||
if (saturation != 1.0) {
|
||||
vec3 desaturated = tex.rgb * vec3( 0.30, 0.59, 0.11 );
|
||||
desaturated = vec3(dot( desaturated, tex.rgb ));
|
||||
tex.rgb = tex.rgb * vec3(saturation) + desaturated * vec3(1.0 - saturation);
|
||||
}
|
||||
|
||||
float L = (17.8824 * tex.r) + (43.5161 * tex.g) + (4.11935 * tex.b);
|
||||
float M = (3.45565 * tex.r) + (27.1554 * tex.g) + (3.86714 * tex.b);
|
||||
float S = (0.0299566 * tex.r) + (0.184309 * tex.g) + (1.46709 * tex.b);
|
||||
|
||||
// Tritanopia
|
||||
float l = 1.0 * L + 0.0 * M + 0.0 * S;
|
||||
float m = 0.0 * L + 1.0 * M + 0.0 * S;
|
||||
float s = -0.395913 * L + 0.801109 * M + 0.0 * S;
|
||||
|
||||
vec4 error;
|
||||
error.r = (0.0809444479 * l) + (-0.130504409 * m) + (0.116721066 * s);
|
||||
error.g = (-0.0102485335 * l) + (0.0540193266 * m) + (-0.113614708 * s);
|
||||
error.b = (-0.000365296938 * l) + (-0.00412161469 * m) + (0.693511405 * s);
|
||||
error.a = 1.0;
|
||||
vec4 diff = tex - error;
|
||||
vec4 correction;
|
||||
correction.r = 0.0;
|
||||
correction.g = (diff.r * 0.7) + (diff.g * 1.0);
|
||||
correction.b = (diff.r * 0.7) + (diff.b * 1.0);
|
||||
correction = tex + correction;
|
||||
|
||||
fragColor = correction * modulation;
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2023 Fushan Wen <qydwhotmail@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC
|
||||
import QtQuick.Layouts
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
import org.kde.kcmutils as KCM
|
||||
import org.kde.plasma.kwin.colorblindnesscorrectioneffect.kcm
|
||||
|
||||
KCM.SimpleKCM {
|
||||
id: root
|
||||
|
||||
implicitWidth: Kirigami.Units.gridUnit * 30
|
||||
implicitHeight: Kirigami.Units.gridUnit * 22
|
||||
|
||||
RowLayout {
|
||||
id: previewArea
|
||||
Layout.fillWidth: true
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: [
|
||||
{ name: i18n("Red"), colors: ["Red", "Orange", "Yellow"] },
|
||||
{ name: i18n("Green"), colors: ["Green", "LimeGreen", "Lime"] },
|
||||
{ name: i18n("Blue"), colors: ["Blue", "DeepSkyBlue", "Aqua"] },
|
||||
{ name: i18n("Purple"), colors: ["Purple", "Fuchsia", "Violet"] },
|
||||
]
|
||||
|
||||
delegate: Column {
|
||||
spacing: 0
|
||||
|
||||
Repeater {
|
||||
model: modelData.colors
|
||||
delegate: Rectangle {
|
||||
width: Kirigami.Units.gridUnit * 5
|
||||
height: Kirigami.Units.gridUnit * 5
|
||||
color: modelData
|
||||
}
|
||||
}
|
||||
|
||||
QQC.Label {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: modelData.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.FormLayout {
|
||||
id: formLayout
|
||||
anchors {
|
||||
top: previewArea.bottom
|
||||
topMargin: Kirigami.Units.largeSpacing
|
||||
}
|
||||
|
||||
QQC.ComboBox {
|
||||
Kirigami.FormData.label: i18nc("@label", "Mode:")
|
||||
currentIndex: kcm.settings.mode
|
||||
textRole: "text"
|
||||
valueRole: "value"
|
||||
model: [
|
||||
{ value: 0, text: i18nc("@option", "Protanopia (red weak)") },
|
||||
{ value: 1, text: i18nc("@option", "Deuteranopia (green weak)") },
|
||||
{ value: 2, text: i18nc("@option", "Tritanopia (blue-yellow)") },
|
||||
]
|
||||
|
||||
onActivated: kcm.settings.mode = currentValue
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue