1#[cfg(feature = "serde")]
2use serde::{Deserialize, Serialize};
3#[derive(Debug, Clone, Copy, PartialEq)]
9#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10#[repr(C)]
11pub struct Color {
12 pub r: f32,
14 pub g: f32,
16 pub b: f32,
18 pub a: f32,
20}
21
22impl Color {
23 pub const fn rgb(r: f32, g: f32, b: f32) -> Self {
36 Self { r, g, b, a: 255.0 }
37 }
38 pub const fn rgba(r: f32, g: f32, b: f32, a: f32) -> Self {
40 Self { r, g, b, a }
41 }
42
43 pub const fn u_rgb(r: u8, g: u8, b: u8) -> Self {
45 Self::rgb(r as _, g as _, b as _)
46 }
47 pub const fn u_rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
49 Self::rgba(r as _, g as _, b as _, a as _)
50 }
51}
52
53impl From<(f32, f32, f32)> for Color {
54 fn from(value: (f32, f32, f32)) -> Self {
55 Self::rgb(value.0, value.1, value.2)
56 }
57}
58impl From<(f32, f32, f32, f32)> for Color {
59 fn from(value: (f32, f32, f32, f32)) -> Self {
60 Self::rgba(value.0, value.1, value.2, value.3)
61 }
62}
63
64impl From<(u8, u8, u8)> for Color {
65 fn from(value: (u8, u8, u8)) -> Self {
66 Self::u_rgb(value.0, value.1, value.2)
67 }
68}
69impl From<(u8, u8, u8, u8)> for Color {
70 fn from(value: (u8, u8, u8, u8)) -> Self {
71 Self::u_rgba(value.0, value.1, value.2, value.3)
72 }
73}
74
75#[derive(Debug, Clone, Copy, PartialEq)]
77#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
78pub enum ColorAttachmentPoint {
79 TopLeft,
80 TopCenter,
81 TopRight,
82 CenterLeft,
83 CenterCenter,
84 CenterRight,
85 BottomLeft,
86 BottomCenter,
87 BottomRight,
88}
89
90#[derive(Debug, Clone, Copy, PartialEq)]
92#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
93pub struct ColorPos {
94 pub x: f32,
96 pub y: f32,
98 pub attachment_point: ColorAttachmentPoint,
100}
101
102#[derive(Debug, Clone, Copy, PartialEq)]
104#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
105pub struct GradientStop {
106 pub offset: f32,
108 pub color: Color,
110}
111
112#[derive(Debug, Clone, PartialEq)]
114#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
115pub struct LinearGradient {
116 pub start: ColorPos,
118 pub end: ColorPos,
120 pub stops: Vec<GradientStop>,
122}
123
124#[derive(Debug, Clone, PartialEq)]
126#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
127pub struct RadialGradient {
128 pub center: ColorPos,
130 pub radius: f32,
132 pub stops: Vec<GradientStop>,
134}
135
136#[derive(Debug, Clone, PartialEq)]
141#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
142pub struct RoundedRectGradient {
143 pub position: ColorPos,
145 pub width: f32,
147 pub height: f32,
149 pub corner_radius: f32,
151 pub stops: Vec<GradientStop>,
153}
154
155#[derive(Debug, Clone, PartialEq)]
157#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
158pub enum ColorLayer {
159 Solid(Color),
161 Linear(LinearGradient),
163 Radial(RadialGradient),
165 RoundedRect(RoundedRectGradient),
167 Layered(LayeredColor),
169}
170
171#[derive(Debug, Clone, PartialEq)]
173#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
174pub struct LayeredColor {
175 pub layers: Vec<ColorLayer>,
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182
183 #[test]
184 fn rgb_sets_opaque_alpha() {
185 let color = Color::rgb(10.0, 20.0, 30.0);
186 assert_eq!(color.a, 255.0);
187 assert_eq!(color.r, 10.0);
188 }
189
190 #[test]
191 fn from_tuple_conversions_set_channels() {
192 let from_float: Color = (1.0, 2.0, 3.0).into();
193 assert_eq!(from_float, Color::rgb(1.0, 2.0, 3.0));
194
195 let from_bytes: Color = (4, 5, 6).into();
196 assert_eq!(from_bytes, Color::u_rgb(4, 5, 6));
197 }
198}