1use crate::{Alpha, Gamut, LinearRgb, Number, Srgb};
2use core::fmt;
3
4impl Srgb {
5 pub fn from_u8(red: u8, green: u8, blue: u8, alpha: f32) -> Self {
7 Self::new(red as f64 / 255.0, green as f64 / 255.0, blue as f64 / 255.0, alpha)
8 }
9
10 pub fn to_u8(&self) -> [u8; 3] {
12 [self.red, self.green, self.blue].map(|channel| (channel * 255.0).round().clamp(0.0, 255.0) as u8)
13 }
14
15 pub(crate) fn hue(&self) -> f64 {
16 let Self { red, green, blue, .. } = *self;
17 let max = red.max(green).max(blue);
18 let delta = max - red.min(green).min(blue);
19 if delta <= 1e-9 {
20 return 0.0;
21 }
22 let sextant = if max == red {
23 (green - blue) / delta + if green < blue { 6.0 } else { 0.0 }
24 } else if max == green {
25 (blue - red) / delta + 2.0
26 } else {
27 (red - green) / delta + 4.0
28 };
29 sextant * 60.0
30 }
31
32 fn legacy_alpha(&self) -> f64 {
33 let byte = self.alpha as f64 * 2.55;
34 let rounded = byte.round();
35 if (byte - rounded).abs() >= 1e-3 {
36 return self.alpha as f64 / 100.0;
37 }
38 let byte = rounded as u32;
39 let percent = (byte * 100 + 127) / 255;
40 if (percent * 255 + 50) / 100 == byte {
41 percent as f64 / 100.0
42 } else {
43 ((byte * 1000 + 127) / 255) as f64 / 1000.0
44 }
45 }
46}
47
48impl fmt::Display for Srgb {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 if !self.in_gamut() {
51 let Self { red, green, blue, alpha } = *self;
52 return write!(f, "color(srgb {} {} {}{})", Number(red), Number(green), Number(blue), Alpha(alpha));
53 }
54 let [red, green, blue] = self.to_u8();
55 if self.alpha < 100.0 {
56 write!(f, "rgba({red}, {green}, {blue}, {})", Number(self.legacy_alpha()))
57 } else {
58 write!(f, "rgb({red}, {green}, {blue})")
59 }
60 }
61}
62
63impl From<Srgb> for LinearRgb {
64 fn from(value: Srgb) -> Self {
65 let Srgb { red, green, blue, alpha } = value;
66 let [red, green, blue] = Srgb::SPACE.transfer.decode_all([red, green, blue]);
67 LinearRgb::new(red, green, blue, alpha)
68 }
69}
70
71impl From<LinearRgb> for Srgb {
72 fn from(value: LinearRgb) -> Self {
73 let LinearRgb { red, green, blue, alpha } = value;
74 let [red, green, blue] = Srgb::SPACE.transfer.encode_all([red, green, blue]);
75 Srgb::new(red, green, blue, alpha)
76 }
77}
78
79#[cfg(feature = "anstyle")]
80impl From<Srgb> for anstyle::RgbColor {
81 fn from(value: Srgb) -> Self {
82 let [red, green, blue] = value.to_u8();
83 anstyle::RgbColor(red, green, blue)
84 }
85}
86
87#[cfg(feature = "anstyle")]
88impl From<anstyle::RgbColor> for Srgb {
89 fn from(value: anstyle::RgbColor) -> Self {
90 Srgb::from_u8(value.0, value.1, value.2, 100.0)
91 }
92}
93
94#[cfg(feature = "owo-colors")]
95impl From<Srgb> for owo_colors::Rgb {
96 fn from(value: Srgb) -> Self {
97 let [red, green, blue] = value.to_u8();
98 owo_colors::Rgb(red, green, blue)
99 }
100}
101
102#[cfg(feature = "owo-colors")]
103impl From<owo_colors::Rgb> for Srgb {
104 fn from(value: owo_colors::Rgb) -> Self {
105 Srgb::from_u8(value.0, value.1, value.2, 100.0)
106 }
107}