use std::fmt::Display;
use image::{Rgb, Rgba};
pub trait RgbaInterface {
fn rgba(&self) -> Rgba<u8>;
}
pub trait PixelColorInterface {
fn r(&self) -> u8;
fn g(&self) -> u8;
fn b(&self) -> u8;
fn rgb(&self) -> Rgb<u8> {
Rgb([self.r(), self.g(), self.b()])
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
pub struct PixelColor {
r: u8,
g: u8,
b: u8,
}
impl RgbaInterface for PixelColor {
fn rgba(&self) -> Rgba<u8> {
Rgba([self.r(), self.g(), self.b(), u8::MAX])
}
}
impl RgbaInterface for &PixelColor {
fn rgba(&self) -> Rgba<u8> {
Rgba([self.r(), self.g(), self.b(), u8::MAX])
}
}
impl RgbaInterface for &mut PixelColor {
fn rgba(&self) -> Rgba<u8> {
Rgba([self.r(), self.g(), self.b(), u8::MAX])
}
}
impl RgbaInterface for Option<PixelColor> {
fn rgba(&self) -> Rgba<u8> {
match self {
Some(color) => color.rgba(),
None => Rgba([0, 0, 0, 0]),
}
}
}
impl RgbaInterface for &Option<PixelColor> {
fn rgba(&self) -> Rgba<u8> {
match self {
Some(color) => color.rgba(),
None => Rgba([0, 0, 0, 0]),
}
}
}
impl RgbaInterface for &mut Option<PixelColor> {
fn rgba(&self) -> Rgba<u8> {
match self {
Some(color) => color.rgba(),
None => Rgba([0, 0, 0, 0]),
}
}
}
impl Display for PixelColor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match (self.r, self.g, self.b) {
(0, 0, 0) => f.write_str("black"),
(255, 255, 255) => f.write_str("white"),
(255, 0, 0) => f.write_str("red"),
(0, 255, 0) => f.write_str("green"),
(0, 0, 255) => f.write_str("blue"),
(r, g, b) => write!(f, "({r}, {g}, {b})"),
}
}
}
impl PixelColorInterface for PixelColor {
fn r(&self) -> u8 {
self.r
}
fn g(&self) -> u8 {
self.g
}
fn b(&self) -> u8 {
self.b
}
}
impl Default for PixelColor {
fn default() -> Self {
Self {
r: u8::MAX,
g: u8::MAX,
b: u8::MAX,
}
}
}
impl PixelColor {
pub const fn new(r: u8, g: u8, b: u8) -> Self {
Self { r, g, b }
}
pub const fn splat(rgb: u8) -> Self {
Self {
r: rgb,
g: rgb,
b: rgb,
}
}
pub const fn from_red(r: u8) -> Self {
Self { r, g: 0, b: 0 }
}
pub const fn from_green(g: u8) -> Self {
Self { r: 0, g, b: 0 }
}
pub const fn from_blue(b: u8) -> Self {
Self { r: 0, g: 0, b }
}
pub const fn red(self, r: u8) -> Self {
Self { r, ..self }
}
pub const fn green(self, g: u8) -> Self {
Self { g, ..self }
}
pub const fn blue(self, b: u8) -> Self {
Self { b, ..self }
}
pub fn r(&self) -> u8 {
self.r
}
pub fn g(&self) -> u8 {
self.g
}
pub fn b(&self) -> u8 {
self.b
}
}
pub trait IntoPixelColor {
fn into_pixel_color(self) -> PixelColor;
}
impl<T> IntoPixelColor for T
where
T: Into<PixelColor>,
{
fn into_pixel_color(self) -> PixelColor {
self.into()
}
}
impl From<(u8, u8, u8)> for PixelColor {
fn from((r, g, b): (u8, u8, u8)) -> Self {
PixelColor { r, g, b }
}
}
impl From<[u8; 3]> for PixelColor {
fn from(rgb: [u8; 3]) -> Self {
PixelColor {
r: rgb[0],
g: rgb[1],
b: rgb[2],
}
}
}
impl From<u8> for PixelColor {
fn from(rgb: u8) -> Self {
PixelColor::splat(rgb)
}
}
pub trait PixelColorExt: PixelColorInterface {
const WHITE: PixelColor = PixelColor::splat(u8::MAX);
const BLACK: PixelColor = PixelColor::splat(u8::MIN);
const RED: PixelColor = PixelColor::from_red(u8::MAX);
const GREEN: PixelColor = PixelColor::from_green(u8::MAX);
const BLUE: PixelColor = PixelColor::from_blue(u8::MAX);
const YELLOW: PixelColor = PixelColor::from_red(u8::MAX).green(u8::MAX);
const CYAN: PixelColor = PixelColor::from_green(u8::MAX).blue(u8::MAX);
const MAGENTA: PixelColor = PixelColor::from_red(u8::MAX).blue(u8::MAX);
fn pixel_color(&self) -> PixelColor {
PixelColor {
r: self.r(),
g: self.g(),
b: self.b(),
}
}
}
impl<T> PixelColorExt for T where T: PixelColorInterface {}
impl Into<Rgba<u8>> for PixelColor {
fn into(self) -> Rgba<u8> {
self.rgba()
}
}
impl Into<Rgba<u8>> for &PixelColor {
fn into(self) -> Rgba<u8> {
self.rgba()
}
}
impl Into<Rgba<u8>> for &mut PixelColor {
fn into(self) -> Rgba<u8> {
self.rgba()
}
}
#[cfg(test)]
mod pixel_color_tests {
use super::*;
#[test]
fn default_color_should_be_white() {
assert_eq!(
PixelColor::default(),
PixelColor {
r: 255,
b: 255,
g: 255
}
);
assert_eq!(PixelColor::default(), PixelColor::WHITE);
}
}