mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-14 19:27:05 -04:00
M9.13: Cell Pattern HQ variants, Advanced Spill Suppressor Ultra controls, Inner/Outer Key masks and cleanup
- Cell Pattern: HQ variants search a wider cell neighbourhood and anti-alias. - Advanced Spill Suppressor: Tolerance, Desaturate, Spill Color Correction. - Inner/Outer Key: Additional Foreground / Background 1-10 and Cleanup Foreground / Background 1-10 brush strokes (older single masks migrate to slot 1). Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
413e3ea97d
commit
6d5e86601f
7 files changed
+236
-34
No files matched your search
@@ -12,9 +12,6 @@ pub const PARTIAL: &[(&str, &str)] = &[
|
||||
("ec.distort.rollingshutterrepair", "Pixel Motion Detail has no effect"),
|
||||
("ec.generate.advancedlightning", "no Alpha Obstacle; most Expert Settings missing"),
|
||||
("ec.generate.scribble", "no caps, joins or Start/End Apply To"),
|
||||
("ec.generate.cellpattern", "HQ variants render like the standard ones"),
|
||||
("ec.key.innerouter", "one additional mask per side; no cleanup strokes"),
|
||||
("ec.key.advancedspill", "no Tolerance, Desaturate or Spill Color Correction"),
|
||||
("ec.keying.keylight", "no Source Crops X/Y Method or Edge Colour; no colour suppression / balancing"),
|
||||
("ec.obsolete.pathtext", "no kerning, shear/scale, line spacing, fade time or jitter"),
|
||||
("ec.noise.addgrain", "no preview region, presets, channel balance or temporal controls"),
|
||||
|
||||
@@ -43,8 +43,9 @@ fn add_light(px: Px, l: [f32; 3], la: f32) -> Px {
|
||||
|
||||
// ---------------------------------------------------------------- Cell Pattern
|
||||
|
||||
/// Cell Pattern options. The HQ variants share the shaping of their standard counterparts
|
||||
/// (we always render at full precision).
|
||||
/// Cell Pattern options. The HQ variants share the shaping of their standard counterparts but
|
||||
/// search a wider neighbourhood of cells (no artefacts at high Disperse) and anti-alias the
|
||||
/// cell edges (4×4 samples per pixel).
|
||||
const CELL_PATTERNS: [&str; 12] = [
|
||||
"Bubbles",
|
||||
"Crystals",
|
||||
@@ -75,13 +76,13 @@ fn pattern_kind(i: u32) -> u32 {
|
||||
|
||||
/// Worley distances for point (u, v) in cell units: (F1, F2, hash of the nearest cell).
|
||||
/// `tile` repeats the cell layout every (columns, rows) cells.
|
||||
fn worley(u: f64, v: f64, disperse: f64, evo: f64, seed: u32, tile: Option<(i64, i64)>) -> (f64, f64, f32) {
|
||||
fn worley_r(u: f64, v: f64, disperse: f64, evo: f64, seed: u32, tile: Option<(i64, i64)>, reach: i64) -> (f64, f64, f32) {
|
||||
let (cx, cy) = (u.floor() as i64, v.floor() as i64);
|
||||
let mut f1 = f64::INFINITY;
|
||||
let mut f2 = f64::INFINITY;
|
||||
let mut id = 0.0f32;
|
||||
for j in -1..=1 {
|
||||
for i in -1..=1 {
|
||||
for j in -reach..=reach {
|
||||
for i in -reach..=reach {
|
||||
let (gx, gy) = (cx + i, cy + j);
|
||||
let (tx, ty) = match tile {
|
||||
Some((nx, ny)) => (gx.rem_euclid(nx), gy.rem_euclid(ny)),
|
||||
@@ -135,6 +136,7 @@ fn overflow(v: f32, mode: u32) -> f32 {
|
||||
|
||||
fn cell_pattern(ctx: &EffectCtx, mut b: Buf) -> Buf {
|
||||
let pattern = pattern_kind(ctx.params.e("cellPattern"));
|
||||
let hq = (6..=10).contains(&ctx.params.e("cellPattern"));
|
||||
let invert = ctx.params.b("invert");
|
||||
let contrast = ctx.params.f("contrast") as f32 / 100.0;
|
||||
let ov = ctx.params.e("overflow");
|
||||
@@ -152,11 +154,19 @@ fn cell_pattern(ctx: &EffectCtx, mut b: Buf) -> Buf {
|
||||
.params
|
||||
.b("tilingOptions/enableTiling")
|
||||
.then(|| (ctx.params.f("tilingOptions/cellsHorizontal").round().max(1.0) as i64, ctx.params.f("tilingOptions/cellsVertical").round().max(1.0) as i64));
|
||||
let subs: Vec<f64> = if hq { (0..4).map(|k| (k as f64 + 0.5) / 4.0).collect() } else { vec![0.5] };
|
||||
let reach = if hq { 2 } else { 1 };
|
||||
map_xy(&mut b.img, |x, y, _| {
|
||||
let u = (x as f64 + 0.5 - off.0) / size;
|
||||
let v = (y as f64 + 0.5 - off.1) / size;
|
||||
let (f1, f2, id) = worley(u, v, disperse, evo, seed, tile);
|
||||
let mut val = cell_value(pattern, f1, f2, id);
|
||||
let mut val = 0.0;
|
||||
for sy in &subs {
|
||||
for sx in &subs {
|
||||
let u = (x as f64 + sx - off.0) / size;
|
||||
let v = (y as f64 + sy - off.1) / size;
|
||||
let (f1, f2, id) = worley_r(u, v, disperse, evo, seed, tile, reach);
|
||||
val += cell_value(pattern, f1, f2, id);
|
||||
}
|
||||
}
|
||||
let mut val = val / (subs.len() * subs.len()) as f32;
|
||||
val = overflow((val - 0.5) * contrast + 0.5, ov);
|
||||
if invert {
|
||||
val = 1.0 - val;
|
||||
@@ -1347,4 +1357,26 @@ mod tests {
|
||||
assert!(old[0] > 0.9 && old[1] < 0.1, "{old:?}");
|
||||
assert!(new[1] > 0.9 && new[0] < 0.1, "{new:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cell_pattern_hq_variants_antialias() {
|
||||
let s = crate::find("ec.generate.cellpattern").unwrap();
|
||||
let r = |pat: u32| {
|
||||
let mut params = Params { values: s.params.iter().map(|p| (p.id.to_string(), p.default.clone())).collect() };
|
||||
params.values.insert("cellPattern".into(), Value::Enum(pat));
|
||||
params.values.insert("size".into(), num(12.0));
|
||||
let ctx = EffectCtx { params: ¶ms, time: 0.0, layer_size: [48.0, 48.0], seed: 1, adjustment: false, env: Default::default() };
|
||||
crate::apply(s, &ctx, Buf { img: Image::new(48, 48), offset: [0.0; 2], scale: 1.0 }).img
|
||||
};
|
||||
let distinct = |img: &Image| {
|
||||
let mut v: Vec<u32> = img.data.iter().map(|p| (p[0] * 10000.0) as u32).collect();
|
||||
v.sort_unstable();
|
||||
v.dedup();
|
||||
v.len()
|
||||
};
|
||||
let (std, hq) = (r(4), r(9));
|
||||
assert_eq!(CELL_PATTERNS[9], "Crystallize HQ");
|
||||
assert_ne!(std, hq);
|
||||
assert!(distinct(&hq) > distinct(&std) * 2, "{} vs {}", distinct(&hq), distinct(&std));
|
||||
}
|
||||
}
|
||||
@@ -496,14 +496,36 @@ fn advanced_spill(ctx: &EffectCtx, mut b: Buf) -> Buf {
|
||||
let amt = ctx.params.f("suppression") as f32 / 100.0;
|
||||
let range = ctx.params.f("ultraSettings/spillRange") as f32 / 100.0;
|
||||
let luma = ctx.params.f("ultraSettings/lumaCorrection") as f32 / 100.0;
|
||||
// Ultra: Tolerance limits suppression to hues near the key colour; Desaturate takes the
|
||||
// colour out of suppressed pixels; Spill Color Correction puts the removed spill back as
|
||||
// neutral light (so suppressed areas keep their brightness without the cast).
|
||||
let key = ctx.params.color("ultraSettings/keyColor");
|
||||
let key_hue = effectcraft_color::rgb_to_hsl(key[0], key[1], key[2]).0;
|
||||
let tol = if ultra { ctx.params.get("ultraSettings/tolerance").map(Value::as_f64).unwrap_or(100.0) as f32 / 100.0 } else { 1.0 };
|
||||
let desat = if ultra { ctx.params.f("ultraSettings/desaturate") as f32 / 100.0 } else { 0.0 };
|
||||
let neutral = if ultra { ctx.params.f("ultraSettings/spillColorCorrection") as f32 / 100.0 } else { 0.0 };
|
||||
b.img.map_straight(|mut c| {
|
||||
let w = if tol < 1.0 {
|
||||
let h = effectcraft_color::rgb_to_hsl(c[0], c[1], c[2]).0;
|
||||
let d = (h - key_hue).abs();
|
||||
let d = d.min(1.0 - d);
|
||||
(1.0 - d / (tol * 0.5).max(1e-4)).clamp(0.0, 1.0)
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
let mx = c[o1].max(c[o2]);
|
||||
let avg = 0.5 * (c[o1] + c[o2]);
|
||||
let limit = mx + (avg - mx) * range;
|
||||
let spill = (c[pi] - limit).max(0.0) * amt;
|
||||
let spill = (c[pi] - limit).max(0.0) * amt * w;
|
||||
c[pi] -= spill;
|
||||
let back = spill * LUMA_W[pi] * luma;
|
||||
c.map(|v| v + back)
|
||||
let back = spill * LUMA_W[pi] * luma + spill * neutral / 3.0;
|
||||
let mut c = c.map(|v| v + back);
|
||||
if desat > 0.0 && spill > 0.0 {
|
||||
let l = effectcraft_color::luminance(c[0], c[1], c[2]);
|
||||
let k = (desat * (spill * 4.0).min(1.0)).min(1.0);
|
||||
c = c.map(|v| v + (l - v) * k);
|
||||
}
|
||||
c
|
||||
});
|
||||
b
|
||||
}
|
||||
@@ -675,7 +697,10 @@ pub fn specs() -> Vec<EffectSpec> {
|
||||
p("method", "Method", Value::Enum(0), popup(&["Standard", "Ultra"])),
|
||||
p("suppression", "Suppression", num(100.0), slider(0.0, 200.0, 0.0, 100.0, 0)),
|
||||
p("ultraSettings/keyColor", "Key Color", col(0.0, 1.0, 0.0), ParamUi::Color),
|
||||
p("ultraSettings/tolerance", "Tolerance", num(50.0), pct()),
|
||||
p("ultraSettings/desaturate", "Desaturate", num(0.0), pct()),
|
||||
p("ultraSettings/spillRange", "Spill Range", num(50.0), pct()),
|
||||
p("ultraSettings/spillColorCorrection", "Spill Color Correction", num(0.0), pct()),
|
||||
p("ultraSettings/lumaCorrection", "Luma Correction", num(0.0), pct()),
|
||||
],
|
||||
advanced_spill,
|
||||
@@ -846,4 +871,26 @@ mod tests {
|
||||
let out = run("ec.key.keycleaner", &[], img.clone());
|
||||
assert_eq!(out.get(12, 2), img.get(12, 2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn advanced_spill_ultra_tolerance_desaturate_and_color_correction() {
|
||||
let ultra = |extra: &[(&str, Value)], c: [f32; 4]| {
|
||||
let mut v = vec![("method", Value::Enum(1)), ("ultraSettings/keyColor", Value::Color([0.0, 1.0, 0.0, 1.0]))];
|
||||
v.extend_from_slice(extra);
|
||||
run("ec.key.advancedspill", &v, Image::filled(1, 1, c)).get(0, 0)
|
||||
};
|
||||
// A yellow-green pixel (hue away from the key) is left alone with a narrow tolerance.
|
||||
let yg = [0.6, 0.8, 0.1, 1.0];
|
||||
assert!((ultra(&[("ultraSettings/tolerance", num(5.0))], yg)[1] - 0.8).abs() < 1e-5);
|
||||
assert!(ultra(&[("ultraSettings/tolerance", num(100.0))], yg)[1] < 0.79);
|
||||
// Desaturate greys the suppressed pixel.
|
||||
let g = [0.3, 0.8, 0.3, 1.0];
|
||||
let d = ultra(&[("ultraSettings/desaturate", num(100.0))], g);
|
||||
assert!((d[0] - d[1]).abs() < 0.05, "{d:?}");
|
||||
// Spill Color Correction keeps the brightness the suppression removed.
|
||||
let plain = ultra(&[], g);
|
||||
let cc = ultra(&[("ultraSettings/spillColorCorrection", num(100.0))], g);
|
||||
let sum = |p: [f32; 4]| p[0] + p[1] + p[2];
|
||||
assert!(sum(cc) > sum(plain) + 0.1 && (sum(cc) - sum(g)).abs() < 1e-4, "{plain:?} {cc:?}");
|
||||
}
|
||||
}
|
||||
@@ -102,8 +102,11 @@ fn mask_sdf(ctx: &EffectCtx, b: &Buf, idx: &[u32]) -> Option<Plane> {
|
||||
}
|
||||
|
||||
fn inner_outer_key(ctx: &EffectCtx, mut b: Buf) -> Buf {
|
||||
let fg_idx = [ctx.params.e("foreground"), ctx.params.e("additionalForeground")];
|
||||
let bg_idx = [ctx.params.e("background"), ctx.params.e("additionalBackground")];
|
||||
// Foreground (Inside) plus Additional Foreground 1–10; likewise for the background.
|
||||
let fg_idx: Vec<u32> =
|
||||
std::iter::once(ctx.params.e("foreground")).chain((1..=10).map(|k| ctx.params.e(&format!("additionalForeground/foreground{k}")))).collect();
|
||||
let bg_idx: Vec<u32> =
|
||||
std::iter::once(ctx.params.e("background")).chain((1..=10).map(|k| ctx.params.e(&format!("additionalBackground/background{k}")))).collect();
|
||||
let radius = (ctx.params.f("singleMaskHighlightRadius") * b.scale) as f32;
|
||||
let thin = ctx.params.f("edgeThin") * b.scale;
|
||||
let feather = ctx.params.f("edgeFeather") * b.scale;
|
||||
@@ -187,6 +190,26 @@ fn inner_outer_key(ctx: &EffectCtx, mut b: Buf) -> Buf {
|
||||
.collect(),
|
||||
);
|
||||
}
|
||||
// Cleanup Foreground / Background: brush strokes along mask paths that paint the matte
|
||||
// towards opaque / transparent (Brush Radius, Brush Pressure).
|
||||
for (side, target) in [("Foreground", 1.0f32), ("Background", 0.0)] {
|
||||
for k in 1..=10 {
|
||||
let g = format!("cleanup{side}/cleanup{side}{k}");
|
||||
let idx = ctx.params.e(&format!("{g}/path"));
|
||||
let Some(m) = (idx > 0).then(|| ctx.env.masks.get(idx as usize - 1)).flatten() else { continue };
|
||||
let rad = (ctx.params.f(&format!("{g}/brushRadius")) * b.scale).max(0.5);
|
||||
let pressure = (ctx.params.f(&format!("{g}/brushPressure")) / 100.0).clamp(0.0, 1.0) as f32;
|
||||
let pts: Vec<[f64; 2]> = m.points.iter().map(|q| [q[0] * b.scale + b.offset[0], q[1] * b.scale + b.offset[1]]).collect();
|
||||
alpha.data.par_iter_mut().enumerate().for_each(|(i, a)| {
|
||||
let (x, y) = ((i % w) as f64 + 0.5, (i / w) as f64 + 0.5);
|
||||
let d = dist_to_poly(&pts, m.closed, x, y);
|
||||
if d < rad + 1.0 {
|
||||
let cov = ((rad - d) / rad.max(1.0) * 2.0).clamp(0.0, 1.0) as f32 * pressure;
|
||||
*a += (target - *a) * cov;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if thin.abs() > 0.01 {
|
||||
alpha = morph_frac(&alpha, thin.abs(), thin < 0.0);
|
||||
}
|
||||
@@ -332,18 +355,34 @@ pub fn specs() -> Vec<EffectSpec> {
|
||||
spec(
|
||||
"ec.key.innerouter",
|
||||
"Inner/Outer Key",
|
||||
vec![
|
||||
p("foreground", "Foreground (Inside)", Value::Enum(0), mask_options()),
|
||||
p("additionalForeground", "Additional Foreground", Value::Enum(0), mask_options()),
|
||||
p("background", "Background (Outside)", Value::Enum(0), mask_options()),
|
||||
p("additionalBackground", "Additional Background", Value::Enum(0), mask_options()),
|
||||
p("singleMaskHighlightRadius", "Single Mask Highlight Radius", num(0.0), slider(0.0, 100.0, 0.0, 100.0, 1)),
|
||||
p("edgeThin", "Edge Thin", num(0.0), slider(-10.0, 10.0, -10.0, 10.0, 1)),
|
||||
p("edgeFeather", "Edge Feather", num(0.0), slider(0.0, 100.0, 0.0, 50.0, 1)),
|
||||
p("edgeThreshold", "Edge Threshold", num(0.0), pct()),
|
||||
p("invertExtraction", "Invert Extraction", Value::Bool(false), ParamUi::Checkbox),
|
||||
p("blendWithOriginal", "Blend with Original", num(0.0), pct()),
|
||||
],
|
||||
{
|
||||
let leak = |s: String| -> &'static str { Box::leak(s.into_boxed_str()) };
|
||||
let mut v = vec![p("foreground", "Foreground (Inside)", Value::Enum(0), mask_options())];
|
||||
for k in 1..=10 {
|
||||
v.push(p(leak(format!("additionalForeground/foreground{k}")), leak(format!("Foreground {k}")), Value::Enum(0), mask_options()));
|
||||
}
|
||||
v.push(p("background", "Background (Outside)", Value::Enum(0), mask_options()));
|
||||
for k in 1..=10 {
|
||||
v.push(p(leak(format!("additionalBackground/background{k}")), leak(format!("Background {k}")), Value::Enum(0), mask_options()));
|
||||
}
|
||||
for side in ["Foreground", "Background"] {
|
||||
for k in 1..=10 {
|
||||
let g = format!("cleanup{side}/cleanup{side}{k}");
|
||||
v.push(p(leak(format!("{g}/path")), "Path", Value::Enum(0), mask_options()));
|
||||
v.push(p(leak(format!("{g}/brushRadius")), "Brush Radius", num(10.0), slider(0.0, 1000.0, 0.0, 100.0, 1)));
|
||||
v.push(p(leak(format!("{g}/brushPressure")), "Brush Pressure", num(80.0), pct()));
|
||||
}
|
||||
}
|
||||
v.extend([
|
||||
p("singleMaskHighlightRadius", "Single Mask Highlight Radius", num(0.0), slider(0.0, 100.0, 0.0, 100.0, 1)),
|
||||
p("edgeThin", "Edge Thin", num(0.0), slider(-10.0, 10.0, -10.0, 10.0, 1)),
|
||||
p("edgeFeather", "Edge Feather", num(0.0), slider(0.0, 100.0, 0.0, 50.0, 1)),
|
||||
p("edgeThreshold", "Edge Threshold", num(0.0), pct()),
|
||||
p("invertExtraction", "Invert Extraction", Value::Bool(false), ParamUi::Checkbox),
|
||||
p("blendWithOriginal", "Blend with Original", num(0.0), pct()),
|
||||
]);
|
||||
v
|
||||
},
|
||||
inner_outer_key,
|
||||
),
|
||||
spec(
|
||||
@@ -478,4 +517,47 @@ mod tests {
|
||||
assert_eq!(out.img.get(2, 10), img.get(2, 10));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inner_outer_key_more_masks_and_cleanup_strokes() {
|
||||
let sq = |x0: f64, x1: f64| MaskShape { name: String::new(), points: vec![[x0, 2.0], [x1, 2.0], [x1, 8.0], [x0, 8.0]], closed: true, inverted: false };
|
||||
// Masks 1..4: foreground squares; mask 5: a horizontal cleanup stroke across the middle.
|
||||
let masks = vec![
|
||||
sq(1.0, 5.0),
|
||||
sq(7.0, 11.0),
|
||||
sq(13.0, 17.0),
|
||||
sq(19.0, 23.0),
|
||||
MaskShape { name: String::new(), points: vec![[0.0, 5.0], [30.0, 5.0]], closed: false, inverted: false },
|
||||
];
|
||||
let env = EffectEnv { masks: &masks, ..Default::default() };
|
||||
let img = Image::filled(30, 10, [0.5, 0.5, 0.5, 1.0]);
|
||||
let vals = [
|
||||
("foreground", Value::Enum(1)),
|
||||
("additionalForeground/foreground1", Value::Enum(2)),
|
||||
("additionalForeground/foreground2", Value::Enum(3)),
|
||||
("additionalForeground/foreground5", Value::Enum(4)),
|
||||
];
|
||||
let out = run_fx("ec.key.innerouter", &vals, img.clone(), 0.0, env).img;
|
||||
for x in [3, 9, 15, 21] {
|
||||
assert_eq!(out.get(x, 3)[3], 1.0, "foreground square at {x}");
|
||||
}
|
||||
assert_eq!(out.get(27, 3)[3], 0.0);
|
||||
// Cleanup Background along mask 5 erases a band; Cleanup Foreground restores outside.
|
||||
let mut bg = vals.to_vec();
|
||||
bg.extend([
|
||||
("cleanupBackground/cleanupBackground1/path", Value::Enum(5)),
|
||||
("cleanupBackground/cleanupBackground1/brushRadius", num(1.5)),
|
||||
("cleanupBackground/cleanupBackground1/brushPressure", num(100.0)),
|
||||
]);
|
||||
let erased = run_fx("ec.key.innerouter", &bg, img.clone(), 0.0, env).img;
|
||||
assert!(erased.get(3, 5)[3] < 0.1 && erased.get(3, 2)[3] > 0.9);
|
||||
let mut fg = vals.to_vec();
|
||||
fg.extend([
|
||||
("cleanupForeground/cleanupForeground3/path", Value::Enum(5)),
|
||||
("cleanupForeground/cleanupForeground3/brushRadius", num(1.5)),
|
||||
("cleanupForeground/cleanupForeground3/brushPressure", num(100.0)),
|
||||
]);
|
||||
let painted = run_fx("ec.key.innerouter", &fg, img, 0.0, env).img;
|
||||
assert!(painted.get(27, 5)[3] > 0.9 && painted.get(27, 2)[3] < 0.1);
|
||||
}
|
||||
}
|
||||
@@ -626,6 +626,30 @@ pub const PARAM_GROUPS: &[(&str, &str)] = &[
|
||||
("options", "Options"),
|
||||
// Keying, Matte, Obsolete (Key Light's groups are in `keylight::GROUPS`).
|
||||
("ultraSettings", "Ultra Settings"),
|
||||
("additionalForeground", "Additional Foreground"),
|
||||
("additionalBackground", "Additional Background"),
|
||||
("cleanupForeground", "Cleanup Foreground"),
|
||||
("cleanupBackground", "Cleanup Background"),
|
||||
("cleanupForeground1", "Cleanup Foreground 1"),
|
||||
("cleanupForeground2", "Cleanup Foreground 2"),
|
||||
("cleanupForeground3", "Cleanup Foreground 3"),
|
||||
("cleanupForeground4", "Cleanup Foreground 4"),
|
||||
("cleanupForeground5", "Cleanup Foreground 5"),
|
||||
("cleanupForeground6", "Cleanup Foreground 6"),
|
||||
("cleanupForeground7", "Cleanup Foreground 7"),
|
||||
("cleanupForeground8", "Cleanup Foreground 8"),
|
||||
("cleanupForeground9", "Cleanup Foreground 9"),
|
||||
("cleanupForeground10", "Cleanup Foreground 10"),
|
||||
("cleanupBackground1", "Cleanup Background 1"),
|
||||
("cleanupBackground2", "Cleanup Background 2"),
|
||||
("cleanupBackground3", "Cleanup Background 3"),
|
||||
("cleanupBackground4", "Cleanup Background 4"),
|
||||
("cleanupBackground5", "Cleanup Background 5"),
|
||||
("cleanupBackground6", "Cleanup Background 6"),
|
||||
("cleanupBackground7", "Cleanup Background 7"),
|
||||
("cleanupBackground8", "Cleanup Background 8"),
|
||||
("cleanupBackground9", "Cleanup Background 9"),
|
||||
("cleanupBackground10", "Cleanup Background 10"),
|
||||
("fillAndStroke", "Fill and Stroke"),
|
||||
("pathOptions", "Path Options"),
|
||||
("controlPoints", "Control Points"),
|
||||
|
||||
@@ -38,6 +38,10 @@ pub const PARAM_ID_ALIASES: &[(&str, &str, &str)] = &[
|
||||
("ec.matte.rotobrush", "refineEdgeMatte/extendWhereSmoothed", "refineEdgeMatte/decontamination/extendWhereSmoothed"),
|
||||
("ec.matte.rotobrush", "refineEdgeMatte/increaseDecontaminationRadius", "refineEdgeMatte/decontamination/increaseDecontaminationRadius"),
|
||||
("ec.matte.rotobrush", "refineEdgeMatte/viewDecontaminationMap", "refineEdgeMatte/decontamination/viewDecontaminationMap"),
|
||||
// Keying: Inner/Outer Key's single additional masks became Additional Foreground /
|
||||
// Background 1 (of 10).
|
||||
("ec.key.innerouter", "additionalForeground", "additionalForeground/foreground1"),
|
||||
("ec.key.innerouter", "additionalBackground", "additionalBackground/background1"),
|
||||
// Generate: Radio Waves' growth speed was called Velocity; Velocity now moves the wave.
|
||||
("ec.generate.radiowaves", "velocity", "waveMotion/expansion"),
|
||||
];
|
||||
@@ -153,6 +157,8 @@ pub fn legacy_default(effect: &str, param: &str) -> Option<Value> {
|
||||
("ec.noise.removegrain", "noiseReductionSettings/mode") => Value::Enum(1),
|
||||
// Audio: the Compressor's release was always manual.
|
||||
("ec.audio.compressor", "autoRelease") => Value::Bool(false),
|
||||
// Keying: Advanced Spill Suppressor's Ultra mode had no hue tolerance.
|
||||
("ec.key.advancedspill", "ultraSettings/tolerance") => Value::Scalar(100.0),
|
||||
// Transition: Block Dissolve's blocks had hard edges.
|
||||
("ec.transition.blockdissolve", "softEdges") => Value::Bool(false),
|
||||
_ => return None,
|
||||
@@ -435,4 +441,18 @@ mod tests {
|
||||
assert_eq!(prop_at(&mut g, "details/reds/redsCyan").unwrap().value, Value::Scalar(40.0));
|
||||
assert_eq!(g.sub("details").unwrap().sub("reds").unwrap().name, "Reds");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inner_outer_key_additional_masks_move_into_their_groups() {
|
||||
let spec = find("ec.key.innerouter").unwrap();
|
||||
let mut next = 1;
|
||||
let mut g = instantiate(spec, &mut Ids(&mut next), "Inner/Outer Key", [100.0, 50.0]);
|
||||
let mut pr = take_at(&mut g, "additionalForeground/foreground1").unwrap();
|
||||
pr.match_id = "additionalForeground".into();
|
||||
pr.value = Value::Enum(3);
|
||||
g.children.push(pr.into());
|
||||
assert!(upgrade_instance(spec, &mut g, &mut Ids(&mut next), [100.0, 50.0]));
|
||||
assert!(g.get("additionalForeground").is_none());
|
||||
assert_eq!(prop_at(&mut g, "additionalForeground/foreground1").unwrap().value, Value::Enum(3));
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -3,11 +3,11 @@
|
||||
<!-- Generated from the effect registry (crates/effects/src/catalog.rs); do not edit by hand.
|
||||
Regenerate: UPDATE_DOCS=1 cargo test -p effectcraft-effects --lib effects_doc_is_current -->
|
||||
|
||||
EffectCraft ships 303 effects with 2773 parameters in total, grouped into the same categories as the Effects & Presets panel. Every effect is our own implementation, written from public behaviour descriptions and standard image-processing literature. Parameter names, order, popup options, units, defaults and ranges follow the reference application so that projects, expressions and muscle memory carry over.
|
||||
EffectCraft ships 303 effects with 2854 parameters in total, grouped into the same categories as the Effects & Presets panel. Every effect is our own implementation, written from public behaviour descriptions and standard image-processing literature. Parameter names, order, popup options, units, defaults and ranges follow the reference application so that projects, expressions and muscle memory carry over.
|
||||
|
||||
- **GPU**: 24 effects also run on the GPU compositor with identical results.
|
||||
- **32**: 303 effects process 32-bit float (HDR, overbright) pixels without clamping.
|
||||
- **Status**: 287 implemented in full, 16 partial (what is missing is listed).
|
||||
- **Status**: 290 implemented in full, 13 partial (what is missing is listed).
|
||||
|
||||
Effects are addressed by id (`ec.<category>.<name>`) or display name in commands, scripts and the control channel, and every parameter by its id (`effects/#1/blurriness`).
|
||||
|
||||
@@ -197,7 +197,7 @@ Effects are addressed by id (`ec.<category>.<name>`) or display name in commands
|
||||
| CC Light Rays | `ec.generate.cclightrays` | 8 | | 32 | Implemented |
|
||||
| CC Light Sweep | `ec.generate.cclightsweep` | 9 | | 32 | Implemented |
|
||||
| CC Threads | `ec.generate.ccthreads` | 8 | | 32 | Implemented |
|
||||
| Cell Pattern | `ec.generate.cellpattern` | 14 | | 32 | Partial: HQ variants render like the standard ones |
|
||||
| Cell Pattern | `ec.generate.cellpattern` | 14 | | 32 | Implemented |
|
||||
| Checkerboard | `ec.generate.checkerboard` | 10 | | 32 | Implemented |
|
||||
| Circle | `ec.generate.circle` | 11 | | 32 | Implemented |
|
||||
| Ellipse | `ec.generate.ellipse` | 8 | | 32 | Implemented |
|
||||
@@ -235,13 +235,13 @@ Effects are addressed by id (`ec.<category>.<name>`) or display name in commands
|
||||
|
||||
| Effect | Id | Params | GPU | 32 | Status |
|
||||
|---|---|---:|:-:|:-:|---|
|
||||
| Advanced Spill Suppressor | `ec.key.advancedspill` | 5 | | 32 | Partial: no Tolerance, Desaturate or Spill Color Correction |
|
||||
| Advanced Spill Suppressor | `ec.key.advancedspill` | 8 | | 32 | Implemented |
|
||||
| CC Simple Wire Removal | `ec.key.ccsimplewireremoval` | 7 | | 32 | Implemented |
|
||||
| Color Difference Key | `ec.key.colordifference` | 16 | | 32 | Implemented |
|
||||
| Color Range | `ec.key.colorrange` | 8 | | 32 | Implemented |
|
||||
| Difference Matte | `ec.key.differencematte` | 6 | | 32 | Implemented |
|
||||
| Extract | `ec.key.extract` | 6 | | 32 | Implemented |
|
||||
| Inner/Outer Key | `ec.key.innerouter` | 10 | | 32 | Partial: one additional mask per side; no cleanup strokes |
|
||||
| Inner/Outer Key | `ec.key.innerouter` | 88 | | 32 | Implemented |
|
||||
| Key Cleaner | `ec.key.keycleaner` | 4 | | 32 | Implemented |
|
||||
| Key Light | `ec.keying.keylight` | 42 | | 32 | Partial: no Source Crops X/Y Method or Edge Colour; no colour suppression / balancing |
|
||||
| Linear Color Key | `ec.key.linearcolor` | 6 | | 32 | Implemented |
|
||||
|
||||
Reference in new issue
Block a user