image/codecs/
farbfeld.rs

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
19use std::i64;
20use std::io::{self, Read, Seek, SeekFrom, Write};
21
22use crate::color::ColorType;
23use crate::error::{
24    DecodingError, ImageError, ImageResult, UnsupportedError, UnsupportedErrorKind,
25};
26use crate::image::{self, ImageDecoder, ImageDecoderRect, ImageEncoder, ImageFormat, Progress};
27
28/// farbfeld Reader
29pub struct FarbfeldReader<R: Read> {
30    width: u32,
31    height: u32,
32    inner: R,
33    /// Relative to the start of the pixel data
34    current_offset: u64,
35    cached_byte: Option<u8>,
36}
37
38impl<R: Read> FarbfeldReader<R> {
39    fn new(mut buffered_read: R) -> ImageResult<FarbfeldReader<R>> {
40        fn read_dimm<R: Read>(from: &mut R) -> ImageResult<u32> {
41            let mut buf = [0u8; 4];
42            from.read_exact(&mut buf).map_err(|err| {
43                ImageError::Decoding(DecodingError::new(ImageFormat::Farbfeld.into(), err))
44            })?;
45            Ok(u32::from_be_bytes(buf))
46        }
47
48        let mut magic = [0u8; 8];
49        buffered_read.read_exact(&mut magic).map_err(|err| {
50            ImageError::Decoding(DecodingError::new(ImageFormat::Farbfeld.into(), err))
51        })?;
52        if &magic != b"farbfeld" {
53            return Err(ImageError::Decoding(DecodingError::new(
54                ImageFormat::Farbfeld.into(),
55                format!("Invalid magic: {:02x?}", magic),
56            )));
57        }
58
59        let reader = FarbfeldReader {
60            width: read_dimm(&mut buffered_read)?,
61            height: read_dimm(&mut buffered_read)?,
62            inner: buffered_read,
63            current_offset: 0,
64            cached_byte: None,
65        };
66
67        if crate::utils::check_dimension_overflow(
68            reader.width,
69            reader.height,
70            // ColorType is always rgba16
71            ColorType::Rgba16.bytes_per_pixel(),
72        ) {
73            return Err(ImageError::Unsupported(
74                UnsupportedError::from_format_and_kind(
75                    ImageFormat::Farbfeld.into(),
76                    UnsupportedErrorKind::GenericFeature(format!(
77                        "Image dimensions ({}x{}) are too large",
78                        reader.width, reader.height
79                    )),
80                ),
81            ));
82        }
83
84        Ok(reader)
85    }
86}
87
88impl<R: Read> Read for FarbfeldReader<R> {
89    fn read(&mut self, mut buf: &mut [u8]) -> io::Result<usize> {
90        let mut bytes_written = 0;
91        if let Some(byte) = self.cached_byte.take() {
92            buf[0] = byte;
93            buf = &mut buf[1..];
94            bytes_written = 1;
95            self.current_offset += 1;
96        }
97
98        if buf.len() == 1 {
99            buf[0] = cache_byte(&mut self.inner, &mut self.cached_byte)?;
100            bytes_written += 1;
101            self.current_offset += 1;
102        } else {
103            for channel_out in buf.chunks_exact_mut(2) {
104                consume_channel(&mut self.inner, channel_out)?;
105                bytes_written += 2;
106                self.current_offset += 2;
107            }
108        }
109
110        Ok(bytes_written)
111    }
112}
113
114impl<R: Read + Seek> Seek for FarbfeldReader<R> {
115    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
116        fn parse_offset(original_offset: u64, end_offset: u64, pos: SeekFrom) -> Option<i64> {
117            match pos {
118                SeekFrom::Start(off) => i64::try_from(off)
119                    .ok()?
120                    .checked_sub(i64::try_from(original_offset).ok()?),
121                SeekFrom::End(off) => {
122                    if off < i64::try_from(end_offset).unwrap_or(i64::MAX) {
123                        None
124                    } else {
125                        Some(i64::try_from(end_offset.checked_sub(original_offset)?).ok()? + off)
126                    }
127                }
128                SeekFrom::Current(off) => {
129                    if off < i64::try_from(original_offset).unwrap_or(i64::MAX) {
130                        None
131                    } else {
132                        Some(off)
133                    }
134                }
135            }
136        }
137
138        let original_offset = self.current_offset;
139        let end_offset = self.width as u64 * self.height as u64 * 2;
140        let offset_from_current =
141            parse_offset(original_offset, end_offset, pos).ok_or_else(|| {
142                io::Error::new(
143                    io::ErrorKind::InvalidInput,
144                    "invalid seek to a negative or overflowing position",
145                )
146            })?;
147
148        // TODO: convert to seek_relative() once that gets stabilised
149        self.inner.seek(SeekFrom::Current(offset_from_current))?;
150        self.current_offset = if offset_from_current < 0 {
151            original_offset.checked_sub(offset_from_current.wrapping_neg() as u64)
152        } else {
153            original_offset.checked_add(offset_from_current as u64)
154        }
155        .expect("This should've been checked above");
156
157        if self.current_offset < end_offset && self.current_offset % 2 == 1 {
158            let curr = self.inner.seek(SeekFrom::Current(-1))?;
159            cache_byte(&mut self.inner, &mut self.cached_byte)?;
160            self.inner.seek(SeekFrom::Start(curr))?;
161        } else {
162            self.cached_byte = None;
163        }
164
165        Ok(original_offset)
166    }
167}
168
169fn consume_channel<R: Read>(from: &mut R, mut to: &mut [u8]) -> io::Result<()> {
170    let mut ibuf = [0u8; 2];
171    from.read_exact(&mut ibuf)?;
172    to.write_all(&u16::from_be_bytes(ibuf).to_ne_bytes())?;
173
174    Ok(())
175}
176
177fn cache_byte<R: Read>(from: &mut R, cached_byte: &mut Option<u8>) -> io::Result<u8> {
178    let mut obuf = [0u8; 2];
179    consume_channel(from, &mut obuf)?;
180    *cached_byte = Some(obuf[1]);
181    Ok(obuf[0])
182}
183
184/// farbfeld decoder
185pub struct FarbfeldDecoder<R: Read> {
186    reader: FarbfeldReader<R>,
187}
188
189impl<R: Read> FarbfeldDecoder<R> {
190    /// Creates a new decoder that decodes from the stream ```r```
191    pub fn new(buffered_read: R) -> ImageResult<FarbfeldDecoder<R>> {
192        Ok(FarbfeldDecoder {
193            reader: FarbfeldReader::new(buffered_read)?,
194        })
195    }
196}
197
198impl<'a, R: 'a + Read> ImageDecoder<'a> for FarbfeldDecoder<R> {
199    type Reader = FarbfeldReader<R>;
200
201    fn dimensions(&self) -> (u32, u32) {
202        (self.reader.width, self.reader.height)
203    }
204
205    fn color_type(&self) -> ColorType {
206        ColorType::Rgba16
207    }
208
209    fn into_reader(self) -> ImageResult<Self::Reader> {
210        Ok(self.reader)
211    }
212
213    fn scanline_bytes(&self) -> u64 {
214        2
215    }
216}
217
218impl<'a, R: 'a + Read + Seek> ImageDecoderRect<'a> for FarbfeldDecoder<R> {
219    fn read_rect_with_progress<F: Fn(Progress)>(
220        &mut self,
221        x: u32,
222        y: u32,
223        width: u32,
224        height: u32,
225        buf: &mut [u8],
226        progress_callback: F,
227    ) -> ImageResult<()> {
228        // A "scanline" (defined as "shortest non-caching read" in the doc) is just one channel in this case
229
230        let start = self.reader.stream_position()?;
231        image::load_rect(
232            x,
233            y,
234            width,
235            height,
236            buf,
237            progress_callback,
238            self,
239            |s, scanline| s.reader.seek(SeekFrom::Start(scanline * 2)).map(|_| ()),
240            |s, buf| s.reader.read_exact(buf),
241        )?;
242        self.reader.seek(SeekFrom::Start(start))?;
243        Ok(())
244    }
245}
246
247/// farbfeld encoder
248pub struct FarbfeldEncoder<W: Write> {
249    w: W,
250}
251
252impl<W: Write> FarbfeldEncoder<W> {
253    /// Create a new encoder that writes its output to ```w```. The writer should be buffered.
254    pub fn new(buffered_writer: W) -> FarbfeldEncoder<W> {
255        FarbfeldEncoder { w: buffered_writer }
256    }
257
258    /// Encodes the image `data` (native endian) that has dimensions `width` and `height`.
259    ///
260    /// # Panics
261    ///
262    /// Panics if `width * height * 8 != data.len()`.
263    #[track_caller]
264    pub fn encode(self, data: &[u8], width: u32, height: u32) -> ImageResult<()> {
265        let expected_buffer_len = (width as u64 * height as u64).saturating_mul(8);
266        assert_eq!(
267            expected_buffer_len,
268            data.len() as u64,
269            "Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image",
270            data.len(),
271        );
272        self.encode_impl(data, width, height)?;
273        Ok(())
274    }
275
276    fn encode_impl(mut self, data: &[u8], width: u32, height: u32) -> io::Result<()> {
277        self.w.write_all(b"farbfeld")?;
278
279        self.w.write_all(&width.to_be_bytes())?;
280        self.w.write_all(&height.to_be_bytes())?;
281
282        for channel in data.chunks_exact(2) {
283            self.w
284                .write_all(&u16::from_ne_bytes(channel.try_into().unwrap()).to_be_bytes())?;
285        }
286
287        Ok(())
288    }
289}
290
291impl<W: Write> ImageEncoder for FarbfeldEncoder<W> {
292    #[track_caller]
293    fn write_image(
294        self,
295        buf: &[u8],
296        width: u32,
297        height: u32,
298        color_type: ColorType,
299    ) -> ImageResult<()> {
300        if color_type != ColorType::Rgba16 {
301            return Err(ImageError::Unsupported(
302                UnsupportedError::from_format_and_kind(
303                    ImageFormat::Farbfeld.into(),
304                    UnsupportedErrorKind::Color(color_type.into()),
305                ),
306            ));
307        }
308
309        self.encode(buf, width, height)
310    }
311}
312
313#[cfg(test)]
314mod tests {
315    use crate::codecs::farbfeld::FarbfeldDecoder;
316    use crate::ImageDecoderRect;
317    use byteorder::{ByteOrder, NativeEndian};
318    use std::io::{Cursor, Seek, SeekFrom};
319
320    static RECTANGLE_IN: &[u8] =     b"farbfeld\
321                                       \x00\x00\x00\x02\x00\x00\x00\x03\
322                                       \xFF\x01\xFE\x02\xFD\x03\xFC\x04\xFB\x05\xFA\x06\xF9\x07\xF8\x08\
323                                       \xF7\x09\xF6\x0A\xF5\x0B\xF4\x0C\xF3\x0D\xF2\x0E\xF1\x0F\xF0\x10\
324                                       \xEF\x11\xEE\x12\xED\x13\xEC\x14\xEB\x15\xEA\x16\xE9\x17\xE8\x18";
325
326    #[test]
327    fn read_rect_1x2() {
328        static RECTANGLE_OUT: &[u16] = &[
329            0xF30D, 0xF20E, 0xF10F, 0xF010, 0xEB15, 0xEA16, 0xE917, 0xE818,
330        ];
331
332        read_rect(1, 1, 1, 2, RECTANGLE_OUT);
333    }
334
335    #[test]
336    fn read_rect_2x2() {
337        static RECTANGLE_OUT: &[u16] = &[
338            0xFF01, 0xFE02, 0xFD03, 0xFC04, 0xFB05, 0xFA06, 0xF907, 0xF808, 0xF709, 0xF60A, 0xF50B,
339            0xF40C, 0xF30D, 0xF20E, 0xF10F, 0xF010,
340        ];
341
342        read_rect(0, 0, 2, 2, RECTANGLE_OUT);
343    }
344
345    #[test]
346    fn read_rect_2x1() {
347        static RECTANGLE_OUT: &[u16] = &[
348            0xEF11, 0xEE12, 0xED13, 0xEC14, 0xEB15, 0xEA16, 0xE917, 0xE818,
349        ];
350
351        read_rect(0, 2, 2, 1, RECTANGLE_OUT);
352    }
353
354    #[test]
355    fn read_rect_2x3() {
356        static RECTANGLE_OUT: &[u16] = &[
357            0xFF01, 0xFE02, 0xFD03, 0xFC04, 0xFB05, 0xFA06, 0xF907, 0xF808, 0xF709, 0xF60A, 0xF50B,
358            0xF40C, 0xF30D, 0xF20E, 0xF10F, 0xF010, 0xEF11, 0xEE12, 0xED13, 0xEC14, 0xEB15, 0xEA16,
359            0xE917, 0xE818,
360        ];
361
362        read_rect(0, 0, 2, 3, RECTANGLE_OUT);
363    }
364
365    #[test]
366    fn read_rect_in_stream() {
367        static RECTANGLE_OUT: &[u16] = &[0xEF11, 0xEE12, 0xED13, 0xEC14];
368
369        let mut input = vec![];
370        input.extend_from_slice(b"This is a 31-byte-long prologue");
371        input.extend_from_slice(RECTANGLE_IN);
372        let mut input_cur = Cursor::new(input);
373        input_cur.seek(SeekFrom::Start(31)).unwrap();
374
375        let mut out_buf = [0u8; 64];
376        FarbfeldDecoder::new(input_cur)
377            .unwrap()
378            .read_rect(0, 2, 1, 1, &mut out_buf)
379            .unwrap();
380        let exp = degenerate_pixels(RECTANGLE_OUT);
381        assert_eq!(&out_buf[..exp.len()], &exp[..]);
382    }
383
384    #[test]
385    fn dimension_overflow() {
386        let header = b"farbfeld\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF";
387
388        assert!(FarbfeldDecoder::new(Cursor::new(header)).is_err());
389    }
390
391    fn read_rect(x: u32, y: u32, width: u32, height: u32, exp_wide: &[u16]) {
392        let mut out_buf = [0u8; 64];
393        FarbfeldDecoder::new(Cursor::new(RECTANGLE_IN))
394            .unwrap()
395            .read_rect(x, y, width, height, &mut out_buf)
396            .unwrap();
397        let exp = degenerate_pixels(exp_wide);
398        assert_eq!(&out_buf[..exp.len()], &exp[..]);
399    }
400
401    fn degenerate_pixels(exp_wide: &[u16]) -> Vec<u8> {
402        let mut exp = vec![0u8; exp_wide.len() * 2];
403        NativeEndian::write_u16_into(exp_wide, &mut exp);
404        exp
405    }
406}