Skip to main content

oxihuman_export/
alpha_map_export.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Alpha map export: per-pixel alpha channel image buffer.
6
7/// Alpha map buffer (single-channel f32).
8#[allow(dead_code)]
9#[derive(Debug, Clone)]
10pub struct AlphaMapExport {
11    pub width: usize,
12    pub height: usize,
13    pub pixels: Vec<f32>,
14}
15
16/// Create a new alpha map buffer, all zeros.
17#[allow(dead_code)]
18pub fn new_alpha_map(width: usize, height: usize) -> AlphaMapExport {
19    AlphaMapExport {
20        width,
21        height,
22        pixels: vec![0.0; width * height],
23    }
24}
25
26/// Set alpha at (x, y).
27#[allow(dead_code)]
28pub fn set_alpha(am: &mut AlphaMapExport, x: usize, y: usize, value: f32) {
29    if let Some(px) = am.pixels.get_mut(y * am.width + x) {
30        *px = value.clamp(0.0, 1.0);
31    }
32}
33
34/// Get alpha at (x, y).
35#[allow(dead_code)]
36pub fn get_alpha(am: &AlphaMapExport, x: usize, y: usize) -> Option<f32> {
37    am.pixels.get(y * am.width + x).copied()
38}
39
40/// Total pixel count.
41#[allow(dead_code)]
42pub fn alpha_pixel_count(am: &AlphaMapExport) -> usize {
43    am.pixels.len()
44}
45
46/// Average alpha value.
47#[allow(dead_code)]
48pub fn average_alpha(am: &AlphaMapExport) -> f32 {
49    if am.pixels.is_empty() {
50        return 0.0;
51    }
52    am.pixels.iter().sum::<f32>() / am.pixels.len() as f32
53}
54
55/// Fill entire buffer with a value.
56#[allow(dead_code)]
57pub fn fill_alpha(am: &mut AlphaMapExport, value: f32) {
58    let v = value.clamp(0.0, 1.0);
59    for px in &mut am.pixels {
60        *px = v;
61    }
62}
63
64/// Invert alpha (1.0 - value).
65#[allow(dead_code)]
66pub fn invert_alpha(am: &mut AlphaMapExport) {
67    for px in &mut am.pixels {
68        *px = 1.0 - *px;
69    }
70}
71
72/// Validate: all pixels in [0, 1].
73#[allow(dead_code)]
74pub fn validate_alpha_map(am: &AlphaMapExport) -> bool {
75    am.pixels.iter().all(|&p| (0.0..=1.0).contains(&p))
76}
77
78/// Encode to PGM bytes (8-bit grayscale).
79#[allow(dead_code)]
80pub fn encode_pgm(am: &AlphaMapExport) -> Vec<u8> {
81    let header = format!("P5\n{} {}\n255\n", am.width, am.height);
82    let mut out: Vec<u8> = header.into_bytes();
83    for &p in &am.pixels {
84        out.push((p.clamp(0.0, 1.0) * 255.0) as u8);
85    }
86    out
87}
88
89/// Export to JSON summary.
90#[allow(dead_code)]
91pub fn alpha_map_to_json(am: &AlphaMapExport) -> String {
92    format!(
93        "{{\"width\":{},\"height\":{},\"average_alpha\":{:.6}}}",
94        am.width,
95        am.height,
96        average_alpha(am)
97    )
98}
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103
104    #[test]
105    fn test_new_alpha_map() {
106        let am = new_alpha_map(4, 4);
107        assert_eq!(alpha_pixel_count(&am), 16);
108    }
109
110    #[test]
111    fn test_set_get_alpha() {
112        let mut am = new_alpha_map(4, 4);
113        set_alpha(&mut am, 2, 1, 0.7);
114        let v = get_alpha(&am, 2, 1).expect("should succeed");
115        assert!((v - 0.7).abs() < 1e-6);
116    }
117
118    #[test]
119    fn test_get_alpha_oob() {
120        let am = new_alpha_map(2, 2);
121        assert!(get_alpha(&am, 10, 10).is_none());
122    }
123
124    #[test]
125    fn test_average_alpha_zero() {
126        let am = new_alpha_map(2, 2);
127        assert!((average_alpha(&am)).abs() < 1e-9);
128    }
129
130    #[test]
131    fn test_fill_alpha() {
132        let mut am = new_alpha_map(2, 2);
133        fill_alpha(&mut am, 0.5);
134        assert!((average_alpha(&am) - 0.5).abs() < 1e-5);
135    }
136
137    #[test]
138    fn test_invert_alpha() {
139        let mut am = new_alpha_map(2, 2);
140        fill_alpha(&mut am, 0.3);
141        invert_alpha(&mut am);
142        assert!((average_alpha(&am) - 0.7).abs() < 1e-5);
143    }
144
145    #[test]
146    fn test_validate_alpha_map() {
147        let am = new_alpha_map(2, 2);
148        assert!(validate_alpha_map(&am));
149    }
150
151    #[test]
152    fn test_encode_pgm_header() {
153        let am = new_alpha_map(2, 2);
154        let pgm = encode_pgm(&am);
155        assert!(!pgm.is_empty());
156        assert!(pgm.starts_with(b"P5"));
157    }
158
159    #[test]
160    fn test_alpha_map_to_json() {
161        let am = new_alpha_map(3, 3);
162        let j = alpha_map_to_json(&am);
163        assert!(j.contains("\"width\":3"));
164    }
165
166    #[test]
167    fn test_alpha_value_in_range() {
168        let mut am = new_alpha_map(1, 1);
169        set_alpha(&mut am, 0, 0, 0.5);
170        let v = get_alpha(&am, 0, 0).expect("should succeed");
171        assert!((0.0..=1.0).contains(&v));
172    }
173}