palette/rgb/
rgb.rs

1use core::{
2    any::TypeId,
3    fmt,
4    fmt::Debug,
5    marker::PhantomData,
6    num::ParseIntError,
7    ops::{Add, Div},
8    str::FromStr,
9};
10
11use crate::{
12    alpha::Alpha,
13    angle::{RealAngle, UnsignedAngle},
14    bool_mask::{BitOps, HasBoolMask, LazySelect},
15    cast::{ComponentOrder, Packed},
16    color_difference::Wcag21RelativeContrast,
17    convert::{FromColorUnclamped, IntoColorUnclamped},
18    encoding::{FromLinear, IntoLinear, Linear, Srgb},
19    luma::LumaStandard,
20    matrix::{matrix_inverse, matrix_map, multiply_xyz_to_rgb, rgb_to_xyz_matrix},
21    num::{
22        Abs, Arithmetics, FromScalar, IsValidDivisor, MinMax, One, PartialCmp, Real, Recip, Round,
23        Trigonometry, Zero,
24    },
25    oklab::oklab_to_linear_srgb,
26    rgb::{RgbSpace, RgbStandard},
27    stimulus::{FromStimulus, Stimulus, StimulusColor},
28    white_point::{Any, WhitePoint, D65},
29    FromColor, GetHue, Hsl, Hsv, IntoColor, Luma, Oklab, RgbHue, Xyz, Yxy,
30};
31
32use super::Primaries;
33
34/// Generic RGB with an alpha component. See the [`Rgba` implementation in
35/// `Alpha`](crate::Alpha#Rgba).
36pub type Rgba<S = Srgb, T = f32> = Alpha<Rgb<S, T>, T>;
37
38/// Generic RGB.
39///
40/// RGB is probably the most common color space, when it comes to computer
41/// graphics, and it's defined as an additive mixture of red, green and blue
42/// light, where gray scale colors are created when these three channels are
43/// equal in strength.
44///
45/// # Creating a Value
46///
47/// RGB comes in different shapes and formats. You will probably want to start
48/// with either the [`Srgb`](crate::Srgb) or [`Srgba`](crate::Srgba) alias,
49/// which represents the common sRGB format that most images and tools use.
50/// Then, depending on your input, you can either just call [`new`](Rgb::new) or
51/// convert from another data format.
52///
53/// ```
54/// use palette::Srgb;
55///
56/// let rgb_u8 = Srgb::new(171u8, 193, 35);
57/// let rgb_f32 = Srgb::new(0.3f32, 0.8, 0.1);
58///
59/// // `new` is also `const`:
60/// const RGB_U8: Srgb<u8> = Srgb::new(171, 193, 35);
61///
62/// // Converting from one number format to another can be as simple as this:
63/// let rgb_u8_from_f32_1: Srgb<u8> = Srgb::new(0.3f32, 0.8, 0.1).into();
64///
65/// // ...or more explicitly like this:
66/// let rgb_u8_from_f32_2 = Srgb::new(0.3f32, 0.8, 0.1).into_format::<u8>();
67///
68/// // Hexadecimal is also supported, with or without the #:
69/// let rgb_from_hex1: Srgb<u8> = "#f034e6".parse().unwrap();
70/// let rgb_from_hex2: Srgb<u8> = "f034e6".parse().unwrap();
71/// assert_eq!(rgb_from_hex1, rgb_from_hex2);
72///
73/// // This includes the shorthand format:
74/// let rgb_from_short_hex: Srgb<u8> = "f3e".parse().unwrap();
75/// let rgb_from_long_hex: Srgb<u8> = "ff33ee".parse().unwrap();
76/// assert_eq!(rgb_from_short_hex, rgb_from_long_hex);
77///
78/// // It's also possible to convert from (and to) arrays, tuples and `u32` values:
79/// let rgb_from_array = Srgb::from([171u8, 193, 35]);
80/// let rgb_from_tuple = Srgb::from((171u8, 193, 35));
81/// let rgb_from_u32 = Srgb::from(0x607F00);
82/// ```
83///
84/// # Linear, sRGB and Gamma Correction
85///
86/// Many conversions and operations on RGB require that it's linear, meaning
87/// that gamma correction is required when converting to and from displayable
88/// RGB, such as sRGB. It's common to store and send RGB values where the
89/// numbers are on a non-linear scale. In a non-linear format, a value of, for
90/// example, 0.5 would not represent a light intensity of 50%, which makes some
91/// operations (such as blurring) give incorrect results.
92///
93/// You will probably encounter or use [`LinSrgb`](crate::LinSrgb) or
94/// [`LinSrgba`](crate::LinSrgba) at some point. These are aliases for linear
95/// sRGB and would usually be obtained by converting an [`Srgb`] value with
96/// [`into_linear`](Rgb::into_linear).
97///
98/// ```no_run
99/// use palette::{LinSrgb, Srgb};
100///
101/// // This function uses linear sRGB for something. But how do we interface with it?
102/// fn uses_linear_srgb(input: LinSrgb<f32>) -> LinSrgb<f32> { todo!() }
103///
104/// // Linear sRGB will usually be created from non-linear sRGB:
105/// let output = uses_linear_srgb(Srgb::new(0.3, 0.8, 0.1).into_linear());
106///
107/// // It's also possible to convert directly from u8 to f32 for sRGB.
108/// // This is much faster than using `into_format` first:
109/// let output = uses_linear_srgb(Srgb::new(171u8, 193, 35).into_linear());
110///
111/// // Converting the output back to `Srgb<u8>` (or `Srgb<f32>`) is just as simple:
112/// let output_u8 = Srgb::<u8>::from_linear(output);
113/// // ..or:
114/// let output_u8: Srgb<u8> = output.into_encoding();
115/// ```
116///
117/// It's of course also possible to create a linear value from constants, but
118/// it's not necessarily as intuitive. It's best to avoid storing them as
119/// `LinSrgb<u8>` (or `LinRgb<_, u8>`) values, to avoid banding among dark
120/// colors.
121///
122/// See the [`encoding`](crate::encoding) module for built-in encoding formats.
123///
124/// # Storage Formats and Pixel Buffers
125///
126/// It's common to read and write RGB values as bytes, hexadecimal strings, or
127/// sometimes `u32` values. A single RGB value can be converted to all of these
128/// formats and more.
129///
130/// ```no_run
131/// use palette::{Srgb, LinSrgb};
132///
133/// let source: LinSrgb<f32> = todo!();
134///
135/// let u8_array: [u8; 3] = Srgb::from_linear(source).into();
136/// let hex_string1 = format!("#{:x}", Srgb::<u8>::from_linear(source)); // The # is optional.
137/// let u32_value: u32 = Srgb::from_linear(source).into();
138/// ```
139///
140/// It's also possible to control the component order.
141/// [`PackedArgb`](crate::rgb::PackedArgb) is one of a few aliases for
142/// [`Packed`], which represents a color that has been "packed" into a specific
143/// data format. This can be a `u32` or `[u8; 4]`, for example. This is helpful
144/// for reading and writing colors with a different order than the default RGBA.
145///
146/// ```no_run
147/// use palette::{rgb::PackedArgb, Srgba, LinSrgba};
148///
149/// let source: LinSrgba<f32> = todo!();
150///
151/// let u8_array: [u8; 4] = PackedArgb::from(Srgba::from_linear(source)).into();
152/// let u32_value: u32 = PackedArgb::from(Srgba::from_linear(source)).into();
153/// ```
154///
155/// If you need to work with colors in a byte buffer, such as `[u8]`, `Vec<u8>`
156/// or the `image` crate, there's a quick way to borrow that buffer as a slice
157/// of RGB(A) colors. The [`cast`](crate::cast) module has a number of traits
158/// and functions for casting values without copying them.
159///
160/// ```no_run
161/// use image::RgbImage;
162/// use palette::{cast::ComponentsAsMut, Srgb};
163///
164/// let mut image: RgbImage = todo!();
165/// let pixels: &mut [Srgb<u8>] = image.components_as_mut();
166///
167/// for pixel in pixels {
168///     std::mem::swap(&mut pixel.red, &mut pixel.blue);
169/// }
170/// ```
171#[derive(Debug, ArrayCast, FromColorUnclamped, WithAlpha)]
172#[cfg_attr(feature = "serializing", derive(Serialize, Deserialize))]
173#[palette(
174    palette_internal,
175    rgb_standard = "S",
176    component = "T",
177    skip_derives(Xyz, Hsv, Hsl, Luma, Rgb, Oklab)
178)]
179#[repr(C)]
180pub struct Rgb<S = Srgb, T = f32> {
181    /// The amount of red light, where 0.0 is no red light and 1.0 (or 255u8) is
182    /// the highest displayable amount.
183    pub red: T,
184
185    /// The amount of green light, where 0.0 is no green light and 1.0 (or
186    /// 255u8) is the highest displayable amount.
187    pub green: T,
188
189    /// The amount of blue light, where 0.0 is no blue light and 1.0 (or 255u8)
190    /// is the highest displayable amount.
191    pub blue: T,
192
193    /// The kind of RGB standard. sRGB is the default.
194    #[cfg_attr(feature = "serializing", serde(skip))]
195    #[palette(unsafe_zero_sized)]
196    pub standard: PhantomData<S>,
197}
198
199impl<S, T> Rgb<S, T> {
200    /// Create an RGB color.
201    ///
202    /// It's possible to create a color in one number format and convert it to
203    /// another format with either [`into_format`](Rgb::into_format) or
204    /// [`into_linear`](Rgb::into_linear).
205    ///
206    /// ```
207    /// use palette::{Srgb, LinSrgb};
208    ///
209    /// // Changes only the number format:
210    /// let rgb_f32: Srgb<f32> =  Srgb::new(171u8, 193, 35).into_format();
211    ///
212    /// // Changes the number format and converts to linear in one go.
213    /// // This is faster than `.into_format().into_linear()`:
214    /// let linear: LinSrgb<f32> = Srgb::new(171u8, 193, 35).into_linear();
215    /// ```
216    pub const fn new(red: T, green: T, blue: T) -> Rgb<S, T> {
217        Rgb {
218            red,
219            green,
220            blue,
221            standard: PhantomData,
222        }
223    }
224
225    /// Convert the RGB components into another number type.
226    ///
227    /// ```
228    /// use palette::Srgb;
229    ///
230    /// let rgb_u8: Srgb<u8> = Srgb::new(0.3, 0.7, 0.2).into_format();
231    /// ```
232    ///
233    /// See also [`into_linear`](Rgb::into_linear) and
234    /// [`into_encoding`](Rgb::into_encoding) for a faster option if you need to
235    /// change between linear and non-linear encoding at the same time.
236    pub fn into_format<U>(self) -> Rgb<S, U>
237    where
238        U: FromStimulus<T>,
239    {
240        Rgb {
241            red: U::from_stimulus(self.red),
242            green: U::from_stimulus(self.green),
243            blue: U::from_stimulus(self.blue),
244            standard: PhantomData,
245        }
246    }
247
248    /// Convert the RGB components from another number type.
249    ///
250    /// ```
251    /// use palette::Srgb;
252    ///
253    /// let rgb_u8 = Srgb::<u8>::from_format(Srgb::new(0.3, 0.7, 0.2));
254    /// ```
255    ///
256    /// See also [`from_linear`](Rgb::from_linear) and
257    /// [`from_encoding`](Rgb::from_encoding) for a faster option if you need to
258    /// change between linear and non-linear encoding at the same time.
259    pub fn from_format<U>(color: Rgb<S, U>) -> Self
260    where
261        T: FromStimulus<U>,
262    {
263        color.into_format()
264    }
265
266    /// Convert to a `(red, green, blue)` tuple.
267    pub fn into_components(self) -> (T, T, T) {
268        (self.red, self.green, self.blue)
269    }
270
271    /// Convert from a `(red, green, blue)` tuple.
272    pub fn from_components((red, green, blue): (T, T, T)) -> Self {
273        Self::new(red, green, blue)
274    }
275}
276
277impl<S, T> Rgb<S, T>
278where
279    T: Stimulus,
280{
281    /// Return the `red` value minimum.
282    pub fn min_red() -> T {
283        T::zero()
284    }
285
286    /// Return the `red` value maximum.
287    pub fn max_red() -> T {
288        T::max_intensity()
289    }
290
291    /// Return the `green` value minimum.
292    pub fn min_green() -> T {
293        T::zero()
294    }
295
296    /// Return the `green` value maximum.
297    pub fn max_green() -> T {
298        T::max_intensity()
299    }
300
301    /// Return the `blue` value minimum.
302    pub fn min_blue() -> T {
303        T::zero()
304    }
305
306    /// Return the `blue` value maximum.
307    pub fn max_blue() -> T {
308        T::max_intensity()
309    }
310}
311
312impl<S> Rgb<S, u8> {
313    /// Convert to a packed `u32` with with specifiable component order.
314    ///
315    /// ```
316    /// use palette::{rgb, Srgb};
317    ///
318    /// let integer = Srgb::new(96u8, 127, 0).into_u32::<rgb::channels::Rgba>();
319    /// assert_eq!(0x607F00FF, integer);
320    /// ```
321    ///
322    /// It's also possible to use `From` and `Into`, which defaults to the
323    /// `0xAARRGGBB` component order:
324    ///
325    /// ```
326    /// use palette::Srgb;
327    ///
328    /// let integer = u32::from(Srgb::new(96u8, 127, 0));
329    /// assert_eq!(0xFF607F00, integer);
330    /// ```
331    ///
332    /// See [Packed](crate::cast::Packed) for more details.
333    #[inline]
334    pub fn into_u32<O>(self) -> u32
335    where
336        O: ComponentOrder<Rgba<S, u8>, u32>,
337    {
338        O::pack(Rgba::from(self))
339    }
340
341    /// Convert from a packed `u32` with specifiable component order.
342    ///
343    /// ```
344    /// use palette::{rgb, Srgb};
345    ///
346    /// let rgb = Srgb::from_u32::<rgb::channels::Rgba>(0x607F00FF);
347    /// assert_eq!(Srgb::new(96u8, 127, 0), rgb);
348    /// ```
349    ///
350    /// It's also possible to use `From` and `Into`, which defaults to the
351    /// `0xAARRGGBB` component order:
352    ///
353    /// ```
354    /// use palette::Srgb;
355    ///
356    /// let rgb = Srgb::from(0x607F00);
357    /// assert_eq!(Srgb::new(96u8, 127, 0), rgb);
358    /// ```
359    ///
360    /// See [Packed](crate::cast::Packed) for more details.
361    #[inline]
362    pub fn from_u32<O>(color: u32) -> Self
363    where
364        O: ComponentOrder<Rgba<S, u8>, u32>,
365    {
366        O::unpack(color).color
367    }
368}
369
370impl<S: RgbStandard, T> Rgb<S, T> {
371    /// Convert the color to linear RGB.
372    ///
373    /// Some transfer functions allow the component type to be converted at the
374    /// same time. This is usually offered with increased performance, compared
375    /// to using [`into_format`][Rgb::into_format].
376    ///
377    /// ```
378    /// use palette::{Srgb, LinSrgb};
379    ///
380    /// let linear: LinSrgb<f32> = Srgb::new(96u8, 127, 0).into_linear();
381    /// ```
382    ///
383    /// See the transfer function types in the [`encoding`](crate::encoding)
384    /// module for details and performance characteristics.
385    #[inline(always)]
386    pub fn into_linear<U>(self) -> Rgb<Linear<S::Space>, U>
387    where
388        S::TransferFn: IntoLinear<U, T>,
389    {
390        Rgb::new(
391            S::TransferFn::into_linear(self.red),
392            S::TransferFn::into_linear(self.green),
393            S::TransferFn::into_linear(self.blue),
394        )
395    }
396
397    /// Convert linear RGB to non-linear RGB.
398    ///
399    /// Some transfer functions allow the component type to be converted at the
400    /// same time. This is usually offered with increased performance, compared
401    /// to using [`into_format`][Rgb::into_format].
402    ///
403    /// ```
404    /// use palette::{Srgb, LinSrgb};
405    ///
406    /// let encoded = Srgb::<u8>::from_linear(LinSrgb::new(0.95f32, 0.90, 0.30));
407    /// ```
408    ///
409    /// See the transfer function types in the [`encoding`](crate::encoding)
410    /// module for details and performance characteristics.
411    #[inline(always)]
412    pub fn from_linear<U>(color: Rgb<Linear<S::Space>, U>) -> Self
413    where
414        S::TransferFn: FromLinear<U, T>,
415    {
416        Rgb::new(
417            S::TransferFn::from_linear(color.red),
418            S::TransferFn::from_linear(color.green),
419            S::TransferFn::from_linear(color.blue),
420        )
421    }
422}
423
424impl<S: RgbSpace, T> Rgb<Linear<S>, T> {
425    /// Convert a linear color to a different encoding.
426    ///
427    /// Some transfer functions allow the component type to be converted at the
428    /// same time. This is usually offered with increased performance, compared
429    /// to using [`into_format`][Rgb::into_format].
430    ///
431    /// ```
432    /// use palette::{Srgb, LinSrgb};
433    ///
434    /// let encoded: Srgb<u8> = LinSrgb::new(0.95f32, 0.90, 0.30).into_encoding();
435    /// ```
436    ///
437    /// See the transfer function types in the [`encoding`](crate::encoding)
438    /// module for details and performance characteristics.
439    pub fn into_encoding<U, St>(self) -> Rgb<St, U>
440    where
441        St: RgbStandard<Space = S>,
442        St::TransferFn: FromLinear<T, U>,
443    {
444        Rgb::<St, U>::from_linear(self)
445    }
446
447    /// Convert linear RGB from a different encoding.
448    ///
449    /// Some transfer functions allow the component type to be converted at the
450    /// same time. This is usually offered with increased performance, compared
451    /// to using [`into_format`][Rgb::into_format].
452    ///
453    /// ```
454    /// use palette::{Srgb, LinSrgb};
455    ///
456    /// let linear = LinSrgb::<f32>::from_encoding(Srgb::new(96u8, 127, 0));
457    /// ```
458    ///
459    /// See the transfer function types in the [`encoding`](crate::encoding)
460    /// module for details and performance characteristics.
461    pub fn from_encoding<U, St>(color: Rgb<St, U>) -> Self
462    where
463        St: RgbStandard<Space = S>,
464        St::TransferFn: IntoLinear<T, U>,
465    {
466        color.into_linear()
467    }
468}
469
470impl<S, T> Rgb<S, T>
471where
472    S: RgbStandard,
473{
474    #[inline]
475    pub(crate) fn reinterpret_as<St>(self) -> Rgb<St, T>
476    where
477        S::Space: RgbSpace<WhitePoint = <St::Space as RgbSpace>::WhitePoint>,
478        St: RgbStandard,
479    {
480        Rgb {
481            red: self.red,
482            green: self.green,
483            blue: self.blue,
484            standard: PhantomData,
485        }
486    }
487}
488
489/// <span id="Rgba"></span>[`Rgba`](crate::rgb::Rgba) implementations.
490impl<S, T, A> Alpha<Rgb<S, T>, A> {
491    /// Non-linear RGB.
492    pub const fn new(red: T, green: T, blue: T, alpha: A) -> Self {
493        Alpha {
494            color: Rgb::new(red, green, blue),
495            alpha,
496        }
497    }
498
499    /// Convert the RGBA components into other number types.
500    ///
501    /// ```
502    /// use palette::Srgba;
503    ///
504    /// let rgba_u8: Srgba<u8> = Srgba::new(0.3, 0.7, 0.2, 0.5).into_format();
505    /// ```
506    ///
507    /// See also `into_linear` and `into_encoding` for a faster option if you
508    /// need to change between linear and non-linear encoding at the same time.
509    pub fn into_format<U, B>(self) -> Alpha<Rgb<S, U>, B>
510    where
511        U: FromStimulus<T>,
512        B: FromStimulus<A>,
513    {
514        Alpha {
515            color: self.color.into_format(),
516            alpha: B::from_stimulus(self.alpha),
517        }
518    }
519
520    /// Convert the RGBA components from other number types.
521    ///
522    /// ```
523    /// use palette::Srgba;
524    ///
525    /// let rgba_u8 = Srgba::<u8>::from_format(Srgba::new(0.3, 0.7, 0.2, 0.5));
526    /// ```
527    ///
528    /// See also `from_linear` and `from_encoding` for a faster option if you
529    /// need to change between linear and non-linear encoding at the same time.
530    pub fn from_format<U, B>(color: Alpha<Rgb<S, U>, B>) -> Self
531    where
532        T: FromStimulus<U>,
533        A: FromStimulus<B>,
534    {
535        color.into_format()
536    }
537
538    /// Convert to a `(red, green, blue, alpha)` tuple.
539    pub fn into_components(self) -> (T, T, T, A) {
540        (
541            self.color.red,
542            self.color.green,
543            self.color.blue,
544            self.alpha,
545        )
546    }
547
548    /// Convert from a `(red, green, blue, alpha)` tuple.
549    pub fn from_components((red, green, blue, alpha): (T, T, T, A)) -> Self {
550        Self::new(red, green, blue, alpha)
551    }
552}
553
554impl<S> Rgba<S, u8> {
555    /// Convert to a packed `u32` with with specifiable component order.
556    ///
557    /// ```
558    /// use palette::{rgb, Srgba};
559    ///
560    /// let integer = Srgba::new(96u8, 127, 0, 255).into_u32::<rgb::channels::Argb>();
561    /// assert_eq!(0xFF607F00, integer);
562    /// ```
563    ///
564    /// It's also possible to use `From` and `Into`, which defaults to the
565    /// `0xRRGGBBAA` component order:
566    ///
567    /// ```
568    /// use palette::Srgba;
569    ///
570    /// let integer = u32::from(Srgba::new(96u8, 127, 0, 255));
571    /// assert_eq!(0x607F00FF, integer);
572    /// ```
573    ///
574    /// See [Packed](crate::cast::Packed) for more details.
575    #[inline]
576    pub fn into_u32<O>(self) -> u32
577    where
578        O: ComponentOrder<Rgba<S, u8>, u32>,
579    {
580        O::pack(self)
581    }
582
583    /// Convert from a packed `u32` with specifiable component order.
584    ///
585    /// ```
586    /// use palette::{rgb, Srgba};
587    ///
588    /// let rgba = Srgba::from_u32::<rgb::channels::Argb>(0xFF607F00);
589    /// assert_eq!(Srgba::new(96u8, 127, 0, 255), rgba);
590    /// ```
591    ///
592    /// It's also possible to use `From` and `Into`, which defaults to the
593    /// `0xRRGGBBAA` component order:
594    ///
595    /// ```
596    /// use palette::Srgba;
597    ///
598    /// let rgba = Srgba::from(0x607F00FF);
599    /// assert_eq!(Srgba::new(96u8, 127, 0, 255), rgba);
600    /// ```
601    ///
602    /// See [Packed](crate::cast::Packed) for more details.
603    #[inline]
604    pub fn from_u32<O>(color: u32) -> Self
605    where
606        O: ComponentOrder<Rgba<S, u8>, u32>,
607    {
608        O::unpack(color)
609    }
610}
611
612impl<S: RgbStandard, T, A> Alpha<Rgb<S, T>, A> {
613    /// Convert the color to linear RGB with transparency.
614    ///
615    /// Some transfer functions allow the component type to be converted at the
616    /// same time. This is usually offered with increased performance, compared
617    /// to using [`into_format`][Rgb::into_format].
618    ///
619    /// ```
620    /// use palette::{Srgba, LinSrgba};
621    ///
622    /// let linear: LinSrgba<f32> = Srgba::new(96u8, 127, 0, 38).into_linear();
623    /// ```
624    ///
625    /// See the transfer function types in the [`encoding`](crate::encoding)
626    /// module for details and performance characteristics.
627    pub fn into_linear<U, B>(self) -> Alpha<Rgb<Linear<S::Space>, U>, B>
628    where
629        S::TransferFn: IntoLinear<U, T>,
630        B: FromStimulus<A>,
631    {
632        Alpha {
633            color: self.color.into_linear(),
634            alpha: B::from_stimulus(self.alpha),
635        }
636    }
637
638    /// Convert linear RGB to non-linear RGB with transparency.
639    ///
640    /// Some transfer functions allow the component type to be converted at the
641    /// same time. This is usually offered with increased performance, compared
642    /// to using [`into_format`][Rgb::into_format].
643    ///
644    /// ```
645    /// use palette::{Srgba, LinSrgba};
646    ///
647    /// let encoded = Srgba::<u8>::from_linear(LinSrgba::new(0.95f32, 0.90, 0.30, 0.75));
648    /// ```
649    ///
650    /// See the transfer function types in the [`encoding`](crate::encoding)
651    /// module for details and performance characteristics.
652    pub fn from_linear<U, B>(color: Alpha<Rgb<Linear<S::Space>, U>, B>) -> Self
653    where
654        S::TransferFn: FromLinear<U, T>,
655        A: FromStimulus<B>,
656    {
657        Alpha {
658            color: Rgb::from_linear(color.color),
659            alpha: A::from_stimulus(color.alpha),
660        }
661    }
662}
663
664impl<S: RgbSpace, T, A> Alpha<Rgb<Linear<S>, T>, A> {
665    /// Convert a linear color to a different encoding with transparency.
666    ///
667    /// Some transfer functions allow the component type to be converted at the
668    /// same time. This is usually offered with increased performance, compared
669    /// to using [`into_format`][Rgb::into_format].
670    ///
671    /// ```
672    /// use palette::{Srgba, LinSrgba};
673    ///
674    /// let encoded: Srgba<u8> = LinSrgba::new(0.95f32, 0.90, 0.30, 0.75).into_encoding();
675    /// ```
676    ///
677    /// See the transfer function types in the [`encoding`](crate::encoding)
678    /// module for details and performance characteristics.
679    pub fn into_encoding<U, B, St>(self) -> Alpha<Rgb<St, U>, B>
680    where
681        St: RgbStandard<Space = S>,
682        St::TransferFn: FromLinear<T, U>,
683        B: FromStimulus<A>,
684    {
685        Alpha::<Rgb<St, U>, B>::from_linear(self)
686    }
687
688    /// Convert RGB from a different encoding to linear with transparency.
689    ///
690    /// Some transfer functions allow the component type to be converted at the
691    /// same time. This is usually offered with increased performance, compared
692    /// to using [`into_format`][Rgb::into_format].
693    ///
694    /// ```
695    /// use palette::{Srgba, LinSrgba};
696    ///
697    /// let linear = LinSrgba::<f32>::from_encoding(Srgba::new(96u8, 127, 0, 38));
698    /// ```
699    ///
700    /// See the transfer function types in the [`encoding`](crate::encoding)
701    /// module for details and performance characteristics.
702    pub fn from_encoding<U, B, St>(color: Alpha<Rgb<St, U>, B>) -> Self
703    where
704        St: RgbStandard<Space = S>,
705        St::TransferFn: IntoLinear<T, U>,
706        A: FromStimulus<B>,
707    {
708        color.into_linear()
709    }
710}
711
712impl_reference_component_methods!(Rgb<S>, [red, green, blue], standard);
713impl_struct_of_arrays_methods!(Rgb<S>, [red, green, blue], standard);
714
715impl<S1, S2, T> FromColorUnclamped<Rgb<S2, T>> for Rgb<S1, T>
716where
717    S1: RgbStandard + 'static,
718    S2: RgbStandard + 'static,
719    S1::TransferFn: FromLinear<T, T>,
720    S2::TransferFn: IntoLinear<T, T>,
721    S2::Space: RgbSpace<WhitePoint = <S1::Space as RgbSpace>::WhitePoint>,
722    Xyz<<S2::Space as RgbSpace>::WhitePoint, T>: FromColorUnclamped<Rgb<S2, T>>,
723    Rgb<S1, T>: FromColorUnclamped<Xyz<<S1::Space as RgbSpace>::WhitePoint, T>>,
724{
725    fn from_color_unclamped(rgb: Rgb<S2, T>) -> Self {
726        let rgb_space1 = TypeId::of::<<S1::Space as RgbSpace>::Primaries>();
727        let rgb_space2 = TypeId::of::<<S2::Space as RgbSpace>::Primaries>();
728
729        if TypeId::of::<S1>() == TypeId::of::<S2>() {
730            rgb.reinterpret_as()
731        } else if rgb_space1 == rgb_space2 {
732            Self::from_linear(rgb.into_linear().reinterpret_as())
733        } else {
734            Self::from_color_unclamped(Xyz::from_color_unclamped(rgb))
735        }
736    }
737}
738
739impl<S, T> FromColorUnclamped<Xyz<<S::Space as RgbSpace>::WhitePoint, T>> for Rgb<S, T>
740where
741    S: RgbStandard,
742    S::TransferFn: FromLinear<T, T>,
743    <S::Space as RgbSpace>::Primaries: Primaries<T::Scalar>,
744    <S::Space as RgbSpace>::WhitePoint: WhitePoint<T::Scalar>,
745    T: Arithmetics + FromScalar,
746    T::Scalar: Real
747        + Recip
748        + IsValidDivisor<Mask = bool>
749        + Arithmetics
750        + Clone
751        + FromScalar<Scalar = T::Scalar>,
752    Yxy<Any, T::Scalar>: IntoColorUnclamped<Xyz<Any, T::Scalar>>,
753{
754    fn from_color_unclamped(color: Xyz<<S::Space as RgbSpace>::WhitePoint, T>) -> Self {
755        let transform_matrix = S::Space::xyz_to_rgb_matrix().map_or_else(
756            || matrix_inverse(rgb_to_xyz_matrix::<S::Space, T::Scalar>()),
757            |matrix| matrix_map(matrix, T::Scalar::from_f64),
758        );
759        Self::from_linear(multiply_xyz_to_rgb(transform_matrix, color))
760    }
761}
762
763impl<S, T> FromColorUnclamped<Hsl<S, T>> for Rgb<S, T>
764where
765    T: Real
766        + RealAngle
767        + UnsignedAngle
768        + Zero
769        + One
770        + Abs
771        + Round
772        + PartialCmp
773        + Arithmetics
774        + Clone,
775    T::Mask: LazySelect<T> + BitOps + Clone,
776{
777    fn from_color_unclamped(hsl: Hsl<S, T>) -> Self {
778        let c = (T::one() - (hsl.lightness.clone() * T::from_f64(2.0) - T::one()).abs())
779            * hsl.saturation;
780        let h = hsl.hue.into_positive_degrees() / T::from_f64(60.0);
781        // We avoid using %, since it's not always (or never?) supported in SIMD
782        let h_mod_two = h.clone() - Round::floor(h.clone() * T::from_f64(0.5)) * T::from_f64(2.0);
783        let x = c.clone() * (T::one() - (h_mod_two - T::one()).abs());
784        let m = hsl.lightness - c.clone() * T::from_f64(0.5);
785
786        let is_zone0 = h.gt_eq(&T::zero()) & h.lt(&T::one());
787        let is_zone1 = h.gt_eq(&T::one()) & h.lt(&T::from_f64(2.0));
788        let is_zone2 = h.gt_eq(&T::from_f64(2.0)) & h.lt(&T::from_f64(3.0));
789        let is_zone3 = h.gt_eq(&T::from_f64(3.0)) & h.lt(&T::from_f64(4.0));
790        let is_zone4 = h.gt_eq(&T::from_f64(4.0)) & h.lt(&T::from_f64(5.0));
791
792        let red = lazy_select! {
793            if is_zone1.clone() | &is_zone4 => x.clone(),
794            if is_zone2.clone() | &is_zone3 => T::zero(),
795            else => c.clone(),
796        };
797
798        let green = lazy_select! {
799            if is_zone0.clone() | &is_zone3 => x.clone(),
800            if is_zone1.clone() | &is_zone2 => c.clone(),
801            else => T::zero(),
802        };
803
804        let blue = lazy_select! {
805            if is_zone0 | is_zone1 => T::zero(),
806            if is_zone3 | is_zone4 => c,
807            else => x,
808        };
809
810        Rgb {
811            red: red + m.clone(),
812            green: green + m.clone(),
813            blue: blue + m,
814            standard: PhantomData,
815        }
816    }
817}
818
819impl<S, T> FromColorUnclamped<Hsv<S, T>> for Rgb<S, T>
820where
821    T: Real
822        + RealAngle
823        + UnsignedAngle
824        + Round
825        + Zero
826        + One
827        + Abs
828        + PartialCmp
829        + Arithmetics
830        + Clone,
831    T::Mask: LazySelect<T> + BitOps + Clone,
832{
833    fn from_color_unclamped(hsv: Hsv<S, T>) -> Self {
834        let c = hsv.value.clone() * hsv.saturation;
835        let h = hsv.hue.into_positive_degrees() / T::from_f64(60.0);
836        // We avoid using %, since it's not always (or never?) supported in SIMD
837        let h_mod_two = h.clone() - Round::floor(h.clone() * T::from_f64(0.5)) * T::from_f64(2.0);
838        let x = c.clone() * (T::one() - (h_mod_two - T::one()).abs());
839        let m = hsv.value - c.clone();
840
841        let is_zone0 = h.gt_eq(&T::zero()) & h.lt(&T::one());
842        let is_zone1 = h.gt_eq(&T::one()) & h.lt(&T::from_f64(2.0));
843        let is_zone2 = h.gt_eq(&T::from_f64(2.0)) & h.lt(&T::from_f64(3.0));
844        let is_zone3 = h.gt_eq(&T::from_f64(3.0)) & h.lt(&T::from_f64(4.0));
845        let is_zone4 = h.gt_eq(&T::from_f64(4.0)) & h.lt(&T::from_f64(5.0));
846
847        let red = lazy_select! {
848            if is_zone1.clone() | &is_zone4 => x.clone(),
849            if is_zone2.clone() | &is_zone3 => T::zero(),
850            else => c.clone(),
851        };
852
853        let green = lazy_select! {
854            if is_zone0.clone() | &is_zone3 => x.clone(),
855            if is_zone1.clone() | &is_zone2 => c.clone(),
856            else => T::zero(),
857        };
858
859        let blue = lazy_select! {
860            if is_zone0 | is_zone1 => T::zero(),
861            if is_zone3 | is_zone4 => c,
862            else => x,
863        };
864
865        Rgb {
866            red: red + m.clone(),
867            green: green + m.clone(),
868            blue: blue + m,
869            standard: PhantomData,
870        }
871    }
872}
873
874impl<S, St, T> FromColorUnclamped<Luma<St, T>> for Rgb<S, T>
875where
876    S: RgbStandard + 'static,
877    St: LumaStandard<WhitePoint = <S::Space as RgbSpace>::WhitePoint> + 'static,
878    S::TransferFn: FromLinear<T, T>,
879    St::TransferFn: IntoLinear<T, T>,
880    T: Clone,
881{
882    #[inline]
883    fn from_color_unclamped(color: Luma<St, T>) -> Self {
884        if TypeId::of::<S::TransferFn>() == TypeId::of::<St::TransferFn>() {
885            Rgb {
886                red: color.luma.clone(),
887                green: color.luma.clone(),
888                blue: color.luma,
889                standard: PhantomData,
890            }
891        } else {
892            let luma = color.into_linear();
893
894            Self::from_linear(Rgb {
895                red: luma.luma.clone(),
896                green: luma.luma.clone(),
897                blue: luma.luma,
898                standard: PhantomData,
899            })
900        }
901    }
902}
903
904impl<S, T> FromColorUnclamped<Oklab<T>> for Rgb<S, T>
905where
906    T: Real + Arithmetics + Copy,
907    S: RgbStandard,
908    S::TransferFn: FromLinear<T, T>,
909    S::Space: RgbSpace<WhitePoint = D65> + 'static,
910    Rgb<Linear<Srgb>, T>: IntoColorUnclamped<Self>,
911    Xyz<D65, T>: FromColorUnclamped<Oklab<T>> + IntoColorUnclamped<Self>,
912{
913    fn from_color_unclamped(oklab: Oklab<T>) -> Self {
914        if TypeId::of::<<S as RgbStandard>::Space>() == TypeId::of::<Srgb>() {
915            // Use direct sRGB to Oklab conversion
916            // Rounding errors are likely a contributing factor to differences.
917            // Also the conversion via XYZ doesn't use pre-defined matrices (yet)
918            oklab_to_linear_srgb(oklab).into_color_unclamped()
919        } else {
920            // Convert via XYZ
921            Xyz::from_color_unclamped(oklab).into_color_unclamped()
922        }
923    }
924}
925
926impl_is_within_bounds! {
927    Rgb<S> {
928        red => [Self::min_red(), Self::max_red()],
929        green => [Self::min_green(), Self::max_green()],
930        blue => [Self::min_blue(), Self::max_blue()]
931    }
932    where T: Stimulus
933}
934impl_clamp! {
935    Rgb<S> {
936        red => [Self::min_red(), Self::max_red()],
937        green => [Self::min_green(), Self::max_green()],
938        blue => [Self::min_blue(), Self::max_blue()]
939    }
940    other {standard}
941    where T: Stimulus
942}
943
944impl_mix!(Rgb<S>);
945impl_lighten! {
946    Rgb<S>
947    increase {
948        red => [Self::min_red(), Self::max_red()],
949        green => [Self::min_green(), Self::max_green()],
950        blue => [Self::min_blue(), Self::max_blue()]
951    }
952    other {}
953    phantom: standard
954    where T: Stimulus,
955}
956
957impl<S, T> GetHue for Rgb<S, T>
958where
959    T: Real + RealAngle + Trigonometry + Arithmetics + Clone,
960{
961    type Hue = RgbHue<T>;
962
963    fn get_hue(&self) -> RgbHue<T> {
964        let sqrt_3: T = T::from_f64(1.73205081);
965
966        RgbHue::from_cartesian(
967            self.red.clone() * T::from_f64(2.0) - self.green.clone() - self.blue.clone(),
968            sqrt_3 * (self.green.clone() - self.blue.clone()),
969        )
970    }
971}
972
973impl_premultiply!(Rgb<S> {red, green, blue} phantom: standard);
974impl_euclidean_distance!(Rgb<S> {red, green, blue});
975
976impl<S, T> StimulusColor for Rgb<S, T> where T: Stimulus {}
977
978impl<S, T> HasBoolMask for Rgb<S, T>
979where
980    T: HasBoolMask,
981{
982    type Mask = T::Mask;
983}
984
985impl<S, T> Default for Rgb<S, T>
986where
987    T: Stimulus,
988{
989    fn default() -> Rgb<S, T> {
990        Rgb::new(Self::min_red(), Self::min_green(), Self::min_blue())
991    }
992}
993
994impl_color_add!(Rgb<S>, [red, green, blue], standard);
995impl_color_sub!(Rgb<S>, [red, green, blue], standard);
996impl_color_mul!(Rgb<S>, [red, green, blue], standard);
997impl_color_div!(Rgb<S>, [red, green, blue], standard);
998
999impl_tuple_conversion!(Rgb<S> as (T, T, T));
1000impl_array_casts!(Rgb<S, T>, [T; 3]);
1001impl_simd_array_conversion!(Rgb<S>, [red, green, blue], standard);
1002impl_struct_of_array_traits!(Rgb<S>, [red, green, blue], standard);
1003
1004impl_eq!(Rgb<S>, [red, green, blue]);
1005impl_copy_clone!(Rgb<S>, [red, green, blue], standard);
1006
1007impl<S, T> fmt::LowerHex for Rgb<S, T>
1008where
1009    T: fmt::LowerHex,
1010{
1011    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1012        let size = f.width().unwrap_or(::core::mem::size_of::<T>() * 2);
1013        write!(
1014            f,
1015            "{:0width$x}{:0width$x}{:0width$x}",
1016            self.red,
1017            self.green,
1018            self.blue,
1019            width = size
1020        )
1021    }
1022}
1023
1024impl<S, T> fmt::UpperHex for Rgb<S, T>
1025where
1026    T: fmt::UpperHex,
1027{
1028    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1029        let size = f.width().unwrap_or(::core::mem::size_of::<T>() * 2);
1030        write!(
1031            f,
1032            "{:0width$X}{:0width$X}{:0width$X}",
1033            self.red,
1034            self.green,
1035            self.blue,
1036            width = size
1037        )
1038    }
1039}
1040
1041/// Error type for parsing a string of hexadecimal characters to an `Rgb` color.
1042#[derive(Debug)]
1043pub enum FromHexError {
1044    /// An error occurred while parsing the string into a valid integer.
1045    ParseIntError(ParseIntError),
1046    /// The hex value was not in a valid 3 or 6 character format.
1047    HexFormatError(&'static str),
1048    /// The hex value was not in a valid 4 or 8 character format.
1049    RgbaHexFormatError(&'static str),
1050}
1051
1052impl From<ParseIntError> for FromHexError {
1053    fn from(err: ParseIntError) -> FromHexError {
1054        FromHexError::ParseIntError(err)
1055    }
1056}
1057
1058impl From<&'static str> for FromHexError {
1059    fn from(err: &'static str) -> FromHexError {
1060        FromHexError::HexFormatError(err)
1061    }
1062}
1063
1064impl core::fmt::Display for FromHexError {
1065    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1066        match self {
1067            FromHexError::ParseIntError(e) => write!(f, "{}", e),
1068            FromHexError::HexFormatError(s) => write!(
1069                f,
1070                "{}, please use format '#fff', 'fff', '#ffffff' or 'ffffff'.",
1071                s
1072            ),
1073            FromHexError::RgbaHexFormatError(s) => write!(
1074                f,
1075                "{}, please use format '#ffff', 'ffff', '#ffffffff' or 'ffffffff'.",
1076                s
1077            ),
1078        }
1079    }
1080}
1081
1082#[cfg(feature = "std")]
1083impl std::error::Error for FromHexError {
1084    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
1085        match self {
1086            FromHexError::HexFormatError(_s) => None,
1087            FromHexError::RgbaHexFormatError(_s) => None,
1088            FromHexError::ParseIntError(e) => Some(e),
1089        }
1090    }
1091}
1092
1093impl<S> FromStr for Rgb<S, u8> {
1094    type Err = FromHexError;
1095
1096    /// Parses a color hex code of format '#ff00bb' or '#abc' (with or without the leading '#') into a
1097    /// [`Rgb<S, u8>`] instance.
1098    fn from_str(hex: &str) -> Result<Self, Self::Err> {
1099        let hex_code = hex.strip_prefix('#').map_or(hex, |stripped| stripped);
1100        match hex_code.len() {
1101            3 => {
1102                let red = u8::from_str_radix(&hex_code[..1], 16)?;
1103                let green = u8::from_str_radix(&hex_code[1..2], 16)?;
1104                let blue = u8::from_str_radix(&hex_code[2..3], 16)?;
1105                let col: Rgb<S, u8> = Rgb::new(red * 17, green * 17, blue * 17);
1106                Ok(col)
1107            }
1108            6 => {
1109                let red = u8::from_str_radix(&hex_code[..2], 16)?;
1110                let green = u8::from_str_radix(&hex_code[2..4], 16)?;
1111                let blue = u8::from_str_radix(&hex_code[4..6], 16)?;
1112                let col: Rgb<S, u8> = Rgb::new(red, green, blue);
1113                Ok(col)
1114            }
1115            _ => Err(FromHexError::HexFormatError("invalid hex code format")),
1116        }
1117    }
1118}
1119
1120impl<S> FromStr for Rgba<S, u8> {
1121    type Err = FromHexError;
1122
1123    /// Parses a color hex code of format '#ff00bbff' or '#abcd' (with or without the leading '#') into a
1124    /// [`Rgba<S, u8>`] instance.
1125    fn from_str(hex: &str) -> Result<Self, Self::Err> {
1126        let hex_code = hex.strip_prefix('#').map_or(hex, |stripped| stripped);
1127        match hex_code.len() {
1128            4 => {
1129                let red = u8::from_str_radix(&hex_code[..1], 16)?;
1130                let green = u8::from_str_radix(&hex_code[1..2], 16)?;
1131                let blue = u8::from_str_radix(&hex_code[2..3], 16)?;
1132                let alpha = u8::from_str_radix(&hex_code[3..4], 16)?;
1133                let col: Rgba<S, u8> = Rgba::new(red * 17, green * 17, blue * 17, alpha * 17);
1134                Ok(col)
1135            }
1136            8 => {
1137                let red = u8::from_str_radix(&hex_code[..2], 16)?;
1138                let green = u8::from_str_radix(&hex_code[2..4], 16)?;
1139                let blue = u8::from_str_radix(&hex_code[4..6], 16)?;
1140                let alpha = u8::from_str_radix(&hex_code[6..8], 16)?;
1141                let col: Rgba<S, u8> = Rgba::new(red, green, blue, alpha);
1142                Ok(col)
1143            }
1144            _ => Err(FromHexError::RgbaHexFormatError("invalid hex code format")),
1145        }
1146    }
1147}
1148
1149impl<S, T, P, O> From<Rgb<S, T>> for Packed<O, P>
1150where
1151    O: ComponentOrder<Rgba<S, T>, P>,
1152    Rgba<S, T>: From<Rgb<S, T>>,
1153{
1154    #[inline]
1155    fn from(color: Rgb<S, T>) -> Self {
1156        Self::from(Rgba::from(color))
1157    }
1158}
1159
1160impl<S, T, O, P> From<Rgba<S, T>> for Packed<O, P>
1161where
1162    O: ComponentOrder<Rgba<S, T>, P>,
1163{
1164    #[inline]
1165    fn from(color: Rgba<S, T>) -> Self {
1166        Packed::pack(color)
1167    }
1168}
1169
1170impl<S, O, P> From<Packed<O, P>> for Rgb<S, u8>
1171where
1172    O: ComponentOrder<Rgba<S, u8>, P>,
1173{
1174    #[inline]
1175    fn from(packed: Packed<O, P>) -> Self {
1176        Rgba::from(packed).color
1177    }
1178}
1179
1180impl<S, T, O, P> From<Packed<O, P>> for Rgba<S, T>
1181where
1182    O: ComponentOrder<Rgba<S, T>, P>,
1183{
1184    #[inline]
1185    fn from(packed: Packed<O, P>) -> Self {
1186        packed.unpack()
1187    }
1188}
1189
1190impl<S> From<u32> for Rgb<S, u8> {
1191    #[inline]
1192    fn from(color: u32) -> Self {
1193        Self::from_u32::<super::channels::Argb>(color)
1194    }
1195}
1196
1197impl<S> From<u32> for Rgba<S, u8> {
1198    #[inline]
1199    fn from(color: u32) -> Self {
1200        Self::from_u32::<super::channels::Rgba>(color)
1201    }
1202}
1203
1204impl<S> From<Rgb<S, u8>> for u32 {
1205    #[inline]
1206    fn from(color: Rgb<S, u8>) -> Self {
1207        Rgb::into_u32::<super::channels::Argb>(color)
1208    }
1209}
1210
1211impl<S> From<Rgba<S, u8>> for u32 {
1212    #[inline]
1213    fn from(color: Rgba<S, u8>) -> Self {
1214        Rgba::into_u32::<super::channels::Rgba>(color)
1215    }
1216}
1217
1218impl<S> From<Rgb<S, u8>> for Rgb<S, f32> {
1219    #[inline]
1220    fn from(color: Rgb<S, u8>) -> Self {
1221        color.into_format()
1222    }
1223}
1224
1225impl<S> From<Rgba<S, u8>> for Rgba<S, f32> {
1226    #[inline]
1227    fn from(color: Rgba<S, u8>) -> Self {
1228        color.into_format()
1229    }
1230}
1231
1232impl<S> From<Rgb<S, f32>> for Rgb<S, u8> {
1233    #[inline]
1234    fn from(color: Rgb<S, f32>) -> Self {
1235        color.into_format()
1236    }
1237}
1238
1239impl<S> From<Rgba<S, f32>> for Rgba<S, u8> {
1240    #[inline]
1241    fn from(color: Rgba<S, f32>) -> Self {
1242        color.into_format()
1243    }
1244}
1245
1246impl<S> From<Rgb<S, u8>> for Rgb<S, f64> {
1247    #[inline]
1248    fn from(color: Rgb<S, u8>) -> Self {
1249        color.into_format()
1250    }
1251}
1252
1253impl<S> From<Rgba<S, u8>> for Rgba<S, f64> {
1254    #[inline]
1255    fn from(color: Rgba<S, u8>) -> Self {
1256        color.into_format()
1257    }
1258}
1259
1260impl<S> From<Rgb<S, f64>> for Rgb<S, u8> {
1261    #[inline]
1262    fn from(color: Rgb<S, f64>) -> Self {
1263        color.into_format()
1264    }
1265}
1266
1267impl<S> From<Rgba<S, f64>> for Rgba<S, u8> {
1268    #[inline]
1269    fn from(color: Rgba<S, f64>) -> Self {
1270        color.into_format()
1271    }
1272}
1273
1274impl<S> From<Rgb<S, f32>> for Rgb<S, f64> {
1275    #[inline]
1276    fn from(color: Rgb<S, f32>) -> Self {
1277        color.into_format()
1278    }
1279}
1280
1281impl<S> From<Rgba<S, f32>> for Rgba<S, f64> {
1282    #[inline]
1283    fn from(color: Rgba<S, f32>) -> Self {
1284        color.into_format()
1285    }
1286}
1287
1288impl<S> From<Rgb<S, f64>> for Rgb<S, f32> {
1289    #[inline]
1290    fn from(color: Rgb<S, f64>) -> Self {
1291        color.into_format()
1292    }
1293}
1294
1295impl<S> From<Rgba<S, f64>> for Rgba<S, f32> {
1296    #[inline]
1297    fn from(color: Rgba<S, f64>) -> Self {
1298        color.into_format()
1299    }
1300}
1301
1302#[allow(deprecated)]
1303impl<S, T> crate::RelativeContrast for Rgb<S, T>
1304where
1305    T: Real + Arithmetics + PartialCmp,
1306    T::Mask: LazySelect<T>,
1307    S: RgbStandard,
1308    Xyz<<<S as RgbStandard>::Space as RgbSpace>::WhitePoint, T>: FromColor<Self>,
1309{
1310    type Scalar = T;
1311
1312    #[inline]
1313    fn get_contrast_ratio(self, other: Self) -> T {
1314        let xyz1 = Xyz::from_color(self);
1315        let xyz2 = Xyz::from_color(other);
1316
1317        crate::contrast_ratio(xyz1.y, xyz2.y)
1318    }
1319}
1320
1321impl<S, T> Wcag21RelativeContrast for Rgb<S, T>
1322where
1323    Self: IntoColor<Luma<Linear<D65>, T>>,
1324    S: RgbStandard<Space = crate::encoding::srgb::Srgb>,
1325    T: Real + Add<T, Output = T> + Div<T, Output = T> + PartialCmp + MinMax,
1326{
1327    type Scalar = T;
1328
1329    fn relative_luminance(self) -> Luma<Linear<D65>, Self::Scalar> {
1330        self.into_color()
1331    }
1332}
1333
1334impl_rand_traits_cartesian!(UniformRgb, Rgb<S> {red, green, blue} phantom: standard: PhantomData<S>);
1335
1336#[cfg(feature = "bytemuck")]
1337unsafe impl<S, T> bytemuck::Zeroable for Rgb<S, T> where T: bytemuck::Zeroable {}
1338
1339#[cfg(feature = "bytemuck")]
1340unsafe impl<S: 'static, T> bytemuck::Pod for Rgb<S, T> where T: bytemuck::Pod {}
1341
1342#[cfg(test)]
1343mod test {
1344    use core::str::FromStr;
1345
1346    use crate::encoding::Srgb;
1347    use crate::rgb::channels;
1348
1349    use super::{Rgb, Rgba};
1350
1351    test_convert_into_from_xyz!(Rgb);
1352
1353    #[test]
1354    fn ranges() {
1355        assert_ranges! {
1356            Rgb<Srgb, f64>;
1357            clamped {
1358                red: 0.0 => 1.0,
1359                green: 0.0 => 1.0,
1360                blue: 0.0 => 1.0
1361            }
1362            clamped_min {}
1363            unclamped {}
1364        }
1365    }
1366
1367    raw_pixel_conversion_tests!(Rgb<Srgb>: red, green, blue);
1368    raw_pixel_conversion_fail_tests!(Rgb<Srgb>: red, green, blue);
1369
1370    #[test]
1371    fn lower_hex() {
1372        assert_eq!(
1373            format!("{:x}", Rgb::<Srgb, u8>::new(171, 193, 35)),
1374            "abc123"
1375        );
1376    }
1377
1378    #[test]
1379    fn lower_hex_small_numbers() {
1380        assert_eq!(format!("{:x}", Rgb::<Srgb, u8>::new(1, 2, 3)), "010203");
1381        assert_eq!(
1382            format!("{:x}", Rgb::<Srgb, u16>::new(1, 2, 3)),
1383            "000100020003"
1384        );
1385        assert_eq!(
1386            format!("{:x}", Rgb::<Srgb, u32>::new(1, 2, 3)),
1387            "000000010000000200000003"
1388        );
1389        assert_eq!(
1390            format!("{:x}", Rgb::<Srgb, u64>::new(1, 2, 3)),
1391            "000000000000000100000000000000020000000000000003"
1392        );
1393    }
1394
1395    #[test]
1396    fn lower_hex_custom_width() {
1397        assert_eq!(
1398            format!("{:03x}", Rgb::<Srgb, u8>::new(1, 2, 3)),
1399            "001002003"
1400        );
1401        assert_eq!(
1402            format!("{:03x}", Rgb::<Srgb, u16>::new(1, 2, 3)),
1403            "001002003"
1404        );
1405        assert_eq!(
1406            format!("{:03x}", Rgb::<Srgb, u32>::new(1, 2, 3)),
1407            "001002003"
1408        );
1409        assert_eq!(
1410            format!("{:03x}", Rgb::<Srgb, u64>::new(1, 2, 3)),
1411            "001002003"
1412        );
1413    }
1414
1415    #[test]
1416    fn upper_hex() {
1417        assert_eq!(
1418            format!("{:X}", Rgb::<Srgb, u8>::new(171, 193, 35)),
1419            "ABC123"
1420        );
1421    }
1422
1423    #[test]
1424    fn upper_hex_small_numbers() {
1425        assert_eq!(format!("{:X}", Rgb::<Srgb, u8>::new(1, 2, 3)), "010203");
1426        assert_eq!(
1427            format!("{:X}", Rgb::<Srgb, u16>::new(1, 2, 3)),
1428            "000100020003"
1429        );
1430        assert_eq!(
1431            format!("{:X}", Rgb::<Srgb, u32>::new(1, 2, 3)),
1432            "000000010000000200000003"
1433        );
1434        assert_eq!(
1435            format!("{:X}", Rgb::<Srgb, u64>::new(1, 2, 3)),
1436            "000000000000000100000000000000020000000000000003"
1437        );
1438    }
1439
1440    #[test]
1441    fn upper_hex_custom_width() {
1442        assert_eq!(
1443            format!("{:03X}", Rgb::<Srgb, u8>::new(1, 2, 3)),
1444            "001002003"
1445        );
1446        assert_eq!(
1447            format!("{:03X}", Rgb::<Srgb, u16>::new(1, 2, 3)),
1448            "001002003"
1449        );
1450        assert_eq!(
1451            format!("{:03X}", Rgb::<Srgb, u32>::new(1, 2, 3)),
1452            "001002003"
1453        );
1454        assert_eq!(
1455            format!("{:03X}", Rgb::<Srgb, u64>::new(1, 2, 3)),
1456            "001002003"
1457        );
1458    }
1459
1460    #[test]
1461    fn rgb_hex_into_from() {
1462        let c1 = Rgb::<Srgb, u8>::from_u32::<channels::Argb>(0x1100_7FFF);
1463        let c2 = Rgb::<Srgb, u8>::new(0u8, 127, 255);
1464        assert_eq!(c1, c2);
1465        assert_eq!(Rgb::<Srgb, u8>::into_u32::<channels::Argb>(c1), 0xFF00_7FFF);
1466
1467        let c1 = Rgba::<Srgb, u8>::from_u32::<channels::Rgba>(0x007F_FF80);
1468        let c2 = Rgba::<Srgb, u8>::new(0u8, 127, 255, 128);
1469        assert_eq!(c1, c2);
1470        assert_eq!(
1471            Rgba::<Srgb, u8>::into_u32::<channels::Rgba>(c1),
1472            0x007F_FF80
1473        );
1474
1475        assert_eq!(
1476            Rgb::<Srgb, u8>::from(0x7FFF_FF80),
1477            Rgb::from((255u8, 255, 128))
1478        );
1479        assert_eq!(
1480            Rgba::<Srgb, u8>::from(0x7FFF_FF80),
1481            Rgba::from((127u8, 255, 255, 128))
1482        );
1483    }
1484
1485    #[cfg(feature = "serializing")]
1486    #[test]
1487    fn serialize() {
1488        let serialized = ::serde_json::to_string(&Rgb::<Srgb>::new(0.3, 0.8, 0.1)).unwrap();
1489
1490        assert_eq!(serialized, r#"{"red":0.3,"green":0.8,"blue":0.1}"#);
1491    }
1492
1493    #[cfg(feature = "serializing")]
1494    #[test]
1495    fn deserialize() {
1496        let deserialized: Rgb<Srgb> =
1497            ::serde_json::from_str(r#"{"red":0.3,"green":0.8,"blue":0.1}"#).unwrap();
1498
1499        assert_eq!(deserialized, Rgb::<Srgb>::new(0.3, 0.8, 0.1));
1500    }
1501
1502    #[test]
1503    fn from_str() {
1504        let c = Rgb::<Srgb, u8>::from_str("#ffffff");
1505        assert!(c.is_ok());
1506        assert_eq!(c.unwrap(), Rgb::<Srgb, u8>::new(255, 255, 255));
1507        let c = Rgb::<Srgb, u8>::from_str("#gggggg");
1508        assert!(c.is_err());
1509        let c = Rgb::<Srgb, u8>::from_str("#fff");
1510        assert!(c.is_ok());
1511        assert_eq!(c.unwrap(), Rgb::<Srgb, u8>::new(255, 255, 255));
1512        let c = Rgb::<Srgb, u8>::from_str("#000000");
1513        assert!(c.is_ok());
1514        assert_eq!(c.unwrap(), Rgb::<Srgb, u8>::new(0, 0, 0));
1515        let c = Rgb::<Srgb, u8>::from_str("");
1516        assert!(c.is_err());
1517        let c = Rgb::<Srgb, u8>::from_str("#123456");
1518        assert!(c.is_ok());
1519        assert_eq!(c.unwrap(), Rgb::<Srgb, u8>::new(18, 52, 86));
1520        let c = Rgb::<Srgb, u8>::from_str("#iii");
1521        assert!(c.is_err());
1522        assert_eq!(
1523            format!("{}", c.err().unwrap()),
1524            "invalid digit found in string"
1525        );
1526        let c = Rgb::<Srgb, u8>::from_str("#08f");
1527        assert_eq!(c.unwrap(), Rgb::<Srgb, u8>::new(0, 136, 255));
1528        let c = Rgb::<Srgb, u8>::from_str("08f");
1529        assert_eq!(c.unwrap(), Rgb::<Srgb, u8>::new(0, 136, 255));
1530        let c = Rgb::<Srgb, u8>::from_str("ffffff");
1531        assert_eq!(c.unwrap(), Rgb::<Srgb, u8>::new(255, 255, 255));
1532        let c = Rgb::<Srgb, u8>::from_str("#12");
1533        assert!(c.is_err());
1534        assert_eq!(
1535            format!("{}", c.err().unwrap()),
1536            "invalid hex code format, \
1537             please use format \'#fff\', \'fff\', \'#ffffff\' or \'ffffff\'."
1538        );
1539        let c = Rgb::<Srgb, u8>::from_str("da0bce");
1540        assert_eq!(c.unwrap(), Rgb::<Srgb, u8>::new(218, 11, 206));
1541        let c = Rgb::<Srgb, u8>::from_str("f034e6");
1542        assert_eq!(c.unwrap(), Rgb::<Srgb, u8>::new(240, 52, 230));
1543        let c = Rgb::<Srgb, u8>::from_str("abc");
1544        assert_eq!(c.unwrap(), Rgb::<Srgb, u8>::new(170, 187, 204));
1545        let c = Rgba::<Srgb, u8>::from_str("#08ff");
1546        assert_eq!(c.unwrap(), Rgba::<Srgb, u8>::new(0, 136, 255, 255));
1547        let c = Rgba::<Srgb, u8>::from_str("08f0");
1548        assert_eq!(c.unwrap(), Rgba::<Srgb, u8>::new(0, 136, 255, 0));
1549        let c = Rgba::<Srgb, u8>::from_str("#da0bce80");
1550        assert_eq!(c.unwrap(), Rgba::<Srgb, u8>::new(218, 11, 206, 128));
1551        let c = Rgba::<Srgb, u8>::from_str("f034e680");
1552        assert_eq!(c.unwrap(), Rgba::<Srgb, u8>::new(240, 52, 230, 128));
1553        let c = Rgba::<Srgb, u8>::from_str("#ffffffff");
1554        assert_eq!(c.unwrap(), Rgba::<Srgb, u8>::new(255, 255, 255, 255));
1555        let c = Rgba::<Srgb, u8>::from_str("#ffff");
1556        assert_eq!(c.unwrap(), Rgba::<Srgb, u8>::new(255, 255, 255, 255));
1557        let c = Rgba::<Srgb, u8>::from_str("#gggggggg");
1558        assert!(c.is_err());
1559        assert_eq!(
1560            format!("{}", c.err().unwrap()),
1561            "invalid digit found in string"
1562        );
1563        let c = Rgba::<Srgb, u8>::from_str("#fff");
1564        assert_eq!(
1565            format!("{}", c.err().unwrap()),
1566            "invalid hex code format, \
1567            please use format '#ffff', 'ffff', '#ffffffff' or 'ffffffff'."
1568        );
1569        let c = Rgba::<Srgb, u8>::from_str("#ffffff");
1570        assert_eq!(
1571            format!("{}", c.err().unwrap()),
1572            "invalid hex code format, \
1573            please use format '#ffff', 'ffff', '#ffffffff' or 'ffffffff'."
1574        );
1575    }
1576
1577    #[test]
1578    fn check_min_max_components() {
1579        assert_eq!(Rgb::<Srgb, f32>::min_red(), 0.0);
1580        assert_eq!(Rgb::<Srgb, f32>::min_green(), 0.0);
1581        assert_eq!(Rgb::<Srgb, f32>::min_blue(), 0.0);
1582        assert_eq!(Rgb::<Srgb, f32>::max_red(), 1.0);
1583        assert_eq!(Rgb::<Srgb, f32>::max_green(), 1.0);
1584        assert_eq!(Rgb::<Srgb, f32>::max_blue(), 1.0);
1585    }
1586
1587    struct_of_arrays_tests!(
1588        Rgb<Srgb>[red, green, blue] phantom: standard,
1589        Rgba::new(0.1f32, 0.2, 0.3, 0.4),
1590        Rgba::new(0.2, 0.3, 0.4, 0.5),
1591        Rgba::new(0.3, 0.4, 0.5, 0.6)
1592    );
1593
1594    test_uniform_distribution! {
1595        Rgb<Srgb, f32> {
1596            red: (0.0, 1.0),
1597            green: (0.0, 1.0),
1598            blue: (0.0, 1.0)
1599        },
1600        min: Rgb::new(0.0f32, 0.0, 0.0),
1601        max: Rgb::new(1.0, 1.0, 1.0)
1602    }
1603}