blockworx_canvas2d/
color.rs1use std::cell::RefCell;
4use std::collections::HashMap;
5use std::rc::Rc;
6
7use blockworx_paint::Color;
8
9#[must_use]
12pub fn css_color(color: Color) -> String {
13 let [r, g, b, a] = color.to_srgba_unmultiplied();
14 if a == 255 {
15 format!("rgb({r},{g},{b})")
16 } else {
17 format!("rgba({r},{g},{b},{:.4})", f32::from(a) / 255.0)
18 }
19}
20
21#[derive(Default)]
26pub struct Swatches(RefCell<HashMap<Color, Rc<str>>>);
27
28impl Swatches {
29 pub fn css(&self, color: Color) -> Rc<str> {
30 if let Some(cached) = self.0.borrow().get(&color) {
31 return Rc::clone(cached);
32 }
33 let css: Rc<str> = Rc::from(css_color(color));
34 self.0.borrow_mut().insert(color, Rc::clone(&css));
35 css
36 }
37}
38
39#[cfg(test)]
40mod tests {
41 use super::*;
42
43 #[test]
44 fn an_opaque_colour_needs_no_alpha_channel() {
45 assert_eq!(css_color(Color::from_rgb(1, 2, 3)), "rgb(1,2,3)");
46 assert_eq!(css_color(Color::WHITE), "rgb(255,255,255)");
47 }
48
49 #[test]
51 fn a_swatch_comes_back_from_the_cache() {
52 let swatches = Swatches::default();
53 let once = swatches.css(Color::GRAY);
54 let twice = swatches.css(Color::GRAY);
55 assert!(Rc::ptr_eq(&once, &twice), "the swatch was formatted twice");
56 assert_eq!(&*once, css_color(Color::GRAY));
57 assert_ne!(swatches.css(Color::WHITE), once);
58 }
59
60 #[test]
61 fn a_translucent_colour_is_unmultiplied_back_to_css() {
62 let half = Color::from_rgba_unmultiplied(200, 100, 50, 128);
63 assert!(
64 half.r() < 200,
65 "precondition: the display list holds it multiplied ({})",
66 half.r()
67 );
68 let css = css_color(half);
69 let channels: Vec<u32> = css
70 .trim_start_matches("rgba(")
71 .trim_end_matches(')')
72 .split(',')
73 .take(3)
74 .filter_map(|channel| channel.parse().ok())
75 .collect();
76 assert_eq!(channels.len(), 3, "{css}");
80 for (written, asked) in channels.iter().zip([200, 100, 50]) {
81 assert!(written.abs_diff(asked) <= 1, "{css}");
82 }
83 assert!(css.ends_with(",0.5020)"), "{css}");
84 assert_eq!(css_color(Color::TRANSPARENT), "rgba(0,0,0,0.0000)");
85 }
86}