mirror of
https://github.com/storytold/photocraft.git
synced 2026-11-02 06:17:22 -06:00
Opening a 2.14 GB PSD (2000×2000, 109 layers, 821 MB of embedded smart objects) peaked at 7.28 GB and took 6.8 s; it now peaks at 3.00 GB and takes 3.6 s (macOS peak memory footprint, `cargo run --release -p photocraft-io --example psd_open_mem`). - photocraft-psd: `PsdFile::from_source` parses from a `ReadAt` source (`FileSource`: pread / seek_read in bounded reads). Channel data and the merged image become `Stored` ranges read when decoded; the model equals `from_bytes`'s and writes back the same bytes (tests compare the two on every generated file, including Lr16/Lr32 layer blocks and truncated files). - `ChannelData::row_decoder` decodes any band of rows: RLE via its row table (a stored channel reads only the band's bytes), raw by offset, ZIP inflated once. - photocraft-io: `import_file` opens a path; PSD/PSB through `from_source`, other formats read whole. Layers decode in bands of whole tile rows (~32 MB) into tiles: no whole-layer plane or interleaved buffer. Global blocks move into the document instead of being copied; finding an embedded smart object no longer copies every file in `lnk2`. - photocraft-raster: `Surface::share_uniform_tiles` + `TileInterner`; the PSD import shares single-value tiles (backgrounds, fill caches, masks) across layers. - The background open job and the CLI/MCP (`automation::files::open`) use `import_file`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
57 lines
2.0 KiB
Rust
57 lines
2.0 KiB
Rust
//! Opens a file the way the app's background open does and reports the time. Run it under
|
|
//! `/usr/bin/time -l` for the peak memory. `--read` reads the whole file first and imports the
|
|
//! bytes, as opens did before `import_file` (to compare).
|
|
//!
|
|
//! `cargo run --release -p photocraft-io --example psd_open_mem -- <file.psd> [--read]`
|
|
|
|
fn main() {
|
|
let Some(path) = std::env::args().nth(1).filter(|a| a != "--read") else {
|
|
eprintln!("usage: psd_open_mem <file.psd>");
|
|
std::process::exit(2);
|
|
};
|
|
if std::env::args().any(|a| a == "--read") {
|
|
read_then_import(&path);
|
|
return;
|
|
}
|
|
let t = std::time::Instant::now();
|
|
let res =
|
|
match photocraft_io::import_file(&path, std::path::Path::new(&path), &photocraft_io::ImportOptions::default(), &photocraft_raster::Interrupt::NONE) {
|
|
Ok(r) => r,
|
|
Err(e) => {
|
|
eprintln!("{path}: open failed: {e}");
|
|
std::process::exit(1);
|
|
}
|
|
};
|
|
report(&path, &format!("open {:.2}s", t.elapsed().as_secs_f64()), &res);
|
|
}
|
|
|
|
fn read_then_import(path: &str) {
|
|
let t = std::time::Instant::now();
|
|
let bytes = match photocraft_format::read_file(std::path::Path::new(path)) {
|
|
Ok(b) => b,
|
|
Err(e) => {
|
|
eprintln!("{path}: read failed: {e}");
|
|
std::process::exit(1);
|
|
}
|
|
};
|
|
let read_s = t.elapsed().as_secs_f64();
|
|
let t = std::time::Instant::now();
|
|
let res = match photocraft_io::import_with(path, &bytes, &photocraft_raster::Interrupt::NONE) {
|
|
Ok(r) => r,
|
|
Err(e) => {
|
|
eprintln!("{path}: open failed: {e}");
|
|
std::process::exit(1);
|
|
}
|
|
};
|
|
drop(bytes);
|
|
report(path, &format!("read {read_s:.2}s, import {:.2}s", t.elapsed().as_secs_f64()), &res);
|
|
}
|
|
|
|
fn report(path: &str, times: &str, res: &photocraft_io::ImportResult) {
|
|
let doc = &res.document;
|
|
println!("{path}: {times}, {} layers ({} top level)", doc.walk().len(), doc.layers.len());
|
|
for w in &res.warnings {
|
|
println!(" warning: {w}");
|
|
}
|
|
}
|