//! 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
}
}