From 999782945cce4dec2e3169934b0594450e7c284b Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Mon, 8 Jul 2013 17:39:33 +1000 Subject: [PATCH] Add color module --- Makefile | 2 +- src/color/channel.rs | 120 +++++++++++++++++++++++++++++++++++++++++++ src/color/color.rs | 30 +++++++++++ src/color/hsv.rs | 66 ++++++++++++++++++++++++ src/color/hsva.rs | 59 +++++++++++++++++++++ src/color/rgb.rs | 61 ++++++++++++++++++++++ src/color/rgba.rs | 59 +++++++++++++++++++++ src/color/srgb.rs | 24 +++++++++ src/color/srgba.rs | 24 +++++++++ src/lmath.rs | 3 ++ 10 files changed, 447 insertions(+), 1 deletion(-) create mode 100644 src/color/channel.rs create mode 100644 src/color/color.rs create mode 100644 src/color/hsv.rs create mode 100644 src/color/hsva.rs create mode 100644 src/color/rgb.rs create mode 100644 src/color/rgba.rs create mode 100644 src/color/srgb.rs create mode 100644 src/color/srgba.rs diff --git a/Makefile b/Makefile index 3b0b9d6..9671953 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/src/color/channel.rs b/src/color/channel.rs new file mode 100644 index 0000000..5b21c71 --- /dev/null +++ b/src/color/channel.rs @@ -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(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(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(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(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(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(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(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(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) } +} \ No newline at end of file diff --git a/src/color/color.rs b/src/color/color.rs new file mode 100644 index 0000000..a29006b --- /dev/null +++ b/src/color/color.rs @@ -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; diff --git a/src/color/hsv.rs b/src/color/hsv.rs new file mode 100644 index 0000000..950b08d --- /dev/null +++ b/src/color/hsv.rs @@ -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 { h: T, s: T, v: T } + +impl HSV { + pub fn new(h: T, s: T, v: T) -> HSV { + HSV { h: h, s: s, v: v } + } +} + +pub trait ToHSV { + pub fn to_hsv(&self) -> 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 + + 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 + } +} diff --git a/src/color/hsva.rs b/src/color/hsva.rs new file mode 100644 index 0000000..e4680ff --- /dev/null +++ b/src/color/hsva.rs @@ -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 { h: T, s: T, v: T, a: T } + +impl HSVA { + #[inline] + pub fn new(h: T, s: T, v: T, a: T) -> HSVA { + HSVA { h: h, s: s, v: v, a: a } + } +} + +pub trait ToHSVA { + pub fn to_hsva(&self) -> HSVA; +} + +impl> ToHSVA for (C, T) { + #[inline] + pub fn to_hsva(&self) -> HSVA { + match *self { + (ref c, ref a) => unsafe { + cast::transmute((c.to_hsv(), a.clone())) + } + } + } +} + +impl ToRGBA for HSVA { + #[inline] + pub fn to_rgba(&self) -> RGBA { + match unsafe { + cast::transmute::<&HSVA, &(HSV, T)>(self) + } { + &(ref c, ref a) => unsafe { + cast::transmute((c.to_rgb(), a.clone())) + }, + } + } +} diff --git a/src/color/rgb.rs b/src/color/rgb.rs new file mode 100644 index 0000000..9313ec5 --- /dev/null +++ b/src/color/rgb.rs @@ -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 { r: T, g: T, b: T } + +impl RGB { + #[inline] + pub fn new(r: T, g: T, b: T) -> RGB { + RGB { r: r, g: g, b: b } + } +} + +pub trait ToRGB { + pub fn to_rgb(&self) -> RGB; +} + +impl ToHSV for RGB { + 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 + + 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) + } + } +} diff --git a/src/color/rgba.rs b/src/color/rgba.rs new file mode 100644 index 0000000..beed60c --- /dev/null +++ b/src/color/rgba.rs @@ -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 { r: T, g: T, b: T, a: T } + +impl RGBA { + #[inline] + pub fn new(r: T, g: T, b: T, a: T) -> RGBA { + RGBA { r: r, g: g, b: b, a: a } + } +} + +pub trait ToRGBA { + pub fn to_rgba(&self) -> RGBA; +} + +impl> ToRGBA for (C, T) { + #[inline] + pub fn to_rgba(&self) -> RGBA { + match *self { + (ref c, ref a) => unsafe { + cast::transmute((c.to_rgb(), a.clone())) + } + } + } +} + +impl ToHSVA for RGBA { + #[inline] + pub fn to_hsva(&self) -> HSVA { + match unsafe { + cast::transmute::<&RGBA, &(RGB, T)>(self) + } { + &(ref c, ref a) => unsafe { + cast::transmute((c.to_hsv(), a.clone())) + } + } + } +} diff --git a/src/color/srgb.rs b/src/color/srgb.rs new file mode 100644 index 0000000..1ab8dc7 --- /dev/null +++ b/src/color/srgb.rs @@ -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 { r: T, g: T, b: T } + +impl SRGB { + #[inline] + pub fn new(r: T, g: T, b: T) -> SRGB { + SRGB { r: r, g: g, b: b } + } +} diff --git a/src/color/srgba.rs b/src/color/srgba.rs new file mode 100644 index 0000000..490e17f --- /dev/null +++ b/src/color/srgba.rs @@ -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 { r: T, g: T, b: T, a: T } + +impl SRGBA { + #[inline] + pub fn new(r: T, g: T, b: T, a: T) -> SRGBA { + SRGBA { r: r, g: g, b: b, a: a } + } +} diff --git a/src/lmath.rs b/src/lmath.rs index 2425667..8327d7d 100644 --- a/src/lmath.rs +++ b/src/lmath.rs @@ -38,3 +38,6 @@ pub mod noise; #[path = "world/world.rs"] pub mod world; +#[cfg(color)] +#[path = "color/color.rs"] +pub mod color;