hyperion/instance/
core.rs1use crate::{
2 color::{color_to16, ChannelAdjustments, ChannelAdjustmentsBuilder},
3 image::{prelude::*, Reducer},
4 models::{Color, Color16, InstanceConfig, Leds},
5};
6
7use super::{BlackBorderDetector, MuxedMessage, MuxedMessageData, Smoothing, SmoothingUpdate};
8
9pub struct Core {
13 leds: Leds,
14 color_data: Vec<Color16>,
15 black_border_detector: BlackBorderDetector,
16 channel_adjustments: ChannelAdjustments,
17 smoothing: Smoothing,
18 notified_inconsistent_led_data: bool,
19 reducer: Reducer,
20}
21
22impl Core {
23 pub async fn new(config: &InstanceConfig) -> Self {
24 let led_count = config.leds.leds.len();
25 let black_border_detector = BlackBorderDetector::new(config.black_border_detector.clone());
26 let channel_adjustments = ChannelAdjustmentsBuilder::new(&config.color)
27 .led_count(led_count as _)
28 .build();
29 let smoothing = Smoothing::new(config.smoothing.clone(), led_count);
30
31 Self {
32 leds: config.leds.clone(),
33 color_data: vec![Color16::default(); led_count],
34 black_border_detector,
35 channel_adjustments,
36 smoothing,
37 notified_inconsistent_led_data: false,
38 reducer: Default::default(),
39 }
40 }
41
42 fn handle_color(&mut self, color: Color) {
43 self.color_data.fill(color_to16(color));
44 }
45
46 fn handle_image(&mut self, image: &impl Image) {
47 self.black_border_detector.process(image);
49 let black_border = self.black_border_detector.current_border();
50
51 let image = {
53 let (x, y) = black_border.get_ranges(image.width(), image.height());
54 image.wrap(x, y)
55 };
56
57 self.reducer
59 .reduce(&image, &self.leds.leds[..], &mut self.color_data);
60 }
61
62 fn handle_led_colors(&mut self, led_colors: &[Color]) {
63 let led_count = self.color_data.len();
64 let data_count = led_colors.len();
65
66 let (src, dst, fill) = if led_count == data_count {
67 self.notified_inconsistent_led_data = false;
68
69 let (dst, fill) = self.color_data.split_at_mut(led_count);
70 (led_colors, dst, fill)
71 } else if led_count < data_count {
72 if !self.notified_inconsistent_led_data {
73 self.notified_inconsistent_led_data = true;
74 warn!(extra = %(data_count - led_count), "too much LED data");
75 }
76
77 let (dst, fill) = self.color_data.split_at_mut(led_count);
78 (&led_colors[..led_count], dst, fill)
79 } else {
80 if !self.notified_inconsistent_led_data {
81 self.notified_inconsistent_led_data = true;
82 warn!(missing = %(led_count - data_count), "not enough LED data");
83 }
84
85 let (dst, fill) = self.color_data.split_at_mut(data_count);
86 (led_colors, dst, fill)
87 };
88
89 for (led_mut, color) in dst.iter_mut().zip(src.iter()) {
90 *led_mut = color_to16(*color);
91 }
92
93 fill.fill(Color16::default());
94 }
95
96 pub fn handle_message(&mut self, message: MuxedMessage) {
97 match message.data() {
99 MuxedMessageData::SolidColor { color, .. } => {
100 self.handle_color(*color);
101 }
102 MuxedMessageData::Image { image, .. } => {
103 self.handle_image(image.as_ref());
104 }
105 MuxedMessageData::LedColors { led_colors, .. } => {
106 self.handle_led_colors(led_colors);
107 }
108 }
109
110 self.channel_adjustments.apply(&mut self.color_data);
112
113 self.smoothing.set_target(&self.color_data);
115 }
116
117 pub async fn update(&mut self) -> (&[Color], SmoothingUpdate) {
118 self.smoothing.update().await
119 }
120}