atmos/os_lib/web_engine/
box_shadow.rs1extern crate alloc;
14use alloc::vec::Vec;
15
16pub const DEFAULT_SHADOW_COLOR: u32 = 0x8000_0000;
19
20pub type Shadow = (i32, i32, i32, u32, bool);
22
23pub fn split_top_level_ws(s: &str) -> Vec<&str> {
25 let mut out = Vec::new();
26 let mut depth = 0i32;
27 let mut start: Option<usize> = None;
28 for (i, c) in s.char_indices() {
29 match c {
30 '(' => {
31 depth += 1;
32 start.get_or_insert(i);
33 }
34 ')' => {
35 depth -= 1;
36 start.get_or_insert(i);
37 }
38 c if c.is_whitespace() && depth <= 0 => {
39 if let Some(st) = start.take() {
40 if let Some(t) = s.get(st..i) {
41 out.push(t);
42 }
43 }
44 }
45 _ => {
46 start.get_or_insert(i);
47 }
48 }
49 }
50 if let Some(st) = start {
51 if let Some(t) = s.get(st..) {
52 out.push(t);
53 }
54 }
55 out
56}
57
58pub fn parse_len_with_font(tok: &str, font_px: f32) -> Option<i32> {
60 let t = tok.trim();
61 if let Some(n) = t.strip_suffix("px") {
62 return n.parse::<f32>().ok().filter(|v| v.is_finite()).map(|v| v as i32);
63 }
64 if let Some(n) = t.strip_suffix("rem") {
65 return n.parse::<f32>().ok().filter(|v| v.is_finite()).map(|v| (v * 16.0) as i32);
66 }
67 if let Some(n) = t.strip_suffix("em") {
68 let f = if font_px > 0.0 { font_px } else { 16.0 };
69 return n.parse::<f32>().ok().filter(|v| v.is_finite()).map(|v| (v * f) as i32);
70 }
71 match t.parse::<f32>() {
73 Ok(0.0) => Some(0),
74 _ => None,
75 }
76}
77
78pub fn parse_len(tok: &str) -> Option<i32> {
79 parse_len_with_font(tok, 16.0)
80}
81
82pub fn parse_one_with_font(seg: &str, font_px: f32) -> Option<Shadow> {
85 let mut lens: Vec<i32> = Vec::new();
86 let mut color = DEFAULT_SHADOW_COLOR;
87 let mut inset = false;
88 for tok in split_top_level_ws(seg.trim()) {
89 if tok.eq_ignore_ascii_case("inset") {
90 inset = true;
91 } else if let Some(n) = parse_len_with_font(tok, font_px) {
92 lens.push(n);
93 } else if let Some(c) = crate::os_lib::css::parse_color(tok) {
94 color = c;
95 }
96 }
97 if lens.len() < 2 {
98 return None;
99 }
100 let blur = lens.get(2).copied().unwrap_or(0).max(0);
101 Some((lens[0], lens[1], blur, color, inset))
102}
103
104pub fn parse_one(seg: &str) -> Option<Shadow> {
105 parse_one_with_font(seg, 16.0)
106}
107
108pub fn parse_list_with_font(v: &str, font_px: f32) -> Vec<Shadow> {
110 let v = v.trim();
111 if v.is_empty() || v.eq_ignore_ascii_case("none") {
112 return Vec::new();
113 }
114 crate::os_lib::css::split_top_level_commas(v)
115 .into_iter()
116 .filter_map(|s| parse_one_with_font(s, font_px))
117 .collect()
118}
119
120pub fn parse_list(v: &str) -> Vec<Shadow> {
121 parse_list_with_font(v, 16.0)
122}
123
124pub fn coverage(d: f32, blur: i32) -> f32 {
129 if blur <= 0 {
130 return if d <= 0.0 { 1.0 } else { 0.0 };
131 }
132 (0.5 - d / blur as f32).clamp(0.0, 1.0)
133}