sites

public wiki contents of suckless.org
git clone git://git.suckless.org/sites
Log | Files | 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