box_image_pyramid/imageview.rs
1/// A borrowed, non-owning view into a row-major u8 grayscale image.
2///
3/// `ImageView` does not allocate or copy; it is a lightweight wrapper
4/// around an existing byte slice. Use it to pass image data into
5/// `build_pyramid` without transferring ownership.
6///
7/// Invariant: `data.len() == width * height`. The constructor
8/// [`ImageView::new`] enforces this and returns `None` on mismatch.
9#[derive(Copy, Clone, Debug)]
10pub struct ImageView<'a> {
11 /// Pixel data in row-major order. Row `y` starts at byte
12 /// `y * width`.
13 pub data: &'a [u8],
14 /// Image width in pixels.
15 pub width: usize,
16 /// Image height in pixels.
17 pub height: usize,
18}
19
20impl<'a> ImageView<'a> {
21 /// Create a view from a raw u8 slice.
22 ///
23 /// Returns `None` if `data.len() != width * height`.
24 pub fn new(width: usize, height: usize, data: &'a [u8]) -> Option<Self> {
25 if width.checked_mul(height)? != data.len() {
26 return None;
27 }
28 Some(Self {
29 data,
30 width,
31 height,
32 })
33 }
34}
35
36/// An owned, heap-allocated grayscale image buffer (u8, row-major).
37///
38/// `ImageBuffer` is the owning counterpart of [`ImageView`]. It is used
39/// internally by `PyramidBuffers` to hold the downsampled pyramid
40/// levels and can be borrowed as an [`ImageView`] via
41/// [`ImageBuffer::as_view`].
42#[derive(Clone, Debug)]
43pub struct ImageBuffer {
44 /// Image width in pixels.
45 pub width: usize,
46 /// Image height in pixels.
47 pub height: usize,
48 /// Pixel data in row-major order, zero-initialized at construction.
49 /// Row `y` starts at byte `y * width`.
50 pub data: Vec<u8>,
51}
52
53impl ImageBuffer {
54 /// Allocate a zero-filled buffer with the given dimensions.
55 ///
56 /// Uses [`usize::saturating_mul`], so multiplication never wraps.
57 /// Extremely large dimensions may still request an impractically
58 /// large allocation.
59 pub fn new(width: usize, height: usize) -> Self {
60 Self {
61 width,
62 height,
63 data: vec![0; width.saturating_mul(height)],
64 }
65 }
66
67 /// Borrow this buffer as a non-owning [`ImageView`].
68 ///
69 /// Panics if the internal invariant `data.len() == width * height`
70 /// is violated, which cannot happen through the public API.
71 pub fn as_view(&self) -> ImageView<'_> {
72 ImageView::new(self.width, self.height, &self.data).unwrap()
73 }
74}