Files
hcie-rust-v3.05/.kilo/plans/fix-psd-writer-for-photoshop.md
T

132 lines
4.7 KiB
Markdown
Raw Normal View History

2026-07-09 02:59:53 +03:00
# PSD Writer: Regression Fix for Photoshop Compatibility
## Problem
The PSD files generated by `hcie-psd/src/psd_saver.rs` can be parsed by `Psd::from_bytes` (our internal parser) but **cannot be opened in Adobe Photoshop**. The user says PSD writing was working earlier in the project's git history and has regressed.
## Git History
There are 3 commits touching `hcie-psd/src/psd_saver.rs`:
1. `cd0b6b1` — Initial PSD writer (997 lines)
2. `0a821ea` — Added examples, 1-line change to saver
3. `6616b31` — "Fix layer extra data and merged image layout" (our recent changes)
## Key Differences: `cd0b6b1` vs `6616b31` (HEAD)
### Change 1: Merged Image Row Length Order (SUSPECT)
**Original (cd0b6b1):** Interleaved — `[R0, G0, B0, A0, R1, G1, B1, A1, ...]`
```rust
for y in 0..h {
for ch_idx in 0..4 {
buf.extend_from_slice(&row_lengths[ch_idx][y]);
}
}
```
**Current (6616b31):** Per-channel — `[R0, R1, ..., Rn, G0, G1, ..., Gn, ...]`
```rust
for ch_idx in 0..4 {
for y in 0..h {
buf.extend_from_slice(&row_lengths[ch_idx][y]);
}
}
```
**PSD Spec says:** "byte counts for all the scan lines (rows × channels)" — ambiguous ordering, but data is planar (`RRR GGG BBB`). Both orderings produce the same total count. The question is which order Photoshop expects.
**CRITICAL:** The original code has a **mismatch** — byte counts are interleaved but compressed data is per-channel. This would cause Photoshop to read wrong row lengths → corrupted data. The current code is internally consistent (both per-channel). But maybe Photoshop expects interleaved for both.
### Change 2: Layer Count Sign (SUSPECT)
Both versions always write **negative** layer count:
```rust
layer_records_buf.extend_from_slice(&(-layer_count.abs()).to_be_bytes());
```
Per PSD spec: "If the count is negative, the first layer in the following record is a group." For a simple document with no groups, Photoshop may reject negative count.
### Change 3: Extra Data Blocks (6616b31 additions)
- Added `luni` (Unicode layer name) block
- Added `lyid` (layer ID) block
- Moved `lfx2` odd-length padding from external to internal
- Used `write_8bim_block` helper for `iOpa`, `brst`, `shmd`
### Change 4: `PassThrough` blend mode added
```rust
BlendMode::PassThrough => b"pass",
```
## Debugging Plan
### Step 1: Create Minimal Test PSD (no effects)
Write a Rust example `examples/test_psd_simple.rs` that:
1. Creates a 256×256 canvas
2. Adds one layer with a red rectangle (no effects)
3. Calls `save_psd()` to write `_tmp/simple_test.psd`
4. The user opens this in Photoshop
This isolates the PSD structural issues from effects serialization.
### Step 2: Binary Comparison with Reference PSD
If Step 1 PSD doesn't open:
1. User creates the same image in Photopea/Photoshop and saves as PSD
2. Write a Python script to hex-dump both files and diff the binary structure
3. Identify exact byte offset where the format diverges
### Step 3: Fix Identified Issues
Based on the debugging results, fix the PSD writer. Most likely fixes:
**Fix A — Merged Image Row Lengths:** Try interleaved order (matching original) for BOTH byte counts AND data. This is the most common PSD layout in practice.
**Fix B — Layer Count:** Write positive count when there are no groups:
```rust
let count = if layers.iter().any(|l| l.layer_type == LayerType::Group) {
-(layers.len() as i16)
} else {
layers.len() as i16
};
```
**Fix C — lfx2/shmd descriptor correctness:** Verify the ActionDescriptor binary format matches what Photoshop writes. Cross-reference with the spec's descriptor format section.
### Step 4: Test with Effects
Once the basic PSD opens, add inner shadow effects and test again.
### Step 5: Batch Generate 192 Inner Shadow PSDs
Write `examples/generate_inner_shadow_psd_set.rs` using the fixed `save_psd()`.
### Step 6: Batch Export PNGs in Photoshop
User runs `_tools/photoshop/export_psd_folder_to_png.jsx` in Photoshop to batch-export all 192 PSDs as transparent PNGs.
### Step 7: Run MAE Grid Search
```bash
cargo run --package hcie-io --example tune_mae_inner_shadow_grid
```
## Files to Modify/Create
| File | Action |
|---|---|
| `hcie-psd/src/psd_saver.rs` | Fix merged image row order, layer count, possibly effects format |
| `hcie-io/examples/test_psd_simple.rs` | **Create** — minimal PSD for Photoshop validation |
| `hcie-io/examples/generate_inner_shadow_psd_set.rs` | **Create** — batch inner shadow PSD generator |
| `hcie-io/examples/tune_mae_inner_shadow_grid.rs` | Already created, ready to use |
## Verification
1. `cargo run --package hcie-io --example test_psd_simple` → user opens in Photoshop
2. If opens → proceed to effects test
3. If fails → hex dump comparison with Photoshop-generated PSD
4. Final: 192 PSDs → Photoshop batch export → MAE tuning