feim 0.25.1

Modular crate for working with images in Rust
Documentation
mod webp_buf;

use std::io::{self, Read, Write};

use either::Either;
pub use webp::WebPConfig as WebpEncodeOptions;

pub use self::webp_buf::*;
use crate::buffer::RawPixBuf;
use crate::color::convert::ConvertInto;
use crate::color::{Nrgba, Rgb};
use crate::image::{Dimensions, Image, ImageMut};
use crate::impl_format;
use crate::serialize::{Decode, DecodeOptions, Encode, EncodeOptions, EncodeSpecialized};
use crate::specialized;

pub struct Webp;

impl_format! {
    name: Webp,
    id: "feim:webp",
    magic: b"RIFF????WEBPVP8",
}

impl DecodeOptions for Webp {
    type Options = ();
}

impl Decode<Either<RgbWebpBuf, NrgbaWebpBuf>> for Webp {
    type Error = io::Error;

    fn decode<R: Read>(mut r: R, _opt: ()) -> io::Result<Either<RgbWebpBuf, NrgbaWebpBuf>> {
        let mut buf = Vec::new();
        r.read_to_end(&mut buf)?;
        webp::Decoder::new(&buf[..])
            .decode()
            .map(WebpFeimExt::into_feim)
            .ok_or_else(|| io::Error::other("Webp decode failed"))
    }
}

pub fn simple_config(lossless: bool, quality: f32) -> WebpEncodeOptions {
    let mut opts = WebpEncodeOptions::new().unwrap();
    opts.lossless = i32::from(lossless);
    opts.alpha_compression = if lossless { 0 } else { 1 };
    opts.quality = quality.clamp(0.0, 100.0);
    opts
}

#[inline]
pub fn default_config() -> WebpEncodeOptions {
    simple_config(true, 0.75)
}

impl EncodeOptions for Webp {
    type Options = WebpEncodeOptions;
}

macro_rules! impl_encode {
    ($type:ty, $pixel_layout:expr) => {
        impl Encode<$type> for Webp {
            type Error = io::Error;

            fn encode<W: Write>(
                mut w: W,
                opts: WebpEncodeOptions,
                image: &$type,
            ) -> io::Result<()> {
                let buf = image.as_ref();
                let (width, height) = image.dimensions();
                let encoded = webp::Encoder::new(buf, $pixel_layout, width as u32, height as u32)
                    .encode_advanced(&opts)
                    .map_err(|e| {
                        io::Error::new(io::ErrorKind::Other, format!("Webp encoding error: {e:?}"))
                    })?;
                w.write_all(&encoded)
            }
        }
    };
}

impl_encode!(RawPixBuf<Rgb>, webp::PixelLayout::Rgb);
impl_encode!(RgbWebpBuf, webp::PixelLayout::Rgb);
impl_encode!(RawPixBuf<Nrgba>, webp::PixelLayout::Rgba);
impl_encode!(NrgbaWebpBuf, webp::PixelLayout::Rgba);

impl<I: Image + Dimensions> Encode<I, specialized::No> for Webp {
    type Error = io::Error;

    fn encode<W: Write>(w: W, opts: WebpEncodeOptions, buf: &I) -> io::Result<()> {
        let (width, height) = buf.dimensions();
        let mut new_buf = RawPixBuf::new(width, height);
        for y in 0..height {
            for x in 0..width {
                let c = buf.color_get(x, y);
                let c: Nrgba = c.convert_into();
                new_buf.pixel_set(x, y, c);
            }
        }
        Webp::encode_specialized(w, opts, &new_buf)
    }
}