jxl_render/
region.rs

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}