feim 0.25.1

Modular crate for working with images in Rust
Documentation
#[cfg(feature = "fmt-farbfeld")]
pub mod farbfeld;

#[cfg(feature = "fmt-jpeg")]
pub mod jpeg;

#[cfg(feature = "fmt-png")]
pub mod png;

#[cfg(feature = "fmt-webp")]
pub mod webp;

use std::fmt;
use std::str::FromStr;

use crate::color::convert::ConvertInto;
use crate::color::Color;
use crate::specialized::{self, No};

#[macro_export]
macro_rules! impl_format {
    (name: $name:ty, id: $id:expr, magic: $magic:expr $(,)?) => {
        impl $crate::image::Format for $name {
            fn id(&self) -> &'static str {
                $id
            }
            fn magic(&self) -> &'static [u8] {
                $magic
            }
        }
    };
}

pub trait Format {
    /// Should return a format id, such as:
    ///
    /// - feim:ff
    /// - feim:png
    /// - feim:jpeg
    /// - feim:webp
    fn id(&self) -> &'static str;

    /// Returns the magic string situated at the start of the image file.
    fn magic(&self) -> &'static [u8];

    /// Compares the format's magic string against another byte string.
    fn is_valid_magic(&self, magic: &[u8]) -> bool {
        if magic.len() < self.magic().len() {
            return false;
        }
        self.magic()
            .iter()
            .copied()
            .zip(magic.iter().copied())
            .all(|(m, n)| m == n || m == b'?')
    }
}

pub trait Dimensions {
    fn width(&self) -> usize;
    fn height(&self) -> usize;

    fn dimensions(&self) -> (usize, usize) {
        let w = self.width();
        let h = self.height();
        (w, h)
    }
}

pub trait Image<Specialized = No> {
    type Pixel: Color;

    fn color_get(&self, x: usize, y: usize) -> Self::Pixel;
}

pub trait ImageMut<Specialized = No> {
    type Pixel: Color;

    fn color_set<C, ColorSpecialized>(&mut self, x: usize, y: usize, color: C)
    where
        C: ConvertInto<Self::Pixel, ColorSpecialized> + Color;

    fn color_set_generic<C>(&mut self, x: usize, y: usize, color: C)
    where
        C: ConvertInto<Self::Pixel, specialized::No> + Color,
    {
        self.color_set::<_, specialized::No>(x, y, color)
    }

    fn pixel_set(&mut self, x: usize, y: usize, color: Self::Pixel) {
        self.color_set::<_, specialized::For<Self::Pixel>>(x, y, color)
    }
}

#[derive(Debug, Copy, Clone)]
pub enum BuiltInFormat {
    #[cfg(feature = "fmt-farbfeld")]
    Farbfeld,
    #[cfg(feature = "fmt-jpeg")]
    Jpeg,
    #[cfg(feature = "fmt-png")]
    Png,
    #[cfg(feature = "fmt-webp")]
    Webp,
}

impl FromStr for BuiltInFormat {
    type Err = ();

    fn from_str(s: &str) -> Result<Self, ()> {
        match s {
            #[cfg(feature = "fmt-farbfeld")]
            "farbfeld" => Ok(Self::Farbfeld),
            #[cfg(feature = "fmt-jpeg")]
            "jpeg" => Ok(Self::Jpeg),
            #[cfg(feature = "fmt-png")]
            "png" => Ok(Self::Png),
            #[cfg(feature = "fmt-webp")]
            "webp" => Ok(Self::Webp),
            _ => Err(()),
        }
    }
}

impl fmt::Display for BuiltInFormat {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            #[cfg(feature = "fmt-farbfeld")]
            Self::Farbfeld => write!(f, "farbfeld"),
            #[cfg(feature = "fmt-jpeg")]
            Self::Jpeg => write!(f, "jpeg"),
            #[cfg(feature = "fmt-png")]
            Self::Png => write!(f, "png"),
            #[cfg(feature = "fmt-webp")]
            Self::Webp => write!(f, "webp"),
            #[allow(unreachable_patterns)]
            _ => Ok(()),
        }
    }
}

impl BuiltInFormat {
    pub fn get(self) -> &'static dyn Format {
        match self {
            #[cfg(feature = "fmt-farbfeld")]
            BuiltInFormat::Farbfeld => &farbfeld::Farbfeld,

            #[cfg(feature = "fmt-jpeg")]
            BuiltInFormat::Jpeg => &jpeg::Jpeg,

            #[cfg(feature = "fmt-png")]
            BuiltInFormat::Png => &png::Png,

            #[cfg(feature = "fmt-webp")]
            BuiltInFormat::Webp => &webp::Webp,
        }
    }
}

pub fn built_in_formats() -> &'static [(BuiltInFormat, &'static dyn Format)] {
    &[
        #[cfg(feature = "fmt-farbfeld")]
        {
            (BuiltInFormat::Farbfeld, &farbfeld::Farbfeld)
        },
        #[cfg(feature = "fmt-jpeg")]
        {
            (BuiltInFormat::Jpeg, &jpeg::Jpeg)
        },
        #[cfg(feature = "fmt-png")]
        {
            (BuiltInFormat::Png, &png::Png)
        },
        #[cfg(feature = "fmt-webp")]
        {
            (BuiltInFormat::Webp, &webp::Webp)
        },
    ]
}

pub fn built_in_formats_iter() -> impl Iterator<Item = (BuiltInFormat, &'static dyn Format)> {
    built_in_formats().iter().copied()
}