fix(psd): keep a pixel layer's vstk when it has no vector mask, as Photoshop does

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon Thomas 2026-10-12 04:57:31 -04:00
parent 91f734aa80
commit 9aaeb6e628
2 changed files with 9 additions and 8 deletions

View File

@ -89,7 +89,7 @@ fn raster_layer_with_stale_blocks_writes_none() {
assert!(l.psd_blocks.iter().any(|(k, _)| k == b"SoCo"));
l.content = LayerContent::Raster(photocraft_raster::Surface::new(fmt));
l.psd_blocks.push((*b"TySh", std::sync::Arc::new(vec![0; 8])));
l.psd_blocks.push((*b"vstk", std::sync::Arc::new(vec![0; 8])));
l.psd_blocks.push((*b"vscg", std::sync::Arc::new(vec![0; 8])));
l.psd_blocks.push((*b"levl", std::sync::Arc::new(vec![0; 8])));
assert!(!has_block(&doc, &name, &[b"SoCo", b"TySh", b"vstk", b"vscg", b"SoLd", b"PlLd", b"levl"]));
assert_eq!(kind_named(&reopen(&doc), &name), "raster");

View File

@ -16,9 +16,9 @@ use crate::pixels::{deinterleave, encode_be, psd_depth};
use crate::psd_import::REGENERATED;
/// Blocks that define a layer's content as something other than pixels: fill (`SoCo`, `GdFl`,
/// `PtFl`), placed / smart object (`SoLd`, `PlLd`, `SoLE`), type (`TySh`), shape fill and stroke
/// (`vscg`, `vstk`). A raster layer never writes them, nor adjustment blocks.
const RASTER_STALE_KEYS: [&[u8; 4]; 9] = [b"SoCo", b"GdFl", b"PtFl", b"SoLd", b"PlLd", b"SoLE", b"TySh", b"vscg", b"vstk"];
/// `PtFl`), placed / smart object (`SoLd`, `PlLd`, `SoLE`), type (`TySh`) and shape fill (`vscg`).
/// A raster layer never writes them, nor adjustment blocks.
const RASTER_STALE_KEYS: [&[u8; 4]; 8] = [b"SoCo", b"GdFl", b"PtFl", b"SoLd", b"PlLd", b"SoLE", b"TySh", b"vscg"];
const PSD_MAX_FILE_SIZE: u64 = 2 * 1024 * 1024 * 1024;
const PSD_ESTIMATE_FIXED_OVERHEAD: u64 = 1024 * 1024;
@ -590,12 +590,13 @@ impl Ex {
}
}
// A pixel layer carries no content-defining blocks: a fill, placed (smart), type or
// shape-content block left over from before Rasterize would make a reader (ours or
// Photoshop's) reopen the layer as that kind and discard the pixels. A vector mask
// (`vmsk`/`vsms`, with its live-shape `vogk`) stays.
// shape-fill block left over from before Rasterize would make a reader (ours or
// Photoshop's) reopen the layer as that kind and discard the pixels. A shape stroke
// (`vstk`) beside a vector mask makes a shape too; without one, Photoshop keeps it on
// pixel layers, so it stays. A vector mask (`vmsk`/`vsms`, `vogk`) stays.
LayerContent::Raster(_) => {
let masked = l.vector_mask.is_some();
raw.retain(|(k, _)| !RASTER_STALE_KEYS.contains(&k) && !adjust_map::ADJUSTMENT_KEYS.contains(&k) && k != b"CgEd" && !(k == b"vogk" && !masked));
raw.retain(|(k, _)| !RASTER_STALE_KEYS.contains(&k) && !adjust_map::ADJUSTMENT_KEYS.contains(&k) && k != b"CgEd" && !(k == b"vstk" && masked));
}
LayerContent::Group(_) => {}
}