color.vim (7738B)
1 " File: color.vim 2 " Author: romgrk 3 " Date: 15 Mar 2016 4 " Description: vimscript RGB/HSL color parsing 5 " This is mostly a transcript from a javascript color parsing module. 6 " !::exe [so %] 7 8 " Example: 9 " Lighten/darken the color under the cursor with Alt-minus/Alt-equal 10 11 " nnoremap <expr><M--> color#Test(expand('<cword>')) 12 " \? '"_ciw' . color#Darken(expand('<cword>')) . "\<Esc>" 13 " \: "\<Nop>" 14 " nnoremap <expr><M-=> color#Test(expand('<cword>')) 15 " \? '"_ciw' . color#Lighten(expand('<cword>')) . "\<Esc>" 16 " \: "\<Nop>" 17 18 " #00f6ff 19 " #00a7cd 20 " #007691 21 22 23 " ================================================================================ 24 " Definitions: {{{1 25 " In function names: 26 " • “Hex” refers to hexadecimal color format e.g. #599eff 27 " • “RGB” refers to an array of numbers [r, g, b] 28 " where r,g,b ∈ [0, 255] 29 " • “HSL” refers to an array of floats [h, s, l] 30 " where h,s,l ∈ [0, 1.0] 31 " }}}1 32 " ================================================================================ 33 " Color-format patterns: 34 " {{{1 35 36 let s:patterns = {} 37 38 " 6 hex-numbers, optionnal #-prefix 39 let s:patterns['hex'] = '\v#?(\x{2})(\x{2})(\x{2})' 40 41 " short version is strict: starting # mandatory 42 let s:patterns['shortHex'] = '\v#(\x{1})(\x{1})(\x{1})' 43 44 " Disabled 45 "let s:patterns['rgb'] = '\vrgb\s*\((\d+)\s*,(\d+)\s*,(\d+)\s*)\s*' 46 "let s:patterns['rgba'] = '\vrgba\s*\((\d+)\s*,(\d+)\s*,(\d+)\s*,(\d+)\s*)\s*' 47 48 " }}}1 49 " ================================================================================ 50 " Functions: 51 " {{{1 52 53 " @params String string The string to test 54 " @returns Boolean [0 or 1] if string matches: rrggbb OR #rrggbb OR #rgb 55 fu! color#Test (string) 56 for key in keys(s:patterns) 57 if a:string =~# s:patterns[key] 58 return 1 59 end 60 endfor 61 return 0 62 endfu 63 64 " @params (r, g, b) 65 " @params ([r, g, b]) 66 " @returns String A RGB color 67 fu! color#RGBtoHex (...) 68 let [r, g, b] = ( a:0==1 ? a:1 : a:000 ) 69 let num = printf('%02x', float2nr(r)) . '' 70 \ . printf('%02x', float2nr(g)) . '' 71 \ . printf('%02x', float2nr(b)) . '' 72 return '#' . num 73 endfu 74 75 " @param {String|Number} color The color to parse 76 fu! color#HexToRGB (color) 77 if type(a:color) == 2 78 let color = printf('%x', a:color) 79 else 80 let color = a:color | end 81 82 let matches = matchlist(color, s:patterns['hex']) 83 let factor = 0x1 84 85 if empty(matches) 86 let matches = matchlist(color, s:patterns['shortHex']) 87 let factor = 0x10 88 end 89 90 if len(matches) < 4 91 throw 'Couldnt parse ' . string(color) . ' ' . string(matches) 92 end 93 94 let r = str2nr(matches[1], 16) * factor 95 let g = str2nr(matches[2], 16) * factor 96 let b = str2nr(matches[3], 16) * factor 97 98 return [r, g, b] 99 endfu 100 101 102 " Converts an HSL color value to RGB. Conversion formula 103 " adapted from http://en.wikipedia.org/wiki/HSL_color_space. 104 " Assumes h, s, and l are contained in the set [0, 1] and 105 " returns r, g, and b in the set [0, 255]. 106 " @param Number h OR @param Array [h, s, l] 107 " @param Number s 108 " @param Number l 109 " @returns Array [r, g, b] The RGB representation 110 fu! color#HSLtoRGB(...) " (h, s, l) 111 let [h, s, l] = ( a:0==1 ? a:1 : a:000 ) 112 113 if (s == 0.0) " achromatic 114 let r = l 115 let g = l 116 let b = l 117 else 118 let q = l < 0.5 ? l * (1 + s) : l + s - l * s 119 let p = 2 * l - q 120 let r = color#Hue2RGB(p, q, h + 0.33333) 121 let g = color#Hue2RGB(p, q, h) 122 let b = color#Hue2RGB(p, q, h - 0.33333) 123 end 124 125 return [r * 255.0, g * 255.0, b * 255.0] 126 endfu 127 128 129 " @param Number r OR @param Array [r, g, b] 130 " @param Number g 131 " @param Number b 132 " @returns Array [h, s, l] The HSL representation 133 fu! color#RGBtoHSL(...) 134 let [r, g, b] = ( a:0==1 ? a:1 : a:000 ) 135 let max = max([r, g, b]) 136 let min = min([r, g, b]) 137 138 let r = str2float(r) 139 let g = str2float(g) 140 let b = str2float(b) 141 let max = str2float(max) 142 let min = str2float(min) 143 144 let max = max / 255 145 let min = min / 255 146 let r = r / 255 147 let g = g / 255 148 let b = b / 255 149 let h = str2float(0) 150 let s = str2float(0) 151 let l = (max + min) / 2 152 153 if (max == min) 154 let h = 0 " achromatic 155 let s = 0 " achromatic 156 else 157 let d = max - min 158 let s = (l > 0.5 ? d / (2 - max - min) 159 \ : d / (max + min) ) 160 if (max == r) 161 let h = (g - b) / d + (g < b ? 6 : 0) 162 end 163 if (max == g) 164 let h = (b - r) / d + 2 165 end 166 if (max == b) 167 let h = (r - g) / d + 4 168 end 169 let h = h / 6 170 end 171 172 return [h, s, l] 173 endfu 174 175 fu! color#Hue2RGB(...) " (p, q, t) 176 let [p, q, t] = ( a:0==1 ? a:1 : a:000 ) 177 178 if(t < 0) | let t += 1 | end 179 if(t > 1) | let t -= 1 | end 180 181 if(t < 1.0/6) | return (p + (q - p) * 6.0 * t) | end 182 if(t < 1.0/2) | return (q) | end 183 if(t < 2.0/3) | return (p + (q - p) * (2.0/3 - t) * 6.0) | end 184 185 return p 186 endfu 187 188 " }}}1 189 " ================================================================================ 190 " Composed functions: 191 " {{{1 192 193 fu! color#HexToHSL (color) 194 let [r, g, b] = color#HexToRGB(a:color) 195 return color#RGBtoHSL(r, g, b) 196 endfu 197 198 fu! color#HSLtoHex (...) 199 let [h, s, l] = ( a:0==1 ? a:1 : a:000 ) 200 let [r, g, b] = color#HSLtoRGB(h, s, l) 201 return color#RGBtoHex(r, g, b) 202 endfu 203 204 " @params String color The color 205 " @params {Number|String|Float} [amount=5] The percentage of light 206 fu! color#Lighten(color, ...) 207 let amount = a:0 ? 208 \(type(a:1) < 2 ? 209 \str2float(a:1) : a:1 ) 210 \: 5.0 211 212 if(amount < 1.0) 213 let amount = 1.0 + amount 214 else 215 let amount = 1.0 + (amount / 100.0) 216 end 217 218 let rgb = color#HexToRGB(a:color) 219 let rgb = map(rgb, 'v:val * amount') 220 let rgb = map(rgb, 'v:val > 255.0 ? 255.0 : v:val') 221 let rgb = map(rgb, 'float2nr(v:val)') 222 let hex = color#RGBtoHex(rgb) 223 return hex 224 endfu 225 226 " @params String color The color 227 " @params {Number|String|Float} [amount=5] The percentage of darkness 228 fu! color#Darken(color, ...) 229 let amount = a:0 ? 230 \(type(a:1) < 2 ? 231 \str2float(a:1) : a:1 ) 232 \: 5.0 233 234 if(amount < 1.0) 235 let amount = 1.0 - amount 236 else 237 let amount = 1.0 - (amount / 100.0) 238 end 239 240 if(amount < 0.0) 241 let amount = 0.0 | end 242 243 244 let rgb = color#HexToRGB(a:color) 245 let rgb = map(rgb, 'v:val * amount') 246 let rgb = map(rgb, 'v:val > 255.0 ? 255.0 : v:val') 247 let rgb = map(rgb, 'float2nr(v:val)') 248 let hex = color#RGBtoHex(rgb) 249 return hex 250 endfu 251 252 function! color#Decrease (...) 253 if &background == 'light' 254 return call(function('color#Lighten'), a:000) 255 end 256 return call(function('color#Darken'), a:000) 257 endfunc 258 259 function! color#Increase (...) 260 if &background == 'light' 261 return call(function('color#Darken'), a:000) 262 end 263 return call(function('color#Lighten'), a:000) 264 endfunc 265 266 function! color#Mix (a, b, ...) 267 let amount = a:0 ? a:1 : 0.5 268 269 let ca = color#HexToRGB(a:a) 270 let cb = color#HexToRGB(a:b) 271 272 let r = s:interpolate(ca[0], cb[0], amount) 273 let g = s:interpolate(ca[1], cb[1], amount) 274 let b = s:interpolate(ca[2], cb[2], amount) 275 276 return color#RGBtoHex([r, g, b]) 277 endfunc 278 279 function! s:interpolate (start, end, amount) 280 let diff = a:end - a:start 281 return a:start + (diff * a:amount) 282 endfunc 283 284 " }}}1 285