From 58c3233fe32a7663906278cffadca966f1e39955 Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Wed, 10 Jul 2013 12:59:02 +1000 Subject: [PATCH] Impl Color trait --- src/color/channel.rs | 86 +++++++++++++++++++++++++++++++++++++++----- src/color/color.rs | 4 +-- src/color/hsv.rs | 69 ++++++++++++++++++++++++++--------- src/color/rgb.rs | 41 +++++++++++++++++++-- 4 files changed, 169 insertions(+), 31 deletions(-) diff --git a/src/color/channel.rs b/src/color/channel.rs index 7adcacc..e604c98 100644 --- a/src/color/channel.rs +++ b/src/color/channel.rs @@ -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(chan: T) -> Self; pub fn to_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(chan: T) -> f32 { chan.to_channel_f32() } #[inlune] pub fn to_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(chan: T) -> f64 { chan.to_channel_f64() } #[inlune] pub fn to_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); + } } diff --git a/src/color/color.rs b/src/color/color.rs index 260ad01..5b47894 100644 --- a/src/color/color.rs +++ b/src/color/color.rs @@ -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 { + pub fn clamp(&self, lo: T, hi: T) -> Self; pub fn inverse(&self) -> Self; - pub fn invert_self(&mut self); } diff --git a/src/color/hsv.rs b/src/color/hsv.rs index b4b8bbc..86b4851 100644 --- a/src/color/hsv.rs +++ b/src/color/hsv.rs @@ -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 { h: T, s: T, v: T } -impl HSV { +impl HSV { pub fn new(h: T, s: T, v: T) -> HSV { HSV { h: h, s: s, v: v } } } +impl Color for HSV { + #[inline] + pub fn clamp(&self, lo: T, hi: T) -> HSV { + HSV::new((*self).h.clamp(&lo, &hi), + (*self).s.clamp(&lo, &hi), + (*self).v.clamp(&lo, &hi)) + } + + #[inline] + pub fn inverse(&self) -> HSV { + HSV::new((*self).h.invert_degrees(), + (*self).s.invert_channel(), + (*self).v.invert_channel()) + } +} + pub trait ToHSV { - pub fn to_hsv(&self) -> HSV; + pub fn to_hsv(&self) -> HSV; } impl ToHSV for u32 { #[inline] - pub fn to_hsv(&self) -> HSV { + pub fn to_hsv(&self) -> HSV { fail!("Not yet implemented") } } impl ToHSV for u64 { #[inline] - pub fn to_hsv(&self) -> HSV { + pub fn to_hsv(&self) -> HSV { fail!("Not yet implemented") } } -impl ToHSV for HSV { +impl ToHSV for HSV { #[inline] - pub fn to_hsv(&self) -> HSV { + pub fn to_hsv(&self) -> HSV { HSV::new((*self).h.to_channel(), (*self).s.to_channel(), (*self).v.to_channel()) } } -impl ToRGB for HSV { +impl ToRGB for HSV { pub fn to_rgb(&self) -> RGB { // 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 ToRGB for HSV { #[deriving(Clone, Eq)] pub struct HSVA { h: T, s: T, v: T, a: T } -impl HSVA { +impl HSVA { #[inline] pub fn new(h: T, s: T, v: T, a: T) -> HSVA { HSVA { h: h, s: s, v: v, a: a } @@ -115,27 +132,45 @@ impl HSVA { } } +impl Color for HSVA { + #[inline] + pub fn clamp(&self, lo: T, hi: T) -> HSVA { + 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 { + 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(&self) -> HSVA; + pub fn to_hsva(&self) -> HSVA; } impl ToHSVA for u32 { #[inline] - pub fn to_hsva(&self) -> HSVA { + pub fn to_hsva(&self) -> HSVA { fail!("Not yet implemented") } } impl ToHSVA for u64 { #[inline] - pub fn to_hsva(&self) -> HSVA { + pub fn to_hsva(&self) -> HSVA { fail!("Not yet implemented") } } -impl ToHSVA for (C, T) { +impl ToHSVA for (C, T) { #[inline] - pub fn to_hsva(&self) -> HSVA { + pub fn to_hsva(&self) -> HSVA { match *self { (ref hsv, ref a) => { HSVA::from_hsv_a(hsv.to_hsv(), a.to_channel()) @@ -144,9 +179,9 @@ impl ToHSVA for (C, T) { } } -impl ToHSVA for HSVA { +impl ToHSVA for HSVA { #[inline] - pub fn to_hsva(&self) -> HSVA { + pub fn to_hsva(&self) -> HSVA { HSVA::new((*self).h.to_channel(), (*self).s.to_channel(), (*self).v.to_channel(), @@ -154,7 +189,7 @@ impl ToHSVA for HSVA { } } -impl ToRGBA for HSVA { +impl ToRGBA for HSVA { #[inline] pub fn to_rgba(&self) -> RGBA { RGBA::from_rgb_a(self.hsv().to_rgb(), (*self).a.to_channel()) diff --git a/src/color/rgb.rs b/src/color/rgb.rs index dcb4041..f7d69ff 100644 --- a/src/color/rgb.rs +++ b/src/color/rgb.rs @@ -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 RGB { } } +impl Color for RGB { + #[inline] + pub fn clamp(&self, lo: T, hi: T) -> RGB { + RGB::new((*self).r.clamp(&lo, &hi), + (*self).g.clamp(&lo, &hi), + (*self).b.clamp(&lo, &hi)) + } + + #[inline] + pub fn inverse(&self) -> RGB { + RGB::new((*self).r.invert_channel(), + (*self).g.invert_channel(), + (*self).b.invert_channel()) + } +} + pub trait ToRGB { pub fn to_rgb(&self) -> RGB; } @@ -61,7 +78,7 @@ impl ToRGB for RGB { impl ToHSV for RGB { #[inline] - pub fn to_hsv(&self) -> HSV { + pub fn to_hsv(&self) -> HSV { // 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 RGBA { } } +impl Color for RGBA { + #[inline] + pub fn clamp(&self, lo: T, hi: T) -> RGBA { + 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 { + 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(&self) -> RGBA; } @@ -154,7 +189,7 @@ impl ToRGBA for RGBA { impl ToHSVA for RGBA { #[inline] - pub fn to_hsva(&self) -> HSVA { + pub fn to_hsva(&self) -> HSVA { HSVA::from_hsv_a(self.rgb().to_hsv(), (*self).a.to_channel()) } }