Impl Color trait

This commit is contained in:
Brendan Zabarauskas 2013-07-10 12:59:02 +10:00
parent a3f997e763
commit 58c3233fe3
4 changed files with 169 additions and 31 deletions

View file

@ -13,13 +13,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
pub trait Channel: Num {
pub trait Channel: Primitive + Orderable {
priv fn from<T:Channel>(chan: T) -> Self;
pub fn to_channel<T:Channel>(&self) -> T;
pub fn to_channel_u8(&self) -> u8;
pub fn to_channel_u16(&self) -> u16;
pub fn to_channel_f32(&self) -> f32;
pub fn to_channel_f64(&self) -> f64;
pub fn invert_channel(&self) -> Self;
}
impl Channel for u8 {
@ -29,6 +31,8 @@ impl Channel for u8 {
#[inline] pub fn to_channel_u16(&self) -> u16 { (*self as u16 << 8) | (*self) as u16 }
#[inline] pub fn to_channel_f32(&self) -> f32 { (*self as f32) / (0xFF as f32) }
#[inline] pub fn to_channel_f64(&self) -> f64 { (*self as f64) / (0xFF as f64) }
#[inline] pub fn invert_channel(&self) -> u8 { !(*self) }
}
impl Channel for u16 {
@ -38,24 +42,42 @@ impl Channel for u16 {
#[inline] pub fn to_channel_u16(&self) -> u16 { (*self) }
#[inline] pub fn to_channel_f32(&self) -> f32 { (*self) / 0xFFFF as f32 }
#[inline] pub fn to_channel_f64(&self) -> f64 { (*self) / 0xFFFF as f64 }
#[inline] pub fn invert_channel(&self) -> u16 { !(*self) }
}
impl Channel for f32 {
#[inline] priv fn from<T:Channel>(chan: T) -> f32 { chan.to_channel_f32() }
#[inlune] pub fn to_channel<T:Channel>(&self) -> T { Channel::from(*self) }
#[inline] pub fn to_channel_u8(&self) -> u8 { (*self) * (0xFF_u8 as f32) as u8 }
#[inline] pub fn to_channel_u16(&self) -> u16 { (*self) * (0xFFFF_u16 as f32) as u16 }
#[inline] pub fn to_channel_u8(&self) -> u8 { (*self) * (0xFF as f32) as u8 }
#[inline] pub fn to_channel_u16(&self) -> u16 { (*self) * (0xFFFF as f32) as u16 }
#[inline] pub fn to_channel_f32(&self) -> f32 { (*self) }
#[inline] pub fn to_channel_f64(&self) -> f64 { (*self) as f64 }
#[inline] pub fn invert_channel(&self) -> f32 { 1.0 - (*self) }
}
impl Channel for f64 {
#[inline] priv fn from<T:Channel>(chan: T) -> f64 { chan.to_channel_f64() }
#[inlune] pub fn to_channel<T:Channel>(&self) -> T { Channel::from(*self) }
#[inline] pub fn to_channel_u8(&self) -> u8 { (*self) * (0xFF_u8 as f64) as u8 }
#[inline] pub fn to_channel_u16(&self) -> u16 { (*self) * (0xFFFF_u16 as f64) as u16 }
#[inline] pub fn to_channel_u8(&self) -> u8 { (*self) * (0xFF as f64) as u8 }
#[inline] pub fn to_channel_u16(&self) -> u16 { (*self) * (0xFFFF as f64) as u16 }
#[inline] pub fn to_channel_f32(&self) -> f32 { (*self) as f32 }
#[inline] pub fn to_channel_f64(&self) -> f64 { (*self) }
#[inline] pub fn invert_channel(&self) -> f64 { 1.0 - (*self) }
}
pub trait FloatChannel: Float + Channel {
pub fn invert_degrees(&self) -> Self;
}
impl FloatChannel for f32 {
#[inline] pub fn invert_degrees(&self) -> f32 { ((*self) + 180.0) % 360.0 }
}
impl FloatChannel for f64 {
#[inline] pub fn invert_degrees(&self) -> f64 { ((*self) + 180.0) % 360.0 }
}
#[cfg(test)]
@ -63,7 +85,7 @@ mod tests {
use color::Channel;
#[test]
fn test_channel_u8() {
fn test_to_channel_u8() {
assert_eq!(0x00_u8.to_channel_u8(), 0x00_u8);
assert_eq!(0x30_u8.to_channel_u8(), 0x30_u8);
assert_eq!(0x66_u8.to_channel_u8(), 0x66_u8);
@ -84,7 +106,14 @@ mod tests {
}
#[test]
fn test_channel_u16() {
fn test_invert_channel_u8() {
assert_eq!(0x00_u8.invert_channel(), 0xFF_u8);
assert_eq!(0x66_u8.invert_channel(), 0x99_u8);
assert_eq!(0xFF_u8.invert_channel(), 0x00_u8);
}
#[test]
fn test_to_channel_u16() {
assert_eq!(0x0000_u16.to_channel_u8(), 0x00_u8);
assert_eq!(0x3300_u16.to_channel_u8(), 0x33_u8);
assert_eq!(0x6666_u16.to_channel_u8(), 0x66_u8);
@ -105,7 +134,14 @@ mod tests {
}
#[test]
fn test_channel_f32() {
fn test_invert_channel_u16() {
assert_eq!(0x0000_u16.invert_channel(), 0xFFFF_u16);
assert_eq!(0x6666_u16.invert_channel(), 0x9999_u16);
assert_eq!(0xFFFF_u16.invert_channel(), 0x0000_u16);
}
#[test]
fn test_to_channel_f32() {
assert_eq!(0.00f32.to_channel_u8(), 0x00);
assert_eq!(0.25f32.to_channel_u8(), 0x3F);
assert_eq!(0.50f32.to_channel_u8(), 0x7F);
@ -126,7 +162,23 @@ mod tests {
}
#[test]
fn test_channel_f64() {
fn test_invert_channel_f32() {
assert_eq!(0.00f32.invert_channel(), 1.00f32);
assert_eq!(0.50f32.invert_channel(), 0.50f32);
assert_eq!(1.00f32.invert_channel(), 0.00f32);
}
#[test]
fn test_invert_degrees_f32() {
assert_eq!( 0.00f32.invert_degrees(), 180.00f32);
assert_eq!( 45.00f32.invert_degrees(), 225.00f32);
assert_eq!( 90.00f32.invert_degrees(), 270.00f32);
assert_eq!(360.00f32.invert_degrees(), 180.00f32);
assert_eq!(720.00f32.invert_degrees(), 180.00f32);
}
#[test]
fn test_to_channel_f64() {
assert_eq!(0.00f64.to_channel_u8(), 0x00);
assert_eq!(0.25f64.to_channel_u8(), 0x3F);
assert_eq!(0.50f64.to_channel_u8(), 0x7F);
@ -145,4 +197,20 @@ mod tests {
assert_eq!(0.00f64.to_channel_f64(), 0.00f64);
assert_eq!(1.00f64.to_channel_f64(), 1.00f64);
}
#[test]
fn test_invert_channel_f64() {
assert_eq!(0.00f64.invert_channel(), 1.00f64);
assert_eq!(0.50f64.invert_channel(), 0.50f64);
assert_eq!(1.00f64.invert_channel(), 0.00f64);
}
#[test]
fn test_invert_degrees_f64() {
assert_eq!( 0.00f64.invert_degrees(), 180.00f64);
assert_eq!( 45.00f64.invert_degrees(), 225.00f64);
assert_eq!( 90.00f64.invert_degrees(), 270.00f64);
assert_eq!(360.00f64.invert_degrees(), 180.00f64);
assert_eq!(720.00f64.invert_degrees(), 180.00f64);
}
}

View file

@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
pub use self::channel::Channel;
pub use self::channel::{Channel, FloatChannel};
pub use self::hsv::{HSV, ToHSV, HSVA, ToHSVA};
pub use self::rgb::{RGB, ToRGB, RGBA, ToRGBA};
pub use self::srgb::{SRGB, SRGBA};
@ -24,6 +24,6 @@ pub mod rgb;
pub mod srgb;
pub trait Color<T> {
pub fn clamp(&self, lo: T, hi: T) -> Self;
pub fn inverse(&self) -> Self;
pub fn invert_self(&mut self);
}

View file

@ -16,7 +16,8 @@
use std::num;
use std::cast;
use color::Channel;
use color::Color;
use color::{Channel, FloatChannel};
use color::{RGB, ToRGB, RGBA, ToRGBA};
#[path = "../num_macros.rs"]
@ -25,40 +26,56 @@ mod num_macros;
#[deriving(Clone, Eq)]
pub struct HSV<T> { h: T, s: T, v: T }
impl<T:Channel + Float> HSV<T> {
impl<T:FloatChannel> HSV<T> {
pub fn new(h: T, s: T, v: T) -> HSV<T> {
HSV { h: h, s: s, v: v }
}
}
impl<T:FloatChannel> Color<T> for HSV<T> {
#[inline]
pub fn clamp(&self, lo: T, hi: T) -> HSV<T> {
HSV::new((*self).h.clamp(&lo, &hi),
(*self).s.clamp(&lo, &hi),
(*self).v.clamp(&lo, &hi))
}
#[inline]
pub fn inverse(&self) -> HSV<T> {
HSV::new((*self).h.invert_degrees(),
(*self).s.invert_channel(),
(*self).v.invert_channel())
}
}
pub trait ToHSV {
pub fn to_hsv<U:Channel + Float>(&self) -> HSV<U>;
pub fn to_hsv<U:FloatChannel>(&self) -> HSV<U>;
}
impl ToHSV for u32 {
#[inline]
pub fn to_hsv<U:Channel + Float>(&self) -> HSV<U> {
pub fn to_hsv<U:FloatChannel>(&self) -> HSV<U> {
fail!("Not yet implemented")
}
}
impl ToHSV for u64 {
#[inline]
pub fn to_hsv<U:Channel + Float>(&self) -> HSV<U> {
pub fn to_hsv<U:FloatChannel>(&self) -> HSV<U> {
fail!("Not yet implemented")
}
}
impl<T:Clone + Channel + Float> ToHSV for HSV<T> {
impl<T:Clone + FloatChannel> ToHSV for HSV<T> {
#[inline]
pub fn to_hsv<U:Channel + Float>(&self) -> HSV<U> {
pub fn to_hsv<U:FloatChannel>(&self) -> HSV<U> {
HSV::new((*self).h.to_channel(),
(*self).s.to_channel(),
(*self).v.to_channel())
}
}
impl<T:Clone + Channel + Float> ToRGB for HSV<T> {
impl<T:Clone + FloatChannel> ToRGB for HSV<T> {
pub fn to_rgb<U:Channel>(&self) -> RGB<U> {
// Algorithm taken from the Wikipedia article on HSL and HSV:
// http://en.wikipedia.org/wiki/HSL_and_HSV#From_HSV
@ -93,7 +110,7 @@ impl<T:Clone + Channel + Float> ToRGB for HSV<T> {
#[deriving(Clone, Eq)]
pub struct HSVA<T> { h: T, s: T, v: T, a: T }
impl<T:Channel + Float> HSVA<T> {
impl<T:FloatChannel> HSVA<T> {
#[inline]
pub fn new(h: T, s: T, v: T, a: T) -> HSVA<T> {
HSVA { h: h, s: s, v: v, a: a }
@ -115,27 +132,45 @@ impl<T:Channel + Float> HSVA<T> {
}
}
impl<T:FloatChannel> Color<T> for HSVA<T> {
#[inline]
pub fn clamp(&self, lo: T, hi: T) -> HSVA<T> {
HSVA::new((*self).h.clamp(&lo, &hi),
(*self).s.clamp(&lo, &hi),
(*self).v.clamp(&lo, &hi),
(*self).a.clamp(&lo, &hi))
}
#[inline]
pub fn inverse(&self) -> HSVA<T> {
HSVA::new((*self).h.invert_degrees(),
(*self).s.invert_channel(),
(*self).v.invert_channel(),
(*self).a.invert_channel())
}
}
pub trait ToHSVA {
pub fn to_hsva<U:Channel + Float>(&self) -> HSVA<U>;
pub fn to_hsva<U:FloatChannel>(&self) -> HSVA<U>;
}
impl ToHSVA for u32 {
#[inline]
pub fn to_hsva<U:Channel + Float>(&self) -> HSVA<U> {
pub fn to_hsva<U:FloatChannel>(&self) -> HSVA<U> {
fail!("Not yet implemented")
}
}
impl ToHSVA for u64 {
#[inline]
pub fn to_hsva<U:Channel + Float>(&self) -> HSVA<U> {
pub fn to_hsva<U:FloatChannel>(&self) -> HSVA<U> {
fail!("Not yet implemented")
}
}
impl<C: ToHSV, T:Clone + Channel + Float> ToHSVA for (C, T) {
impl<C: ToHSV, T:Clone + FloatChannel> ToHSVA for (C, T) {
#[inline]
pub fn to_hsva<U:Channel + Float>(&self) -> HSVA<U> {
pub fn to_hsva<U:FloatChannel>(&self) -> HSVA<U> {
match *self {
(ref hsv, ref a) => {
HSVA::from_hsv_a(hsv.to_hsv(), a.to_channel())
@ -144,9 +179,9 @@ impl<C: ToHSV, T:Clone + Channel + Float> ToHSVA for (C, T) {
}
}
impl<T:Clone + Channel + Float> ToHSVA for HSVA<T> {
impl<T:Clone + FloatChannel> ToHSVA for HSVA<T> {
#[inline]
pub fn to_hsva<U:Channel + Float>(&self) -> HSVA<U> {
pub fn to_hsva<U:FloatChannel>(&self) -> HSVA<U> {
HSVA::new((*self).h.to_channel(),
(*self).s.to_channel(),
(*self).v.to_channel(),
@ -154,7 +189,7 @@ impl<T:Clone + Channel + Float> ToHSVA for HSVA<T> {
}
}
impl<T:Clone + Channel + Float> ToRGBA for HSVA<T> {
impl<T:Clone + FloatChannel> ToRGBA for HSVA<T> {
#[inline]
pub fn to_rgba<U:Channel>(&self) -> RGBA<U> {
RGBA::from_rgb_a(self.hsv().to_rgb(), (*self).a.to_channel())

View file

@ -16,7 +16,8 @@
use std::num;
use std::cast;
use color::Channel;
use color::Color;
use color::{Channel, FloatChannel};
use color::{HSV, ToHSV, HSVA, ToHSVA};
#[path = "../num_macros.rs"]
@ -32,6 +33,22 @@ impl<T:Channel> RGB<T> {
}
}
impl<T:Channel> Color<T> for RGB<T> {
#[inline]
pub fn clamp(&self, lo: T, hi: T) -> RGB<T> {
RGB::new((*self).r.clamp(&lo, &hi),
(*self).g.clamp(&lo, &hi),
(*self).b.clamp(&lo, &hi))
}
#[inline]
pub fn inverse(&self) -> RGB<T> {
RGB::new((*self).r.invert_channel(),
(*self).g.invert_channel(),
(*self).b.invert_channel())
}
}
pub trait ToRGB {
pub fn to_rgb<U:Channel>(&self) -> RGB<U>;
}
@ -61,7 +78,7 @@ impl<T:Clone + Channel> ToRGB for RGB<T> {
impl<T:Clone + Channel> ToHSV for RGB<T> {
#[inline]
pub fn to_hsv<U:Channel + Float>(&self) -> HSV<U> {
pub fn to_hsv<U:FloatChannel>(&self) -> HSV<U> {
// Algorithm taken from the Wikipedia article on HSL and HSV:
// http://en.wikipedia.org/wiki/HSL_and_HSV#From_HSV
@ -113,6 +130,24 @@ impl<T:Channel> RGBA<T> {
}
}
impl<T:Channel> Color<T> for RGBA<T> {
#[inline]
pub fn clamp(&self, lo: T, hi: T) -> RGBA<T> {
RGBA::new((*self).r.clamp(&lo, &hi),
(*self).g.clamp(&lo, &hi),
(*self).b.clamp(&lo, &hi),
(*self).a.clamp(&lo, &hi))
}
#[inline]
pub fn inverse(&self) -> RGBA<T> {
RGBA::new((*self).r.invert_channel(),
(*self).g.invert_channel(),
(*self).b.invert_channel(),
(*self).a.invert_channel())
}
}
pub trait ToRGBA {
pub fn to_rgba<U:Channel>(&self) -> RGBA<U>;
}
@ -154,7 +189,7 @@ impl<T:Clone + Channel> ToRGBA for RGBA<T> {
impl<T:Clone + Channel> ToHSVA for RGBA<T> {
#[inline]
pub fn to_hsva<U:Channel + Float>(&self) -> HSVA<U> {
pub fn to_hsva<U:FloatChannel>(&self) -> HSVA<U> {
HSVA::from_hsv_a(self.rgb().to_hsv(), (*self).a.to_channel())
}
}