Skip to content

Commit fc91978

Browse files
committed
refactor: Move image reduction code to image module
1 parent 059387a commit fc91978

File tree

3 files changed

+71
-52
lines changed

3 files changed

+71
-52
lines changed

src/image.rs

+3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ use thiserror::Error;
44

55
use crate::models::Color;
66

7+
mod reducer;
8+
pub use reducer::*;
9+
710
pub trait Image: Sized {
811
/// Get the width of the image, in pixels
912
fn width(&self) -> u32;

src/image/reducer.rs

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
use crate::models::{Color16, Led};
2+
3+
use super::Image;
4+
5+
#[derive(Debug, Default)]
6+
pub struct Reducer {}
7+
8+
impl Reducer {
9+
pub fn reduce(&mut self, image: &impl Image, leds: &[Led], color_data: &mut [Color16]) {
10+
let width = image.width() as f32;
11+
let height = image.height() as f32;
12+
for (spec, value) in leds.iter().zip(color_data.iter_mut()) {
13+
let mut r_acc = 0u64;
14+
let mut g_acc = 0u64;
15+
let mut b_acc = 0u64;
16+
let mut cnt = 0u64;
17+
18+
// TODO: Fixed point arithmetic
19+
let lxmin = spec.hmin * width;
20+
let lxmax = spec.hmax * width;
21+
let lymin = spec.vmin * height;
22+
let lymax = spec.vmax * height;
23+
24+
for y in lymin.floor() as u32..=(lymax.ceil() as u32).min(image.height() - 1) {
25+
let y_area = if (y as f32) < lymin {
26+
(255. * (1. - lymin.fract())) as u64
27+
} else if (y + 1) as f32 > lymax {
28+
(255. * lymax.fract()) as u64
29+
} else {
30+
255
31+
};
32+
33+
for x in lxmin.floor() as u32..=(lxmax.ceil() as u32).min(image.width() - 1) {
34+
if let Some(rgb) = image.color_at(x, y) {
35+
let x_area = if (x as f32) < lxmin {
36+
(255. * (1. - lxmin.fract())) as u64
37+
} else if (x + 1) as f32 > lxmax {
38+
(255. * lxmax.fract()) as u64
39+
} else {
40+
255
41+
};
42+
43+
let area = x_area * y_area / 255;
44+
45+
let (r, g, b) = rgb.into_components();
46+
r_acc += (r as u64 * 255) * area;
47+
g_acc += (g as u64 * 255) * area;
48+
b_acc += (b as u64 * 255) * area;
49+
cnt += area;
50+
}
51+
}
52+
}
53+
54+
*value = Color16::new(
55+
(r_acc / cnt.max(1)).max(0).min(u16::MAX as _) as u16,
56+
(g_acc / cnt.max(1)).max(0).min(u16::MAX as _) as u16,
57+
(b_acc / cnt.max(1)).max(0).min(u16::MAX as _) as u16,
58+
);
59+
}
60+
}
61+
}

src/instance/core.rs

+7-52
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::{
22
color::{color_to16, ChannelAdjustments, ChannelAdjustmentsBuilder},
3-
image::prelude::*,
3+
image::{prelude::*, Reducer},
44
models::{Color, Color16, InstanceConfig, Leds},
55
};
66

@@ -16,6 +16,7 @@ pub struct Core {
1616
channel_adjustments: ChannelAdjustments,
1717
smoothing: Smoothing,
1818
notified_inconsistent_led_data: bool,
19+
reducer: Reducer,
1920
}
2021

2122
impl Core {
@@ -35,6 +36,7 @@ impl Core {
3536
channel_adjustments,
3637
smoothing,
3738
notified_inconsistent_led_data: false,
39+
reducer: Default::default(),
3840
}
3941
}
4042

@@ -47,62 +49,15 @@ impl Core {
4749
self.black_border_detector.process(image);
4850
let black_border = self.black_border_detector.current_border();
4951

50-
// Update the 16-bit color data from the LED ranges and the image
52+
// Crop the image using a view
5153
let image = {
5254
let (x, y) = black_border.get_ranges(image.width(), image.height());
5355
image.wrap(x, y)
5456
};
5557

56-
let width = image.width() as f32;
57-
let height = image.height() as f32;
58-
for (spec, value) in self.leds.leds.iter().zip(self.color_data.iter_mut()) {
59-
let mut r_acc = 0u64;
60-
let mut g_acc = 0u64;
61-
let mut b_acc = 0u64;
62-
let mut cnt = 0u64;
63-
64-
// TODO: Fixed point arithmetic
65-
let lxmin = spec.hmin * width;
66-
let lxmax = spec.hmax * width;
67-
let lymin = spec.vmin * height;
68-
let lymax = spec.vmax * height;
69-
70-
for y in lymin.floor() as u32..=(lymax.ceil() as u32).min(image.height() - 1) {
71-
let y_area = if (y as f32) < lymin {
72-
(255. * (1. - lymin.fract())) as u64
73-
} else if (y + 1) as f32 > lymax {
74-
(255. * lymax.fract()) as u64
75-
} else {
76-
255
77-
};
78-
79-
for x in lxmin.floor() as u32..=(lxmax.ceil() as u32).min(image.width() - 1) {
80-
if let Some(rgb) = image.color_at(x, y) {
81-
let x_area = if (x as f32) < lxmin {
82-
(255. * (1. - lxmin.fract())) as u64
83-
} else if (x + 1) as f32 > lxmax {
84-
(255. * lxmax.fract()) as u64
85-
} else {
86-
255
87-
};
88-
89-
let area = x_area * y_area / 255;
90-
91-
let (r, g, b) = rgb.into_components();
92-
r_acc += (r as u64 * 255) * area;
93-
g_acc += (g as u64 * 255) * area;
94-
b_acc += (b as u64 * 255) * area;
95-
cnt += area;
96-
}
97-
}
98-
}
99-
100-
*value = Color16::new(
101-
(r_acc / cnt.max(1)).max(0).min(u16::MAX as _) as u16,
102-
(g_acc / cnt.max(1)).max(0).min(u16::MAX as _) as u16,
103-
(b_acc / cnt.max(1)).max(0).min(u16::MAX as _) as u16,
104-
);
105-
}
58+
// Update the 16-bit color data from the LED ranges and the image
59+
self.reducer
60+
.reduce(&image, &self.leds.leds[..], &mut self.color_data);
10661
}
10762

10863
fn handle_led_colors(&mut self, led_colors: &[Color]) {

0 commit comments

Comments
 (0)