/src/image/src/codecs/farbfeld.rs
Line | Count | Source |
1 | | //! Decoding of farbfeld images |
2 | | //! |
3 | | //! farbfeld is a lossless image format which is easy to parse, pipe and compress. |
4 | | //! |
5 | | //! It has the following format: |
6 | | //! |
7 | | //! | Bytes | Description | |
8 | | //! |--------|---------------------------------------------------------| |
9 | | //! | 8 | "farbfeld" magic value | |
10 | | //! | 4 | 32-Bit BE unsigned integer (width) | |
11 | | //! | 4 | 32-Bit BE unsigned integer (height) | |
12 | | //! | (2222) | 4⋅16-Bit BE unsigned integers (RGBA) / pixel, row-major | |
13 | | //! |
14 | | //! The RGB-data should be sRGB for best interoperability and not alpha-premultiplied. |
15 | | //! |
16 | | //! # Related Links |
17 | | //! * <https://tools.suckless.org/farbfeld/> - the farbfeld specification |
18 | | |
19 | | use std::io::{self, Read, Seek, SeekFrom, Write}; |
20 | | |
21 | | use crate::color::ExtendedColorType; |
22 | | use crate::error::{ |
23 | | DecodingError, ImageError, ImageResult, UnsupportedError, UnsupportedErrorKind, |
24 | | }; |
25 | | use crate::{ColorType, ImageDecoder, ImageEncoder, ImageFormat}; |
26 | | |
27 | | /// farbfeld Reader |
28 | | pub struct FarbfeldReader<R: Read> { |
29 | | width: u32, |
30 | | height: u32, |
31 | | inner: R, |
32 | | /// Relative to the start of the pixel data |
33 | | current_offset: u64, |
34 | | cached_byte: Option<u8>, |
35 | | } |
36 | | |
37 | | impl<R: Read> FarbfeldReader<R> { |
38 | 2 | fn new(mut buffered_read: R) -> ImageResult<FarbfeldReader<R>> { |
39 | 4 | fn read_dimm<R: Read>(from: &mut R) -> ImageResult<u32> { |
40 | 4 | let mut buf = [0u8; 4]; |
41 | 4 | from.read_exact(&mut buf).map_err(|err| { |
42 | 0 | ImageError::Decoding(DecodingError::new(ImageFormat::Farbfeld.into(), err)) |
43 | 0 | })?; |
44 | 4 | Ok(u32::from_be_bytes(buf)) |
45 | 4 | } |
46 | | |
47 | 2 | let mut magic = [0u8; 8]; |
48 | 2 | buffered_read.read_exact(&mut magic).map_err(|err| { |
49 | 0 | ImageError::Decoding(DecodingError::new(ImageFormat::Farbfeld.into(), err)) |
50 | 0 | })?; |
51 | 2 | if &magic != b"farbfeld" { |
52 | 0 | return Err(ImageError::Decoding(DecodingError::new( |
53 | 0 | ImageFormat::Farbfeld.into(), |
54 | 0 | format!("Invalid magic: {magic:02x?}"), |
55 | 0 | ))); |
56 | 2 | } |
57 | | |
58 | 2 | let reader = FarbfeldReader { |
59 | 2 | width: read_dimm(&mut buffered_read)?, |
60 | 2 | height: read_dimm(&mut buffered_read)?, |
61 | 2 | inner: buffered_read, |
62 | | current_offset: 0, |
63 | 2 | cached_byte: None, |
64 | | }; |
65 | | |
66 | 2 | if crate::utils::check_dimension_overflow( |
67 | 2 | reader.width, |
68 | 2 | reader.height, |
69 | | // ExtendedColorType is always rgba16 |
70 | | 8, |
71 | | ) { |
72 | 0 | return Err(ImageError::Unsupported( |
73 | 0 | UnsupportedError::from_format_and_kind( |
74 | 0 | ImageFormat::Farbfeld.into(), |
75 | 0 | UnsupportedErrorKind::GenericFeature(format!( |
76 | 0 | "Image dimensions ({}x{}) are too large", |
77 | 0 | reader.width, reader.height |
78 | 0 | )), |
79 | 0 | ), |
80 | 0 | )); |
81 | 2 | } |
82 | | |
83 | 2 | Ok(reader) |
84 | 2 | } |
85 | | } |
86 | | |
87 | | impl<R: Read> Read for FarbfeldReader<R> { |
88 | 2 | fn read(&mut self, mut buf: &mut [u8]) -> io::Result<usize> { |
89 | 2 | let mut bytes_written = 0; |
90 | 2 | if let Some(byte) = self.cached_byte.take() { |
91 | 0 | buf[0] = byte; |
92 | 0 | buf = &mut buf[1..]; |
93 | 0 | bytes_written = 1; |
94 | 0 | self.current_offset += 1; |
95 | 2 | } |
96 | | |
97 | 2 | if buf.len() == 1 { |
98 | 0 | buf[0] = cache_byte(&mut self.inner, &mut self.cached_byte)?; |
99 | 0 | bytes_written += 1; |
100 | 0 | self.current_offset += 1; |
101 | | } else { |
102 | 205 | for channel_out in buf.chunks_exact_mut(2) { |
103 | 205 | consume_channel(&mut self.inner, channel_out)?; |
104 | 203 | bytes_written += 2; |
105 | 203 | self.current_offset += 2; |
106 | | } |
107 | | } |
108 | | |
109 | 0 | Ok(bytes_written) |
110 | 2 | } |
111 | | } |
112 | | |
113 | | impl<R: Read + Seek> Seek for FarbfeldReader<R> { |
114 | 0 | fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { |
115 | 0 | fn parse_offset(original_offset: u64, end_offset: u64, pos: SeekFrom) -> Option<i64> { |
116 | 0 | match pos { |
117 | 0 | SeekFrom::Start(off) => i64::try_from(off) |
118 | 0 | .ok()? |
119 | 0 | .checked_sub(i64::try_from(original_offset).ok()?), |
120 | 0 | SeekFrom::End(off) => { |
121 | 0 | if off < i64::try_from(end_offset).unwrap_or(i64::MAX) { |
122 | 0 | None |
123 | | } else { |
124 | 0 | Some(i64::try_from(end_offset.checked_sub(original_offset)?).ok()? + off) |
125 | | } |
126 | | } |
127 | 0 | SeekFrom::Current(off) => { |
128 | 0 | if off < i64::try_from(original_offset).unwrap_or(i64::MAX) { |
129 | 0 | None |
130 | | } else { |
131 | 0 | Some(off) |
132 | | } |
133 | | } |
134 | | } |
135 | 0 | } |
136 | | |
137 | 0 | let original_offset = self.current_offset; |
138 | 0 | let end_offset = u64::from(self.width) * u64::from(self.height) * 2; |
139 | 0 | let offset_from_current = |
140 | 0 | parse_offset(original_offset, end_offset, pos).ok_or_else(|| { |
141 | 0 | io::Error::new( |
142 | 0 | io::ErrorKind::InvalidInput, |
143 | | "invalid seek to a negative or overflowing position", |
144 | | ) |
145 | 0 | })?; |
146 | | |
147 | | // TODO: convert to seek_relative() once that gets stabilised |
148 | 0 | self.inner.seek(SeekFrom::Current(offset_from_current))?; |
149 | 0 | self.current_offset = if offset_from_current < 0 { |
150 | 0 | original_offset.checked_sub(offset_from_current.wrapping_neg() as u64) |
151 | | } else { |
152 | 0 | original_offset.checked_add(offset_from_current as u64) |
153 | | } |
154 | 0 | .expect("This should've been checked above"); |
155 | | |
156 | 0 | if self.current_offset < end_offset && self.current_offset % 2 == 1 { |
157 | 0 | let curr = self.inner.seek(SeekFrom::Current(-1))?; |
158 | 0 | cache_byte(&mut self.inner, &mut self.cached_byte)?; |
159 | 0 | self.inner.seek(SeekFrom::Start(curr))?; |
160 | 0 | } else { |
161 | 0 | self.cached_byte = None; |
162 | 0 | } |
163 | | |
164 | 0 | Ok(original_offset) |
165 | 0 | } |
166 | | } |
167 | | |
168 | 205 | fn consume_channel<R: Read>(from: &mut R, mut to: &mut [u8]) -> io::Result<()> { |
169 | 205 | let mut ibuf = [0u8; 2]; |
170 | 205 | from.read_exact(&mut ibuf)?; |
171 | 203 | to.write_all(&u16::from_be_bytes(ibuf).to_ne_bytes())?; |
172 | | |
173 | 203 | Ok(()) |
174 | 205 | } |
175 | | |
176 | 0 | fn cache_byte<R: Read>(from: &mut R, cached_byte: &mut Option<u8>) -> io::Result<u8> { |
177 | 0 | let mut obuf = [0u8; 2]; |
178 | 0 | consume_channel(from, &mut obuf)?; |
179 | 0 | *cached_byte = Some(obuf[1]); |
180 | 0 | Ok(obuf[0]) |
181 | 0 | } |
182 | | |
183 | | /// farbfeld decoder |
184 | | pub struct FarbfeldDecoder<R: Read> { |
185 | | reader: FarbfeldReader<R>, |
186 | | } |
187 | | |
188 | | impl<R: Read> FarbfeldDecoder<R> { |
189 | | /// Creates a new decoder that decodes from the stream ```r``` |
190 | 2 | pub fn new(buffered_read: R) -> ImageResult<FarbfeldDecoder<R>> { |
191 | | Ok(FarbfeldDecoder { |
192 | 2 | reader: FarbfeldReader::new(buffered_read)?, |
193 | | }) |
194 | 2 | } |
195 | | } |
196 | | |
197 | | impl<R: Read> ImageDecoder for FarbfeldDecoder<R> { |
198 | 10 | fn dimensions(&self) -> (u32, u32) { |
199 | 10 | (self.reader.width, self.reader.height) |
200 | 10 | } |
201 | | |
202 | 8 | fn color_type(&self) -> ColorType { |
203 | 8 | ColorType::Rgba16 |
204 | 8 | } |
205 | | |
206 | 2 | fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> { |
207 | 2 | assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes())); |
208 | 2 | self.reader.read_exact(buf)?; |
209 | 0 | Ok(()) |
210 | 2 | } |
211 | | |
212 | 2 | fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> { |
213 | 2 | (*self).read_image(buf) |
214 | 2 | } |
215 | | } |
216 | | |
217 | | /// farbfeld encoder |
218 | | pub struct FarbfeldEncoder<W: Write> { |
219 | | w: W, |
220 | | } |
221 | | |
222 | | impl<W: Write> FarbfeldEncoder<W> { |
223 | | /// Create a new encoder that writes its output to ```w```. The writer should be buffered. |
224 | 0 | pub fn new(buffered_writer: W) -> FarbfeldEncoder<W> { |
225 | 0 | FarbfeldEncoder { w: buffered_writer } |
226 | 0 | } Unexecuted instantiation: <image::codecs::farbfeld::FarbfeldEncoder<_>>::new Unexecuted instantiation: <image::codecs::farbfeld::FarbfeldEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::new |
227 | | |
228 | | /// Encodes the image `data` (native endian) that has dimensions `width` and `height`. |
229 | | /// |
230 | | /// # Panics |
231 | | /// |
232 | | /// Panics if `width * height * 8 != data.len()`. |
233 | | #[track_caller] |
234 | 0 | pub fn encode(self, data: &[u8], width: u32, height: u32) -> ImageResult<()> { |
235 | 0 | let expected_buffer_len = (u64::from(width) * u64::from(height)).saturating_mul(8); |
236 | 0 | assert_eq!( |
237 | | expected_buffer_len, |
238 | 0 | data.len() as u64, |
239 | 0 | "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image", |
240 | 0 | data.len(), |
241 | | ); |
242 | 0 | self.encode_impl(data, width, height)?; |
243 | 0 | Ok(()) |
244 | 0 | } Unexecuted instantiation: <image::codecs::farbfeld::FarbfeldEncoder<_>>::encode Unexecuted instantiation: <image::codecs::farbfeld::FarbfeldEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode |
245 | | |
246 | 0 | fn encode_impl(mut self, data: &[u8], width: u32, height: u32) -> io::Result<()> { |
247 | 0 | self.w.write_all(b"farbfeld")?; |
248 | | |
249 | 0 | self.w.write_all(&width.to_be_bytes())?; |
250 | 0 | self.w.write_all(&height.to_be_bytes())?; |
251 | | |
252 | 0 | for channel in data.chunks_exact(2) { |
253 | 0 | self.w |
254 | 0 | .write_all(&u16::from_ne_bytes(channel.try_into().unwrap()).to_be_bytes())?; |
255 | | } |
256 | | |
257 | 0 | Ok(()) |
258 | 0 | } Unexecuted instantiation: <image::codecs::farbfeld::FarbfeldEncoder<_>>::encode_impl Unexecuted instantiation: <image::codecs::farbfeld::FarbfeldEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::encode_impl |
259 | | } |
260 | | |
261 | | impl<W: Write> ImageEncoder for FarbfeldEncoder<W> { |
262 | | #[track_caller] |
263 | 0 | fn write_image( |
264 | 0 | self, |
265 | 0 | buf: &[u8], |
266 | 0 | width: u32, |
267 | 0 | height: u32, |
268 | 0 | color_type: ExtendedColorType, |
269 | 0 | ) -> ImageResult<()> { |
270 | 0 | if color_type != ExtendedColorType::Rgba16 { |
271 | 0 | return Err(ImageError::Unsupported( |
272 | 0 | UnsupportedError::from_format_and_kind( |
273 | 0 | ImageFormat::Farbfeld.into(), |
274 | 0 | UnsupportedErrorKind::Color(color_type), |
275 | 0 | ), |
276 | 0 | )); |
277 | 0 | } |
278 | | |
279 | 0 | self.encode(buf, width, height) |
280 | 0 | } Unexecuted instantiation: <image::codecs::farbfeld::FarbfeldEncoder<_> as image::io::encoder::ImageEncoder>::write_image Unexecuted instantiation: <image::codecs::farbfeld::FarbfeldEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>> as image::io::encoder::ImageEncoder>::write_image |
281 | | } |
282 | | |
283 | | #[cfg(test)] |
284 | | mod tests { |
285 | | use crate::codecs::farbfeld::FarbfeldDecoder; |
286 | | use std::io::Cursor; |
287 | | |
288 | | #[test] |
289 | | fn dimension_overflow() { |
290 | | let header = b"farbfeld\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"; |
291 | | |
292 | | assert!(FarbfeldDecoder::new(Cursor::new(header)).is_err()); |
293 | | } |
294 | | } |