|
| 1 | +" RGB/HSL conversion library |
| 2 | + |
| 3 | +let s:save_cpo = &cpo |
| 4 | +set cpo&vim |
| 5 | + |
| 6 | + |
| 7 | +let s:Color = {} |
| 8 | + |
| 9 | +function! s:Color.as_rgb_str() abort |
| 10 | + let [r, g, b] = self.as_rgb() |
| 11 | + let [r, g, b] = map([r, g, b], 'float2nr(round(v:val))') |
| 12 | + return printf('rgb(%d,%d,%d)', r, g, b) |
| 13 | +endfunction |
| 14 | + |
| 15 | +function! s:Color.as_hsl_str() abort |
| 16 | + let [h, s, l] = self.as_hsl() |
| 17 | + let [h, s, l] = map([h, s, l], 'float2nr(round(v:val))') |
| 18 | + return printf('hsl(%d,%d%%,%d%%)', h, s, l) |
| 19 | +endfunction |
| 20 | + |
| 21 | +function! s:Color.eq(color) abort |
| 22 | + return self.as_rgb() ==# a:color.as_rgb() |
| 23 | +endfunction |
| 24 | + |
| 25 | +function! s:Color.diff(color) abort |
| 26 | + let [r1, g1, b1] = self.as_rgb() |
| 27 | + let [r2, g2, b2] = a:color.as_rgb() |
| 28 | + return abs(r1 - r2) + abs(g1 - g2) + abs(b1 - b2) |
| 29 | +endfunction |
| 30 | + |
| 31 | +let s:RGB = deepcopy(s:Color) |
| 32 | + |
| 33 | +function! s:RGB.as_rgb_hex() abort |
| 34 | + let [r, g, b] = map([self._r, self._g, self._b], 'float2nr(round(v:val))') |
| 35 | + return '#' . s:_nr2hex(r) . s:_nr2hex(g) . s:_nr2hex(b) |
| 36 | +endfunction |
| 37 | + |
| 38 | +function! s:RGB.as_rgb() abort |
| 39 | + return [self._r, self._g, self._b] |
| 40 | +endfunction |
| 41 | + |
| 42 | +function! s:RGB.as_hsl() abort |
| 43 | + return s:_rgb2hsl(self._r, self._g, self._b) |
| 44 | +endfunction |
| 45 | + |
| 46 | +let s:HSL = deepcopy(s:Color) |
| 47 | + |
| 48 | +function! s:HSL.as_rgb_hex() abort |
| 49 | + let [r, g, b] = s:_hsl2rgb(self._h, self._s, self._l) |
| 50 | + let [r, g, b] = map([r, g, b], 'float2nr(round(v:val))') |
| 51 | + return '#' . s:_nr2hex(r) . s:_nr2hex(g) . s:_nr2hex(b) |
| 52 | +endfunction |
| 53 | + |
| 54 | +function! s:HSL.as_rgb() abort |
| 55 | + return s:_hsl2rgb(self._h, self._s, self._l) |
| 56 | +endfunction |
| 57 | + |
| 58 | +function! s:HSL.as_hsl() abort |
| 59 | + return [self._h, self._s, self._l] |
| 60 | +endfunction |
| 61 | + |
| 62 | + |
| 63 | +" Constructors |
| 64 | + |
| 65 | +let s:RGB_HEX_RE = '\v^#(\x{3}(\x{3})?)$' |
| 66 | +let s:RGB_RE = '\v^rgb\((\d+),\s*(\d+),\s*(\d+)\)$' |
| 67 | +let s:HSL_RE = '\v^hsl\((\d+),\s*(\d+)\%,\s*(\d+)\%\)$' |
| 68 | +function! s:parse(str) abort |
| 69 | + if type(a:str) !=# type('') |
| 70 | + throw 'vital: Color: parse(): invalid format: ' . a:str |
| 71 | + endif |
| 72 | + let m = matchlist(a:str, s:RGB_HEX_RE) |
| 73 | + if !empty(m) |
| 74 | + if strlen(m[1]) ==# 3 |
| 75 | + let [r, g, b] = [str2float('0x' . m[1][0] . m[1][0]), str2float('0x' . m[1][1] . m[1][1]), str2float('0x' . m[1][2] . m[1][2])] |
| 76 | + else |
| 77 | + let [r, g, b] = [str2float('0x' . m[1][0:1]), str2float('0x' . m[1][2:3]), str2float('0x' . m[1][4:5])] |
| 78 | + endif |
| 79 | + return s:rgb(r, g, b) |
| 80 | + endif |
| 81 | + let m = matchlist(a:str, s:RGB_RE) |
| 82 | + if !empty(m) |
| 83 | + let [r, g, b] = [str2float(m[1]), str2float(m[2]), str2float(m[3])] |
| 84 | + return s:rgb(r, g, b) |
| 85 | + endif |
| 86 | + let m = matchlist(a:str, s:HSL_RE) |
| 87 | + if !empty(m) |
| 88 | + let [h, s, l] = [str2float(m[1]), str2float(m[2]), str2float(m[3])] |
| 89 | + return s:hsl(h, s, l) |
| 90 | + endif |
| 91 | + throw 'vital: Color: parse(): invalid format: ' . a:str |
| 92 | +endfunction |
| 93 | + |
| 94 | +function! s:rgb(r, g, b) abort |
| 95 | + if !s:_check_rgb_range(a:r, a:g, a:b) |
| 96 | + throw printf('vital: Color: rgb(): invalid value: r = %s, g = %s, b = %s', |
| 97 | + \ string(a:r), string(a:g), string(a:b)) |
| 98 | + endif |
| 99 | + return extend(deepcopy(s:RGB), {'_r': a:r, '_g': a:g, '_b': a:b}) |
| 100 | +endfunction |
| 101 | + |
| 102 | +function! s:_check_rgb_range(r, g, b) abort |
| 103 | + for n in [a:r, a:g, a:b] |
| 104 | + if type(n) !=# type(0) && type(n) !=# type(0.0) |
| 105 | + \ || 0 ># n || n ># 255 |
| 106 | + return 0 |
| 107 | + endif |
| 108 | + endfor |
| 109 | + return 1 |
| 110 | +endfunction |
| 111 | + |
| 112 | +function! s:hsl(h, s, l) abort |
| 113 | + if !s:_check_hsl_range(a:h, a:s, a:l) |
| 114 | + throw printf('vital: Color: hsl(): invalid value: h = %s, s = %s, l = %s', |
| 115 | + \ string(a:h), string(a:s), string(a:l)) |
| 116 | + endif |
| 117 | + return extend(deepcopy(s:HSL), {'_h': a:h, '_s': a:s, '_l': a:l}) |
| 118 | +endfunction |
| 119 | + |
| 120 | +function! s:_check_hsl_range(h, s, l) abort |
| 121 | + for n in [a:h, a:s, a:l] |
| 122 | + if type(n) !=# type(0) && type(n) !=# type(0.0) |
| 123 | + return 0 |
| 124 | + endif |
| 125 | + endfor |
| 126 | + if 0 ># a:h || a:h ># 360 |
| 127 | + return 0 |
| 128 | + endif |
| 129 | + if 0 ># a:s || a:s ># 100 |
| 130 | + return 0 |
| 131 | + endif |
| 132 | + if 0 ># a:l || a:l ># 100 |
| 133 | + return 0 |
| 134 | + endif |
| 135 | + return 1 |
| 136 | +endfunction |
| 137 | + |
| 138 | + |
| 139 | +" Numeric Conversion Functions |
| 140 | + |
| 141 | +" NOTE: min()/max() can't take Float numbers |
| 142 | +function! s:_rgb2hsl(r, g, b) abort |
| 143 | + let [min, _, max] = sort([a:r, a:g, a:b], 'f') |
| 144 | + if min ==# max |
| 145 | + return [0, 0, 100.0 * min / 255.0] |
| 146 | + elseif max ==# a:r |
| 147 | + let h = 60.0 * (a:g - a:b) / (max - min) |
| 148 | + elseif max ==# a:g |
| 149 | + let h = 60.0 * (a:b - a:r) / (max - min) + 120.0 |
| 150 | + else |
| 151 | + let h = 60.0 * (a:r - a:g) / (max - min) + 240.0 |
| 152 | + endif |
| 153 | + if h < 0 |
| 154 | + let h += 360 |
| 155 | + endif |
| 156 | + let l = 100.0 * (max + min) / 2.0 / 255.0 |
| 157 | + if l <=# 50.0 |
| 158 | + let s = 100.0 * (max - min) / (max + min) |
| 159 | + else |
| 160 | + let s = 100.0 * (max - min) / (510.0 - max - min) |
| 161 | + endif |
| 162 | + return [h, s, l] |
| 163 | +endfunction |
| 164 | + |
| 165 | +function! s:_hsl2rgb(h, s, l) abort |
| 166 | + if a:l <# 50.0 |
| 167 | + let l2 = a:l |
| 168 | + else |
| 169 | + let l2 = 100.0 - a:l |
| 170 | + endif |
| 171 | + let max = 2.55 * (a:l + l2 * (a:s / 100.0)) |
| 172 | + let min = 2.55 * (a:l - l2 * (a:s / 100.0)) |
| 173 | + if a:h <# 60.0 |
| 174 | + let r = max |
| 175 | + let g = (a:h / 60.0) * (max - min) + min |
| 176 | + let b = min |
| 177 | + elseif a:h <# 120.0 |
| 178 | + let r = ((120.0 - a:h) / 60.0) * (max - min) + min |
| 179 | + let g = max |
| 180 | + let b = min |
| 181 | + elseif a:h <# 180.0 |
| 182 | + let r = min |
| 183 | + let g = max |
| 184 | + let b = ((a:h - 120.0) / 60.0) * (max - min) + min |
| 185 | + elseif a:h <# 240.0 |
| 186 | + let r = min |
| 187 | + let g = ((240.0 - a:h) / 60.0) * (max - min) + min |
| 188 | + let b = max |
| 189 | + elseif a:h <# 300.0 |
| 190 | + let r = ((a:h - 240.0) / 60.0) * (max - min) + min |
| 191 | + let g = min |
| 192 | + let b = max |
| 193 | + else |
| 194 | + let r = max |
| 195 | + let g = min |
| 196 | + let b = ((360.0 - a:h) / 60.0) * (max - min) + min |
| 197 | + endif |
| 198 | + return [r, g, b] |
| 199 | +endfunction |
| 200 | + |
| 201 | +let s:HEX_TABLE = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'] |
| 202 | +function! s:_nr2hex(n) abort |
| 203 | + return s:HEX_TABLE[a:n / 16] . s:HEX_TABLE[a:n % 16] |
| 204 | +endfunction |
| 205 | + |
| 206 | + |
| 207 | +let &cpo = s:save_cpo |
| 208 | +unlet s:save_cpo |
| 209 | +" vim:set et ts=2 sts=2 sw=2 tw=0: |
0 commit comments