ddp_rs/protocol/
pixel_config.rs

1/// Pixel data type (color space).
2///
3/// Defines how pixel color values should be interpreted.
4#[derive(Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Clone, Copy)]
5#[repr(u8)]
6#[allow(dead_code)]
7pub enum DataType {
8    /// Undefined or custom data type
9    Undefined,
10    /// Red, Green, Blue
11    RGB,
12    /// Hue, Saturation, Lightness
13    HSL,
14    /// Red, Green, Blue, White
15    RGBW,
16    /// Grayscale/monochrome
17    Grayscale,
18}
19
20/// Number of bits per pixel.
21///
22/// Defines the bit depth for each pixel's data.
23#[derive(Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Clone, Copy)]
24#[repr(u8)]
25#[allow(dead_code)]
26pub enum PixelFormat {
27    /// Undefined format
28    Undefined,
29    /// 1 bit per pixel
30    Pixel1Bits,
31    /// 4 bits per pixel
32    Pixel4Bits,
33    /// 8 bits per pixel (1 byte)
34    Pixel8Bits,
35    /// 16 bits per pixel (2 bytes)
36    Pixel16Bits,
37    /// 24 bits per pixel (3 bytes) - standard RGB
38    Pixel24Bits,
39    /// 32 bits per pixel (4 bytes) - RGBA or RGBW
40    Pixel32Bits,
41}
42
43/// Pixel format configuration.
44///
45/// Describes how pixel data is encoded in the packet. The default configuration
46/// is RGB with 8 bits per channel (24 bits total per pixel).
47///
48/// # Examples
49///
50/// ```
51/// use ddp_rs::protocol::{PixelConfig, DataType, PixelFormat};
52///
53/// // Default: RGB, 8 bits per channel
54/// let config = PixelConfig::default();
55/// assert_eq!(config.data_type, DataType::RGB);
56/// assert_eq!(config.data_size, PixelFormat::Pixel24Bits);
57///
58/// // Custom: RGBW pixels
59/// let rgbw_config = PixelConfig {
60///     data_type: DataType::RGBW,
61///     data_size: PixelFormat::Pixel32Bits,
62///     customer_defined: false,
63/// };
64/// ```
65#[derive(Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Clone, Copy)]
66#[allow(dead_code)]
67pub struct PixelConfig {
68    /// Color space / data type
69    pub data_type: DataType,
70
71    /// Bits per pixel
72    pub data_size: PixelFormat,
73
74    /// Whether this is a custom/vendor-specific configuration
75    pub customer_defined: bool,
76}
77
78impl From<u8> for PixelConfig {
79    fn from(byte: u8) -> Self {
80        let data_type = match (byte >> 3) & 0x07 {
81            0 => DataType::Undefined,
82            1 => DataType::RGB,
83            2 => DataType::HSL,
84            3 => DataType::RGBW,
85            4 => DataType::Grayscale,
86            _ => DataType::Undefined,
87        };
88
89        let data_size = match (byte & 0x07) as usize {
90            0 => PixelFormat::Undefined,
91            1 => PixelFormat::Pixel1Bits,
92            2 => PixelFormat::Pixel4Bits,
93            3 => PixelFormat::Pixel8Bits,
94            4 => PixelFormat::Pixel16Bits,
95            5 => PixelFormat::Pixel24Bits,
96            6 => PixelFormat::Pixel32Bits,
97            _ => PixelFormat::Undefined,
98        };
99
100        let customer_defined = (byte >> 7) != 0;
101
102        PixelConfig {
103            data_type,
104            data_size,
105            customer_defined,
106        }
107    }
108}
109
110impl Into<u8> for PixelConfig {
111    fn into(self) -> u8 {
112        let mut byte = 0u8;
113
114        byte |= match self.data_type {
115            DataType::Undefined => 0,
116            DataType::RGB => 1,
117            DataType::HSL => 2,
118            DataType::RGBW => 3,
119            DataType::Grayscale => 4,
120        } << 3;
121
122        byte |= match self.data_size {
123            PixelFormat::Undefined => 0,
124            PixelFormat::Pixel1Bits => 1,
125            PixelFormat::Pixel4Bits => 2,
126            PixelFormat::Pixel8Bits => 3,
127            PixelFormat::Pixel16Bits => 4,
128            PixelFormat::Pixel24Bits => 5,
129            PixelFormat::Pixel32Bits => 6,
130        };
131
132        if self.customer_defined {
133            byte |= 0x80;
134        }
135
136        byte
137    }
138}
139
140impl Default for PixelConfig {
141    fn default() -> Self {
142        Self {
143            data_type: DataType::RGB,
144            data_size: PixelFormat::Pixel24Bits,
145            customer_defined: Default::default(),
146        }
147    }
148}
149
150#[cfg(test)]
151mod tests {
152    use super::*;
153
154    #[test]
155    fn test_pixel_config_from_u8() {
156        // WLED oddities
157        {
158            let byte = 0x0A;
159            let pixel_config = PixelConfig::from(byte);
160
161            assert_eq!(pixel_config.data_type, DataType::RGB);
162            assert_eq!(pixel_config.data_size, PixelFormat::Pixel4Bits);
163            assert!(!pixel_config.customer_defined);
164        }
165
166        // RGB 24
167        {
168            let byte = 0x0D;
169            let pixel_config = PixelConfig::from(byte);
170
171            assert_eq!(pixel_config.data_type, DataType::RGB);
172            assert_eq!(pixel_config.data_size, PixelFormat::Pixel24Bits);
173            assert!(!pixel_config.customer_defined);
174        }
175
176        // RGBW 32
177        {
178            let byte = 0x1E;
179            let pixel_config = PixelConfig::from(byte);
180
181            assert_eq!(pixel_config.data_type, DataType::RGBW);
182            assert_eq!(pixel_config.data_size, PixelFormat::Pixel32Bits);
183            assert!(!pixel_config.customer_defined);
184        }
185    }
186}