1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
use super::{IsColorChannel, IsColor, HasAlpha, HasntAlpha, Hsva, Hsl, Rgb}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Hsv<T> { pub hue: T, pub saturation: T, pub value: T, } impl<T: IsColorChannel> IsColor for Hsv<T> { type Channel = T; } impl<T: IsColorChannel> HasntAlpha for Hsv<T> { type Alphaful = Hsva<T>; fn with_alpha(self, alpha: Self::Channel) -> Self::Alphaful { let Hsv { hue, saturation, value } = self; Hsva { hue, saturation, value, alpha } } } impl<T> Hsv<T> { pub fn new(hue: T, saturation: T, value: T) -> Self { Self { hue, saturation, value } } } impl<T: IsColorChannel> Default for Hsv<T> { fn default() -> Self { Self::new(T::MIN, T::MIN, T::MIN) } } impl<T: IsColorChannel> From<Hsva<T>> for Hsv<T> { fn from(hsva: Hsva<T>) -> Self { hsva.without_alpha() } } impl From<Hsv<u8>> for Hsv<f32> { fn from(Hsv { hue, saturation, value }: Hsv<u8>) -> Self { Self::new( hue as f32 / 255.0, saturation as f32 / 255.0, value as f32 / 255.0, ) } } impl From<Hsv<f32>> for Hsv<u8> { fn from(Hsv { hue, saturation, value }: Hsv<f32>) -> Self { Self::new( (hue.clamp_channel() * 255.0).round() as u8, (saturation.clamp_channel() * 255.0).round() as u8, (value.clamp_channel() * 255.0).round() as u8, ) } } impl From<Hsl<f32>> for Hsv<f32> { fn from(Hsl { hue, saturation, lightness }: Hsl<f32>) -> Self { let lightness = lightness * 2.0; let saturation = saturation * if lightness > 1.0 { 2.0 - lightness } else { lightness }; let lightness_plus_saturation = lightness + saturation; let value = lightness_plus_saturation * 0.5; let saturation = 2.0 * saturation / lightness_plus_saturation; Self { hue, saturation, value } } } impl From<Rgb<f32>> for Hsv<f32> { fn from(Rgb { red, green, blue }: Rgb<f32>) -> Self { let max = 0.0f32.max(red).max(green).max(blue); let min = 1.0f32.min(red).min(green).min(blue); if max > 0.0 { let value = max; let delta = max - min; let saturation = delta / max; let hue = if delta != 0.0 { ( if red == max { (green - blue) / delta } else if green == max { 2.0 + (blue - red) / delta } else { 4.0 + (red - green) / delta } * 1.0 / 6.0 ).unwind_channel() } else { 0.0 }; Self { hue, saturation, value } } else { Self { hue: 0.0, saturation: 0.0, value: 0.0 } } } } #[cfg(test)] mod test { use super::*; #[test] fn from_rgb() { assert_eq!(Hsv::<u8>::from(Hsv::from(Rgb::<f32>::from(Rgb::new(84u8, 37, 181)))), Hsv::new(184u8, 203, 181)); } #[test] fn from_hsl() { assert_eq!(Hsv::<u8>::from(Hsv::from(Hsl::<f32>::from(Hsl::new(184u8, 169, 109)))), Hsv::new(184u8, 203, 181)); } }