libcamera/
geometry.rs

1use libcamera_sys::*;
2
3/// Represents `libcamera::Point`
4#[derive(Debug, Clone, Copy)]
5pub struct Point {
6    pub x: i32,
7    pub y: i32,
8}
9
10impl From<libcamera_point_t> for Point {
11    fn from(p: libcamera_point_t) -> Self {
12        Self { x: p.x, y: p.y }
13    }
14}
15
16/// Represents `libcamera::Size`
17#[derive(Debug, Clone, Copy)]
18pub struct Size {
19    pub width: u32,
20    pub height: u32,
21}
22
23impl Size {
24    pub const fn new(width: u32, height: u32) -> Self {
25        Self { width, height }
26    }
27
28    pub fn align_down_to(self, h_alignment: u32, v_alignment: u32) -> Self {
29        if h_alignment == 0 || v_alignment == 0 {
30            return self;
31        }
32        Self {
33            width: self.width / h_alignment * h_alignment,
34            height: self.height / v_alignment * v_alignment,
35        }
36    }
37
38    pub fn align_up_to(self, h_alignment: u32, v_alignment: u32) -> Self {
39        if h_alignment == 0 || v_alignment == 0 {
40            return self;
41        }
42        Self {
43            width: self.width.div_ceil(h_alignment) * h_alignment,
44            height: self.height.div_ceil(v_alignment) * v_alignment,
45        }
46    }
47
48    pub fn bound_to(self, bound: Size) -> Self {
49        Self {
50            width: self.width.min(bound.width),
51            height: self.height.min(bound.height),
52        }
53    }
54
55    pub fn expand_to(self, expand: Size) -> Self {
56        Self {
57            width: self.width.max(expand.width),
58            height: self.height.max(expand.height),
59        }
60    }
61
62    pub fn grow_by(self, margins: Size) -> Self {
63        Self {
64            width: self.width.saturating_add(margins.width),
65            height: self.height.saturating_add(margins.height),
66        }
67    }
68
69    pub fn shrink_by(self, margins: Size) -> Self {
70        Self {
71            width: self.width.saturating_sub(margins.width),
72            height: self.height.saturating_sub(margins.height),
73        }
74    }
75
76    /// Bound this size down to match the aspect ratio of `ratio`.
77    pub fn bounded_to_aspect_ratio(self, ratio: Size) -> Self {
78        if ratio.width == 0 || ratio.height == 0 {
79            return self;
80        }
81
82        let ratio1 = self.width as u64 * ratio.height as u64;
83        let ratio2 = ratio.width as u64 * self.height as u64;
84
85        if ratio1 > ratio2 {
86            Self {
87                width: (ratio2 / ratio.height as u64) as u32,
88                height: self.height,
89            }
90        } else {
91            Self {
92                width: self.width,
93                height: (ratio1 / ratio.width as u64) as u32,
94            }
95        }
96    }
97
98    /// Expand this size up to match the aspect ratio of `ratio`.
99    pub fn expanded_to_aspect_ratio(self, ratio: Size) -> Self {
100        if ratio.width == 0 || ratio.height == 0 {
101            return self;
102        }
103
104        let ratio1 = self.width as u64 * ratio.height as u64;
105        let ratio2 = ratio.width as u64 * self.height as u64;
106
107        if ratio1 < ratio2 {
108            Self {
109                width: (ratio2 / ratio.height as u64) as u32,
110                height: self.height,
111            }
112        } else {
113            Self {
114                width: self.width,
115                height: (ratio1 / ratio.width as u64) as u32,
116            }
117        }
118    }
119
120    /// Center a rectangle of this size at the given point.
121    pub fn centered_to(self, center: Point) -> Rectangle {
122        let x = center.x - (self.width as i32 / 2);
123        let y = center.y - (self.height as i32 / 2);
124        Rectangle {
125            x,
126            y,
127            width: self.width,
128            height: self.height,
129        }
130    }
131}
132
133impl From<libcamera_size_t> for Size {
134    fn from(s: libcamera_size_t) -> Self {
135        Self {
136            width: s.width,
137            height: s.height,
138        }
139    }
140}
141
142impl From<Size> for libcamera_size_t {
143    fn from(s: Size) -> Self {
144        Self {
145            width: s.width,
146            height: s.height,
147        }
148    }
149}
150
151/// Represents `libcamera::SizeRange`
152#[derive(Debug, Clone, Copy)]
153pub struct SizeRange {
154    pub min: Size,
155    pub max: Size,
156    pub h_step: u32,
157    pub v_step: u32,
158}
159
160impl SizeRange {
161    pub fn contains(&self, size: Size) -> bool {
162        if size.width < self.min.width
163            || size.width > self.max.width
164            || size.height < self.min.height
165            || size.height > self.max.height
166        {
167            return false;
168        }
169
170        if self.h_step != 0 {
171            let delta_w = size.width - self.min.width;
172            if !delta_w.is_multiple_of(self.h_step) {
173                return false;
174            }
175        }
176        if self.v_step != 0 {
177            let delta_h = size.height - self.min.height;
178            if !delta_h.is_multiple_of(self.v_step) {
179                return false;
180            }
181        }
182
183        true
184    }
185}
186
187impl From<libcamera_size_range_t> for SizeRange {
188    fn from(r: libcamera_size_range_t) -> Self {
189        Self {
190            min: r.min.into(),
191            max: r.max.into(),
192            h_step: r.hStep,
193            v_step: r.vStep,
194        }
195    }
196}
197
198impl From<SizeRange> for libcamera_size_range_t {
199    fn from(r: SizeRange) -> Self {
200        Self {
201            min: r.min.into(),
202            max: r.max.into(),
203            hStep: r.h_step,
204            vStep: r.v_step,
205        }
206    }
207}
208
209/// Represents `libcamera::Rectangle`
210#[derive(Debug, Clone, Copy)]
211pub struct Rectangle {
212    pub x: i32,
213    pub y: i32,
214    pub width: u32,
215    pub height: u32,
216}
217
218impl Rectangle {
219    pub fn size(&self) -> Size {
220        Size {
221            width: self.width,
222            height: self.height,
223        }
224    }
225
226    /// Center point of the rectangle.
227    pub fn center(&self) -> Point {
228        Point {
229            x: self.x.saturating_add((self.width / 2) as i32),
230            y: self.y.saturating_add((self.height / 2) as i32),
231        }
232    }
233
234    /// Top-left corner of the rectangle.
235    pub fn top_left(&self) -> Point {
236        Point { x: self.x, y: self.y }
237    }
238
239    /// Intersection of this rectangle with another.
240    pub fn bounded_to(self, bound: Rectangle) -> Rectangle {
241        let top_left_x = i64::from(self.x).max(i64::from(bound.x));
242        let top_left_y = i64::from(self.y).max(i64::from(bound.y));
243        let bottom_right_x =
244            (i64::from(self.x) + i64::from(self.width)).min(i64::from(bound.x) + i64::from(bound.width));
245        let bottom_right_y =
246            (i64::from(self.y) + i64::from(self.height)).min(i64::from(bound.y) + i64::from(bound.height));
247
248        let new_width = if bottom_right_x > top_left_x {
249            (bottom_right_x - top_left_x) as u32
250        } else {
251            0
252        };
253        let new_height = if bottom_right_y > top_left_y {
254            (bottom_right_y - top_left_y) as u32
255        } else {
256            0
257        };
258
259        Rectangle {
260            x: top_left_x as i32,
261            y: top_left_y as i32,
262            width: new_width,
263            height: new_height,
264        }
265    }
266
267    /// Translate the rectangle so it remains enclosed within the given boundary.
268    pub fn enclosed_in(self, boundary: Rectangle) -> Rectangle {
269        let mut result = self.bounded_to(Rectangle {
270            x: self.x,
271            y: self.y,
272            width: boundary.width,
273            height: boundary.height,
274        });
275
276        let clamp = |val: i64, min: i64, max: i64| -> i64 {
277            if val < min {
278                min
279            } else if val > max {
280                max
281            } else {
282                val
283            }
284        };
285
286        let max_x = i64::from(boundary.x) + i64::from(boundary.width) - i64::from(result.width);
287        let max_y = i64::from(boundary.y) + i64::from(boundary.height) - i64::from(result.height);
288
289        result.x = clamp(i64::from(result.x), i64::from(boundary.x), max_x) as i32;
290        result.y = clamp(i64::from(result.y), i64::from(boundary.y), max_y) as i32;
291        result
292    }
293
294    /// Return a translated rectangle.
295    pub fn translated_by(self, delta: Point) -> Rectangle {
296        Rectangle {
297            x: self.x.saturating_add(delta.x),
298            y: self.y.saturating_add(delta.y),
299            width: self.width,
300            height: self.height,
301        }
302    }
303
304    /// Return a rectangle scaled by rational factors.
305    pub fn scaled_by(self, numerator: Size, denominator: Size) -> Rectangle {
306        if denominator.width == 0 || denominator.height == 0 {
307            return self;
308        }
309        Rectangle {
310            x: (i64::from(self.x) * i64::from(numerator.width) / i64::from(denominator.width)) as i32,
311            y: (i64::from(self.y) * i64::from(numerator.height) / i64::from(denominator.height)) as i32,
312            width: (u64::from(self.width) * u64::from(numerator.width) / u64::from(denominator.width)) as u32,
313            height: (u64::from(self.height) * u64::from(numerator.height) / u64::from(denominator.height)) as u32,
314        }
315    }
316}
317
318impl core::ops::Mul<f32> for Size {
319    type Output = Size;
320
321    fn mul(self, rhs: f32) -> Self::Output {
322        Size {
323            width: (self.width as f32 * rhs) as u32,
324            height: (self.height as f32 * rhs) as u32,
325        }
326    }
327}
328
329impl core::ops::Div<f32> for Size {
330    type Output = Size;
331
332    fn div(self, rhs: f32) -> Self::Output {
333        Size {
334            width: (self.width as f32 / rhs) as u32,
335            height: (self.height as f32 / rhs) as u32,
336        }
337    }
338}
339
340impl core::ops::MulAssign<f32> for Size {
341    fn mul_assign(&mut self, rhs: f32) {
342        *self = *self * rhs;
343    }
344}
345
346impl core::ops::DivAssign<f32> for Size {
347    fn div_assign(&mut self, rhs: f32) {
348        *self = *self / rhs;
349    }
350}
351
352impl From<libcamera_rectangle_t> for Rectangle {
353    fn from(r: libcamera_rectangle_t) -> Self {
354        Self {
355            x: r.x,
356            y: r.y,
357            width: r.width,
358            height: r.height,
359        }
360    }
361}
362
363impl From<Rectangle> for libcamera_rectangle_t {
364    fn from(r: Rectangle) -> Self {
365        Self {
366            x: r.x,
367            y: r.y,
368            width: r.width,
369            height: r.height,
370        }
371    }
372}
373
374impl Size {
375    /// Return a size with width and height swapped.
376    pub fn transposed(self) -> Self {
377        Size {
378            width: self.height,
379            height: self.width,
380        }
381    }
382}