use crate::decode::SourceColor;
use zenpixels::{Cicp, PixelDescriptor, PixelFormat};
pub fn descriptor_for_decoded_pixels(
format: PixelFormat,
source_color: &SourceColor,
corrected_to: Option<&Cicp>,
tolerance: IccMatchTolerance,
) -> PixelDescriptor {
if let Some(target) = corrected_to {
return target.to_descriptor(format);
}
if let Some(cicp) = source_color.cicp {
return cicp.to_descriptor(format);
}
if let Some(ref icc) = source_color.icc_profile {
if let Some(cicp) = identify_well_known_icc(icc, tolerance) {
return cicp.to_descriptor(format);
}
return Cicp::SRGB
.to_descriptor(format)
.with_transfer(zenpixels::TransferFunction::Unknown)
.with_primaries(zenpixels::ColorPrimaries::Unknown);
}
Cicp::SRGB.to_descriptor(format)
}
const fn fnv1a_64(data: &[u8]) -> u64 {
const OFFSET: u64 = 0xcbf29ce484222325;
const PRIME: u64 = 0x100000001b3;
let mut hash = OFFSET;
let mut i = 0;
while i < data.len() {
hash ^= data[i] as u64;
hash = hash.wrapping_mul(PRIME);
i += 1;
}
hash
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[must_use]
pub enum IccMatchTolerance {
Exact = 1,
Precise = 3,
Approximate = 13,
Intent = 56,
}
const KNOWN_ICC_PROFILES: &[(u64, u8, u8, u8)] = {
const S: (u8, u8) = (1, 13); const P: (u8, u8) = (12, 13); const R: (u8, u8) = (9, 1); const B: (u8, u8) = (1, 1); &[
(0x01b2_7967_14a9_5fd5, S.0, S.1, 1), (0x038b_a989_75d3_6160, S.0, S.1, 1), (0x131b_e18b_256c_1005, S.0, S.1, 1), (0x190f_0cbe_0744_3404, S.0, S.1, 1), (0x1b01_56ec_7dcf_0fa3, S.0, S.1, 1), (0x1b89_293e_8c83_89ad, S.0, S.1, 1), (0x1dab_4fbb_a3fd_913f, P.0, P.1, 1), (0x203c_34c1_fba5_38d2, S.0, S.1, 1), (0x2735_dda6_6786_337b, S.0, S.1, 1), (0x2862_fba6_3274_7f0d, P.0, P.1, 5), (0x2cac_00e9_d69a_9840, P.0, P.1, 2), (0x3132_2772_0f77_8b89, P.0, P.1, 2), (0x358f_d60d_2c26_341b, B.0, B.1, 3), (0x3e45_d1a7_e6ab_852f, S.0, S.1, 1), (0x3f59_a3a4_9d8d_6f25, P.0, P.1, 13), (0x43f7_b099_aa77_a523, S.0, S.1, 1), (0x45b5_2ef1_ca8c_6fcb, R.0, R.1, 1), (0x4b41_6441_92da_c35c, S.0, S.1, 1), (0x569a_1a2b_b183_597a, S.0, S.1, 1), (0x56d2_cbfc_a6b5_4318, S.0, S.1, 1), (0x70d6_01da_f84f_28ff, S.0, S.1, 1), (0x717b_5b97_bad9_374d, B.0, B.1, 1), (0x7271_2df1_0196_b1db, S.0, S.1, 13), (0x77e2_3b94_c4e2_39d8, S.0, S.1, 1), (0x78cb_2b5d_cdf4_e965, S.0, S.1, 2), (0x7aa2_2d54_73ad_99bd, P.0, P.1, 1), (0x7f3b_a380_1001_a58b, S.0, S.1, 1), (0x7fdb_28fb_34fc_eedb, R.0, R.1, 1), (0x809e_740f_f28f_1ad8, R.0, R.1, 1), (0x869a_3fee_fd88_a489, S.0, S.1, 1), (0x8d0c_ab95_b0b4_0498, B.0, B.1, 3), (0x9b9c_0685_797a_bfdb, S.0, S.1, 1), (0x9ea9_cacd_e728_5742, P.0, P.1, 1), (0xa52c_7f17_7bff_1392, P.0, P.1, 1), (0xb263_a19b_44f5_faba, R.0, R.1, 8), (0xb5fc_4c1a_2d96_fbeb, S.0, S.1, 1), (0xb5fe_02fb_0e03_d19b, S.0, S.1, 33), (0xbd19_8ece_9409_9edc, R.0, R.1, 1), (0xc54d_44a1_49a7_d61a, S.0, S.1, 56), (0xca3e_5c85_c24b_4889, S.0, S.1, 1), (0xcd42_2ac4_b90b_32b3, S.0, S.1, 1), (0xd140_a802_3d39_d033, P.0, P.1, 13), (0xdae0_b26f_b1f4_db65, R.0, R.1, 8), (0xe132_14e4_1c8a_55b6, B.0, B.1, 8), (0xe8a3_3e37_d747_9a46, S.0, S.1, 1), ]
};
pub fn identify_well_known_icc(icc_bytes: &[u8], tolerance: IccMatchTolerance) -> Option<Cicp> {
let hash = fnv1a_64(icc_bytes);
let idx = KNOWN_ICC_PROFILES
.binary_search_by_key(&hash, |&(h, _, _, _)| h)
.ok()?;
let (_, cp, tc, err) = KNOWN_ICC_PROFILES[idx];
if err > tolerance as u8 {
return None;
}
Some(Cicp::new(cp, tc, 0, true))
}
pub fn icc_profile_is_srgb(icc_bytes: &[u8]) -> bool {
identify_well_known_icc(icc_bytes, IccMatchTolerance::Intent)
.is_some_and(|c| c.color_primaries == 1 && c.transfer_characteristics == 13)
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::sync::Arc;
use zenpixels::{AlphaMode, Cicp, ColorPrimaries, SignalRange, TransferFunction};
#[test]
fn no_metadata_assumes_srgb() {
let sc = SourceColor::default();
let desc =
descriptor_for_decoded_pixels(PixelFormat::Rgb8, &sc, None, IccMatchTolerance::Intent);
assert_eq!(desc.transfer, TransferFunction::Srgb);
assert_eq!(desc.primaries, ColorPrimaries::Bt709);
}
#[test]
fn no_metadata_gray_assumes_srgb() {
let sc = SourceColor::default();
let desc =
descriptor_for_decoded_pixels(PixelFormat::Gray8, &sc, None, IccMatchTolerance::Intent);
assert_eq!(desc.transfer, TransferFunction::Srgb);
assert_eq!(desc.primaries, ColorPrimaries::Bt709);
assert_eq!(desc.pixel_format(), PixelFormat::Gray8);
}
#[test]
fn no_metadata_rgba_assumes_srgb() {
let sc = SourceColor::default();
let desc =
descriptor_for_decoded_pixels(PixelFormat::Rgba8, &sc, None, IccMatchTolerance::Intent);
assert_eq!(desc.transfer, TransferFunction::Srgb);
assert_eq!(desc.primaries, ColorPrimaries::Bt709);
assert_eq!(desc.pixel_format(), PixelFormat::Rgba8);
assert_eq!(desc.alpha(), Some(AlphaMode::Straight));
}
#[test]
fn no_metadata_f32_assumes_srgb() {
let sc = SourceColor::default();
let desc = descriptor_for_decoded_pixels(
PixelFormat::RgbF32,
&sc,
None,
IccMatchTolerance::Intent,
);
assert_eq!(desc.transfer, TransferFunction::Srgb);
assert_eq!(desc.primaries, ColorPrimaries::Bt709);
assert_eq!(desc.pixel_format(), PixelFormat::RgbF32);
}
#[test]
fn cicp_srgb_sets_srgb_descriptor() {
let sc = SourceColor::default().with_cicp(Cicp::SRGB);
let desc =
descriptor_for_decoded_pixels(PixelFormat::Rgb8, &sc, None, IccMatchTolerance::Intent);
assert_eq!(desc.transfer, TransferFunction::Srgb);
assert_eq!(desc.primaries, ColorPrimaries::Bt709);
}
#[test]
fn cicp_p3_sets_descriptor() {
let sc = SourceColor::default().with_cicp(Cicp::DISPLAY_P3);
let desc =
descriptor_for_decoded_pixels(PixelFormat::Rgb8, &sc, None, IccMatchTolerance::Intent);
assert_eq!(desc.transfer, TransferFunction::Srgb);
assert_eq!(desc.primaries, ColorPrimaries::DisplayP3);
}
#[test]
fn cicp_pq_sets_descriptor() {
let sc = SourceColor::default().with_cicp(Cicp::BT2100_PQ);
let desc = descriptor_for_decoded_pixels(
PixelFormat::RgbaF32,
&sc,
None,
IccMatchTolerance::Intent,
);
assert_eq!(desc.transfer, TransferFunction::Pq);
assert_eq!(desc.primaries, ColorPrimaries::Bt2020);
}
#[test]
fn cicp_hlg_sets_descriptor() {
let sc = SourceColor::default().with_cicp(Cicp::BT2100_HLG);
let desc = descriptor_for_decoded_pixels(
PixelFormat::RgbF32,
&sc,
None,
IccMatchTolerance::Intent,
);
assert_eq!(desc.transfer, TransferFunction::Hlg);
assert_eq!(desc.primaries, ColorPrimaries::Bt2020);
}
#[test]
fn cicp_takes_precedence_over_icc() {
let fake_icc: Arc<[u8]> = Arc::from(alloc::vec![0u8; 64].into_boxed_slice());
let sc = SourceColor::default()
.with_cicp(Cicp::DISPLAY_P3)
.with_icc_profile(fake_icc);
let desc =
descriptor_for_decoded_pixels(PixelFormat::Rgb8, &sc, None, IccMatchTolerance::Intent);
assert_eq!(desc.transfer, TransferFunction::Srgb);
assert_eq!(desc.primaries, ColorPrimaries::DisplayP3);
}
#[test]
fn cicp_preserves_pixel_format() {
let sc = SourceColor::default().with_cicp(Cicp::DISPLAY_P3);
for fmt in [
PixelFormat::Rgb8,
PixelFormat::Rgba8,
PixelFormat::Gray8,
PixelFormat::RgbF32,
PixelFormat::Bgra8,
] {
let desc = descriptor_for_decoded_pixels(fmt, &sc, None, IccMatchTolerance::Intent);
assert_eq!(desc.pixel_format(), fmt, "format mismatch for {fmt:?}");
}
}
#[test]
fn unknown_icc_yields_unknown_descriptor() {
let fake_icc: Arc<[u8]> = Arc::from(alloc::vec![0u8; 64].into_boxed_slice());
let sc = SourceColor::default().with_icc_profile(fake_icc);
let desc =
descriptor_for_decoded_pixels(PixelFormat::Rgb8, &sc, None, IccMatchTolerance::Intent);
assert_eq!(desc.transfer, TransferFunction::Unknown);
assert_eq!(desc.primaries, ColorPrimaries::Unknown);
}
#[test]
fn unknown_icc_preserves_format_and_alpha() {
let fake_icc: Arc<[u8]> = Arc::from(alloc::vec![99u8; 128].into_boxed_slice());
let sc = SourceColor::default().with_icc_profile(fake_icc);
let desc =
descriptor_for_decoded_pixels(PixelFormat::Rgba8, &sc, None, IccMatchTolerance::Intent);
assert_eq!(desc.pixel_format(), PixelFormat::Rgba8);
assert_eq!(desc.alpha(), Some(AlphaMode::Straight));
assert_eq!(desc.signal_range, SignalRange::Full);
let desc =
descriptor_for_decoded_pixels(PixelFormat::Rgb8, &sc, None, IccMatchTolerance::Intent);
assert_eq!(desc.pixel_format(), PixelFormat::Rgb8);
assert!(desc.alpha().is_none());
}
#[test]
fn unknown_icc_gray_preserves_format() {
let fake_icc: Arc<[u8]> = Arc::from(alloc::vec![42u8; 96].into_boxed_slice());
let sc = SourceColor::default().with_icc_profile(fake_icc);
let desc =
descriptor_for_decoded_pixels(PixelFormat::Gray8, &sc, None, IccMatchTolerance::Intent);
assert_eq!(desc.pixel_format(), PixelFormat::Gray8);
assert_eq!(desc.transfer, TransferFunction::Unknown);
assert_eq!(desc.primaries, ColorPrimaries::Unknown);
}
#[test]
fn empty_icc_yields_unknown() {
let empty_icc: Arc<[u8]> = Arc::from(alloc::vec![].into_boxed_slice());
let sc = SourceColor::default().with_icc_profile(empty_icc);
let desc =
descriptor_for_decoded_pixels(PixelFormat::Rgb8, &sc, None, IccMatchTolerance::Intent);
assert_eq!(desc.transfer, TransferFunction::Unknown);
assert_eq!(desc.primaries, ColorPrimaries::Unknown);
}
#[test]
fn corrected_to_overrides_source_cicp() {
let sc = SourceColor::default().with_cicp(Cicp::DISPLAY_P3);
let desc = descriptor_for_decoded_pixels(
PixelFormat::Rgb8,
&sc,
Some(&Cicp::SRGB),
IccMatchTolerance::Intent,
);
assert_eq!(desc.transfer, TransferFunction::Srgb);
assert_eq!(desc.primaries, ColorPrimaries::Bt709);
}
#[test]
fn corrected_to_overrides_unknown_icc() {
let fake_icc: Arc<[u8]> = Arc::from(alloc::vec![0u8; 64].into_boxed_slice());
let sc = SourceColor::default().with_icc_profile(fake_icc);
let desc = descriptor_for_decoded_pixels(
PixelFormat::Rgb8,
&sc,
Some(&Cicp::SRGB),
IccMatchTolerance::Intent,
);
assert_eq!(desc.transfer, TransferFunction::Srgb);
assert_eq!(desc.primaries, ColorPrimaries::Bt709);
}
#[test]
fn corrected_to_overrides_no_metadata() {
let sc = SourceColor::default();
let desc = descriptor_for_decoded_pixels(
PixelFormat::Rgb8,
&sc,
Some(&Cicp::SRGB),
IccMatchTolerance::Intent,
);
assert_eq!(desc.transfer, TransferFunction::Srgb);
assert_eq!(desc.primaries, ColorPrimaries::Bt709);
}
#[test]
fn corrected_to_p3_target() {
let sc = SourceColor::default().with_cicp(Cicp::SRGB);
let desc = descriptor_for_decoded_pixels(
PixelFormat::Rgb8,
&sc,
Some(&Cicp::DISPLAY_P3),
IccMatchTolerance::Intent,
);
assert_eq!(desc.transfer, TransferFunction::Srgb);
assert_eq!(desc.primaries, ColorPrimaries::DisplayP3);
}
#[test]
fn corrected_to_preserves_format() {
let sc = SourceColor::default().with_cicp(Cicp::BT2100_PQ);
let desc = descriptor_for_decoded_pixels(
PixelFormat::Bgra8,
&sc,
Some(&Cicp::SRGB),
IccMatchTolerance::Intent,
);
assert_eq!(desc.pixel_format(), PixelFormat::Bgra8);
assert_eq!(desc.transfer, TransferFunction::Srgb);
}
#[test]
fn identify_rejects_empty() {
assert!(identify_well_known_icc(&[], IccMatchTolerance::Intent).is_none());
assert!(!icc_profile_is_srgb(&[]));
}
#[test]
fn identify_rejects_garbage() {
assert!(identify_well_known_icc(&[0u8; 100], IccMatchTolerance::Intent).is_none());
}
#[test]
fn identify_rejects_short() {
assert!(identify_well_known_icc(&[1, 2, 3, 4], IccMatchTolerance::Intent).is_none());
}
#[test]
fn icc_profile_is_srgb_compat() {
assert!(!icc_profile_is_srgb(&[0u8; 100]));
}
#[test]
fn fnv1a_deterministic() {
let data = b"sRGB IEC61966-2.1";
assert_eq!(fnv1a_64(data), fnv1a_64(data));
}
#[test]
fn fnv1a_distinct_inputs() {
assert_ne!(fnv1a_64(b"abc"), fnv1a_64(b"abd"));
}
#[test]
fn known_profiles_table_sorted() {
for i in 1..KNOWN_ICC_PROFILES.len() {
assert!(
KNOWN_ICC_PROFILES[i - 1].0 < KNOWN_ICC_PROFILES[i].0,
"KNOWN_ICC_PROFILES not sorted at index {i}: 0x{:016x} >= 0x{:016x}",
KNOWN_ICC_PROFILES[i - 1].0,
KNOWN_ICC_PROFILES[i].0,
);
}
}
#[test]
fn tolerance_exact_filters_lut_profiles() {
let micro_entry = KNOWN_ICC_PROFILES
.iter()
.find(|e| e.0 == 0x7271_2df1_0196_b1db); assert!(micro_entry.is_some());
let (_, _, _, err) = micro_entry.unwrap();
assert_eq!(*err, 13);
assert!(*err > IccMatchTolerance::Exact as u8);
assert!(*err <= IccMatchTolerance::Approximate as u8);
}
#[test]
fn tolerance_intent_accepts_nano() {
let nano_entry = KNOWN_ICC_PROFILES
.iter()
.find(|e| e.0 == 0xc54d_44a1_49a7_d61a); assert!(nano_entry.is_some());
let (_, _, _, err) = nano_entry.unwrap();
assert_eq!(*err, 56);
assert!(*err > IccMatchTolerance::Approximate as u8);
assert!(*err <= IccMatchTolerance::Intent as u8);
}
fn sc_none() -> SourceColor {
SourceColor::default()
}
fn sc_cicp(c: Cicp) -> SourceColor {
SourceColor::default().with_cicp(c)
}
fn sc_icc(fill: u8, len: usize) -> SourceColor {
let icc: Arc<[u8]> = Arc::from(alloc::vec![fill; len].into_boxed_slice());
SourceColor::default().with_icc_profile(icc)
}
fn sc_cicp_icc(c: Cicp, fill: u8, len: usize) -> SourceColor {
let icc: Arc<[u8]> = Arc::from(alloc::vec![fill; len].into_boxed_slice());
SourceColor::default().with_cicp(c).with_icc_profile(icc)
}
use ColorPrimaries as CP;
use TransferFunction as TF;
type FormatScenario = (&'static str, PixelFormat, SourceColor, Option<Cicp>, TF, CP);
#[test]
fn format_scenarios() {
let cases: &[FormatScenario] = &[
(
"jpeg_no_icc",
PixelFormat::Rgb8,
sc_none(),
None,
TF::Srgb,
CP::Bt709,
),
(
"jpeg_unknown_icc",
PixelFormat::Rgb8,
sc_icc(0xCA, 3144),
None,
TF::Unknown,
CP::Unknown,
),
(
"jpeg_corrected",
PixelFormat::Rgb8,
sc_icc(0xCA, 3144),
Some(Cicp::SRGB),
TF::Srgb,
CP::Bt709,
),
(
"png_cicp_p3",
PixelFormat::Rgba8,
sc_cicp(Cicp::DISPLAY_P3),
None,
TF::Srgb,
CP::DisplayP3,
),
(
"png_cicp_over_icc",
PixelFormat::Rgba8,
sc_cicp_icc(Cicp::DISPLAY_P3, 0, 100),
None,
TF::Srgb,
CP::DisplayP3,
),
(
"png_no_metadata",
PixelFormat::Rgba8,
sc_none(),
None,
TF::Srgb,
CP::Bt709,
),
(
"png_hdr_pq",
PixelFormat::Rgba16,
sc_cicp(Cicp::BT2100_PQ),
None,
TF::Pq,
CP::Bt2020,
),
(
"webp_no_icc",
PixelFormat::Rgba8,
sc_none(),
None,
TF::Srgb,
CP::Bt709,
),
(
"webp_unknown_icc",
PixelFormat::Rgba8,
sc_icc(0xA3, 480),
None,
TF::Unknown,
CP::Unknown,
),
(
"avif_srgb",
PixelFormat::Rgba8,
sc_cicp(Cicp::SRGB),
None,
TF::Srgb,
CP::Bt709,
),
(
"avif_hdr10",
PixelFormat::RgbaF32,
sc_cicp(Cicp::BT2100_PQ),
None,
TF::Pq,
CP::Bt2020,
),
(
"avif_hlg",
PixelFormat::RgbF32,
sc_cicp(Cicp::BT2100_HLG),
None,
TF::Hlg,
CP::Bt2020,
),
(
"avif_p3",
PixelFormat::Rgb8,
sc_cicp(Cicp::DISPLAY_P3),
None,
TF::Srgb,
CP::DisplayP3,
),
(
"jxl_srgb",
PixelFormat::Rgb8,
sc_cicp(Cicp::SRGB),
None,
TF::Srgb,
CP::Bt709,
),
(
"jxl_p3_pq",
PixelFormat::RgbaF32,
sc_cicp(Cicp::new(12, 16, 0, true)),
None,
TF::Pq,
CP::DisplayP3,
),
(
"heic_p3",
PixelFormat::Rgba8,
sc_cicp(Cicp::DISPLAY_P3),
None,
TF::Srgb,
CP::DisplayP3,
),
(
"heic_hdr10",
PixelFormat::RgbaF32,
sc_cicp(Cicp::BT2100_PQ),
None,
TF::Pq,
CP::Bt2020,
),
(
"gif_srgb",
PixelFormat::Rgba8,
sc_none(),
None,
TF::Srgb,
CP::Bt709,
),
(
"bmp_srgb",
PixelFormat::Rgb8,
sc_none(),
None,
TF::Srgb,
CP::Bt709,
),
(
"pnm_gray",
PixelFormat::Gray8,
sc_none(),
None,
TF::Srgb,
CP::Bt709,
),
(
"tiff_unknown_icc",
PixelFormat::Rgb16,
sc_icc(0x54, 7261),
None,
TF::Unknown,
CP::Unknown,
),
(
"tiff_no_icc",
PixelFormat::Rgb16,
sc_none(),
None,
TF::Srgb,
CP::Bt709,
),
];
for &(name, fmt, ref sc, ref corrected, exp_tf, exp_cp) in cases {
let desc = descriptor_for_decoded_pixels(
fmt,
sc,
corrected.as_ref(),
IccMatchTolerance::Intent,
);
assert_eq!(desc.transfer, exp_tf, "{name}: transfer");
assert_eq!(desc.primaries, exp_cp, "{name}: primaries");
assert_eq!(desc.pixel_format(), fmt, "{name}: format");
}
}
#[test]
fn identify_family_counts() {
let count = |cp: u8, tc: u8| {
KNOWN_ICC_PROFILES
.iter()
.filter(|e| e.1 == cp && e.2 == tc)
.count()
};
assert_eq!(count(1, 13), 26, "sRGB");
assert_eq!(count(12, 13), 9, "Display P3");
assert_eq!(count(9, 1), 6, "BT.2020");
assert_eq!(count(1, 1), 4, "BT.709");
assert_eq!(KNOWN_ICC_PROFILES.len(), 45, "total");
}
#[test]
fn no_duplicate_hashes() {
let mut seen = alloc::collections::BTreeSet::new();
for entry in KNOWN_ICC_PROFILES {
assert!(seen.insert(entry.0), "duplicate hash 0x{:016x}", entry.0);
}
}
#[test]
fn all_errors_within_intent_tolerance() {
for entry in KNOWN_ICC_PROFILES {
assert!(
entry.3 <= IccMatchTolerance::Intent as u8,
"entry 0x{:016x} has err={} exceeding Intent({})",
entry.0,
entry.3,
IccMatchTolerance::Intent as u8,
);
}
}
#[test]
fn all_cicp_codes_valid() {
for entry in KNOWN_ICC_PROFILES {
let (_, cp, tc, _) = *entry;
assert!(
matches!(cp, 1 | 9 | 12),
"entry 0x{:016x} has invalid CP={}",
entry.0,
cp
);
assert!(
matches!(tc, 1 | 13),
"entry 0x{:016x} has invalid TC={}",
entry.0,
tc
);
}
}
#[test]
fn zero_filled_data_no_false_positive() {
for len in [
410, 456, 480, 524, 548, 656, 736, 790, 2576, 3024, 3144, 6184, 20420,
] {
let zeros = alloc::vec![0u8; len];
assert!(
identify_well_known_icc(&zeros, IccMatchTolerance::Intent).is_none(),
"zeros({len}) falsely matched a profile"
);
}
}
#[test]
fn tolerance_ordering() {
assert!(IccMatchTolerance::Exact < IccMatchTolerance::Precise);
assert!(IccMatchTolerance::Precise < IccMatchTolerance::Approximate);
assert!(IccMatchTolerance::Approximate < IccMatchTolerance::Intent);
}
#[test]
fn all_paths_produce_full_range() {
let cases: &[(SourceColor, Option<&Cicp>)] = &[
(SourceColor::default(), None),
(SourceColor::default().with_cicp(Cicp::SRGB), None),
(SourceColor::default().with_cicp(Cicp::DISPLAY_P3), None),
(SourceColor::default().with_cicp(Cicp::BT2100_PQ), None),
(SourceColor::default(), Some(&Cicp::SRGB)),
];
for (sc, corrected) in cases {
let desc = descriptor_for_decoded_pixels(
PixelFormat::Rgb8,
sc,
*corrected,
IccMatchTolerance::Intent,
);
assert_eq!(
desc.signal_range,
SignalRange::Full,
"non-full range for {sc:?} corrected={corrected:?}"
);
}
}
}