Files
hcie-rust-v3.05/.kilo/plans/fix-psd-writer-for-photoshop.md
T
2026-07-09 02:59:53 +03:00

132 lines
4.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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