5.5 KiB
5.5 KiB
PSD Layer Style (fx) Test Files — Handoff
Goal
Otomatik PSD layer style test pipeline: her efekt tipi için doğru formatta PSD dosyası → Photopea referans render → engine çıktısı ile karşılaştırma.
Output
16 test PSD dosyası /home/hc/Pictures/_psd_stil_test/ altında:
fx_baseline.psd— efektsiz basefx_drop_shadow.psd,fx_inner_shadow.psd,fx_outer_glow.psd,fx_inner_glow.psdfx_bevel_inner.psd,fx_bevel_outer.psd,fx_emboss.psd,fx_pillow_emboss.psdfx_satin.psd,fx_color_overlay.psd,fx_gradient_overlay.psd,fx_pattern_overlay.psdfx_stroke_inside.psd,fx_stroke_center.psd,fx_stroke_outside.psd
Changes Made
src/psd_saver.rs — lfx2 descriptor writer (replaces lrFX)
- Tag:
"lrFX"→"lfx2"(line 188) write_lfx2_block(): Writes lfx2 descriptor format matching Photopea output:- Header:
[ver=0 u32][dver=16 u32] _read_body: name(U+0000), classID('null'), count(2+n_effects)- Fixed items:
Scl(UntF #Prc 100),masterFXSwitch(bool true) - Per-effect: Objc with effect-specific fields (see effect field lists below)
- Header:
- Effect descriptors (
write_fx_descriptor):DrSh(DropShadow): 14 fields — enab, Md, Opct, Clr, uglg, lagl, Dstn, Ckmt, blur, Nose, AntA, TrnS, layerConceals, CntnIrSh(InnerShadow): 14 fields (same as DrSh)OrGl(OuterGlow): 10 fields — enab, Md, Opct, Clr, GlwT, blur, Ckmt, AntA, TrnS, NoseIrGl(InnerGlow): 10 fields — enab, Md, Opct, Clr, GlwS, blur, Ckmt, AntA, TrnS, Noseebbl(BevelEmboss): 29 fields — enab, hglM, hglC, hglO, sdwM, sdwC, sdwO, bvlT, bvlS, uglg, lagl, Lald, srgR, blur, bvlD, TrnS, antialiasGloss, Sftn, useShape, MpgS, AntA, Inpr, useTexture, InvT, Algn, Scl, textureDepth, Ptrn, phaselagl(Satin): 10 fields — enab, Md, Opct, Clr, lagl, Dstn, blur, MpgS, Invr, AntAsofi(ColorOverlay): 5 fields — enab, Md, Opct, Clr, CntnGrFl(GradientOverlay): 10 fields — enab, Md, Opct, Angl, Type, Rvrs, Algn, Scl, Ofst, Grad(Grdn)PtFl(PatternOverlay): 5 fields + Ptrn Objc with Nm, Idnt, phaseFrFX(Stroke): 8 fields — enab, Styl, PntT, Md, Opct, Sz, Clr (+ TrnS if not Color fill)
Descriptor helper functions
w_key4(buf, &[u8; 4])— 4-byte OSType key (length prefix = 0)w_key_var(buf, &[u8])— variable-length key (length prefix = N)w_ustr_null(buf)— Unicode name: length=1, U+0000w_ustr(buf, &str)— Unicode name: length=N, UTF-16BE bytesw_class_id(buf, &[u8; 4])— classID with length=0 prefixw_bool(buf, bool),w_enum(buf, type, val),w_untf(buf, unit, f64),w_doub(buf, f64),w_long(buf, i32)w_objc_start(buf)— writes "Objc" tagw_objc_body_start(buf, class_id, count)— _read_body: name(U+0000) + classID + countw_rgbc_color(buf, &[u8; 4])— writes RGBC Objc with Rd/Grn/Bl doubw_default_contour(buf)— standard linear contour (TrnS/MpgS)w_contour_with_key(buf, key)— key + default contour
write_shmd_block() — Photopea sheet metadata
- Structure:
[ver=1][8BIMcmls][sub_size=0][desc_size][descriptor...] - Descriptor items:
LyrI: long 0layerSettings: VlLs → Objc null with 6 items:compList: VlLs[long 0]enab: bool trueOfst: Objc {Hrzn long 0, Vrtc long 0}FXRefPoint: Objc {Hrzn long 0, Vrtc long 0}blendOptions: Objc {Md enum Nrml, Opct UntF 100, fillOpacity UntF 100}Lefx: Objc — FULL effect descriptor copy (same as lfx2: Scl + masterFXSwitch + effects)
- Total: ~1038 bytes per single-effect layer
Layer extra data additions
- Blending ranges: 40 bytes → 10 pairs of (u16=0, u16=65535)
- iOpa block:
8BIMiOpasize=4, data=[0xff, 0x00, 0x00, 0x00] - brst block:
8BIMbrstsize=0 - lfx2 block:
8BIMlfx2size=N, descriptor data - shmd block:
8BIMshmdsize=M, Photopea metadata
examples/generate_style_tests.rs
- Generates all 16 PSD files: 15 single-effect + 1 baseline
- 512×512 canvas, asymmetric trapezoidal shape per layer
- Baseline has no effects; all others have exactly 1 effect
Imports
- Added
GradientDeftouse hcie_fx::types::{..., GradientDef} - Uses
hcie_blend::BlendModefor blend mode mapping
Key Format Decisions
Objc format
Photopea Objc descriptor: NO outer wrapper. Direct _read_body:
"Objc" + name_len(u32) + name(u16×N) + classID_len(u32) + classID + count(u32) + items...
where name is always U+0000 (len=1), and classID is length-prefixed (len=0 → 4-byte key).
enum format
"enum" + classID_len(u32=0) + type_id(4B) + classID_len(u32=0) + value(4B)
Nested Objc
Contour (TrnS/MpgS), Color (RGBC), Pattern (Ptrn), Gradient (Grdn) all use the same format recursively.
How to Regenerate
cd /home/hc/Documents/00_PROJECTS/hcie-rust-v4
cargo run -p hcie-io --example generate_style_tests
Files go to /home/hc/Pictures/_psd_stil_test/fx_*.psd.
Verification
- All 15 effect files have: lfx2 + shmd + iOpa + brst blocks
- fx_baseline.psd has no effect blocks (baseline reference)
- lfx2 binary format byte-identical in structure to working
hc_drop_shadow.psd - shmd mirrors the lfx2 descriptor inside
Lefxsection
Remaining / Known Issues
- lfx2 format verified against Photopea output (
hc_drop_shadow.psd) - Files open in Photopea — effects should render based on shmd+lfx2 metadata
- GradientOverlay and PatternOverlay use minimal default gradient/pattern data
- BevelEmboss texture (Ptrn) uses hardcoded default UUID
- Contour names use ASCII "Linear" (Photopea uses localized names like "Lineární")