// 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::{RGB, ToRGB, RGBA, ToRGBA}; #[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 } } } impl Color for HSV { /// Clamps the components of the color to the range `(lo,hi)`. #[inline] pub fn clamp(&self, lo: T, hi: T) -> HSV { HSV::new((*self).h.clamp(&lo, &hi), // Should the hue component be clamped? (*self).s.clamp(&lo, &hi), (*self).v.clamp(&lo, &hi)) } /// Inverts the color. #[inline] pub fn inverse(&self) -> HSV { HSV::new((*self).h.invert_degrees(), (*self).s.invert_channel(), (*self).v.invert_channel()) } } impl FloatColor for HSV { /// Normalizes the components of the color. Modulo `360` is applied to the /// `h` component, and `s` and `v` are clamped to the range `(0,1)`. #[inline] pub fn normalize(&self) -> HSV { HSV::new((*self).h.normalize_degrees(), (*self).s.normalize_channel(), (*self).v.normalize_channel()) } } pub trait ToHSV { pub fn to_hsv(&self) -> HSV; } impl ToHSV for u32 { #[inline] pub fn to_hsv(&self) -> HSV { fail!("Not yet implemented") } } impl ToHSV for u64 { #[inline] pub fn to_hsv(&self) -> HSV { fail!("Not yet implemented") } } impl ToHSV for HSV { #[inline] pub fn to_hsv(&self) -> HSV { HSV::new((*self).h.to_channel(), (*self).s.to_channel(), (*self).v.to_channel()) } } 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 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; rgb.r = rgb.r + mn; rgb.g = rgb.g + mn; rgb.b = rgb.b + mn; rgb.to_rgb::() } } #[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 } } #[inline] pub fn from_hsv_a(hsv: HSV, a: T) -> HSVA { unsafe { cast::transmute((hsv, a)) } } #[inline] pub fn hsv<'a>(&'a self) -> &'a HSV { unsafe { cast::transmute(self) } } #[inline] pub fn hsv_mut<'a>(&'a mut self) -> &'a mut HSV { unsafe { cast::transmute(self) } } } impl Color for HSVA { /// Clamps the components of the color to the range `(lo,hi)`. #[inline] pub fn clamp(&self, lo: T, hi: T) -> HSVA { HSVA::new((*self).h.clamp(&lo, &hi), // Should the hue component be clamped? (*self).s.clamp(&lo, &hi), (*self).v.clamp(&lo, &hi), (*self).a.clamp(&lo, &hi)) } /// Inverts the color. #[inline] pub fn inverse(&self) -> HSVA { HSVA::new((*self).h.invert_degrees(), (*self).s.invert_channel(), (*self).v.invert_channel(), (*self).a.invert_channel()) } } impl FloatColor for HSVA { /// Normalizes the components of the color. Modulo `360` is applied to the /// `h` component, and `s`, `v` and `a` are clamped to the range `(0,1)`. #[inline] pub fn normalize(&self) -> HSVA { HSVA::new((*self).h.normalize_degrees(), (*self).s.normalize_channel(), (*self).v.normalize_channel(), (*self).a.normalize_channel()) } } pub trait ToHSVA { pub fn to_hsva(&self) -> HSVA; } impl ToHSVA for u32 { #[inline] pub fn to_hsva(&self) -> HSVA { fail!("Not yet implemented") } } impl ToHSVA for u64 { #[inline] pub fn to_hsva(&self) -> HSVA { fail!("Not yet implemented") } } impl ToHSVA for (C, T) { #[inline] pub fn to_hsva(&self) -> HSVA { match *self { (ref hsv, ref a) => { HSVA::from_hsv_a(hsv.to_hsv(), a.to_channel()) } } } } impl ToHSVA for HSVA { #[inline] pub fn to_hsva(&self) -> HSVA { HSVA::new((*self).h.to_channel(), (*self).s.to_channel(), (*self).v.to_channel(), (*self).a.to_channel()) } } impl ToRGBA for HSVA { #[inline] pub fn to_rgba(&self) -> RGBA { RGBA::from_rgb_a(self.hsv().to_rgb(), (*self).a.to_channel()) } } #[cfg(test)] mod tests { use color::*; #[test] fn test_hsv_to_hsv() { assert_eq!(HSV::new::(0.0, 0.0, 1.0).to_hsv::(), HSV::new::(0.0, 0.0, 1.0)); assert_eq!(HSV::new::(0.0, 1.0, 0.6).to_hsv::(), HSV::new::(0.0, 1.0, 0.6)); assert_eq!(HSV::new::(120.0, 1.0, 0.6).to_hsv::(), HSV::new::(120.0, 1.0, 0.6)); assert_eq!(HSV::new::(240.0, 1.0, 0.6).to_hsv::(), HSV::new::(240.0, 1.0, 0.6)); } #[test] fn test_hsv_to_rgb() { assert_eq!(HSV::new::(0.0, 0.0, 1.0).to_rgb::(), RGB::new::(0xFF, 0xFF, 0xFF)); assert_eq!(HSV::new::(0.0, 1.0, 0.6).to_rgb::(), RGB::new::(0x99, 0x00, 0x00)); assert_eq!(HSV::new::(120.0, 1.0, 0.6).to_rgb::(), RGB::new::(0x00, 0x99, 0x00)); assert_eq!(HSV::new::(240.0, 1.0, 0.6).to_rgb::(), RGB::new::(0x00, 0x00, 0x99)); } #[test] fn test_tuple_to_hsva() { assert_eq!((RGB::new::(0xFF, 0xFF, 0xFF), 0.5f32).to_hsva::(), HSVA::new::(0.0, 0.0, 1.0, 0.5)); assert_eq!((RGB::new::(0x99, 0x00, 0x00), 0.5f32).to_hsva::(), HSVA::new::(0.0, 1.0, 0.6, 0.5)); assert_eq!((RGB::new::(0x00, 0x99, 0x00), 0.5f32).to_hsva::(), HSVA::new::(120.0, 1.0, 0.6, 0.5)); assert_eq!((RGB::new::(0x00, 0x00, 0x99), 0.5f32).to_hsva::(), HSVA::new::(240.0, 1.0, 0.6, 0.5)); } #[test] fn test_hsva_to_hsva() { assert_eq!(HSVA::new::(0.0, 0.0, 1.0, 0.5).to_hsva::(), HSVA::new::(0.0, 0.0, 1.0, 0.5)); assert_eq!(HSVA::new::(0.0, 1.0, 0.6, 0.5).to_hsva::(), HSVA::new::(0.0, 1.0, 0.6, 0.5)); assert_eq!(HSVA::new::(120.0, 1.0, 0.6, 0.5).to_hsva::(), HSVA::new::(120.0, 1.0, 0.6, 0.5)); assert_eq!(HSVA::new::(240.0, 1.0, 0.6, 0.5).to_hsva::(), HSVA::new::(240.0, 1.0, 0.6, 0.5)); } #[test] fn test_hsva_to_rgba() { assert_eq!(HSVA::new::(0.0, 0.0, 1.0, 0.5).to_rgba::(), RGBA::new::(0xFF, 0xFF, 0xFF, 0x7F)); assert_eq!(HSVA::new::(0.0, 1.0, 0.6, 0.5).to_rgba::(), RGBA::new::(0x99, 0x00, 0x00, 0x7F)); assert_eq!(HSVA::new::(120.0, 1.0, 0.6, 0.5).to_rgba::(), RGBA::new::(0x00, 0x99, 0x00, 0x7F)); assert_eq!(HSVA::new::(240.0, 1.0, 0.6, 0.5).to_rgba::(), RGBA::new::(0x00, 0x00, 0x99, 0x7F)); } }