use crate::error::{Error, Result};
use crate::eval::session::ImageData;
#[cfg(feature = "jpeg-decode")]
pub fn decode_jpeg_with_icc(data: &[u8]) -> Result<ImageData> {
use std::io::Cursor;
let mut decoder = jpeg_decoder::Decoder::new(Cursor::new(data));
let pixels = decoder.decode().map_err(|e| Error::Codec {
codec: "jpeg-decoder".to_string(),
message: e.to_string(),
})?;
let info = decoder.info().ok_or_else(|| Error::Codec {
codec: "jpeg-decoder".to_string(),
message: "Missing JPEG info after decode".to_string(),
})?;
let width = info.width as usize;
let height = info.height as usize;
let rgb = match info.pixel_format {
jpeg_decoder::PixelFormat::RGB24 => pixels,
jpeg_decoder::PixelFormat::L8 => {
pixels.iter().flat_map(|&g| [g, g, g]).collect()
}
jpeg_decoder::PixelFormat::L16 => {
pixels
.chunks_exact(2)
.flat_map(|c| {
let g = c[0]; [g, g, g]
})
.collect()
}
jpeg_decoder::PixelFormat::CMYK32 => {
return Err(Error::Codec {
codec: "jpeg-decoder".to_string(),
message: "CMYK JPEGs are not currently supported".to_string(),
});
}
};
let icc_profile = decoder.icc_profile();
Ok(match icc_profile {
Some(icc) if !icc.is_empty() => ImageData::RgbSliceWithIcc {
data: rgb,
width,
height,
icc_profile: icc.clone(),
},
_ => ImageData::RgbSlice {
data: rgb,
width,
height,
},
})
}
#[cfg(feature = "jpeg-decode")]
pub type JpegDecodeCallback = Box<dyn Fn(&[u8]) -> Result<ImageData> + Send + Sync + 'static>;
#[cfg(feature = "jpeg-decode")]
pub fn jpeg_decode_callback() -> JpegDecodeCallback {
Box::new(decode_jpeg_with_icc)
}
#[cfg(test)]
mod tests {
#[cfg(feature = "jpeg-decode")]
use super::*;
#[test]
#[cfg(feature = "jpeg-decode")]
fn test_decode_jpeg_no_icc() {
let _: fn(&[u8]) -> Result<ImageData> = decode_jpeg_with_icc;
}
}