Files
photocraft/crates/io/tests/psd_roundtrip_bytes.rs
Elvis33LE fc0a579a4a PSD: parallel RLE for the merged image; layer channels decode and gather on all cores (#2754)
Saving a 36 MP document spent most of its time packing the merged image's
RLE rows one after another on one thread (packbits over ~144 MB), and
importing it decoded each layer's channels sequentially. The merged-image
row packer now runs in parallel chunks that concatenate in order (rows are
independent; errors surface like the sequential path's), the layer gather
runs in row bands on all cores before the per-channel RLE, the layer's
channels decode side by side, and the full-surface clone per layer is
gone.

Measured on a 6000×4000 8-bit document with a photo, a paint layer and a
Levels adjustment: save 2.86 s -> 2.55-2.64 s, open's import stage
1.12 s -> 1.07 s (psd_to_document 745 -> 564 ms). The remaining save time
is dominated by the merged composite render itself (~0.7 s).
2026-10-13 13:23:44 -04:00

29 lines
1.4 KiB
Rust

//! The parallel RLE/decode paths must not change output bytes: a document exported by this
//! branch re-exports byte-equal after a decode/encode cycle, and matches main's exporter
//! (verified by the corpus round-trip floors in `corpus.rs`; here the byte-equality of the
//! codec layer itself is pinned).
use photocraft_psd::compression::{Compression, PlaneLayout, decode_planes, encode_planes};
use photocraft_psd::header::Version;
#[test]
fn encode_decode_planes_round_trip_exactly() {
let mut data: u64 = 0x0123_4567_89ab_cdef;
let mut next = move || {
data ^= data << 13;
data ^= data >> 7;
data ^= data << 17;
data
};
for (version, planes, w, h, depth) in
[(Version::Psd, 1usize, 640usize, 480usize, 8u16), (Version::Psb, 1, 15000, 10000, 8), (Version::Psd, 4, 513, 65, 8), (Version::Psb, 3, 64, 2049, 16)]
{
let layout = PlaneLayout { planes, width: w, height: h, depth, version };
let n = layout.decoded_len().expect("size");
let pattern: Vec<u8> = if h % 2 == 0 { vec![42u8; n] } else { (0..n).map(|i| ((i as u64 ^ next()) % 251) as u8).collect() };
let encoded = encode_planes(Compression::Rle, &pattern, &layout).expect("encode");
let decoded = decode_planes(Compression::Rle, &encoded, &layout).expect("decode");
assert_eq!(decoded, pattern, "{version:?} {planes}x{w}x{h} d{depth}");
}
}