Is This Color Definition Optimal? Exploring Its Use Cases, Limitations, and Rust Design Philosophy
⚓ Rust 📅 2025-10-23 👤 surdeus 👁️ 2I am currently learning about graphics-related knowledge. Regarding color, I have the following definition, and I would like to understand the applicable scenarios and limitations of this definition, as well as any issues from the perspective of Rust's design philosophy. Thank you all for your help.
use std::marker::PhantomData;
pub trait ColorSpace {}
#[derive(Debug)]
pub struct Srgb ;
impl ColorSpace for Srgb {}
pub trait Premultipliable {
type Output;
fn premultiply(&self) -> Self::Output;
}
pub trait Depremultipliable {
type Output;
fn depremultiply(&self) -> Self::Output;
}
pub trait IsPremultiplied {
const IS_PREMULTIPLIED: bool;
}
#[derive(Debug)]
pub struct NotPremultiplied;
#[derive(Debug)]
pub struct Premultiplied;
impl IsPremultiplied for NotPremultiplied {
const IS_PREMULTIPLIED: bool = false;
}
impl IsPremultiplied for Premultiplied {
const IS_PREMULTIPLIED: bool = true;
}
#[derive(Debug, Clone, Copy)]
#[repr(transparent)]
pub struct Color<T, const N: usize, CS: ColorSpace = Srgb , P = NotPremultiplied> {
pub components: [T; N],
pub _cs: PhantomData<CS>,
pub _p: PhantomData<P>,
}
impl<T, const N: usize, CS: ColorSpace, P: IsPremultiplied> Color<T, N, CS, P> {
pub fn new(components: [T; N]) -> Self {
Self {
components,
_cs: PhantomData,
_p: PhantomData,
}
}
pub fn is_premultiplied(self) -> bool {
P::IS_PREMULTIPLIED
}
}
pub trait OpaqueColor<T> {
fn red(&self) -> T;
fn green(&self) -> T;
fn blue(&self) -> T;
}
pub trait AlphaColor<T> {
fn red(&self) -> T;
fn green(&self) -> T;
fn blue(&self) -> T;
fn alpha(&self) -> T;
}
impl<T: Copy, CS: ColorSpace> OpaqueColor<T> for Color<T, 3, CS> {
fn red(&self) -> T {
self.components[0]
}
fn green(&self) -> T {
self.components[1]
}
fn blue(&self) -> T {
self.components[2]
}
}
impl<T: Copy, CS: ColorSpace, P: IsPremultiplied> AlphaColor<T> for Color<T, 4, CS, P> {
fn red(&self) -> T {
self.components[0]
}
fn green(&self) -> T {
self.components[1]
}
fn blue(&self) -> T {
self.components[2]
}
fn alpha(&self) -> T {
self.components[3]
}
}
impl<CS: ColorSpace> Premultipliable for Color<u8, 4, CS, NotPremultiplied> {
type Output = Color<u8, 4, CS, Premultiplied>;
fn premultiply(&self) -> Self::Output {
let a = self.alpha();
if a != 255 {
Color {
components: [
premultiply_u8(self.red(), a),
premultiply_u8(self.green(), a),
premultiply_u8(self.blue(), a),
a,
],
_cs: PhantomData,
_p: PhantomData,
}
} else {
Color {
components: [self.red(), self.green(), self.blue(), a],
_cs: PhantomData,
_p: PhantomData,
}
}
}
}
impl<CS: ColorSpace> Depremultipliable for Color<u8, 4, CS, Premultiplied> {
type Output = Color<u8, 4, CS, NotPremultiplied>;
fn depremultiply(&self) -> Self::Output {
let alpha = self.alpha();
if alpha == 255 {
Color {
components: self.components,
_cs: PhantomData,
_p: PhantomData,
}
} else {
let a = alpha as f64 / 255.0;
Color {
components: [
(self.red() as f64 / a + 0.5) as u8,
(self.green() as f64 / a + 0.5) as u8,
(self.blue() as f64 / a + 0.5) as u8,
alpha,
],
_cs: PhantomData,
_p: PhantomData,
}
}
}
}
pub type Rgb = Color<u8, 3>;
pub type Rgba = Color<u8, 4>;
pub type PremulRgba = Color<u8, 4, Srgb , Premultiplied>;
pub type Rgbaf32 = Color<f32, 4>;
pub type PremulRgbaf32 = Color<f32, 4, Srgb , Premultiplied>;
/// Return a*b/255, rounding any fractional bits.
pub fn premultiply_u8(c: u8, a: u8) -> u8 {
let prod = u32::from(c) * u32::from(a) + 128;
((prod + (prod >> 8)) >> 8) as u8
}
#[test]
fn test1() {
let c = Rgba::new([12, 32, 56, 65]);
let pc = c.premultiply();
println!("{:?}", pc);
}
1 post - 1 participant
🏷️ Rust_feed