diff --git a/src/color/color.rs b/src/color/color.rs new file mode 100644 index 0000000..07b9c13 --- /dev/null +++ b/src/color/color.rs @@ -0,0 +1,552 @@ +use core::cast::transmute; +use core::cmp::{Eq, Ord}; +use core::ptr::to_unsafe_ptr; +use core::sys::size_of; +use core::vec::raw::buf_as_slice; + +use angle::Degrees; +use channel::Channel; +use dim::Dimensional; +use funs::common::Sign; +use num::cast::{cast, NumCast}; + + +pub trait Color: Dimensional, Eq { + pure fn inverse(&self) -> self; + + pure fn to_rgb_u8(&self) -> RGB; + pure fn to_rgb_u16(&self) -> RGB; + pure fn to_rgb_u32(&self) -> RGB; + pure fn to_rgb_u64(&self) -> RGB; + pure fn to_rgb_f32(&self) -> RGB; + pure fn to_rgb_f64(&self) -> RGB; + + pure fn to_hsv_f32(&self) -> HSV; + pure fn to_hsv_f64(&self) -> HSV; +} + +// pub trait ColorRGB { +// static pure fn from_hex(hex: u8) -> self; +// } + +pub trait Color3: Color { + pure fn to_rgba_u8(&self, a: u8) -> RGBA; + pure fn to_rgba_u16(&self, a: u16) -> RGBA; + pure fn to_rgba_u32(&self, a: u32) -> RGBA; + pure fn to_rgba_u64(&self, a: u64) -> RGBA; + pure fn to_rgba_f32(&self, a: f32) -> RGBA; + pure fn to_rgba_f64(&self, a: f64) -> RGBA; + + pure fn to_hsva_f32(&self, a: f32) -> HSVA; + pure fn to_hsva_f64(&self, a: f64) -> HSVA; +} + +pub trait Color4: Color { + pure fn to_rgba_u8(&self) -> RGBA; + pure fn to_rgba_u16(&self) -> RGBA; + pure fn to_rgba_u32(&self) -> RGBA; + pure fn to_rgba_u64(&self) -> RGBA; + pure fn to_rgba_f32(&self) -> RGBA; + pure fn to_rgba_f64(&self) -> RGBA; + + pure fn to_hsva_f32(&self) -> HSVA; + pure fn to_hsva_f64(&self) -> HSVA; +} + + + + +/** + * A generic rgb to hsv conversion + * + * Assumes that T is a floating point type + * + * TODO: Use some sort of 'Float' trait bound to make this safer + */ +#[inline(always)] +pub pure fn to_hsv(color: &RGB) -> HSV { + // Algorithm taken from the Wikipedia article on HSL and HSV: + // http://en.wikipedia.org/wiki/HSL_and_HSV#From_HSV + + let _0 = cast(0); + + let mx = [color.r, color.g, color.b].max(); + let mn = [color.r, color.g, color.b].min(); + let chr = mx - mn; + + if chr != cast(0) { + let h = Degrees(if color.r == mx { ((color.g - color.b) / chr) % cast(6) } + else if color.g == mx { ((color.b - color.r) / chr) + cast(2) } + else /* color.b == mx */{ ((color.r - color.g) / chr) + cast(4) } * cast(60)); + + let s = chr / mx; + + HSV::new(h, s, mx) + + } else { + HSV::new(Degrees(_0), _0, mx) + } +} + +/** + * A generic hsv to rgb conversion + * + * Assumes that T is a floating point type + * + * TODO: Use some sort of 'Float' trait bound to make this safer + */ +#[inline(always)] +pub pure fn to_rgb(color: &HSV) -> RGB { + // Algorithm taken from the Wikipedia article on HSL and HSV: + // http://en.wikipedia.org/wiki/HSL_and_HSV#From_HSV + + let _0: T = cast(0); + let _1: T = cast(1); + let _2: T = cast(2); + + let chr = color.v * color.s; + let h_ = *(color.h) / cast(60); // TODO: it'd be nice if Degrees / Degrees returned a scalar + + // the 2nd largest component + let x = chr * (_1 - ((h_ % _2) - _1).abs()); + + + let mut color_rgb = + if h_ < cast(1) { RGB::new(chr, x, _0) } + else if h_ < cast(2) { RGB::new( x, chr, _0) } + else if h_ < cast(3) { RGB::new( _0, chr, x) } + else if h_ < cast(4) { RGB::new( _0, x, chr) } + else if h_ < cast(5) { RGB::new( x, _0, chr) } + else if h_ < cast(6) { RGB::new(chr, _0, x) } + else { RGB::new( _0, _0, _0) }; + + // match the value by adding the same amount to each component + let mn = color.v - chr; + + color_rgb.r += mn; + color_rgb.g += mn; + color_rgb.b += mn; + + return color_rgb; +} + + + + +pub struct RGB { r: T, g: T, b: T } + +pub impl RGB { + #[inline(always)] + static pure fn new(r: T, g: T, b: T) -> RGB { + RGB { r: move r, g: move g, b: move b } + } +} + +pub impl RGB: Dimensional { + #[inline(always)] + pure fn index(i: uint) -> T { + unsafe { do buf_as_slice( + transmute::<*RGB, *T>( + to_unsafe_ptr(&self)), 3) |slice| { slice[i] } + } + } + + #[inline(always)] + static pure fn dim() -> uint { 3 } + + #[inline(always)] + static pure fn size_of() -> uint { + size_of::>() + } + + #[inline(always)] + pure fn to_ptr(&self) -> *T { + ptr::to_unsafe_ptr(&self[0]) + } +} + +pub impl RGB: Color { + #[inline(always)] + pure fn inverse(&self) -> RGB { + RGB::new(self.r.inverse(), + self.g.inverse(), + self.b.inverse()) + } + + #[inline(always)] + pure fn to_rgb_u8(&self) -> RGB { + RGB::new(self.r.to_channel_u8(), + self.g.to_channel_u8(), + self.b.to_channel_u8()) + } + + #[inline(always)] + pure fn to_rgb_u16(&self) -> RGB { + RGB::new(self.r.to_channel_u16(), + self.g.to_channel_u16(), + self.b.to_channel_u16()) + } + + #[inline(always)] + pure fn to_rgb_u32(&self) -> RGB { + RGB::new(self.r.to_channel_u32(), + self.g.to_channel_u32(), + self.b.to_channel_u32()) + } + + #[inline(always)] + pure fn to_rgb_u64(&self) -> RGB { + RGB::new(self.r.to_channel_u64(), + self.g.to_channel_u64(), + self.b.to_channel_u64()) + } + + #[inline(always)] + pure fn to_rgb_f32(&self) -> RGB { + RGB::new(self.r.to_channel_f32(), + self.g.to_channel_f32(), + self.b.to_channel_f32()) + } + + #[inline(always)] + pure fn to_rgb_f64(&self) -> RGB { + RGB::new(self.r.to_channel_f64(), + self.g.to_channel_f64(), + self.b.to_channel_f64()) + } + + #[inline(always)] pure fn to_hsv_f32(&self) -> HSV { to_hsv(&self.to_rgb_f32()) } + #[inline(always)] pure fn to_hsv_f64(&self) -> HSV { to_hsv(&self.to_rgb_f64()) } +} + +pub impl RGB: Color3 { + #[inline(always)] pure fn to_rgba_u8(&self, a: u8) -> RGBA { RGBA::from_rgb_a(&self.to_rgb_u8(), a) } + #[inline(always)] pure fn to_rgba_u16(&self, a: u16) -> RGBA { RGBA::from_rgb_a(&self.to_rgb_u16(), a) } + #[inline(always)] pure fn to_rgba_u32(&self, a: u32) -> RGBA { RGBA::from_rgb_a(&self.to_rgb_u32(), a) } + #[inline(always)] pure fn to_rgba_u64(&self, a: u64) -> RGBA { RGBA::from_rgb_a(&self.to_rgb_u64(), a) } + #[inline(always)] pure fn to_rgba_f32(&self, a: f32) -> RGBA { RGBA::from_rgb_a(&self.to_rgb_f32(), a) } + #[inline(always)] pure fn to_rgba_f64(&self, a: f64) -> RGBA { RGBA::from_rgb_a(&self.to_rgb_f64(), a) } + + #[inline(always)] pure fn to_hsva_f32(&self, a: f32) -> HSVA { HSVA::from_hsv_a(&self.to_hsv_f32(), a) } + #[inline(always)] pure fn to_hsva_f64(&self, a: f64) -> HSVA { HSVA::from_hsv_a(&self.to_hsv_f64(), a) } +} + +pub impl RGB: Eq { + pure fn eq(&self, other: &RGB) -> bool { + self.r == other.r && + self.g == other.g && + self.b == other.b + } + + pure fn ne(&self, other: &RGB) -> bool { + !(self == other) + } +} + + + + + +pub struct RGBA { r: T, g: T, b: T, a: T } + +pub impl RGBA { + #[inline(always)] + static pure fn new(r: T, g: T, b: T, a: T) -> RGBA { + RGBA { r: move r, g: move g, b: move b, a: move a } + } + + #[inline(always)] + static pure fn from_rgb_a(rgb: &RGB, a: T) -> RGBA { + RGBA::new(rgb.r, rgb.g, rgb.b, move a) + } +} + +pub impl RGBA: Dimensional { + #[inline(always)] + pure fn index(i: uint) -> T { + unsafe { do buf_as_slice( + transmute::<*RGBA, *T>( + to_unsafe_ptr(&self)), 4) |slice| { slice[i] } + } + } + + #[inline(always)] + static pure fn dim() -> uint { 4 } + + #[inline(always)] + static pure fn size_of() -> uint { + size_of::>() + } + + #[inline(always)] + pure fn to_ptr(&self) -> *T { + ptr::to_unsafe_ptr(&self[0]) + } +} + +pub impl RGBA: Color { + #[inline(always)] + pure fn inverse(&self) -> RGBA { + RGBA::new(self.r.inverse(), + self.g.inverse(), + self.b.inverse(), + self.a.inverse()) + } + + #[inline(always)] + pure fn to_rgb_u8(&self) -> RGB { + RGB::new(self.r.to_channel_u8(), + self.g.to_channel_u8(), + self.b.to_channel_u8()) + } + + #[inline(always)] + pure fn to_rgb_u16(&self) -> RGB { + RGB::new(self.r.to_channel_u16(), + self.g.to_channel_u16(), + self.b.to_channel_u16()) + } + + #[inline(always)] + pure fn to_rgb_u32(&self) -> RGB { + RGB::new(self.r.to_channel_u32(), + self.g.to_channel_u32(), + self.b.to_channel_u32()) + } + + #[inline(always)] + pure fn to_rgb_u64(&self) -> RGB { + RGB::new(self.r.to_channel_u64(), + self.g.to_channel_u64(), + self.b.to_channel_u64()) + } + + #[inline(always)] + pure fn to_rgb_f32(&self) -> RGB { + RGB::new(self.r.to_channel_f32(), + self.g.to_channel_f32(), + self.b.to_channel_f32()) + } + + #[inline(always)] + pure fn to_rgb_f64(&self) -> RGB { + RGB::new(self.r.to_channel_f64(), + self.g.to_channel_f64(), + self.b.to_channel_f64()) + } + + #[inline(always)] pure fn to_hsv_f32(&self) -> HSV { to_hsv(&self.to_rgb_f32()) } + #[inline(always)] pure fn to_hsv_f64(&self) -> HSV { to_hsv(&self.to_rgb_f64()) } +} + +pub impl RGBA: Color4 { + #[inline(always)] pure fn to_rgba_u8(&self) -> RGBA { RGBA::from_rgb_a(&self.to_rgb_u8(), self.a.to_channel_u8()) } + #[inline(always)] pure fn to_rgba_u16(&self) -> RGBA { RGBA::from_rgb_a(&self.to_rgb_u16(), self.a.to_channel_u16()) } + #[inline(always)] pure fn to_rgba_u32(&self) -> RGBA { RGBA::from_rgb_a(&self.to_rgb_u32(), self.a.to_channel_u32()) } + #[inline(always)] pure fn to_rgba_u64(&self) -> RGBA { RGBA::from_rgb_a(&self.to_rgb_u64(), self.a.to_channel_u64()) } + #[inline(always)] pure fn to_rgba_f32(&self) -> RGBA { RGBA::from_rgb_a(&self.to_rgb_f32(), self.a.to_channel_f32()) } + #[inline(always)] pure fn to_rgba_f64(&self) -> RGBA { RGBA::from_rgb_a(&self.to_rgb_f64(), self.a.to_channel_f64()) } + + #[inline(always)] pure fn to_hsva_f32(&self) -> HSVA { HSVA::from_hsv_a(&self.to_hsv_f32(), self.a.to_channel_f32()) } + #[inline(always)] pure fn to_hsva_f64(&self) -> HSVA { HSVA::from_hsv_a(&self.to_hsv_f64(), self.a.to_channel_f64()) } +} + +pub impl RGBA: Eq { + pure fn eq(&self, other: &RGBA) -> bool { + self.r == other.r && + self.g == other.g && + self.b == other.b && + self.a == other.a + } + + pure fn ne(&self, other: &RGBA) -> bool { + !(self == other) + } +} + + + + + + +pub struct HSV { h: Degrees, s: T, v: T } + +pub impl HSV { + static pure fn new(h: Degrees, s: T, v: T) -> HSV { + HSV { h: move h, s: move s, v: move v } + } +} + +pub impl HSV: Dimensional { + #[inline(always)] + pure fn index(i: uint) -> T { + unsafe { do buf_as_slice( + transmute::<*HSV, *T>( + to_unsafe_ptr(&self)), 3) |slice| { slice[i] } + } + } + + #[inline(always)] + static pure fn dim() -> uint { 3 } + + #[inline(always)] + static pure fn size_of() -> uint { + size_of::>() + } + + #[inline(always)] + pure fn to_ptr(&self) -> *T { + ptr::to_unsafe_ptr(&self[0]) + } +} + +pub impl HSV: Color { + #[inline(always)] + pure fn inverse(&self) -> HSV { + HSV::new(self.h.opposite(), + self.s.inverse(), + self.v.inverse()) + } + + #[inline(always)] pure fn to_rgb_u8(&self) -> RGB { to_rgb(&self.to_hsv_f32()).to_rgb_u8() } + #[inline(always)] pure fn to_rgb_u16(&self) -> RGB { to_rgb(&self.to_hsv_f32()).to_rgb_u16() } + #[inline(always)] pure fn to_rgb_u32(&self) -> RGB { to_rgb(&self.to_hsv_f32()).to_rgb_u32() } + #[inline(always)] pure fn to_rgb_u64(&self) -> RGB { to_rgb(&self.to_hsv_f32()).to_rgb_u64() } + #[inline(always)] pure fn to_rgb_f32(&self) -> RGB { to_rgb(&self.to_hsv_f32()).to_rgb_f32() } + #[inline(always)] pure fn to_rgb_f64(&self) -> RGB { to_rgb(&self.to_hsv_f64()).to_rgb_f64() } + + #[inline(always)] + pure fn to_hsv_f32(&self) -> HSV { + HSV::new(Degrees((*self.h).to_f32()), + self.s.to_channel_f32(), + self.v.to_channel_f32()) + } + + #[inline(always)] + pure fn to_hsv_f64(&self) -> HSV { + HSV::new(Degrees((*self.h).to_f64()), + self.s.to_channel_f64(), + self.v.to_channel_f64()) + } +} + +pub impl HSV: Color3 { + #[inline(always)] pure fn to_rgba_u8(&self, a: u8) -> RGBA { RGBA::from_rgb_a(&self.to_rgb_u8(), a) } + #[inline(always)] pure fn to_rgba_u16(&self, a: u16) -> RGBA { RGBA::from_rgb_a(&self.to_rgb_u16(), a) } + #[inline(always)] pure fn to_rgba_u32(&self, a: u32) -> RGBA { RGBA::from_rgb_a(&self.to_rgb_u32(), a) } + #[inline(always)] pure fn to_rgba_u64(&self, a: u64) -> RGBA { RGBA::from_rgb_a(&self.to_rgb_u64(), a) } + #[inline(always)] pure fn to_rgba_f32(&self, a: f32) -> RGBA { RGBA::from_rgb_a(&self.to_rgb_f32(), a) } + #[inline(always)] pure fn to_rgba_f64(&self, a: f64) -> RGBA { RGBA::from_rgb_a(&self.to_rgb_f64(), a) } + + #[inline(always)] pure fn to_hsva_f32(&self, a: f32) -> HSVA { HSVA::from_hsv_a(&self.to_hsv_f32(), a) } + #[inline(always)] pure fn to_hsva_f64(&self, a: f64) -> HSVA { HSVA::from_hsv_a(&self.to_hsv_f64(), a) } +} + +pub impl HSV: Eq { + pure fn eq(&self, other: &HSV) -> bool { + self.h == other.h && + self.s == other.s && + self.v == other.v + } + + pure fn ne(&self, other: &HSV) -> bool { + !(self == other) + } +} + + + + + +pub struct HSVA { h: Degrees, s: T, v: T, a: T } + +pub impl HSVA { + #[inline(always)] + static pure fn new(h: Degrees, s: T, v: T, a: T) -> HSVA { + HSVA { h: move h, s: move s, v: move v, a: move a } + } + + #[inline(always)] + static pure fn from_hsv_a(hsv: &HSV, a: T) -> HSVA { + HSVA::new(hsv.h, hsv.s, hsv.v, move a) + } +} + +pub impl HSVA: Dimensional { + #[inline(always)] + pure fn index(i: uint) -> T { + unsafe { do buf_as_slice( + transmute::<*HSVA, *T>( + to_unsafe_ptr(&self)), 4) |slice| { slice[i] } + } + } + + #[inline(always)] + static pure fn dim() -> uint { 4 } + + #[inline(always)] + static pure fn size_of() -> uint { + size_of::>() + } + + #[inline(always)] + pure fn to_ptr(&self) -> *T { + ptr::to_unsafe_ptr(&self[0]) + } +} + +pub impl HSVA: Color { + #[inline(always)] + pure fn inverse(&self) -> HSVA { + HSVA::new(self.h.opposite(), + self.s.inverse(), + self.v.inverse(), + self.a.inverse()) + } + + #[inline(always)] pure fn to_rgb_u8(&self) -> RGB { to_rgb(&self.to_hsv_f32()).to_rgb_u8() } + #[inline(always)] pure fn to_rgb_u16(&self) -> RGB { to_rgb(&self.to_hsv_f32()).to_rgb_u16() } + #[inline(always)] pure fn to_rgb_u32(&self) -> RGB { to_rgb(&self.to_hsv_f32()).to_rgb_u32() } + #[inline(always)] pure fn to_rgb_u64(&self) -> RGB { to_rgb(&self.to_hsv_f32()).to_rgb_u64() } + #[inline(always)] pure fn to_rgb_f32(&self) -> RGB { to_rgb(&self.to_hsv_f32()).to_rgb_f32() } + #[inline(always)] pure fn to_rgb_f64(&self) -> RGB { to_rgb(&self.to_hsv_f64()).to_rgb_f64() } + + #[inline(always)] + pure fn to_hsv_f32(&self) -> HSV { + HSV::new(Degrees((*self.h).to_f32()), + self.s.to_channel_f32(), + self.v.to_channel_f32()) + } + + #[inline(always)] + pure fn to_hsv_f64(&self) -> HSV { + HSV::new(Degrees((*self.h).to_f64()), + self.s.to_channel_f64(), + self.v.to_channel_f64()) + } +} + +pub impl HSVA: Color4 { + #[inline(always)] pure fn to_rgba_u8(&self) -> RGBA { RGBA::from_rgb_a(&self.to_rgb_u8(), self.a.to_channel_u8()) } + #[inline(always)] pure fn to_rgba_u16(&self) -> RGBA { RGBA::from_rgb_a(&self.to_rgb_u16(), self.a.to_channel_u16()) } + #[inline(always)] pure fn to_rgba_u32(&self) -> RGBA { RGBA::from_rgb_a(&self.to_rgb_u32(), self.a.to_channel_u32()) } + #[inline(always)] pure fn to_rgba_u64(&self) -> RGBA { RGBA::from_rgb_a(&self.to_rgb_u64(), self.a.to_channel_u64()) } + #[inline(always)] pure fn to_rgba_f32(&self) -> RGBA { RGBA::from_rgb_a(&self.to_rgb_f32(), self.a.to_channel_f32()) } + #[inline(always)] pure fn to_rgba_f64(&self) -> RGBA { RGBA::from_rgb_a(&self.to_rgb_f64(), self.a.to_channel_f64()) } + + #[inline(always)] pure fn to_hsva_f32(&self) -> HSVA { HSVA::from_hsv_a(&self.to_hsv_f32(), self.a.to_channel_f32()) } + #[inline(always)] pure fn to_hsva_f64(&self) -> HSVA { HSVA::from_hsv_a(&self.to_hsv_f64(), self.a.to_channel_f64()) } +} + +pub impl HSVA: Eq { + pure fn eq(&self, other: &HSVA) -> bool { + self.h == other.h && + self.s == other.s && + self.v == other.v && + self.a == other.a + } + + pure fn ne(&self, other: &HSVA) -> bool { + !(self == other) + } +} \ No newline at end of file diff --git a/src/color/test/test_color.rs b/src/color/test/test_color.rs new file mode 100644 index 0000000..212cbd0 --- /dev/null +++ b/src/color/test/test_color.rs @@ -0,0 +1,29 @@ +use angle::*; +use color::*; + +#[test] +fn test_color_rgb() { + // TODO + assert RGB::new::(0xA0, 0xA0, 0xA0).to_rgb_u8() == RGB::new(0xA0, 0xA0, 0xA0); + assert RGB::new::(0xA0, 0xA0, 0xA0).to_rgb_u16() == RGB::new(0xA0A0, 0xA0A0, 0xA0A0); + assert RGB::new::(0xA0, 0xA0, 0xA0).to_rgb_u32() == RGB::new(0xA0A0_A0A0, 0xA0A0_A0A0, 0xA0A0_A0A0); + assert RGB::new::(0xA0, 0xA0, 0xA0).to_rgb_u64() == RGB::new(0xA0A0_A0A0_A0A0_A0A0, 0xA0A0_A0A0_A0A0_A0A0, 0xA0A0_A0A0_A0A0_A0A0); + + // assert RGB::new::(0xFF, 0xFF, 0xFF).to_hsv_f32() == HSV::new(Degrees(0.0), 0.0, 1.0); // FIXME: causes an ICE + // RGB::new::(0xFF, 0xFF, 0xFF).to_hsv_f32(); // FIXME: causes an ICE as well :( +} + +#[test] +fn test_color_rgba() { + // TODO +} + +#[test] +fn test_color_hsv() { + // TODO +} + +#[test] +fn test_color_hsva() { + // TODO +} \ No newline at end of file diff --git a/src/lmath.rc b/src/lmath.rc index 03bc648..e6c496f 100644 --- a/src/lmath.rc +++ b/src/lmath.rc @@ -28,10 +28,12 @@ mod test { pub mod color { pub mod channel; + pub mod color; #[test] mod test { mod test_channel; + mod test_color; } }