rasterize/
color.rs

1use crate::{Paint, Point, Scalar, Transform, Units, simd::f32x4};
2use bytemuck::{Pod, Zeroable};
3#[cfg(feature = "serde")]
4use serde::{Deserialize, Deserializer, Serialize, de::DeserializeSeed};
5use std::{
6    collections::HashMap,
7    fmt,
8    ops::{Add, Mul},
9    str::FromStr,
10    sync::LazyLock,
11};
12
13pub static SVG_COLORS: LazyLock<HashMap<String, RGBA>> = LazyLock::new(|| {
14    let empty = HashMap::new(); // do not use parse to avoid recursive lock
15    include_str!("./svg-colors.txt")
16        .lines()
17        .map(|line| {
18            let mut iter = line.split(' ');
19            let name = iter.next()?;
20            let color = RGBA::from_str_named(iter.next()?, &empty).ok()?;
21            Some((name.to_owned(), color))
22        })
23        .collect::<Option<HashMap<String, RGBA>>>()
24        .expect("failed to parse embedded svg colors")
25});
26
27/// Common interface to all color representations
28pub trait Color: Copy {
29    /// Blend other color on top of this color
30    fn blend_over(self, other: Self) -> Self;
31
32    /// Override alpha component of the color
33    fn with_alpha(self, alpha: Scalar) -> Self;
34
35    /// Convert color to sRGBA list
36    fn to_rgba(self) -> [u8; 4];
37
38    /// Convert color to sRGB list (alpha is discarded)
39    fn to_rgb(self) -> [u8; 3] {
40        let [r, g, b, _] = self.to_rgba();
41        [r, g, b]
42    }
43
44    /// Calculate LUMA of the color.
45    fn luma(self) -> f32 {
46        let [r, g, b] = self.to_rgb();
47        0.2126 * (r as f32 / 255.0) + 0.7152 * (g as f32 / 255.0) + 0.0722 * (b as f32 / 255.0)
48    }
49
50    /// Pick color that produces the best contrast with self
51    fn best_contrast(self, c0: Self, c1: Self) -> Self {
52        let luma = self.luma();
53        if (luma - c0.luma()).abs() < (luma - c1.luma()).abs() {
54            c1
55        } else {
56            c0
57        }
58    }
59
60    /// Linear interpolation between self and other colors.
61    fn lerp(self, other: Self, t: f32) -> Self;
62}
63
64/// sRGBA color packed as [u8; 4]
65#[repr(transparent)]
66#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default, Pod, Zeroable)]
67pub struct RGBA([u8; 4]);
68
69impl RGBA {
70    /// Create new RGBA color
71    pub const fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
72        Self([r, g, b, a])
73    }
74
75    /// Red channel value
76    pub const fn red(self) -> u8 {
77        self.0[0]
78    }
79
80    /// Green channel value
81    pub const fn green(self) -> u8 {
82        self.0[1]
83    }
84
85    /// Blue channel value
86    pub const fn blue(self) -> u8 {
87        self.0[2]
88    }
89
90    /// Alpha channel value
91    pub const fn alpha(self) -> u8 {
92        self.0[3]
93    }
94
95    /// Parse color or resolve by name
96    pub fn from_str_named(color: &str, colors: &HashMap<String, RGBA>) -> Result<Self, ColorError> {
97        // parse alpha suffix
98        let (color, alpha) = match color.rfind('/') {
99            None => (color, None),
100            Some(alpha_offset) => {
101                let alpha: f32 = color[alpha_offset + 1..]
102                    .parse()
103                    .map_err(|_| ColorError::InvalidAlpha)?;
104                (&color[..alpha_offset], Some(alpha))
105            }
106        };
107        // #RRGGBB(AA)
108        let rgba = if color.starts_with('#') && (color.len() == 7 || color.len() == 9) {
109            let bytes: &[u8] = color[1..].as_ref();
110            let digit = |byte| match byte {
111                b'A'..=b'F' => Ok(byte - b'A' + 10),
112                b'a'..=b'f' => Ok(byte - b'a' + 10),
113                b'0'..=b'9' => Ok(byte - b'0'),
114                _ => Err(ColorError::HexExpected),
115            };
116            let mut hex = bytes
117                .chunks(2)
118                .map(|pair| Ok((digit(pair[0])? << 4) | digit(pair[1])?));
119            RGBA::new(
120                hex.next().unwrap_or(Ok(0))?,
121                hex.next().unwrap_or(Ok(0))?,
122                hex.next().unwrap_or(Ok(0))?,
123                hex.next().unwrap_or(Ok(255))?,
124            )
125        } else {
126            colors
127                .get(color)
128                .copied()
129                .ok_or_else(|| ColorError::UnkownColor(color.to_owned()))?
130        };
131        // apply alpha
132        match alpha {
133            None => Ok(rgba),
134            Some(alpha) => Ok(RGBA::new(
135                rgba.red(),
136                rgba.green(),
137                rgba.blue(),
138                (rgba.alpha() as f32 * alpha) as u8,
139            )),
140        }
141    }
142}
143
144impl Color for RGBA {
145    fn to_rgba(self) -> [u8; 4] {
146        self.0
147    }
148
149    fn blend_over(self, other: Self) -> Self {
150        LinColor::from(self)
151            .blend_over(LinColor::from(other))
152            .into()
153    }
154
155    fn with_alpha(self, alpha: Scalar) -> Self {
156        LinColor::from(self).with_alpha(alpha).into()
157    }
158
159    fn lerp(self, other: Self, t: f32) -> Self {
160        LinColor::from(self).lerp(LinColor::from(other), t).into()
161    }
162}
163
164impl From<LinColor> for RGBA {
165    #[inline(always)]
166    fn from(lin: LinColor) -> Self {
167        let [r, g, b, _]: [f32; 4] = crate::simd::l2s(lin.unmultiply()).into();
168        RGBA::new(
169            (r * 255.0 + 0.5) as u8,
170            (g * 255.0 + 0.5) as u8,
171            (b * 255.0 + 0.5) as u8,
172            (lin.alpha() * 255.0 + 0.5) as u8,
173        )
174    }
175}
176
177impl From<[u8; 4]> for RGBA {
178    #[inline]
179    fn from(rgba: [u8; 4]) -> Self {
180        RGBA(rgba)
181    }
182}
183
184impl From<[u8; 3]> for RGBA {
185    #[inline]
186    fn from([r, g, b]: [u8; 3]) -> Self {
187        RGBA::new(r, g, b, 255)
188    }
189}
190
191impl fmt::Debug for RGBA {
192    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
193        let [bg_r, bg_g, bg_b] = self.to_rgb();
194        let [fg_r, fg_g, fg_b] = self
195            .best_contrast(RGBA::new(255, 255, 255, 255), RGBA::new(0, 0, 0, 255))
196            .to_rgb();
197        write!(
198            fmt,
199            "\x1b[38;2;{};{};{};48;2;{};{};{}m",
200            fg_r, fg_g, fg_b, bg_r, bg_g, bg_b
201        )?;
202        write!(fmt, "{}", self)?;
203        write!(fmt, "\x1b[m")
204    }
205}
206
207impl fmt::Display for RGBA {
208    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
209        let [r, g, b, a] = self.to_rgba();
210        write!(f, "#{:02x}{:02x}{:02x}", r, g, b)?;
211        if a != 255 {
212            write!(f, "{:02x}", a)?;
213        }
214        Ok(())
215    }
216}
217
218impl FromStr for RGBA {
219    type Err = ColorError;
220
221    fn from_str(color: &str) -> Result<Self, Self::Err> {
222        RGBA::from_str_named(color, &SVG_COLORS)
223    }
224}
225
226#[cfg(feature = "serde")]
227impl Serialize for RGBA {
228    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
229    where
230        S: serde::Serializer,
231    {
232        serializer.collect_str(self)
233    }
234}
235
236#[cfg(feature = "serde")]
237#[derive(Clone)]
238pub struct RGBADeserializer<'a> {
239    pub colors: &'a HashMap<String, RGBA>,
240}
241
242#[cfg(feature = "serde")]
243impl<'de> Deserialize<'de> for RGBA {
244    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
245    where
246        D: Deserializer<'de>,
247    {
248        RGBADeserializer {
249            colors: &SVG_COLORS,
250        }
251        .deserialize(deserializer)
252    }
253}
254
255#[cfg(feature = "serde")]
256impl<'de> DeserializeSeed<'de> for RGBADeserializer<'_> {
257    type Value = RGBA;
258
259    fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
260    where
261        D: Deserializer<'de>,
262    {
263        let color = std::borrow::Cow::<'de, str>::deserialize(deserializer)?;
264        RGBA::from_str_named(color.as_ref(), self.colors).map_err(serde::de::Error::custom)
265    }
266}
267
268/// Alpha premultiplied RGBA color in the linear color space (no gamma correction)
269#[repr(transparent)]
270#[derive(Debug, Clone, Copy, PartialEq, Default, Pod, Zeroable)]
271pub struct LinColor(crate::simd::f32x4);
272
273impl LinColor {
274    #[inline(always)]
275    pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
276        LinColor(f32x4::new(r, g, b, a))
277    }
278
279    #[inline(always)]
280    pub fn red(self) -> f32 {
281        self.0.x0()
282    }
283
284    #[inline(always)]
285    pub fn green(self) -> f32 {
286        self.0.x1()
287    }
288
289    #[inline(always)]
290    pub fn blue(self) -> f32 {
291        self.0.x2()
292    }
293
294    #[inline(always)]
295    pub fn alpha(self) -> f32 {
296        self.0.x3()
297    }
298
299    #[inline(always)]
300    pub fn distance(self, other: Self) -> f32 {
301        let diff = self.unmultiply() - other.unmultiply();
302        diff.dot(diff).sqrt()
303    }
304
305    /// Linear color is by default pre-multiplied by alpha, this function removes
306    /// pre-multiplication.
307    #[inline(always)]
308    pub fn unmultiply(self) -> f32x4 {
309        let alpha = self.alpha();
310        if alpha <= 1e-6 {
311            // avoid division by zero, check firefox scene.
312            f32x4::zero()
313        } else {
314            self.0 / f32x4::splat(alpha)
315        }
316    }
317
318    /// Convert into alpha-premultiplied SRGB from Linear RGB
319    ///
320    /// Used by gradients, do not make public
321    #[inline(always)]
322    pub(crate) fn into_srgb(self) -> Self {
323        Self(crate::simd::l2s(self.unmultiply()) * f32x4::splat(self.alpha()))
324    }
325
326    /// Convert into alpha-premultiplied Linear RGB from SRGB
327    ///
328    /// Used by gradient, do not make public
329    #[inline(always)]
330    pub(crate) fn into_linear(self) -> Self {
331        Self(crate::simd::s2l(self.unmultiply()) * f32x4::splat(self.alpha()))
332    }
333}
334
335impl Color for LinColor {
336    #[inline(always)]
337    fn to_rgba(self) -> [u8; 4] {
338        RGBA::from(self).to_rgba()
339    }
340
341    #[inline(always)]
342    fn blend_over(self, other: Self) -> Self {
343        other + self * (1.0 - other.alpha())
344    }
345
346    #[inline(always)]
347    fn with_alpha(self, alpha: Scalar) -> Self {
348        self * (alpha as f32)
349    }
350
351    #[inline(always)]
352    fn lerp(self, other: Self, t: f32) -> Self {
353        other * t + self * (1.0 - t)
354    }
355}
356
357impl Paint for LinColor {
358    fn at(&self, _: Point) -> LinColor {
359        *self
360    }
361
362    fn units(&self) -> Option<Units> {
363        None
364    }
365
366    fn transform(&self) -> Transform {
367        Transform::identity()
368    }
369
370    #[cfg(feature = "serde")]
371    fn to_json(&self) -> Result<serde_json::Value, crate::SvgParserError> {
372        Ok(serde_json::Value::String(self.to_string()))
373    }
374}
375
376impl Add<Self> for LinColor {
377    type Output = Self;
378
379    #[inline(always)]
380    fn add(self, other: Self) -> Self::Output {
381        Self(self.0 + other.0)
382    }
383}
384
385impl Mul<f32> for LinColor {
386    type Output = Self;
387
388    #[inline(always)]
389    fn mul(self, scale: f32) -> Self::Output {
390        Self(self.0 * scale)
391    }
392}
393
394impl From<RGBA> for LinColor {
395    fn from(color: RGBA) -> Self {
396        // !!! overshots at 1.0
397        // let [r, g, b, a] = color.to_rgba();
398        // let rgba = f32x4::new(r as f32, g as f32, b as f32, 255.0) * 0.00392156862745098;
399        // LinColor(s2l(rgba) * f32x4::splat(a as f32 / 255.0))
400        let a = color.alpha() as f32 / 255.0;
401        let r = srgb_to_linear(color.red() as f32 / 255.0) * a;
402        let g = srgb_to_linear(color.green() as f32 / 255.0) * a;
403        let b = srgb_to_linear(color.blue() as f32 / 255.0) * a;
404        LinColor::new(r, g, b, a)
405    }
406}
407
408impl From<LinColor> for [f32; 4] {
409    fn from(color: LinColor) -> Self {
410        color.0.into()
411    }
412}
413
414impl FromStr for LinColor {
415    type Err = ColorError;
416
417    fn from_str(color: &str) -> Result<Self, Self::Err> {
418        Ok(RGBA::from_str(color)?.into())
419    }
420}
421
422impl fmt::Display for LinColor {
423    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
424        RGBA::from(*self).fmt(f)
425    }
426}
427
428impl Color for Scalar {
429    fn to_rgba(self) -> [u8; 4] {
430        let color = (linear_to_srgb(1.0 - (self as f32)) * 255.0 + 0.5) as u8;
431        [color, color, color, 255]
432    }
433
434    fn blend_over(self, other: Self) -> Self {
435        other + self * (1.0 - other)
436    }
437
438    fn with_alpha(self, alpha: Scalar) -> Self {
439        self * alpha
440    }
441
442    fn lerp(self, other: Self, t: f32) -> Self {
443        let t = t as Scalar;
444        self * (1.0 - t) + other * t
445    }
446}
447
448/// Convert Linear RGB color component into a SRGB color component.
449///
450/// It was hard to optimize this function, even current version
451/// is slow because of the conditional jump. Lookup table is not working
452/// here as well it should be at least 4K in size an not cache friendly.
453///
454/// Precise implementation
455/// ```no_run
456/// pub fn linear_to_srgb(value: f32) -> f32 {
457///     if value <= 0.0031308 {
458///         value * 12.92
459///     } else {
460///         1.055 * value.powf(1.0 / 2.4) - 0.055
461///     }
462/// }
463/// ```
464#[inline]
465pub fn linear_to_srgb(x0: f32) -> f32 {
466    if x0 <= 0.0031308 {
467        x0 * 12.92
468    } else {
469        // This function is generated by least square fitting of
470        // `f(x) = 1.055 * x.powf(1.0 / 2.4) - 0.055` on value [0.0031308..1.0]
471        // see `scripts/srgb.py` for details.
472        let x1 = x0.sqrt();
473        let x2 = x1.sqrt();
474        let x3 = x2.sqrt();
475        -0.01848558 * x0 + 0.6445592 * x1 + 0.70994765 * x2 - 0.33605254 * x3
476    }
477}
478
479#[inline]
480pub fn srgb_to_linear(value: f32) -> f32 {
481    if value <= 0.04045 {
482        value / 12.92
483    } else {
484        ((value + 0.055) / 1.055).powf(2.4)
485    }
486}
487
488#[derive(Debug, Clone)]
489pub enum ColorError {
490    HexExpected,
491    InvalidAlpha,
492    UnkownColor(String),
493}
494
495impl fmt::Display for ColorError {
496    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
497        write!(f, "Color format (#RRGGBB(AA)?|<name>)(/<float alpha>)? :")?;
498        match self {
499            ColorError::HexExpected => write!(f, "Hex value expected"),
500            ColorError::InvalidAlpha => write!(f, "Alpha must be float"),
501            ColorError::UnkownColor(name) => write!(f, "Unkown named color: {}", name),
502        }
503    }
504}
505
506impl std::error::Error for ColorError {}
507
508#[cfg(test)]
509mod tests {
510    use super::*;
511    use crate::assert_approx_eq;
512
513    #[test]
514    fn test_color_rgba() {
515        let c = RGBA::new(1, 2, 3, 4);
516        assert_eq!([1, 2, 3, 4], c.to_rgba());
517        assert_eq!(1, c.red());
518        assert_eq!(2, c.green());
519        assert_eq!(3, c.blue());
520        assert_eq!(4, c.alpha());
521    }
522
523    #[test]
524    fn test_color_parse() -> Result<(), ColorError> {
525        assert_eq!(RGBA::new(1, 2, 3, 4), "#01020304".parse::<RGBA>()?);
526        assert_eq!(RGBA::new(170, 187, 204, 255), "#aabbcc".parse::<RGBA>()?);
527        assert_eq!(RGBA::new(0, 0, 0, 255), "#000000".parse::<RGBA>()?);
528        assert_eq!(RGBA::new(1, 2, 3, 63), "#010203/.25".parse::<RGBA>()?);
529        assert_eq!(RGBA::new(0xff, 0x7f, 0x50, 0xff), "coral".parse::<RGBA>()?);
530        assert_eq!(
531            RGBA::new(0xfe, 0x80, 0x19, 0xff),
532            "gruv-orange-2".parse::<RGBA>()?
533        );
534        assert_eq!(SVG_COLORS.len(), 185);
535        Ok(())
536    }
537
538    #[test]
539    fn test_conversion() -> Result<(), ColorError> {
540        let c: RGBA = "#ff804010".parse()?;
541        let l: LinColor = c.into();
542        let r: RGBA = l.into();
543        assert_eq!(c, r);
544        Ok(())
545    }
546
547    #[test]
548    fn test_lin_and_srgb() {
549        for i in 0..255 {
550            let v = i as f32 / 255.0;
551            assert_approx_eq!(v, linear_to_srgb(srgb_to_linear(v)), 1e-4);
552            assert_approx_eq!(v, srgb_to_linear(linear_to_srgb(v)), 1e-4);
553        }
554    }
555
556    #[test]
557    fn test_display_parse() -> Result<(), ColorError> {
558        let c: RGBA = "#01020304".parse()?;
559        assert_eq!(c, RGBA::new(1, 2, 3, 4));
560        assert_eq!(c.to_string(), "#01020304");
561
562        let c: RGBA = "#010203".parse()?;
563        assert_eq!(c, RGBA::new(1, 2, 3, 255));
564        assert_eq!(c.to_string(), "#010203");
565
566        Ok(())
567    }
568
569    #[cfg(feature = "serde")]
570    #[test]
571    fn test_serde() -> Result<(), Box<dyn std::error::Error>> {
572        use serde_json::de::StrRead;
573
574        let mut colors = HashMap::new();
575        colors.insert("aqua".to_owned(), "#008080".parse()?);
576
577        let mut deserializer = serde_json::Deserializer::new(StrRead::new("\"aqua/.5\""));
578        let color = RGBADeserializer { colors: &colors }.deserialize(&mut deserializer)?;
579        assert_eq!(color, "#0080807f".parse()?);
580
581        Ok(())
582    }
583}