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(tok: &str) -> 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").or_else(|| t.strip_suffix("em")) {
65 return n.parse::<f32>().ok().filter(|v| v.is_finite()).map(|v| (v * 16.0) as i32);
66 }
67 match t.parse::<f32>() {
69 Ok(0.0) => Some(0),
70 _ => None,
71 }
72}
73
74pub fn parse_one(seg: &str) -> Option<Shadow> {
77 let mut lens: Vec<i32> = Vec::new();
78 let mut color = DEFAULT_SHADOW_COLOR;
79 let mut inset = false;
80 for tok in split_top_level_ws(seg.trim()) {
81 if tok.eq_ignore_ascii_case("inset") {
82 inset = true;
83 } else if let Some(n) = parse_len(tok) {
84 lens.push(n);
85 } else if let Some(c) = crate::os_lib::css::parse_color(tok) {
86 color = c;
87 }
88 }
89 if lens.len() < 2 {
90 return None;
91 }
92 let blur = lens.get(2).copied().unwrap_or(0).max(0);
93 Some((lens[0], lens[1], blur, color, inset))
94}
95
96pub fn parse_list(v: &str) -> Vec<Shadow> {
98 let v = v.trim();
99 if v.is_empty() || v.eq_ignore_ascii_case("none") {
100 return Vec::new();
101 }
102 crate::os_lib::css::split_top_level_commas(v)
103 .into_iter()
104 .filter_map(parse_one)
105 .collect()
106}
107
108pub fn coverage(d: f32, blur: i32) -> f32 {
113 if blur <= 0 {
114 return if d <= 0.0 { 1.0 } else { 0.0 };
115 }
116 (0.5 - d / blur as f32).clamp(0.0, 1.0)
117}