oxiui_render_soft/
clip.rs1#[derive(Clone, Copy, Debug, PartialEq, Eq)]
9pub struct ClipRect {
10 pub x0: i64,
12 pub y0: i64,
14 pub x1: i64,
16 pub y1: i64,
18}
19
20impl ClipRect {
21 pub fn full(width: u32, height: u32) -> Self {
23 Self {
24 x0: 0,
25 y0: 0,
26 x1: width as i64,
27 y1: height as i64,
28 }
29 }
30
31 pub fn from_rect(x: i64, y: i64, w: i64, h: i64) -> Self {
33 Self {
34 x0: x,
35 y0: y,
36 x1: x + w.max(0),
37 y1: y + h.max(0),
38 }
39 }
40
41 pub fn is_empty(&self) -> bool {
43 self.x1 <= self.x0 || self.y1 <= self.y0
44 }
45
46 pub fn intersect(&self, other: &ClipRect) -> ClipRect {
48 ClipRect {
49 x0: self.x0.max(other.x0),
50 y0: self.y0.max(other.y0),
51 x1: self.x1.min(other.x1),
52 y1: self.y1.min(other.y1),
53 }
54 }
55
56 pub fn contains(&self, x: i64, y: i64) -> bool {
58 x >= self.x0 && x < self.x1 && y >= self.y0 && y < self.y1
59 }
60}
61
62#[derive(Clone, Debug)]
67pub struct ClipStack {
68 stack: Vec<ClipRect>,
69}
70
71impl ClipStack {
72 pub fn new(width: u32, height: u32) -> Self {
74 Self {
75 stack: vec![ClipRect::full(width, height)],
76 }
77 }
78
79 pub fn current(&self) -> ClipRect {
81 *self.stack.last().unwrap_or(&ClipRect {
83 x0: 0,
84 y0: 0,
85 x1: 0,
86 y1: 0,
87 })
88 }
89
90 pub fn push(&mut self, clip: ClipRect) {
92 let next = self.current().intersect(&clip);
93 self.stack.push(next);
94 }
95
96 pub fn pop(&mut self) {
98 if self.stack.len() > 1 {
99 self.stack.pop();
100 }
101 }
102
103 pub fn depth(&self) -> usize {
105 self.stack.len()
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112
113 #[test]
114 fn nested_clips_intersect() {
115 let mut s = ClipStack::new(100, 100);
116 assert_eq!(s.current(), ClipRect::full(100, 100));
117 s.push(ClipRect::from_rect(10, 10, 50, 50)); s.push(ClipRect::from_rect(40, 40, 50, 50)); let c = s.current();
120 assert_eq!(
121 c,
122 ClipRect {
123 x0: 40,
124 y0: 40,
125 x1: 60,
126 y1: 60
127 }
128 );
129 s.pop();
130 assert_eq!(
131 s.current(),
132 ClipRect {
133 x0: 10,
134 y0: 10,
135 x1: 60,
136 y1: 60
137 }
138 );
139 }
140
141 #[test]
142 fn base_clip_never_popped() {
143 let mut s = ClipStack::new(10, 10);
144 s.pop();
145 s.pop();
146 assert_eq!(s.depth(), 1);
147 assert_eq!(s.current(), ClipRect::full(10, 10));
148 }
149
150 #[test]
151 fn contains_and_empty() {
152 let c = ClipRect::from_rect(0, 0, 5, 5);
153 assert!(c.contains(0, 0));
154 assert!(c.contains(4, 4));
155 assert!(!c.contains(5, 5));
156 assert!(ClipRect::from_rect(0, 0, 0, 5).is_empty());
157 let disjoint =
158 ClipRect::from_rect(0, 0, 5, 5).intersect(&ClipRect::from_rect(10, 10, 5, 5));
159 assert!(disjoint.is_empty());
160 }
161}