Skip to main content

oxihuman_export/
deep_image_export.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Deep image export (per-pixel depth sample lists).
6
7/// A single depth sample at a pixel.
8#[derive(Debug, Clone)]
9pub struct DeepPixel {
10    pub depth: f32,
11    pub alpha: f32,
12    pub color: [f32; 3],
13}
14
15/// A 2D deep image with variable-length sample lists per pixel.
16#[derive(Debug, Clone)]
17pub struct DeepImage {
18    pub width: usize,
19    pub height: usize,
20    pub samples: Vec<Vec<DeepPixel>>,
21}
22
23/// Create a new `DeepPixel`.
24pub fn new_deep_pixel(depth: f32, alpha: f32, color: [f32; 3]) -> DeepPixel {
25    DeepPixel {
26        depth,
27        alpha,
28        color,
29    }
30}
31
32/// Push a sample into a pixel's sample list.
33pub fn deep_pixel_push(samples: &mut Vec<DeepPixel>, pixel: DeepPixel) {
34    samples.push(pixel);
35}
36
37/// Flatten (composite) a pixel's samples into a single RGBA value (front-to-back).
38pub fn deep_pixel_flatten(samples: &[DeepPixel]) -> [f32; 4] {
39    let mut out = [0.0f32; 4];
40    for s in samples.iter() {
41        let remaining = 1.0 - out[3];
42        out[0] += s.color[0] * s.alpha * remaining;
43        out[1] += s.color[1] * s.alpha * remaining;
44        out[2] += s.color[2] * s.alpha * remaining;
45        out[3] += s.alpha * remaining;
46        if out[3] >= 1.0 - f32::EPSILON {
47            break;
48        }
49    }
50    out
51}
52
53/// Create a new zeroed `DeepImage`.
54pub fn new_deep_image(width: usize, height: usize) -> DeepImage {
55    DeepImage {
56        width,
57        height,
58        samples: vec![Vec::new(); width * height],
59    }
60}
61
62/// Total sample count across all pixels.
63pub fn deep_image_sample_count(img: &DeepImage) -> usize {
64    img.samples.iter().map(|s| s.len()).sum()
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    #[test]
72    fn test_new_deep_pixel() {
73        let p = new_deep_pixel(1.0, 0.8, [1.0, 0.0, 0.0]);
74        assert!((p.depth - 1.0).abs() < 1e-5);
75    }
76
77    #[test]
78    fn test_deep_pixel_push() {
79        let mut s: Vec<DeepPixel> = Vec::new();
80        deep_pixel_push(&mut s, new_deep_pixel(1.0, 1.0, [1.0, 1.0, 1.0]));
81        assert_eq!(s.len(), 1);
82    }
83
84    #[test]
85    fn test_deep_pixel_flatten_opaque() {
86        /* single fully opaque red sample → red output */
87        let s = vec![new_deep_pixel(1.0, 1.0, [1.0, 0.0, 0.0])];
88        let rgba = deep_pixel_flatten(&s);
89        assert!((rgba[0] - 1.0).abs() < 1e-5);
90        assert!((rgba[3] - 1.0).abs() < 1e-5);
91    }
92
93    #[test]
94    fn test_new_deep_image() {
95        let img = new_deep_image(4, 4);
96        assert_eq!(img.samples.len(), 16);
97    }
98
99    #[test]
100    fn test_deep_image_sample_count() {
101        let mut img = new_deep_image(2, 2);
102        img.samples[0].push(new_deep_pixel(1.0, 0.5, [1.0, 1.0, 1.0]));
103        assert_eq!(deep_image_sample_count(&img), 1);
104    }
105}