use std::io::Read;
use jpeg_decoder as jpeg;
use crate::{
buffer::{cast, Buffer},
color,
error::{self, Error},
pixel,
};
pub struct Decoder<R: Read> {
inner: jpeg::Decoder<R>,
metadata: Option<jpeg::ImageInfo>,
}
impl<R: Read> Decoder<R> {
#[inline]
pub fn new(input: R) -> Self {
Decoder {
inner: jpeg::Decoder::new(input),
metadata: None,
}
}
#[inline]
pub fn metadata(&mut self) -> error::Result<jpeg::ImageInfo> {
if self.metadata.is_none() {
self.inner.read_info()?;
self.metadata = Some(self.inner.info().unwrap());
}
Ok(self.metadata.unwrap())
}
}
impl<P, C, R> super::Decoder<P, C> for Decoder<R>
where
P: pixel::Write<C>,
P: From<color::Rgb> + From<color::Luma>,
C: pixel::Channel,
R: Read,
{
fn frame(&mut self) -> error::Result<Buffer<P, C, Vec<C>>> {
#[inline]
fn convert(buffer: &mut Vec<u8>) {
let length = buffer.len();
let mut cmyk = 0;
let mut rgb = 0;
while cmyk < length {
let c = buffer[cmyk] as f32 / 255.0;
let m = buffer[cmyk + 1] as f32 / 255.0;
let y = buffer[cmyk + 2] as f32 / 255.0;
let k = buffer[cmyk + 3] as f32 / 255.0;
let c = c * (1.0 - k) + k;
let m = m * (1.0 - k) + k;
let y = y * (1.0 - k) + k;
let r = (1.0 - c) * 255.0;
let g = (1.0 - m) * 255.0;
let b = (1.0 - y) * 255.0;
buffer[rgb] = r as u8;
buffer[rgb + 1] = g as u8;
buffer[rgb + 2] = b as u8;
cmyk += 4;
rgb += 3;
}
buffer.resize((length / 4) * 3, 0);
buffer.shrink_to_fit();
}
let mut buffer = self.inner.decode()?;
macro_rules! buffer {
($ch:ty, $ty:path) => {{
Ok(cast::Into::<P, C>::into(Buffer::<$ty, $ch, _>::from_raw(
self.metadata()?.width as u32,
self.metadata()?.height as u32,
buffer,
)
.map_err(|_| Error::Format("wrong dimensions".into()))?))
}};
}
match self.metadata()?.pixel_format {
jpeg::PixelFormat::L8 => buffer!(u8, color::Luma),
jpeg::PixelFormat::RGB24 => buffer!(u8, color::Rgb),
jpeg::PixelFormat::CMYK32 => {
convert(&mut buffer);
buffer!(u8, color::Rgb)
}
}
}
}