1#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
8#[non_exhaustive]
9pub struct DamageSlot {
10 pub armed: bool,
12 pub x: i32,
14 pub y: i32,
16 pub w: u32,
18 pub h: u32,
20 pub generation: u64,
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
30#[allow(
31 clippy::exhaustive_structs,
32 reason = "a value defined by its fields; there is nothing to add to it"
33)]
34pub struct DamageRect {
35 pub x: i32,
37 pub y: i32,
39 pub w: u32,
41 pub h: u32,
43}
44
45impl DamageRect {
46 #[must_use]
48 pub const fn is_empty(self) -> bool {
49 self.w == 0 || self.h == 0
50 }
51}
52
53#[derive(Default)]
55struct Bounds {
56 valid: bool,
57 x0: i32,
58 y0: i32,
59 x1: i32,
60 y1: i32,
61}
62
63impl Bounds {
64 fn add(&mut self, x: i32, y: i32, w: u32, h: u32) {
65 if w == 0 || h == 0 {
66 return;
67 }
68 let x1 = x.saturating_add(i32::try_from(w).unwrap_or(i32::MAX));
69 let y1 = y.saturating_add(i32::try_from(h).unwrap_or(i32::MAX));
70 if self.valid {
71 self.x0 = self.x0.min(x);
72 self.y0 = self.y0.min(y);
73 self.x1 = self.x1.max(x1);
74 self.y1 = self.y1.max(y1);
75 } else {
76 *self = Self {
77 valid: true,
78 x0: x,
79 y0: y,
80 x1,
81 y1,
82 };
83 }
84 }
85}
86
87#[must_use]
98pub fn compute_damage(
99 prev: &[DamageSlot],
100 cur: &[DamageSlot],
101 canvas_w: u32,
102 canvas_h: u32,
103) -> DamageRect {
104 let whole = DamageRect {
105 x: 0,
106 y: 0,
107 w: canvas_w,
108 h: canvas_h,
109 };
110 if prev.len() != cur.len() || prev.is_empty() {
113 return whole;
114 }
115
116 let mut bounds = Bounds::default();
117 for (before, after) in prev.iter().zip(cur) {
118 let changed = before.armed != after.armed
119 || (after.armed
120 && (before.x != after.x
121 || before.y != after.y
122 || before.w != after.w
123 || before.h != after.h
124 || before.generation != after.generation));
125 if !changed {
126 continue;
127 }
128 if before.armed {
131 bounds.add(before.x, before.y, before.w, before.h);
132 }
133 if after.armed {
134 bounds.add(after.x, after.y, after.w, after.h);
135 }
136 }
137
138 if !bounds.valid {
139 return DamageRect::default();
140 }
141
142 let x0 = bounds.x0.max(0);
143 let y0 = bounds.y0.max(0);
144 let x1 = bounds.x1.min(i32::try_from(canvas_w).unwrap_or(i32::MAX));
145 let y1 = bounds.y1.min(i32::try_from(canvas_h).unwrap_or(i32::MAX));
146 if x1 <= x0 || y1 <= y0 {
147 return DamageRect::default();
148 }
149
150 DamageRect {
151 x: x0,
152 y: y0,
153 w: (x1 - x0).unsigned_abs(),
154 h: (y1 - y0).unsigned_abs(),
155 }
156}
157
158#[must_use]
160pub fn union_rect(a: DamageRect, b: DamageRect) -> DamageRect {
161 if a.is_empty() {
162 return b;
163 }
164 if b.is_empty() {
165 return a;
166 }
167 let mut bounds = Bounds::default();
168 bounds.add(a.x, a.y, a.w, a.h);
169 bounds.add(b.x, b.y, b.w, b.h);
170 DamageRect {
171 x: bounds.x0,
172 y: bounds.y0,
173 w: (bounds.x1 - bounds.x0).unsigned_abs(),
174 h: (bounds.y1 - bounds.y0).unsigned_abs(),
175 }
176}
177
178#[must_use]
180pub fn intersect_rect(x: i32, y: i32, w: u32, h: u32, region: DamageRect) -> DamageRect {
181 let ax1 = x.saturating_add(i32::try_from(w).unwrap_or(i32::MAX));
182 let ay1 = y.saturating_add(i32::try_from(h).unwrap_or(i32::MAX));
183 let bx1 = region
184 .x
185 .saturating_add(i32::try_from(region.w).unwrap_or(i32::MAX));
186 let by1 = region
187 .y
188 .saturating_add(i32::try_from(region.h).unwrap_or(i32::MAX));
189
190 let x0 = x.max(region.x);
191 let y0 = y.max(region.y);
192 let x1 = ax1.min(bx1);
193 let y1 = ay1.min(by1);
194 if x1 <= x0 || y1 <= y0 {
195 return DamageRect::default();
196 }
197 DamageRect {
198 x: x0,
199 y: y0,
200 w: (x1 - x0).unsigned_abs(),
201 h: (y1 - y0).unsigned_abs(),
202 }
203}