Skip to content

Commit 2bd5797

Browse files
committed
Created fast rgb to xterm converter
1 parent 855e582 commit 2bd5797

File tree

3 files changed

+79
-36
lines changed

3 files changed

+79
-36
lines changed

autoload/capesky.vim

+3-3
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ function! capesky#init(...)
3636
let g:capesky_loaded = 1
3737
endfunction
3838

39-
function! s:getcolorstr(ground, color)
39+
function! s:getColorStr(ground, color)
4040
" ground = 'fg' or 'bg'
4141
" color = 'red' or '#123456' or 123 or ['#123456',123]
4242
if (tolower(a:ground) == tolower(a:color)) || (tolower(a:color) == 'none')
@@ -75,15 +75,15 @@ function! capesky#hi(group, fg, ...)
7575

7676
" forground
7777
if strlen(a:fg)
78-
let str .= s:getcolorstr('fg', a:fg)
78+
let str .= s:getColorStr('fg', a:fg)
7979
endif
8080

8181
" background
8282
" a:0 stores the number of optional args passed in
8383
if a:0 >= 1
8484
let bg = a:1
8585
if strlen(bg)
86-
let str .= s:getcolorstr('bg', bg)
86+
let str .= s:getColorStr('bg', bg)
8787
endif
8888
endif
8989

autoload/capesky/t_xterm.vim

+58-26
Original file line numberDiff line numberDiff line change
@@ -25,46 +25,35 @@ let s:c = s:getXterm2RgbDict()
2525

2626
" Given two RGB ojects, gives the difference between the two
2727
function! s:colorDiff(rgb1, rgb2)
28-
" The human eye is most sensitive to green light, less to red and least to blue.
29-
let diff_r = 33 * abs(a:rgb1.r - a:rgb2.r)
30-
let diff_g = 50 * abs(a:rgb1.g - a:rgb2.g)
31-
let diff_b = 16 * abs(a:rgb1.b - a:rgb2.b)
32-
let diff = diff_r + diff_g + diff_b
33-
return diff
34-
endfunction
35-
36-
" Given two RGB ojects, and one is gray, gives the manhattan difference.
37-
function! s:grayDiff(rgb1, rgb2)
3828
let diff_r = abs(a:rgb1.r - a:rgb2.r)
3929
let diff_g = abs(a:rgb1.g - a:rgb2.g)
4030
let diff_b = abs(a:rgb1.b - a:rgb2.b)
41-
let diff = diff_r + diff_g + diff_b
42-
return diff
31+
let is_gray_diff = (a:rgb1.r == a:rgb1.b) && (a:rgb1.b == a:rgb1.g) && (a:rgb2.r == a:rgb2.b) && (a:rgb2.b == a:rgb2.g)
32+
if !is_gray_diff
33+
" The human eye is most sensitive to green light, less to red and least to blue.
34+
" In the ratio of about 50 / 33 / 16
35+
return 16*diff_r + 33*diff_g + 50*diff_b
36+
else
37+
" Slightly favour pure gray colors if is gray
38+
return 32*diff_r + 32*diff_g + 32*diff_b
39+
endif
4340
endfunction
44-
let s:rgb_black = capesky#t_hex#toRgb('#000000')
45-
let s:rgb_white = capesky#t_hex#toRgb('#ffffff')
46-
let s:max_error = s:colorDiff(s:rgb_black, s:rgb_white)
41+
let s:max_error = s:colorDiff({'r':0, 'g':0, 'b':0}, {'r':0xff, 'g':0xff, 'b':0xff})
4742

4843
" Converts RGB object -> xterm 255 color number
4944
" Does not use colors 0-15 because they are none standard (customised)
50-
function! capesky#t_xterm#fromRgb(rgb)
51-
let is_gray = (a:rgb.r == a:rgb.b) && (a:rgb.b == a:rgb.g)
45+
" This function is too slow to use to be used to convert all colours on
46+
" the fly, but used to check capesky#t_xterm#fromRgb (faster version same
47+
" output)
48+
function! capesky#t_xterm#fromRgbSlow(rgb)
5249
let best = s:max_error
5350
let e = 0 " Init for scope
5451
for i in range(16, 255)
5552
let rgb_check = s:c[i]
56-
if is_gray
57-
let e = s:grayDiff(a:rgb, rgb_check)
58-
else
59-
let e = s:colorDiff(a:rgb, rgb_check)
60-
endif
53+
let e = s:colorDiff(a:rgb, rgb_check)
6154
if e <= best
6255
let best = e
6356
let n = i
64-
if e <= 8
65-
" I think won't find a closer match
66-
return n
67-
endif
6857
endif
6958
endfor
7059
return n
@@ -75,3 +64,46 @@ function! capesky#t_xterm#toRgb(n)
7564
return rgb
7665
endfunction
7766

67+
" Colors step at these points
68+
" let s:steps = [ 0, 95, 135, 175, 215, 255]
69+
" Thresholds (dec) [ 48, 115, 155, 195, 235 ]
70+
" Thresholds (hex) [ 30, 73, 9b, c3, eb ]
71+
function! s:getClosestColorComponent(n)
72+
if a:n >= 0x73
73+
return (a:n - 35) / 0x28
74+
elseif a:n < 0x30
75+
return 0
76+
else
77+
return 1
78+
endif
79+
endfunction
80+
function! s:getClosestXtermColor16To231(rgb)
81+
let r = s:getClosestColorComponent(a:rgb.r)
82+
let g = s:getClosestColorComponent(a:rgb.g)
83+
let b = s:getClosestColorComponent(a:rgb.b)
84+
return 16 + 0x24*r + 6*g + b
85+
endfunction
86+
87+
function! s:getClosestXtermGray232To255(rgb)
88+
let l = a:rgb.r
89+
if l <= 0x03
90+
return 16 " black
91+
elseif l >= 0xf7
92+
return 231 "white
93+
elseif l>= 0xe9
94+
return 255 "darkest gray
95+
else
96+
" Some gray inbetween
97+
return (l - 3)/10 + 232
98+
endif
99+
endfunction
100+
101+
" Fast implentation of of getting xterm colour from RGB object
102+
function! capesky#t_xterm#fromRgb(rgb)
103+
let color_n = s:getClosestXtermColor16To231(a:rgb)
104+
let gray_n = s:getClosestXtermGray232To255(a:rgb)
105+
let color_rgb = s:c[color_n]
106+
let gray_rgb = s:c[gray_n]
107+
let n = (s:colorDiff(a:rgb, color_rgb) < s:colorDiff(a:rgb, gray_rgb)) ? color_n : gray_n
108+
return n
109+
endfunction

test/t_xterm.vim

+18-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
" Hex colour codes with matching closest terminal color
2-
function! s:xterm_fromRgb_test()
2+
function! s:xterm_fromRgbSlow_test()
33
let tests = [
44
\['#000000', 16],
55
\['#5f8700', 64],
@@ -13,18 +13,28 @@ function! s:xterm_fromRgb_test()
1313
\['#171717', 234],
1414
\['#739beb', 111],
1515
\]
16-
1716
for test in tests
1817
let correct_hex = test[0]
1918
let correct_xterm = test[1]
2019
let rgb = capesky#t_hex#toRgb(correct_hex)
21-
let test_xterm = capesky#t_xterm#fromRgb(rgb)
20+
let test_xterm = capesky#t_xterm#fromRgbSlow(rgb) " change to capesky#t_xterm#fromRgbSlow(rgb) to test the other function
2221
let result = (correct_xterm == test_xterm) ? 'OK' : 'FAIL'
2322
echom printf("t_xterm#fromRgb('%s') -> %3d == %3d ? %s", correct_hex, correct_xterm, test_xterm, result)
24-
if result == 'FAIL'
25-
echom correct_hex
26-
endif
2723
endfor
24+
endfunction
25+
call s:xterm_fromRgbSlow_test()
26+
27+
function! s:xterm_fromRgb_test()
28+
for i in range(0, 255)
29+
let l = i
30+
let rgb = {'r':l, 'g':l, 'b':l}
31+
let correct_xterm = capesky#t_xterm#fromRgb_slow(rgb)
32+
let test_xterm = capesky#t_xterm#fromRgb(rgb)
33+
let correct_hex = printf('#%02x%02x%02x', l, l, l)
34+
let result = (correct_xterm == test_xterm) ? 'OK' : 'FAIL'
35+
echom printf("%5d. t_xterm#fromRgb('%s') -> %3d == %3d ? %s", i, correct_hex, correct_xterm, test_xterm, result)
36+
endfor
37+
" redir END
2838
echom
2939
endfunction
3040
call s:xterm_fromRgb_test()
@@ -54,4 +64,5 @@ function! s:t_xterm_toRgb_test()
5464
endfor
5565
echom
5666
endfunction
57-
" call s:t_xterm_toRgb_test()
67+
call s:t_xterm_toRgb_test()
68+

0 commit comments

Comments
 (0)