use anyhow::Result;
use turbojpeg;
use crate::image::{Image, ImageSize};
pub struct ImageDecoder {
pub decompressor: turbojpeg::Decompressor,
}
pub struct ImageEncoder {
pub compressor: turbojpeg::Compressor,
}
impl Default for ImageDecoder {
fn default() -> Self {
Self::new().unwrap()
}
}
impl Default for ImageEncoder {
fn default() -> Self {
Self::new().unwrap()
}
}
impl ImageEncoder {
pub fn new() -> Result<Self> {
let compressor = turbojpeg::Compressor::new()?;
Ok(Self { compressor })
}
pub fn encode(&mut self, image: &Image<u8, 3>) -> Result<Vec<u8>> {
let image_data = match image.data.as_slice() {
Some(d) => d,
None => Err(anyhow::anyhow!("Image data is not contiguous"))?,
};
let buf = turbojpeg::Image {
pixels: image_data,
width: image.width(),
pitch: 3 * image.width(),
height: image.height(),
format: turbojpeg::PixelFormat::RGB,
};
Ok(self.compressor.compress_to_vec(buf)?)
}
pub fn set_quality(&mut self, quality: i32) -> Result<()> {
Ok(self.compressor.set_quality(quality)?)
}
}
impl ImageDecoder {
pub fn new() -> Result<Self> {
let decompressor = turbojpeg::Decompressor::new()?;
Ok(ImageDecoder { decompressor })
}
pub fn read_header(&mut self, jpeg_data: &[u8]) -> Result<ImageSize> {
let header = self.decompressor.read_header(jpeg_data)?;
Ok(ImageSize {
width: header.width,
height: header.height,
})
}
pub fn decode(&mut self, jpeg_data: &[u8]) -> Result<Image<u8, 3>> {
let image_size: ImageSize = self.read_header(jpeg_data)?;
let mut pixels = vec![0u8; image_size.height * image_size.width * 3];
let buf = turbojpeg::Image {
pixels: pixels.as_mut_slice(),
width: image_size.width,
pitch: 3 * image_size.width, height: image_size.height,
format: turbojpeg::PixelFormat::RGB,
};
self.decompressor.decompress(jpeg_data, buf)?;
Image::new(image_size, pixels)
}
}
#[cfg(test)]
mod tests {
use crate::io::jpeg::{ImageDecoder, ImageEncoder};
use anyhow::Result;
#[test]
fn image_decoder() -> Result<()> {
let jpeg_data = std::fs::read("tests/data/dog.jpeg").unwrap();
let image_size = ImageDecoder::new()?.read_header(&jpeg_data)?;
assert_eq!(image_size.width, 258);
assert_eq!(image_size.height, 195);
let image = ImageDecoder::new()?.decode(&jpeg_data)?;
assert_eq!(image.size().width, 258);
assert_eq!(image.size().height, 195);
assert_eq!(image.num_channels(), 3);
Ok(())
}
#[test]
fn image_encoder() -> Result<()> {
let jpeg_data_fs = std::fs::read("tests/data/dog.jpeg")?;
let image = ImageDecoder::new()?.decode(&jpeg_data_fs)?;
let jpeg_data = ImageEncoder::new()?.encode(&image)?;
let image_back = ImageDecoder::new()?.decode(&jpeg_data)?;
assert_eq!(image_back.size().width, 258);
assert_eq!(image_back.size().height, 195);
assert_eq!(image_back.num_channels(), 3);
Ok(())
}
}