1use ffcine_core::lut::Lut;
10
11use crate::shaders::LookParams;
12
13pub const DEFAULT_SIZE: usize = 65;
15
16#[derive(Debug, Clone, Default)]
18pub struct Bake<'a> {
19 pub camera: Option<&'a Lut>,
21 pub lut: Option<&'a Lut>,
23 pub look: LookParams,
25}
26
27impl Bake<'_> {
28 pub fn eval(&self, rgb: [f32; 3]) -> [f32; 3] {
30 let mut v = rgb;
31 for l in [self.camera, self.lut].into_iter().flatten() {
32 v = l.sample(v);
33 }
34 if self.look.is_identity() {
35 v
36 } else {
37 self.look.apply(v)
38 }
39 }
40
41 pub fn bake(&self, size: usize) -> Lut {
43 let mut lut = Lut::identity(size.max(2));
44 for e in &mut lut.data {
45 *e = self.eval(*e);
46 }
47 let parts: Vec<&str> = [self.camera, self.lut]
48 .into_iter()
49 .flatten()
50 .map(|l| l.title.as_deref().unwrap_or("LUT"))
51 .chain((!self.look.is_identity()).then_some("look"))
52 .collect();
53 lut.title = Some(format!("ffcine bake: {}", parts.join(" + ")));
54 lut
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61
62 fn mono(f: impl Fn(f32) -> f32) -> Lut {
63 let mut l = Lut::identity(17);
64 for e in &mut l.data {
65 *e = e.map(&f);
66 }
67 l
68 }
69
70 #[test]
71 fn identity_bakes_to_identity() {
72 let b = Bake::default().bake(9);
73 assert_eq!(b.data, Lut::identity(9).data);
74 }
75
76 #[test]
77 fn chain_order_and_accuracy() {
78 let cam = mono(|x| x * x);
79 let grade = mono(|x| 0.5 + 0.5 * x);
80 let look = LookParams {
81 saturation: 0.8,
82 contrast: 0.1,
83 ..LookParams::default()
84 };
85 let b = Bake {
86 camera: Some(&cam),
87 lut: Some(&grade),
88 look,
89 };
90 let baked = b.bake(DEFAULT_SIZE);
91 for v in [[0.1, 0.4, 0.9], [0.33, 0.33, 0.7], [0.95, 0.05, 0.5]] {
93 let want = b.eval(v);
94 let got = baked.sample(v);
95 for c in 0..3 {
96 assert!(
97 (want[c] - got[c]).abs() < 0.01,
98 "{v:?}: {want:?} vs {got:?}"
99 );
100 }
101 }
102 let plain = Bake {
104 camera: Some(&cam),
105 lut: Some(&grade),
106 look: LookParams::default(),
107 };
108 assert!((plain.eval([0.5; 3])[0] - 0.625).abs() < 0.01);
109 assert!(baked.title.unwrap().ends_with("+ look"));
110 }
111}