Files
Dylan M. Taylor 628a235ae5 craft-cms: the changes PhotoCraft made to its copy since extraction (#858)
* craft-cms: the changes PhotoCraft made to its copy since extraction

PhotoCraft kept editing crates/cms after craft-cms was extracted from it at
storytold/photocraft@b3222ee8. This ports its 41 commits up to
storytold/photocraft@0143b148 with a three-way merge per file, keeping the
changes craft-cms made on its own (Result-returning API, white preservation,
FOGRA39, named colours, profile ID).

Ported: the .3dl shaper, Mesh header, UTF-16 and .look fixes in lutfile; the
checked ICC offsets and the per-profile curve sample budget; same_bytes and the
byte-confirmed transform cache; the blocked matrix/TRC path and the 8-bit
first-stage table; GpuMatrixShaper; cicp_rgb; the ACEScg and Dot Gain 20 %
built-ins (the latter now the default gray); bisection for a table's flat ends;
PhotoCraft spelled with a capital C in the built-in descriptions, with the
shipped coated CMYK profile's bytes as PhotoCraft regenerated them.

Not taken, because craft-cms already does it another way: Curve::inverse_table
(inverse64_many), the effective-dimension loop in multilinear interpolation,
the curve-count and mluc checks, and the .cube DOMAIN writer.

The 8-bit first-stage table checks device white as the exact path does.

Refs #779

* craft-cms: Lut3d::from_fn

PhotoCraft's proof and HDR display tables are built from a chain of several
transforms, which it wrote into Lut3d's fields. Those are private here, so
from_fn samples any RGB function, with the same size bounds as from_transform.

Refs #779

* craft-cms: README names Lut3d::from_fn
2026-11-11 18:37:23 -05:00

56 lines
1.8 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! The header's profile ID (ICC.1:2022 §7.2.18).
use craft_cms::{Builtin, CmsError, Profile, builtin, profile_id};
fn hex(id: [u8; 16]) -> String {
id.iter().map(|b| format!("{b:02x}")).collect()
}
#[test]
fn the_id_matches_an_independent_md5() {
// Computed outside Rust: MD5 of the shipped file with bytes 44–47, 64–67 and 84–99 zeroed.
assert_eq!(hex(profile_id(builtin::COATED_CMYK_ICC).unwrap()), "ab1397490b9355815dea5668de6eb073");
}
#[test]
fn encoded_profiles_carry_their_id() {
for b in [Builtin::Srgb, Builtin::LabD50, Builtin::CoatedFogra39] {
let p = b.profile().clone();
let bytes = p.with_encoded_bytes().to_bytes();
let id = profile_id(&bytes).unwrap();
assert_ne!(id, [0; 16], "{b:?}");
assert_eq!(bytes[84..100], id, "{b:?}");
assert!(Profile::parse(&bytes).is_ok(), "{b:?}");
}
}
#[test]
fn flags_and_intent_are_left_out_but_tag_data_is_not() {
let bytes = Builtin::Srgb.profile().to_bytes().to_vec();
let id = profile_id(&bytes).unwrap();
for at in [44, 47, 64, 67, 84, 99] {
let mut b = bytes.clone();
b[at] ^= 0x5a;
assert_eq!(profile_id(&b).unwrap(), id, "byte {at} is excluded");
}
for at in [8, 100, 128, bytes.len() - 1] {
let mut b = bytes.clone();
b[at] ^= 0x5a;
assert_ne!(profile_id(&b).unwrap(), id, "byte {at} is included");
}
}
#[test]
fn bytes_after_the_declared_size_are_not_hashed() {
let bytes = Builtin::Srgb.profile().to_bytes().to_vec();
let mut padded = bytes.clone();
padded.extend([0xAB; 7]);
assert_eq!(profile_id(&padded).unwrap(), profile_id(&bytes).unwrap());
}
#[test]
fn a_short_profile_has_no_id() {
assert_eq!(profile_id(&[0; 127]), Err(CmsError::Truncated));
assert!(profile_id(&[0; 128]).is_ok());
}