~tardypad/dotfiles

ref: HEAD dotfiles/utilities/test-contrast -rwxr-xr-x 4.4 KiB
2a7e6d96Damien Tardy-Panis Add missing backup-dir in backup script for quickmarks 5 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#!/bin/sh

# Following the Web Content Accessibility Guidelines 2.0
# https://www.w3.org/TR/WCAG20/#visual-audio-contrast
# we'll consider a contrast af 4.5 to be the minimum target
# as defined there to be the minimum contrast for normal text size
# 3.0 would be enough for larger text size or when using bold

init_variables() {
  COMMAND=${0##*/}

  MINIMUM_CONTRAST_NORMAL=4.5
  MINIMUM_CONTRAST_LARGE=3.0

  HEX_COLORS=

  PLAIN=0
}

show_usage() {
  cat <<- EOF
	usage: ${COMMAND} <color1> <color2> ...

	Test the base colors contrast to each other

	Colors need to be specified in the hexadecimal format without # prefix

	Values are bold when higher than minimum desired for bold or large text
	Values are also underlined when higher than minimum desired for normal text

	Options:
	  -h     show this message only
	  -p     disable bold and underline attributes
	EOF
}

parse_command_line() {
  while getopts hp OPT; do
    case "${OPT}" in
      h) show_usage; exit 0 ;;
      p) PLAIN=1 ;;
      ?) exit_error ;;
    esac
  done

  shift $(( OPTIND - 1 ))

  HEX_COLORS="$*"

  # read hex values from stdin if no argument specified
  if [ -z "${HEX_COLORS}" ]; then
    while read -r HEX_COLOR; do
      HEX_COLORS="${HEX_COLORS} ${HEX_COLOR}"
    done
  fi
}

# shellcheck disable=SC2120
exit_error() {
  [ -z "$1" ] || echo "${COMMAND}: $1"
  printf "Try '%s -h' for more information.\n" "${COMMAND}"
  exit 1
} >&2

relative_luminance() {
  R="$1"
  G="$2"
  B="$3"

  tmp_value() {
    VALUE="$1"

    DEC_VALUE=$( echo "${VALUE} / 255" | bc -l )

    if [ "$( echo "${DEC_VALUE} <= 0.03928" | bc -l )" -eq 1 ]; then
      echo "${DEC_VALUE} / 12.92" | bc -l
    else
      # bc doesn't support fractional exponent so we replace it with exp/ln usage
      echo "e(2.4 * l( ( ${DEC_VALUE} + 0.055 ) / 1.055 ) )" | bc -l
    fi
  }

  R=$( tmp_value "${R}" )
  G=$( tmp_value "${G}" )
  B=$( tmp_value "${B}" )

  echo "( 0.2126 * ${R} + 0.7152 * ${G} + 0.0722 * ${B} )" | bc -l
}

contrast_ratio() {
  COLOR_1="$1"
  COLOR_2="$2"

  R1=$( echo "${COLOR_1}" | cut -f 1 -d ';' )
  G1=$( echo "${COLOR_1}" | cut -f 2 -d ';' )
  B1=$( echo "${COLOR_1}" | cut -f 3 -d ';' )

  R2=$( echo "${COLOR_2}" | cut -f 1 -d ';' )
  G2=$( echo "${COLOR_2}" | cut -f 2 -d ';' )
  B2=$( echo "${COLOR_2}" | cut -f 3 -d ';' )

  L1=$( relative_luminance "${R1}" "${G1}" "${B1}" )
  L2=$( relative_luminance "${R2}" "${G2}" "${B2}" )

  if [ "$( echo "${L1} < ${L2}" | bc -l )" -eq 1 ]; then
    L_HIGH="${L2}"
    L_LOW="${L1}"
  else
    L_HIGH="${L1}"
    L_LOW="${L2}"
  fi

  echo "( ${L_HIGH} + 0.05 ) / ( ${L_LOW} + 0.05 )" | bc -l
}

print_contrast() {
  RGB1="$1"
  RGB2="$2"
  CONTRAST="$3"

  # only keep 1 decimal for contrast
  # done before minimum comparisons to prevent the issue
  # of having same values displayed differently because of late rounding
  # The lost of precision is not so important
  CONTRAST=$( printf '%.1f' "${CONTRAST}" )

  printf '\033[0m'                                 # reset
  printf '\033[38;2;%sm' "${RGB1}"                 # use rgb1 for foreground
  printf '\033[48;2;%sm' "${RGB2}"                 # use rgb2 for background
  printf ' '                                       # spacing

  if [ "${PLAIN}" -eq 0 ]; then
    TEST_NORMAL=$( echo "${CONTRAST} >= ${MINIMUM_CONTRAST_NORMAL}" | bc -l )
    [ "${TEST_NORMAL}" -eq 1 ] && printf '\033[4m' # underline display

    TEST_BOLD=$( echo "${CONTRAST} >= ${MINIMUM_CONTRAST_LARGE}" | bc -l )
    [ "${TEST_BOLD}" -eq 1 ] && printf '\033[1m'   # bold display
  fi

  printf '%4s' "${CONTRAST}"                       # constant width
  printf '\033[21;24m'                             # reset underline and bold
  printf ' '                                       # spacing
  printf '\033[0m'                                 # reset
}

hex_to_rgb() {
  HEX_R=$(echo "$1" | cut -c 1-2)
  HEX_G=$(echo "$1" | cut -c 3-4)
  HEX_B=$(echo "$1" | cut -c 5-6)

  # use ; as a delimiter between the RGB components
  # to be able to directly use the full value inside ANSI escape sequences
  printf "%d;%d;%d\n" "0x${HEX_R}" "0x${HEX_G}" "0x${HEX_B}"
}

test_contrast() {
  for HEX1 in ${HEX_COLORS}; do
    for HEX2 in ${HEX_COLORS}; do
      RGB1=$( hex_to_rgb "${HEX1}" )
      RGB2=$( hex_to_rgb "${HEX2}" )
      CONTRAST=$( contrast_ratio "${RGB1}" "${RGB2}" )
      print_contrast "${RGB1}" "${RGB2}" "${CONTRAST}"
    done
    printf '\n'
  done
}

init_variables
parse_command_line "$@"

test_contrast