macro_rules! impl_packed_u16_boilerplate {
($name:ident) => {
impl $name {
#[must_use]
pub const fn new(packed: u16) -> Self {
Self { packed }
}
}
impl From<u16> for $name {
fn from(packed: u16) -> Self {
Self::new(packed)
}
}
impl From<$name> for u16 {
fn from(color: $name) -> Self {
color.packed
}
}
impl core::fmt::LowerHex for $name {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::LowerHex::fmt(&self.packed, f)
}
}
impl core::fmt::UpperHex for $name {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::UpperHex::fmt(&self.packed, f)
}
}
};
}
pub(crate) use impl_packed_u16_boilerplate;
macro_rules! define_packed_rgb {
(
$(#[$attr:meta])*
$vis:vis struct $name:ident {
red: $rbits:literal @ $rshift:literal,
green: $gbits:literal @ $gshift:literal,
blue: $bbits:literal @ $bshift:literal $(,)?
}
) => {
$(#[$attr])*
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Zeroable, bytemuck::Pod))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", allow(clippy::unsafe_derive_deserialize))]
#[repr(transparent)]
$vis struct $name {
packed: u16,
}
crate::rgb::macros::impl_packed_u16_boilerplate!($name);
impl $name {
#[must_use]
pub const fn from_rgb(r: u8, g: u8, b: u8) -> Self {
let packed = ((r as u16 & ((1u16 << $rbits) - 1)) << $rshift)
| ((g as u16 & ((1u16 << $gbits) - 1)) << $gshift)
| ((b as u16 & ((1u16 << $bbits) - 1)) << $bshift);
Self { packed }
}
}
crate::rgb::macros::impl_rgb_packed!(
$name,
red: { shift: $rshift, mask: (1u16 << $rbits) - 1, clear: !(((1u16 << $rbits) - 1) << $rshift) },
green: { shift: $gshift, mask: (1u16 << $gbits) - 1, clear: !(((1u16 << $gbits) - 1) << $gshift) },
blue: { shift: $bshift, mask: (1u16 << $bbits) - 1, clear: !(((1u16 << $bbits) - 1) << $bshift) }
);
impl crate::space::RgbChannelScale for $name {
const RED_MAX: f32 = ((1u16 << $rbits) - 1) as f32;
const GREEN_MAX: f32 = ((1u16 << $gbits) - 1) as f32;
const BLUE_MAX: f32 = ((1u16 << $bbits) - 1) as f32;
}
impl From<crate::space::Srgb> for $name {
fn from(c: crate::space::Srgb) -> Self {
crate::space::FromSrgb::from_srgb(c)
}
}
};
}
pub(crate) use define_packed_rgb;
macro_rules! define_packed_argb {
(
$(#[$attr:meta])*
$vis:vis struct $name:ident {
alpha: $abits:literal @ $ashift:literal,
red: $rbits:literal @ $rshift:literal,
green: $gbits:literal @ $gshift:literal,
blue: $bbits:literal @ $bshift:literal $(,)?
}
) => {
$(#[$attr])*
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Zeroable, bytemuck::Pod))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", allow(clippy::unsafe_derive_deserialize))]
#[repr(transparent)]
$vis struct $name {
packed: u16,
}
crate::rgb::macros::impl_packed_u16_boilerplate!($name);
impl $name {
#[must_use]
pub const fn from_argb(a: u8, r: u8, g: u8, b: u8) -> Self {
let packed = ((a as u16 & ((1u16 << $abits) - 1)) << $ashift)
| ((r as u16 & ((1u16 << $rbits) - 1)) << $rshift)
| ((g as u16 & ((1u16 << $gbits) - 1)) << $gshift)
| ((b as u16 & ((1u16 << $bbits) - 1)) << $bshift);
Self { packed }
}
#[must_use]
#[expect(
clippy::cast_possible_truncation,
reason = "alpha bit width is always <= 8, so the max value always fits in a u8"
)]
pub const fn from_rgb(r: u8, g: u8, b: u8) -> Self {
Self::from_argb(((1u16 << $abits) - 1) as u8, r, g, b)
}
}
crate::rgb::macros::impl_rgb_packed!(
$name,
red: { shift: $rshift, mask: (1u16 << $rbits) - 1, clear: !(((1u16 << $rbits) - 1) << $rshift) },
green: { shift: $gshift, mask: (1u16 << $gbits) - 1, clear: !(((1u16 << $gbits) - 1) << $gshift) },
blue: { shift: $bshift, mask: (1u16 << $bbits) - 1, clear: !(((1u16 << $bbits) - 1) << $bshift) }
);
crate::rgb::macros::impl_with_alpha_packed!(
$name,
$ashift,
(1u16 << $abits) - 1,
!(((1u16 << $abits) - 1) << $ashift)
);
impl crate::space::RgbChannelScale for $name {
const RED_MAX: f32 = ((1u16 << $rbits) - 1) as f32;
const GREEN_MAX: f32 = ((1u16 << $gbits) - 1) as f32;
const BLUE_MAX: f32 = ((1u16 << $bbits) - 1) as f32;
}
impl From<crate::space::Srgb> for $name {
fn from(c: crate::space::Srgb) -> Self {
crate::space::FromSrgb::from_srgb(c)
}
}
};
}
pub(crate) use define_packed_argb;