1use std::sync::OnceLock;
14
15fn to_linear() -> &'static [f32; 256] {
17 static T: OnceLock<[f32; 256]> = OnceLock::new();
18 T.get_or_init(|| {
19 std::array::from_fn(|i| {
20 let c = i as f32 / 255.0;
21 if c <= 0.04045 {
22 c / 12.92
23 } else {
24 ((c + 0.055) / 1.055).powf(2.4)
25 }
26 })
27 })
28}
29
30const ENCODE_STEPS: usize = 4096;
33
34fn to_srgb() -> &'static [u8; ENCODE_STEPS] {
36 static T: OnceLock<[u8; ENCODE_STEPS]> = OnceLock::new();
37 T.get_or_init(|| {
38 std::array::from_fn(|i| {
39 let l = i as f32 / (ENCODE_STEPS - 1) as f32;
40 let c = if l <= 0.003_130_8 {
41 l * 12.92
42 } else {
43 1.055 * l.powf(1.0 / 2.4) - 0.055
44 };
45 (c * 255.0).round().clamp(0.0, 255.0) as u8
46 })
47 })
48}
49
50pub fn halved(w: u32, h: u32) -> (u32, u32) {
52 (w.div_ceil(2).max(1), h.div_ceil(2).max(1))
53}
54
55pub fn depth(w: u32, h: u32) -> u8 {
58 let mut n = 0;
59 let (mut w, mut h) = (w, h);
60 while w > 1 || h > 1 {
61 (w, h) = halved(w, h);
62 n += 1;
63 }
64 n
65}
66
67pub fn halve(w: u32, h: u32, rgba: &[u8]) -> (u32, u32, Vec<u8>) {
71 debug_assert_eq!(rgba.len(), w as usize * h as usize * 4);
72 let (nw, nh) = halved(w, h);
73 let (w, h) = (w as usize, h as usize);
74 let lin = to_linear();
75 let enc = to_srgb();
76 let scale = (ENCODE_STEPS - 1) as f32;
77 let mut out = vec![0u8; nw as usize * nh as usize * 4];
78 let quarter = scale / 4.0;
79 for y in 0..nh as usize {
80 let rows = [2 * y, (2 * y + 1).min(h - 1)];
81 let (r0, r1) = (
82 &rgba[rows[0] * w * 4..][..w * 4],
83 &rgba[rows[1] * w * 4..][..w * 4],
84 );
85 let out_row = &mut out[y * nw as usize * 4..][..nw as usize * 4];
86 for (x, o) in out_row.as_chunks_mut::<4>().0.iter_mut().enumerate() {
87 let (c0, c1) = (8 * x, (8 * x + 4).min(w * 4 - 4));
88 let block = [
89 &r0[c0..c0 + 4],
90 &r0[c1..c1 + 4],
91 &r1[c0..c0 + 4],
92 &r1[c1..c1 + 4],
93 ];
94 if block.iter().all(|p| p[3] == 255) {
96 for c in 0..3 {
97 let sum: f32 = block.iter().map(|p| lin[p[c] as usize]).sum();
98 o[c] = enc[(sum * quarter) as usize];
99 }
100 o[3] = 255;
101 continue;
102 }
103 let (mut r, mut g, mut b, mut a) = (0.0f32, 0.0f32, 0.0f32, 0u32);
104 for p in block {
105 let pa = p[3] as f32;
106 r += lin[p[0] as usize] * pa;
107 g += lin[p[1] as usize] * pa;
108 b += lin[p[2] as usize] * pa;
109 a += p[3] as u32;
110 }
111 if a > 0 {
112 let inv = scale / a as f32;
115 o[0] = enc[(r * inv) as usize];
116 o[1] = enc[(g * inv) as usize];
117 o[2] = enc[(b * inv) as usize];
118 o[3] = ((a + 2) / 4) as u8;
119 }
120 }
121 }
122 (nw, nh, out)
123}
124
125pub fn level_for(texels: (f32, f32), pixels: (f32, f32), deepest: u8) -> u8 {
133 let (tw, th) = texels;
134 let (pw, ph) = pixels;
135 if !(pw > 0.0 && ph > 0.0 && tw > 0.0 && th > 0.0) {
136 return 0;
137 }
138 let r = (tw / pw).min(th / ph);
139 if r.partial_cmp(&2.0).is_none_or(|o| o.is_lt()) {
141 return 0;
142 }
143 (r.log2().floor() as u32).min(deepest as u32) as u8
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151
152 fn px(c: [u8; 4], n: usize) -> Vec<u8> {
153 c.iter().copied().cycle().take(n * 4).collect()
154 }
155
156 #[test]
157 fn a_flat_image_halves_to_itself() {
158 let (w, h, out) = halve(4, 2, &px([200, 100, 30, 255], 8));
159 assert_eq!((w, h), (2, 1));
160 assert_eq!(out, px([200, 100, 30, 255], 2));
161 }
162
163 #[test]
166 fn the_average_is_in_linear_light() {
167 let rgba = [0, 0, 0, 255, 255, 255, 255, 255].repeat(2);
168 let (_, _, out) = halve(2, 2, &rgba);
169 assert!((187..=189).contains(&out[0]), "{out:?}");
170 assert_eq!(out[3], 255);
171 }
172
173 #[test]
176 fn the_alpha_is_premultiplied() {
177 let rgba = [255, 0, 0, 255, 0, 0, 0, 0].repeat(2);
178 let (_, _, out) = halve(2, 2, &rgba);
179 assert_eq!(&out[..3], &[255, 0, 0]);
180 assert_eq!(out[3], 128);
181 }
182
183 #[test]
186 fn an_odd_side_repeats_its_edge() {
187 let rgba = [10, 10, 10, 255, 10, 10, 10, 255, 250, 250, 250, 255];
188 let (w, h, out) = halve(3, 1, &rgba);
189 assert_eq!((w, h), (2, 1));
190 assert_eq!(&out[4..], &[250, 250, 250, 255]);
191 }
192
193 #[test]
194 fn the_chain_ends_at_one_texel() {
195 assert_eq!(depth(1, 1), 0);
196 assert_eq!(depth(2, 1), 1);
197 assert_eq!(depth(4032, 3024), 12);
198 assert_eq!(halved(1, 7), (1, 4));
199 }
200
201 #[test]
202 fn the_level_never_magnifies() {
203 assert_eq!(level_for((1000.0, 1000.0), (600.0, 600.0), 10), 0);
205 assert_eq!(level_for((1000.0, 1000.0), (500.0, 500.0), 10), 1);
206 assert_eq!(level_for((1000.0, 1000.0), (260.0, 260.0), 10), 1);
207 assert_eq!(level_for((1000.0, 1000.0), (250.0, 250.0), 10), 2);
208 assert_eq!(level_for((1000.0, 1000.0), (900.0, 100.0), 10), 0);
211 assert_eq!(level_for((1000.0, 1000.0), (0.5, 0.5), 3), 3);
213 assert_eq!(level_for((1000.0, 1000.0), (0.0, 10.0), 10), 0);
214 assert_eq!(level_for((1000.0, 1000.0), (f32::NAN, 10.0), 10), 0);
215 }
216}