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