Skip to main content

opendefocus_datastructure/
lib.rs

1#![warn(unused_extern_crates)]
2
3use circle_of_confusion::Math;
4use glam::{IVec4, UVec2, Vec2};
5use opendefocus_shared::{ConvolveSettings, GlobalFlags, NonUniformFlags};
6
7use opendefocus_shared::math::{ceilf, floorf};
8
9use crate::defocus::DefocusMode;
10use crate::render::{FilterMode, Quality};
11
12include!(concat!(env!("OUT_DIR"), "/opendefocus.rs"));
13
14impl IVector4 {
15    pub fn to_ivec4(&self) -> IVec4 {
16        IVec4::new(self.x, self.y, self.z, self.w)
17    }
18}
19
20impl UVector2 {
21    pub fn to_uvec2(&self) -> UVec2 {
22        UVec2::new(self.x, self.y)
23    }
24}
25
26impl Vector2 {
27    pub fn to_vec2(&self) -> Vec2 {
28        Vec2::new(self.x, self.y)
29    }
30}
31
32impl render::Filter {
33    pub fn calculate_filter_box(&self, aspect_ratio: f32) -> [u32; 4] {
34        let resolution = UVector2 {
35            x: self.resolution,
36            y: self.resolution,
37        };
38        let offset_multiplier = Vector2 {
39            x: 3.0 - aspect_ratio.min(1.0) * 2.0,
40            y: aspect_ratio.max(1.0) * 2.0 - 1.0,
41        };
42        let offset = UVector2 {
43            x: floorf((resolution.x as f32 / offset_multiplier.x) * (1.0 - aspect_ratio).max(0.0))
44                as u32,
45            y: floorf((resolution.y as f32 / offset_multiplier.y) * (aspect_ratio - 1.0).max(0.0))
46                as u32,
47        };
48        [
49            offset.x,
50            offset.y,
51            resolution.x - offset.x,
52            resolution.y - offset.y,
53        ]
54    }
55}
56
57impl render::RenderSpecs {
58    /// Creates render specs from rectangles.
59    pub fn from_rects(full_region: IVec4, render_region: IVec4) -> Self {
60        Self {
61            full_region: IVector4 {
62                x: full_region.x,
63                y: full_region.y,
64                z: full_region.z,
65                w: full_region.w,
66            },
67            render_region: IVector4 {
68                x: render_region.x,
69                y: render_region.y,
70                z: render_region.z,
71                w: render_region.w,
72            },
73        }
74    }
75
76    /// Scale the render specs by a specified factor.
77    pub fn scale(&self, scale: f32) -> Self {
78        let mut scaled_full_region = self.full_region;
79        scaled_full_region.x = floorf(scaled_full_region.x as f32 * scale) as i32;
80        scaled_full_region.y = floorf(scaled_full_region.y as f32 * scale) as i32;
81        scaled_full_region.z = ceilf(scaled_full_region.z as f32 * scale) as i32;
82        scaled_full_region.w = ceilf(scaled_full_region.w as f32 * scale) as i32;
83
84        let mut scaled_render_region = self.render_region;
85        scaled_render_region.x = floorf(scaled_render_region.x as f32 * scale) as i32;
86        scaled_render_region.y = floorf(scaled_render_region.y as f32 * scale) as i32;
87        scaled_render_region.z = ceilf(scaled_render_region.z as f32 * scale) as i32;
88        scaled_render_region.w = ceilf(scaled_render_region.w as f32 * scale) as i32;
89        Self {
90            full_region: scaled_full_region,
91            render_region: scaled_render_region,
92        }
93    }
94
95    /// Get the resolution of the full region.
96    pub fn get_resolution(&self) -> UVec2 {
97        UVec2::new(
98            (self.full_region.z - self.full_region.x) as u32,
99            (self.full_region.w - self.full_region.y) as u32,
100        )
101    }
102
103    /// Get the resolution of the render region.
104    pub fn get_render_resolution(&self) -> UVec2 {
105        UVec2::new(
106            (self.render_region.z - self.render_region.x) as u32,
107            (self.render_region.w - self.render_region.y) as u32,
108        )
109    }
110}
111
112impl defocus::Settings {
113    /// Calculate the effective defocus size.
114    pub fn get_size(&self) -> f32 {
115        self.circle_of_confusion.size
116            * self.circle_of_confusion.pixel_aspect
117            * self.size_multiplier.max(0.0)
118            * self.proxy_scale.unwrap_or(1.0)
119    }
120
121    pub fn get_raw_max_size(&self) -> f32 {
122        match self.circle_of_confusion.camera_data {
123            Some(_) => self.camera_max_size,
124            _ => self.circle_of_confusion.max_size,
125        }
126    }
127
128    fn get_size_multiplier(&self) -> f32 {
129        self.size_multiplier.max(0.0)
130    }
131
132    fn get_proxy_scale(&self) -> f32 {
133        self.proxy_scale.unwrap_or(1.0)
134    }
135
136    /// Calculate the maximum defocus size based on the current mode.
137    pub fn get_max_size(&self) -> f32 {
138        let max_size = self.get_raw_max_size();
139        max_size
140            * self.circle_of_confusion.pixel_aspect
141            * self.get_size_multiplier()
142            * self.get_proxy_scale()
143    }
144
145    /// Determines the math interpretation of depth.
146    pub fn get_math(&self) -> Math {
147        todo!()
148    }
149
150    /// Gets the current maximum size for defocus.
151    pub fn get_current_max_size(&self) -> f32 {
152        if self.defocus_mode() == DefocusMode::Twod {
153            return self.get_size();
154        }
155        self.get_max_size()
156    }
157
158    /// Calculate the padding required for rendering.
159    pub fn get_padding(&self) -> u32 {
160        (ceilf(self.get_current_max_size())) as u32
161    }
162}
163
164impl render::Settings {
165    pub fn get_quality(&self) -> Quality {
166        if self.gui {
167            self.quality()
168        } else {
169            self.farm_quality()
170        }
171    }
172}
173
174fn settings_to_globalflags(settings: &Settings, non_uniform_flags: NonUniformFlags) -> GlobalFlags {
175    let mut flags = GlobalFlags::empty();
176    if settings.render.filter.mode() == FilterMode::Simple {
177        flags |= GlobalFlags::SIMPLE_DISC;
178    }
179    if non_uniform_flags.intersects(
180        NonUniformFlags::CATSEYE_ENABLED
181            | NonUniformFlags::BARNDOORS_ENABLED
182            | NonUniformFlags::ASTIGMATISM_ENABLED,
183    ) {
184        flags |= GlobalFlags::USE_NON_UNIFORM;
185    }
186
187    if settings.defocus.defocus_mode() == DefocusMode::Twod {
188        flags |= GlobalFlags::IS_2D;
189    }
190    if settings.non_uniform.inverse_foreground {
191        flags |= GlobalFlags::INVERSE_FOREGROUND_BOKEH_SHAPE;
192    }
193    if settings.render.inverse_y {
194        flags |= GlobalFlags::INVERSE_Y;
195    }
196    if settings.non_uniform.axial_aberration.enable {
197        flags |= GlobalFlags::AXIAL_ABERRATION_ENABLE;
198    }
199    flags
200}
201
202fn non_uniform_to_flags(settings: &non_uniform::Settings) -> NonUniformFlags {
203    let mut non_uniform_flags = NonUniformFlags::empty();
204    if settings.catseye.enable {
205        non_uniform_flags |= NonUniformFlags::CATSEYE_ENABLED;
206        if settings.catseye.inverse {
207            non_uniform_flags |= NonUniformFlags::CATSEYE_INVERSE;
208        }
209        if settings.catseye.inverse_foreground {
210            non_uniform_flags |= NonUniformFlags::CATSEYE_INVERSE_FOREGROUND;
211        }
212        if settings.catseye.relative_to_screen {
213            non_uniform_flags |= NonUniformFlags::CATSEYE_SCREEN_RELATIVE;
214        }
215    }
216
217    if settings.astigmatism.enable {
218        non_uniform_flags |= NonUniformFlags::ASTIGMATISM_ENABLED;
219    }
220
221    if settings.barndoors.enable {
222        non_uniform_flags |= NonUniformFlags::BARNDOORS_ENABLED;
223        if settings.barndoors.inverse {
224            non_uniform_flags |= NonUniformFlags::BARNDOORS_INVERSE
225        }
226        if settings.barndoors.inverse_foreground {
227            non_uniform_flags |= NonUniformFlags::BARNDOORS_INVERSE_FOREGROUND
228        }
229    }
230    non_uniform_flags
231}
232
233/// Calculates the number of samples to use for convolution based on render settings and size.
234///
235/// # Arguments
236///
237/// * `settings` - Reference to the render settings
238/// * `size` - The size parameter used to calculate samples
239///
240/// # Returns
241///
242/// The calculated number of samples as an i32 value
243pub fn get_samples(settings: &render::Settings, size: i32) -> u32 {
244    let quality = settings.get_quality();
245    match quality {
246        render::Quality::Low => (size as f32 * 0.1).clamp(8.0, 20.0) as u32,
247        render::Quality::Medium => (size as f32 * 0.2).clamp(12.0, 50.0) as u32,
248        render::Quality::High => (size as f32 * 0.5).clamp(30.0, 100.0) as u32,
249        render::Quality::Custom => settings.samples as u32,
250    }
251}
252
253pub fn settings_to_convolve_settings(
254    settings: &Settings,
255    render_specs: &render::RenderSpecs,
256    filter_resolution: UVec2,
257    image_elements: u32,
258) -> ConvolveSettings {
259    let max_size = if settings.defocus.defocus_mode() == DefocusMode::Twod {
260        settings.defocus.get_size()
261    } else {
262        settings.defocus.get_raw_max_size()
263    };
264    let samples = get_samples(&settings.render, ceilf(max_size) as i32);
265    let filter_aspect_ratio = filter_resolution.x as f32 / filter_resolution.y as f32;
266    let filter_aspect_ratio_normalizer = 1.0 / filter_aspect_ratio;
267    let ring_distance = max_size / samples as f32;
268    let pixel_aspect_normalizer = 1.0 / settings.defocus.circle_of_confusion.pixel_aspect;
269
270    let non_uniform_flags = non_uniform_to_flags(&settings.non_uniform);
271    let flags = settings_to_globalflags(settings, non_uniform_flags);
272
273    ConvolveSettings {
274        samples,
275        center: settings.render.center.to_vec2(),
276        process_region: render_specs.render_region.to_ivec4(),
277        full_region: render_specs.full_region.to_ivec4(),
278        resolution: settings.render.resolution.to_uvec2(),
279        pixel_aspect: settings.defocus.circle_of_confusion.pixel_aspect,
280        size: settings.defocus.get_size(),
281        max_size,
282        flags: flags.bits(),
283        non_uniform_flags: non_uniform_flags.bits(),
284        catseye_amount: settings.non_uniform.catseye.amount,
285        catseye_gamma: settings.non_uniform.catseye.gamma,
286        catseye_softness: settings.non_uniform.catseye.softness,
287        astigmatism_amount: settings.non_uniform.astigmatism.amount,
288        astigmatism_gamma: settings.non_uniform.astigmatism.gamma,
289        barndoors_amount: settings.non_uniform.barndoors.amount,
290        barndoors_gamma: settings.non_uniform.barndoors.gamma,
291        barndoors_top: settings.non_uniform.barndoors.top,
292        barndoors_bottom: settings.non_uniform.barndoors.bottom,
293        barndoors_left: settings.non_uniform.barndoors.left,
294        barndoors_right: settings.non_uniform.barndoors.right,
295        axial_aberration_amount: settings.non_uniform.axial_aberration.amount,
296        axial_aberration_type: settings.non_uniform.axial_aberration.color_type,
297        filter_aspect_ratio_normalizer,
298        filter_aspect_ratio,
299        ring_distance,
300        filter_resolution,
301        pixel_aspect_normalizer,
302        render_scale: 1 as u32,
303        image_elements,
304        _padding: 0,
305    }
306}