1use crate::deserialize::deserialize_space_separated;
2use anyhow::{bail, ensure, Context, Result};
3
4#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Deserialize)]
5#[cfg_attr(
6 feature = "rkyv",
7 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize),
8 archive_attr(derive(bytecheck::CheckBytes))
9)]
10pub enum BitDepth {
11 #[serde(rename = "8i")]
12 I8,
13 #[serde(rename = "10i")]
14 I10,
15 #[serde(rename = "12i")]
16 I12,
17 #[serde(rename = "16i")]
18 I16,
19 #[serde(rename = "16f")]
20 F16,
21 #[serde(rename = "32f")]
22 F32,
23}
24
25#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Deserialize)]
26#[cfg_attr(
27 feature = "rkyv",
28 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize),
29 archive_attr(derive(bytecheck::CheckBytes))
30)]
31#[serde(rename_all = "camelCase")]
32pub struct OperatorBitDepth {
33 in_bit_depth: BitDepth,
34 out_bit_depth: BitDepth,
35}
36
37impl OperatorBitDepth {
38 fn validate(&self) -> Result<()> {
39 ensure!(
40 self.in_bit_depth == self.out_bit_depth && self.out_bit_depth == BitDepth::F32,
41 "currently only BitDepth::F32 is supported, found in bit depth: {:?}, out bit depth: {:?}", self.in_bit_depth, self.out_bit_depth
42 );
43 Ok(())
44 }
45}
46
47#[derive(Clone, Debug, PartialEq, serde::Deserialize)]
48#[cfg_attr(
49 feature = "rkyv",
50 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize),
51 archive_attr(derive(bytecheck::CheckBytes))
52)]
53pub struct Array {
54 #[serde(deserialize_with = "deserialize_space_separated")]
55 pub dim: Vec<u32>,
56 #[serde(rename = "$value", deserialize_with = "deserialize_space_separated")]
57 pub data: Vec<f32>,
58}
59
60#[derive(Clone, Copy, Debug, PartialEq, serde::Deserialize)]
65#[cfg_attr(
66 feature = "rkyv",
67 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize),
68 archive_attr(derive(bytecheck::CheckBytes))
69)]
70#[serde(rename_all = "camelCase")]
71pub struct Range {
72 #[serde(flatten)]
73 bit_depth: OperatorBitDepth,
74 min_in_value: f32,
75 max_in_value: f32,
76 min_out_value: f32,
77 max_out_value: f32,
78}
79
80impl Range {
81 pub fn scale(&self) -> f32 {
82 (self.max_out_value - self.min_out_value) / (self.max_in_value - self.min_in_value)
83 }
84
85 pub fn offset(&self) -> f32 {
86 self.min_out_value - self.scale() * self.min_in_value
87 }
88}
89
90#[derive(Clone, Debug, PartialEq, serde::Deserialize)]
91#[cfg_attr(
92 feature = "rkyv",
93 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize),
94 archive_attr(derive(bytecheck::CheckBytes))
95)]
96#[serde(rename = "camelCase")]
97pub struct Lut1d {
98 #[serde(flatten)]
99 pub bit_depth: OperatorBitDepth,
100 #[serde(rename = "Array")]
101 pub array: Array,
102}
103
104impl Lut1d {
105 pub fn validate(&self) -> Result<()> {
106 self.bit_depth.validate()?;
107
108 if self.array.dim.len() != 2 {
109 bail!(
110 "A dim attribute defined for a Lut1d should have 2 elements; {} were found instead.",
111 self.array.dim.len()
112 )
113 }
114
115 if self.array.dim.iter().product::<u32>() != self.array.data.len() as u32 {
116 bail!(
117 "expected {} elements, got {}",
118 self.array.dim.iter().product::<u32>(),
119 self.array.data.len()
120 )
121 }
122 Ok(())
123 }
124}
125
126#[derive(Clone, Debug, PartialEq, serde::Deserialize)]
127#[cfg_attr(
128 feature = "rkyv",
129 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize),
130 archive_attr(derive(bytecheck::CheckBytes))
131)]
132#[serde(rename_all = "camelCase")]
133pub struct Lut3d {
134 #[serde(flatten)]
135 pub bit_depth: OperatorBitDepth,
136 #[serde(rename = "Array")]
137 pub array: Array,
138}
139
140impl Lut3d {
141 fn validate(&self) -> Result<()> {
142 self.bit_depth.validate()?;
143
144 if self.array.dim.len() == 4 {
145 if self.array.dim[0] != self.array.dim[1] || self.array.dim[0] != self.array.dim[2] {
146 bail!(
147 "A Lut3d should have the same dimensions on all three axes. Found {:?} instead.",
148 self.array.dim
149 )
150 }
151 if self.array.dim[3] != 3 {
152 bail!(
153 "A Lut3d should have 3 color components; {} were found instead.",
154 self.array.dim[3]
155 )
156 }
157 } else {
158 bail!("A dim attribute defined for a Lut3D should have 4 elements; {} were found instead.", self.array.dim.len())
159 }
160
161 if self.array.dim.iter().product::<u32>() != self.array.data.len() as u32 {
162 bail!(
163 "expected {} elements, got {}",
164 self.array.dim.iter().product::<u32>(),
165 self.array.data.len()
166 )
167 }
168 Ok(())
169 }
170}
171
172#[derive(Clone, Debug, PartialEq, serde::Deserialize)]
173#[cfg_attr(
174 feature = "rkyv",
175 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize),
176 archive_attr(derive(bytecheck::CheckBytes))
177)]
178#[serde(rename_all = "camelCase")]
179pub struct ProcessList {
180 #[serde(rename = "compCLFversion")]
181 pub comp_clf_version: u32,
182 pub id: String,
183 #[serde(rename = "$value")]
184 pub operators: Vec<Operator>,
185}
186
187impl ProcessList {
188 pub fn validate(&self) -> Result<()> {
189 if self.comp_clf_version > 3 {
190 bail!(
191 "CLF versions higher than 3 (currently parsing {}) are not yet supported",
192 self.comp_clf_version
193 )
194 }
195
196 for x in &self.operators {
197 x.validate()?
198 }
199
200 Ok(())
201 }
202}
203
204#[derive(Clone, Debug, PartialEq, serde::Deserialize)]
205#[cfg_attr(
206 feature = "rkyv",
207 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize),
208 archive_attr(derive(bytecheck::CheckBytes))
209)]
210#[serde(deny_unknown_fields)]
211pub enum Operator {
212 Range(Range),
213 #[serde(rename = "LUT1D")]
214 Lut1D(Lut1d),
215 #[serde(rename = "LUT3D")]
216 Lut3D(Lut3d),
217}
218
219impl Operator {
220 fn validate(&self) -> Result<()> {
221 match self {
222 Self::Range(range) => range
223 .bit_depth
224 .validate()
225 .context("Range operator invalid."),
226 Self::Lut1D(lut1d) => lut1d.validate().context("LUT 1D operator invalid."),
227 Self::Lut3D(lut3d) => lut3d.validate().context("LUT 3D operator invalid."),
228 }
229 }
230}