commit f59a8f3702933fc603571f5ed61c27a4735b1643
parent 710223e36e408b0dc6dadf56317d220043e9245e
Author: Nick Lott <nick.lott@gmail.com>
Date: Tue, 29 Oct 2024 21:51:30 +1300
[st][patch] Add a minimum contrast ratio feature.
Diffstat:
4 files changed, 283 insertions(+), 0 deletions(-)
diff --git a/st.suckless.org/patches/minimum_contrast/after.png b/st.suckless.org/patches/minimum_contrast/after.png
Binary files differ.
diff --git a/st.suckless.org/patches/minimum_contrast/before.png b/st.suckless.org/patches/minimum_contrast/before.png
Binary files differ.
diff --git a/st.suckless.org/patches/minimum_contrast/index.md b/st.suckless.org/patches/minimum_contrast/index.md
@@ -0,0 +1,36 @@
+# Minimum Contrast
+
+A patch that enforces a minimum contrast ratio.
+
+## Description
+
+Apply the patch and set the `min_contrast_ratio` in `config.h` and the
+terminal will attempt to enforce this contrast ratio by increasing or
+decreasing the brightness of the foreground color when needed.
+
+This is useful when your chosen theme clashes with another program to produce unreadable text.
+
+You can see the effect with these two bash one liners.
+
+
+```
+for i in {0..255}; do printf "\e[30m\e[48;5;%sm%3d\e[0m " "$i" "$i"; if (( i == 15 )) || (( i > 15 )) && (( (i-15) % 6 == 0 )); then echo; fi; done
+```
+
+```
+for i in {0..255}; do printf "\e[48;5;%sm%3d\e[0m " "$i" "$i"; if (( i == 15 )) || (( i > 15 )) && (( (i-15) % 6 == 0 )); then echo; fi; done
+```
+
+### Before and After
+
+![before](before.png) ![after](after.png)
+
+## Download
+
+
+[st-minimumcontrast-20241029-0.9.2.diff](st-minimumcontrast-20241029-0.9.2.diff)
+
+
+## Author
+
+Nick Lott - <nick.lott@gmail.com>
diff --git a/st.suckless.org/patches/minimum_contrast/st-minimumcontrast-20241029-0.9.2.diff b/st.suckless.org/patches/minimum_contrast/st-minimumcontrast-20241029-0.9.2.diff
@@ -0,0 +1,247 @@
+From b0601465dc4d654485db7c3bcb28e019b21e78ca Mon Sep 17 00:00:00 2001
+From: Nick Lott <nick.lott@gmail.com>
+Date: Sun, 13 Oct 2024 11:14:38 +1300
+Subject: [PATCH] Add minimum contrast ratio feature
+
+---
+ Makefile | 2 +-
+ config.def.h | 6 +++
+ contrast.c | 146 +++++++++++++++++++++++++++++++++++++++++++++++++++
+ contrast.h | 11 ++++
+ x.c | 12 +++++
+ 5 files changed, 176 insertions(+), 1 deletion(-)
+ create mode 100644 contrast.c
+ create mode 100644 contrast.h
+
+diff --git a/Makefile b/Makefile
+index 15db421..893c71b 100644
+--- a/Makefile
++++ b/Makefile
+@@ -4,7 +4,7 @@
+
+ include config.mk
+
+-SRC = st.c x.c
++SRC = st.c x.c contrast.c
+ OBJ = $(SRC:.c=.o)
+
+ all: st
+diff --git a/config.def.h b/config.def.h
+index 2cd740a..03eed3a 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -134,6 +134,12 @@ unsigned int defaultbg = 259;
+ unsigned int defaultcs = 256;
+ static unsigned int defaultrcs = 257;
+
++
++/*
++* Minimum contrast ratio (1-21)
++*/
++const float min_contrast_ratio = 2.2f;
++
+ /*
+ * Default shape of cursor
+ * 2: Block ("█")
+diff --git a/contrast.c b/contrast.c
+new file mode 100644
+index 0000000..d956bd6
+--- /dev/null
++++ b/contrast.c
+@@ -0,0 +1,146 @@
++#include <math.h>
++
++#include "contrast.h"
++
++
++/* Linear RGB floating point color space for use in calculations */
++typedef struct {
++ float r;
++ float g;
++ float b;
++ float l;
++} RGBf;
++
++
++
++static float
++sRGB_to_lin(const unsigned short c)
++{
++ /* Convert to normalized float. */
++ const float f = c / 65535.0f;
++
++ /* Convert from sRGB to linear space. */
++ return (f <= 0.03928f) ? f / 12.92f : pow((f + 0.055f) / 1.055f, 2.4f);
++}
++
++
++
++static unsigned short
++lin_to_sRGB(const float c)
++{
++ /* Convert to sRGB space. */
++ const float f = (c <= 0.0031308f) ?
++ 12.92f * c : 1.055f * pow(c, 1.0f / 2.4f) - 0.055f;
++
++ /* Clamp and convert back to 16-bit values. */
++ return (unsigned short)(fminf(fmaxf(f * 65535.0f, 0.0f), 65535.0f));
++}
++
++
++
++static RGBf
++XftColor_to_RGBf( XftColor const * const c )
++{
++ const float r = sRGB_to_lin(c->color.red);
++ const float g = sRGB_to_lin(c->color.green);
++ const float b = sRGB_to_lin(c->color.blue);
++
++ /* Calculate luminance. */
++ const float l = 0.2126f * r + 0.7152f * g + 0.0722f * b;
++
++ return (RGBf) {r, g, b, l};
++}
++
++
++
++static void
++RGBf_to_XftColor( const RGBf rgb, XftColor * const c)
++{
++ c->color.red = lin_to_sRGB(rgb.r);
++ c->color.green = lin_to_sRGB(rgb.g);
++ c->color.blue = lin_to_sRGB(rgb.b);
++}
++
++
++
++static float
++get_luminance(XftColor const * const c)
++{
++ const RGBf rgb = XftColor_to_RGBf(c);
++ return rgb.l;
++}
++
++
++
++static float
++get_contrast_ratio(const float fg_lum, const float bg_lum)
++{
++ if (fg_lum > bg_lum) {
++ return (fg_lum + 0.05f) / (bg_lum + 0.05f);
++ } else {
++ return (bg_lum + 0.05f) / (fg_lum + 0.05f);
++ }
++}
++
++
++
++static void
++adjust_luminance(XftColor * const c, const float adjustment)
++{
++ /* Convert sRGB to linear space and calculate luminance. */
++ RGBf rgb = XftColor_to_RGBf(c);
++
++ /* Adjust luminance, clamping to 0-100% */
++ const float factor = fminf(fmaxf(rgb.l + adjustment, 0.0f), 1.0f) / rgb.l;
++ rgb.r *= factor;
++ rgb.g *= factor;
++ rgb.b *= factor;
++
++ /* Convert back to sRGB space. */
++ RGBf_to_XftColor(rgb, c);
++}
++
++
++static float
++get_luminance_adjustment( float fl, float bl, float contrast )
++{
++ /* Increase existing any luminance difference to get contrast. */
++ float adjustment = (bl > fl)?
++ ((((bl + 0.05f) / min_contrast_ratio) - 0.05f) - fl) :
++ (((min_contrast_ratio * (bl + 0.05f)) - 0.05f) - fl);
++
++ const float new_lum = fl + adjustment;
++
++ /* Use the opposite direction if we exceed valid luminance range. */
++ if (new_lum < 0.0 || new_lum > 1.0) {
++ adjustment = (bl <= fl)?
++ ((((bl + 0.05f) / min_contrast_ratio) - 0.05f) - fl) :
++ (((min_contrast_ratio * (bl + 0.05f)) - 0.05f) - fl);
++ }
++
++ return adjustment;
++}
++
++
++void
++adjust_color_for_contrast(XftColor * const fg, XftColor * const bg)
++{
++ float fl = get_luminance(fg);
++ const float bl = get_luminance(bg);
++ const float contrast = get_contrast_ratio(fl, bl);
++
++ if (contrast < min_contrast_ratio) {
++ /* Change black to dark grey so the luminance calculations can work. */
++ if (fl < 0.00001) {
++ fg->color.red = 0x1fff;
++ fg->color.green = 0x1fff;
++ fg->color.blue = 0x1fff;
++ fl = get_luminance(fg);
++ }
++
++ const float adjustment = get_luminance_adjustment(
++ fl, bl, min_contrast_ratio);
++
++ adjust_luminance(fg, adjustment);
++ }
++}
+diff --git a/contrast.h b/contrast.h
+new file mode 100644
+index 0000000..2aa6dd3
+--- /dev/null
++++ b/contrast.h
+@@ -0,0 +1,11 @@
++#ifndef __ST_CONTRAST_H_
++#define __ST_CONTRAST_H_
++
++#include <X11/Xft/Xft.h>
++
++void adjust_color_for_contrast(XftColor * const , XftColor * const );
++
++/* config.h globals */
++extern const float min_contrast_ratio;
++
++#endif
+diff --git a/x.c b/x.c
+index bd23686..4c79e52 100644
+--- a/x.c
++++ b/x.c
+@@ -19,6 +19,7 @@ char *argv0;
+ #include "arg.h"
+ #include "st.h"
+ #include "win.h"
++#include "contrast.h"
+
+ /* types used in config.h */
+ typedef struct {
+@@ -1478,6 +1479,17 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
+ if (winy + win.ch >= borderpx + win.th)
+ xclear(winx, winy + win.ch, winx + width, win.h);
+
++
++ /*
++ * Adjust colours to enforce a minimum contrast. Using the local truefg/bg
++ * here to ensure we don't alter the dc.cols table permanently.
++ */
++ fg = memcpy( &truefg, fg, sizeof(Color));
++ bg = memcpy( &truebg, bg, sizeof(Color));
++
++ adjust_color_for_contrast( fg, bg);
++
++
+ /* Clean up the region we want to draw to. */
+ XftDrawRect(xw.draw, bg, winx, winy, width, win.ch);
+
+--
+2.34.1
+