style.rs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. use std::{num::ParseFloatError, str::FromStr};
  2. use tui::style::{Color, Modifier, Style};
  3. use crate::RenderingMode;
  4. #[derive(Clone, Copy, Debug, PartialEq)]
  5. pub struct RinkColor {
  6. pub color: Color,
  7. pub alpha: u8,
  8. }
  9. impl Default for RinkColor {
  10. fn default() -> Self {
  11. Self {
  12. color: Color::Black,
  13. alpha: 0,
  14. }
  15. }
  16. }
  17. impl RinkColor {
  18. pub fn blend(self, other: Color) -> Color {
  19. if self.color == Color::Reset {
  20. Color::Reset
  21. } else if self.alpha == 0 {
  22. other
  23. } else {
  24. let [sr, sg, sb] = to_rgb(self.color).map(|e| e as u16);
  25. let [or, og, ob] = to_rgb(other).map(|e| e as u16);
  26. let sa = self.alpha as u16;
  27. let rsa = 255 - sa;
  28. Color::Rgb(
  29. ((sr * sa + or * rsa) / 255) as u8,
  30. ((sg * sa + og * rsa) / 255) as u8,
  31. ((sb * sa + ob * rsa) / 255) as u8,
  32. )
  33. }
  34. }
  35. }
  36. fn parse_value(
  37. v: &str,
  38. current_max_output: f32,
  39. required_max_output: f32,
  40. ) -> Result<f32, ParseFloatError> {
  41. if let Some(stripped) = v.strip_suffix('%') {
  42. Ok((stripped.trim().parse::<f32>()? / 100.0) * required_max_output)
  43. } else {
  44. Ok((v.trim().parse::<f32>()? / current_max_output) * required_max_output)
  45. }
  46. }
  47. pub struct ParseColorError;
  48. fn parse_hex(color: &str) -> Result<Color, ParseColorError> {
  49. let mut values = [0, 0, 0];
  50. let mut color_ok = true;
  51. for i in 0..values.len() {
  52. if let Ok(v) = u8::from_str_radix(&color[(1 + 2 * i)..(1 + 2 * (i + 1))], 16) {
  53. values[i] = v;
  54. } else {
  55. color_ok = false;
  56. }
  57. }
  58. if color_ok {
  59. Ok(Color::Rgb(values[0], values[1], values[2]))
  60. } else {
  61. Err(ParseColorError)
  62. }
  63. }
  64. fn parse_rgb(color: &str) -> Result<Color, ParseColorError> {
  65. let mut values = [0, 0, 0];
  66. let mut color_ok = true;
  67. for (v, i) in color.split(',').zip(0..values.len()) {
  68. if let Ok(v) = parse_value(v.trim(), 255.0, 255.0) {
  69. values[i] = v as u8;
  70. } else {
  71. color_ok = false;
  72. }
  73. }
  74. if color_ok {
  75. Ok(Color::Rgb(values[0], values[1], values[2]))
  76. } else {
  77. Err(ParseColorError)
  78. }
  79. }
  80. fn parse_hsl(color: &str) -> Result<Color, ParseColorError> {
  81. let mut values = [0.0, 0.0, 0.0];
  82. let mut color_ok = true;
  83. for (v, i) in color.split(',').zip(0..values.len()) {
  84. if let Ok(v) = parse_value(v.trim(), if i == 0 { 360.0 } else { 100.0 }, 1.0) {
  85. values[i] = v;
  86. } else {
  87. color_ok = false;
  88. }
  89. }
  90. if color_ok {
  91. let [h, s, l] = values;
  92. let rgb = if s == 0.0 {
  93. [l as u8; 3]
  94. } else {
  95. fn hue_to_rgb(p: f32, q: f32, mut t: f32) -> f32 {
  96. if t < 0.0 {
  97. t += 1.0;
  98. }
  99. if t > 1.0 {
  100. t -= 1.0;
  101. }
  102. if t < 1.0 / 6.0 {
  103. p + (q - p) * 6.0 * t
  104. } else if t < 1.0 / 2.0 {
  105. q
  106. } else if t < 2.0 / 3.0 {
  107. p + (q - p) * (2.0 / 3.0 - t) * 6.0
  108. } else {
  109. p
  110. }
  111. }
  112. let q = if l < 0.5 {
  113. l * (1.0 + s)
  114. } else {
  115. l + s - l * s
  116. };
  117. let p = 2.0 * l - q;
  118. [
  119. (hue_to_rgb(p, q, h + 1.0 / 3.0) * 255.0) as u8,
  120. (hue_to_rgb(p, q, h) * 255.0) as u8,
  121. (hue_to_rgb(p, q, h - 1.0 / 3.0) * 255.0) as u8,
  122. ]
  123. };
  124. Ok(Color::Rgb(rgb[0], rgb[1], rgb[2]))
  125. } else {
  126. Err(ParseColorError)
  127. }
  128. }
  129. impl FromStr for RinkColor {
  130. type Err = ParseColorError;
  131. fn from_str(color: &str) -> Result<Self, Self::Err> {
  132. match color {
  133. "red" => Ok(RinkColor {
  134. color: Color::Red,
  135. alpha: 255,
  136. }),
  137. "black" => Ok(RinkColor {
  138. color: Color::Black,
  139. alpha: 255,
  140. }),
  141. "green" => Ok(RinkColor {
  142. color: Color::Green,
  143. alpha: 255,
  144. }),
  145. "yellow" => Ok(RinkColor {
  146. color: Color::Yellow,
  147. alpha: 255,
  148. }),
  149. "blue" => Ok(RinkColor {
  150. color: Color::Blue,
  151. alpha: 255,
  152. }),
  153. "magenta" => Ok(RinkColor {
  154. color: Color::Magenta,
  155. alpha: 255,
  156. }),
  157. "cyan" => Ok(RinkColor {
  158. color: Color::Cyan,
  159. alpha: 255,
  160. }),
  161. "gray" => Ok(RinkColor {
  162. color: Color::Gray,
  163. alpha: 255,
  164. }),
  165. "darkgray" => Ok(RinkColor {
  166. color: Color::DarkGray,
  167. alpha: 255,
  168. }),
  169. // light red does not exist
  170. "orangered" => Ok(RinkColor {
  171. color: Color::LightRed,
  172. alpha: 255,
  173. }),
  174. "lightgreen" => Ok(RinkColor {
  175. color: Color::LightGreen,
  176. alpha: 255,
  177. }),
  178. "lightyellow" => Ok(RinkColor {
  179. color: Color::LightYellow,
  180. alpha: 255,
  181. }),
  182. "lightblue" => Ok(RinkColor {
  183. color: Color::LightBlue,
  184. alpha: 255,
  185. }),
  186. // light magenta does not exist
  187. "orchid" => Ok(RinkColor {
  188. color: Color::LightMagenta,
  189. alpha: 255,
  190. }),
  191. "lightcyan" => Ok(RinkColor {
  192. color: Color::LightCyan,
  193. alpha: 255,
  194. }),
  195. "white" => Ok(RinkColor {
  196. color: Color::White,
  197. alpha: 255,
  198. }),
  199. _ => {
  200. if color.len() == 7 && color.starts_with('#') {
  201. parse_hex(color).map(|c| RinkColor {
  202. color: c,
  203. alpha: 255,
  204. })
  205. } else if let Some(stripped) = color.strip_prefix("rgb(") {
  206. let color_values = stripped.trim_end_matches(')');
  207. if color.matches(',').count() == 3 {
  208. let (alpha, rgb_values) =
  209. color_values.rsplit_once(',').ok_or(ParseColorError)?;
  210. if let Ok(a) = alpha.parse() {
  211. parse_rgb(rgb_values).map(|c| RinkColor { color: c, alpha: a })
  212. } else {
  213. Err(ParseColorError)
  214. }
  215. } else {
  216. parse_rgb(color_values).map(|c| RinkColor {
  217. color: c,
  218. alpha: 255,
  219. })
  220. }
  221. } else if let Some(stripped) = color.strip_prefix("rgba(") {
  222. let color_values = stripped.trim_end_matches(')');
  223. if color.matches(',').count() == 3 {
  224. let (rgb_values, alpha) =
  225. color_values.rsplit_once(',').ok_or(ParseColorError)?;
  226. if let Ok(a) = parse_value(alpha, 1.0, 1.0) {
  227. parse_rgb(rgb_values).map(|c| RinkColor {
  228. color: c,
  229. alpha: (a * 255.0) as u8,
  230. })
  231. } else {
  232. Err(ParseColorError)
  233. }
  234. } else {
  235. parse_rgb(color_values).map(|c| RinkColor {
  236. color: c,
  237. alpha: 255,
  238. })
  239. }
  240. } else if let Some(stripped) = color.strip_prefix("hsl(") {
  241. let color_values = stripped.trim_end_matches(')');
  242. if color.matches(',').count() == 3 {
  243. let (rgb_values, alpha) =
  244. color_values.rsplit_once(',').ok_or(ParseColorError)?;
  245. if let Ok(a) = parse_value(alpha, 1.0, 1.0) {
  246. parse_hsl(rgb_values).map(|c| RinkColor {
  247. color: c,
  248. alpha: (a * 255.0) as u8,
  249. })
  250. } else {
  251. Err(ParseColorError)
  252. }
  253. } else {
  254. parse_hsl(color_values).map(|c| RinkColor {
  255. color: c,
  256. alpha: 255,
  257. })
  258. }
  259. } else if let Some(stripped) = color.strip_prefix("hsla(") {
  260. let color_values = stripped.trim_end_matches(')');
  261. if color.matches(',').count() == 3 {
  262. let (rgb_values, alpha) =
  263. color_values.rsplit_once(',').ok_or(ParseColorError)?;
  264. if let Ok(a) = parse_value(alpha, 1.0, 1.0) {
  265. parse_hsl(rgb_values).map(|c| RinkColor {
  266. color: c,
  267. alpha: (a * 255.0) as u8,
  268. })
  269. } else {
  270. Err(ParseColorError)
  271. }
  272. } else {
  273. parse_hsl(color_values).map(|c| RinkColor {
  274. color: c,
  275. alpha: 255,
  276. })
  277. }
  278. } else {
  279. Err(ParseColorError)
  280. }
  281. }
  282. }
  283. }
  284. }
  285. fn to_rgb(c: Color) -> [u8; 3] {
  286. match c {
  287. Color::Black => [0, 0, 0],
  288. Color::Red => [255, 0, 0],
  289. Color::Green => [0, 128, 0],
  290. Color::Yellow => [255, 255, 0],
  291. Color::Blue => [0, 0, 255],
  292. Color::Magenta => [255, 0, 255],
  293. Color::Cyan => [0, 255, 255],
  294. Color::Gray => [128, 128, 128],
  295. Color::DarkGray => [169, 169, 169],
  296. Color::LightRed => [255, 69, 0],
  297. Color::LightGreen => [144, 238, 144],
  298. Color::LightYellow => [255, 255, 224],
  299. Color::LightBlue => [173, 216, 230],
  300. Color::LightMagenta => [218, 112, 214],
  301. Color::LightCyan => [224, 255, 255],
  302. Color::White => [255, 255, 255],
  303. Color::Rgb(r, g, b) => [r, g, b],
  304. Color::Indexed(idx) => match idx {
  305. 16..=231 => {
  306. let v = idx - 16;
  307. // add 3 to round up
  308. let r = ((v as u16 / 36) * 255 + 3) / 5;
  309. let g = (((v as u16 % 36) / 6) * 255 + 3) / 5;
  310. let b = ((v as u16 % 6) * 255 + 3) / 5;
  311. [r as u8, g as u8, b as u8]
  312. }
  313. 232..=255 => {
  314. let l = (idx - 232) / 24;
  315. [l; 3]
  316. }
  317. // rink will never generate these colors, but they might be on the screen from another program
  318. _ => [0, 0, 0],
  319. },
  320. Color::Reset => [0, 0, 0],
  321. }
  322. }
  323. pub fn convert(mode: RenderingMode, c: Color) -> Color {
  324. if let Color::Reset = c {
  325. c
  326. } else {
  327. match mode {
  328. crate::RenderingMode::BaseColors => match c {
  329. Color::Rgb(_, _, _) => panic!("cannot convert rgb color to base color"),
  330. Color::Indexed(_) => panic!("cannot convert Ansi color to base color"),
  331. _ => c,
  332. },
  333. crate::RenderingMode::Rgb => {
  334. let rgb = to_rgb(c);
  335. Color::Rgb(rgb[0], rgb[1], rgb[2])
  336. }
  337. crate::RenderingMode::Ansi => match c {
  338. Color::Indexed(_) => c,
  339. _ => {
  340. let rgb = to_rgb(c);
  341. // 16-231: 6 × 6 × 6 color cube
  342. // 232-255: 23 step grayscale
  343. if rgb[0] == rgb[1] && rgb[1] == rgb[2] {
  344. let idx = 232 + (rgb[0] as u16 * 23 / 255) as u8;
  345. Color::Indexed(idx)
  346. } else {
  347. let r = (rgb[0] as u16 * 5) / 255;
  348. let g = (rgb[1] as u16 * 5) / 255;
  349. let b = (rgb[2] as u16 * 5) / 255;
  350. let idx = 16 + r * 36 + g * 6 + b;
  351. Color::Indexed(idx as u8)
  352. }
  353. }
  354. },
  355. }
  356. }
  357. }
  358. #[test]
  359. fn rgb_to_ansi() {
  360. for idx in 17..=231 {
  361. let idxed = Color::Indexed(idx);
  362. let rgb = to_rgb(idxed);
  363. // gray scale colors have two equivelent repersentations
  364. let color = Color::Rgb(rgb[0], rgb[1], rgb[2]);
  365. let converted = convert(RenderingMode::Ansi, color);
  366. if let Color::Indexed(i) = converted {
  367. if rgb[0] != rgb[1] || rgb[1] != rgb[2] {
  368. assert_eq!(idxed, converted);
  369. } else {
  370. assert!(i >= 232);
  371. }
  372. } else {
  373. panic!("color is not indexed")
  374. }
  375. }
  376. for idx in 232..=255 {
  377. let idxed = Color::Indexed(idx);
  378. let rgb = to_rgb(idxed);
  379. assert!(rgb[0] == rgb[1] && rgb[1] == rgb[2]);
  380. }
  381. }
  382. #[derive(Clone, Copy, PartialEq, Debug)]
  383. pub struct RinkStyle {
  384. pub fg: Option<RinkColor>,
  385. pub bg: Option<RinkColor>,
  386. pub add_modifier: Modifier,
  387. pub sub_modifier: Modifier,
  388. }
  389. impl Default for RinkStyle {
  390. fn default() -> Self {
  391. Self {
  392. fg: Some(RinkColor {
  393. color: Color::White,
  394. alpha: 255,
  395. }),
  396. bg: None,
  397. add_modifier: Modifier::empty(),
  398. sub_modifier: Modifier::empty(),
  399. }
  400. }
  401. }
  402. impl RinkStyle {
  403. pub fn add_modifier(mut self, m: Modifier) -> Self {
  404. self.sub_modifier.remove(m);
  405. self.add_modifier.insert(m);
  406. self
  407. }
  408. pub fn remove_modifier(mut self, m: Modifier) -> Self {
  409. self.add_modifier.remove(m);
  410. self.sub_modifier.insert(m);
  411. self
  412. }
  413. pub fn merge(mut self, other: RinkStyle) -> Self {
  414. self.fg = self.fg.or(other.fg);
  415. self.add_modifier(other.add_modifier)
  416. .remove_modifier(other.sub_modifier)
  417. }
  418. }
  419. impl From<RinkStyle> for Style {
  420. fn from(val: RinkStyle) -> Self {
  421. Style {
  422. fg: val.fg.map(|c| c.color),
  423. bg: val.bg.map(|c| c.color),
  424. add_modifier: val.add_modifier,
  425. sub_modifier: val.sub_modifier,
  426. }
  427. }
  428. }