load_image 3.5.0-beta.1

Load PNG or JPEG with color profile support
Documentation
//! [Little CMS](https://lib.rs/crates/lcms2) backend.

use crate::pixel_format::{ColorSpace, PixelFormat};
use bytemuck::Pod;
use lcms2::{InfoType, Intent, Locale, Transform as LcmsTransform};

pub use lcms2::Error as CmsError;

pub(crate) struct Profile(lcms2::Profile);

impl Profile {
    pub(crate) fn new_icc(data: &[u8]) -> Result<Profile, CmsError> {
        lcms2::Profile::new_icc(data).map(Profile)
    }

    pub(crate) fn new_srgb() -> Profile {
        Profile(lcms2::Profile::new_srgb())
    }

    pub(crate) fn color_space(&self) -> ColorSpace {
        match self.0.color_space() {
            lcms2::ColorSpaceSignature::RgbData => ColorSpace::Rgb,
            lcms2::ColorSpaceSignature::GrayData => ColorSpace::Gray,
            lcms2::ColorSpaceSignature::CmykData => ColorSpace::Cmyk,
            _ => ColorSpace::Other,
        }
    }

    pub(crate) fn description(&self) -> Option<String> {
        self.0.info(InfoType::Description, Locale::new("en_US"))
    }
}

pub(crate) struct Transform<In: Pod, Out: Pod> {
    inner: LcmsTransform<In, Out>,
}

fn to_lcms_format(format: PixelFormat) -> lcms2::PixelFormat {
    use lcms2::PixelFormat as P;
    match format {
        PixelFormat::RGB8 => P::RGB_8,
        PixelFormat::RGBA8 => P::RGBA_8,
        PixelFormat::GRAY8 => P::GRAY_8,
        PixelFormat::GRAYA8 => P::GRAYA_8,
        PixelFormat::RGB16 => P::RGB_16,
        PixelFormat::RGBA16 => P::RGBA_16,
        PixelFormat::GRAY16 => P::GRAY_16,
        PixelFormat::GRAYA16 => P::GRAYA_16,
        // Loaders normalize CMYK to ICC convention (ink coverage, 0 = no ink),
        #[cfg(any(feature = "jpeg", feature = "mozjpeg"))]
        PixelFormat::CMYK8 => P::CMYK_8,
    }
}

impl<In: Pod, Out: Pod> Transform<In, Out> {
    pub(crate) fn new(src: &Profile, src_format: PixelFormat, dst: &Profile, dst_format: PixelFormat) -> Result<Self, CmsError> {
        let inner = LcmsTransform::new(
            &src.0,
            to_lcms_format(src_format),
            &dst.0,
            to_lcms_format(dst_format),
            Intent::RelativeColorimetric,
        )?;
        Ok(Self { inner })
    }

    pub(crate) fn transform_pixels(&self, src: &[In], dst: &mut [Out]) -> Result<(), CmsError> {
        self.inner.transform_pixels(src, dst);
        Ok(())
    }
}