Skip to main content

pfx_scene/types/
finish.rs

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}