st-minimumcontrast-20241029-0.9.2.diff - sites - public wiki contents of suckless.org
(HTM) git clone git://git.suckless.org/sites
(DIR) Log
(DIR) Files
(DIR) Refs
---
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