diff options
author | Valentin Popov <valentin@popov.link> | 2024-01-08 00:21:28 +0300 |
---|---|---|
committer | Valentin Popov <valentin@popov.link> | 2024-01-08 00:21:28 +0300 |
commit | 1b6a04ca5504955c571d1c97504fb45ea0befee4 (patch) | |
tree | 7579f518b23313e8a9748a88ab6173d5e030b227 /vendor/image/src/codecs/farbfeld.rs | |
parent | 5ecd8cf2cba827454317368b68571df0d13d7842 (diff) | |
download | fparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.tar.xz fparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.zip |
Initial vendor packages
Signed-off-by: Valentin Popov <valentin@popov.link>
Diffstat (limited to 'vendor/image/src/codecs/farbfeld.rs')
-rw-r--r-- | vendor/image/src/codecs/farbfeld.rs | 400 |
1 files changed, 400 insertions, 0 deletions
diff --git a/vendor/image/src/codecs/farbfeld.rs b/vendor/image/src/codecs/farbfeld.rs new file mode 100644 index 0000000..b543ade --- /dev/null +++ b/vendor/image/src/codecs/farbfeld.rs @@ -0,0 +1,400 @@ +//! Decoding of farbfeld images +//! +//! farbfeld is a lossless image format which is easy to parse, pipe and compress. +//! +//! It has the following format: +//! +//! | Bytes | Description | +//! |--------|---------------------------------------------------------| +//! | 8 | "farbfeld" magic value | +//! | 4 | 32-Bit BE unsigned integer (width) | +//! | 4 | 32-Bit BE unsigned integer (height) | +//! | [2222] | 4⋅16-Bit BE unsigned integers [RGBA] / pixel, row-major | +//! +//! The RGB-data should be sRGB for best interoperability and not alpha-premultiplied. +//! +//! # Related Links +//! * <https://tools.suckless.org/farbfeld/> - the farbfeld specification + +use std::convert::TryFrom; +use std::i64; +use std::io::{self, Read, Seek, SeekFrom, Write}; + +use byteorder::{BigEndian, ByteOrder, NativeEndian}; + +use crate::color::ColorType; +use crate::error::{ + DecodingError, ImageError, ImageResult, UnsupportedError, UnsupportedErrorKind, +}; +use crate::image::{self, ImageDecoder, ImageDecoderRect, ImageEncoder, ImageFormat, Progress}; + +/// farbfeld Reader +pub struct FarbfeldReader<R: Read> { + width: u32, + height: u32, + inner: R, + /// Relative to the start of the pixel data + current_offset: u64, + cached_byte: Option<u8>, +} + +impl<R: Read> FarbfeldReader<R> { + fn new(mut buffered_read: R) -> ImageResult<FarbfeldReader<R>> { + fn read_dimm<R: Read>(from: &mut R) -> ImageResult<u32> { + let mut buf = [0u8; 4]; + from.read_exact(&mut buf).map_err(|err| { + ImageError::Decoding(DecodingError::new(ImageFormat::Farbfeld.into(), err)) + })?; + Ok(BigEndian::read_u32(&buf)) + } + + let mut magic = [0u8; 8]; + buffered_read.read_exact(&mut magic).map_err(|err| { + ImageError::Decoding(DecodingError::new(ImageFormat::Farbfeld.into(), err)) + })?; + if &magic != b"farbfeld" { + return Err(ImageError::Decoding(DecodingError::new( + ImageFormat::Farbfeld.into(), + format!("Invalid magic: {:02x?}", magic), + ))); + } + + let reader = FarbfeldReader { + width: read_dimm(&mut buffered_read)?, + height: read_dimm(&mut buffered_read)?, + inner: buffered_read, + current_offset: 0, + cached_byte: None, + }; + + if crate::utils::check_dimension_overflow( + reader.width, + reader.height, + // ColorType is always rgba16 + ColorType::Rgba16.bytes_per_pixel(), + ) { + return Err(ImageError::Unsupported( + UnsupportedError::from_format_and_kind( + ImageFormat::Farbfeld.into(), + UnsupportedErrorKind::GenericFeature(format!( + "Image dimensions ({}x{}) are too large", + reader.width, reader.height + )), + ), + )); + } + + Ok(reader) + } +} + +impl<R: Read> Read for FarbfeldReader<R> { + fn read(&mut self, mut buf: &mut [u8]) -> io::Result<usize> { + let mut bytes_written = 0; + if let Some(byte) = self.cached_byte.take() { + buf[0] = byte; + buf = &mut buf[1..]; + bytes_written = 1; + self.current_offset += 1; + } + + if buf.len() == 1 { + buf[0] = cache_byte(&mut self.inner, &mut self.cached_byte)?; + bytes_written += 1; + self.current_offset += 1; + } else { + for channel_out in buf.chunks_exact_mut(2) { + consume_channel(&mut self.inner, channel_out)?; + bytes_written += 2; + self.current_offset += 2; + } + } + + Ok(bytes_written) + } +} + +impl<R: Read + Seek> Seek for FarbfeldReader<R> { + fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { + fn parse_offset(original_offset: u64, end_offset: u64, pos: SeekFrom) -> Option<i64> { + match pos { + SeekFrom::Start(off) => i64::try_from(off) + .ok()? + .checked_sub(i64::try_from(original_offset).ok()?), + SeekFrom::End(off) => { + if off < i64::try_from(end_offset).unwrap_or(i64::MAX) { + None + } else { + Some(i64::try_from(end_offset.checked_sub(original_offset)?).ok()? + off) + } + } + SeekFrom::Current(off) => { + if off < i64::try_from(original_offset).unwrap_or(i64::MAX) { + None + } else { + Some(off) + } + } + } + } + + let original_offset = self.current_offset; + let end_offset = self.width as u64 * self.height as u64 * 2; + let offset_from_current = + parse_offset(original_offset, end_offset, pos).ok_or_else(|| { + io::Error::new( + io::ErrorKind::InvalidInput, + "invalid seek to a negative or overflowing position", + ) + })?; + + // TODO: convert to seek_relative() once that gets stabilised + self.inner.seek(SeekFrom::Current(offset_from_current))?; + self.current_offset = if offset_from_current < 0 { + original_offset.checked_sub(offset_from_current.wrapping_neg() as u64) + } else { + original_offset.checked_add(offset_from_current as u64) + } + .expect("This should've been checked above"); + + if self.current_offset < end_offset && self.current_offset % 2 == 1 { + let curr = self.inner.seek(SeekFrom::Current(-1))?; + cache_byte(&mut self.inner, &mut self.cached_byte)?; + self.inner.seek(SeekFrom::Start(curr))?; + } else { + self.cached_byte = None; + } + + Ok(original_offset) + } +} + +fn consume_channel<R: Read>(from: &mut R, to: &mut [u8]) -> io::Result<()> { + let mut ibuf = [0u8; 2]; + from.read_exact(&mut ibuf)?; + NativeEndian::write_u16(to, BigEndian::read_u16(&ibuf)); + Ok(()) +} + +fn cache_byte<R: Read>(from: &mut R, cached_byte: &mut Option<u8>) -> io::Result<u8> { + let mut obuf = [0u8; 2]; + consume_channel(from, &mut obuf)?; + *cached_byte = Some(obuf[1]); + Ok(obuf[0]) +} + +/// farbfeld decoder +pub struct FarbfeldDecoder<R: Read> { + reader: FarbfeldReader<R>, +} + +impl<R: Read> FarbfeldDecoder<R> { + /// Creates a new decoder that decodes from the stream ```r``` + pub fn new(buffered_read: R) -> ImageResult<FarbfeldDecoder<R>> { + Ok(FarbfeldDecoder { + reader: FarbfeldReader::new(buffered_read)?, + }) + } +} + +impl<'a, R: 'a + Read> ImageDecoder<'a> for FarbfeldDecoder<R> { + type Reader = FarbfeldReader<R>; + + fn dimensions(&self) -> (u32, u32) { + (self.reader.width, self.reader.height) + } + + fn color_type(&self) -> ColorType { + ColorType::Rgba16 + } + + fn into_reader(self) -> ImageResult<Self::Reader> { + Ok(self.reader) + } + + fn scanline_bytes(&self) -> u64 { + 2 + } +} + +impl<'a, R: 'a + Read + Seek> ImageDecoderRect<'a> for FarbfeldDecoder<R> { + fn read_rect_with_progress<F: Fn(Progress)>( + &mut self, + x: u32, + y: u32, + width: u32, + height: u32, + buf: &mut [u8], + progress_callback: F, + ) -> ImageResult<()> { + // A "scanline" (defined as "shortest non-caching read" in the doc) is just one channel in this case + + let start = self.reader.stream_position()?; + image::load_rect( + x, + y, + width, + height, + buf, + progress_callback, + self, + |s, scanline| s.reader.seek(SeekFrom::Start(scanline * 2)).map(|_| ()), + |s, buf| s.reader.read_exact(buf), + )?; + self.reader.seek(SeekFrom::Start(start))?; + Ok(()) + } +} + +/// farbfeld encoder +pub struct FarbfeldEncoder<W: Write> { + w: W, +} + +impl<W: Write> FarbfeldEncoder<W> { + /// Create a new encoder that writes its output to ```w```. The writer should be buffered. + pub fn new(buffered_writer: W) -> FarbfeldEncoder<W> { + FarbfeldEncoder { w: buffered_writer } + } + + /// Encodes the image ```data``` (native endian) + /// that has dimensions ```width``` and ```height``` + pub fn encode(self, data: &[u8], width: u32, height: u32) -> ImageResult<()> { + self.encode_impl(data, width, height)?; + Ok(()) + } + + fn encode_impl(mut self, data: &[u8], width: u32, height: u32) -> io::Result<()> { + self.w.write_all(b"farbfeld")?; + + let mut buf = [0u8; 4]; + BigEndian::write_u32(&mut buf, width); + self.w.write_all(&buf)?; + + BigEndian::write_u32(&mut buf, height); + self.w.write_all(&buf)?; + + for channel in data.chunks_exact(2) { + BigEndian::write_u16(&mut buf, NativeEndian::read_u16(channel)); + self.w.write_all(&buf[..2])?; + } + + Ok(()) + } +} + +impl<W: Write> ImageEncoder for FarbfeldEncoder<W> { + fn write_image( + self, + buf: &[u8], + width: u32, + height: u32, + color_type: ColorType, + ) -> ImageResult<()> { + if color_type != ColorType::Rgba16 { + return Err(ImageError::Unsupported( + UnsupportedError::from_format_and_kind( + ImageFormat::Farbfeld.into(), + UnsupportedErrorKind::Color(color_type.into()), + ), + )); + } + + self.encode(buf, width, height) + } +} + +#[cfg(test)] +mod tests { + use crate::codecs::farbfeld::FarbfeldDecoder; + use crate::ImageDecoderRect; + use byteorder::{ByteOrder, NativeEndian}; + use std::io::{Cursor, Seek, SeekFrom}; + + static RECTANGLE_IN: &[u8] = b"farbfeld\ + \x00\x00\x00\x02\x00\x00\x00\x03\ + \xFF\x01\xFE\x02\xFD\x03\xFC\x04\xFB\x05\xFA\x06\xF9\x07\xF8\x08\ + \xF7\x09\xF6\x0A\xF5\x0B\xF4\x0C\xF3\x0D\xF2\x0E\xF1\x0F\xF0\x10\ + \xEF\x11\xEE\x12\xED\x13\xEC\x14\xEB\x15\xEA\x16\xE9\x17\xE8\x18"; + + #[test] + fn read_rect_1x2() { + static RECTANGLE_OUT: &[u16] = &[ + 0xF30D, 0xF20E, 0xF10F, 0xF010, 0xEB15, 0xEA16, 0xE917, 0xE818, + ]; + + read_rect(1, 1, 1, 2, RECTANGLE_OUT); + } + + #[test] + fn read_rect_2x2() { + static RECTANGLE_OUT: &[u16] = &[ + 0xFF01, 0xFE02, 0xFD03, 0xFC04, 0xFB05, 0xFA06, 0xF907, 0xF808, 0xF709, 0xF60A, 0xF50B, + 0xF40C, 0xF30D, 0xF20E, 0xF10F, 0xF010, + ]; + + read_rect(0, 0, 2, 2, RECTANGLE_OUT); + } + + #[test] + fn read_rect_2x1() { + static RECTANGLE_OUT: &[u16] = &[ + 0xEF11, 0xEE12, 0xED13, 0xEC14, 0xEB15, 0xEA16, 0xE917, 0xE818, + ]; + + read_rect(0, 2, 2, 1, RECTANGLE_OUT); + } + + #[test] + fn read_rect_2x3() { + static RECTANGLE_OUT: &[u16] = &[ + 0xFF01, 0xFE02, 0xFD03, 0xFC04, 0xFB05, 0xFA06, 0xF907, 0xF808, 0xF709, 0xF60A, 0xF50B, + 0xF40C, 0xF30D, 0xF20E, 0xF10F, 0xF010, 0xEF11, 0xEE12, 0xED13, 0xEC14, 0xEB15, 0xEA16, + 0xE917, 0xE818, + ]; + + read_rect(0, 0, 2, 3, RECTANGLE_OUT); + } + + #[test] + fn read_rect_in_stream() { + static RECTANGLE_OUT: &[u16] = &[0xEF11, 0xEE12, 0xED13, 0xEC14]; + + let mut input = vec![]; + input.extend_from_slice(b"This is a 31-byte-long prologue"); + input.extend_from_slice(RECTANGLE_IN); + let mut input_cur = Cursor::new(input); + input_cur.seek(SeekFrom::Start(31)).unwrap(); + + let mut out_buf = [0u8; 64]; + FarbfeldDecoder::new(input_cur) + .unwrap() + .read_rect(0, 2, 1, 1, &mut out_buf) + .unwrap(); + let exp = degenerate_pixels(RECTANGLE_OUT); + assert_eq!(&out_buf[..exp.len()], &exp[..]); + } + + #[test] + fn dimension_overflow() { + let header = b"farbfeld\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"; + + assert!(FarbfeldDecoder::new(Cursor::new(header)).is_err()); + } + + fn read_rect(x: u32, y: u32, width: u32, height: u32, exp_wide: &[u16]) { + let mut out_buf = [0u8; 64]; + FarbfeldDecoder::new(Cursor::new(RECTANGLE_IN)) + .unwrap() + .read_rect(x, y, width, height, &mut out_buf) + .unwrap(); + let exp = degenerate_pixels(exp_wide); + assert_eq!(&out_buf[..exp.len()], &exp[..]); + } + + fn degenerate_pixels(exp_wide: &[u16]) -> Vec<u8> { + let mut exp = vec![0u8; exp_wide.len() * 2]; + NativeEndian::write_u16_into(exp_wide, &mut exp); + exp + } +} |