11 KiB
Plan: Layer Effects (lfx2/lrFX) — Full Pipeline
Context
PSD'de layer efektleri (Drop Shadow, Outer Glow, Bevel & Emboss, Stroke vb.) lfx2 ve lrFX tagged block'larda saklanıyor. Şu an bu bloklar parse edilmiyor — sadece atlanıyor. hcie-io'da LayerStyle enum'u var ama blend_mode: String ile tanımlanmış (tip güvenli değil) ve hiçbir yerde kullanılmıyor.
Mevcut Durum
| Crate | İlgili kısım | Durum |
|---|---|---|
hcie-blend |
BlendMode enum (27 mod) |
✅ Hazır, layer fx blend modes için kullanılabilir |
hcie-blend |
blend_pixels() |
✅ Hazır, efekt compositing için kullanılabilir |
hcie-io |
LayerStyle enum |
⚠️ Var ama blend_mode: String, parse yok |
hcie-io |
lfx2/lrFX blokları |
❌ Atlanıyor, parse edilmiyor |
hcie-composite |
Layer struct |
❌ styles alanı yok |
hcie-composite |
composite_layers_parallel |
❌ Efekt render yok |
Mimari Karar: Yeni crate hcie-fx
hcie-blend sadece piksel matematiği (blend mode, LUT). Efektler YENİ piksel buffer üretiyor (gölgeler, parıltılar). Bu hcie-blend'in scope'unun dışında. hcie-filter mevcut pikselleri dönüştürüyor (blur, color adj). Efektler bağımsız katman oluşturuyor. hcie-composite pipeline'ı. hcie-io dosya I/O.
hcie-fx doğru yer: efekt veri yapılarını, binary parsing'i ve piksel render'ı sahiplenir. hcie-blend'e bağımlı (blend mode matematiği için). hcie-io ve hcie-composite'e bağımlı değil — onlar hcie-fx'e bağımlı.
hcie-blend ← hcie-fx → hcie-io (sadece tip paylaşımı)
↑
hcie-composite
Dosya Yapısı
hcie-fx/
├── Cargo.toml
├── src/
│ ├── lib.rs # re-exports, apply_layer_effects()
│ ├── types.rs # LayerEffect enum, FxParams
│ ├── parser.rs # parse_lfx2(), parse_lrFX()
│ ├── shadow.rs # DropShadow, InnerShadow renderer
│ ├── glow.rs # OuterGlow, InnerGlow renderer
│ ├── bevel.rs # BevelEmboss renderer
│ ├── overlay.rs # ColorOverlay, GradientOverlay, PatternOverlay
│ ├── stroke.rs # Stroke renderer
│ └── satin.rs # Satin renderer
Adım 1: hcie-fx/src/types.rs — Veri Yapıları
use hcie_blend::BlendMode;
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum LayerEffect {
DropShadow {
enabled: bool,
blend_mode: BlendMode, // String değil!
color: [u8; 4],
opacity: f32, // 0.0-1.0
angle: f32, // derece
distance: f32, // piksel
spread: f32, // 0-100%
size: f32, // piksel
noise: f32, // 0-100%
contour: Option<ContourCurve>,
},
InnerShadow { /* aynı alanlar */ },
OuterGlow {
enabled: bool,
blend_mode: BlendMode,
color: [u8; 4],
opacity: f32,
spread: f32,
size: f32,
noise: f32,
contour: Option<ContourCurve>,
},
InnerGlow { /* benzer */ },
BevelEmboss {
enabled: bool,
style: BevelStyle, // InnerBevel, OuterBevel, Emboss, PillowEmboss, StrokeEmboss
technique: Technique, // Smooth, ChiselHard, ChiselSoft
depth: f32,
direction: Direction, // Up, Down
size: f32,
soften: f32,
angle: f32,
altitude: f32,
highlight_blend: BlendMode,
highlight_color: [u8; 4],
highlight_opacity: f32,
shadow_blend: BlendMode,
shadow_color: [u8; 4],
shadow_opacity: f32,
contour: Option<ContourCurve>,
},
Satin {
enabled: bool,
blend_mode: BlendMode,
color: [u8; 4],
opacity: f32,
angle: f32,
distance: f32,
size: f32,
invert: bool,
},
ColorOverlay {
enabled: bool,
blend_mode: BlendMode,
color: [u8; 4],
opacity: f32,
},
GradientOverlay {
enabled: bool,
blend_mode: BlendMode,
opacity: f32,
angle: f32,
scale: f32,
gradient: GradientDef,
},
PatternOverlay {
enabled: bool,
blend_mode: BlendMode,
opacity: f32,
scale: f32,
pattern_id: String,
},
Stroke {
enabled: bool,
position: StrokePosition, // Inside, Center, Outside
fill_type: StrokeFill, // Color, Gradient, Pattern
blend_mode: BlendMode,
opacity: f32,
size: f32,
color: [u8; 4],
},
}
// Yardımcı tipler
pub struct ContourCurve { pub points: Vec<(f32, f32)> }
pub struct GradientDef { /* color stops, midpoint, angle, type */ }
pub enum BevelStyle { InnerBevel, OuterBevel, Emboss, PillowEmboss, StrokeEmboss }
pub enum Technique { Smooth, ChiselHard, ChiselSoft }
pub enum Direction { Up, Down }
pub enum StrokePosition { Inside, Center, Outside }
pub enum StrokeFill { Color([u8;4]), Gradient(GradientDef), Pattern(String) }
Adım 2: hcie-fx/src/parser.rs — Binary Parsing
lrFX bloğu (legacy, 396 byte)
Offset Size Description
0 4 Version (=0)
4 4 Count of effects
8 ... Effect records (her biri 8BIM signature + veri)
Her efekt kaydı:
0 4 Signature ('cmnS' = common)
4 4 Size of common data
8 2 Enabled flag
10 4 Blend mode key (4-char, ör: 'Mltp' = Multiply)
14 1 Opacity (0-255)
15 4 Color (4 bytes RGBA)
19 ... Effect-specific data
lfx2 bloğu (extended, 5496 byte)
Descriptor-based format. hcie-io'daki PsdReader ile parse edilebilir:
Version (4 bytes) = 16
Descriptor count
For each descriptor:
Key (string)
Type (classID)
Value (type-dependent)
Blend mode mapping (4-char key → BlendMode):
'Nrml' → Normal, 'Dslv' → Dissolve, 'Drkn' → Darken,
'Mltp' → Multiply, 'CBrn' → ColorBurn, 'LBrn' → LinearBurn,
'Lghn' → Lighten, 'Scrn' → Screen, 'CDdg' → ColorDodge,
'LDdg' → LinearDodge, 'Ovrl' → Overlay, 'SftL' → SoftLight,
'HrdL' → HardLight, 'VvdL' → VividLight, 'LnrL' → LinearLight,
'PnLt' → PinLight, 'HrdM' → HardMix, 'Dffc' → Difference,
'Xclu' → Exclusion, 'H ' → Hue, 'Strt' → Saturation,
'Clr ' → Color, 'Lmns' → Luminosity
Adım 3: hcie-fx/src/shadow.rs — Drop Shadow Renderer
pub fn render_drop_shadow(
layer_alpha: &[u8], // layer alpha channel (canvas_w * canvas_h)
canvas_w: u32,
canvas_h: u32,
params: &DropShadowParams,
) -> Vec<u8> {
// 1. Alpha'dan offset'li kopya oluştur (distance + angle)
// 2. Gaussian blur uygula (size parametresi)
// 3. Spread uygula (threshold + dilate)
// 4. Noise ekle (opsiyonel)
// 5. Color + opacity ile doldur
// 6. RGBA buffer döndür
}
Diğer efektler benzer pattern:
- Inner Shadow: maskelenmiş alpha ile aynı mantık
- Outer Glow: shadow yerine glow (genellikle Screen blend)
- Inner Glow: inner shadow gibi ama glow
- Bevel & Emboss: directional lighting, bump map from alpha
- Stroke: edge detection + fill
- Color/Gradient/Pattern Overlay: doğrudan layer alpha ile maskelenmiş fill
Adım 4: hcie-io Entegrasyonu
4a: LayerStyle'ı hcie_fx::LayerEffect ile değiştir
hcie-io/src/lib.rs:
// Eski:
pub enum LayerStyle {
DropShadow { ..., blend_mode: String },
...
}
// Yeni:
pub use hcie_fx::LayerEffect; // Re-export
// veya hcie-io'nun kendi Layer'ında:
pub struct Layer {
...
pub effects: Vec<hcie_fx::LayerEffect>, // styles yerine
}
4b: parse_psd_sequential'de lfx2/lrFX parsing
hcie-io/src/lib.rs, tagged block loop'u içinde:
} else if key == "lfx2" {
let block_data = r.read(block_len)?;
match hcie_fx::parser::parse_lfx2(&block_data) {
Ok(effects) => { layer_effects = effects; }
Err(e) => { eprintln!("lfx2 parse error: {}", e); }
}
} else if key == "lrFX" {
let block_data = r.read(block_len)?;
match hcie_fx::parser::parse_lrFX(&block_data) {
Ok(effects) => { layer_effects = effects; }
Err(e) => { eprintln!("lrFX parse error: {}", e); }
}
}
4c: LayerRecord ve ParsedLayerData'ya effects ekle
struct LayerRecord {
...
effects: Vec<hcie_fx::LayerEffect>,
}
pub struct ParsedLayerData {
...
pub effects: Vec<hcie_fx::LayerEffect>,
}
Adım 5: hcie-composite Entegrasyonu
5a: Layer'a effects ekle
pub struct Layer {
...
pub effects: Vec<hcie_fx::LayerEffect>,
}
5b: composite_layers_parallel'da efekt uygulama
Her layer compositing'den ÖNCE:
for (rev_i, layer) in layers.iter().enumerate() {
if !layer.visible { continue; }
// 1. Efektleri render et
let effect_buf = if !layer.effects.is_empty() {
hcie_fx::apply_layer_effects(&layer.pixels, layer.width, layer.height, &layer.effects)
} else {
vec![0u8; (layer.width * layer.height * 4) as usize]
};
// 2. Efekt buffer'ını layer pikselleriyle composite et
// (her efekt kendi blend_mode ve opacity'siyle)
// 3. Sonucu normal layer compositing'e sok
}
5c: Efekt render sırası (PSD spec)
1. Inner Shadow
2. Inner Glow
3. Satin
4. Color Overlay
5. Gradient Overlay
6. Pattern Overlay
7. Stroke
8. Outer Shadow (Drop Shadow)
9. Outer Glow
10. Bevel & Emboss
Her efekt kendi piksel buffer'ını üretir, sonra layer ile blend edilir.
Adım 6: GUI Test
gui.rs'de:
LayerEntry'yeeffects: Vec<hcie_fx::LayerEffect>ekle- Properties panelinde efekt listesini göster
- Her efekt için enabled/disabled toggle
- Efekt parametrelerini göster (şimdilik read-only)
Workspace Değişiklikleri
Cargo.toml (root)
[workspace]
members = [
...
"hcie-fx", # yeni
...
]
hcie-fx/Cargo.toml
[package]
name = "hcie-fx"
version = "0.1.0"
edition = "2021"
[dependencies]
hcie-blend = { path = "../hcie-blend" }
rayon = "1.10"
serde = { version = "1.0", features = ["derive"] }
[lib]
crate-type = ["staticlib", "rlib"]
hcie-io/Cargo.toml
[dependencies]
hcie-fx = { path = "../hcie-fx" } # yeni
hcie-composite/Cargo.toml
[dependencies]
hcie-fx = { path = "../hcie-fx" } # yeni
Uygulama Sırası
hcie-fxcrate oluştur — Cargo.toml, lib.rs, types.rstypes.rsyaz — LayerEffect enum, helper tiplerparser.rsyaz — lrFX parser (basit binary), lfx2 parser (descriptor)- Test: parse — Example3-org.psd'deki lfx2/lrFX bloklarını parse et, debug print
shadow.rsyaz — Drop Shadow renderer (en yaygın efekt)- hcie-io entegrasyonu — lfx2/lrFX parsing, Layer.effects
- hcie-composite entegrasyonu — efekt render + compositing
- GUI test — efekt listesi gösterimi
- Diğer renderers — glow, bevel, stroke, overlay, satin (sırayla)
Kritik Bağlamlar
hcie-blend::BlendModezaten 27 mode'u kapsıyor — layer fx için yeterlihcie-blend::blend_pixels()efekt compositing'de kullanılacakhcie-io'dakiPsdReaderbinary parsing için kullanılabilirlfx2bloğu descriptor-based,lrFXlegacy binary — ikisi de parse edilmeli- Efekt render'ı rayon ile paralelleştirilebilir (her scanline bağımsız)
- PSD'de efekt blend modes 4-char key olarak saklanıyor ('Mltp', 'Scrn' vb.)