ponsic-color 0.1.0

The dependency of the `ponsic` crate.
Documentation
mod const_color;

#[repr(C, align(4))]
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Color {
    _ph: u8,
    pub blue: u8,
    pub green: u8,
    pub red: u8,
}

pub struct ColorHSV {
    pub hue: f32,
    pub saturation: f32,
    pub value: f32,
}

pub struct ColorHSL {
    pub hue: f32,
    pub saturation: f32,
    pub lightness: f32,
}

impl Color {
    pub const fn new(red: u8, green: u8, blue: u8) -> Color {
        Color {
            _ph: 0,
            blue,
            green,
            red,
        }
    }
}

impl Color {
    pub const fn into_hsv(self) -> ColorHSV {
        let r = self.red as f32 / 255.0;
        let g = self.green as f32 / 255.0;
        let b = self.blue as f32 / 255.0;

        let max = r.max(g).max(b);
        let min = r.min(g).min(b);
        let delta = max - min;

        let hue = if delta == 0.0 {
            0.0
        } else if max == r {
            (g - b) / delta
        } else if max == g {
            (b - r) / delta + 2.0
        } else {
            (r - g) / delta + 4.0
        };
        let saturation = if max == 0.0 { 0.0 } else { delta / max };
        let value = max;

        let mut hue = hue * 60.0;
        if hue < 0.0 {
            hue += 360.0;
        }

        ColorHSV {
            hue,
            saturation,
            value,
        }
    }

    pub const fn into_hsl(self) -> ColorHSL {
        let r = self.red as f32 / 255.0;
        let g = self.green as f32 / 255.0;
        let b = self.blue as f32 / 255.0;

        let max = r.max(g).max(b);
        let min = r.min(g).min(b);
        let delta = max - min;

        let lightness = (max + min) / 2.0;
        let saturation = if lightness == 0.0 || lightness == 1.0 {
            0.0
        } else {
            delta / (1.0 - (2.0 * lightness - 1.0).abs())
        };

        let hue = if delta == 0.0 {
            0.0
        } else if max == r {
            (g - b) / delta
        } else if max == g {
            (b - r) / delta + 2.0
        } else {
            (r - g) / delta + 4.0
        };

        let mut hue = hue * 60.0;
        if hue < 0.0 {
            hue += 360.0;
        }

        ColorHSL {
            hue,
            saturation,
            lightness,
        }
    }

    pub const fn from_hsv(hsv: ColorHSV) -> Color {
        let chroma = hsv.value * hsv.saturation;
        let hue_prime = hsv.hue / 60.0;
        let x = chroma * (1.0 - (hue_prime % 2.0 - 1.0).abs());

        let (r, g, b) = if hue_prime < 1.0 {
            (chroma, x, 0.0)
        } else if hue_prime < 2.0 {
            (x, chroma, 0.0)
        } else if hue_prime < 3.0 {
            (0.0, chroma, x)
        } else if hue_prime < 4.0 {
            (0.0, x, chroma)
        } else if hue_prime < 5.0 {
            (x, 0.0, chroma)
        } else {
            (chroma, 0.0, x)
        };

        let m = hsv.value - chroma;

        Color::new(
            ((r + m) * 255.0) as u8,
            ((g + m) * 255.0) as u8,
            ((b + m) * 255.0) as u8,
        )
    }

    pub const fn from_hsl(hsl: ColorHSL) -> Color {
        let chroma = (1.0 - (2.0 * hsl.lightness - 1.0).abs()) * hsl.saturation;
        let hue_prime = hsl.hue / 60.0;
        let x = chroma * (1.0 - (hue_prime % 2.0 - 1.0).abs());

        let (r, g, b) = if hue_prime < 1.0 {
            (chroma, x, 0.0)
        } else if hue_prime < 2.0 {
            (x, chroma, 0.0)
        } else if hue_prime < 3.0 {
            (0.0, chroma, x)
        } else if hue_prime < 4.0 {
            (0.0, x, chroma)
        } else if hue_prime < 5.0 {
            (x, 0.0, chroma)
        } else {
            (chroma, 0.0, x)
        };

        let m = hsl.lightness - chroma / 2.0;

        Color::new(
            ((r + m) * 255.0) as u8,
            ((g + m) * 255.0) as u8,
            ((b + m) * 255.0) as u8,
        )
    }
}

impl Into<u32> for Color {
    fn into(self) -> u32 {
        unsafe { std::mem::transmute(self) }
    }
}

impl From<u32> for Color {
    fn from(color: u32) -> Self {
        unsafe { std::mem::transmute(color & 0x00ffffff) }
    }
}

impl Into<ColorHSV> for Color {
    fn into(self) -> ColorHSV {
        self.into_hsv()
    }
}

impl From<ColorHSV> for Color {
    fn from(hsv: ColorHSV) -> Self {
        Self::from_hsv(hsv)
    }
}

impl Into<ColorHSL> for Color {
    fn into(self) -> ColorHSL {
        self.into_hsl()
    }
}

impl From<ColorHSL> for Color {
    fn from(hsl: ColorHSL) -> Self {
        Self::from_hsl(hsl)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_color_hsv() {
        let color = Color::new(255, 0, 0);
        let hsv = color.into_hsv();
        assert_eq!(hsv.hue, 0.0);
        assert_eq!(hsv.saturation, 1.0);
        assert_eq!(hsv.value, 1.0);

        let color = Color::new(0, 255, 0);
        let hsv = color.into_hsv();
        assert_eq!(hsv.hue, 120.0);
        assert_eq!(hsv.saturation, 1.0);
        assert_eq!(hsv.value, 1.0);

        let color = Color::new(0, 0, 255);
        let hsv = color.into_hsv();
        assert_eq!(hsv.hue, 240.0);
        assert_eq!(hsv.saturation, 1.0);
        assert_eq!(hsv.value, 1.0);

        let color = Color::new(255, 255, 0);
        let hsv = color.into_hsv();
        assert_eq!(hsv.hue, 60.0);
        assert_eq!(hsv.saturation, 1.0);
        assert_eq!(hsv.value, 1.0);

        let color = Color::new(0, 255, 255);
        let hsv = color.into_hsv();
        assert_eq!(hsv.hue, 180.0);
        assert_eq!(hsv.saturation, 1.0);
        assert_eq!(hsv.value, 1.0);

        let color = Color::new(255, 0, 255);
        let hsv = color.into_hsv();
        assert_eq!(hsv.hue, 300.0);
        assert_eq!(hsv.saturation, 1.0);
        assert_eq!(hsv.value, 1.0);
    }

    #[test]
    fn test_color_hsl() {
        let color = Color::new(255, 0, 0);
        let hsl = color.into_hsl();
        assert_eq!(hsl.hue, 0.0);
        assert_eq!(hsl.saturation, 1.0);
        assert_eq!(hsl.lightness, 0.5);

        let color = Color::new(0, 255, 0);
        let hsl = color.into_hsl();
        assert_eq!(hsl.hue, 120.0);
        assert_eq!(hsl.saturation, 1.0);
        assert_eq!(hsl.lightness, 0.5);

        let color = Color::new(0, 0, 255);
        let hsl = color.into_hsl();
        assert_eq!(hsl.hue, 240.0);
        assert_eq!(hsl.saturation, 1.0);
        assert_eq!(hsl.lightness, 0.5);

        let color = Color::new(255, 255, 0);
        let hsl = color.into_hsl();
        assert_eq!(hsl.hue, 60.0);
        assert_eq!(hsl.saturation, 1.0);
        assert_eq!(hsl.lightness, 0.5);
        let color = Color::new(0, 255, 255);
        let hsl = color.into_hsl();
        assert_eq!(hsl.hue, 180.0);
        assert_eq!(hsl.saturation, 1.0);
        assert_eq!(hsl.lightness, 0.5);

        let color = Color::new(255, 0, 255);
        let hsl = color.into_hsl();
        assert_eq!(hsl.hue, 300.0);
        assert_eq!(hsl.saturation, 1.0);
        assert_eq!(hsl.lightness, 0.5);
    }

    #[test]
    fn test_color_from_hsv() {
        let color = Color::from_hsv(ColorHSV {
            hue: 0.0,
            saturation: 1.0,
            value: 1.0,
        });
        assert_eq!(color, Color::new(255, 0, 0));

        let color = Color::from_hsv(ColorHSV {
            hue: 120.0,
            saturation: 1.0,
            value: 1.0,
        });
        assert_eq!(color, Color::new(0, 255, 0));

        let color = Color::from_hsv(ColorHSV {
            hue: 240.0,
            saturation: 1.0,
            value: 1.0,
        });
        assert_eq!(color, Color::new(0, 0, 255));

        let color = Color::from_hsv(ColorHSV {
            hue: 60.0,
            saturation: 1.0,
            value: 1.0,
        });
        assert_eq!(color, Color::new(255, 255, 0));

        let color = Color::from_hsv(ColorHSV {
            hue: 180.0,
            saturation: 1.0,
            value: 1.0,
        });
        assert_eq!(color, Color::new(0, 255, 255));

        let color = Color::from_hsv(ColorHSV {
            hue: 300.0,
            saturation: 1.0,
            value: 1.0,
        });
        assert_eq!(color, Color::new(255, 0, 255));
    }

    #[test]
    fn test_color_from_hsl() {
        let color = Color::from_hsl(ColorHSL {
            hue: 0.0,
            saturation: 1.0,
            lightness: 0.5,
        });
        assert_eq!(color, Color::new(255, 0, 0));

        let color = Color::from_hsl(ColorHSL {
            hue: 120.0,
            saturation: 1.0,
            lightness: 0.5,
        });
        assert_eq!(color, Color::new(0, 255, 0));

        let color = Color::from_hsl(ColorHSL {
            hue: 240.0,
            saturation: 1.0,
            lightness: 0.5,
        });
        assert_eq!(color, Color::new(0, 0, 255));

        let color = Color::from_hsl(ColorHSL {
            hue: 60.0,
            saturation: 1.0,
            lightness: 0.5,
        });
        assert_eq!(color, Color::new(255, 255, 0));

        let color = Color::from_hsl(ColorHSL {
            hue: 180.0,
            saturation: 1.0,
            lightness: 0.5,
        });
        assert_eq!(color, Color::new(0, 255, 255));

        let color = Color::from_hsl(ColorHSL {
            hue: 300.0,
            saturation: 1.0,
            lightness: 0.5,
        });
        assert_eq!(color, Color::new(255, 0, 255));
    }
}