// 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 std::cast; use color::{Color, FloatColor}; use color::{Channel, FloatChannel}; use color::{HSV, ToHSV, HSVA, ToHSVA}; #[deriving(Clone, Eq)] pub struct RGB { r: T, g: T, b: T } impl_approx!(RGB { r, g, b }) impl RGB { #[inline] pub fn new(r: T, g: T, b: T) -> RGB { RGB { r: r, g: g, b: b } } } impl Color for RGB { /// Clamps the components of the color to the range `(lo,hi)`. #[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)) } /// Inverts the color. #[inline] pub fn inverse(&self) -> RGB { RGB::new((*self).r.invert_channel(), (*self).g.invert_channel(), (*self).b.invert_channel()) } } impl FloatColor for RGB { /// Normalizes the components of the color by clamping them to the range `(0,1)`. #[inline] pub fn normalize(&self) -> RGB { RGB::new((*self).r.normalize_channel(), (*self).g.normalize_channel(), (*self).b.normalize_channel()) } } pub trait ToRGB { pub fn to_rgb(&self) -> RGB; } impl ToRGB for u32 { #[inline] pub fn to_rgb(&self) -> RGB { fail!("Not yet implemented") } } impl ToRGB for u64 { #[inline] pub fn to_rgb(&self) -> RGB { fail!("Not yet implemented") } } impl ToRGB for RGB { #[inline] pub fn to_rgb(&self) -> RGB { RGB::new((*self).r.to_channel(), (*self).g.to_channel(), (*self).b.to_channel()) } } impl ToHSV for RGB { #[inline] 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 rgb_u = self.to_rgb::(); let mx = rgb_u.r.max(&rgb_u.g).max(&rgb_u.b); let mn = rgb_u.r.min(&rgb_u.g).min(&rgb_u.b); let chr = mx - mn; if chr != zero!(U) { let h = cond! ( (rgb_u.r == mx) { ((rgb_u.g - rgb_u.b) / chr) % num::cast(6) } (rgb_u.g == mx) { ((rgb_u.b - rgb_u.r) / chr) + num::cast(2) } _ /* rgb_u.b == mx */ { ((rgb_u.r - rgb_u.g) / chr) + num::cast(4) } ) * num::cast(60); let s = chr / mx; HSV::new(h, s, mx) } else { HSV::new(zero!(U), zero!(U), mx) } } } #[deriving(Clone, Eq)] pub struct RGBA { r: T, g: T, b: T, a: T } impl_approx!(RGBA { r, g, b, a }) impl RGBA { #[inline] pub fn new(r: T, g: T, b: T, a: T) -> RGBA { RGBA { r: r, g: g, b: b, a: a } } #[inline] pub fn from_rgb_a(rgb: RGB, a: T) -> RGBA { unsafe { cast::transmute((rgb, a)) } } #[inline] pub fn rgb<'a>(&'a self) -> &'a RGB { unsafe { cast::transmute(self) } } #[inline] pub fn rgb_mut<'a>(&'a mut self) -> &'a mut RGB { unsafe { cast::transmute(self) } } } impl Color for RGBA { /// Clamps the components of the color to the range `(lo,hi)`. #[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)) } /// Inverts the color. #[inline] pub fn inverse(&self) -> RGBA { RGBA::new((*self).r.invert_channel(), (*self).g.invert_channel(), (*self).b.invert_channel(), (*self).a.invert_channel()) } } impl FloatColor for RGBA { /// Normalizes the components of the color by clamping them to the range `(0,1)`. #[inline] pub fn normalize(&self) -> RGBA { RGBA::new((*self).r.normalize_channel(), (*self).g.normalize_channel(), (*self).b.normalize_channel(), (*self).a.normalize_channel()) } } pub trait ToRGBA { pub fn to_rgba(&self) -> RGBA; } impl ToRGBA for u32 { #[inline] pub fn to_rgba(&self) -> RGBA { fail!("Not yet implemented") } } impl ToRGBA for u64 { #[inline] pub fn to_rgba(&self) -> RGBA { fail!("Not yet implemented") } } impl ToRGBA for (C, T) { #[inline] pub fn to_rgba(&self) -> RGBA { match *self { (ref rgb, ref a) => { RGBA::from_rgb_a(rgb.to_rgb(), a.to_channel()) } } } } impl ToRGBA for RGBA { #[inline] pub fn to_rgba(&self) -> RGBA { RGBA::new((*self).r.to_channel(), (*self).g.to_channel(), (*self).b.to_channel(), (*self).a.to_channel()) } } impl ToHSVA for RGBA { #[inline] pub fn to_hsva(&self) -> HSVA { HSVA::from_hsv_a(self.rgb().to_hsv(), (*self).a.to_channel()) } } /// SVG 1.0 color constants: http://www.w3.org/TR/SVG/types.html#ColorKeywords pub mod consts { use color::RGB; static ALICEBLUE: RGB = RGB { r: 0xF0, g: 0xF8, b: 0xFF }; static ANTIQUEWHITE: RGB = RGB { r: 0xFA, g: 0xEB, b: 0xD7 }; static AQUA: RGB = RGB { r: 0x00, g: 0xFF, b: 0xFF }; static AQUAMARINE: RGB = RGB { r: 0x7F, g: 0xFF, b: 0xD4 }; static AZURE: RGB = RGB { r: 0xF0, g: 0xFF, b: 0xFF }; static BEIGE: RGB = RGB { r: 0xF5, g: 0xF5, b: 0xDC }; static BISQUE: RGB = RGB { r: 0xFF, g: 0xE4, b: 0xC4 }; static BLACK: RGB = RGB { r: 0x00, g: 0x00, b: 0x00 }; static BLANCHEDALMOND: RGB = RGB { r: 0xFF, g: 0xEB, b: 0xCD }; static BLUE: RGB = RGB { r: 0x00, g: 0x00, b: 0xFF }; static BLUEVIOLET: RGB = RGB { r: 0x8A, g: 0x2B, b: 0xE2 }; static BROWN: RGB = RGB { r: 0xA5, g: 0x2A, b: 0x2A }; static BURLYWOOD: RGB = RGB { r: 0xDE, g: 0xB8, b: 0x87 }; static CADETBLUE: RGB = RGB { r: 0x5F, g: 0x9E, b: 0xA0 }; static CHARTREUSE: RGB = RGB { r: 0x7F, g: 0xFF, b: 0x00 }; static CHOCOLATE: RGB = RGB { r: 0xD2, g: 0x69, b: 0x1E }; static CORAL: RGB = RGB { r: 0xFF, g: 0x7F, b: 0x50 }; static CORNFLOWERBLUE: RGB = RGB { r: 0x64, g: 0x95, b: 0xED }; static CORNSILK: RGB = RGB { r: 0xFF, g: 0xF8, b: 0xDC }; static CRIMSON: RGB = RGB { r: 0xDC, g: 0x14, b: 0x3C }; static CYAN: RGB = RGB { r: 0x00, g: 0xFF, b: 0xFF }; static DARKBLUE: RGB = RGB { r: 0x00, g: 0x00, b: 0x8B }; static DARKCYAN: RGB = RGB { r: 0x00, g: 0x8B, b: 0x8B }; static DARKGOLDENROD: RGB = RGB { r: 0xB8, g: 0x86, b: 0x0B }; static DARKGRAY: RGB = RGB { r: 0xA9, g: 0xA9, b: 0xA9 }; static DARKGREEN: RGB = RGB { r: 0x00, g: 0x64, b: 0x00 }; static DARKKHAKI: RGB = RGB { r: 0xBD, g: 0xB7, b: 0x6B }; static DARKMAGENTA: RGB = RGB { r: 0x8B, g: 0x00, b: 0x8B }; static DARKOLIVEGREEN: RGB = RGB { r: 0x55, g: 0x6B, b: 0x2F }; static DARKORANGE: RGB = RGB { r: 0xFF, g: 0x8C, b: 0x00 }; static DARKORCHID: RGB = RGB { r: 0x99, g: 0x32, b: 0xCC }; static DARKRED: RGB = RGB { r: 0x8B, g: 0x00, b: 0x00 }; static DARKSALMON: RGB = RGB { r: 0xE9, g: 0x96, b: 0x7A }; static DARKSEAGREEN: RGB = RGB { r: 0x8F, g: 0xBC, b: 0x8F }; static DARKSLATEBLUE: RGB = RGB { r: 0x48, g: 0x3D, b: 0x8B }; static DARKSLATEGRAY: RGB = RGB { r: 0x2F, g: 0x4F, b: 0x4F }; static DARKTURQUOISE: RGB = RGB { r: 0x00, g: 0xCE, b: 0xD1 }; static DARKVIOLET: RGB = RGB { r: 0x94, g: 0x00, b: 0xD3 }; static DEEPPINK: RGB = RGB { r: 0xFF, g: 0x14, b: 0x93 }; static DEEPSKYBLUE: RGB = RGB { r: 0x00, g: 0xBF, b: 0xFF }; static DIMGRAY: RGB = RGB { r: 0x69, g: 0x69, b: 0x69 }; static DODGERBLUE: RGB = RGB { r: 0x1E, g: 0x90, b: 0xFF }; static FIREBRICK: RGB = RGB { r: 0xB2, g: 0x22, b: 0x22 }; static FLORALWHITE: RGB = RGB { r: 0xFF, g: 0xFA, b: 0xF0 }; static FORESTGREEN: RGB = RGB { r: 0x22, g: 0x8B, b: 0x22 }; static FUCHSIA: RGB = RGB { r: 0xFF, g: 0x00, b: 0xFF }; static GAINSBORO: RGB = RGB { r: 0xDC, g: 0xDC, b: 0xDC }; static GHOSTWHITE: RGB = RGB { r: 0xF8, g: 0xF8, b: 0xFF }; static GOLD: RGB = RGB { r: 0xFF, g: 0xD7, b: 0x00 }; static GOLDENROD: RGB = RGB { r: 0xDA, g: 0xA5, b: 0x20 }; static GRAY: RGB = RGB { r: 0x80, g: 0x80, b: 0x80 }; static GREEN: RGB = RGB { r: 0x00, g: 0x80, b: 0x00 }; static GREENYELLOW: RGB = RGB { r: 0xAD, g: 0xFF, b: 0x2F }; static HONEYDEW: RGB = RGB { r: 0xF0, g: 0xFF, b: 0xF0 }; static HOTPINK: RGB = RGB { r: 0xFF, g: 0x69, b: 0xB4 }; static INDIANRED: RGB = RGB { r: 0xCD, g: 0x5C, b: 0x5C }; static INDIGO: RGB = RGB { r: 0x4B, g: 0x00, b: 0x82 }; static IVORY: RGB = RGB { r: 0xFF, g: 0xFF, b: 0xF0 }; static KHAKI: RGB = RGB { r: 0xF0, g: 0xE6, b: 0x8C }; static LAVENDER: RGB = RGB { r: 0xE6, g: 0xE6, b: 0xFA }; static LAVENDERBLUSH: RGB = RGB { r: 0xFF, g: 0xF0, b: 0xF5 }; static LAWNGREEN: RGB = RGB { r: 0x7C, g: 0xFC, b: 0x00 }; static LEMONCHIFFON: RGB = RGB { r: 0xFF, g: 0xFA, b: 0xCD }; static LIGHTBLUE: RGB = RGB { r: 0xAD, g: 0xD8, b: 0xE6 }; static LIGHTCORAL: RGB = RGB { r: 0xF0, g: 0x80, b: 0x80 }; static LIGHTCYAN: RGB = RGB { r: 0xE0, g: 0xFF, b: 0xFF }; static LIGHTGOLDENRODYELLOW: RGB = RGB { r: 0xFA, g: 0xFA, b: 0xD2 }; static LIGHTGREEN: RGB = RGB { r: 0x90, g: 0xEE, b: 0x90 }; static LIGHTGREY: RGB = RGB { r: 0xD3, g: 0xD3, b: 0xD3 }; static LIGHTPINK: RGB = RGB { r: 0xFF, g: 0xB6, b: 0xC1 }; static LIGHTSALMON: RGB = RGB { r: 0xFF, g: 0xA0, b: 0x7A }; static LIGHTSEAGREEN: RGB = RGB { r: 0x20, g: 0xB2, b: 0xAA }; static LIGHTSKYBLUE: RGB = RGB { r: 0x87, g: 0xCE, b: 0xFA }; static LIGHTSLATEGRAY: RGB = RGB { r: 0x77, g: 0x88, b: 0x99 }; static LIGHTSTEELBLUE: RGB = RGB { r: 0xB0, g: 0xC4, b: 0xDE }; static LIGHTYELLOW: RGB = RGB { r: 0xFF, g: 0xFF, b: 0xE0 }; static LIME: RGB = RGB { r: 0x00, g: 0xFF, b: 0x00 }; static LIMEGREEN: RGB = RGB { r: 0x32, g: 0xCD, b: 0x32 }; static LINEN: RGB = RGB { r: 0xFA, g: 0xF0, b: 0xE6 }; static MAGENTA: RGB = RGB { r: 0xFF, g: 0x00, b: 0xFF }; static MAROON: RGB = RGB { r: 0x80, g: 0x00, b: 0x00 }; static MEDIUMAQUAMARINE: RGB = RGB { r: 0x66, g: 0xCD, b: 0xAA }; static MEDIUMBLUE: RGB = RGB { r: 0x00, g: 0x00, b: 0xCD }; static MEDIUMORCHID: RGB = RGB { r: 0xBA, g: 0x55, b: 0xD3 }; static MEDIUMPURPLE: RGB = RGB { r: 0x93, g: 0x70, b: 0xDB }; static MEDIUMSEAGREEN: RGB = RGB { r: 0x3C, g: 0xB3, b: 0x71 }; static MEDIUMSLATEBLUE: RGB = RGB { r: 0x7B, g: 0x68, b: 0xEE }; static MEDIUMSPRINGGREEN: RGB = RGB { r: 0x00, g: 0xFA, b: 0x9A }; static MEDIUMTURQUOISE: RGB = RGB { r: 0x48, g: 0xD1, b: 0xCC }; static MEDIUMVIOLETRED: RGB = RGB { r: 0xC7, g: 0x15, b: 0x85 }; static MIDNIGHTBLUE: RGB = RGB { r: 0x19, g: 0x19, b: 0x70 }; static MINTCREAM: RGB = RGB { r: 0xF5, g: 0xFF, b: 0xFA }; static MISTYROSE: RGB = RGB { r: 0xFF, g: 0xE4, b: 0xE1 }; static MOCCASIN: RGB = RGB { r: 0xFF, g: 0xE4, b: 0xB5 }; static NAVAJOWHITE: RGB = RGB { r: 0xFF, g: 0xDE, b: 0xAD }; static NAVY: RGB = RGB { r: 0x00, g: 0x00, b: 0x80 }; static OLDLACE: RGB = RGB { r: 0xFD, g: 0xF5, b: 0xE6 }; static OLIVE: RGB = RGB { r: 0x80, g: 0x80, b: 0x00 }; static OLIVEDRAB: RGB = RGB { r: 0x6B, g: 0x8E, b: 0x23 }; static ORANGE: RGB = RGB { r: 0xFF, g: 0xA5, b: 0x00 }; static ORANGERED: RGB = RGB { r: 0xFF, g: 0x45, b: 0x00 }; static ORCHID: RGB = RGB { r: 0xDA, g: 0x70, b: 0xD6 }; static PALEGOLDENROD: RGB = RGB { r: 0xEE, g: 0xE8, b: 0xAA }; static PALEGREEN: RGB = RGB { r: 0x98, g: 0xFB, b: 0x98 }; static PALEVIOLETRED: RGB = RGB { r: 0xDB, g: 0x70, b: 0x93 }; static PAPAYAWHIP: RGB = RGB { r: 0xFF, g: 0xEF, b: 0xD5 }; static PEACHPUFF: RGB = RGB { r: 0xFF, g: 0xDA, b: 0xB9 }; static PERU: RGB = RGB { r: 0xCD, g: 0x85, b: 0x3F }; static PINK: RGB = RGB { r: 0xFF, g: 0xC0, b: 0xCB }; static PLUM: RGB = RGB { r: 0xDD, g: 0xA0, b: 0xDD }; static POWDERBLUE: RGB = RGB { r: 0xB0, g: 0xE0, b: 0xE6 }; static PURPLE: RGB = RGB { r: 0x80, g: 0x00, b: 0x80 }; static RED: RGB = RGB { r: 0xFF, g: 0x00, b: 0x00 }; static ROSYBROWN: RGB = RGB { r: 0xBC, g: 0x8F, b: 0x8F }; static ROYALBLUE: RGB = RGB { r: 0x41, g: 0x69, b: 0xE1 }; static SADDLEBROWN: RGB = RGB { r: 0x8B, g: 0x45, b: 0x13 }; static SALMON: RGB = RGB { r: 0xFA, g: 0x80, b: 0x72 }; static SANDYBROWN: RGB = RGB { r: 0xFA, g: 0xA4, b: 0x60 }; static SEAGREEN: RGB = RGB { r: 0x2E, g: 0x8B, b: 0x57 }; static SEASHELL: RGB = RGB { r: 0xFF, g: 0xF5, b: 0xEE }; static SIENNA: RGB = RGB { r: 0xA0, g: 0x52, b: 0x2D }; static SILVER: RGB = RGB { r: 0xC0, g: 0xC0, b: 0xC0 }; static SKYBLUE: RGB = RGB { r: 0x87, g: 0xCE, b: 0xEB }; static SLATEBLUE: RGB = RGB { r: 0x6A, g: 0x5A, b: 0xCD }; static SLATEGRAY: RGB = RGB { r: 0x70, g: 0x80, b: 0x90 }; static SNOW: RGB = RGB { r: 0xFF, g: 0xFA, b: 0xFA }; static SPRINGGREEN: RGB = RGB { r: 0x00, g: 0xFF, b: 0x7F }; static STEELBLUE: RGB = RGB { r: 0x46, g: 0x82, b: 0xB4 }; static TAN: RGB = RGB { r: 0xD2, g: 0xB4, b: 0x8C }; static TEAL: RGB = RGB { r: 0x00, g: 0x80, b: 0x80 }; static THISTLE: RGB = RGB { r: 0xD8, g: 0xBF, b: 0xD8 }; static TOMATO: RGB = RGB { r: 0xFF, g: 0x63, b: 0x47 }; static TURQUOISE: RGB = RGB { r: 0x40, g: 0xE0, b: 0xD0 }; static VIOLET: RGB = RGB { r: 0xEE, g: 0x82, b: 0xEE }; static WHEAT: RGB = RGB { r: 0xF5, g: 0xDE, b: 0xB3 }; static WHITE: RGB = RGB { r: 0xFF, g: 0xFF, b: 0xFF }; static WHITESMOKE: RGB = RGB { r: 0xF5, g: 0xF5, b: 0xF5 }; static YELLOW: RGB = RGB { r: 0xFF, g: 0xFF, b: 0x00 }; static YELLOWGREEN: RGB = RGB { r: 0x9A, g: 0xCD, b: 0x32 }; } #[cfg(test)] mod tests { use color::*; #[test] fn test_rgb_to_rgb() { assert_eq!(RGB::new::(0xA0, 0xA0, 0xA0).to_rgb::(), RGB::new::(0xA0, 0xA0, 0xA0)); assert_eq!(RGB::new::(0xA0, 0xA0, 0xA0).to_rgb::(), RGB::new::(0xA0A0, 0xA0A0, 0xA0A0)); } #[test] fn test_rgb_to_hsv() { assert_eq!(RGB::new::(0xFF, 0xFF, 0xFF).to_hsv::(), HSV::new::(0.0, 0.0, 1.0)); assert_eq!(RGB::new::(0x99, 0x00, 0x00).to_hsv::(), HSV::new::(0.0, 1.0, 0.6)); assert_eq!(RGB::new::(0x00, 0x99, 0x00).to_hsv::(), HSV::new::(120.0, 1.0, 0.6)); assert_eq!(RGB::new::(0x00, 0x00, 0x99).to_hsv::(), HSV::new::(240.0, 1.0, 0.6)); } #[test] fn test_tuple_to_rgba() { assert_eq!((RGB::new::(1.0, 1.0, 1.0), 0xFFu8).to_rgba::(), RGBA::new::(1.0, 1.0, 1.0, 1.0)); assert_eq!((RGB::new::(1.0, 1.0, 1.0), 0xFFu8).to_rgba::(), RGBA::new::(1.0, 1.0, 1.0, 1.0)); assert_eq!((RGB::new::(1.0, 1.0, 1.0), 0xFFu8).to_rgba::(), RGBA::new::(1.0, 1.0, 1.0, 1.0)); assert_eq!((RGB::new::(1.0, 1.0, 1.0), 0xFFu8).to_rgba::(), RGBA::new::(1.0, 1.0, 1.0, 1.0)); } #[test] fn test_rgba_to_rgba() { assert_eq!(RGBA::new::(0xA0, 0xA0, 0xA0, 0xA0).to_rgba::(), RGBA::new::(0xA0, 0xA0, 0xA0, 0xA0)); assert_eq!(RGBA::new::(0xA0, 0xA0, 0xA0, 0xA0).to_rgba::(), RGBA::new::(0xA0A0, 0xA0A0, 0xA0A0, 0xA0A0)); } #[test] fn test_rgba_to_hsva() { assert_eq!(RGBA::new::(0xFF, 0xFF, 0xFF, 0xFF).to_hsva::(), HSVA::new::(0.0, 0.0, 1.0, 1.0)); assert_eq!(RGBA::new::(0x99, 0x00, 0x00, 0xFF).to_hsva::(), HSVA::new::(0.0, 1.0, 0.6, 1.0)); assert_eq!(RGBA::new::(0x00, 0x99, 0x00, 0xFF).to_hsva::(), HSVA::new::(120.0, 1.0, 0.6, 1.0)); assert_eq!(RGBA::new::(0x00, 0x00, 0x99, 0xFF).to_hsva::(), HSVA::new::(240.0, 1.0, 0.6, 1.0)); } }