From eb2b734059e63e94cae031a97b44966e340cf9e8 Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Tue, 13 Oct 2026 02:34:15 -0400 Subject: [PATCH] PSD: take the integration branch's adapted non-finite test and psd_import formatting (#1149) The six PSD PRs merged one by one leave #1131's test calling samples()/to_rgb(), which #1139 replaced; vangel76's integration draft #1149 has the adapted version. Co-Authored-By: Claude Opus 5.5 --- crates/engine/src/psd_import.rs | 9 +-- crates/psd/src/tests.rs | 115 ++++++++++++++++---------------- 2 files changed, 61 insertions(+), 63 deletions(-) diff --git a/crates/engine/src/psd_import.rs b/crates/engine/src/psd_import.rs index 25c19ef5..8829f656 100644 --- a/crates/engine/src/psd_import.rs +++ b/crates/engine/src/psd_import.rs @@ -111,16 +111,13 @@ struct Ctx<'a> { pub fn import(proj: &mut Project, psd: &Psd, path: &str, name: &str, retain: bool, rate: FrameRate, duration: Tick) -> PsdImport { let folder = proj.add_item(&format!("{name} Layers"), Label::Yellow, None, ItemKind::Folder); let mut cx = Ctx { psd, path, retain, folder, rate, duration, items: vec![], warnings: vec![] }; + cx.warnings.extend(psd.warnings.iter().cloned()); let (tree, flattened) = psd.tree_report(); if flattened > 0 { cx.warnings.push(format!("{flattened} group(s) nested deeper than {} levels were flattened into their parent", effectcraft_psd::MAX_GROUP_DEPTH)); } - cx.warnings.extend(psd.warnings.iter().cloned()); - let comp = if psd.layers.is_empty() && !psd.warnings.is_empty() { - flattened_comp(proj, &mut cx, name) - } else { - build_comp(proj, &mut cx, name, &tree, None) - }; + let comp = + if psd.layers.is_empty() && !psd.warnings.is_empty() { flattened_comp(proj, &mut cx, name) } else { build_comp(proj, &mut cx, name, &tree, None) }; PsdImport { comp, folder, items: cx.items, warnings: cx.warnings } } diff --git a/crates/psd/src/tests.rs b/crates/psd/src/tests.rs index 9252808e..60283b22 100644 --- a/crates/psd/src/tests.rs +++ b/crates/psd/src/tests.rs @@ -340,9 +340,10 @@ fn non_finite_32_bit_samples_become_finite() { d.layers = vec![WLayer::solid("L", Rect::new(0, 0, 4, 4), [0.5, 0.5, 0.5, 1.0])]; let p = Psd::parse(write(&d)).unwrap(); let raw: Vec = [f32::NAN, f32::INFINITY, f32::NEG_INFINITY, 0.25].iter().flat_map(|v| v.to_be_bytes()).collect(); - let s = p.samples(&raw, 4, 1); + let mut s = vec![f32::NAN; 4]; + p.for_each_sample(&raw, 4, 1, |i, v| s[i] = v); assert_eq!(s, vec![0.0, 1.0, 0.0, 0.25]); - assert!(p.to_rgb(&[Some(s.clone()), Some(s.clone()), Some(s)], 1).iter().all(|v| v.is_finite())); + assert!(p.rgb_from([s[1], s[1], s[1], 0.0]).iter().all(|v| v.is_finite())); } #[test] @@ -352,6 +353,61 @@ fn rect_new_near_i32_max_saturates() { assert!(r.is_empty()); } +fn find(bytes: &[u8], needle: &[u8]) -> usize { + bytes.windows(needle.len()).position(|w| w == needle).expect("needle present") +} + +/// The second layer record's signature overwritten. +fn bad_signature() -> Vec { + let mut bytes = write(&doc_with_layers(8, ColorMode::Rgb, true)); + let at = find(&bytes, b"8BIMmul "); + bytes[at] = b'X'; + bytes +} + +/// The first channel of the first layer claims more data than the file holds. +fn bad_channel_length() -> Vec { + let mut bytes = write(&doc_with_layers(8, ColorMode::Rgb, true)); + let at = find(&bytes, b"8BIMnorm"); + let first_len = at - 4 * 6 + 2; + bytes[first_len..first_len + 4].copy_from_slice(&0x7fff_fff0u32.to_be_bytes()); + bytes +} + +#[test] +fn damaged_layer_section_keeps_the_merged_image() { + let good = Psd::parse(write(&doc_with_layers(8, ColorMode::Rgb, true))).unwrap(); + assert!(good.warnings.is_empty() && good.layers.len() == 3); + let want = good.composite().unwrap(); + for bytes in [bad_signature(), bad_channel_length()] { + let p = Psd::parse(bytes).unwrap(); + assert!(p.layers.is_empty()); + assert_eq!(p.warnings.len(), 1, "{:?}", p.warnings); + assert!(p.warnings[0].contains("Layers could not be read")); + assert_eq!((p.width, p.height), (40, 30)); + let got = p.composite().unwrap(); + assert_eq!(got.data, want.data); + } +} + +#[test] +fn header_damage_and_missing_merged_image_still_fail() { + let good = write(&doc_with_layers(8, ColorMode::Rgb, true)); + // Zero width. + let mut b = good.clone(); + b[18..22].copy_from_slice(&0u32.to_be_bytes()); + assert!(Psd::parse(b).is_err()); + // Unknown colour mode. + let mut b = good.clone(); + b[24..26].copy_from_slice(&99u16.to_be_bytes()); + assert!(Psd::parse(b).is_err()); + // Damaged layers and no merged image at all: nothing to fall back to. + let end = Psd::parse(good).unwrap().merged_offset; + let mut b = bad_signature(); + b.truncate(end); + assert!(Psd::parse(b).is_err()); +} + /// A ZIP plane body for the decoder: the plane delta-coded per row when `predict`. fn zip_body(raw: &[u8], w: usize, depth: u16, predict: bool) -> Vec { if !predict { @@ -434,58 +490,3 @@ fn rle_composite_matches_the_raw_one() { assert_eq!(rle.layer_pixels(2, true).unwrap().data, raw.layer_pixels(2, true).unwrap().data, "{depth} bit"); } } - -fn find(bytes: &[u8], needle: &[u8]) -> usize { - bytes.windows(needle.len()).position(|w| w == needle).expect("needle present") -} - -/// The second layer record's signature overwritten. -fn bad_signature() -> Vec { - let mut bytes = write(&doc_with_layers(8, ColorMode::Rgb, true)); - let at = find(&bytes, b"8BIMmul "); - bytes[at] = b'X'; - bytes -} - -/// The first channel of the first layer claims more data than the file holds. -fn bad_channel_length() -> Vec { - let mut bytes = write(&doc_with_layers(8, ColorMode::Rgb, true)); - let at = find(&bytes, b"8BIMnorm"); - let first_len = at - 4 * 6 + 2; - bytes[first_len..first_len + 4].copy_from_slice(&0x7fff_fff0u32.to_be_bytes()); - bytes -} - -#[test] -fn damaged_layer_section_keeps_the_merged_image() { - let good = Psd::parse(write(&doc_with_layers(8, ColorMode::Rgb, true))).unwrap(); - assert!(good.warnings.is_empty() && good.layers.len() == 3); - let want = good.composite().unwrap(); - for bytes in [bad_signature(), bad_channel_length()] { - let p = Psd::parse(bytes).unwrap(); - assert!(p.layers.is_empty()); - assert_eq!(p.warnings.len(), 1, "{:?}", p.warnings); - assert!(p.warnings[0].contains("Layers could not be read")); - assert_eq!((p.width, p.height), (40, 30)); - let got = p.composite().unwrap(); - assert_eq!(got.data, want.data); - } -} - -#[test] -fn header_damage_and_missing_merged_image_still_fail() { - let good = write(&doc_with_layers(8, ColorMode::Rgb, true)); - // Zero width. - let mut b = good.clone(); - b[18..22].copy_from_slice(&0u32.to_be_bytes()); - assert!(Psd::parse(b).is_err()); - // Unknown colour mode. - let mut b = good.clone(); - b[24..26].copy_from_slice(&99u16.to_be_bytes()); - assert!(Psd::parse(b).is_err()); - // Damaged layers and no merged image at all: nothing to fall back to. - let end = Psd::parse(good).unwrap().merged_offset; - let mut b = bad_signature(); - b.truncate(end); - assert!(Psd::parse(b).is_err()); -}