mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-27 11:27:21 -06:00
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).
29 lines
1.4 KiB
Rust
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}");
|
|
}
|
|
}
|