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