colors.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. /**
  2. * colors.js - color-related functions for blessed.
  3. * Copyright (c) 2013-2015, Christopher Jeffrey and contributors (MIT License).
  4. * https://github.com/chjj/blessed
  5. */
  6. exports.match = function(r1, g1, b1) {
  7. if (typeof r1 === 'string') {
  8. var hex = r1;
  9. if (hex[0] !== '#') {
  10. return -1;
  11. }
  12. hex = exports.hexToRGB(hex);
  13. r1 = hex[0], g1 = hex[1], b1 = hex[2];
  14. } else if (Array.isArray(r1)) {
  15. b1 = r1[2], g1 = r1[1], r1 = r1[0];
  16. }
  17. var hash = (r1 << 16) | (g1 << 8) | b1;
  18. if (exports._cache[hash] != null) {
  19. return exports._cache[hash];
  20. }
  21. var ldiff = Infinity
  22. , li = -1
  23. , i = 0
  24. , c
  25. , r2
  26. , g2
  27. , b2
  28. , diff;
  29. for (; i < exports.vcolors.length; i++) {
  30. c = exports.vcolors[i];
  31. r2 = c[0];
  32. g2 = c[1];
  33. b2 = c[2];
  34. diff = colorDistance(r1, g1, b1, r2, g2, b2);
  35. if (diff === 0) {
  36. li = i;
  37. break;
  38. }
  39. if (diff < ldiff) {
  40. ldiff = diff;
  41. li = i;
  42. }
  43. }
  44. return exports._cache[hash] = li;
  45. };
  46. exports.RGBToHex = function(r, g, b) {
  47. if (Array.isArray(r)) {
  48. b = r[2], g = r[1], r = r[0];
  49. }
  50. function hex(n) {
  51. n = n.toString(16);
  52. if (n.length < 2) n = '0' + n;
  53. return n;
  54. }
  55. return '#' + hex(r) + hex(g) + hex(b);
  56. };
  57. exports.hexToRGB = function(hex) {
  58. if (hex.length === 4) {
  59. hex = hex[0]
  60. + hex[1] + hex[1]
  61. + hex[2] + hex[2]
  62. + hex[3] + hex[3];
  63. }
  64. var col = parseInt(hex.substring(1), 16)
  65. , r = (col >> 16) & 0xff
  66. , g = (col >> 8) & 0xff
  67. , b = col & 0xff;
  68. return [r, g, b];
  69. };
  70. // As it happens, comparing how similar two colors are is really hard. Here is
  71. // one of the simplest solutions, which doesn't require conversion to another
  72. // color space, posted on stackoverflow[1]. Maybe someone better at math can
  73. // propose a superior solution.
  74. // [1] http://stackoverflow.com/questions/1633828
  75. function colorDistance(r1, g1, b1, r2, g2, b2) {
  76. return Math.pow(30 * (r1 - r2), 2)
  77. + Math.pow(59 * (g1 - g2), 2)
  78. + Math.pow(11 * (b1 - b2), 2);
  79. }
  80. // This might work well enough for a terminal's colors: treat RGB as XYZ in a
  81. // 3-dimensional space and go midway between the two points.
  82. exports.mixColors = function(c1, c2, alpha) {
  83. // if (c1 === 0x1ff) return c1;
  84. // if (c2 === 0x1ff) return c1;
  85. if (c1 === 0x1ff) c1 = 0;
  86. if (c2 === 0x1ff) c2 = 0;
  87. if (alpha == null) alpha = 0.5;
  88. c1 = exports.vcolors[c1];
  89. var r1 = c1[0];
  90. var g1 = c1[1];
  91. var b1 = c1[2];
  92. c2 = exports.vcolors[c2];
  93. var r2 = c2[0];
  94. var g2 = c2[1];
  95. var b2 = c2[2];
  96. r1 += (r2 - r1) * alpha | 0;
  97. g1 += (g2 - g1) * alpha | 0;
  98. b1 += (b2 - b1) * alpha | 0;
  99. return exports.match([r1, g1, b1]);
  100. };
  101. exports.blend = function blend(attr, attr2, alpha) {
  102. var name, i, c, nc;
  103. var bg = attr & 0x1ff;
  104. if (attr2 != null) {
  105. var bg2 = attr2 & 0x1ff;
  106. if (bg === 0x1ff) bg = 0;
  107. if (bg2 === 0x1ff) bg2 = 0;
  108. bg = exports.mixColors(bg, bg2, alpha);
  109. } else {
  110. if (blend._cache[bg] != null) {
  111. bg = blend._cache[bg];
  112. // } else if (bg < 8) {
  113. // bg += 8;
  114. } else if (bg >= 8 && bg <= 15) {
  115. bg -= 8;
  116. } else {
  117. name = exports.ncolors[bg];
  118. if (name) {
  119. for (i = 0; i < exports.ncolors.length; i++) {
  120. if (name === exports.ncolors[i] && i !== bg) {
  121. c = exports.vcolors[bg];
  122. nc = exports.vcolors[i];
  123. if (nc[0] + nc[1] + nc[2] < c[0] + c[1] + c[2]) {
  124. blend._cache[bg] = i;
  125. bg = i;
  126. break;
  127. }
  128. }
  129. }
  130. }
  131. }
  132. }
  133. attr &= ~0x1ff;
  134. attr |= bg;
  135. var fg = (attr >> 9) & 0x1ff;
  136. if (attr2 != null) {
  137. var fg2 = (attr2 >> 9) & 0x1ff;
  138. // 0, 7, 188, 231, 251
  139. if (fg === 0x1ff) {
  140. // XXX workaround
  141. fg = 248;
  142. } else {
  143. if (fg === 0x1ff) fg = 7;
  144. if (fg2 === 0x1ff) fg2 = 7;
  145. fg = exports.mixColors(fg, fg2, alpha);
  146. }
  147. } else {
  148. if (blend._cache[fg] != null) {
  149. fg = blend._cache[fg];
  150. // } else if (fg < 8) {
  151. // fg += 8;
  152. } else if (fg >= 8 && fg <= 15) {
  153. fg -= 8;
  154. } else {
  155. name = exports.ncolors[fg];
  156. if (name) {
  157. for (i = 0; i < exports.ncolors.length; i++) {
  158. if (name === exports.ncolors[i] && i !== fg) {
  159. c = exports.vcolors[fg];
  160. nc = exports.vcolors[i];
  161. if (nc[0] + nc[1] + nc[2] < c[0] + c[1] + c[2]) {
  162. blend._cache[fg] = i;
  163. fg = i;
  164. break;
  165. }
  166. }
  167. }
  168. }
  169. }
  170. }
  171. attr &= ~(0x1ff << 9);
  172. attr |= fg << 9;
  173. return attr;
  174. };
  175. exports.blend._cache = {};
  176. exports._cache = {};
  177. exports.reduce = function(color, total) {
  178. if (color >= 16 && total <= 16) {
  179. color = exports.ccolors[color];
  180. } else if (color >= 8 && total <= 8) {
  181. color -= 8;
  182. } else if (color >= 2 && total <= 2) {
  183. color %= 2;
  184. }
  185. return color;
  186. };
  187. // XTerm Colors
  188. // These were actually tough to track down. The xterm source only uses color
  189. // keywords. The X11 source needed to be examined to find the actual values.
  190. // They then had to be mapped to rgb values and then converted to hex values.
  191. exports.xterm = [
  192. '#000000', // black
  193. '#cd0000', // red3
  194. '#00cd00', // green3
  195. '#cdcd00', // yellow3
  196. '#0000ee', // blue2
  197. '#cd00cd', // magenta3
  198. '#00cdcd', // cyan3
  199. '#e5e5e5', // gray90
  200. '#7f7f7f', // gray50
  201. '#ff0000', // red
  202. '#00ff00', // green
  203. '#ffff00', // yellow
  204. '#5c5cff', // rgb:5c/5c/ff
  205. '#ff00ff', // magenta
  206. '#00ffff', // cyan
  207. '#ffffff' // white
  208. ];
  209. // Seed all 256 colors. Assume xterm defaults.
  210. // Ported from the xterm color generation script.
  211. exports.colors = (function() {
  212. var cols = exports.colors = []
  213. , _cols = exports.vcolors = []
  214. , r
  215. , g
  216. , b
  217. , i
  218. , l;
  219. function hex(n) {
  220. n = n.toString(16);
  221. if (n.length < 2) n = '0' + n;
  222. return n;
  223. }
  224. function push(i, r, g, b) {
  225. cols[i] = '#' + hex(r) + hex(g) + hex(b);
  226. _cols[i] = [r, g, b];
  227. }
  228. // 0 - 15
  229. exports.xterm.forEach(function(c, i) {
  230. c = parseInt(c.substring(1), 16);
  231. push(i, (c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff);
  232. });
  233. // 16 - 231
  234. for (r = 0; r < 6; r++) {
  235. for (g = 0; g < 6; g++) {
  236. for (b = 0; b < 6; b++) {
  237. i = 16 + (r * 36) + (g * 6) + b;
  238. push(i,
  239. r ? (r * 40 + 55) : 0,
  240. g ? (g * 40 + 55) : 0,
  241. b ? (b * 40 + 55) : 0);
  242. }
  243. }
  244. }
  245. // 232 - 255 are grey.
  246. for (g = 0; g < 24; g++) {
  247. l = (g * 10) + 8;
  248. i = 232 + g;
  249. push(i, l, l, l);
  250. }
  251. return cols;
  252. })();
  253. // Map higher colors to the first 8 colors.
  254. // This allows translation of high colors to low colors on 8-color terminals.
  255. exports.ccolors = (function() {
  256. var _cols = exports.vcolors.slice()
  257. , cols = exports.colors.slice()
  258. , out;
  259. exports.vcolors = exports.vcolors.slice(0, 8);
  260. exports.colors = exports.colors.slice(0, 8);
  261. out = cols.map(exports.match);
  262. exports.colors = cols;
  263. exports.vcolors = _cols;
  264. exports.ccolors = out;
  265. return out;
  266. })();
  267. var colorNames = exports.colorNames = {
  268. // special
  269. default: -1,
  270. normal: -1,
  271. bg: -1,
  272. fg: -1,
  273. // normal
  274. black: 0,
  275. red: 1,
  276. green: 2,
  277. yellow: 3,
  278. blue: 4,
  279. magenta: 5,
  280. cyan: 6,
  281. white: 7,
  282. // light
  283. lightblack: 8,
  284. lightred: 9,
  285. lightgreen: 10,
  286. lightyellow: 11,
  287. lightblue: 12,
  288. lightmagenta: 13,
  289. lightcyan: 14,
  290. lightwhite: 15,
  291. // bright
  292. brightblack: 8,
  293. brightred: 9,
  294. brightgreen: 10,
  295. brightyellow: 11,
  296. brightblue: 12,
  297. brightmagenta: 13,
  298. brightcyan: 14,
  299. brightwhite: 15,
  300. // alternate spellings
  301. grey: 8,
  302. gray: 8,
  303. lightgrey: 7,
  304. lightgray: 7,
  305. brightgrey: 7,
  306. brightgray: 7
  307. };
  308. exports.convert = function(color) {
  309. if (typeof color === 'number') {
  310. ;
  311. } else if (typeof color === 'string') {
  312. color = color.replace(/[\- ]/g, '');
  313. if (colorNames[color] != null) {
  314. color = colorNames[color];
  315. } else {
  316. color = exports.match(color);
  317. }
  318. } else if (Array.isArray(color)) {
  319. color = exports.match(color);
  320. } else {
  321. color = -1;
  322. }
  323. return color !== -1 ? color : 0x1ff;
  324. };
  325. // Map higher colors to the first 8 colors.
  326. // This allows translation of high colors to low colors on 8-color terminals.
  327. // Why the hell did I do this by hand?
  328. exports.ccolors = {
  329. blue: [
  330. 4,
  331. 12,
  332. [17, 21],
  333. [24, 27],
  334. [31, 33],
  335. [38, 39],
  336. 45,
  337. [54, 57],
  338. [60, 63],
  339. [67, 69],
  340. [74, 75],
  341. 81,
  342. [91, 93],
  343. [97, 99],
  344. [103, 105],
  345. [110, 111],
  346. 117,
  347. [128, 129],
  348. [134, 135],
  349. [140, 141],
  350. [146, 147],
  351. 153,
  352. 165,
  353. 171,
  354. 177,
  355. 183,
  356. 189
  357. ],
  358. green: [
  359. 2,
  360. 10,
  361. 22,
  362. [28, 29],
  363. [34, 36],
  364. [40, 43],
  365. [46, 50],
  366. [64, 65],
  367. [70, 72],
  368. [76, 79],
  369. [82, 86],
  370. [106, 108],
  371. [112, 115],
  372. [118, 122],
  373. [148, 151],
  374. [154, 158],
  375. [190, 194]
  376. ],
  377. cyan: [
  378. 6,
  379. 14,
  380. 23,
  381. 30,
  382. 37,
  383. 44,
  384. 51,
  385. 66,
  386. 73,
  387. 80,
  388. 87,
  389. 109,
  390. 116,
  391. 123,
  392. 152,
  393. 159,
  394. 195
  395. ],
  396. red: [
  397. 1,
  398. 9,
  399. 52,
  400. [88, 89],
  401. [94, 95],
  402. [124, 126],
  403. [130, 132],
  404. [136, 138],
  405. [160, 163],
  406. [166, 169],
  407. [172, 175],
  408. [178, 181],
  409. [196, 200],
  410. [202, 206],
  411. [208, 212],
  412. [214, 218],
  413. [220, 224]
  414. ],
  415. magenta: [
  416. 5,
  417. 13,
  418. 53,
  419. 90,
  420. 96,
  421. 127,
  422. 133,
  423. 139,
  424. 164,
  425. 170,
  426. 176,
  427. 182,
  428. 201,
  429. 207,
  430. 213,
  431. 219,
  432. 225
  433. ],
  434. yellow: [
  435. 3,
  436. 11,
  437. 58,
  438. [100, 101],
  439. [142, 144],
  440. [184, 187],
  441. [226, 230]
  442. ],
  443. black: [
  444. 0,
  445. 8,
  446. 16,
  447. 59,
  448. 102,
  449. [232, 243]
  450. ],
  451. white: [
  452. 7,
  453. 15,
  454. 145,
  455. 188,
  456. 231,
  457. [244, 255]
  458. ]
  459. };
  460. exports.ncolors = [];
  461. Object.keys(exports.ccolors).forEach(function(name) {
  462. exports.ccolors[name].forEach(function(offset) {
  463. if (typeof offset === 'number') {
  464. exports.ncolors[offset] = name;
  465. exports.ccolors[offset] = exports.colorNames[name];
  466. return;
  467. }
  468. for (var i = offset[0], l = offset[1]; i <= l; i++) {
  469. exports.ncolors[i] = name;
  470. exports.ccolors[i] = exports.colorNames[name];
  471. }
  472. });
  473. delete exports.ccolors[name];
  474. });