use crate::error::{Error, Result};
#[derive(Debug, Clone, Default)]
pub enum ColorProfile {
#[default]
Srgb,
Icc(Vec<u8>),
}
impl ColorProfile {
#[must_use]
pub fn is_srgb(&self) -> bool {
matches!(self, Self::Srgb)
}
#[must_use]
pub fn from_icc_bytes(icc: Option<&[u8]>) -> Self {
match icc {
Some(data) if !data.is_empty() => Self::Icc(data.to_vec()),
_ => Self::Srgb,
}
}
}
#[cfg(feature = "icc")]
pub fn transform_to_srgb(rgb: &[u8], profile: &ColorProfile) -> Result<Vec<u8>> {
use moxcms::{ColorProfile as MoxProfile, Layout, TransformOptions};
match profile {
ColorProfile::Srgb => Ok(rgb.to_vec()),
ColorProfile::Icc(icc_data) => {
let input_profile =
MoxProfile::new_from_slice(icc_data).map_err(|e| Error::MetricCalculation {
metric: "ICC".to_string(),
reason: format!("Failed to parse ICC profile: {e}"),
})?;
let srgb = MoxProfile::new_srgb();
let transform = input_profile
.create_transform_8bit(Layout::Rgb, &srgb, Layout::Rgb, TransformOptions::default())
.map_err(|e| Error::MetricCalculation {
metric: "ICC".to_string(),
reason: format!("Failed to create ICC transform: {e}"),
})?;
let mut output = vec![0u8; rgb.len()];
transform
.transform(rgb, &mut output)
.map_err(|e| Error::MetricCalculation {
metric: "ICC".to_string(),
reason: format!("Failed to apply ICC transform: {e}"),
})?;
Ok(output)
}
}
}
#[cfg(not(feature = "icc"))]
pub fn transform_to_srgb(rgb: &[u8], profile: &ColorProfile) -> Result<Vec<u8>> {
match profile {
ColorProfile::Srgb => Ok(rgb.to_vec()),
ColorProfile::Icc(_) => Err(Error::MetricCalculation {
metric: "ICC".to_string(),
reason: "ICC profile support requires the 'icc' feature".to_string(),
}),
}
}
pub fn prepare_for_comparison(
reference: &[u8],
reference_profile: &ColorProfile,
test: &[u8],
test_profile: &ColorProfile,
) -> Result<(Vec<u8>, Vec<u8>)> {
let ref_srgb = transform_to_srgb(reference, reference_profile)?;
let test_srgb = transform_to_srgb(test, test_profile)?;
Ok((ref_srgb, test_srgb))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_srgb_passthrough() {
let rgb = vec![100u8, 150, 200, 50, 100, 150];
let result = transform_to_srgb(&rgb, &ColorProfile::Srgb).unwrap();
assert_eq!(result, rgb);
}
#[test]
fn test_color_profile_default() {
assert!(ColorProfile::default().is_srgb());
}
#[test]
fn test_from_icc_bytes_none() {
let profile = ColorProfile::from_icc_bytes(None);
assert!(profile.is_srgb());
}
#[test]
fn test_from_icc_bytes_empty() {
let profile = ColorProfile::from_icc_bytes(Some(&[]));
assert!(profile.is_srgb());
}
#[test]
fn test_from_icc_bytes_data() {
let profile = ColorProfile::from_icc_bytes(Some(&[1, 2, 3, 4]));
assert!(!profile.is_srgb());
}
}