//! 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 //! * - 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 { width: u32, height: u32, inner: R, /// Relative to the start of the pixel data current_offset: u64, cached_byte: Option, } impl FarbfeldReader { fn new(mut buffered_read: R) -> ImageResult> { fn read_dimm(from: &mut R) -> ImageResult { 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 Read for FarbfeldReader { fn read(&mut self, mut buf: &mut [u8]) -> io::Result { 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 Seek for FarbfeldReader { fn seek(&mut self, pos: SeekFrom) -> io::Result { fn parse_offset(original_offset: u64, end_offset: u64, pos: SeekFrom) -> Option { 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(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(from: &mut R, cached_byte: &mut Option) -> io::Result { let mut obuf = [0u8; 2]; consume_channel(from, &mut obuf)?; *cached_byte = Some(obuf[1]); Ok(obuf[0]) } /// farbfeld decoder pub struct FarbfeldDecoder { reader: FarbfeldReader, } impl FarbfeldDecoder { /// Creates a new decoder that decodes from the stream ```r``` pub fn new(buffered_read: R) -> ImageResult> { Ok(FarbfeldDecoder { reader: FarbfeldReader::new(buffered_read)?, }) } } impl<'a, R: 'a + Read> ImageDecoder<'a> for FarbfeldDecoder { type Reader = FarbfeldReader; fn dimensions(&self) -> (u32, u32) { (self.reader.width, self.reader.height) } fn color_type(&self) -> ColorType { ColorType::Rgba16 } fn into_reader(self) -> ImageResult { Ok(self.reader) } fn scanline_bytes(&self) -> u64 { 2 } } impl<'a, R: 'a + Read + Seek> ImageDecoderRect<'a> for FarbfeldDecoder { fn read_rect_with_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: W, } impl FarbfeldEncoder { /// Create a new encoder that writes its output to ```w```. The writer should be buffered. pub fn new(buffered_writer: W) -> FarbfeldEncoder { 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 ImageEncoder for FarbfeldEncoder { 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 { let mut exp = vec![0u8; exp_wide.len() * 2]; NativeEndian::write_u16_into(exp_wide, &mut exp); exp } }