vim中将色彩转换为终端下色彩的算法的选择
2011年8月01日 19:24
看了 依云的文章, 让 Vim 在终端下和 GVIM 一样漂亮:gui2term.py 更新至 3.0 版
决定给自己的vim色彩插件加上终端支持。
colorscheme生成约50个色彩
ColorV的调色板生成约125个色彩
每个色彩需要对比255-16个终端颜色。
最后发现算法 delta_e_cie2000 实在太复杂,vim运行太费时间,要30+s。
而编译成delta.so,用vim的libcall()调用,
gcc -c -fpic -o delta.o delta.c
gcc -shared -lc -o delta.so delta.o
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> /* should declare function if it's not int; */ double degrees(); double radians(); int delta2000(char p_str[100]) { char ca[80],cb[80]; sscanf(p_str,"%6s:%6s",ca,cb); hex2lab(ca); hex2lab(cb); float L1,a1,b1,L2,a2,b2,delta_E; sscanf(ca,"%f:%f:%f",&L1,&a1,&b1); sscanf(cb,"%f:%f:%f",&L2,&a2,&b2); int Kl = 1;int Kc = 1;int Kh = 1; float avg_Lp,C1,C2,avg_C1_C2,G,a1p,a2p,C1p,C2p,avg_C1p_C2p; float h1p,h2p,avg_Hp,T,diff_h2p_h1p,delta_hp,delta_Lp,delta_Cp,delta_Hp; float S_L,S_C,S_H,delta_ro,R_C,R_T; avg_Lp = (L1 + L2) / 2.0; C1 = sqrt(pow(a1, 2) + pow(b1, 2)); C2 = sqrt(pow(a2, 2) + pow(b2, 2)); avg_C1_C2 = (C1 + C2) / 2.0; G = 0.5 * (1 - sqrt(pow(avg_C1_C2 , 7.0) / (pow(avg_C1_C2, 7.0) + pow(25.0, 7.0)))); a1p = (1.0 + G) * a1; a2p = (1.0 + G) * a2; C1p = sqrt(pow(a1p, 2) + pow(b1, 2)); C2p = sqrt(pow(a2p, 2) + pow(b2, 2)); avg_C1p_C2p =(C1p + C2p) / 2.0; if (degrees(atan2(b1,a1p)) >= 0) h1p = degrees(atan2(b1,a1p)); else h1p = degrees(atan2(b1,a1p)) + 360; if (degrees(atan2(b2,a2p)) >= 0) h2p = degrees(atan2(b2,a2p)); else h2p = degrees(atan2(b2,a2p)) + 360; if (abs(h1p - h2p) > 180) avg_Hp = (h1p + h2p + 360) / 2.0; else avg_Hp = (h1p + h2p) / 2.0; T = 1 - 0.17 * cos(radians(avg_Hp - 30)) + 0.24 * cos(radians(2 * avg_Hp)) + 0.32 * cos(radians(3 * avg_Hp + 6)) - 0.2 * cos(radians(4 * avg_Hp - 63)); diff_h2p_h1p = h2p - h1p; if (abs(diff_h2p_h1p) <= 180) delta_hp = diff_h2p_h1p; else if ((abs(diff_h2p_h1p) > 180) && (h2p <= h1p)) delta_hp = diff_h2p_h1p + 360; else delta_hp = diff_h2p_h1p - 360; delta_Lp = L2 - L1; delta_Cp = C2p - C1p; delta_Hp = 2 * sqrt(C2p * C1p) * sin(radians(delta_hp) / 2.0); S_L = 1 + ((0.015 * pow(avg_Lp - 50, 2)) / sqrt(20 + pow(avg_Lp - 50, 2.0))); S_C = 1 + 0.045 * avg_C1p_C2p; S_H = 1 + 0.015 * avg_C1p_C2p * T; delta_ro = 30 * exp(-(pow(((avg_Hp - 275) / 25), 2.0))); R_C = sqrt((pow(avg_C1p_C2p, 7.0)) / (pow(avg_C1p_C2p, 7.0) + pow(25.0, 7.0))); R_T = -2 * R_C * sin(2 * radians(delta_ro)); delta_E = sqrt(pow(delta_Lp /(S_L * Kl), 2) + pow(delta_Cp /(S_C * Kc), 2) + pow(delta_Hp /(S_H * Kh), 2) + R_T * (delta_Cp /(S_C * Kc)) * (delta_Hp / (S_H * Kh))); sprintf(p_str,"%f",delta_E); return p_str; } double degrees(double radian) { double degree ; degree = radian * 180 / 3.1415926 ; return degree; } double radians(double degree) { double radian ; radian = degree / 180 * 3.1415926 ; return radian; } int hex2lab(char *hex) { int ri,gi,bi; sscanf(hex,"%2x%2x%2x",&ri,&gi,&bi); double r,g,b; r=(double)ri/255.0,g=(double)gi/255.0,b=(double)bi/255.0; if (r>0.04045) { r = pow(((r + 0.055) / 1.055) , 2.4); } else { r = r / 12.92; } if (g>0.04045) { g = pow(((g + 0.055) / 1.055) , 2.4); } else{ g = g / 12.92; } if (b>0.04045) { b = pow(((b + 0.055) / 1.055) , 2.4); } else { b = b / 12.92; } r=r*100,g=g*100,b=b*100; double X,Y,Z; X = r * 0.4124 + g * 0.3576 + b * 0.1805; Y = r * 0.2126 + g * 0.7152 + b * 0.0722; Z = r * 0.0193 + g * 0.1192 + b * 0.9505; X = X/95.047; Y = Y/100.000; Z = Z/108.883; if (X > 0.008856) X = pow(X ,(0.3333333)); else X = (7.787 * X) + (16 / 116); if (Y > 0.008856) Y = pow(Y ,(0.3333333)); else Y = (7.787 * Y) + (16 / 116); if (Z > 0.008856) Z = pow(Z ,(0.3333333)); else Z = (7.787 * Z) + (16 / 116); double L,a; L = (116 * Y) - 16; a = 500 * (X - Y); b = 200 * (Y - Z); sprintf(hex,"%f:%f:%f",L,a,b); return 0; }
不得不吐槽一下,vim的libcall不但功能很弱,只能传递一个参数,其速度也非常坑人。
要消耗20+s,只比用vim本身运行略好。
用py_delta_2000()的速度不错,只需要3秒。
之后用 delta_e_cie76 速度差别不大。
然后改成hsv之差的绝对值,不过默认scheme的转换效果不好,而且速度仍要20+s。
最后还是决定用最简单的算法,直接用rgb之差的绝对值。
d=abs(r1-r2)+abs(b1-b2)+abs(g1-g2)
效果还算不错。
py_delta_rgb()只需要1秒左右
best_match = 0 smallest_distance = 10000000 hex1 = vim.eval("hex1") r1,g1,b1 = int(hex1[0:2],16),int(hex1[2:4],16),int(hex1[4:6],16) for c in range(16, 256): hex2 = termcolor[c] r2,g2,b2 = int(hex2[1:3],16),int(hex2[3:5],16),int(hex2[5:7],16) d = abs(r1-r2)+abs(g1-g2)+abs(b1-b2) if d < smallest_distance: smallest_distance = d best_match = c vim.command("let best_match = "+str(best_match))
vi_delta_rgb()需要3秒。
let best_match=0 let smallest_distance = 100000000 let hex1=a:hex let [r1,g1,b1] = ["0x".hex1[0:1],"0x".hex1[2:3],"0x".hex1[4:5]] for c in range(16,255) let hex2=s:fmt_hex(s:tmclr_dict[c]) let [r2,g2,b2] = ["0x".hex2[0:1],"0x".hex2[2:3],"0x".hex2[4:5]] let d = abs(r1-r2)+abs(g1-g2)+abs(b1-b2) if d < smallest_distance let smallest_distance = d let best_match = c endif endfor return best_match
不过最后一想,还是自己的问题。
毕竟别人的脚本是生成文件,我的目的是动态生成色彩,要求不一样,算法当然不同。
下图为各算法生成色彩与gvim调色板的对比
这个scheme是仿照butterscream.vim的配色生成的。
不过配色生成插件还没完成。