1use core::{marker::Copy, ops::Mul};
2use core::convert::AsRef;
3use core::result::Result;
4use core::iter::Iterator;
5
6use smart_leds_trait::{SmartLedsWrite, SmartLedsWriteAsync};
7
8use figments::{liber8tion::interpolate::Fract8, mappings::linear::LinearSpace, prelude::*};
9
10use crate::{gamma::{GammaCurve, WithGamma}, output::{Brightness, GammaCorrected, Output, OutputAsync}, power::*};
11
12#[derive(Debug)]
13pub struct PowerControls {
14 max_mw: Milliwatts,
15 brightness: Fract8,
16 is_on: bool,
17 gamma_curve: GammaCurve,
18 cur_mw: u32
19}
20
21impl PowerControls {
22 pub fn new(max_mw: Milliwatts) -> Self {
23 Self {
24 max_mw,
25 brightness: Fract8::MAX,
26 is_on: true,
27 gamma_curve: GammaCurve::default(),
28 cur_mw: 0
29 }
30 }
31
32 pub fn max_power(&self) -> Milliwatts {
33 self.max_mw
34 }
35
36 pub fn set_max_power(&mut self, max_mw: Milliwatts) {
37 self.max_mw = max_mw;
38 }
39
40 pub fn iter_brightness<'a, Color, P: AsRef<[Color]> + ?Sized>(&'a mut self, pixbuf: &'a P) -> impl Iterator<Item = Color> + use<'a, Color, P> where Color: 'a + Copy + WithGamma + AsMilliwatts + Mul<Fract8, Output = Color> {
41 self.cur_mw = pixbuf.as_ref().iter().map(|x| { x.with_gamma(&self.gamma_curve).as_milliwatts() }).sum();
42 let b = brightness_for_mw(self.cur_mw, self.brightness, self.max_mw);
43 pixbuf.as_ref().iter().map(move |x| { x.with_gamma(&self.gamma_curve) * b })
44 }
45}
46
47impl Brightness for PowerControls {
48 fn set_brightness(&mut self, brightness: Fract8) {
49 self.brightness = brightness;
50 }
51
52 fn set_on(&mut self, is_on: bool) {
53 self.is_on = is_on;
54 }
55}
56
57impl GammaCorrected for PowerControls {
58 fn set_gamma(&mut self, gamma: GammaCurve) {
59 self.gamma_curve = gamma
60 }
61}
62
63#[derive(Debug)]
64pub struct PowerManagedWriter<T> {
65 target: T,
66 controls: PowerControls
67}
68
69impl<T> PowerManagedWriter<T> {
70 pub fn new(target: T, max_mw: Milliwatts) -> Self {
71 Self {
72 target,
73 controls: PowerControls::new(max_mw)
74 }
75 }
76
77 pub fn write<P: AsRef<[T::Color]> + ?Sized>(&mut self, pixbuf: &P) -> Result<(), T::Error> where T: SmartLedsWrite, T::Color: Mul<Fract8, Output = T::Color> + Copy + WithGamma + AsMilliwatts + core::fmt::Debug {
78 if self.controls.is_on {
79 self.target.write(self.controls.iter_brightness(pixbuf))
80 } else {
81 self.target.write(pixbuf.as_ref().iter().map(|x| { *x * Fract8::MIN }))
82 }
83 }
84
85
86 pub async fn write_async<P: AsRef<[T::Color]> + ?Sized>(&mut self, pixbuf: &P) -> Result<(), T::Error> where T: SmartLedsWriteAsync, T::Color: Mul<Fract8, Output = T::Color> + Copy + WithGamma + AsMilliwatts + core::fmt::Debug {
87 if self.controls.is_on {
88 self.target.write(self.controls.iter_brightness(pixbuf)).await
89 } else {
90 self.target.write(pixbuf.as_ref().iter().map(|x| { *x * Fract8::MIN })).await
91 }
92 }
93
94 pub fn controls(&mut self) -> &mut PowerControls {
95 &mut self.controls
96 }
97
98 pub const fn max_mw(&self) -> u32 {
100 self.controls.cur_mw
101 }
102}
103
104pub struct SmartLedsOutput<'a, T, Pixbuf> {
105 writer: PowerManagedWriter<T>,
106 pixbuf: &'a mut Pixbuf,
107 buf_idx: usize,
108 clip: Rectangle<LinearSpace>
109}
110
111impl<'a, T, Pixel, const PIXEL_COUNT: usize> SmartLedsOutput<'a, T, [Pixel; PIXEL_COUNT]> {
112 pub fn new(target: T, pixbuf: &'a mut [Pixel; PIXEL_COUNT], max_mw: Milliwatts) -> Self {
113 Self {
114 writer: PowerManagedWriter::new(target, max_mw),
115 pixbuf,
116 buf_idx: 0,
117 clip: Rectangle::everything()
118 }
119 }
120
121 pub const fn pixbuf(&mut self) -> &mut [Pixel; PIXEL_COUNT] {
122 self.pixbuf
123 }
124
125 pub fn set_clip(&mut self, clip: Rectangle<LinearSpace>) {
126 self.clip = clip;
127 }
128
129 pub fn swap_buffer(&mut self, pixbuf: &'a mut [Pixel; PIXEL_COUNT]) -> &'a mut [Pixel; PIXEL_COUNT] {
131 self.buf_idx = (self.buf_idx + 1) % self.pixbuf.as_ref().len();
132 core::mem::replace(&mut self.pixbuf, pixbuf)
133 }
134}
135
136impl<'a, T: SmartLedsWrite + 'a, Pixbuf: AsRef<[T::Color]>> Output<'a, LinearSpace> for SmartLedsOutput<'a, T, Pixbuf> where Self: Sample<'a, LinearSpace>, T::Color: core::fmt::Debug + AsMilliwatts + Mul<Fract8, Output = T::Color> + Copy + WithGamma {
137 type Error = T::Error;
138
139 type Controls = PowerControls;
140
141 fn commit(&mut self) -> Result<(), Self::Error> {
142 self.writer.write(&self.pixbuf)
143 }
144
145 fn controls(&mut self) -> Option<&mut Self::Controls> {
146 Some(self.writer.controls())
147 }
148}
149
150impl<'a, T: SmartLedsWriteAsync + 'a, Pixbuf: AsRef<[T::Color]>> OutputAsync<'a, LinearSpace> for SmartLedsOutput<'a, T, Pixbuf> where Self: Sample<'a, LinearSpace>, T::Color: core::fmt::Debug + AsMilliwatts + Mul<Fract8, Output = T::Color> + Copy + WithGamma {
151 type Error = T::Error;
152
153 type Controls = PowerControls;
154
155 async fn commit_async(&mut self) -> Result<(), Self::Error> {
156 self.writer.write_async(&self.pixbuf).await
157 }
158
159 fn controls(&mut self) -> Option<&mut Self::Controls> {
160 Some(self.writer.controls())
161 }
162}
163
164impl<'a, T, Color, const PIXEL_COUNT: usize> Sample<'a, LinearSpace> for SmartLedsOutput<'a, T, [Color; PIXEL_COUNT]> where Color: 'a {
165 type Output = Color;
166
167 fn sample(&mut self, rect: &figments::prelude::Rectangle<LinearSpace>) -> impl Iterator<Item = (figments::prelude::Coordinates<LinearSpace>, &'a mut Self::Output)> {
168 let start = self.clip.top_left.x.clamp(0, self.pixbuf.len() - 1);
169 let end = self.clip.bottom_right.x.clamp(0, self.pixbuf.len() - 1);
170 self.pixbuf[start..=end].sample(rect)
171 }
172}