Skip to main content

edgefirst_codec/
traits.rs

1// SPDX-FileCopyrightText: Copyright 2026 Au-Zone Technologies
2// SPDX-License-Identifier: Apache-2.0
3
4//! [`ImageLoad`] extension trait for loading images into pre-allocated tensors.
5
6use crate::decoder::ImageDecoder;
7use crate::error::CodecError;
8use crate::options::{DecodeOptions, ImageInfo};
9use crate::pixel::ImagePixel;
10use edgefirst_tensor::{Tensor, TensorDyn};
11use std::io::{BufReader, Read};
12use std::path::Path;
13
14/// Extension trait for loading images into pre-allocated tensors.
15///
16/// Import this trait to add `load_image`, `load_image_read`, and
17/// `load_image_file` methods to [`Tensor<T>`] and [`TensorDyn`].
18///
19/// # Performance
20///
21/// For best performance, allocate tensors via
22/// [`ImageProcessor::create_image()`] which selects the optimal memory
23/// backend (DMA → PBO → Mem) with GPU-aligned pitch. Free-standing tensors
24/// work but cannot use PBO and may not have GPU-aligned pitch.
25///
26/// # Strided Buffers
27///
28/// The decoder writes directly into the tensor's strided memory layout,
29/// respecting [`effective_row_stride()`]. Allocate the tensor at the maximum
30/// expected image size; smaller images decode into the top-left region.
31///
32/// # Example
33///
34/// ```rust,no_run
35/// use edgefirst_codec::{ImageDecoder, ImageLoad, DecodeOptions};
36/// use edgefirst_tensor::{Tensor, TensorTrait, TensorMemory, PixelFormat};
37///
38/// let mut tensor = Tensor::<u8>::image(1920, 1080, PixelFormat::Rgb, Some(TensorMemory::Mem))
39///     .expect("alloc");
40/// let mut decoder = ImageDecoder::new();
41///
42/// // Decode from bytes
43/// let jpeg = std::fs::read("image.jpg").unwrap();
44/// let info = tensor.load_image(&mut decoder, &jpeg, &DecodeOptions::default()).unwrap();
45/// println!("Decoded {}×{} {:?}", info.width, info.height, info.format);
46///
47/// // Decode from file
48/// let info = tensor.load_image_file(&mut decoder, "image.jpg", &DecodeOptions::default()).unwrap();
49/// ```
50pub trait ImageLoad {
51    /// Decode image bytes (`&[u8]`) into this tensor.
52    ///
53    /// The image format (JPEG or PNG) is detected from magic bytes.
54    ///
55    /// # Errors
56    ///
57    /// Returns [`CodecError::InsufficientCapacity`] if the decoded image
58    /// dimensions exceed the tensor's capacity.
59    fn load_image(
60        &mut self,
61        decoder: &mut ImageDecoder,
62        data: &[u8],
63        opts: &DecodeOptions,
64    ) -> crate::Result<ImageInfo>;
65
66    /// Decode image data from a [`Read`] source into this tensor.
67    ///
68    /// The input is buffered into the decoder's internal scratch before
69    /// decoding. For large inputs, prefer [`load_image`](Self::load_image)
70    /// with a pre-read `&[u8]` to avoid the copy.
71    fn load_image_read<R: Read>(
72        &mut self,
73        decoder: &mut ImageDecoder,
74        reader: R,
75        opts: &DecodeOptions,
76    ) -> crate::Result<ImageInfo>;
77
78    /// Decode an image file into this tensor.
79    ///
80    /// Convenience wrapper that opens the file, buffers it, and decodes.
81    fn load_image_file(
82        &mut self,
83        decoder: &mut ImageDecoder,
84        path: impl AsRef<Path>,
85        opts: &DecodeOptions,
86    ) -> crate::Result<ImageInfo>;
87}
88
89impl<T: ImagePixel> ImageLoad for Tensor<T> {
90    fn load_image(
91        &mut self,
92        decoder: &mut ImageDecoder,
93        data: &[u8],
94        opts: &DecodeOptions,
95    ) -> crate::Result<ImageInfo> {
96        decoder.decode_into(data, self, opts)
97    }
98
99    fn load_image_read<R: Read>(
100        &mut self,
101        decoder: &mut ImageDecoder,
102        reader: R,
103        opts: &DecodeOptions,
104    ) -> crate::Result<ImageInfo> {
105        decoder.decode_from_reader(reader, self, opts)
106    }
107
108    fn load_image_file(
109        &mut self,
110        decoder: &mut ImageDecoder,
111        path: impl AsRef<Path>,
112        opts: &DecodeOptions,
113    ) -> crate::Result<ImageInfo> {
114        let file = std::fs::File::open(path.as_ref()).map_err(CodecError::Io)?;
115        self.load_image_read(decoder, BufReader::new(file), opts)
116    }
117}
118
119impl ImageLoad for TensorDyn {
120    fn load_image(
121        &mut self,
122        decoder: &mut ImageDecoder,
123        data: &[u8],
124        opts: &DecodeOptions,
125    ) -> crate::Result<ImageInfo> {
126        decoder.decode_into_dyn(data, self, opts)
127    }
128
129    fn load_image_read<R: Read>(
130        &mut self,
131        decoder: &mut ImageDecoder,
132        reader: R,
133        opts: &DecodeOptions,
134    ) -> crate::Result<ImageInfo> {
135        decoder.decode_from_reader_dyn(reader, self, opts)
136    }
137
138    fn load_image_file(
139        &mut self,
140        decoder: &mut ImageDecoder,
141        path: impl AsRef<Path>,
142        opts: &DecodeOptions,
143    ) -> crate::Result<ImageInfo> {
144        let file = std::fs::File::open(path.as_ref()).map_err(CodecError::Io)?;
145        self.load_image_read(decoder, BufReader::new(file), opts)
146    }
147}