use std::fmt;
use std::str::FromStr;
use error::{Error, ErrorKind, Result};
#[derive(Copy, Clone, PartialEq, Hash, Debug)]
pub struct Pixel {
pub position: Coordinate,
pub color: Color,
}
impl Pixel {
pub fn new<P: Into<Coordinate>, C: Into<Color>>(position: P, color: C) -> Pixel {
Pixel {
position: position.into(),
color: color.into(),
}
}
}
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 = Error;
fn from_str(s: &str) -> Result<Pixel> {
let mut iter = s.split_whitespace();
let pixel = Pixel::new(
Coordinate::new(
iter.next().ok_or(ErrorKind::WrongNumberOfArguments)?.parse()?,
iter.next().ok_or(ErrorKind::WrongNumberOfArguments)?.parse()?
),
iter.next().ok_or(ErrorKind::WrongNumberOfArguments)?.parse::<Color>()?
);
if iter.next().is_some() {
Err(ErrorKind::WrongNumberOfArguments.into())
} else {
Ok(pixel)
}
}
}
#[derive(Copy, Clone, PartialEq, Hash, Debug)]
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 Into<(u32, u32)> for Coordinate {
fn into(self) -> (u32, u32) {
(self.x, self.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)]
pub struct Color {
r: u8,
g: u8,
b: u8,
a: Option<u8>,
}
impl Color {
pub fn rgb(r: u8, g: u8, b: u8) -> Color {
Color {
r, g, b, a: None
}
}
pub fn rgba(r: u8, g: u8, b: u8, a: u8) -> Color {
Color {
r, g, b, a: Some(a)
}
}
pub 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 fn pack(&self) -> Color {
match self.a {
None | Some(255) => Color::rgb(self.r, self.g, self.b),
_ => *self
}
}
pub 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 Into<(u8, u8, u8)> for Color {
fn into(self) -> (u8, u8, u8) {
(self.r, self.g, self.b)
}
}
impl Into<(u8, u8, u8, u8)> for Color {
fn into(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),
}
}
}
#[cfg(feature = "image")]
impl From<image::Rgb<u8>> for Color {
fn from(color: image::Rgb<u8>) -> Color {
let [r, g, b] = color.data;
Color::rgb(r, g, b)
}
}
#[cfg(feature = "image")]
impl Into<image::Rgb<u8>> for Color {
fn into(self) -> image::Rgb<u8> {
image::Rgb { data: [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.data;
Color::packed(r, g, b, a)
}
}
#[cfg(feature = "image")]
impl Into<image::Rgba<u8>> for Color {
fn into(self) -> image::Rgba<u8> {
image::Rgba { data: [self.r, self.g, self.b, self.a.unwrap_or(255)] }
}
}
impl FromStr for Color {
type Err = Error;
fn from_str(s: &str) -> Result<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(ErrorKind::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 Pixel;
use Coordinate;
use Color;
#[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());
}
}