1use std::io::{Cursor, Result, Write};
4
5use crate::serial::{ReadFrom, WriteTo};
6
7#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
12#[repr(transparent)]
13pub struct RgbColor(i32);
14
15impl RgbColor {
16 #[must_use]
18 pub const fn new(raw: i32) -> Self {
19 Self(raw)
20 }
21
22 #[must_use]
24 pub const fn raw(self) -> i32 {
25 self.0
26 }
27
28 #[must_use]
30 pub const fn red(self) -> u8 {
31 (self.0 as u32 >> 16) as u8
32 }
33
34 #[must_use]
36 pub const fn green(self) -> u8 {
37 (self.0 as u32 >> 8) as u8
38 }
39
40 #[must_use]
42 pub const fn blue(self) -> u8 {
43 self.0 as u8
44 }
45
46 #[must_use]
48 pub const fn with_alpha(self, alpha: u8) -> ArgbColor {
49 ArgbColor::new(((alpha as u32) << 24 | (self.0 as u32 & 0x00ff_ffff)) as i32)
50 }
51}
52
53impl WriteTo for RgbColor {
54 fn write(&self, writer: &mut impl Write) -> Result<()> {
55 self.0.write(writer)
56 }
57}
58
59impl ReadFrom for RgbColor {
60 fn read(data: &mut Cursor<&[u8]>) -> Result<Self> {
61 Ok(Self::new(i32::read(data)?))
62 }
63}
64
65#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
67#[repr(transparent)]
68pub struct ArgbColor(i32);
69
70impl ArgbColor {
71 #[must_use]
73 pub const fn new(raw: i32) -> Self {
74 Self(raw)
75 }
76
77 #[must_use]
79 pub const fn raw(self) -> i32 {
80 self.0
81 }
82
83 #[must_use]
85 pub const fn alpha(self) -> u8 {
86 (self.0 as u32 >> 24) as u8
87 }
88
89 #[must_use]
91 pub const fn red(self) -> u8 {
92 (self.0 as u32 >> 16) as u8
93 }
94
95 #[must_use]
97 pub const fn green(self) -> u8 {
98 (self.0 as u32 >> 8) as u8
99 }
100
101 #[must_use]
103 pub const fn blue(self) -> u8 {
104 self.0 as u8
105 }
106
107 #[must_use]
109 pub const fn rgb(self) -> RgbColor {
110 RgbColor::new(self.0)
111 }
112
113 #[must_use]
115 pub const fn average(self, other: Self) -> Self {
116 Self(
117 ((Self::average_channel(self.alpha(), other.alpha()) << 24)
118 | (Self::average_channel(self.red(), other.red()) << 16)
119 | (Self::average_channel(self.green(), other.green()) << 8)
120 | Self::average_channel(self.blue(), other.blue())) as i32,
121 )
122 }
123
124 const fn average_channel(lhs: u8, rhs: u8) -> u32 {
125 u32::midpoint(lhs as u32, rhs as u32)
126 }
127}
128
129impl WriteTo for ArgbColor {
130 fn write(&self, writer: &mut impl Write) -> Result<()> {
131 self.0.write(writer)
132 }
133}
134
135impl ReadFrom for ArgbColor {
136 fn read(data: &mut Cursor<&[u8]>) -> Result<Self> {
137 Ok(Self::new(i32::read(data)?))
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144
145 #[test]
146 fn argb_average_matches_vanilla_per_channel_mean() {
147 let lhs = ArgbColor::new(0xff00_0000_u32 as i32);
148 let rhs = ArgbColor::new(0x00ff_ffff);
149
150 assert_eq!(lhs.average(rhs).raw(), 0x7f7f_7f7f);
152 assert_eq!(
153 ArgbColor::new(0xffff_0000_u32 as i32)
154 .average(ArgbColor::new(0xff00_00ff_u32 as i32))
155 .raw(),
156 0xff7f_007f_u32 as i32
157 );
158 }
159
160 #[test]
161 fn rgb_preserves_ignored_upper_byte() {
162 let color = RgbColor::new(0x7f12_3456);
163
164 assert_eq!(color.raw(), 0x7f12_3456);
165 assert_eq!(
166 (color.red(), color.green(), color.blue()),
167 (0x12, 0x34, 0x56)
168 );
169 assert_eq!(color.with_alpha(0xaa).raw(), 0xaa12_3456_u32 as i32);
170 }
171
172 #[test]
173 fn argb_exposes_all_channels() {
174 let color = ArgbColor::new(0xaabb_ccdd_u32 as i32);
175
176 assert_eq!(
177 (color.alpha(), color.red(), color.green(), color.blue()),
178 (0xaa, 0xbb, 0xcc, 0xdd)
179 );
180 }
181}