mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-24 10:09:32 -06:00
M9.11: OCIO transforms, Color Stabilizer and Color Profile Converter
Built-in minimal OCIO config (ACES2065-1, ACEScg, ACEScct, sRGB, Rec.709, Rec.2020, Display P3, linear variants, XYZ) from published primaries, transfer functions and Bradford adaptation; CDL v1.2 maths; .cube/.3dl/.csp and .cc/.ccc/.cdl file transforms with nearest/trilinear/tetrahedral interpolation and inverse; displays/views/looks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
97cf7e2c02
commit
fe81588867
@@ -27,6 +27,7 @@ mod misc;
|
||||
mod noise;
|
||||
mod noise2;
|
||||
mod obsolete;
|
||||
mod ocio;
|
||||
pub mod paint;
|
||||
mod perspective;
|
||||
mod perspective2;
|
||||
@@ -245,6 +246,10 @@ pub struct EffectEnv<'a> {
|
||||
pub frame_rate: f64,
|
||||
/// Index of the running effect in its layer's stack (for [`EffectHost::self_at`]).
|
||||
pub effect_index: usize,
|
||||
/// The project's working colour space (`None` = unmanaged, treated as sRGB).
|
||||
pub working_space: Option<effectcraft_color::ColorSpace>,
|
||||
/// Working-space pixels are linear light (Linearize Working Space).
|
||||
pub working_linear: bool,
|
||||
}
|
||||
|
||||
/// What an effect gets to render with.
|
||||
@@ -348,6 +353,7 @@ pub fn registry() -> &'static [EffectSpec] {
|
||||
v.extend(noise2::specs());
|
||||
v.extend(color3::specs());
|
||||
v.extend(obsolete::specs());
|
||||
v.extend(ocio::specs());
|
||||
v.extend(time_fx::specs());
|
||||
v.extend(audio_fx::specs());
|
||||
v.extend(paint::specs());
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -153,7 +153,7 @@ fn cineon(ctx: &EffectCtx, mut b: Buf) -> Buf {
|
||||
// ---- Colour LUTs (.cube) ----
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub(crate) struct Lut {
|
||||
pub struct Lut {
|
||||
pub n1: usize,
|
||||
pub d1: Vec<[f32; 3]>,
|
||||
pub n3: usize,
|
||||
@@ -163,7 +163,7 @@ pub(crate) struct Lut {
|
||||
}
|
||||
|
||||
/// Parse `.cube` text (TITLE, LUT_1D_SIZE, LUT_3D_SIZE, DOMAIN_MIN/MAX, data red-fastest).
|
||||
pub(crate) fn parse_cube(text: &str) -> Option<Lut> {
|
||||
pub fn parse_cube(text: &str) -> Option<Lut> {
|
||||
let mut lut = Lut { n1: 0, d1: vec![], n3: 0, d3: vec![], dmin: [0.0; 3], dmax: [1.0; 3] };
|
||||
let mut rows: Vec<[f32; 3]> = Vec::new();
|
||||
let three = |it: &mut std::str::SplitWhitespace| -> Option<[f32; 3]> {
|
||||
@@ -238,6 +238,57 @@ impl Lut {
|
||||
}
|
||||
c
|
||||
}
|
||||
/// Apply with a choice of 3D interpolation: 0 nearest, 1 trilinear, otherwise tetrahedral.
|
||||
pub fn apply_interp(&self, c: [f32; 3], interp: u32) -> [f32; 3] {
|
||||
let mut c = c;
|
||||
if self.n1 > 1 {
|
||||
let t = self.norm(c);
|
||||
c = [0, 1, 2].map(|i| {
|
||||
let x = t[i] * (self.n1 - 1) as f32;
|
||||
if interp == 0 {
|
||||
return self.d1[(x.round() as usize).min(self.n1 - 1)][i];
|
||||
}
|
||||
let i0 = (x.floor() as usize).min(self.n1 - 2);
|
||||
let f = x - i0 as f32;
|
||||
self.d1[i0][i] + (self.d1[i0 + 1][i] - self.d1[i0][i]) * f
|
||||
});
|
||||
}
|
||||
if self.n3 > 1 {
|
||||
let t = self.norm(c);
|
||||
c = match interp {
|
||||
0 => self.nearest(t),
|
||||
1 => self.trilinear(t),
|
||||
_ => self.tetra(t),
|
||||
};
|
||||
}
|
||||
c
|
||||
}
|
||||
fn nearest(&self, t: [f32; 3]) -> [f32; 3] {
|
||||
let n = self.n3;
|
||||
let s = (n - 1) as f32;
|
||||
let i = |x: f32| ((x * s).round() as usize).min(n - 1);
|
||||
self.d3[i(t[0]) + i(t[1]) * n + i(t[2]) * n * n]
|
||||
}
|
||||
/// Trilinear interpolation on the lattice (red fastest).
|
||||
pub fn trilinear(&self, t: [f32; 3]) -> [f32; 3] {
|
||||
let n = self.n3;
|
||||
let s = (n - 1) as f32;
|
||||
let idx = |x: f32| {
|
||||
let v = x * s;
|
||||
let i = (v.floor() as usize).min(n - 2);
|
||||
(i, v - i as f32)
|
||||
};
|
||||
let (r, fr) = idx(t[0]);
|
||||
let (g, fg) = idx(t[1]);
|
||||
let (b, fb) = idx(t[2]);
|
||||
let at = |dr: usize, dg: usize, db: usize| self.d3[(r + dr) + (g + dg) * n + (b + db) * n * n];
|
||||
let l = |a: [f32; 3], b: [f32; 3], f: f32| [a[0] + (b[0] - a[0]) * f, a[1] + (b[1] - a[1]) * f, a[2] + (b[2] - a[2]) * f];
|
||||
let c00 = l(at(0, 0, 0), at(1, 0, 0), fr);
|
||||
let c10 = l(at(0, 1, 0), at(1, 1, 0), fr);
|
||||
let c01 = l(at(0, 0, 1), at(1, 0, 1), fr);
|
||||
let c11 = l(at(0, 1, 1), at(1, 1, 1), fr);
|
||||
l(l(c00, c10, fg), l(c01, c11, fg), fb)
|
||||
}
|
||||
fn tetra(&self, t: [f32; 3]) -> [f32; 3] {
|
||||
let n = self.n3;
|
||||
let s = (n - 1) as f32;
|
||||
|
||||
@@ -527,8 +527,15 @@ impl<'a> Renderer<'a> {
|
||||
let layer_size = if size.0 == 0 { [ctx.comp.width as f64, ctx.comp.height as f64] } else { [size.0 as f64, size.1 as f64] };
|
||||
let mask_shapes = masks::shapes(ctx, layer);
|
||||
let host = FxHost { r: self, ctx, layer, index: Default::default() };
|
||||
let env =
|
||||
EffectEnv { masks: &mask_shapes, host: Some(&host), comp_time: ctx.time.seconds(), frame_rate: ctx.comp.frame_rate.as_f64(), effect_index: 0 };
|
||||
let env = EffectEnv {
|
||||
masks: &mask_shapes,
|
||||
host: Some(&host),
|
||||
comp_time: ctx.time.seconds(),
|
||||
frame_rate: ctx.comp.frame_rate.as_f64(),
|
||||
effect_index: 0,
|
||||
working_space: self.pipe.space,
|
||||
working_linear: self.pipe.linear,
|
||||
};
|
||||
// Video effects in stack order (index, group, spec); disabled and audio effects skipped.
|
||||
let stack: Vec<(usize, &effectcraft_project::PropGroup, &'static effectcraft_effects::EffectSpec)> = fx
|
||||
.groups()
|
||||
|
||||
Reference in New Issue
Block a user