st-minimumcontrast-20241029-0.9.2.diff (5744B)
1 From b0601465dc4d654485db7c3bcb28e019b21e78ca Mon Sep 17 00:00:00 2001 2 From: Nick Lott <nick.lott@gmail.com> 3 Date: Sun, 13 Oct 2024 11:14:38 +1300 4 Subject: [PATCH] Add minimum contrast ratio feature 5 6 --- 7 Makefile | 2 +- 8 config.def.h | 6 +++ 9 contrast.c | 146 +++++++++++++++++++++++++++++++++++++++++++++++++++ 10 contrast.h | 11 ++++ 11 x.c | 12 +++++ 12 5 files changed, 176 insertions(+), 1 deletion(-) 13 create mode 100644 contrast.c 14 create mode 100644 contrast.h 15 16 diff --git a/Makefile b/Makefile 17 index 15db421..893c71b 100644 18 --- a/Makefile 19 +++ b/Makefile 20 @@ -4,7 +4,7 @@ 21 22 include config.mk 23 24 -SRC = st.c x.c 25 +SRC = st.c x.c contrast.c 26 OBJ = $(SRC:.c=.o) 27 28 all: st 29 diff --git a/config.def.h b/config.def.h 30 index 2cd740a..03eed3a 100644 31 --- a/config.def.h 32 +++ b/config.def.h 33 @@ -134,6 +134,12 @@ unsigned int defaultbg = 259; 34 unsigned int defaultcs = 256; 35 static unsigned int defaultrcs = 257; 36 37 + 38 +/* 39 +* Minimum contrast ratio (1-21) 40 +*/ 41 +const float min_contrast_ratio = 2.2f; 42 + 43 /* 44 * Default shape of cursor 45 * 2: Block ("█") 46 diff --git a/contrast.c b/contrast.c 47 new file mode 100644 48 index 0000000..d956bd6 49 --- /dev/null 50 +++ b/contrast.c 51 @@ -0,0 +1,146 @@ 52 +#include <math.h> 53 + 54 +#include "contrast.h" 55 + 56 + 57 +/* Linear RGB floating point color space for use in calculations */ 58 +typedef struct { 59 + float r; 60 + float g; 61 + float b; 62 + float l; 63 +} RGBf; 64 + 65 + 66 + 67 +static float 68 +sRGB_to_lin(const unsigned short c) 69 +{ 70 + /* Convert to normalized float. */ 71 + const float f = c / 65535.0f; 72 + 73 + /* Convert from sRGB to linear space. */ 74 + return (f <= 0.03928f) ? f / 12.92f : pow((f + 0.055f) / 1.055f, 2.4f); 75 +} 76 + 77 + 78 + 79 +static unsigned short 80 +lin_to_sRGB(const float c) 81 +{ 82 + /* Convert to sRGB space. */ 83 + const float f = (c <= 0.0031308f) ? 84 + 12.92f * c : 1.055f * pow(c, 1.0f / 2.4f) - 0.055f; 85 + 86 + /* Clamp and convert back to 16-bit values. */ 87 + return (unsigned short)(fminf(fmaxf(f * 65535.0f, 0.0f), 65535.0f)); 88 +} 89 + 90 + 91 + 92 +static RGBf 93 +XftColor_to_RGBf( XftColor const * const c ) 94 +{ 95 + const float r = sRGB_to_lin(c->color.red); 96 + const float g = sRGB_to_lin(c->color.green); 97 + const float b = sRGB_to_lin(c->color.blue); 98 + 99 + /* Calculate luminance. */ 100 + const float l = 0.2126f * r + 0.7152f * g + 0.0722f * b; 101 + 102 + return (RGBf) {r, g, b, l}; 103 +} 104 + 105 + 106 + 107 +static void 108 +RGBf_to_XftColor( const RGBf rgb, XftColor * const c) 109 +{ 110 + c->color.red = lin_to_sRGB(rgb.r); 111 + c->color.green = lin_to_sRGB(rgb.g); 112 + c->color.blue = lin_to_sRGB(rgb.b); 113 +} 114 + 115 + 116 + 117 +static float 118 +get_luminance(XftColor const * const c) 119 +{ 120 + const RGBf rgb = XftColor_to_RGBf(c); 121 + return rgb.l; 122 +} 123 + 124 + 125 + 126 +static float 127 +get_contrast_ratio(const float fg_lum, const float bg_lum) 128 +{ 129 + if (fg_lum > bg_lum) { 130 + return (fg_lum + 0.05f) / (bg_lum + 0.05f); 131 + } else { 132 + return (bg_lum + 0.05f) / (fg_lum + 0.05f); 133 + } 134 +} 135 + 136 + 137 + 138 +static void 139 +adjust_luminance(XftColor * const c, const float adjustment) 140 +{ 141 + /* Convert sRGB to linear space and calculate luminance. */ 142 + RGBf rgb = XftColor_to_RGBf(c); 143 + 144 + /* Adjust luminance, clamping to 0-100% */ 145 + const float factor = fminf(fmaxf(rgb.l + adjustment, 0.0f), 1.0f) / rgb.l; 146 + rgb.r *= factor; 147 + rgb.g *= factor; 148 + rgb.b *= factor; 149 + 150 + /* Convert back to sRGB space. */ 151 + RGBf_to_XftColor(rgb, c); 152 +} 153 + 154 + 155 +static float 156 +get_luminance_adjustment( float fl, float bl, float contrast ) 157 +{ 158 + /* Increase existing any luminance difference to get contrast. */ 159 + float adjustment = (bl > fl)? 160 + ((((bl + 0.05f) / min_contrast_ratio) - 0.05f) - fl) : 161 + (((min_contrast_ratio * (bl + 0.05f)) - 0.05f) - fl); 162 + 163 + const float new_lum = fl + adjustment; 164 + 165 + /* Use the opposite direction if we exceed valid luminance range. */ 166 + if (new_lum < 0.0 || new_lum > 1.0) { 167 + adjustment = (bl <= fl)? 168 + ((((bl + 0.05f) / min_contrast_ratio) - 0.05f) - fl) : 169 + (((min_contrast_ratio * (bl + 0.05f)) - 0.05f) - fl); 170 + } 171 + 172 + return adjustment; 173 +} 174 + 175 + 176 +void 177 +adjust_color_for_contrast(XftColor * const fg, XftColor * const bg) 178 +{ 179 + float fl = get_luminance(fg); 180 + const float bl = get_luminance(bg); 181 + const float contrast = get_contrast_ratio(fl, bl); 182 + 183 + if (contrast < min_contrast_ratio) { 184 + /* Change black to dark grey so the luminance calculations can work. */ 185 + if (fl < 0.00001) { 186 + fg->color.red = 0x1fff; 187 + fg->color.green = 0x1fff; 188 + fg->color.blue = 0x1fff; 189 + fl = get_luminance(fg); 190 + } 191 + 192 + const float adjustment = get_luminance_adjustment( 193 + fl, bl, min_contrast_ratio); 194 + 195 + adjust_luminance(fg, adjustment); 196 + } 197 +} 198 diff --git a/contrast.h b/contrast.h 199 new file mode 100644 200 index 0000000..2aa6dd3 201 --- /dev/null 202 +++ b/contrast.h 203 @@ -0,0 +1,11 @@ 204 +#ifndef __ST_CONTRAST_H_ 205 +#define __ST_CONTRAST_H_ 206 + 207 +#include <X11/Xft/Xft.h> 208 + 209 +void adjust_color_for_contrast(XftColor * const , XftColor * const ); 210 + 211 +/* config.h globals */ 212 +extern const float min_contrast_ratio; 213 + 214 +#endif 215 diff --git a/x.c b/x.c 216 index bd23686..4c79e52 100644 217 --- a/x.c 218 +++ b/x.c 219 @@ -19,6 +19,7 @@ char *argv0; 220 #include "arg.h" 221 #include "st.h" 222 #include "win.h" 223 +#include "contrast.h" 224 225 /* types used in config.h */ 226 typedef struct { 227 @@ -1478,6 +1479,17 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i 228 if (winy + win.ch >= borderpx + win.th) 229 xclear(winx, winy + win.ch, winx + width, win.h); 230 231 + 232 + /* 233 + * Adjust colours to enforce a minimum contrast. Using the local truefg/bg 234 + * here to ensure we don't alter the dc.cols table permanently. 235 + */ 236 + fg = memcpy( &truefg, fg, sizeof(Color)); 237 + bg = memcpy( &truebg, bg, sizeof(Color)); 238 + 239 + adjust_color_for_contrast( fg, bg); 240 + 241 + 242 /* Clean up the region we want to draw to. */ 243 XftDrawRect(xw.draw, bg, winx, winy, width, win.ch); 244 245 -- 246 2.34.1 247