1use jxl_image::ImageHeader;
2use jxl_modular::ChannelShift;
3
4#[derive(Debug, Copy, Clone, PartialEq, Eq, Default, Hash)]
5pub struct Region {
6 pub left: i32,
7 pub top: i32,
8 pub width: u32,
9 pub height: u32,
10}
11
12impl Region {
13 #[inline]
14 pub fn empty() -> Self {
15 Self::default()
16 }
17
18 #[inline]
19 pub fn with_size(width: u32, height: u32) -> Self {
20 Self {
21 left: 0,
22 top: 0,
23 width,
24 height,
25 }
26 }
27
28 #[inline]
29 pub fn is_empty(self) -> bool {
30 self.width == 0 || self.height == 0
31 }
32
33 #[inline]
34 pub fn right(self) -> i32 {
35 self.left.saturating_add_unsigned(self.width)
36 }
37
38 #[inline]
39 pub fn bottom(self) -> i32 {
40 self.top.saturating_add_unsigned(self.height)
41 }
42
43 pub fn contains(self, target: Region) -> bool {
44 if target.is_empty() {
45 return true;
46 }
47
48 self.left <= target.left
49 && self.top <= target.top
50 && self.right() >= target.right()
51 && self.bottom() >= target.bottom()
52 }
53
54 pub fn translate(self, x: i32, y: i32) -> Self {
55 Self {
56 left: self.left + x,
57 top: self.top + y,
58 ..self
59 }
60 }
61
62 pub fn intersection(self, rhs: Region) -> Self {
63 if self.width == 0 || rhs.width == 0 || self.height == 0 || rhs.height == 0 {
64 return Self {
65 left: 0,
66 top: 0,
67 width: 0,
68 height: 0,
69 };
70 }
71
72 let mut ax = (self.left, self.right());
73 let mut ay = (self.top, self.bottom());
74 let mut bx = (rhs.left, rhs.right());
75 let mut by = (rhs.top, rhs.bottom());
76 if ax.0 > bx.0 {
77 std::mem::swap(&mut ax, &mut bx);
78 }
79 if ay.0 > by.0 {
80 std::mem::swap(&mut ay, &mut by);
81 }
82
83 if ax.1 <= bx.0 || ay.1 <= by.0 {
84 Self {
85 left: 0,
86 top: 0,
87 width: 0,
88 height: 0,
89 }
90 } else {
91 Self {
92 left: bx.0,
93 top: by.0,
94 width: std::cmp::min(ax.1, bx.1).abs_diff(bx.0),
95 height: std::cmp::min(ay.1, by.1).abs_diff(by.0),
96 }
97 }
98 }
99
100 #[inline]
101 pub fn merge(self, other: Self) -> Self {
102 if other.is_empty() {
103 return self;
104 }
105 if self.is_empty() {
106 return other;
107 }
108
109 let left = self.left.min(other.left);
110 let top = self.top.min(other.top);
111 let right = self
112 .left
113 .wrapping_add_unsigned(self.width)
114 .max(other.left.wrapping_add_unsigned(other.width));
115 let bottom = self
116 .top
117 .wrapping_add_unsigned(self.height)
118 .max(other.top.wrapping_add_unsigned(other.height));
119 let width = right.abs_diff(left);
120 let height = bottom.abs_diff(top);
121
122 Self {
123 left,
124 top,
125 width,
126 height,
127 }
128 }
129
130 #[inline]
131 pub fn pad(self, size: u32) -> Self {
132 Self {
133 left: self.left.saturating_sub_unsigned(size),
134 top: self.top.saturating_sub_unsigned(size),
135 width: self.width + size * 2,
136 height: self.height + size * 2,
137 }
138 }
139
140 #[inline]
141 pub fn downsample(self, factor: u32) -> Self {
142 if factor == 0 {
143 return self;
144 }
145
146 let add = (1u32 << factor) - 1;
147 let new_left = self.left >> factor;
148 let new_top = self.top >> factor;
149 let adj_width = self.width + self.left.abs_diff(new_left << factor);
150 let adj_height = self.height + self.top.abs_diff(new_top << factor);
151 Self {
152 left: new_left,
153 top: new_top,
154 width: (adj_width + add) >> factor,
155 height: (adj_height + add) >> factor,
156 }
157 }
158
159 #[inline]
160 pub fn downsample_separate(self, factor_x: u32, factor_y: u32) -> Self {
161 if factor_x == 0 && factor_y == 0 {
162 return self;
163 }
164
165 let add_x = (1u32 << factor_x) - 1;
166 let new_left = self.left >> factor_x;
167 let adj_width = self.width + self.left.abs_diff(new_left << factor_x);
168 let add_y = (1u32 << factor_y) - 1;
169 let new_top = self.top >> factor_y;
170 let adj_height = self.height + self.top.abs_diff(new_top << factor_y);
171 Self {
172 left: new_left,
173 top: new_top,
174 width: (adj_width + add_x) >> factor_x,
175 height: (adj_height + add_y) >> factor_y,
176 }
177 }
178
179 #[inline]
180 pub fn downsample_with_shift(self, shift: ChannelShift) -> Self {
181 let factor_x = shift.hshift();
182 let factor_y = shift.vshift();
183
184 let left = self.left >> factor_x;
185 let top = self.top >> factor_y;
186 let (width, height) = shift.shift_size((self.width, self.height));
187
188 Self {
189 left,
190 top,
191 width,
192 height,
193 }
194 }
195
196 #[inline]
197 pub fn upsample(self, factor: u32) -> Self {
198 self.upsample_separate(factor, factor)
199 }
200
201 #[inline]
202 pub fn upsample_separate(self, factor_x: u32, factor_y: u32) -> Self {
203 Self {
204 left: self.left << factor_x,
205 top: self.top << factor_y,
206 width: self.width << factor_x,
207 height: self.height << factor_y,
208 }
209 }
210
211 pub(crate) fn container_aligned(self, grid_dim: u32) -> Self {
212 debug_assert!(grid_dim.is_power_of_two());
213 let add = grid_dim - 1;
214 let mask = !add;
215 let new_left = ((self.left as u32) & mask) as i32;
216 let new_top = ((self.top as u32) & mask) as i32;
217 let x_diff = self.left.abs_diff(new_left);
218 let y_diff = self.top.abs_diff(new_top);
219 Self {
220 left: new_left,
221 top: new_top,
222 width: (self.width + x_diff + add) & mask,
223 height: (self.height + y_diff + add) & mask,
224 }
225 }
226
227 pub fn apply_orientation(self, image_header: &ImageHeader) -> Self {
228 let image_width = image_header.width_with_orientation();
229 let image_height = image_header.height_with_orientation();
230 let (_, _, mut left, mut top) = image_header.metadata.apply_orientation(
231 image_width,
232 image_height,
233 self.left,
234 self.top,
235 true,
236 );
237 let (_, _, mut right, mut bottom) = image_header.metadata.apply_orientation(
238 image_width,
239 image_height,
240 self.left + self.width as i32 - 1,
241 self.top + self.height as i32 - 1,
242 true,
243 );
244
245 if left > right {
246 std::mem::swap(&mut left, &mut right);
247 }
248 if top > bottom {
249 std::mem::swap(&mut top, &mut bottom);
250 }
251 let width = right.abs_diff(left) + 1;
252 let height = bottom.abs_diff(top) + 1;
253 Self {
254 left,
255 top,
256 width,
257 height,
258 }
259 }
260}