cursive-image 0.0.6

Image view for the Cursive TUI library
Documentation
use super::{
    super::{format::*, image::*, stream::*},
    zlib::*,
};

use {
    cursive::*,
    std::{fs::*, io, path::*},
    zune_jpeg::{
        zune_core::{colorspace::*, options::*},
        *,
    },
};

impl Image {
    /// Constructor.
    ///
    /// The implementation supports both [RGB](ImageFormat::RGB) and [RGBA](ImageFormat::RGBA).
    pub fn new_owned_from_jpeg_file<PathT>(path: PathT, format: ImageFormat, compress: bool) -> io::Result<Self>
    where
        PathT: AsRef<Path>,
    {
        let (data, size) = read_jpeg(path, format, true)?;
        let data = if compress { zlib(&data)? } else { data };
        Ok(Self::new_owned(data, compress, format, size))
    }

    /// Constructor.
    ///
    /// The implementation supports both [RGB](ImageFormat::RGB) and [RGBA](ImageFormat::RGBA).
    pub fn new_stream_from_jpeg_file<PathT>(path: PathT, format: ImageFormat) -> io::Result<Self>
    where
        PathT: AsRef<Path>,
    {
        let path = path.as_ref();
        let mut decoder = open_jpeg(path, format)?;
        decoder.decode_headers().map_err(io::Error::other)?; // required in order to get info
        let info = decoder.info().ok_or_else(|| io::Error::other("missing JPEG info"))?;
        let stream = JpegImageStream::new(path.into(), format);
        Ok(Self::new_stream(stream, format, (info.width, info.height)))
    }
}

//
// JpegImageStream
//

struct JpegImageStream {
    path: PathBuf,
    format: ImageFormat,
}

impl JpegImageStream {
    fn new(path: PathBuf, format: ImageFormat) -> Self {
        Self { path, format }
    }
}

impl ImageStream for JpegImageStream {
    fn open(&self) -> io::Result<(Box<dyn io::Read>, bool)> {
        // We must load the entire file into memory to decode it
        let (data, _size) = read_jpeg(&self.path, self.format, false)?;
        Ok((Box::new(io::Cursor::new(data)), false))
    }
}

// Utils

fn read_jpeg<PathT>(path: PathT, format: ImageFormat, with_size: bool) -> io::Result<(Vec<u8>, Vec2)>
where
    PathT: AsRef<Path>,
{
    let mut decoder = open_jpeg(path, format)?;
    let data = decoder.decode().map_err(io::Error::other)?;
    let size = if with_size {
        let info = decoder.info().ok_or_else(|| io::Error::other("missing JPEG info"))?; // call *after* decode()
        (info.width, info.height).into()
    } else {
        Default::default()
    };
    Ok((data, size))
}

fn open_jpeg<PathT>(path: PathT, format: ImageFormat) -> io::Result<JpegDecoder<io::BufReader<File>>>
where
    PathT: AsRef<Path>,
{
    let options = DecoderOptions::new_fast().jpeg_set_out_colorspace(match format {
        ImageFormat::RGB => ColorSpace::RGB,
        ImageFormat::RGBA => ColorSpace::RGBA,
        _ => return Err(io::Error::other("unsupported format")),
    });

    Ok(JpegDecoder::new_with_options(io::BufReader::new(File::open(path)?), options))
}