You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
269 lines
7.2 KiB
269 lines
7.2 KiB
" File: color.vim
|
|
" Author: romgrk
|
|
" Date: 15 Mar 2016
|
|
" Description: vimscript RGB/HSL color parsing
|
|
" This is mostly a transcript from a javascript color parsing module.
|
|
" !::exe [so %]
|
|
" ================================================================================
|
|
" Definitions: {{{1
|
|
" In function names:
|
|
" • “Hex” refers to hexadecimal color format e.g. #599eff
|
|
" • “RGB” refers to an array of numbers [r, g, b]
|
|
" where r,g,b ∈ [0, 255]
|
|
" • “HSL” refers to an array of floats [h, s, l]
|
|
" where h,s,l ∈ [0, 1.0]
|
|
" }}}1
|
|
" ================================================================================
|
|
" Color-format patterns:
|
|
" {{{1
|
|
|
|
let s:patterns = {}
|
|
|
|
" 6 hex-numbers, optionnal #-prefix
|
|
let s:patterns['hex'] = '\v#?(\x{2})(\x{2})(\x{2})'
|
|
|
|
" short version is strict: starting # mandatory
|
|
let s:patterns['shortHex'] = '\v#(\x{1})(\x{1})(\x{1})'
|
|
|
|
" Disabled
|
|
"let s:patterns['rgb'] = '\vrgb\s*\((\d+)\s*,(\d+)\s*,(\d+)\s*)\s*'
|
|
"let s:patterns['rgba'] = '\vrgba\s*\((\d+)\s*,(\d+)\s*,(\d+)\s*,(\d+)\s*)\s*'
|
|
|
|
" }}}1
|
|
" ================================================================================
|
|
" Functions:
|
|
" {{{1
|
|
|
|
" @params String string The string to test
|
|
" @returns Boolean [0 or 1] if string matches: rrggbb OR #rrggbb OR #rgb
|
|
fu! color#Test (string)
|
|
for key in keys(s:patterns)
|
|
if a:string =~# s:patterns[key]
|
|
return 1
|
|
end
|
|
endfor
|
|
return 0
|
|
endfu
|
|
|
|
" @params (r, g, b)
|
|
" @params ([r, g, b])
|
|
" @returns String A RGB color
|
|
fu! color#RGBtoHex (...)
|
|
let [r, g, b] = ( a:0==1 ? a:1 : a:000 )
|
|
let num = printf('%02x', float2nr(r)) . ''
|
|
\ . printf('%02x', float2nr(g)) . ''
|
|
\ . printf('%02x', float2nr(b)) . ''
|
|
return '#' . num
|
|
endfu
|
|
|
|
" @param {String|Number} color The color to parse
|
|
fu! color#HexToRGB (color)
|
|
if type(a:color) == 2
|
|
let color = printf('%x', a:color)
|
|
else
|
|
let color = a:color | end
|
|
|
|
let matches = matchlist(color, s:patterns['hex'])
|
|
let factor = 0x1
|
|
|
|
if empty(matches)
|
|
let matches = matchlist(color, s:patterns['shortHex'])
|
|
let factor = 0x10
|
|
end
|
|
|
|
if len(matches) < 4
|
|
throw 'Couldnt parse ' . string(color) . ' ' . string(matches)
|
|
end
|
|
|
|
let r = str2nr(matches[1], 16) * factor
|
|
let g = str2nr(matches[2], 16) * factor
|
|
let b = str2nr(matches[3], 16) * factor
|
|
|
|
return [r, g, b]
|
|
endfu
|
|
|
|
|
|
" Converts an HSL color value to RGB. Conversion formula
|
|
" adapted from http://en.wikipedia.org/wiki/HSL_color_space.
|
|
" Assumes h, s, and l are contained in the set [0, 1] and
|
|
" returns r, g, and b in the set [0, 255].
|
|
" @param Number h OR @param Array [h, s, l]
|
|
" @param Number s
|
|
" @param Number l
|
|
" @returns Array [r, g, b] The RGB representation
|
|
fu! color#HSLtoRGB(...) " (h, s, l)
|
|
let [h, s, l] = ( a:0==1 ? a:1 : a:000 )
|
|
|
|
if (s == 0.0) " achromatic
|
|
let r = l
|
|
let g = l
|
|
let b = l
|
|
else
|
|
let q = l < 0.5 ? l * (1 + s) : l + s - l * s
|
|
let p = 2 * l - q
|
|
let r = color#Hue2RGB(p, q, h + 0.33333)
|
|
let g = color#Hue2RGB(p, q, h)
|
|
let b = color#Hue2RGB(p, q, h - 0.33333)
|
|
end
|
|
|
|
return [r * 255.0, g * 255.0, b * 255.0]
|
|
endfu
|
|
|
|
|
|
" @param Number r OR @param Array [r, g, b]
|
|
" @param Number g
|
|
" @param Number b
|
|
" @returns Array [h, s, l] The HSL representation
|
|
fu! color#RGBtoHSL(...)
|
|
let [r, g, b] = ( a:0==1 ? a:1 : a:000 )
|
|
let max = max([r, g, b])
|
|
let min = min([r, g, b])
|
|
|
|
let r = str2float(r)
|
|
let g = str2float(g)
|
|
let b = str2float(b)
|
|
let max = str2float(max)
|
|
let min = str2float(min)
|
|
|
|
let max = max / 255
|
|
let min = min / 255
|
|
let r = r / 255
|
|
let g = g / 255
|
|
let b = b / 255
|
|
let h = str2float(0)
|
|
let s = str2float(0)
|
|
let l = (max + min) / 2
|
|
|
|
if (max == min)
|
|
let h = 0 " achromatic
|
|
let s = 0 " achromatic
|
|
else
|
|
let d = max - min
|
|
let s = (l > 0.5 ? d / (2 - max - min)
|
|
\ : d / (max + min) )
|
|
if (max == r)
|
|
let h = (g - b) / d + (g < b ? 6 : 0)
|
|
end
|
|
if (max == g)
|
|
let h = (b - r) / d + 2
|
|
end
|
|
if (max == b)
|
|
let h = (r - g) / d + 4
|
|
end
|
|
let h = h / 6
|
|
end
|
|
|
|
return [h, s, l]
|
|
endfu
|
|
|
|
fu! color#Hue2RGB(...) " (p, q, t)
|
|
let [p, q, t] = ( a:0==1 ? a:1 : a:000 )
|
|
|
|
if(t < 0) | let t += 1 | end
|
|
if(t > 1) | let t -= 1 | end
|
|
|
|
if(t < 1.0/6) | return (p + (q - p) * 6.0 * t) | end
|
|
if(t < 1.0/2) | return (q) | end
|
|
if(t < 2.0/3) | return (p + (q - p) * (2.0/3 - t) * 6.0) | end
|
|
|
|
return p
|
|
endfu
|
|
|
|
" }}}1
|
|
" ================================================================================
|
|
" Composed functions:
|
|
" {{{1
|
|
|
|
fu! color#HexToHSL (color)
|
|
let [r, g, b] = color#HexToRGB(a:color)
|
|
return color#RGBtoHSL(r, g, b)
|
|
endfu
|
|
|
|
fu! color#HSLtoHex (...)
|
|
let [h, s, l] = ( a:0==1 ? a:1 : a:000 )
|
|
let [r, g, b] = color#HSLtoRGB(h, s, l)
|
|
return color#RGBtoHex(r, g, b)
|
|
endfu
|
|
|
|
" @params String color The color
|
|
" @params {Number|String|Float} [amount=5] The percentage of light
|
|
fu! color#Lighten(color, ...)
|
|
let amount = a:0 ?
|
|
\(type(a:1) < 2 ?
|
|
\str2float(a:1) : a:1 )
|
|
\: 5.0
|
|
|
|
if(amount < 1.0)
|
|
let amount = 1.0 + amount
|
|
else
|
|
let amount = 1.0 + (amount / 100.0)
|
|
end
|
|
|
|
let rgb = color#HexToRGB(a:color)
|
|
let rgb = map(rgb, 'v:val * amount')
|
|
let rgb = map(rgb, 'v:val > 255.0 ? 255.0 : v:val')
|
|
let rgb = map(rgb, 'float2nr(v:val)')
|
|
let hex = color#RGBtoHex(rgb)
|
|
return hex
|
|
endfu
|
|
|
|
" @params String color The color
|
|
" @params {Number|String|Float} [amount=5] The percentage of darkness
|
|
fu! color#Darken(color, ...)
|
|
let amount = a:0 ?
|
|
\(type(a:1) < 2 ?
|
|
\str2float(a:1) : a:1 )
|
|
\: 5.0
|
|
|
|
if(amount < 1.0)
|
|
let amount = 1.0 - amount
|
|
else
|
|
let amount = 1.0 - (amount / 100.0)
|
|
end
|
|
|
|
if(amount < 0.0)
|
|
let amount = 0.0 | end
|
|
|
|
|
|
let rgb = color#HexToRGB(a:color)
|
|
let rgb = map(rgb, 'v:val * amount')
|
|
let rgb = map(rgb, 'v:val > 255.0 ? 255.0 : v:val')
|
|
let rgb = map(rgb, 'float2nr(v:val)')
|
|
let hex = color#RGBtoHex(rgb)
|
|
return hex
|
|
endfu
|
|
|
|
function! color#Decrease (...)
|
|
if &background == 'light'
|
|
return call(function('color#Lighten'), a:000)
|
|
end
|
|
return call(function('color#Darken'), a:000)
|
|
endfunc
|
|
|
|
function! color#Increase (...)
|
|
if &background == 'light'
|
|
return call(function('color#Darken'), a:000)
|
|
end
|
|
return call(function('color#Lighten'), a:000)
|
|
endfunc
|
|
|
|
function! color#Mix (a, b, ...)
|
|
let amount = a:0 ? a:1 : 0.5
|
|
|
|
let ca = color#HexToRGB(a:a)
|
|
let cb = color#HexToRGB(a:b)
|
|
|
|
let r = s:interpolate(ca[0], cb[0], amount)
|
|
let g = s:interpolate(ca[1], cb[1], amount)
|
|
let b = s:interpolate(ca[2], cb[2], amount)
|
|
|
|
return color#RGBtoHex([r, g, b])
|
|
endfunc
|
|
|
|
function! s:interpolate (start, end, amount)
|
|
let diff = a:end - a:start
|
|
return a:start + (diff * a:amount)
|
|
endfunc
|
|
|
|
" }}}1
|
|
|
|
|