use crate::error::{PixelflutError, PixelflutErrorKind, PixelflutResult};
use std::fmt;
use std::str::FromStr;
pub static MAX_FORMATTED_COORDINATE_SIZE: usize = 10;
pub static MAX_FORMATTED_COLOR_SIZE: usize = 8;
pub static MAX_FORMATTED_PIXEL_SIZE: usize = 3
+ MAX_FORMATTED_COORDINATE_SIZE
+ 1
+ MAX_FORMATTED_COORDINATE_SIZE
+ 1
+ MAX_FORMATTED_COLOR_SIZE;
pub static MAX_FORMATTED_PIXEL_SIZE_NEWLINE: usize = MAX_FORMATTED_PIXEL_SIZE + 1;
#[derive(Copy, Clone, PartialEq, Hash, Debug, Default)]
pub struct Pixel {
pub position: Coordinate,
pub color: Color,
}
impl Pixel {
pub fn new(position: Coordinate, color: Color) -> Pixel {
Pixel { position, color }
}
}
impl fmt::Display for Pixel {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {}", self.position, self.color)
}
}
impl FromStr for Pixel {
type Err = PixelflutError;
fn from_str(s: &str) -> PixelflutResult<Pixel> {
let mut iter = s.split_whitespace();
let pixel = Pixel::new(
Coordinate::new(
iter.next()
.ok_or(PixelflutErrorKind::WrongNumberOfArguments)?
.parse()?,
iter.next()
.ok_or(PixelflutErrorKind::WrongNumberOfArguments)?
.parse()?,
),
iter.next()
.ok_or(PixelflutErrorKind::WrongNumberOfArguments)?
.parse::<Color>()?,
);
if iter.next().is_some() {
Err(PixelflutErrorKind::WrongNumberOfArguments.into())
} else {
Ok(pixel)
}
}
}
impl<P: Into<Coordinate>, C: Into<Color>> From<(P, C)> for Pixel {
fn from((position, color): (P, C)) -> Self {
Pixel::new(position.into(), color.into())
}
}
#[derive(Copy, Clone, PartialEq, Hash, Debug, Default)]
pub struct Coordinate {
pub x: u32,
pub y: u32,
}
impl Coordinate {
pub fn new(x: u32, y: u32) -> Coordinate {
Coordinate { x, y }
}
}
impl From<(u32, u32)> for Coordinate {
fn from(coordinate: (u32, u32)) -> Coordinate {
Coordinate::new(coordinate.0, coordinate.1)
}
}
impl From<Coordinate> for (u32, u32) {
fn from(coordinate: Coordinate) -> (u32, u32) {
(coordinate.x, coordinate.y)
}
}
impl fmt::Display for Coordinate {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {}", self.x, self.y)
}
}
#[derive(Copy, Clone, PartialEq, Hash, Default)]
pub struct Color {
pub r: u8,
pub g: u8,
pub b: u8,
pub a: Option<u8>,
}
impl Color {
pub const fn rgb(r: u8, g: u8, b: u8) -> Color {
Color { r, g, b, a: None }
}
pub const fn rgba(r: u8, g: u8, b: u8, a: u8) -> Color {
Color {
r,
g,
b,
a: Some(a),
}
}
pub const fn alpha(self) -> u8 {
if let Some(alpha) = self.a {
alpha
} else {
255
}
}
pub const fn packed(r: u8, g: u8, b: u8, a: u8) -> Color {
match a {
255 => Color::rgb(r, g, b),
a => Color::rgba(r, g, b, a),
}
}
pub const fn pack(&self) -> Color {
match self.a {
None | Some(255) => Color::rgb(self.r, self.g, self.b),
_ => *self,
}
}
pub const fn normalized(self) -> (u8, u8, u8, u8) {
match self.a {
Some(a) => (self.r, self.g, self.b, a),
None => (self.r, self.g, self.b, 255),
}
}
}
impl From<(u8, u8, u8)> for Color {
fn from(color: (u8, u8, u8)) -> Color {
Color::rgb(color.0, color.1, color.2)
}
}
impl From<(u8, u8, u8, u8)> for Color {
fn from(color: (u8, u8, u8, u8)) -> Color {
Color::rgba(color.0, color.1, color.2, color.3)
}
}
impl From<Color> for (u8, u8, u8) {
fn from(color: Color) -> (u8, u8, u8) {
(color.r, color.g, color.b)
}
}
impl From<Color> for (u8, u8, u8, u8) {
fn from(color: Color) -> (u8, u8, u8, u8) {
(color.r, color.g, color.b, color.alpha())
}
}
#[cfg(feature = "image")]
impl From<image::Rgb<u8>> for Color {
fn from(color: image::Rgb<u8>) -> Color {
let [r, g, b] = color.0;
Color::rgb(r, g, b)
}
}
#[cfg(feature = "image")]
impl Into<image::Rgb<u8>> for Color {
fn into(self) -> image::Rgb<u8> {
image::Rgb([self.r, self.g, self.b])
}
}
#[cfg(feature = "image")]
impl From<image::Rgba<u8>> for Color {
fn from(color: image::Rgba<u8>) -> Color {
let [r, g, b, a] = color.0;
Color::packed(r, g, b, a)
}
}
#[cfg(feature = "image")]
impl Into<image::Rgba<u8>> for Color {
fn into(self) -> image::Rgba<u8> {
image::Rgba([self.r, self.g, self.b, self.a.unwrap_or(255)])
}
}
impl FromStr for Color {
type Err = PixelflutError;
fn from_str(s: &str) -> PixelflutResult<Color> {
match s.len() {
6 => Ok(Color::rgb(
u8::from_str_radix(&s[0..2], 16)?,
u8::from_str_radix(&s[2..4], 16)?,
u8::from_str_radix(&s[4..6], 16)?,
)),
8 => Ok(Color::rgba(
u8::from_str_radix(&s[0..2], 16)?,
u8::from_str_radix(&s[2..4], 16)?,
u8::from_str_radix(&s[4..6], 16)?,
u8::from_str_radix(&s[6..8], 16)?,
)),
_ => Err(PixelflutErrorKind::Parse.with_description("color length is wrong")),
}
}
}
impl fmt::Display for Color {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.a {
Some(a) => write!(f, "{:02x}{:02x}{:02x}{:02x}", self.r, self.g, self.b, a),
None => write!(f, "{:02x}{:02x}{:02x}", self.r, self.g, self.b),
}
}
}
impl fmt::Debug for Color {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Color: 0x{}", self)
}
}
#[cfg(test)]
mod tests {
use crate::{Color, Coordinate, Pixel};
#[test]
fn test_pixel_from_str() {
assert_eq!(
Pixel::new(Coordinate::new(10, 20), Color::rgb(0x11, 0x22, 0x33),),
"10 20 112233".parse().unwrap()
);
}
#[test]
fn test_color_rgb() {
assert_eq!(
Color {
r: 0x11,
g: 0x22,
b: 0x33,
a: None
},
Color::rgb(0x11, 0x22, 0x33)
);
}
#[test]
fn test_color_rgba() {
assert_eq!(
Color {
r: 0x11,
g: 0x22,
b: 0x33,
a: Some(0x44)
},
Color::rgba(0x11, 0x22, 0x33, 0x44)
);
}
#[test]
fn test_color_normalized() {
assert_eq!(
(0x11, 0x22, 0x33, 0xff),
Color::rgb(0x11, 0x22, 0x33).normalized()
);
assert_eq!(
(0x11, 0x22, 0x33, 0x44),
Color::rgba(0x11, 0x22, 0x33, 0x44).normalized()
);
}
}