#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(C)]
pub struct Color {
pub r: f32,
pub g: f32,
pub b: f32,
pub a: f32,
}
impl Color {
pub const fn rgb(r: f32, g: f32, b: f32) -> Self {
Self { r, g, b, a: 255.0 }
}
pub const fn rgba(r: f32, g: f32, b: f32, a: f32) -> Self {
Self { r, g, b, a }
}
pub const fn u_rgb(r: u8, g: u8, b: u8) -> Self {
Self::rgb(r as _, g as _, b as _)
}
pub const fn u_rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
Self::rgba(r as _, g as _, b as _, a as _)
}
}
impl From<(f32, f32, f32)> for Color {
fn from(value: (f32, f32, f32)) -> Self {
Self::rgb(value.0, value.1, value.2)
}
}
impl From<(f32, f32, f32, f32)> for Color {
fn from(value: (f32, f32, f32, f32)) -> Self {
Self::rgba(value.0, value.1, value.2, value.3)
}
}
impl From<(u8, u8, u8)> for Color {
fn from(value: (u8, u8, u8)) -> Self {
Self::u_rgb(value.0, value.1, value.2)
}
}
impl From<(u8, u8, u8, u8)> for Color {
fn from(value: (u8, u8, u8, u8)) -> Self {
Self::u_rgba(value.0, value.1, value.2, value.3)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ColorAttachmentPoint {
TopLeft,
TopCenter,
TopRight,
CenterLeft,
CenterCenter,
CenterRight,
BottomLeft,
BottomCenter,
BottomRight,
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ColorPos {
pub x: f32,
pub y: f32,
pub attachment_point: ColorAttachmentPoint,
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct GradientStop {
pub offset: f32,
pub color: Color,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LinearGradient {
pub start: ColorPos,
pub end: ColorPos,
pub stops: Vec<GradientStop>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct RadialGradient {
pub center: ColorPos,
pub radius: f32,
pub stops: Vec<GradientStop>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct RoundedRectGradient {
pub position: ColorPos,
pub width: f32,
pub height: f32,
pub corner_radius: f32,
pub stops: Vec<GradientStop>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ColorLayer {
Solid(Color),
Linear(LinearGradient),
Radial(RadialGradient),
RoundedRect(RoundedRectGradient),
Layered(LayeredColor),
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LayeredColor {
pub layers: Vec<ColorLayer>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rgb_sets_opaque_alpha() {
let color = Color::rgb(10.0, 20.0, 30.0);
assert_eq!(color.a, 255.0);
assert_eq!(color.r, 10.0);
}
#[test]
fn from_tuple_conversions_set_channels() {
let from_float: Color = (1.0, 2.0, 3.0).into();
assert_eq!(from_float, Color::rgb(1.0, 2.0, 3.0));
let from_bytes: Color = (4, 5, 6).into();
assert_eq!(from_bytes, Color::u_rgb(4, 5, 6));
}
}