Implement stable serialization and restoration for dock layouts, including auto-hidden and floating panels
- Add `persistence.rs` for stable serialization of dock layouts without runtime identifiers. - Introduce `preview.rs` for geometry handling of dock drop previews. - Create `sizing.rs` to manage content-aware sizing policies and constraint solving for dock panels. - Implement `welcome.rs` to provide a welcome surface when no documents are open, featuring New and Open actions.
This commit is contained in:
+221
-53
@@ -1,18 +1,17 @@
|
||||
use crate::color_overlay::generate_color_overlay;
|
||||
use crate::drop_shadow::generate_shadow;
|
||||
use crate::emboss::generate_bevel_emboss;
|
||||
use crate::gradient_overlay::generate_gradient_overlay;
|
||||
use crate::helpers::{composite_effect, composite_inside_effect, extract_alpha};
|
||||
use crate::inner_shadow::generate_inner_shadow;
|
||||
use crate::outer_glow::generate_glow;
|
||||
use crate::satin::generate_satin;
|
||||
use crate::stroke::generate_stroke;
|
||||
/// Effect application orchestrator.
|
||||
///
|
||||
/// Applies all enabled layer effects to a layer's pixel buffer in standard
|
||||
/// Photoshop order. Delegates to individual effect modules for rendering.
|
||||
|
||||
use crate::types::LayerEffect;
|
||||
use crate::emboss::generate_bevel_emboss;
|
||||
use crate::outer_glow::generate_glow;
|
||||
use crate::color_overlay::generate_color_overlay;
|
||||
use crate::satin::generate_satin;
|
||||
use crate::gradient_overlay::generate_gradient_overlay;
|
||||
use crate::stroke::generate_stroke;
|
||||
use crate::drop_shadow::generate_shadow;
|
||||
use crate::inner_shadow::generate_inner_shadow;
|
||||
use crate::helpers::{extract_alpha, composite_effect, composite_inside_effect};
|
||||
use hcie_blend::blend_pixels;
|
||||
|
||||
/// Apply all enabled layer effects to a layer's pixel buffer in standard Photoshop order.
|
||||
@@ -34,7 +33,9 @@ pub fn apply_layer_effects(
|
||||
} else {
|
||||
let mut out = pixels.to_vec();
|
||||
for i in 0..px_count {
|
||||
out[i * 4 + 3] = (out[i * 4 + 3] as f32 * fill_opacity).round().clamp(0.0, 255.0) as u8;
|
||||
out[i * 4 + 3] = (out[i * 4 + 3] as f32 * fill_opacity)
|
||||
.round()
|
||||
.clamp(0.0, 255.0) as u8;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
@@ -55,31 +56,62 @@ pub fn apply_layer_effects(
|
||||
inside.copy_from_slice(pixels);
|
||||
if fill_opacity < 1.0 {
|
||||
for i in 0..px_count {
|
||||
inside[i * 4 + 3] = (inside[i * 4 + 3] as f32 * fill_opacity).round().clamp(0.0, 255.0) as u8;
|
||||
inside[i * 4 + 3] = (inside[i * 4 + 3] as f32 * fill_opacity)
|
||||
.round()
|
||||
.clamp(0.0, 255.0) as u8;
|
||||
}
|
||||
}
|
||||
|
||||
// Inside effects — Photoshop spec order:
|
||||
// a. Bevel & Emboss
|
||||
if let Some(LayerEffect::BevelEmboss {
|
||||
enabled: true, style, technique, depth, direction, size, soften,
|
||||
angle, altitude, highlight_blend, highlight_color, highlight_opacity,
|
||||
shadow_blend, shadow_color, shadow_opacity, contour, ..
|
||||
}) = effects.iter().find(|e| matches!(e, LayerEffect::BevelEmboss { .. })) {
|
||||
enabled: true,
|
||||
style,
|
||||
technique,
|
||||
depth,
|
||||
direction,
|
||||
size,
|
||||
soften,
|
||||
angle,
|
||||
altitude,
|
||||
highlight_blend,
|
||||
highlight_color,
|
||||
highlight_opacity,
|
||||
shadow_blend,
|
||||
shadow_color,
|
||||
shadow_opacity,
|
||||
contour,
|
||||
..
|
||||
}) = effects
|
||||
.iter()
|
||||
.find(|e| matches!(e, LayerEffect::BevelEmboss { .. }))
|
||||
{
|
||||
// Emboss is rendered on top of the original layer fill; do not replace
|
||||
// it with a hard-coded grey. Photoshop keeps the original pixel color
|
||||
// and applies highlight/shadow blends over it.
|
||||
let (highlight, shadow) = generate_bevel_emboss(
|
||||
&alpha, canvas_w, canvas_h,
|
||||
*depth, *size, *soften, *angle, *altitude,
|
||||
*direction, *technique, *style,
|
||||
*highlight_color, *shadow_color,
|
||||
&alpha,
|
||||
canvas_w,
|
||||
canvas_h,
|
||||
*depth,
|
||||
*size,
|
||||
*soften,
|
||||
*angle,
|
||||
*altitude,
|
||||
*direction,
|
||||
*technique,
|
||||
*style,
|
||||
*highlight_color,
|
||||
*shadow_color,
|
||||
contour,
|
||||
);
|
||||
|
||||
// For Emboss and OuterBevel, the effect extends outside the shape.
|
||||
// Inside pixels go to `inside`, outside pixels go to `behind`.
|
||||
let has_outer = matches!(*style, crate::types::BevelStyle::Emboss | crate::types::BevelStyle::OuterBevel);
|
||||
let has_outer = matches!(
|
||||
*style,
|
||||
crate::types::BevelStyle::Emboss | crate::types::BevelStyle::OuterBevel
|
||||
);
|
||||
if has_outer {
|
||||
// Split highlight/shadow into inside and outside portions
|
||||
let mut hl_inside = vec![0u8; px_count * 4];
|
||||
@@ -95,8 +127,20 @@ pub fn apply_layer_effects(
|
||||
sh_outside[i * 4..i * 4 + 4].copy_from_slice(&shadow[i * 4..i * 4 + 4]);
|
||||
}
|
||||
}
|
||||
composite_effect(&mut inside, &hl_inside, *highlight_blend, *highlight_opacity, px_count);
|
||||
composite_effect(&mut inside, &sh_inside, *shadow_blend, *shadow_opacity, px_count);
|
||||
composite_effect(
|
||||
&mut inside,
|
||||
&hl_inside,
|
||||
*highlight_blend,
|
||||
*highlight_opacity,
|
||||
px_count,
|
||||
);
|
||||
composite_effect(
|
||||
&mut inside,
|
||||
&sh_inside,
|
||||
*shadow_blend,
|
||||
*shadow_opacity,
|
||||
px_count,
|
||||
);
|
||||
// Store outside portions to apply to `behind` later
|
||||
bevel_hl_outside = Some(hl_outside);
|
||||
bevel_sh_outside = Some(sh_outside);
|
||||
@@ -105,65 +149,146 @@ pub fn apply_layer_effects(
|
||||
bevel_hl_op = *highlight_opacity;
|
||||
bevel_sh_op = *shadow_opacity;
|
||||
} else {
|
||||
composite_effect(&mut inside, &highlight, *highlight_blend, *highlight_opacity, px_count);
|
||||
composite_effect(&mut inside, &shadow, *shadow_blend, *shadow_opacity, px_count);
|
||||
composite_effect(
|
||||
&mut inside,
|
||||
&highlight,
|
||||
*highlight_blend,
|
||||
*highlight_opacity,
|
||||
px_count,
|
||||
);
|
||||
composite_effect(
|
||||
&mut inside,
|
||||
&shadow,
|
||||
*shadow_blend,
|
||||
*shadow_opacity,
|
||||
px_count,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// b. Satin
|
||||
if let Some(LayerEffect::Satin {
|
||||
enabled: true, blend_mode, color, opacity, angle, distance, size, invert, ..
|
||||
}) = effects.iter().find(|e| matches!(e, LayerEffect::Satin { .. })) {
|
||||
let satin = generate_satin(&alpha, canvas_w, canvas_h, *angle, *distance, *size, *color, *invert);
|
||||
enabled: true,
|
||||
blend_mode,
|
||||
color,
|
||||
opacity,
|
||||
angle,
|
||||
distance,
|
||||
size,
|
||||
invert,
|
||||
..
|
||||
}) = effects
|
||||
.iter()
|
||||
.find(|e| matches!(e, LayerEffect::Satin { .. }))
|
||||
{
|
||||
let satin = generate_satin(
|
||||
&alpha, canvas_w, canvas_h, *angle, *distance, *size, *color, *invert,
|
||||
);
|
||||
composite_effect(&mut inside, &satin, *blend_mode, *opacity, px_count);
|
||||
}
|
||||
|
||||
// c. Color Overlay
|
||||
if let Some(LayerEffect::ColorOverlay {
|
||||
enabled: true, blend_mode, color, opacity, ..
|
||||
}) = effects.iter().find(|e| matches!(e, LayerEffect::ColorOverlay { .. })) {
|
||||
enabled: true,
|
||||
blend_mode,
|
||||
color,
|
||||
opacity,
|
||||
..
|
||||
}) = effects
|
||||
.iter()
|
||||
.find(|e| matches!(e, LayerEffect::ColorOverlay { .. }))
|
||||
{
|
||||
let overlay = generate_color_overlay(&alpha, canvas_w, canvas_h, *color);
|
||||
composite_inside_effect(&mut inside, &overlay, *blend_mode, *opacity, px_count);
|
||||
}
|
||||
|
||||
// d. Gradient Overlay
|
||||
if let Some(LayerEffect::GradientOverlay {
|
||||
enabled: true, blend_mode, opacity, angle, scale, gradient, ..
|
||||
}) = effects.iter().find(|e| matches!(e, LayerEffect::GradientOverlay { .. })) {
|
||||
let overlay = generate_gradient_overlay(&alpha, canvas_w, canvas_h, *angle, *scale, gradient);
|
||||
enabled: true,
|
||||
blend_mode,
|
||||
opacity,
|
||||
angle,
|
||||
scale,
|
||||
gradient,
|
||||
..
|
||||
}) = effects
|
||||
.iter()
|
||||
.find(|e| matches!(e, LayerEffect::GradientOverlay { .. }))
|
||||
{
|
||||
let overlay =
|
||||
generate_gradient_overlay(&alpha, canvas_w, canvas_h, *angle, *scale, gradient);
|
||||
composite_inside_effect(&mut inside, &overlay, *blend_mode, *opacity, px_count);
|
||||
}
|
||||
|
||||
// e. Pattern Overlay
|
||||
if let Some(LayerEffect::PatternOverlay {
|
||||
enabled: true, blend_mode, opacity, ..
|
||||
}) = effects.iter().find(|e| matches!(e, LayerEffect::PatternOverlay { .. })) {
|
||||
enabled: true,
|
||||
blend_mode,
|
||||
opacity,
|
||||
..
|
||||
}) = effects
|
||||
.iter()
|
||||
.find(|e| matches!(e, LayerEffect::PatternOverlay { .. }))
|
||||
{
|
||||
let overlay = generate_color_overlay(&alpha, canvas_w, canvas_h, [128, 128, 128, 255]);
|
||||
composite_inside_effect(&mut inside, &overlay, *blend_mode, *opacity, px_count);
|
||||
}
|
||||
|
||||
// f. Inner Glow
|
||||
if let Some(LayerEffect::InnerGlow {
|
||||
enabled: true, blend_mode, color, opacity, choke, size, ..
|
||||
}) = effects.iter().find(|e| matches!(e, LayerEffect::InnerGlow { .. })) {
|
||||
enabled: true,
|
||||
blend_mode,
|
||||
color,
|
||||
opacity,
|
||||
choke,
|
||||
size,
|
||||
..
|
||||
}) = effects
|
||||
.iter()
|
||||
.find(|e| matches!(e, LayerEffect::InnerGlow { .. }))
|
||||
{
|
||||
let glow = generate_glow(&alpha, canvas_w, canvas_h, *choke, *size, *color, true);
|
||||
composite_effect(&mut inside, &glow, *blend_mode, *opacity, px_count);
|
||||
}
|
||||
|
||||
// g. Inner Shadow
|
||||
if let Some(LayerEffect::InnerShadow {
|
||||
enabled: true, blend_mode, color, opacity, angle, distance, choke, size, ..
|
||||
}) = effects.iter().find(|e| matches!(e, LayerEffect::InnerShadow { .. })) {
|
||||
let shadow = generate_inner_shadow(&alpha, canvas_w, canvas_h, *angle, *distance, *choke, *size, *color);
|
||||
enabled: true,
|
||||
blend_mode,
|
||||
color,
|
||||
opacity,
|
||||
angle,
|
||||
distance,
|
||||
choke,
|
||||
size,
|
||||
..
|
||||
}) = effects
|
||||
.iter()
|
||||
.find(|e| matches!(e, LayerEffect::InnerShadow { .. }))
|
||||
{
|
||||
let shadow = generate_inner_shadow(
|
||||
&alpha, canvas_w, canvas_h, *angle, *distance, *choke, *size, *color,
|
||||
);
|
||||
composite_effect(&mut inside, &shadow, *blend_mode, *opacity, px_count);
|
||||
}
|
||||
|
||||
// h. Stroke (Inside and Center — Outside handled in behind phase)
|
||||
if let Some(LayerEffect::Stroke {
|
||||
enabled: true, position, blend_mode, opacity, size, color, ..
|
||||
}) = effects.iter().find(|e| matches!(e, LayerEffect::Stroke { .. })) {
|
||||
enabled: true,
|
||||
position,
|
||||
blend_mode,
|
||||
opacity,
|
||||
size,
|
||||
color,
|
||||
..
|
||||
}) = effects
|
||||
.iter()
|
||||
.find(|e| matches!(e, LayerEffect::Stroke { .. }))
|
||||
{
|
||||
if !matches!(position, crate::types::StrokePosition::Outside) {
|
||||
let stroke = generate_stroke(&alpha, canvas_w, canvas_h, *size, *position, *color, *opacity);
|
||||
let stroke = generate_stroke(
|
||||
&alpha, canvas_w, canvas_h, *size, *position, *color, *opacity,
|
||||
);
|
||||
composite_inside_effect(&mut inside, &stroke, *blend_mode, 1.0, px_count);
|
||||
}
|
||||
}
|
||||
@@ -174,26 +299,59 @@ pub fn apply_layer_effects(
|
||||
// Behind effects — Photoshop spec order:
|
||||
// a. Drop Shadow
|
||||
if let Some(LayerEffect::DropShadow {
|
||||
enabled: true, blend_mode, color, opacity, angle, distance, spread, size, ..
|
||||
}) = effects.iter().find(|e| matches!(e, LayerEffect::DropShadow { .. })) {
|
||||
let shadow = generate_shadow(&alpha, canvas_w, canvas_h, *angle, *distance, *spread, *size, *color, false);
|
||||
enabled: true,
|
||||
blend_mode,
|
||||
color,
|
||||
opacity,
|
||||
angle,
|
||||
distance,
|
||||
spread,
|
||||
size,
|
||||
..
|
||||
}) = effects
|
||||
.iter()
|
||||
.find(|e| matches!(e, LayerEffect::DropShadow { .. }))
|
||||
{
|
||||
let shadow = generate_shadow(
|
||||
&alpha, canvas_w, canvas_h, *angle, *distance, *spread, *size, *color, false,
|
||||
);
|
||||
composite_effect(&mut behind, &shadow, *blend_mode, *opacity, px_count);
|
||||
}
|
||||
|
||||
// b. Outer Glow
|
||||
if let Some(LayerEffect::OuterGlow {
|
||||
enabled: true, blend_mode, color, opacity, spread, size, ..
|
||||
}) = effects.iter().find(|e| matches!(e, LayerEffect::OuterGlow { .. })) {
|
||||
enabled: true,
|
||||
blend_mode,
|
||||
color,
|
||||
opacity,
|
||||
spread,
|
||||
size,
|
||||
..
|
||||
}) = effects
|
||||
.iter()
|
||||
.find(|e| matches!(e, LayerEffect::OuterGlow { .. }))
|
||||
{
|
||||
let glow = generate_glow(&alpha, canvas_w, canvas_h, *spread, *size, *color, false);
|
||||
composite_effect(&mut behind, &glow, *blend_mode, *opacity, px_count);
|
||||
}
|
||||
|
||||
// c. Outside Stroke
|
||||
if let Some(LayerEffect::Stroke {
|
||||
enabled: true, position, blend_mode, opacity, size, color, ..
|
||||
}) = effects.iter().find(|e| matches!(e, LayerEffect::Stroke { .. })) {
|
||||
enabled: true,
|
||||
position,
|
||||
blend_mode,
|
||||
opacity,
|
||||
size,
|
||||
color,
|
||||
..
|
||||
}) = effects
|
||||
.iter()
|
||||
.find(|e| matches!(e, LayerEffect::Stroke { .. }))
|
||||
{
|
||||
if matches!(position, crate::types::StrokePosition::Outside) {
|
||||
let stroke = generate_stroke(&alpha, canvas_w, canvas_h, *size, *position, *color, *opacity);
|
||||
let stroke = generate_stroke(
|
||||
&alpha, canvas_w, canvas_h, *size, *position, *color, *opacity,
|
||||
);
|
||||
composite_effect(&mut behind, &stroke, *blend_mode, 1.0, px_count);
|
||||
}
|
||||
}
|
||||
@@ -209,8 +367,18 @@ pub fn apply_layer_effects(
|
||||
|
||||
// 3. Blend inside ON TOP of behind using Normal blend
|
||||
for i in 0..px_count {
|
||||
let b = [behind[i * 4], behind[i * 4 + 1], behind[i * 4 + 2], behind[i * 4 + 3]];
|
||||
let ins = [inside[i * 4], inside[i * 4 + 1], inside[i * 4 + 2], inside[i * 4 + 3]];
|
||||
let b = [
|
||||
behind[i * 4],
|
||||
behind[i * 4 + 1],
|
||||
behind[i * 4 + 2],
|
||||
behind[i * 4 + 3],
|
||||
];
|
||||
let ins = [
|
||||
inside[i * 4],
|
||||
inside[i * 4 + 1],
|
||||
inside[i * 4 + 2],
|
||||
inside[i * 4 + 3],
|
||||
];
|
||||
if ins[3] == 0 {
|
||||
behind[i * 4..i * 4 + 4].copy_from_slice(&b);
|
||||
} else {
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
pub fn generate_color_overlay(
|
||||
alpha: &[u8],
|
||||
w: u32,
|
||||
h: u32,
|
||||
color: [u8; 4],
|
||||
) -> Vec<u8> {
|
||||
pub fn generate_color_overlay(alpha: &[u8], w: u32, h: u32, color: [u8; 4]) -> Vec<u8> {
|
||||
let px = (w * h) as usize;
|
||||
let mut buf = vec![0u8; px * 4];
|
||||
|
||||
@@ -18,4 +13,4 @@ pub fn generate_color_overlay(
|
||||
}
|
||||
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
///
|
||||
/// Renders a drop shadow by offsetting the layer alpha, applying spread,
|
||||
/// blurring, and filling with the shadow color. Supports knock-out mode.
|
||||
|
||||
use crate::helpers::{apply_spread, box_blur_f32};
|
||||
|
||||
/// Generate a drop shadow buffer.
|
||||
@@ -56,7 +55,9 @@ pub(crate) fn generate_shadow(
|
||||
for y in 0..h as i32 {
|
||||
for x in 0..w as i32 {
|
||||
let a = alpha[(y as u32 * w + x as u32) as usize];
|
||||
if a == 0 { continue; }
|
||||
if a == 0 {
|
||||
continue;
|
||||
}
|
||||
let dx = x + dx_raw;
|
||||
let dy = y + dy_raw;
|
||||
if dx >= 0 && dx < w as i32 && dy >= 0 && dy < h as i32 {
|
||||
|
||||
+81
-25
@@ -1,5 +1,5 @@
|
||||
use crate::types;
|
||||
use crate::helpers::box_blur_f32;
|
||||
use crate::types;
|
||||
|
||||
/// Look up a value in a contour curve using linear interpolation.
|
||||
/// input is in [0.0, 1.0], output is remapped through the curve.
|
||||
@@ -96,7 +96,10 @@ pub fn generate_bevel_emboss(
|
||||
// the outer bevel band can inherit the alpha of the nearest content pixel.
|
||||
// This is critical: without it, alpha[i]==0 outside the shape produces
|
||||
// zero-strength highlight/shadow, making Emboss/OuterBevel invisible.
|
||||
let outside_src = if matches!(style, types::BevelStyle::Emboss | types::BevelStyle::OuterBevel) {
|
||||
let outside_src = if matches!(
|
||||
style,
|
||||
types::BevelStyle::Emboss | types::BevelStyle::OuterBevel
|
||||
) {
|
||||
edt_outside_src(alpha, iw, ih)
|
||||
} else {
|
||||
vec![0usize; px]
|
||||
@@ -112,10 +115,18 @@ pub fn generate_bevel_emboss(
|
||||
if alpha[i] > 0 {
|
||||
let x = i % iw;
|
||||
let y = i / iw;
|
||||
if x < min_x { min_x = x; }
|
||||
if x > max_x { max_x = x; }
|
||||
if y < min_y { min_y = y; }
|
||||
if y > max_y { max_y = y; }
|
||||
if x < min_x {
|
||||
min_x = x;
|
||||
}
|
||||
if x > max_x {
|
||||
max_x = x;
|
||||
}
|
||||
if y < min_y {
|
||||
min_y = y;
|
||||
}
|
||||
if y > max_y {
|
||||
max_y = y;
|
||||
}
|
||||
}
|
||||
}
|
||||
((min_x + max_x) as f32 / 2.0, (min_y + max_y) as f32 / 2.0)
|
||||
@@ -180,7 +191,11 @@ pub fn generate_bevel_emboss(
|
||||
// Apply soften box-blur on the height field.
|
||||
// Even at soften=0, apply minimal 2px blur for smoother gradient estimation at edges.
|
||||
// Emboss benefits from more blur to reduce wrinkled edges on large shapes.
|
||||
let base_blur = if matches!(style, types::BevelStyle::Emboss) { 2 } else { 1 };
|
||||
let base_blur = if matches!(style, types::BevelStyle::Emboss) {
|
||||
2
|
||||
} else {
|
||||
1
|
||||
};
|
||||
let soften_radius = (soften.max(0.0).round() as i32).max(base_blur);
|
||||
height = box_blur_f32(&height, w, h, soften_radius);
|
||||
|
||||
@@ -200,20 +215,32 @@ pub fn generate_bevel_emboss(
|
||||
types::BevelStyle::Emboss => true,
|
||||
_ => alpha[i] > 0,
|
||||
};
|
||||
if !show { continue; }
|
||||
if !show {
|
||||
continue;
|
||||
}
|
||||
|
||||
let is_inner = alpha[i] > 0;
|
||||
let dist_abs = if is_inner { dist_inside_sq[i].sqrt() } else { dist_outside_sq[i].sqrt() };
|
||||
let dist_abs = if is_inner {
|
||||
dist_inside_sq[i].sqrt()
|
||||
} else {
|
||||
dist_outside_sq[i].sqrt()
|
||||
};
|
||||
|
||||
if !is_inner && dist_abs > radius { continue; }
|
||||
if !is_inner && dist_abs > radius {
|
||||
continue;
|
||||
}
|
||||
|
||||
let technique_show = match technique {
|
||||
types::Technique::Smooth => true,
|
||||
_ => dist_abs <= radius,
|
||||
};
|
||||
if !technique_show { continue; }
|
||||
if !technique_show {
|
||||
continue;
|
||||
}
|
||||
|
||||
if is_inner && dist_abs > radius { continue; }
|
||||
if is_inner && dist_abs > radius {
|
||||
continue;
|
||||
}
|
||||
|
||||
let x0 = if x > 0 { x - 1 } else { x };
|
||||
let x1 = if x < iw - 1 { x + 1 } else { x };
|
||||
@@ -250,7 +277,11 @@ pub fn generate_bevel_emboss(
|
||||
// Inner/Outer bevel need blur for smooth corners.
|
||||
// MAE-optimized against Photoshop ground truth (288 emboss samples, 256×256).
|
||||
// BEGIN_TUNING_ZONE
|
||||
let lighting_blur = if matches!(style, crate::types::BevelStyle::Emboss) { 0 } else { 5 };
|
||||
let lighting_blur = if matches!(style, crate::types::BevelStyle::Emboss) {
|
||||
0
|
||||
} else {
|
||||
5
|
||||
};
|
||||
lighting = box_blur_f32(&lighting, w, h, lighting_blur);
|
||||
|
||||
for i in 0..px {
|
||||
@@ -261,20 +292,31 @@ pub fn generate_bevel_emboss(
|
||||
crate::types::BevelStyle::Emboss => true,
|
||||
_ => is_inner,
|
||||
};
|
||||
if !show { continue; }
|
||||
let dist_abs = if is_inner { dist_inside_sq[i].sqrt() } else { dist_outside_sq[i].sqrt() };
|
||||
if !is_inner && dist_abs > radius { continue; }
|
||||
if !show {
|
||||
continue;
|
||||
}
|
||||
let dist_abs = if is_inner {
|
||||
dist_inside_sq[i].sqrt()
|
||||
} else {
|
||||
dist_outside_sq[i].sqrt()
|
||||
};
|
||||
if !is_inner && dist_abs > radius {
|
||||
continue;
|
||||
}
|
||||
let technique_show = match technique {
|
||||
crate::types::Technique::Smooth => true,
|
||||
_ => dist_abs <= radius,
|
||||
};
|
||||
if !technique_show { continue; }
|
||||
if !technique_show {
|
||||
continue;
|
||||
}
|
||||
|
||||
let ndl = lighting[i];
|
||||
// Emboss has tilt so the interior is never truly flat — skip the
|
||||
// is_flat shortcut for emboss to allow tilt-based highlight/shadow
|
||||
// across the entire shape interior.
|
||||
let is_flat = is_inner && dist_abs > radius && !matches!(style, crate::types::BevelStyle::Emboss);
|
||||
let is_flat =
|
||||
is_inner && dist_abs > radius && !matches!(style, crate::types::BevelStyle::Emboss);
|
||||
if is_flat {
|
||||
continue;
|
||||
}
|
||||
@@ -417,7 +459,9 @@ fn edt_2d(dt: &mut [f32], w: usize, h: usize) {
|
||||
/// 1D EDT (Felzenszwalb & Huttenlocher).
|
||||
fn edt_1d(f: &mut [f32]) {
|
||||
let n = f.len();
|
||||
if n == 0 { return; }
|
||||
if n == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut d = vec![0.0f32; n];
|
||||
let mut v = vec![0usize; n];
|
||||
@@ -437,7 +481,10 @@ fn edt_1d(f: &mut [f32]) {
|
||||
let mut s = (fq - fv_vk) / (2.0 * qf - 2.0 * vk as f32);
|
||||
while s <= z[k] {
|
||||
k = k.wrapping_sub(1);
|
||||
if k == usize::MAX { k = 0; break; }
|
||||
if k == usize::MAX {
|
||||
k = 0;
|
||||
break;
|
||||
}
|
||||
let vk2 = v[k];
|
||||
let fv_vk2 = f[vk2] + vk2 as f32 * vk2 as f32;
|
||||
s = (fq - fv_vk2) / (2.0 * qf - 2.0 * vk2 as f32);
|
||||
@@ -451,7 +498,9 @@ fn edt_1d(f: &mut [f32]) {
|
||||
k = 0;
|
||||
for q in 0..n {
|
||||
let qf = q as f32;
|
||||
while z[k + 1] < qf { k += 1; }
|
||||
while z[k + 1] < qf {
|
||||
k += 1;
|
||||
}
|
||||
let vk = v[k];
|
||||
let diff = qf - vk as f32;
|
||||
d[q] = diff * diff + f[vk];
|
||||
@@ -494,7 +543,9 @@ fn edt_2d_src(dt: &mut [f32], src_idx: &mut [usize], w: usize, h: usize) {
|
||||
/// 1D EDT with source index tracking (Felzenszwalb & Huttenlocher).
|
||||
fn edt_1d_src(f: &mut [f32], src_idx: &mut [usize]) {
|
||||
let n = f.len();
|
||||
if n == 0 { return; }
|
||||
if n == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut d = vec![0.0f32; n];
|
||||
let mut d_src = vec![0usize; n];
|
||||
@@ -515,7 +566,10 @@ fn edt_1d_src(f: &mut [f32], src_idx: &mut [usize]) {
|
||||
let mut s = (fq - fv_vk) / (2.0 * qf - 2.0 * vk as f32);
|
||||
while s <= z[k] {
|
||||
k = k.wrapping_sub(1);
|
||||
if k == usize::MAX { k = 0; break; }
|
||||
if k == usize::MAX {
|
||||
k = 0;
|
||||
break;
|
||||
}
|
||||
let vk2 = v[k];
|
||||
let fv_vk2 = f[vk2] + vk2 as f32 * vk2 as f32;
|
||||
s = (fq - fv_vk2) / (2.0 * qf - 2.0 * vk2 as f32);
|
||||
@@ -529,7 +583,9 @@ fn edt_1d_src(f: &mut [f32], src_idx: &mut [usize]) {
|
||||
k = 0;
|
||||
for q in 0..n {
|
||||
let qf = q as f32;
|
||||
while z[k + 1] < qf { k += 1; }
|
||||
while z[k + 1] < qf {
|
||||
k += 1;
|
||||
}
|
||||
let vk = v[k];
|
||||
let diff = qf - vk as f32;
|
||||
d[q] = diff * diff + f[vk];
|
||||
@@ -538,4 +594,4 @@ fn edt_1d_src(f: &mut [f32], src_idx: &mut [usize]) {
|
||||
|
||||
f.copy_from_slice(&d);
|
||||
src_idx.copy_from_slice(&d_src);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,9 @@ pub fn generate_gradient_overlay(
|
||||
for x in 0..w as usize {
|
||||
let i = y * w as usize + x;
|
||||
let a = alpha[i];
|
||||
if a == 0 { continue; }
|
||||
if a == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let fx = x as f32 - cx;
|
||||
let fy = y as f32 - cy;
|
||||
@@ -89,4 +91,4 @@ fn sample_gradient(t: f32, gradient: &crate::types::GradientDef) -> [u8; 4] {
|
||||
}
|
||||
|
||||
stops[stops.len() - 1].1
|
||||
}
|
||||
}
|
||||
|
||||
+23
-7
@@ -40,8 +40,15 @@ pub(crate) fn composite_effect(
|
||||
) {
|
||||
for i in 0..px_count {
|
||||
let dst_px = [dst[i * 4], dst[i * 4 + 1], dst[i * 4 + 2], dst[i * 4 + 3]];
|
||||
let src_px = [effect_buf[i * 4], effect_buf[i * 4 + 1], effect_buf[i * 4 + 2], effect_buf[i * 4 + 3]];
|
||||
if src_px[3] == 0 { continue; }
|
||||
let src_px = [
|
||||
effect_buf[i * 4],
|
||||
effect_buf[i * 4 + 1],
|
||||
effect_buf[i * 4 + 2],
|
||||
effect_buf[i * 4 + 3],
|
||||
];
|
||||
if src_px[3] == 0 {
|
||||
continue;
|
||||
}
|
||||
let out = hcie_blend::blend_pixels(dst_px, src_px, blend_mode, opacity);
|
||||
dst[i * 4..i * 4 + 4].copy_from_slice(&out);
|
||||
}
|
||||
@@ -68,11 +75,18 @@ pub(crate) fn composite_inside_effect(
|
||||
) {
|
||||
for i in 0..px_count {
|
||||
let dst_px = [dst[i * 4], dst[i * 4 + 1], dst[i * 4 + 2], dst[i * 4 + 3]];
|
||||
let src_px = [effect_buf[i * 4], effect_buf[i * 4 + 1], effect_buf[i * 4 + 2], effect_buf[i * 4 + 3]];
|
||||
if src_px[3] == 0 || dst_px[3] == 0 { continue; }
|
||||
|
||||
let src_px = [
|
||||
effect_buf[i * 4],
|
||||
effect_buf[i * 4 + 1],
|
||||
effect_buf[i * 4 + 2],
|
||||
effect_buf[i * 4 + 3],
|
||||
];
|
||||
if src_px[3] == 0 || dst_px[3] == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let eff_sa = (src_px[3] as f32 / 255.0) * opacity;
|
||||
|
||||
|
||||
if blend_mode == hcie_blend::BlendMode::Normal {
|
||||
if eff_sa >= 1.0 {
|
||||
dst[i * 4] = src_px[0];
|
||||
@@ -110,7 +124,9 @@ pub(crate) fn apply_spread(alpha: &mut [u8], spread: f32) {
|
||||
}
|
||||
if spread >= 100.0 {
|
||||
for a in alpha.iter_mut() {
|
||||
if *a > 0 { *a = 255; }
|
||||
if *a > 0 {
|
||||
*a = 255;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
/// Renders an inner shadow by offsetting the inverted layer alpha,
|
||||
/// blurring, and masking with the original alpha. MAE-optimized
|
||||
/// parameters for Photoshop-compatible output.
|
||||
|
||||
use crate::helpers::{apply_spread, box_blur_f32};
|
||||
|
||||
/// Generate an inner shadow buffer.
|
||||
@@ -54,7 +53,7 @@ pub(crate) fn generate_inner_shadow(
|
||||
for x in 0..w as i32 {
|
||||
let orig_i = (y as u32 * w + x as u32) as usize;
|
||||
let val = 255 - alpha[orig_i];
|
||||
|
||||
|
||||
let dx = x + dx_raw;
|
||||
let dy = y + dy_raw;
|
||||
if dx >= 0 && dx < w as i32 && dy >= 0 && dy < h as i32 {
|
||||
|
||||
+14
-11
@@ -25,22 +25,25 @@
|
||||
//! ## Dependencies
|
||||
//! - `hcie-blend` — Blend mode math for compositing effects with layers
|
||||
|
||||
pub mod types;
|
||||
pub mod parser;
|
||||
pub mod helpers;
|
||||
pub mod apply_effects;
|
||||
pub mod drop_shadow;
|
||||
pub mod inner_shadow;
|
||||
pub mod emboss;
|
||||
pub mod outer_glow;
|
||||
pub mod color_overlay;
|
||||
pub mod satin;
|
||||
pub mod drop_shadow;
|
||||
pub mod emboss;
|
||||
pub mod gradient_overlay;
|
||||
pub mod helpers;
|
||||
pub mod inner_shadow;
|
||||
pub mod outer_glow;
|
||||
pub mod parser;
|
||||
pub mod satin;
|
||||
pub mod stroke;
|
||||
pub mod tuned;
|
||||
pub mod types;
|
||||
|
||||
pub use types::LayerEffect;
|
||||
pub use types::{blend_mode_to_string, blend_mode_from_string, protocol_to_hcie_fx_effect, hcie_fx_effect_to_protocol, layer_style_to_effect};
|
||||
pub use parser::{parse_lfx2, parse_lrFX, parse_asl, parse_asl_styles};
|
||||
pub use apply_effects::apply_layer_effects;
|
||||
pub use helpers::box_blur_f32;
|
||||
pub use parser::{parse_asl, parse_asl_styles, parse_lfx2, parse_lrFX};
|
||||
pub use types::LayerEffect;
|
||||
pub use types::{
|
||||
blend_mode_from_string, blend_mode_to_string, hcie_fx_effect_to_protocol,
|
||||
layer_style_to_effect, protocol_to_hcie_fx_effect,
|
||||
};
|
||||
|
||||
@@ -73,4 +73,4 @@ pub fn generate_glow(
|
||||
}
|
||||
}
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
+534
-168
File diff suppressed because it is too large
Load Diff
@@ -74,4 +74,4 @@ pub fn generate_satin(
|
||||
}
|
||||
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
+14
-6
@@ -29,9 +29,13 @@ pub fn generate_stroke(
|
||||
crate::types::StrokePosition::Inside => {
|
||||
let dist_sq = crate::tuned::edt_inside(alpha, iw, ih);
|
||||
for i in 0..px {
|
||||
if alpha[i] == 0 { continue; }
|
||||
if alpha[i] == 0 {
|
||||
continue;
|
||||
}
|
||||
let d = dist_sq[i].sqrt();
|
||||
if d >= radius { continue; }
|
||||
if d >= radius {
|
||||
continue;
|
||||
}
|
||||
let outer_dist = radius - d;
|
||||
let coverage = (outer_dist / aa).clamp(0.0, 1.0);
|
||||
stroke_alpha[i] = coverage * alpha[i] as f32;
|
||||
@@ -42,7 +46,9 @@ pub fn generate_stroke(
|
||||
let nearest = crate::tuned::edt_outside_src(alpha, iw, ih);
|
||||
for i in 0..px {
|
||||
let d = dist_sq[i].sqrt();
|
||||
if d == 0.0 || d >= radius { continue; }
|
||||
if d == 0.0 || d >= radius {
|
||||
continue;
|
||||
}
|
||||
let x_i = (i % iw) as f32;
|
||||
let y_i = (i / iw) as f32;
|
||||
let x_n = (nearest[i] % iw) as f32;
|
||||
@@ -76,8 +82,10 @@ pub fn generate_stroke(
|
||||
} else {
|
||||
(dist_out_sq[i].sqrt(), false)
|
||||
};
|
||||
if d == 0.0 || d >= half_r { continue; }
|
||||
|
||||
if d == 0.0 || d >= half_r {
|
||||
continue;
|
||||
}
|
||||
|
||||
let src_a = if is_inside {
|
||||
alpha[i] as f32
|
||||
} else {
|
||||
@@ -123,4 +131,4 @@ pub fn generate_stroke(
|
||||
}
|
||||
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
+109
-35
@@ -36,7 +36,10 @@ pub fn generate_bevel_tuned(
|
||||
|
||||
// For Emboss and OuterBevel, track the nearest opaque source pixel so
|
||||
// the outer bevel band can inherit the alpha of the nearest content pixel.
|
||||
let outside_src = if matches!(*style, types::BevelStyle::Emboss | types::BevelStyle::OuterBevel) {
|
||||
let outside_src = if matches!(
|
||||
*style,
|
||||
types::BevelStyle::Emboss | types::BevelStyle::OuterBevel
|
||||
) {
|
||||
edt_outside_src(alpha, iw, ih)
|
||||
} else {
|
||||
vec![0usize; px]
|
||||
@@ -52,10 +55,18 @@ pub fn generate_bevel_tuned(
|
||||
if alpha[i] > 0 {
|
||||
let x = i % iw;
|
||||
let y = i / iw;
|
||||
if x < min_x { min_x = x; }
|
||||
if x > max_x { max_x = x; }
|
||||
if y < min_y { min_y = y; }
|
||||
if y > max_y { max_y = y; }
|
||||
if x < min_x {
|
||||
min_x = x;
|
||||
}
|
||||
if x > max_x {
|
||||
max_x = x;
|
||||
}
|
||||
if y < min_y {
|
||||
min_y = y;
|
||||
}
|
||||
if y > max_y {
|
||||
max_y = y;
|
||||
}
|
||||
}
|
||||
}
|
||||
((min_x + max_x) as f32 / 2.0, (min_y + max_y) as f32 / 2.0)
|
||||
@@ -134,11 +145,19 @@ pub fn generate_bevel_tuned(
|
||||
types::BevelStyle::Emboss => true,
|
||||
_ => alpha[i] > 0,
|
||||
};
|
||||
if !show { continue; }
|
||||
if !show {
|
||||
continue;
|
||||
}
|
||||
let is_inner = alpha[i] > 0;
|
||||
let dist_abs = if is_inner { dist_inside_sq[i].sqrt() } else { dist_outside_sq[i].sqrt() };
|
||||
if !is_inner && dist_abs > radius { continue; }
|
||||
let x0 = if x > 0 { x - 1 } else { x };
|
||||
let dist_abs = if is_inner {
|
||||
dist_inside_sq[i].sqrt()
|
||||
} else {
|
||||
dist_outside_sq[i].sqrt()
|
||||
};
|
||||
if !is_inner && dist_abs > radius {
|
||||
continue;
|
||||
}
|
||||
let x0 = if x > 0 { x - 1 } else { x };
|
||||
let x1 = if x < iw - 1 { x + 1 } else { x };
|
||||
let y0 = if y > 0 { y - 1 } else { y };
|
||||
let y1 = if y < ih - 1 { y + 1 } else { y };
|
||||
@@ -177,9 +196,17 @@ pub fn generate_bevel_tuned(
|
||||
types::BevelStyle::Emboss => true,
|
||||
_ => is_inner,
|
||||
};
|
||||
if !show { continue; }
|
||||
let dist_abs = if is_inner { dist_inside_sq[i].sqrt() } else { dist_outside_sq[i].sqrt() };
|
||||
if !is_inner && dist_abs > radius { continue; }
|
||||
if !show {
|
||||
continue;
|
||||
}
|
||||
let dist_abs = if is_inner {
|
||||
dist_inside_sq[i].sqrt()
|
||||
} else {
|
||||
dist_outside_sq[i].sqrt()
|
||||
};
|
||||
if !is_inner && dist_abs > radius {
|
||||
continue;
|
||||
}
|
||||
let ndl = lighting[i];
|
||||
// Emboss has tilt so the interior is never truly flat — skip the
|
||||
// is_flat shortcut for emboss to allow tilt-based highlight/shadow
|
||||
@@ -325,7 +352,9 @@ pub fn generate_shadow_tuned(
|
||||
for y in 0..h as i32 {
|
||||
for x in 0..w as i32 {
|
||||
let a = alpha[(y as u32 * w + x as u32) as usize];
|
||||
if a == 0 { continue; }
|
||||
if a == 0 {
|
||||
continue;
|
||||
}
|
||||
let dx = x + dx_raw;
|
||||
let dy = y + dy_raw;
|
||||
if dx >= 0 && dx < w as i32 && dy >= 0 && dy < h as i32 {
|
||||
@@ -587,9 +616,13 @@ pub fn generate_stroke_tuned(
|
||||
types::StrokePosition::Inside => {
|
||||
let dist_sq = edt_inside(alpha, iw, ih);
|
||||
for i in 0..px {
|
||||
if alpha[i] == 0 { continue; }
|
||||
if alpha[i] == 0 {
|
||||
continue;
|
||||
}
|
||||
let d = dist_sq[i].sqrt();
|
||||
if d >= radius { continue; }
|
||||
if d >= radius {
|
||||
continue;
|
||||
}
|
||||
let outer_dist = radius - d; // distance to outer stroke edge
|
||||
let coverage = (outer_dist / aa).clamp(0.0, 1.0);
|
||||
stroke_alpha[i] = coverage * alpha[i] as f32;
|
||||
@@ -600,7 +633,9 @@ pub fn generate_stroke_tuned(
|
||||
let nearest = edt_outside_src(alpha, iw, ih);
|
||||
for i in 0..px {
|
||||
let d = dist_sq[i].sqrt();
|
||||
if d == 0.0 || d >= radius { continue; }
|
||||
if d == 0.0 || d >= radius {
|
||||
continue;
|
||||
}
|
||||
let src_a = alpha[nearest[i]] as f32;
|
||||
let outer_dist = radius - d; // distance to outer stroke edge
|
||||
let coverage = (outer_dist / aa).clamp(0.0, 1.0);
|
||||
@@ -618,7 +653,9 @@ pub fn generate_stroke_tuned(
|
||||
} else {
|
||||
(dist_out_sq[i].sqrt(), alpha[nearest[i]] as f32)
|
||||
};
|
||||
if d == 0.0 || d >= half_r { continue; }
|
||||
if d == 0.0 || d >= half_r {
|
||||
continue;
|
||||
}
|
||||
let outer_dist = half_r - d; // distance to outer stroke edge
|
||||
let coverage = (outer_dist / aa).clamp(0.0, 1.0);
|
||||
stroke_alpha[i] = coverage * base_a;
|
||||
@@ -653,7 +690,9 @@ pub fn edt_inside(alpha: &[u8], w: usize, h: usize) -> Vec<f32> {
|
||||
for y in 0..h {
|
||||
for x in 0..w {
|
||||
let i = y * w + x;
|
||||
if alpha[i] == 0 { dt[i] = 0.0; }
|
||||
if alpha[i] == 0 {
|
||||
dt[i] = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
edt_2d(&mut dt, w, h);
|
||||
@@ -670,7 +709,9 @@ pub fn edt_outside(alpha: &[u8], w: usize, h: usize) -> Vec<f32> {
|
||||
for y in 0..h {
|
||||
for x in 0..w {
|
||||
let i = y * w + x;
|
||||
if alpha[i] > 0 { dt[i] = 0.0; }
|
||||
if alpha[i] > 0 {
|
||||
dt[i] = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
edt_2d(&mut dt, w, h);
|
||||
@@ -696,7 +737,9 @@ pub fn edt_outside_src(alpha: &[u8], w: usize, h: usize) -> Vec<usize> {
|
||||
|
||||
fn edt_1d_src(f: &mut [f32], src_idx: &mut [usize]) {
|
||||
let n = f.len();
|
||||
if n == 0 { return; }
|
||||
if n == 0 {
|
||||
return;
|
||||
}
|
||||
let mut d = vec![0.0f32; n];
|
||||
let mut d_src = vec![0usize; n];
|
||||
let mut v = vec![0usize; n];
|
||||
@@ -713,7 +756,10 @@ fn edt_1d_src(f: &mut [f32], src_idx: &mut [usize]) {
|
||||
let mut s = (fq - fv_vk) / (2.0 * qf - 2.0 * vk as f32);
|
||||
while s <= z[k] {
|
||||
k = k.wrapping_sub(1);
|
||||
if k == usize::MAX { k = 0; break; }
|
||||
if k == usize::MAX {
|
||||
k = 0;
|
||||
break;
|
||||
}
|
||||
let vk2 = v[k];
|
||||
let fv_vk2 = f[vk2] + vk2 as f32 * vk2 as f32;
|
||||
s = (fq - fv_vk2) / (2.0 * qf - 2.0 * vk2 as f32);
|
||||
@@ -726,7 +772,9 @@ fn edt_1d_src(f: &mut [f32], src_idx: &mut [usize]) {
|
||||
k = 0;
|
||||
for q in 0..n {
|
||||
let qf = q as f32;
|
||||
while z[k + 1] < qf { k += 1; }
|
||||
while z[k + 1] < qf {
|
||||
k += 1;
|
||||
}
|
||||
let vk = v[k];
|
||||
let diff = qf - vk as f32;
|
||||
d[q] = diff * diff + f[vk];
|
||||
@@ -740,39 +788,60 @@ fn edt_2d_src(dt: &mut [f32], src_idx: &mut [usize], w: usize, h: usize) {
|
||||
let mut col = vec![0.0f32; h];
|
||||
let mut col_src = vec![0usize; h];
|
||||
for x in 0..w {
|
||||
for y in 0..h { col[y] = dt[y * w + x]; col_src[y] = src_idx[y * w + x]; }
|
||||
for y in 0..h {
|
||||
col[y] = dt[y * w + x];
|
||||
col_src[y] = src_idx[y * w + x];
|
||||
}
|
||||
edt_1d_src(&mut col, &mut col_src);
|
||||
for y in 0..h { dt[y * w + x] = col[y]; src_idx[y * w + x] = col_src[y]; }
|
||||
for y in 0..h {
|
||||
dt[y * w + x] = col[y];
|
||||
src_idx[y * w + x] = col_src[y];
|
||||
}
|
||||
}
|
||||
let mut row = vec![0.0f32; w];
|
||||
let mut row_src = vec![0usize; w];
|
||||
for y in 0..h {
|
||||
for x in 0..w { row[x] = dt[y * w + x]; row_src[x] = src_idx[y * w + x]; }
|
||||
for x in 0..w {
|
||||
row[x] = dt[y * w + x];
|
||||
row_src[x] = src_idx[y * w + x];
|
||||
}
|
||||
edt_1d_src(&mut row, &mut row_src);
|
||||
for x in 0..w { dt[y * w + x] = row[x]; src_idx[y * w + x] = row_src[x]; }
|
||||
for x in 0..w {
|
||||
dt[y * w + x] = row[x];
|
||||
src_idx[y * w + x] = row_src[x];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn edt_2d(dt: &mut [f32], w: usize, h: usize) {
|
||||
let mut col = vec![0.0f32; h];
|
||||
for x in 0..w {
|
||||
for y in 0..h { col[y] = dt[y * w + x]; }
|
||||
for y in 0..h {
|
||||
col[y] = dt[y * w + x];
|
||||
}
|
||||
edt_1d(&mut col);
|
||||
for y in 0..h { dt[y * w + x] = col[y]; }
|
||||
for y in 0..h {
|
||||
dt[y * w + x] = col[y];
|
||||
}
|
||||
}
|
||||
|
||||
let mut row = vec![0.0f32; w];
|
||||
for y in 0..h {
|
||||
for x in 0..w { row[x] = dt[y * w + x]; }
|
||||
for x in 0..w {
|
||||
row[x] = dt[y * w + x];
|
||||
}
|
||||
edt_1d(&mut row);
|
||||
for x in 0..w { dt[y * w + x] = row[x]; }
|
||||
for x in 0..w {
|
||||
dt[y * w + x] = row[x];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn edt_1d(f: &mut [f32]) {
|
||||
let n = f.len();
|
||||
if n == 0 { return; }
|
||||
if n == 0 {
|
||||
return;
|
||||
}
|
||||
let mut d = vec![0.0f32; n];
|
||||
let mut v = vec![0usize; n];
|
||||
let mut z = vec![0.0f32; n + 1];
|
||||
@@ -788,7 +857,10 @@ fn edt_1d(f: &mut [f32]) {
|
||||
let mut s = (fq - fv_vk) / (2.0 * qf - 2.0 * vk as f32);
|
||||
while s <= z[k] {
|
||||
k = k.wrapping_sub(1);
|
||||
if k == usize::MAX { k = 0; break; }
|
||||
if k == usize::MAX {
|
||||
k = 0;
|
||||
break;
|
||||
}
|
||||
let vk2 = v[k];
|
||||
let fv_vk2 = f[vk2] + vk2 as f32 * vk2 as f32;
|
||||
s = (fq - fv_vk2) / (2.0 * qf - 2.0 * vk2 as f32);
|
||||
@@ -801,10 +873,12 @@ fn edt_1d(f: &mut [f32]) {
|
||||
k = 0;
|
||||
for q in 0..n {
|
||||
let qf = q as f32;
|
||||
while z[k + 1] < qf { k += 1; }
|
||||
while z[k + 1] < qf {
|
||||
k += 1;
|
||||
}
|
||||
let vk = v[k];
|
||||
let diff = qf - vk as f32;
|
||||
d[q] = diff * diff + f[vk];
|
||||
}
|
||||
f.copy_from_slice(&d);
|
||||
}
|
||||
}
|
||||
|
||||
+664
-194
@@ -2,19 +2,40 @@ use hcie_blend::BlendMode;
|
||||
use hcie_protocol::effects;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum BevelStyle { InnerBevel, OuterBevel, Emboss, PillowEmboss, StrokeEmboss }
|
||||
pub enum BevelStyle {
|
||||
InnerBevel,
|
||||
OuterBevel,
|
||||
Emboss,
|
||||
PillowEmboss,
|
||||
StrokeEmboss,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Technique { Smooth, ChiselHard, ChiselSoft }
|
||||
pub enum Technique {
|
||||
Smooth,
|
||||
ChiselHard,
|
||||
ChiselSoft,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Direction { Up, Down }
|
||||
pub enum Direction {
|
||||
Up,
|
||||
Down,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum StrokePosition { Inside, Center, Outside }
|
||||
pub enum StrokePosition {
|
||||
Inside,
|
||||
Center,
|
||||
Outside,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum StrokeFillType { Color, Gradient, Pattern }
|
||||
pub enum StrokeFillType {
|
||||
Color,
|
||||
Gradient,
|
||||
Pattern,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ContourCurve {
|
||||
@@ -25,20 +46,74 @@ pub struct ContourCurve {
|
||||
pub fn contour_name_to_curve(name: &str) -> Option<ContourCurve> {
|
||||
match name {
|
||||
"Linear" => None, // Linear is the default (no remapping needed)
|
||||
"Cone" => Some(ContourCurve { points: vec![(0.0, 0.0), (0.5, 1.0), (1.0, 0.0)] }),
|
||||
"Cone-Inverted" => Some(ContourCurve { points: vec![(0.0, 1.0), (0.5, 0.0), (1.0, 1.0)] }),
|
||||
"Gaussian" => Some(ContourCurve { points: vec![(0.0, 0.0), (0.5, 0.85), (1.0, 1.0)] }),
|
||||
"Half Round" => Some(ContourCurve { points: vec![(0.0, 0.0), (0.25, 0.75), (0.5, 1.0), (0.75, 0.75), (1.0, 0.0)] }),
|
||||
"Round" => Some(ContourCurve { points: vec![(0.0, 0.0), (0.5, 1.0), (1.0, 0.0)] }),
|
||||
"Ring" => Some(ContourCurve { points: vec![(0.0, 0.0), (0.25, 1.0), (0.5, 0.0), (0.75, 1.0), (1.0, 0.0)] }),
|
||||
"Ring-Double" => Some(ContourCurve { points: vec![(0.0, 0.0), (0.125, 1.0), (0.25, 0.0), (0.375, 1.0), (0.5, 0.0), (0.625, 1.0), (0.75, 0.0), (0.875, 1.0), (1.0, 0.0)] }),
|
||||
"Sawtooth" => Some(ContourCurve { points: vec![(0.0, 0.0), (0.5, 1.0), (0.5, 0.0), (1.0, 1.0)] }),
|
||||
"Square" => Some(ContourCurve { points: vec![(0.0, 0.0), (0.5, 0.0), (0.5, 1.0), (1.0, 1.0)] }),
|
||||
"Valley" => Some(ContourCurve { points: vec![(0.0, 1.0), (0.5, 0.0), (1.0, 1.0)] }),
|
||||
"Shallow-Slope" => Some(ContourCurve { points: vec![(0.0, 0.0), (0.5, 0.25), (1.0, 1.0)] }),
|
||||
"Wave" => Some(ContourCurve { points: vec![(0.0, 0.0), (0.25, 1.0), (0.5, 0.5), (0.75, 1.0), (1.0, 0.0)] }),
|
||||
"Cove" => Some(ContourCurve { points: vec![(0.0, 0.0), (0.25, 0.5), (0.5, 1.0), (0.75, 0.5), (1.0, 0.0)] }),
|
||||
"Washboard" => Some(ContourCurve { points: vec![(0.0, 0.0), (0.125, 1.0), (0.25, 0.0), (0.375, 1.0), (0.5, 0.0), (0.625, 1.0), (0.75, 0.0), (0.875, 1.0), (1.0, 0.0)] }),
|
||||
"Cone" => Some(ContourCurve {
|
||||
points: vec![(0.0, 0.0), (0.5, 1.0), (1.0, 0.0)],
|
||||
}),
|
||||
"Cone-Inverted" => Some(ContourCurve {
|
||||
points: vec![(0.0, 1.0), (0.5, 0.0), (1.0, 1.0)],
|
||||
}),
|
||||
"Gaussian" => Some(ContourCurve {
|
||||
points: vec![(0.0, 0.0), (0.5, 0.85), (1.0, 1.0)],
|
||||
}),
|
||||
"Half Round" => Some(ContourCurve {
|
||||
points: vec![
|
||||
(0.0, 0.0),
|
||||
(0.25, 0.75),
|
||||
(0.5, 1.0),
|
||||
(0.75, 0.75),
|
||||
(1.0, 0.0),
|
||||
],
|
||||
}),
|
||||
"Round" => Some(ContourCurve {
|
||||
points: vec![(0.0, 0.0), (0.5, 1.0), (1.0, 0.0)],
|
||||
}),
|
||||
"Ring" => Some(ContourCurve {
|
||||
points: vec![(0.0, 0.0), (0.25, 1.0), (0.5, 0.0), (0.75, 1.0), (1.0, 0.0)],
|
||||
}),
|
||||
"Ring-Double" => Some(ContourCurve {
|
||||
points: vec![
|
||||
(0.0, 0.0),
|
||||
(0.125, 1.0),
|
||||
(0.25, 0.0),
|
||||
(0.375, 1.0),
|
||||
(0.5, 0.0),
|
||||
(0.625, 1.0),
|
||||
(0.75, 0.0),
|
||||
(0.875, 1.0),
|
||||
(1.0, 0.0),
|
||||
],
|
||||
}),
|
||||
"Sawtooth" => Some(ContourCurve {
|
||||
points: vec![(0.0, 0.0), (0.5, 1.0), (0.5, 0.0), (1.0, 1.0)],
|
||||
}),
|
||||
"Square" => Some(ContourCurve {
|
||||
points: vec![(0.0, 0.0), (0.5, 0.0), (0.5, 1.0), (1.0, 1.0)],
|
||||
}),
|
||||
"Valley" => Some(ContourCurve {
|
||||
points: vec![(0.0, 1.0), (0.5, 0.0), (1.0, 1.0)],
|
||||
}),
|
||||
"Shallow-Slope" => Some(ContourCurve {
|
||||
points: vec![(0.0, 0.0), (0.5, 0.25), (1.0, 1.0)],
|
||||
}),
|
||||
"Wave" => Some(ContourCurve {
|
||||
points: vec![(0.0, 0.0), (0.25, 1.0), (0.5, 0.5), (0.75, 1.0), (1.0, 0.0)],
|
||||
}),
|
||||
"Cove" => Some(ContourCurve {
|
||||
points: vec![(0.0, 0.0), (0.25, 0.5), (0.5, 1.0), (0.75, 0.5), (1.0, 0.0)],
|
||||
}),
|
||||
"Washboard" => Some(ContourCurve {
|
||||
points: vec![
|
||||
(0.0, 0.0),
|
||||
(0.125, 1.0),
|
||||
(0.25, 0.0),
|
||||
(0.375, 1.0),
|
||||
(0.5, 0.0),
|
||||
(0.625, 1.0),
|
||||
(0.75, 0.0),
|
||||
(0.875, 1.0),
|
||||
(1.0, 0.0),
|
||||
],
|
||||
}),
|
||||
_ => None, // Default to linear
|
||||
}
|
||||
}
|
||||
@@ -223,7 +298,8 @@ pub fn blend_mode_to_string(mode: &BlendMode) -> String {
|
||||
BlendMode::Color => "color",
|
||||
BlendMode::PassThrough => "passthrough",
|
||||
BlendMode::Luminosity => "luminosity",
|
||||
}.to_string()
|
||||
}
|
||||
.to_string()
|
||||
}
|
||||
|
||||
pub fn blend_mode_from_string(s: &str) -> BlendMode {
|
||||
@@ -265,88 +341,244 @@ pub fn blend_mode_from_string(s: &str) -> BlendMode {
|
||||
/// Used by engine-api before calling apply_layer_effects().
|
||||
pub fn protocol_to_hcie_fx_effect(fx: &effects::LayerEffect) -> LayerEffect {
|
||||
match fx {
|
||||
effects::LayerEffect::DropShadow { enabled, blend_mode, color, opacity, angle, distance, spread, size, noise, contour } => {
|
||||
LayerEffect::DropShadow {
|
||||
enabled: *enabled, blend_mode: blend_mode_from_string(blend_mode), color: *color,
|
||||
opacity: *opacity, angle: *angle, distance: *distance, spread: *spread, size: *size,
|
||||
noise: *noise, contour: contour.as_ref().map(|c| ContourCurve { points: c.points.clone() }),
|
||||
}
|
||||
}
|
||||
effects::LayerEffect::InnerShadow { enabled, blend_mode, color, opacity, angle, distance, choke, size, noise, contour } => {
|
||||
LayerEffect::InnerShadow {
|
||||
enabled: *enabled, blend_mode: blend_mode_from_string(blend_mode), color: *color,
|
||||
opacity: *opacity, angle: *angle, distance: *distance, choke: *choke, size: *size,
|
||||
noise: *noise, contour: contour.as_ref().map(|c| ContourCurve { points: c.points.clone() }),
|
||||
}
|
||||
}
|
||||
effects::LayerEffect::OuterGlow { enabled, blend_mode, color, opacity, spread, size, noise, contour } => {
|
||||
LayerEffect::OuterGlow {
|
||||
enabled: *enabled, blend_mode: blend_mode_from_string(blend_mode), color: *color,
|
||||
opacity: *opacity, spread: *spread, size: *size,
|
||||
noise: *noise, contour: contour.as_ref().map(|c| ContourCurve { points: c.points.clone() }),
|
||||
}
|
||||
}
|
||||
effects::LayerEffect::InnerGlow { enabled, blend_mode, color, opacity, choke, size, noise, contour, source } => {
|
||||
LayerEffect::InnerGlow {
|
||||
enabled: *enabled, blend_mode: blend_mode_from_string(blend_mode), color: *color,
|
||||
opacity: *opacity, choke: *choke, size: *size,
|
||||
noise: *noise, contour: contour.as_ref().map(|c| ContourCurve { points: c.points.clone() }),
|
||||
source: *source,
|
||||
}
|
||||
}
|
||||
effects::LayerEffect::BevelEmboss { enabled, style, technique, depth, direction, size, soften, angle, altitude, highlight_blend, highlight_color, highlight_opacity, shadow_blend, shadow_color, shadow_opacity, contour } => {
|
||||
LayerEffect::BevelEmboss {
|
||||
enabled: *enabled,
|
||||
style: match style { effects::BevelStyle::InnerBevel => BevelStyle::InnerBevel, effects::BevelStyle::OuterBevel => BevelStyle::OuterBevel, effects::BevelStyle::Emboss => BevelStyle::Emboss, effects::BevelStyle::PillowEmboss => BevelStyle::PillowEmboss, effects::BevelStyle::StrokeEmboss => BevelStyle::StrokeEmboss },
|
||||
technique: match technique { effects::Technique::Smooth => Technique::Smooth, effects::Technique::ChiselHard => Technique::ChiselHard, effects::Technique::ChiselSoft => Technique::ChiselSoft },
|
||||
depth: *depth,
|
||||
direction: match direction { effects::Direction::Up => Direction::Up, effects::Direction::Down => Direction::Down },
|
||||
size: *size, soften: *soften, angle: *angle, altitude: *altitude,
|
||||
highlight_blend: blend_mode_from_string(highlight_blend), highlight_color: *highlight_color, highlight_opacity: *highlight_opacity,
|
||||
shadow_blend: blend_mode_from_string(shadow_blend), shadow_color: *shadow_color, shadow_opacity: *shadow_opacity,
|
||||
contour: contour.as_ref().map(|c| ContourCurve { points: c.points.clone() }),
|
||||
}
|
||||
}
|
||||
effects::LayerEffect::Satin { enabled, blend_mode, color, opacity, angle, distance, size, invert, contour } => {
|
||||
LayerEffect::Satin {
|
||||
enabled: *enabled, blend_mode: blend_mode_from_string(blend_mode), color: *color,
|
||||
opacity: *opacity, angle: *angle, distance: *distance, size: *size, invert: *invert,
|
||||
contour: contour.as_ref().map(|c| ContourCurve { points: c.points.clone() }),
|
||||
}
|
||||
}
|
||||
effects::LayerEffect::ColorOverlay { enabled, blend_mode, color, opacity } => {
|
||||
LayerEffect::ColorOverlay {
|
||||
enabled: *enabled, blend_mode: blend_mode_from_string(blend_mode), color: *color, opacity: *opacity,
|
||||
}
|
||||
}
|
||||
effects::LayerEffect::GradientOverlay { enabled, blend_mode, opacity, angle, scale, gradient } => {
|
||||
LayerEffect::GradientOverlay {
|
||||
enabled: *enabled, blend_mode: blend_mode_from_string(blend_mode), opacity: *opacity,
|
||||
angle: *angle, scale: *scale,
|
||||
gradient: GradientDef {
|
||||
color_stops: gradient.color_stops.clone(),
|
||||
transparency_stops: gradient.transparency_stops.clone(),
|
||||
midpoint: gradient.midpoint,
|
||||
angle: gradient.angle,
|
||||
scale: gradient.scale,
|
||||
gradient_type: gradient.gradient_type,
|
||||
},
|
||||
}
|
||||
}
|
||||
effects::LayerEffect::PatternOverlay { enabled, blend_mode, opacity, scale, pattern_id } => {
|
||||
LayerEffect::PatternOverlay {
|
||||
enabled: *enabled, blend_mode: blend_mode_from_string(blend_mode), opacity: *opacity,
|
||||
scale: *scale, pattern_id: pattern_id.clone(),
|
||||
}
|
||||
}
|
||||
effects::LayerEffect::Stroke { enabled, position, fill_type, blend_mode, opacity, size, color } => {
|
||||
LayerEffect::Stroke {
|
||||
enabled: *enabled,
|
||||
position: match position { effects::StrokePosition::Inside => StrokePosition::Inside, effects::StrokePosition::Center => StrokePosition::Center, effects::StrokePosition::Outside => StrokePosition::Outside },
|
||||
fill_type: match fill_type { effects::StrokeFillType::Color => StrokeFillType::Color, effects::StrokeFillType::Gradient => StrokeFillType::Gradient, effects::StrokeFillType::Pattern => StrokeFillType::Pattern },
|
||||
blend_mode: blend_mode_from_string(blend_mode), opacity: *opacity, size: *size, color: *color,
|
||||
}
|
||||
}
|
||||
effects::LayerEffect::DropShadow {
|
||||
enabled,
|
||||
blend_mode,
|
||||
color,
|
||||
opacity,
|
||||
angle,
|
||||
distance,
|
||||
spread,
|
||||
size,
|
||||
noise,
|
||||
contour,
|
||||
} => LayerEffect::DropShadow {
|
||||
enabled: *enabled,
|
||||
blend_mode: blend_mode_from_string(blend_mode),
|
||||
color: *color,
|
||||
opacity: *opacity,
|
||||
angle: *angle,
|
||||
distance: *distance,
|
||||
spread: *spread,
|
||||
size: *size,
|
||||
noise: *noise,
|
||||
contour: contour.as_ref().map(|c| ContourCurve {
|
||||
points: c.points.clone(),
|
||||
}),
|
||||
},
|
||||
effects::LayerEffect::InnerShadow {
|
||||
enabled,
|
||||
blend_mode,
|
||||
color,
|
||||
opacity,
|
||||
angle,
|
||||
distance,
|
||||
choke,
|
||||
size,
|
||||
noise,
|
||||
contour,
|
||||
} => LayerEffect::InnerShadow {
|
||||
enabled: *enabled,
|
||||
blend_mode: blend_mode_from_string(blend_mode),
|
||||
color: *color,
|
||||
opacity: *opacity,
|
||||
angle: *angle,
|
||||
distance: *distance,
|
||||
choke: *choke,
|
||||
size: *size,
|
||||
noise: *noise,
|
||||
contour: contour.as_ref().map(|c| ContourCurve {
|
||||
points: c.points.clone(),
|
||||
}),
|
||||
},
|
||||
effects::LayerEffect::OuterGlow {
|
||||
enabled,
|
||||
blend_mode,
|
||||
color,
|
||||
opacity,
|
||||
spread,
|
||||
size,
|
||||
noise,
|
||||
contour,
|
||||
} => LayerEffect::OuterGlow {
|
||||
enabled: *enabled,
|
||||
blend_mode: blend_mode_from_string(blend_mode),
|
||||
color: *color,
|
||||
opacity: *opacity,
|
||||
spread: *spread,
|
||||
size: *size,
|
||||
noise: *noise,
|
||||
contour: contour.as_ref().map(|c| ContourCurve {
|
||||
points: c.points.clone(),
|
||||
}),
|
||||
},
|
||||
effects::LayerEffect::InnerGlow {
|
||||
enabled,
|
||||
blend_mode,
|
||||
color,
|
||||
opacity,
|
||||
choke,
|
||||
size,
|
||||
noise,
|
||||
contour,
|
||||
source,
|
||||
} => LayerEffect::InnerGlow {
|
||||
enabled: *enabled,
|
||||
blend_mode: blend_mode_from_string(blend_mode),
|
||||
color: *color,
|
||||
opacity: *opacity,
|
||||
choke: *choke,
|
||||
size: *size,
|
||||
noise: *noise,
|
||||
contour: contour.as_ref().map(|c| ContourCurve {
|
||||
points: c.points.clone(),
|
||||
}),
|
||||
source: *source,
|
||||
},
|
||||
effects::LayerEffect::BevelEmboss {
|
||||
enabled,
|
||||
style,
|
||||
technique,
|
||||
depth,
|
||||
direction,
|
||||
size,
|
||||
soften,
|
||||
angle,
|
||||
altitude,
|
||||
highlight_blend,
|
||||
highlight_color,
|
||||
highlight_opacity,
|
||||
shadow_blend,
|
||||
shadow_color,
|
||||
shadow_opacity,
|
||||
contour,
|
||||
} => LayerEffect::BevelEmboss {
|
||||
enabled: *enabled,
|
||||
style: match style {
|
||||
effects::BevelStyle::InnerBevel => BevelStyle::InnerBevel,
|
||||
effects::BevelStyle::OuterBevel => BevelStyle::OuterBevel,
|
||||
effects::BevelStyle::Emboss => BevelStyle::Emboss,
|
||||
effects::BevelStyle::PillowEmboss => BevelStyle::PillowEmboss,
|
||||
effects::BevelStyle::StrokeEmboss => BevelStyle::StrokeEmboss,
|
||||
},
|
||||
technique: match technique {
|
||||
effects::Technique::Smooth => Technique::Smooth,
|
||||
effects::Technique::ChiselHard => Technique::ChiselHard,
|
||||
effects::Technique::ChiselSoft => Technique::ChiselSoft,
|
||||
},
|
||||
depth: *depth,
|
||||
direction: match direction {
|
||||
effects::Direction::Up => Direction::Up,
|
||||
effects::Direction::Down => Direction::Down,
|
||||
},
|
||||
size: *size,
|
||||
soften: *soften,
|
||||
angle: *angle,
|
||||
altitude: *altitude,
|
||||
highlight_blend: blend_mode_from_string(highlight_blend),
|
||||
highlight_color: *highlight_color,
|
||||
highlight_opacity: *highlight_opacity,
|
||||
shadow_blend: blend_mode_from_string(shadow_blend),
|
||||
shadow_color: *shadow_color,
|
||||
shadow_opacity: *shadow_opacity,
|
||||
contour: contour.as_ref().map(|c| ContourCurve {
|
||||
points: c.points.clone(),
|
||||
}),
|
||||
},
|
||||
effects::LayerEffect::Satin {
|
||||
enabled,
|
||||
blend_mode,
|
||||
color,
|
||||
opacity,
|
||||
angle,
|
||||
distance,
|
||||
size,
|
||||
invert,
|
||||
contour,
|
||||
} => LayerEffect::Satin {
|
||||
enabled: *enabled,
|
||||
blend_mode: blend_mode_from_string(blend_mode),
|
||||
color: *color,
|
||||
opacity: *opacity,
|
||||
angle: *angle,
|
||||
distance: *distance,
|
||||
size: *size,
|
||||
invert: *invert,
|
||||
contour: contour.as_ref().map(|c| ContourCurve {
|
||||
points: c.points.clone(),
|
||||
}),
|
||||
},
|
||||
effects::LayerEffect::ColorOverlay {
|
||||
enabled,
|
||||
blend_mode,
|
||||
color,
|
||||
opacity,
|
||||
} => LayerEffect::ColorOverlay {
|
||||
enabled: *enabled,
|
||||
blend_mode: blend_mode_from_string(blend_mode),
|
||||
color: *color,
|
||||
opacity: *opacity,
|
||||
},
|
||||
effects::LayerEffect::GradientOverlay {
|
||||
enabled,
|
||||
blend_mode,
|
||||
opacity,
|
||||
angle,
|
||||
scale,
|
||||
gradient,
|
||||
} => LayerEffect::GradientOverlay {
|
||||
enabled: *enabled,
|
||||
blend_mode: blend_mode_from_string(blend_mode),
|
||||
opacity: *opacity,
|
||||
angle: *angle,
|
||||
scale: *scale,
|
||||
gradient: GradientDef {
|
||||
color_stops: gradient.color_stops.clone(),
|
||||
transparency_stops: gradient.transparency_stops.clone(),
|
||||
midpoint: gradient.midpoint,
|
||||
angle: gradient.angle,
|
||||
scale: gradient.scale,
|
||||
gradient_type: gradient.gradient_type,
|
||||
},
|
||||
},
|
||||
effects::LayerEffect::PatternOverlay {
|
||||
enabled,
|
||||
blend_mode,
|
||||
opacity,
|
||||
scale,
|
||||
pattern_id,
|
||||
} => LayerEffect::PatternOverlay {
|
||||
enabled: *enabled,
|
||||
blend_mode: blend_mode_from_string(blend_mode),
|
||||
opacity: *opacity,
|
||||
scale: *scale,
|
||||
pattern_id: pattern_id.clone(),
|
||||
},
|
||||
effects::LayerEffect::Stroke {
|
||||
enabled,
|
||||
position,
|
||||
fill_type,
|
||||
blend_mode,
|
||||
opacity,
|
||||
size,
|
||||
color,
|
||||
} => LayerEffect::Stroke {
|
||||
enabled: *enabled,
|
||||
position: match position {
|
||||
effects::StrokePosition::Inside => StrokePosition::Inside,
|
||||
effects::StrokePosition::Center => StrokePosition::Center,
|
||||
effects::StrokePosition::Outside => StrokePosition::Outside,
|
||||
},
|
||||
fill_type: match fill_type {
|
||||
effects::StrokeFillType::Color => StrokeFillType::Color,
|
||||
effects::StrokeFillType::Gradient => StrokeFillType::Gradient,
|
||||
effects::StrokeFillType::Pattern => StrokeFillType::Pattern,
|
||||
},
|
||||
blend_mode: blend_mode_from_string(blend_mode),
|
||||
opacity: *opacity,
|
||||
size: *size,
|
||||
color: *color,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,88 +586,244 @@ pub fn protocol_to_hcie_fx_effect(fx: &effects::LayerEffect) -> LayerEffect {
|
||||
/// to protocol-layer effect (serialization format with String blend_mode).
|
||||
pub fn hcie_fx_effect_to_protocol(fx: &LayerEffect) -> effects::LayerEffect {
|
||||
match fx {
|
||||
LayerEffect::DropShadow { enabled, blend_mode, color, opacity, angle, distance, spread, size, noise, contour } => {
|
||||
effects::LayerEffect::DropShadow {
|
||||
enabled: *enabled, blend_mode: blend_mode_to_string(blend_mode), color: *color,
|
||||
opacity: *opacity, angle: *angle, distance: *distance, spread: *spread, size: *size,
|
||||
noise: *noise, contour: contour.as_ref().map(|c| effects::ContourCurve { points: c.points.clone() }),
|
||||
}
|
||||
}
|
||||
LayerEffect::InnerShadow { enabled, blend_mode, color, opacity, angle, distance, choke, size, noise, contour } => {
|
||||
effects::LayerEffect::InnerShadow {
|
||||
enabled: *enabled, blend_mode: blend_mode_to_string(blend_mode), color: *color,
|
||||
opacity: *opacity, angle: *angle, distance: *distance, choke: *choke, size: *size,
|
||||
noise: *noise, contour: contour.as_ref().map(|c| effects::ContourCurve { points: c.points.clone() }),
|
||||
}
|
||||
}
|
||||
LayerEffect::OuterGlow { enabled, blend_mode, color, opacity, spread, size, noise, contour } => {
|
||||
effects::LayerEffect::OuterGlow {
|
||||
enabled: *enabled, blend_mode: blend_mode_to_string(blend_mode), color: *color,
|
||||
opacity: *opacity, spread: *spread, size: *size,
|
||||
noise: *noise, contour: contour.as_ref().map(|c| effects::ContourCurve { points: c.points.clone() }),
|
||||
}
|
||||
}
|
||||
LayerEffect::InnerGlow { enabled, blend_mode, color, opacity, choke, size, noise, contour, source } => {
|
||||
effects::LayerEffect::InnerGlow {
|
||||
enabled: *enabled, blend_mode: blend_mode_to_string(blend_mode), color: *color,
|
||||
opacity: *opacity, choke: *choke, size: *size,
|
||||
noise: *noise, contour: contour.as_ref().map(|c| effects::ContourCurve { points: c.points.clone() }),
|
||||
source: *source,
|
||||
}
|
||||
}
|
||||
LayerEffect::BevelEmboss { enabled, style, technique, depth, direction, size, soften, angle, altitude, highlight_blend, highlight_color, highlight_opacity, shadow_blend, shadow_color, shadow_opacity, contour } => {
|
||||
effects::LayerEffect::BevelEmboss {
|
||||
enabled: *enabled,
|
||||
style: match style { BevelStyle::InnerBevel => effects::BevelStyle::InnerBevel, BevelStyle::OuterBevel => effects::BevelStyle::OuterBevel, BevelStyle::Emboss => effects::BevelStyle::Emboss, BevelStyle::PillowEmboss => effects::BevelStyle::PillowEmboss, BevelStyle::StrokeEmboss => effects::BevelStyle::StrokeEmboss },
|
||||
technique: match technique { Technique::Smooth => effects::Technique::Smooth, Technique::ChiselHard => effects::Technique::ChiselHard, Technique::ChiselSoft => effects::Technique::ChiselSoft },
|
||||
depth: *depth,
|
||||
direction: match direction { Direction::Up => effects::Direction::Up, Direction::Down => effects::Direction::Down },
|
||||
size: *size, soften: *soften, angle: *angle, altitude: *altitude,
|
||||
highlight_blend: blend_mode_to_string(highlight_blend), highlight_color: *highlight_color, highlight_opacity: *highlight_opacity,
|
||||
shadow_blend: blend_mode_to_string(shadow_blend), shadow_color: *shadow_color, shadow_opacity: *shadow_opacity,
|
||||
contour: contour.as_ref().map(|c| effects::ContourCurve { points: c.points.clone() }),
|
||||
}
|
||||
}
|
||||
LayerEffect::Satin { enabled, blend_mode, color, opacity, angle, distance, size, invert, contour } => {
|
||||
effects::LayerEffect::Satin {
|
||||
enabled: *enabled, blend_mode: blend_mode_to_string(blend_mode), color: *color,
|
||||
opacity: *opacity, angle: *angle, distance: *distance, size: *size, invert: *invert,
|
||||
contour: contour.as_ref().map(|c| effects::ContourCurve { points: c.points.clone() }),
|
||||
}
|
||||
}
|
||||
LayerEffect::ColorOverlay { enabled, blend_mode, color, opacity } => {
|
||||
effects::LayerEffect::ColorOverlay {
|
||||
enabled: *enabled, blend_mode: blend_mode_to_string(blend_mode), color: *color, opacity: *opacity,
|
||||
}
|
||||
}
|
||||
LayerEffect::GradientOverlay { enabled, blend_mode, opacity, angle, scale, gradient } => {
|
||||
effects::LayerEffect::GradientOverlay {
|
||||
enabled: *enabled, blend_mode: blend_mode_to_string(blend_mode), opacity: *opacity,
|
||||
angle: *angle, scale: *scale,
|
||||
gradient: effects::GradientDef {
|
||||
color_stops: gradient.color_stops.clone(),
|
||||
transparency_stops: gradient.transparency_stops.clone(),
|
||||
midpoint: gradient.midpoint,
|
||||
angle: gradient.angle,
|
||||
scale: gradient.scale,
|
||||
gradient_type: gradient.gradient_type,
|
||||
},
|
||||
}
|
||||
}
|
||||
LayerEffect::PatternOverlay { enabled, blend_mode, opacity, scale, pattern_id } => {
|
||||
effects::LayerEffect::PatternOverlay {
|
||||
enabled: *enabled, blend_mode: blend_mode_to_string(blend_mode), opacity: *opacity,
|
||||
scale: *scale, pattern_id: pattern_id.clone(),
|
||||
}
|
||||
}
|
||||
LayerEffect::Stroke { enabled, position, fill_type, blend_mode, opacity, size, color } => {
|
||||
effects::LayerEffect::Stroke {
|
||||
enabled: *enabled,
|
||||
position: match position { StrokePosition::Inside => effects::StrokePosition::Inside, StrokePosition::Center => effects::StrokePosition::Center, StrokePosition::Outside => effects::StrokePosition::Outside },
|
||||
fill_type: match fill_type { StrokeFillType::Color => effects::StrokeFillType::Color, StrokeFillType::Gradient => effects::StrokeFillType::Gradient, StrokeFillType::Pattern => effects::StrokeFillType::Pattern },
|
||||
blend_mode: blend_mode_to_string(blend_mode), opacity: *opacity, size: *size, color: *color,
|
||||
}
|
||||
}
|
||||
LayerEffect::DropShadow {
|
||||
enabled,
|
||||
blend_mode,
|
||||
color,
|
||||
opacity,
|
||||
angle,
|
||||
distance,
|
||||
spread,
|
||||
size,
|
||||
noise,
|
||||
contour,
|
||||
} => effects::LayerEffect::DropShadow {
|
||||
enabled: *enabled,
|
||||
blend_mode: blend_mode_to_string(blend_mode),
|
||||
color: *color,
|
||||
opacity: *opacity,
|
||||
angle: *angle,
|
||||
distance: *distance,
|
||||
spread: *spread,
|
||||
size: *size,
|
||||
noise: *noise,
|
||||
contour: contour.as_ref().map(|c| effects::ContourCurve {
|
||||
points: c.points.clone(),
|
||||
}),
|
||||
},
|
||||
LayerEffect::InnerShadow {
|
||||
enabled,
|
||||
blend_mode,
|
||||
color,
|
||||
opacity,
|
||||
angle,
|
||||
distance,
|
||||
choke,
|
||||
size,
|
||||
noise,
|
||||
contour,
|
||||
} => effects::LayerEffect::InnerShadow {
|
||||
enabled: *enabled,
|
||||
blend_mode: blend_mode_to_string(blend_mode),
|
||||
color: *color,
|
||||
opacity: *opacity,
|
||||
angle: *angle,
|
||||
distance: *distance,
|
||||
choke: *choke,
|
||||
size: *size,
|
||||
noise: *noise,
|
||||
contour: contour.as_ref().map(|c| effects::ContourCurve {
|
||||
points: c.points.clone(),
|
||||
}),
|
||||
},
|
||||
LayerEffect::OuterGlow {
|
||||
enabled,
|
||||
blend_mode,
|
||||
color,
|
||||
opacity,
|
||||
spread,
|
||||
size,
|
||||
noise,
|
||||
contour,
|
||||
} => effects::LayerEffect::OuterGlow {
|
||||
enabled: *enabled,
|
||||
blend_mode: blend_mode_to_string(blend_mode),
|
||||
color: *color,
|
||||
opacity: *opacity,
|
||||
spread: *spread,
|
||||
size: *size,
|
||||
noise: *noise,
|
||||
contour: contour.as_ref().map(|c| effects::ContourCurve {
|
||||
points: c.points.clone(),
|
||||
}),
|
||||
},
|
||||
LayerEffect::InnerGlow {
|
||||
enabled,
|
||||
blend_mode,
|
||||
color,
|
||||
opacity,
|
||||
choke,
|
||||
size,
|
||||
noise,
|
||||
contour,
|
||||
source,
|
||||
} => effects::LayerEffect::InnerGlow {
|
||||
enabled: *enabled,
|
||||
blend_mode: blend_mode_to_string(blend_mode),
|
||||
color: *color,
|
||||
opacity: *opacity,
|
||||
choke: *choke,
|
||||
size: *size,
|
||||
noise: *noise,
|
||||
contour: contour.as_ref().map(|c| effects::ContourCurve {
|
||||
points: c.points.clone(),
|
||||
}),
|
||||
source: *source,
|
||||
},
|
||||
LayerEffect::BevelEmboss {
|
||||
enabled,
|
||||
style,
|
||||
technique,
|
||||
depth,
|
||||
direction,
|
||||
size,
|
||||
soften,
|
||||
angle,
|
||||
altitude,
|
||||
highlight_blend,
|
||||
highlight_color,
|
||||
highlight_opacity,
|
||||
shadow_blend,
|
||||
shadow_color,
|
||||
shadow_opacity,
|
||||
contour,
|
||||
} => effects::LayerEffect::BevelEmboss {
|
||||
enabled: *enabled,
|
||||
style: match style {
|
||||
BevelStyle::InnerBevel => effects::BevelStyle::InnerBevel,
|
||||
BevelStyle::OuterBevel => effects::BevelStyle::OuterBevel,
|
||||
BevelStyle::Emboss => effects::BevelStyle::Emboss,
|
||||
BevelStyle::PillowEmboss => effects::BevelStyle::PillowEmboss,
|
||||
BevelStyle::StrokeEmboss => effects::BevelStyle::StrokeEmboss,
|
||||
},
|
||||
technique: match technique {
|
||||
Technique::Smooth => effects::Technique::Smooth,
|
||||
Technique::ChiselHard => effects::Technique::ChiselHard,
|
||||
Technique::ChiselSoft => effects::Technique::ChiselSoft,
|
||||
},
|
||||
depth: *depth,
|
||||
direction: match direction {
|
||||
Direction::Up => effects::Direction::Up,
|
||||
Direction::Down => effects::Direction::Down,
|
||||
},
|
||||
size: *size,
|
||||
soften: *soften,
|
||||
angle: *angle,
|
||||
altitude: *altitude,
|
||||
highlight_blend: blend_mode_to_string(highlight_blend),
|
||||
highlight_color: *highlight_color,
|
||||
highlight_opacity: *highlight_opacity,
|
||||
shadow_blend: blend_mode_to_string(shadow_blend),
|
||||
shadow_color: *shadow_color,
|
||||
shadow_opacity: *shadow_opacity,
|
||||
contour: contour.as_ref().map(|c| effects::ContourCurve {
|
||||
points: c.points.clone(),
|
||||
}),
|
||||
},
|
||||
LayerEffect::Satin {
|
||||
enabled,
|
||||
blend_mode,
|
||||
color,
|
||||
opacity,
|
||||
angle,
|
||||
distance,
|
||||
size,
|
||||
invert,
|
||||
contour,
|
||||
} => effects::LayerEffect::Satin {
|
||||
enabled: *enabled,
|
||||
blend_mode: blend_mode_to_string(blend_mode),
|
||||
color: *color,
|
||||
opacity: *opacity,
|
||||
angle: *angle,
|
||||
distance: *distance,
|
||||
size: *size,
|
||||
invert: *invert,
|
||||
contour: contour.as_ref().map(|c| effects::ContourCurve {
|
||||
points: c.points.clone(),
|
||||
}),
|
||||
},
|
||||
LayerEffect::ColorOverlay {
|
||||
enabled,
|
||||
blend_mode,
|
||||
color,
|
||||
opacity,
|
||||
} => effects::LayerEffect::ColorOverlay {
|
||||
enabled: *enabled,
|
||||
blend_mode: blend_mode_to_string(blend_mode),
|
||||
color: *color,
|
||||
opacity: *opacity,
|
||||
},
|
||||
LayerEffect::GradientOverlay {
|
||||
enabled,
|
||||
blend_mode,
|
||||
opacity,
|
||||
angle,
|
||||
scale,
|
||||
gradient,
|
||||
} => effects::LayerEffect::GradientOverlay {
|
||||
enabled: *enabled,
|
||||
blend_mode: blend_mode_to_string(blend_mode),
|
||||
opacity: *opacity,
|
||||
angle: *angle,
|
||||
scale: *scale,
|
||||
gradient: effects::GradientDef {
|
||||
color_stops: gradient.color_stops.clone(),
|
||||
transparency_stops: gradient.transparency_stops.clone(),
|
||||
midpoint: gradient.midpoint,
|
||||
angle: gradient.angle,
|
||||
scale: gradient.scale,
|
||||
gradient_type: gradient.gradient_type,
|
||||
},
|
||||
},
|
||||
LayerEffect::PatternOverlay {
|
||||
enabled,
|
||||
blend_mode,
|
||||
opacity,
|
||||
scale,
|
||||
pattern_id,
|
||||
} => effects::LayerEffect::PatternOverlay {
|
||||
enabled: *enabled,
|
||||
blend_mode: blend_mode_to_string(blend_mode),
|
||||
opacity: *opacity,
|
||||
scale: *scale,
|
||||
pattern_id: pattern_id.clone(),
|
||||
},
|
||||
LayerEffect::Stroke {
|
||||
enabled,
|
||||
position,
|
||||
fill_type,
|
||||
blend_mode,
|
||||
opacity,
|
||||
size,
|
||||
color,
|
||||
} => effects::LayerEffect::Stroke {
|
||||
enabled: *enabled,
|
||||
position: match position {
|
||||
StrokePosition::Inside => effects::StrokePosition::Inside,
|
||||
StrokePosition::Center => effects::StrokePosition::Center,
|
||||
StrokePosition::Outside => effects::StrokePosition::Outside,
|
||||
},
|
||||
fill_type: match fill_type {
|
||||
StrokeFillType::Color => effects::StrokeFillType::Color,
|
||||
StrokeFillType::Gradient => effects::StrokeFillType::Gradient,
|
||||
StrokeFillType::Pattern => effects::StrokeFillType::Pattern,
|
||||
},
|
||||
blend_mode: blend_mode_to_string(blend_mode),
|
||||
opacity: *opacity,
|
||||
size: *size,
|
||||
color: *color,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -445,7 +833,16 @@ pub fn hcie_fx_effect_to_protocol(fx: &LayerEffect) -> effects::LayerEffect {
|
||||
pub fn layer_style_to_effect(style: &hcie_protocol::LayerStyle) -> Option<LayerEffect> {
|
||||
use hcie_protocol::LayerStyle;
|
||||
match style {
|
||||
LayerStyle::DropShadow { enabled, opacity, angle, distance, spread, size, color, blend_mode } => Some(LayerEffect::DropShadow {
|
||||
LayerStyle::DropShadow {
|
||||
enabled,
|
||||
opacity,
|
||||
angle,
|
||||
distance,
|
||||
spread,
|
||||
size,
|
||||
color,
|
||||
blend_mode,
|
||||
} => Some(LayerEffect::DropShadow {
|
||||
enabled: *enabled,
|
||||
blend_mode: blend_mode_from_string(blend_mode),
|
||||
color: *color,
|
||||
@@ -457,7 +854,16 @@ pub fn layer_style_to_effect(style: &hcie_protocol::LayerStyle) -> Option<LayerE
|
||||
noise: 0.0,
|
||||
contour: None,
|
||||
}),
|
||||
LayerStyle::InnerShadow { enabled, opacity, angle, distance, spread, size, color, blend_mode } => Some(LayerEffect::InnerShadow {
|
||||
LayerStyle::InnerShadow {
|
||||
enabled,
|
||||
opacity,
|
||||
angle,
|
||||
distance,
|
||||
spread,
|
||||
size,
|
||||
color,
|
||||
blend_mode,
|
||||
} => Some(LayerEffect::InnerShadow {
|
||||
enabled: *enabled,
|
||||
blend_mode: blend_mode_from_string(blend_mode),
|
||||
color: *color,
|
||||
@@ -469,7 +875,14 @@ pub fn layer_style_to_effect(style: &hcie_protocol::LayerStyle) -> Option<LayerE
|
||||
noise: 0.0,
|
||||
contour: None,
|
||||
}),
|
||||
LayerStyle::OuterGlow { enabled, opacity, spread, size, color, blend_mode } => Some(LayerEffect::OuterGlow {
|
||||
LayerStyle::OuterGlow {
|
||||
enabled,
|
||||
opacity,
|
||||
spread,
|
||||
size,
|
||||
color,
|
||||
blend_mode,
|
||||
} => Some(LayerEffect::OuterGlow {
|
||||
enabled: *enabled,
|
||||
blend_mode: blend_mode_from_string(blend_mode),
|
||||
color: *color,
|
||||
@@ -479,7 +892,14 @@ pub fn layer_style_to_effect(style: &hcie_protocol::LayerStyle) -> Option<LayerE
|
||||
noise: 0.0,
|
||||
contour: None,
|
||||
}),
|
||||
LayerStyle::InnerGlow { enabled, opacity, spread, size, color, blend_mode } => Some(LayerEffect::InnerGlow {
|
||||
LayerStyle::InnerGlow {
|
||||
enabled,
|
||||
opacity,
|
||||
spread,
|
||||
size,
|
||||
color,
|
||||
blend_mode,
|
||||
} => Some(LayerEffect::InnerGlow {
|
||||
enabled: *enabled,
|
||||
blend_mode: blend_mode_from_string(blend_mode),
|
||||
color: *color,
|
||||
@@ -490,7 +910,24 @@ pub fn layer_style_to_effect(style: &hcie_protocol::LayerStyle) -> Option<LayerE
|
||||
contour: None,
|
||||
source: 0,
|
||||
}),
|
||||
LayerStyle::BevelEmboss { enabled, depth, size, angle, altitude, highlight_opacity, shadow_opacity, direction, style, technique, soften, highlight_blend_mode, highlight_color, shadow_blend_mode, shadow_color, contour } => Some(LayerEffect::BevelEmboss {
|
||||
LayerStyle::BevelEmboss {
|
||||
enabled,
|
||||
depth,
|
||||
size,
|
||||
angle,
|
||||
altitude,
|
||||
highlight_opacity,
|
||||
shadow_opacity,
|
||||
direction,
|
||||
style,
|
||||
technique,
|
||||
soften,
|
||||
highlight_blend_mode,
|
||||
highlight_color,
|
||||
shadow_blend_mode,
|
||||
shadow_color,
|
||||
contour,
|
||||
} => Some(LayerEffect::BevelEmboss {
|
||||
enabled: *enabled,
|
||||
style: match style.as_str() {
|
||||
"InnerBevel" => BevelStyle::InnerBevel,
|
||||
@@ -524,7 +961,15 @@ pub fn layer_style_to_effect(style: &hcie_protocol::LayerStyle) -> Option<LayerE
|
||||
shadow_opacity: *shadow_opacity,
|
||||
contour: contour_name_to_curve(contour),
|
||||
}),
|
||||
LayerStyle::Satin { enabled, opacity, angle, distance, size, color, invert } => Some(LayerEffect::Satin {
|
||||
LayerStyle::Satin {
|
||||
enabled,
|
||||
opacity,
|
||||
angle,
|
||||
distance,
|
||||
size,
|
||||
color,
|
||||
invert,
|
||||
} => Some(LayerEffect::Satin {
|
||||
enabled: *enabled,
|
||||
blend_mode: BlendMode::Multiply,
|
||||
color: *color,
|
||||
@@ -535,13 +980,25 @@ pub fn layer_style_to_effect(style: &hcie_protocol::LayerStyle) -> Option<LayerE
|
||||
invert: *invert,
|
||||
contour: None,
|
||||
}),
|
||||
LayerStyle::ColorOverlay { enabled, opacity, color, blend_mode } => Some(LayerEffect::ColorOverlay {
|
||||
LayerStyle::ColorOverlay {
|
||||
enabled,
|
||||
opacity,
|
||||
color,
|
||||
blend_mode,
|
||||
} => Some(LayerEffect::ColorOverlay {
|
||||
enabled: *enabled,
|
||||
blend_mode: blend_mode_from_string(blend_mode),
|
||||
color: *color,
|
||||
opacity: *opacity,
|
||||
}),
|
||||
LayerStyle::GradientOverlay { enabled, opacity, blend_mode, angle, scale, gradient_type: _ } => Some(LayerEffect::GradientOverlay {
|
||||
LayerStyle::GradientOverlay {
|
||||
enabled,
|
||||
opacity,
|
||||
blend_mode,
|
||||
angle,
|
||||
scale,
|
||||
gradient_type: _,
|
||||
} => Some(LayerEffect::GradientOverlay {
|
||||
enabled: *enabled,
|
||||
blend_mode: blend_mode_from_string(blend_mode),
|
||||
opacity: *opacity,
|
||||
@@ -556,14 +1013,27 @@ pub fn layer_style_to_effect(style: &hcie_protocol::LayerStyle) -> Option<LayerE
|
||||
gradient_type: 0,
|
||||
},
|
||||
}),
|
||||
LayerStyle::PatternOverlay { enabled, opacity, blend_mode, scale, pattern_name } => Some(LayerEffect::PatternOverlay {
|
||||
LayerStyle::PatternOverlay {
|
||||
enabled,
|
||||
opacity,
|
||||
blend_mode,
|
||||
scale,
|
||||
pattern_name,
|
||||
} => Some(LayerEffect::PatternOverlay {
|
||||
enabled: *enabled,
|
||||
blend_mode: blend_mode_from_string(blend_mode),
|
||||
opacity: *opacity,
|
||||
scale: *scale,
|
||||
pattern_id: pattern_name.clone(),
|
||||
}),
|
||||
LayerStyle::Stroke { enabled, size, position, opacity, color, blend_mode } => Some(LayerEffect::Stroke {
|
||||
LayerStyle::Stroke {
|
||||
enabled,
|
||||
size,
|
||||
position,
|
||||
opacity,
|
||||
color,
|
||||
blend_mode,
|
||||
} => Some(LayerEffect::Stroke {
|
||||
enabled: *enabled,
|
||||
position: match position.as_str() {
|
||||
"Inside" => StrokePosition::Inside,
|
||||
|
||||
Reference in New Issue
Block a user