Add color module

This commit is contained in:
Brendan Zabarauskas 2013-07-08 17:39:33 +10:00
parent 6925c5eaad
commit 999782945c
10 changed files with 447 additions and 1 deletions

View file

@ -22,7 +22,7 @@ SRC_CRATE = $(TARGET).rs
EXTERN_DIR = $(ROOT_DIR)/extern
BUILD_DIR = $(ROOT_DIR)/lib
CFG = --cfg=geom --cfg=noise --cfg=world
CFG = --cfg=color --cfg=geom --cfg=noise --cfg=world
TEST = $(TARGET)
TEST_BUILD_DIR = $(ROOT_DIR)/test

120
src/color/channel.rs Normal file
View file

@ -0,0 +1,120 @@
// Copyright 2013 The Lmath Developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/// A color channel
pub trait Channel: Num {
/// Convert a channel to the enclosing type
///
/// # Example
///
/// ~~~
/// let chan: f32 = Channel::from(0xFFFFu16);
/// assert chan == 1.0f32;
/// ~~~
pub fn from<T:Channel>(val: T) -> Self;
pub fn to_channel_u8(&self) -> u8;
pub fn to_channel_u16(&self) -> u16;
pub fn to_channel_u32(&self) -> u32;
pub fn to_channel_u64(&self) -> u64;
pub fn to_channel_f32(&self) -> f32;
pub fn to_channel_f64(&self) -> f64;
pub fn to_channel_float(&self) -> float;
}
impl Channel for u8 {
#[inline] pub fn from<T:Channel>(val: T) -> u8 { val.to_channel_u8() }
#[inline] pub fn to_channel_u8(&self) -> u8 { (*self) }
#[inline] pub fn to_channel_u16(&self) -> u16 { (*self as u16 << 8) | (*self) as u16 }
#[inline] pub fn to_channel_u32(&self) -> u32 { (self.to_channel_u16() as u32 << 16) | self.to_channel_u16() as u32 }
#[inline] pub fn to_channel_u64(&self) -> u64 { (self.to_channel_u32() as u64 << 32) | self.to_channel_u32() as u64 }
#[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 to_channel_float(&self) -> float { (*self as float) / (0xFF as float) }
}
impl Channel for u16 {
#[inline] pub fn from<T:Channel>(val: T) -> u16 { val.to_channel_u16() }
#[inline] pub fn to_channel_u8(&self) -> u8 { (*self >> 8) as u8 } // this is the equivalent of `self/256`. Some folks prefer to do `self/257`
#[inline] pub fn to_channel_u16(&self) -> u16 { (*self) }
#[inline] pub fn to_channel_u32(&self) -> u32 { (*self as u32 << 16) | (*self) as u32 }
#[inline] pub fn to_channel_u64(&self) -> u64 { (self.to_channel_u32() as u64 << 32) | self.to_channel_u32() as u64 }
#[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 to_channel_float(&self) -> float { (*self) / 0xFFFF as float }
}
impl Channel for u32 {
#[inline] pub fn from<T:Channel>(val: T) -> u32 { val.to_channel_u32() }
#[inline] pub fn to_channel_u8(&self) -> u8 { (*self >> 24) as u8 }
#[inline] pub fn to_channel_u16(&self) -> u16 { (*self >> 16) as u16 }
#[inline] pub fn to_channel_u32(&self) -> u32 { (*self) }
#[inline] pub fn to_channel_u64(&self) -> u64 { (*self as u64 << 32) | (*self) as u64 }
#[inline] pub fn to_channel_f32(&self) -> f32 { (*self) / 0xFFFF_FFFF as f32 }
#[inline] pub fn to_channel_f64(&self) -> f64 { (*self) / 0xFFFF_FFFF as f64 }
#[inline] pub fn to_channel_float(&self) -> float { (*self) / 0xFFFF_FFFF as float }
}
impl Channel for u64 {
#[inline] pub fn from<T:Channel>(val: T) -> u64 { val.to_channel_u64() }
#[inline] pub fn to_channel_u8(&self) -> u8 { (*self >> 56) as u8 }
#[inline] pub fn to_channel_u16(&self) -> u16 { (*self >> 48) as u16 }
#[inline] pub fn to_channel_u32(&self) -> u32 { (*self >> 32) as u32 }
#[inline] pub fn to_channel_u64(&self) -> u64 { (*self) }
#[inline] pub fn to_channel_f32(&self) -> f32 { (*self) / 0xFFFF_FFFF_FFFF_FFFF_u64 as f32 }
#[inline] pub fn to_channel_f64(&self) -> f64 { (*self) / 0xFFFF_FFFF_FFFF_FFFF_u64 as f64 }
#[inline] pub fn to_channel_float(&self) -> float { (*self) / 0xFFFF_FFFF_FFFF_FFFF_u64 as float }
}
impl Channel for f32 {
#[inline] pub fn from<T:Channel>(val: T) -> f32 { val.to_channel_f32() }
#[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_u32(&self) -> u32 { fail!(~"to_channel_u32 not yet implemented for f32") }
#[inline] pub fn to_channel_u64(&self) -> u64 { fail!(~"to_channel_u64 not yet implemented for f32") }
#[inline] pub fn to_channel_f32(&self) -> f32 { (*self) }
#[inline] pub fn to_channel_f64(&self) -> f64 { (*self) as f64 }
#[inline] pub fn to_channel_float(&self) -> float { (*self) as float }
}
impl Channel for f64 {
#[inline] pub fn from<T:Channel>(val: T) -> f64 { val.to_channel_f64() }
#[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_u32(&self) -> u32 { fail!(~"to_channel_u32 not yet implemented for f64") }
#[inline] pub fn to_channel_u64(&self) -> u64 { fail!(~"to_channel_u64 not yet implemented for f64") }
#[inline] pub fn to_channel_f32(&self) -> f32 { (*self) as f32 }
#[inline] pub fn to_channel_f64(&self) -> f64 { (*self) }
#[inline] pub fn to_channel_float(&self) -> float { (*self) as float }
}
impl Channel for float {
#[inline] pub fn from<T:Channel>(val: T) -> float { val.to_channel_float() }
#[inline] pub fn to_channel_u8(&self) -> u8 { (*self) * (0xFF_u8 as float) as u8 }
#[inline] pub fn to_channel_u16(&self) -> u16 { (*self) * (0xFFFF_u16 as float) as u16 }
#[inline] pub fn to_channel_u32(&self) -> u32 { fail!(~"to_channel_u32 not yet implemented for float") }
#[inline] pub fn to_channel_u64(&self) -> u64 { fail!(~"to_channel_u64 not yet implemented for float") }
#[inline] pub fn to_channel_f32(&self) -> f32 { (*self) as f32 }
#[inline] pub fn to_channel_f64(&self) -> f64 { (*self) as f64 }
#[inline] pub fn to_channel_float(&self) -> float { (*self) }
}

30
src/color/color.rs Normal file
View file

@ -0,0 +1,30 @@
// Copyright 2013 The Lmath Developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
pub use self::channel::Channel;
pub use self::hsv::{HSV, ToHSV};
pub use self::hsva::{HSVA, ToHSVA};
pub use self::rgb::{RGB, ToRGB};
pub use self::rgba::{RGBA, ToRGBA};
pub use self::srgb::SRGB;
pub use self::srgba::SRGBA;
pub mod channel;
pub mod hsv;
pub mod hsva;
pub mod rgb;
pub mod rgba;
pub mod srgb;
pub mod srgba;

66
src/color/hsv.rs Normal file
View file

@ -0,0 +1,66 @@
// Copyright 2013 The Lmath Developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::num;
use color::{Channel, RGB, ToRGB};
#[path = "../num_macros.rs"]
mod num_macros;
#[deriving(Clone, Eq)]
pub struct HSV<T> { h: T, s: T, v: T }
impl<T> HSV<T> {
pub fn new(h: T, s: T, v: T) -> HSV<T> {
HSV { h: h, s: s, v: v }
}
}
pub trait ToHSV<T> {
pub fn to_hsv(&self) -> HSV<T>;
}
impl<T:Clone + Channel + Float> ToRGB<T> for HSV<T> {
pub fn to_rgb(&self) -> RGB<T> {
// Algorithm taken from the Wikipedia article on HSL and HSV:
// http://en.wikipedia.org/wiki/HSL_and_HSV#From_HSV
let chr = (*self).v * (*self).s;
let h = (*self).h / num::cast(60);
// the 2nd largest component
let x = chr * (one!(T) - ((h % two!(T)) - one!(T)).abs());
let mut color_rgb = cond! (
(h < num::cast(1)) { RGB::new(chr.clone(), x, zero!(T)) }
(h < num::cast(2)) { RGB::new(x, chr.clone(), zero!(T)) }
(h < num::cast(3)) { RGB::new(zero!(T), chr.clone(), x) }
(h < num::cast(4)) { RGB::new(zero!(T), x, chr.clone()) }
(h < num::cast(5)) { RGB::new(x, zero!(T), chr.clone()) }
(h < num::cast(6)) { RGB::new(chr.clone(), zero!(T), x) }
_ { RGB::new(zero!(T), zero!(T), zero!(T)) }
);
// match the value by adding the same amount to each component
let mn = (*self).v - chr;
color_rgb.r = color_rgb.r + mn;
color_rgb.g = color_rgb.g + mn;
color_rgb.b = color_rgb.b + mn;
color_rgb
}
}

59
src/color/hsva.rs Normal file
View file

@ -0,0 +1,59 @@
// Copyright 2013 The Lmath Developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::cast;
use color::{Channel, HSV, ToHSV, ToRGB, RGBA, ToRGBA};
#[path = "../num_macros.rs"]
mod num_macros;
#[deriving(Clone, Eq)]
pub struct HSVA<T> { h: T, s: T, v: T, a: T }
impl<T> 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 }
}
}
pub trait ToHSVA<T> {
pub fn to_hsva(&self) -> HSVA<T>;
}
impl<T:Clone + Channel, C: ToHSV<T>> ToHSVA<T> for (C, T) {
#[inline]
pub fn to_hsva(&self) -> HSVA<T> {
match *self {
(ref c, ref a) => unsafe {
cast::transmute((c.to_hsv(), a.clone()))
}
}
}
}
impl<T:Clone + Channel + Float> ToRGBA<T> for HSVA<T> {
#[inline]
pub fn to_rgba(&self) -> RGBA<T> {
match unsafe {
cast::transmute::<&HSVA<T>, &(HSV<T>, T)>(self)
} {
&(ref c, ref a) => unsafe {
cast::transmute((c.to_rgb(), a.clone()))
},
}
}
}

61
src/color/rgb.rs Normal file
View file

@ -0,0 +1,61 @@
// Copyright 2013 The Lmath Developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::num;
use color::{Channel, HSV, ToHSV};
#[path = "../num_macros.rs"]
mod num_macros;
#[deriving(Clone, Eq)]
pub struct RGB<T> { r: T, g: T, b: T }
impl<T> RGB<T> {
#[inline]
pub fn new(r: T, g: T, b: T) -> RGB<T> {
RGB { r: r, g: g, b: b }
}
}
pub trait ToRGB<T> {
pub fn to_rgb(&self) -> RGB<T>;
}
impl<T:Clone + Channel + Float> ToHSV<T> for RGB<T> {
pub fn to_hsv(&self) -> HSV<T> {
// Algorithm taken from the Wikipedia article on HSL and HSV:
// http://en.wikipedia.org/wiki/HSL_and_HSV#From_HSV
let mx = (*self).r.max(&(*self).g).max(&(*self).b);
let mn = (*self).r.min(&(*self).g).min(&(*self).b);
let chr = mx - mn;
if chr != zero!(T) {
let h = cond! (
((*self).r == mx) { (((*self).g - (*self).b) / chr) % num::cast(6) }
((*self).g == mx) { (((*self).b - (*self).r) / chr) + num::cast(2) }
_ /* (*self).b == mx */ { (((*self).r - (*self).g) / chr) + num::cast(4) }
) * num::cast(60);
let s = chr / mx;
HSV::new(h, s, mx)
} else {
HSV::new(zero!(T), zero!(T), mx)
}
}
}

59
src/color/rgba.rs Normal file
View file

@ -0,0 +1,59 @@
// Copyright 2013 The Lmath Developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::cast;
use color::{Channel, RGB, ToRGB, ToHSV, HSVA, ToHSVA};
#[path = "../num_macros.rs"]
mod num_macros;
#[deriving(Clone, Eq)]
pub struct RGBA<T> { r: T, g: T, b: T, a: T }
impl<T> RGBA<T> {
#[inline]
pub fn new(r: T, g: T, b: T, a: T) -> RGBA<T> {
RGBA { r: r, g: g, b: b, a: a }
}
}
pub trait ToRGBA<T> {
pub fn to_rgba(&self) -> RGBA<T>;
}
impl<T:Clone + Channel, C: ToRGB<T>> ToRGBA<T> for (C, T) {
#[inline]
pub fn to_rgba(&self) -> RGBA<T> {
match *self {
(ref c, ref a) => unsafe {
cast::transmute((c.to_rgb(), a.clone()))
}
}
}
}
impl<T:Clone + Channel + Float> ToHSVA<T> for RGBA<T> {
#[inline]
pub fn to_hsva(&self) -> HSVA<T> {
match unsafe {
cast::transmute::<&RGBA<T>, &(RGB<T>, T)>(self)
} {
&(ref c, ref a) => unsafe {
cast::transmute((c.to_hsv(), a.clone()))
}
}
}
}

24
src/color/srgb.rs Normal file
View file

@ -0,0 +1,24 @@
// Copyright 2013 The Lmath Developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#[deriving(Clone, Eq)]
pub struct SRGB<T> { r: T, g: T, b: T }
impl<T> SRGB<T> {
#[inline]
pub fn new(r: T, g: T, b: T) -> SRGB<T> {
SRGB { r: r, g: g, b: b }
}
}

24
src/color/srgba.rs Normal file
View file

@ -0,0 +1,24 @@
// Copyright 2013 The Lmath Developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#[deriving(Clone, Eq)]
pub struct SRGBA<T> { r: T, g: T, b: T, a: T }
impl<T> SRGBA<T> {
#[inline]
pub fn new(r: T, g: T, b: T, a: T) -> SRGBA<T> {
SRGBA { r: r, g: g, b: b, a: a }
}
}

View file

@ -38,3 +38,6 @@ pub mod noise;
#[path = "world/world.rs"]
pub mod world;
#[cfg(color)]
#[path = "color/color.rs"]
pub mod color;