use otf_pixels_core::{
DecodeCapability, Decoder, ImageDescriptor, Limits, Orientation, PixelFormat, PixelsError,
Result, Source,
};
const MAX_COMPRESSED: usize = 256 * 1024 * 1024;
#[derive(Debug)]
pub struct Progressive {
descriptor: ImageDescriptor,
pixels: Vec<u8>,
row: u32,
orientation: Option<Orientation>,
}
impl Progressive {
pub fn new<S: Source>(
replay: Vec<u8>,
mut source: S,
limits: Limits,
orientation: Option<Orientation>,
) -> Result<Self> {
let mut bytes = replay;
let mut chunk = [0_u8; 64 * 1024];
loop {
if bytes.len() > MAX_COMPRESSED {
return Err(PixelsError::malformed(
"jpeg",
format!("progressive stream exceeds {MAX_COMPRESSED} bytes"),
));
}
match source.read(&mut chunk)? {
0 => break,
read => {
let Some(filled) = chunk.get(..read) else {
break;
};
bytes.extend_from_slice(filled);
}
}
}
let mut decoder = jpeg_decoder::Decoder::new(std::io::Cursor::new(bytes));
decoder.read_info().map_err(wrapped_error)?;
let info = decoder
.info()
.ok_or_else(|| PixelsError::malformed("jpeg", "no frame header in the stream"))?;
let pixel = match info.pixel_format {
jpeg_decoder::PixelFormat::L8 => PixelFormat::Gray8,
jpeg_decoder::PixelFormat::L16 => PixelFormat::Gray16,
jpeg_decoder::PixelFormat::RGB24 => PixelFormat::Rgb8,
jpeg_decoder::PixelFormat::CMYK32 => {
return Err(PixelsError::unsupported(
"jpeg: CMYK/YCCK images are not supported",
));
}
};
let descriptor = ImageDescriptor::with_limits(
u32::from(info.width),
u32::from(info.height),
pixel,
&limits,
)?;
let pixels = decoder.decode().map_err(wrapped_error)?;
let expected = descriptor
.byte_len()
.ok_or_else(|| PixelsError::malformed("jpeg", "image size overflows"))?;
if pixels.len() != expected {
return Err(PixelsError::malformed(
"jpeg",
format!(
"decoded {} bytes for a {}x{} image expecting {expected}",
pixels.len(),
descriptor.width,
descriptor.height
),
));
}
Ok(Self {
descriptor,
pixels,
row: 0,
orientation,
})
}
#[must_use]
pub const fn orientation(&self) -> Option<Orientation> {
self.orientation
}
}
fn wrapped_error(error: jpeg_decoder::Error) -> PixelsError {
match error {
jpeg_decoder::Error::Format(detail) => PixelsError::malformed("jpeg", detail),
jpeg_decoder::Error::Unsupported(feature) => {
PixelsError::unsupported(format!("jpeg: {feature:?}"))
}
jpeg_decoder::Error::Io(error) => PixelsError::io("decoding a progressive JPEG", error),
other => PixelsError::malformed("jpeg", other.to_string()),
}
}
impl Decoder for Progressive {
fn descriptor(&self) -> ImageDescriptor {
self.descriptor
}
fn capability(&self) -> DecodeCapability {
DecodeCapability::Sequential
}
fn read_row(&mut self, out: &mut [u8]) -> Result<()> {
if self.row >= self.descriptor.height {
return Err(PixelsError::invalid_argument(
"out",
format!("all {} rows have already been read", self.descriptor.height),
));
}
let row_bytes = self.descriptor.row_bytes();
if out.len() != row_bytes {
return Err(PixelsError::invalid_argument(
"out",
format!("row buffer is {} bytes, expected {row_bytes}", out.len()),
));
}
let start = self.row as usize * row_bytes;
let row = self
.pixels
.get(start..)
.and_then(|rest| rest.get(..row_bytes))
.ok_or_else(|| PixelsError::malformed("jpeg", "decoded image is short"))?;
out.copy_from_slice(row);
self.row += 1;
Ok(())
}
}