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}