From 9b1e9845644473a2f910080321c9962462d34846 Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Wed, 28 Nov 2012 17:50:26 +1000 Subject: [PATCH] Add channel module --- src/color/channel.rs | 140 +++++++++++++++++ src/color/test/test_channel.rs | 275 +++++++++++++++++++++++++++++++++ src/lmath.rc | 21 ++- 3 files changed, 430 insertions(+), 6 deletions(-) create mode 100644 src/color/channel.rs create mode 100644 src/color/test/test_channel.rs diff --git a/src/color/channel.rs b/src/color/channel.rs new file mode 100644 index 0000000..eb7d10d --- /dev/null +++ b/src/color/channel.rs @@ -0,0 +1,140 @@ +use num::cast::{NumCast, cast}; + +pub trait Channel { + static pure fn channel_max() -> self; + + static pure fn from_channel(val: T) -> self; + pure fn convert_channel() -> T; + + pure fn to_channel_u8() -> u8; + pure fn to_channel_u16() -> u16; + pure fn to_channel_u32() -> u32; + pure fn to_channel_u64() -> u64; + pure fn to_channel_f32() -> f32; + pure fn to_channel_f64() -> f64; + pure fn to_channel_float() -> float; + + pure fn inverse() -> self; +} + +pub pure fn convert_channel(val: T) -> U { val.convert_channel() } + +pub impl u8: Channel { + static pure fn channel_max() -> u8 { 0xFF } // 2^8 + + static pure fn from_channel(val: T) -> u8 { val.to_channel_u8() } + pure fn convert_channel() -> T { from_channel(self) } + + pure fn to_channel_u8() -> u8 { self } + pure fn to_channel_u16() -> u16 { (self as u16 << 8) | self as u16 } + pure fn to_channel_u32() -> u32 { (self.to_channel_u16() as u32 << 16) | self.to_channel_u16() as u32 } + pure fn to_channel_u64() -> u64 { (self.to_channel_u32() as u64 << 32) | self.to_channel_u32() as u64 } + pure fn to_channel_f32() -> f32 { (self as f32) / (0xFF as f32) } + pure fn to_channel_f64() -> f64 { (self as f64) / (0xFF as f64) } + pure fn to_channel_float() -> float { (self as float) / (0xFF as float) } + + pure fn inverse() -> u8 { !self } +} + +pub impl u16: Channel { + static pure fn channel_max() -> u16 { 0xFFFF } // 2^16 + + static pure fn from_channel(val: T) -> u16 { val.to_channel_u16() } + pure fn convert_channel() -> T { from_channel(self) } + + pure fn to_channel_u8() -> u8 { self / 0x100 as u8 } + pure fn to_channel_u16() -> u16 { self } + pure fn to_channel_u32() -> u32 { (self as u32 << 16) | self as u32 } + pure fn to_channel_u64() -> u64 { (self.to_channel_u32() as u64 << 32) | self.to_channel_u32() as u64 } + pure fn to_channel_f32() -> f32 { self / 0xFFFF as f32 } + pure fn to_channel_f64() -> f64 { self / 0xFFFF as f64 } + pure fn to_channel_float() -> float { self / 0xFFFF as float } + + pure fn inverse() -> u16 { !self } +} + +pub impl u32: Channel { + static pure fn channel_max() -> u32 { 0xFFFF_FFFF } // 2^32 + + static pure fn from_channel(val: T) -> u32 { val.to_channel_u32() } + pure fn convert_channel() -> T { from_channel(self) } + + pure fn to_channel_u8() -> u8 { self / 0x1_0000_00 as u8 } + pure fn to_channel_u16() -> u16 { self / 0x1_0000 as u16 } + pure fn to_channel_u32() -> u32 { self } + pure fn to_channel_u64() -> u64 { (self as u64 << 32) | self as u64 } + pure fn to_channel_f32() -> f32 { self / 0xFFFF_FFFF as f32 } + pure fn to_channel_f64() -> f64 { self / 0xFFFF_FFFF as f64 } + pure fn to_channel_float() -> float { self / 0xFFFF_FFFF as float } + + pure fn inverse() -> u32 { !self } +} + +pub impl u64: Channel { + static pure fn channel_max() -> u64 { 0xFFFF_FFFF_FFFF_FFFF_u64 } // 2^64 + + static pure fn from_channel(val: T) -> u64 { val.to_channel_u64() } + pure fn convert_channel() -> T { from_channel(self) } + + pure fn to_channel_u8() -> u8 { self / 0x1_0000_0000_0000_00 as u8 } + pure fn to_channel_u16() -> u16 { self / 0x1_0000_0000_0000 as u16 } + pure fn to_channel_u32() -> u32 { self / 0x1_0000_0000 as u32 } + pure fn to_channel_u64() -> u64 { self } + pure fn to_channel_f32() -> f32 { self / 0xFFFF_FFFF_FFFF_FFFF_u64 as f32 } + pure fn to_channel_f64() -> f64 { self / 0xFFFF_FFFF_FFFF_FFFF_u64 as f64 } + pure fn to_channel_float() -> float { self / 0xFFFF_FFFF_FFFF_FFFF_u64 as float } + + pure fn inverse() -> u64 { !self } +} + +pub impl f32: Channel { + static pure fn channel_max() -> f32 { 1f32 } + + static pure fn from_channel(val: T) -> f32 { val.to_channel_f32() } + pure fn convert_channel() -> T { from_channel(self) } + + + pure fn to_channel_u8() -> u8 { self * (0xFFu8 as f32) as u8 } + pure fn to_channel_u16() -> u16 { self * (0xFFFFu16 as f32) as u16 } + pure fn to_channel_u32() -> u32 { fail(~"to_channel_u32 not yet implemented for f32") } + pure fn to_channel_u64() -> u64 { fail(~"to_channel_u64 not yet implemented for f32") } + pure fn to_channel_f32() -> f32 { self } + pure fn to_channel_f64() -> f64 { self as f64 } + pure fn to_channel_float() -> float { self as float } + + pure fn inverse() -> f32 { 1.0 - self } +} + +pub impl f64: Channel { + static pure fn channel_max() -> f64 { 1f64 } + + static pure fn from_channel(val: T) -> f64 { val.to_channel_f64() } + pure fn convert_channel() -> T { from_channel(self) } + + pure fn to_channel_u8() -> u8 { self * (0xFFu8 as f64) as u8 } + pure fn to_channel_u16() -> u16 { self * (0xFFFFu16 as f64) as u16 } + pure fn to_channel_u32() -> u32 { fail(~"to_channel_u32 not yet implemented for f64") } + pure fn to_channel_u64() -> u64 { fail(~"to_channel_u64 not yet implemented for f64") } + pure fn to_channel_f32() -> f32 { self as f32 } + pure fn to_channel_f64() -> f64 { self } + pure fn to_channel_float() -> float { self as float } + + pure fn inverse() -> f64 { 1.0 - self } +} + +pub impl float: Channel { + static pure fn channel_max() -> float { 1f } + + static pure fn from_channel(val: T) -> float { val.to_channel_float() } + pure fn convert_channel() -> T { from_channel(self) } + + pure fn to_channel_u8() -> u8 { self * (0xFFu8 as float) as u8 } + pure fn to_channel_u16() -> u16 { self * (0xFFFFu16 as float) as u16 } + pure fn to_channel_u32() -> u32 { fail(~"to_channel_u32 not yet implemented for float") } + pure fn to_channel_u64() -> u64 { fail(~"to_channel_u64 not yet implemented for float") } + pure fn to_channel_f32() -> f32 { self as f32 } + pure fn to_channel_f64() -> f64 { self as f64 } + pure fn to_channel_float() -> float { self } + + pure fn inverse() -> float { 1.0 - self } +} \ No newline at end of file diff --git a/src/color/test/test_channel.rs b/src/color/test/test_channel.rs new file mode 100644 index 0000000..2c7fda7 --- /dev/null +++ b/src/color/test/test_channel.rs @@ -0,0 +1,275 @@ +use channel::*; + +#[test] +fn test_channel_u8() { + assert 0x00_u8.to_channel_u8() == 0x00_u8; + assert 0x30_u8.to_channel_u8() == 0x30_u8; + assert 0x66_u8.to_channel_u8() == 0x66_u8; + assert 0xA0_u8.to_channel_u8() == 0xA0_u8; + assert 0xFF_u8.to_channel_u8() == 0xFF_u8; + + assert 0x00_u8.to_channel_u16() == 0x0000_u16; + assert 0x30_u8.to_channel_u16() == 0x3030_u16; + assert 0x66_u8.to_channel_u16() == 0x6666_u16; + assert 0xA0_u8.to_channel_u16() == 0xA0A0_u16; + assert 0xFF_u8.to_channel_u16() == 0xFFFF_u16; + + assert 0x00_u8.to_channel_u32() == 0x0000_0000_u32; + assert 0x30_u8.to_channel_u32() == 0x3030_3030_u32; + assert 0x66_u8.to_channel_u32() == 0x6666_6666_u32; + assert 0xA0_u8.to_channel_u32() == 0xA0A0_A0A0_u32; + assert 0xFF_u8.to_channel_u32() == 0xFFFF_FFFF_u32; + + assert 0x00_u8.to_channel_u64() == 0x0000_0000_0000_0000_u64; + assert 0x30_u8.to_channel_u64() == 0x3030_3030_3030_3030_u64; + assert 0x66_u8.to_channel_u64() == 0x6666_6666_6666_6666_u64; + assert 0xA0_u8.to_channel_u64() == 0xA0A0_A0A0_A0A0_A0A0_u64; + assert 0xFF_u8.to_channel_u64() == 0xFFFF_FFFF_FFFF_FFFF_u64; + + assert 0x00_u8.to_channel_f32() == 0f32; + assert 0xFF_u8.to_channel_f32() == 1f32; + + assert 0x00_u8.to_channel_f64() == 0f64; + assert 0xFF_u8.to_channel_f64() == 1f64; + + assert 0x00_u8.to_channel_float() == 0f; + assert 0xFF_u8.to_channel_float() == 1f; + + + // Test inverse + + assert 0x00_u8.inverse() == 0xFF_u8; + assert 0x66_u8.inverse() == 0x99_u8; + assert 0xFF_u8.inverse() == 0x00_u8; +} + +#[test] +fn test_channel_u16() { + assert 0x0000_u16.to_channel_u8() == 0x00_u8; + assert 0x3300_u16.to_channel_u8() == 0x33_u8; + assert 0x6666_u16.to_channel_u8() == 0x66_u8; + assert 0xAA00_u16.to_channel_u8() == 0xAA_u8; + assert 0xFFFF_u16.to_channel_u8() == 0xFF_u8; + + assert 0x0000_u16.to_channel_u16() == 0x0000_u16; + assert 0x3300_u16.to_channel_u16() == 0x3300_u16; + assert 0x6666_u16.to_channel_u16() == 0x6666_u16; + assert 0xAA00_u16.to_channel_u16() == 0xAA00_u16; + assert 0xFFFF_u16.to_channel_u16() == 0xFFFF_u16; + + assert 0x0000_u16.to_channel_u32() == 0x0000_0000_u32; + assert 0x3300_u16.to_channel_u32() == 0x3300_3300_u32; + assert 0x6666_u16.to_channel_u32() == 0x6666_6666_u32; + assert 0xAA00_u16.to_channel_u32() == 0xAA00_AA00_u32; + assert 0xFFFF_u16.to_channel_u32() == 0xFFFF_FFFF_u32; + + assert 0x0000_u16.to_channel_u64() == 0x0000_0000_0000_0000_u64; + assert 0x3300_u16.to_channel_u64() == 0x3300_3300_3300_3300_u64; + assert 0x6666_u16.to_channel_u64() == 0x6666_6666_6666_6666_u64; + assert 0xAA00_u16.to_channel_u64() == 0xAA00_AA00_AA00_AA00_u64; + assert 0xFFFF_u16.to_channel_u64() == 0xFFFF_FFFF_FFFF_FFFF_u64; + + assert 0x0000_u16.to_channel_f32() == 0f32; + assert 0xFFFF_u16.to_channel_f32() == 1f32; + + assert 0x0000_u16.to_channel_f64() == 0f64; + assert 0xFFFF_u16.to_channel_f64() == 1f64; + + assert 0x0000_u16.to_channel_float() == 0f; + assert 0xFFFF_u16.to_channel_float() == 1f; + + + // Test inverse + + assert 0x0000_u16.inverse() == 0xFFFF_u16; + assert 0x6666_u16.inverse() == 0x9999_u16; + assert 0xFFFF_u16.inverse() == 0x0000_u16; +} + +#[test] +fn test_channel_u32() { + assert 0x0000_0000_u32.to_channel_u8() == 0x00_u8; + assert 0x3333_0000_u32.to_channel_u8() == 0x33_u8; + assert 0x6666_6666_u32.to_channel_u8() == 0x66_u8; + assert 0xAAAA_0000_u32.to_channel_u8() == 0xAA_u8; + assert 0xFFFF_FFFF_u32.to_channel_u8() == 0xFF_u8; + + assert 0x0000_0000_u32.to_channel_u16() == 0x0000_u16; + assert 0x3333_0000_u32.to_channel_u16() == 0x3333_u16; + assert 0x6666_6666_u32.to_channel_u16() == 0x6666_u16; + assert 0xAAAA_0000_u32.to_channel_u16() == 0xAAAA_u16; + assert 0xFFFF_FFFF_u32.to_channel_u16() == 0xFFFF_u16; + + assert 0x0000_0000_u32.to_channel_u32() == 0x0000_0000_u32; + assert 0x3333_0000_u32.to_channel_u32() == 0x3333_0000_u32; + assert 0x6666_6666_u32.to_channel_u32() == 0x6666_6666_u32; + assert 0xAAAA_0000_u32.to_channel_u32() == 0xAAAA_0000_u32; + assert 0xFFFF_FFFF_u32.to_channel_u32() == 0xFFFF_FFFF_u32; + + assert 0x0000_0000_u32.to_channel_u64() == 0x0000_0000_0000_0000_u64; + assert 0x3333_0000_u32.to_channel_u64() == 0x3333_0000_3333_0000_u64; + assert 0x6666_6666_u32.to_channel_u64() == 0x6666_6666_6666_6666_u64; + assert 0xAAAA_0000_u32.to_channel_u64() == 0xAAAA_0000_AAAA_0000_u64; + assert 0xFFFF_FFFF_u32.to_channel_u64() == 0xFFFF_FFFF_FFFF_FFFF_u64; + + assert 0x0000_0000_u32.to_channel_f32() == 0f32; + assert 0xFFFF_FFFF_u32.to_channel_f32() == 1f32; + + assert 0x0000_0000_u32.to_channel_f64() == 0f64; + assert 0xFFFF_FFFF_u32.to_channel_f64() == 1f64; + + assert 0x0000_0000_u32.to_channel_float() == 0f; + assert 0xFFFF_FFFF_u32.to_channel_float() == 1f; + + + // Test inverse + + assert 0x0000_0000_u32.inverse() == 0xFFFF_FFFF_u32; + assert 0x6666_6666_u32.inverse() == 0x9999_9999_u32; + assert 0xFFFF_FFFF_u32.inverse() == 0x0000_0000_u32; +} + +#[test] +fn test_channel_u64() { + assert 0x0000_0000_0000_0000_u64.to_channel_u8() == 0x00_u8; + assert 0x3333_3333_0000_0000_u64.to_channel_u8() == 0x33_u8; // FIXME: color shift? + assert 0x6666_6666_6666_6666_u64.to_channel_u8() == 0x66_u8; + assert 0xAAAA_AAAA_0000_0000_u64.to_channel_u8() == 0xAA_u8; // FIXME: color shift? + assert 0xFFFF_FFFF_FFFF_FFFF_u64.to_channel_u8() == 0xFF_u8; + + assert 0x0000_0000_0000_0000_u64.to_channel_u16() == 0x0000_u16; + assert 0x3333_3333_0000_0000_u64.to_channel_u16() == 0x3333_u16; // FIXME: color shift? + assert 0x6666_6666_6666_6666_u64.to_channel_u16() == 0x6666_u16; + assert 0xAAAA_AAAA_0000_0000_u64.to_channel_u16() == 0xAAAA_u16; // FIXME: color shift? + assert 0xFFFF_FFFF_FFFF_FFFF_u64.to_channel_u16() == 0xFFFF_u16; + + assert 0x0000_0000_0000_0000_u64.to_channel_u32() == 0x0000_0000_u32; + assert 0x3333_3333_0000_0000_u64.to_channel_u32() == 0x3333_3333_u32; // FIXME: color shift? + assert 0x6666_6666_6666_6666_u64.to_channel_u32() == 0x6666_6666_u32; + assert 0xAAAA_AAAA_0000_0000_u64.to_channel_u32() == 0xAAAA_AAAA_u32; // FIXME: color shift? + assert 0xFFFF_FFFF_FFFF_FFFF_u64.to_channel_u32() == 0xFFFF_FFFF_u32; + + assert 0x0000_0000_0000_0000_u64.to_channel_u64() == 0x0000_0000_0000_0000_u64; + assert 0x3333_3333_0000_0000_u64.to_channel_u64() == 0x3333_3333_0000_0000_u64; + assert 0x6666_6666_6666_6666_u64.to_channel_u64() == 0x6666_6666_6666_6666_u64; + assert 0xAAAA_AAAA_0000_0000_u64.to_channel_u64() == 0xAAAA_AAAA_0000_0000_u64; + assert 0xFFFF_FFFF_FFFF_FFFF_u64.to_channel_u64() == 0xFFFF_FFFF_FFFF_FFFF_u64; + + assert 0x0000_0000_0000_0000_u64.to_channel_f32() == 0f32; + assert 0xFFFF_FFFF_FFFF_FFFF_u64.to_channel_f32() == 1f32; + + assert 0x0000_0000_0000_0000_u64.to_channel_f64() == 0f64; + assert 0xFFFF_FFFF_FFFF_FFFF_u64.to_channel_f64() == 1f64; + + assert 0x0000_0000_0000_0000_u64.to_channel_float() == 0f; + assert 0xFFFF_FFFF_FFFF_FFFF_u64.to_channel_float() == 1f; + + + // Test inverse + + assert 0x0000_0000_0000_0000_u64.inverse() == 0xFFFF_FFFF_FFFF_FFFF_u64; + assert 0x6666_6666_6666_6666_u64.inverse() == 0x9999_9999_9999_9999_u64; + assert 0xFFFF_FFFF_FFFF_FFFF_u64.inverse() == 0x0000_0000_0000_0000_u64; +} + +#[test] +fn test_channel_f32() { + assert 0.00f32.to_channel_u8() == 0x00; + assert 0.25f32.to_channel_u8() == 0x3F; + assert 0.50f32.to_channel_u8() == 0x7F; + assert 0.75f32.to_channel_u8() == 0xBF; + assert 1.00f32.to_channel_u8() == 0xFF; + + assert 0.00f32.to_channel_u16() == 0x0000; + assert 0.25f32.to_channel_u16() == 0x3FFF; + assert 0.50f32.to_channel_u16() == 0x7FFF; + assert 0.75f32.to_channel_u16() == 0xBFFF; + assert 1.00f32.to_channel_u16() == 0xFFFF; + + // TODO: test to_channel_u32() + + // TODO: test to_channel_u64() + + // TODO: test to_channel_f32() + + // TODO: test to_channel_f64() + + // TODO: test to_channel_float() + + + // Test inverse + + assert 0.00f32.inverse() == 1.00f32; + assert 0.25f32.inverse() == 0.75f32; + assert 0.50f32.inverse() == 0.50f32; + assert 0.75f32.inverse() == 0.25f32; + assert 1.00f32.inverse() == 0.00f32; +} + +#[test] +fn test_channel_f64() { + assert 0.00f64.to_channel_u8() == 0x00; + assert 0.25f64.to_channel_u8() == 0x3F; + assert 0.50f64.to_channel_u8() == 0x7F; + assert 0.75f64.to_channel_u8() == 0xBF; + assert 1.00f64.to_channel_u8() == 0xFF; + + assert 0.00f64.to_channel_u16() == 0x0000; + assert 0.25f64.to_channel_u16() == 0x3FFF; + assert 0.50f64.to_channel_u16() == 0x7FFF; + assert 0.75f64.to_channel_u16() == 0xBFFF; + assert 1.00f64.to_channel_u16() == 0xFFFF; + + // TODO: test to_channel_u32() + + // TODO: test to_channel_u64() + + // TODO: test to_channel_f32() + + // TODO: test to_channel_f64() + + // TODO: test to_channel_float() + + + // Test inverse + + assert 0.00f64.inverse() == 1.00f64; + assert 0.25f64.inverse() == 0.75f64; + assert 0.50f64.inverse() == 0.50f64; + assert 0.75f64.inverse() == 0.25f64; + assert 1.00f64.inverse() == 0.00f64; +} + +#[test] +fn test_channel_float() { + assert 0.00f.to_channel_u8() == 0x00; + assert 0.25f.to_channel_u8() == 0x3F; + assert 0.50f.to_channel_u8() == 0x7F; + assert 0.75f.to_channel_u8() == 0xBF; + assert 1.00f.to_channel_u8() == 0xFF; + + assert 0.00f.to_channel_u16() == 0x0000; + assert 0.25f.to_channel_u16() == 0x3FFF; + assert 0.50f.to_channel_u16() == 0x7FFF; + assert 0.75f.to_channel_u16() == 0xBFFF; + assert 1.00f.to_channel_u16() == 0xFFFF; + + // TODO: test to_channel_u32() + + // TODO: test to_channel_u64() + + // TODO: test to_channel_f32() + + // TODO: test to_channel_f64() + + // TODO: test to_channel_float() + + + // Test inverse + + assert 0.00f.inverse() == 1.00f; + assert 0.25f.inverse() == 0.75f; + assert 0.50f.inverse() == 0.50f; + assert 0.75f.inverse() == 0.25f; + assert 1.00f.inverse() == 0.00f; +} \ No newline at end of file diff --git a/src/lmath.rc b/src/lmath.rc index 7591fe9..03bc648 100644 --- a/src/lmath.rc +++ b/src/lmath.rc @@ -26,12 +26,13 @@ mod test { mod test_vec; } -pub mod num { - pub mod cast; - pub mod consts; - pub mod default_eq; - pub mod ext; - pub mod rhs; // FIXME: see module description +pub mod color { + pub mod channel; + + #[test] + mod test { + mod test_channel; + } } pub mod funs { @@ -46,4 +47,12 @@ pub mod funs { mod test_common; mod test_relational; } +} + +pub mod num { + pub mod cast; + pub mod consts; + pub mod default_eq; + pub mod ext; + pub mod rhs; // FIXME: see module description } \ No newline at end of file