hyperion/image/
reducer.rs

1use crate::models::{Color16, Led};
2
3use super::Image;
4
5#[derive(Debug, Default)]
6pub struct Reducer {
7    spec: Vec<LedSpec>,
8    spec_width: u16,
9    spec_height: u16,
10}
11
12#[derive(Debug)]
13struct LedSpec {
14    lxmin: u16,
15    lxmax: u16,
16    lymin: u16,
17    lymax: u16,
18}
19
20impl LedSpec {
21    pub fn new(spec: &Led, width: u16, height: u16, fwidth: f32, fheight: f32) -> Self {
22        let lxmin = spec.hmin * fwidth;
23        let lxmax = spec.hmax * fwidth;
24        let lymin = spec.vmin * fheight;
25        let lymax = spec.vmax * fheight;
26
27        Self {
28            lxmin: lxmin.floor() as u16,
29            lxmax: (lxmax.ceil() as u16).min(width - 1),
30            lymin: lymin.floor() as u16,
31            lymax: (lymax.ceil() as u16).min(height - 1),
32        }
33    }
34}
35
36impl Reducer {
37    pub fn reset(&mut self, width: u16, height: u16, leds: &[Led]) {
38        self.spec_width = width;
39        self.spec_height = height;
40
41        let fwidth = width as f32;
42        let fheight = height as f32;
43
44        self.spec.clear();
45        self.spec.reserve(leds.len());
46
47        for spec in leds.iter() {
48            self.spec
49                .push(LedSpec::new(spec, width, height, fwidth, fheight));
50        }
51    }
52
53    pub fn reduce(&mut self, image: &impl Image, leds: &[Led], color_data: &mut [Color16]) {
54        let width = image.width();
55        let height = image.height();
56
57        if self.spec_width != width || self.spec_height != height || self.spec.len() != leds.len() {
58            self.reset(width, height, leds);
59        }
60
61        for (spec, value) in self.spec.iter().zip(color_data.iter_mut()) {
62            let mut r_acc = 0u64;
63            let mut g_acc = 0u64;
64            let mut b_acc = 0u64;
65            let mut cnt = 0u64;
66
67            for y in spec.lymin..=spec.lymax {
68                for x in spec.lxmin..=spec.lxmax {
69                    // Safety: x (resp. y) are necessarily in 0..width (resp. 0..height)
70                    let rgb = unsafe { image.color_at_unchecked(x as _, y as _) };
71                    let area = 255;
72
73                    let (r, g, b) = rgb.into_components();
74                    r_acc += (r as u64 * 255) * area;
75                    g_acc += (g as u64 * 255) * area;
76                    b_acc += (b as u64 * 255) * area;
77                    cnt += area;
78                }
79            }
80
81            *value = Color16::new(
82                ((r_acc / cnt.max(1)) * 65535 / (255 * 255)).min(u16::MAX as _) as u16,
83                ((g_acc / cnt.max(1)) * 65535 / (255 * 255)).min(u16::MAX as _) as u16,
84                ((b_acc / cnt.max(1)) * 65535 / (255 * 255)).min(u16::MAX as _) as u16,
85            );
86        }
87    }
88}