1use serde::{Deserialize, Serialize};
2
3use super::either::{Color, either};
4
5pub const STYLES: &[&str] = &[
6 "cel",
7 "bw",
8 "noir",
9 "vignette",
10 "neon",
11 "rubber_hose",
12 "sepia",
13 "film",
14 "crt",
15 "one_bit",
16 "halftone",
17 "comic",
18 "modern_comic",
19 "watercolor",
20 "paper_grain",
21 "pixel",
22 "duotone",
23 "gradient_map",
24 "posterize",
25 "kuwahara",
26 "tilt_shift",
27];
28
29#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
30#[serde(rename_all = "snake_case")]
31pub enum Style {
32 Cel,
33 Bw,
34 Noir,
35 Vignette,
36 Neon,
37 RubberHose,
38 Sepia,
39 Film,
40 Crt,
41 OneBit,
42 Halftone,
43 Comic,
44 ModernComic,
45 Watercolor,
46 PaperGrain,
47 Pixel,
48 Duotone,
49 GradientMap,
50 Posterize,
51 Kuwahara,
52 TiltShift,
53}
54
55#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
56#[serde(rename_all = "snake_case")]
57pub enum WarmthKind {
58 Linear,
59 LumaCurve,
60}
61
62#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
63#[serde(deny_unknown_fields)]
64pub struct WarmthTable {
65 #[serde(default, skip_serializing_if = "Option::is_none")]
66 pub amount: Option<f32>,
67 #[serde(default, skip_serializing_if = "Option::is_none")]
68 pub high: Option<[f32; 2]>,
69 #[serde(default, skip_serializing_if = "Option::is_none")]
70 pub highlight: Option<f32>,
71 #[serde(default, skip_serializing_if = "Option::is_none")]
72 pub kind: Option<WarmthKind>,
73 #[serde(default, skip_serializing_if = "Option::is_none")]
74 pub low: Option<[f32; 2]>,
75 #[serde(default, skip_serializing_if = "Option::is_none")]
76 pub shadow: Option<f32>,
77}
78
79#[derive(Clone, Debug, PartialEq, Serialize)]
80#[serde(untagged)]
81pub enum Warmth {
82 Amount(f32),
83 Table(WarmthTable),
84}
85
86either!(Warmth, "a number or a table", number => Amount, map => Table(WarmthTable),);
87
88#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
89#[serde(rename_all = "snake_case")]
90pub enum ToneKind {
91 Aces,
92 Agx,
93 Neutral,
94}
95
96#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
97#[serde(deny_unknown_fields)]
98pub struct ToneTable {
99 pub kind: ToneKind,
100 #[serde(default, skip_serializing_if = "Option::is_none")]
101 pub start: Option<f32>,
102 #[serde(default, skip_serializing_if = "Option::is_none")]
103 pub clamp: Option<bool>,
104}
105
106#[derive(Clone, Debug, PartialEq, Serialize)]
107#[serde(untagged)]
108pub enum Tone {
109 Kind(ToneKind),
110 Table(ToneTable),
111}
112
113either!(Tone, "aces, agx, neutral or a table", text => Kind(ToneKind), map => Table(ToneTable),);
114
115#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
116#[serde(rename_all = "snake_case")]
117pub enum BloomKind {
118 Box,
119 Rings,
120 Tent,
121}
122
123#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
124#[serde(deny_unknown_fields)]
125pub struct BloomTable {
126 #[serde(default, skip_serializing_if = "Option::is_none")]
127 pub clamp: Option<f32>,
128 #[serde(default, skip_serializing_if = "Option::is_none")]
129 pub down: Option<f32>,
130 #[serde(default, skip_serializing_if = "Option::is_none")]
131 pub gains: Option<[f32; 4]>,
132 #[serde(default, skip_serializing_if = "Option::is_none")]
133 pub kind: Option<BloomKind>,
134 #[serde(default, skip_serializing_if = "Option::is_none")]
135 pub knee: Option<f32>,
136 #[serde(default, skip_serializing_if = "Option::is_none")]
137 pub knee_width: Option<f32>,
138 #[serde(default, skip_serializing_if = "Option::is_none")]
139 pub linear_taps: Option<bool>,
140 #[serde(default, skip_serializing_if = "Option::is_none")]
141 pub radii: Option<[f32; 4]>,
142 #[serde(default, skip_serializing_if = "Option::is_none")]
143 pub radius: Option<f32>,
144 #[serde(default, skip_serializing_if = "Option::is_none")]
145 pub sigma: Option<f32>,
146 #[serde(default, skip_serializing_if = "Option::is_none")]
147 pub soft: Option<f32>,
148 #[serde(default, skip_serializing_if = "Option::is_none")]
149 pub strength: Option<f32>,
150 #[serde(default, skip_serializing_if = "Option::is_none")]
151 pub threshold: Option<f32>,
152 #[serde(default, skip_serializing_if = "Option::is_none")]
153 pub unit: Option<f32>,
154}
155
156#[derive(Clone, Debug, PartialEq, Serialize)]
157#[serde(untagged)]
158pub enum Bloom {
159 Strength(f32),
160 Table(BloomTable),
161}
162
163either!(Bloom, "a number or a table", number => Strength, map => Table(BloomTable),);
164
165#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
166#[serde(rename_all = "snake_case")]
167pub enum VignetteKind {
168 Power,
169 Smoothstep,
170}
171
172#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
173#[serde(deny_unknown_fields)]
174pub struct VignetteTable {
175 #[serde(default, skip_serializing_if = "Option::is_none")]
176 pub aspect: Option<f32>,
177 #[serde(default, skip_serializing_if = "Option::is_none")]
178 pub inner: Option<f32>,
179 #[serde(default, skip_serializing_if = "Option::is_none")]
180 pub kind: Option<VignetteKind>,
181 #[serde(default, skip_serializing_if = "Option::is_none")]
182 pub outer: Option<f32>,
183 #[serde(default, skip_serializing_if = "Option::is_none")]
184 pub power: Option<f32>,
185 #[serde(default, skip_serializing_if = "Option::is_none")]
186 pub scale: Option<f32>,
187 #[serde(default, skip_serializing_if = "Option::is_none")]
188 pub strength: Option<f32>,
189}
190
191#[derive(Clone, Debug, PartialEq, Serialize)]
192#[serde(untagged)]
193pub enum Vignette {
194 Strength(f32),
195 Table(VignetteTable),
196}
197
198either!(Vignette, "a number or a table", number => Strength, map => Table(VignetteTable),);
199
200#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
201#[serde(deny_unknown_fields)]
202pub struct GrainTable {
203 #[serde(default, skip_serializing_if = "Option::is_none")]
204 pub clamp: Option<bool>,
205 #[serde(default, skip_serializing_if = "Option::is_none")]
206 pub response: Option<f32>,
207 #[serde(default, skip_serializing_if = "Option::is_none")]
208 pub strength: Option<f32>,
209}
210
211#[derive(Clone, Debug, PartialEq, Serialize)]
212#[serde(untagged)]
213pub enum Grain {
214 Strength(f32),
215 Table(GrainTable),
216}
217
218either!(Grain, "a number or a table", number => Strength, map => Table(GrainTable),);
219
220#[derive(Clone, Copy, Debug, PartialEq, Serialize)]
221#[serde(untagged)]
222pub enum Dither {
223 On(bool),
224 Amplitude(f32),
225}
226
227either!(Dither, "a bool or a number", bool => On, number => Amplitude,);
228
229#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
230#[serde(rename_all = "snake_case")]
231pub enum DitherKind {
232 Blue,
233 Ordered,
234}
235
236#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
237#[serde(deny_unknown_fields)]
238pub struct Lut {
239 pub size: u32,
240 pub values: Vec<f32>,
241}
242
243impl Lut {
244 pub const SIZES: std::ops::RangeInclusive<u32> = 2..=32;
245
246 pub fn needs(&self) -> usize {
247 (self.size as usize).pow(3) * 3
248 }
249}
250
251#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
252#[serde(deny_unknown_fields)]
253pub struct OutlineTable {
254 #[serde(default, skip_serializing_if = "Option::is_none")]
255 pub alpha: Option<f32>,
256 #[serde(default, skip_serializing_if = "Option::is_none")]
257 pub color: Option<Color>,
258 #[serde(default, skip_serializing_if = "Option::is_none")]
259 pub crease_angle: Option<f32>,
260 #[serde(default, skip_serializing_if = "Option::is_none")]
261 pub enabled: Option<bool>,
262 #[serde(default, skip_serializing_if = "Option::is_none")]
263 pub local_color: Option<bool>,
264 #[serde(default, skip_serializing_if = "Option::is_none")]
265 pub thickness: Option<f32>,
266}
267
268#[derive(Clone, Debug, PartialEq, Serialize)]
269#[serde(untagged)]
270pub enum Outline {
271 On(bool),
272 Table(OutlineTable),
273}
274
275either!(Outline, "a bool or a table", bool => On, map => Table(OutlineTable),);
276
277#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
278#[serde(deny_unknown_fields)]
279pub struct ScratchesTable {
280 #[serde(default, skip_serializing_if = "Option::is_none")]
281 pub count: Option<u32>,
282 #[serde(default, skip_serializing_if = "Option::is_none")]
283 pub strength: Option<f32>,
284}
285
286#[derive(Clone, Debug, PartialEq, Serialize)]
287#[serde(untagged)]
288pub enum Scratches {
289 Strength(f32),
290 Table(ScratchesTable),
291}
292
293either!(Scratches, "a number or a table", number => Strength, map => Table(ScratchesTable),);
294
295#[derive(Clone, Debug, PartialEq, Serialize)]
296#[serde(untagged)]
297pub enum Ink {
298 Amount(f32),
299 Color(Color),
300}
301
302either!(Ink, "a number, \"#rrggbb\" or three numbers", number => Amount, text => Color(Color), seq => Color(Color),);
303
304#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
305#[serde(rename_all = "snake_case")]
306pub enum TapeDirection {
307 Forward,
308 Rewind,
309}
310
311#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
312#[serde(deny_unknown_fields)]
313pub struct TapeTable {
314 #[serde(default, skip_serializing_if = "Option::is_none")]
315 pub bands: Option<f32>,
316 #[serde(default, skip_serializing_if = "Option::is_none")]
317 pub direction: Option<TapeDirection>,
318 #[serde(default, skip_serializing_if = "Option::is_none")]
319 pub echo: Option<f32>,
320 #[serde(default, skip_serializing_if = "Option::is_none")]
321 pub grain: Option<f32>,
322 #[serde(default, skip_serializing_if = "Option::is_none")]
323 pub smear: Option<f32>,
324 #[serde(default, skip_serializing_if = "Option::is_none")]
325 pub split: Option<f32>,
326 #[serde(default, skip_serializing_if = "Option::is_none")]
327 pub strength: Option<f32>,
328 #[serde(default, skip_serializing_if = "Option::is_none")]
329 pub wash: Option<f32>,
330}
331
332#[derive(Clone, Debug, PartialEq, Serialize)]
333#[serde(untagged)]
334pub enum Tape {
335 Direction(TapeDirection),
336 Strength(f32),
337 Table(TapeTable),
338}
339
340either!(Tape, "forward, rewind, a number or a table", number => Strength, text => Direction(TapeDirection), map => Table(TapeTable),);
341
342macro_rules! finish {
343 ($($field:ident: $kind:ty,)*) => {
344 #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
345 #[serde(deny_unknown_fields)]
346 pub struct FinishKeys {
347 $(
348 #[serde(default, skip_serializing_if = "Option::is_none")]
349 pub $field: Option<$kind>,
350 )*
351 }
352
353 #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
354 #[serde(deny_unknown_fields)]
355 pub struct Finish {
356 #[serde(default, skip_serializing_if = "Option::is_none")]
357 pub file: Option<String>,
358 $(
359 #[serde(default, skip_serializing_if = "Option::is_none")]
360 pub $field: Option<$kind>,
361 )*
362 #[serde(default, skip_serializing_if = "Vec::is_empty")]
363 pub pass: Vec<FinishKeys>,
364 }
365
366 impl Finish {
367 pub fn keys(&self) -> FinishKeys {
368 FinishKeys {
369 $($field: self.$field.clone(),)*
370 }
371 }
372 }
373
374 impl FinishKeys {
375 pub const KEYS: &[&str] = &[$(stringify!($field),)*];
376 }
377 };
378}
379
380finish! {
381 aberration: f32,
382 angle: f32,
383 black: f32,
384 black_threshold: f32,
385 bloom: Bloom,
386 cavity: f32,
387 cavity_distance: f32,
388 cel_bands: f32,
389 cel_shadow: f32,
390 cel_softness: f32,
391 cel_spec: f32,
392 cel_spec_roughness: f32,
393 cel_spec_threshold: f32,
394 cel_threshold: f32,
395 contrast: f32,
396 crease_angle: f32,
397 crush: f32,
398 curvature: f32,
399 darkening: f32,
400 dither: Dither,
401 dither_kind: DitherKind,
402 distortion: f32,
403 dot: f32,
404 dust: f32,
405 encode: bool,
406 exposure: f32,
407 flicker: f32,
408 focus: f32,
409 frame: u32,
410 gain: f32,
411 grain: Grain,
412 halation: f32,
413 highlight: Color,
414 highlight_threshold: f32,
415 ink: Ink,
416 interior_line_strength: f32,
417 keep: Color,
418 keep_range: f32,
419 levels: f32,
420 line_weight: f32,
421 lut: Lut,
422 mask: f32,
423 outline: Outline,
424 outline_alpha: f32,
425 outline_color: Color,
426 outline_thickness: f32,
427 palette: bool,
428 paper_grain: f32,
429 pixel: u32,
430 posterize: f32,
431 radius: f32,
432 rim: f32,
433 rim_color: Color,
434 rim_width: f32,
435 saturation: f32,
436 saturation_lift: f32,
437 scale: f32,
438 scanlines: f32,
439 scratches: Scratches,
440 seed: u32,
441 sepia: f32,
442 shadow: Color,
443 style: Style,
444 tape: Tape,
445 threshold: f32,
446 tilt_range: f32,
447 tone: Tone,
448 tone_steps: f32,
449 vignette: Vignette,
450 warmth: Warmth,
451 weave: f32,
452 weights: [f32; 3],
453}
454
455impl FinishKeys {
456 pub fn is_empty(&self) -> bool {
457 *self == Self::default()
458 }
459
460 pub fn to_table(&self) -> toml::Table {
461 toml::Table::try_from(self).unwrap_or_default()
462 }
463}