tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

color.rs (4889B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
      4 
      5 //! Computed color values.
      6 
      7 use crate::color::AbsoluteColor;
      8 use crate::values::animated::ToAnimatedZero;
      9 use crate::values::computed::percentage::Percentage;
     10 use crate::values::generics::color::{
     11    GenericCaretColor, GenericColor, GenericColorMix, GenericColorOrAuto,
     12 };
     13 use std::fmt::{self, Write};
     14 use style_traits::{CssWriter, ToCss};
     15 
     16 pub use crate::values::specified::color::{ColorScheme, ForcedColorAdjust, PrintColorAdjust};
     17 
     18 /// The computed value of the `color` property.
     19 pub type ColorPropertyValue = AbsoluteColor;
     20 
     21 /// A computed value for `<color>`.
     22 pub type Color = GenericColor<Percentage>;
     23 
     24 /// A computed color-mix().
     25 pub type ColorMix = GenericColorMix<Color, Percentage>;
     26 
     27 impl ToCss for Color {
     28    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
     29    where
     30        W: fmt::Write,
     31    {
     32        match *self {
     33            Self::Absolute(ref c) => c.to_css(dest),
     34            Self::ColorFunction(ref color_function) => color_function.to_css(dest),
     35            Self::CurrentColor => dest.write_str("currentcolor"),
     36            Self::ColorMix(ref m) => m.to_css(dest),
     37            Self::ContrastColor(ref c) => {
     38                dest.write_str("contrast-color(")?;
     39                c.to_css(dest)?;
     40                dest.write_char(')')
     41            },
     42        }
     43    }
     44 }
     45 
     46 impl Color {
     47    /// A fully transparent color.
     48    pub const TRANSPARENT_BLACK: Self = Self::Absolute(AbsoluteColor::TRANSPARENT_BLACK);
     49 
     50    /// An opaque black color.
     51    pub const BLACK: Self = Self::Absolute(AbsoluteColor::BLACK);
     52 
     53    /// An opaque white color.
     54    pub const WHITE: Self = Self::Absolute(AbsoluteColor::WHITE);
     55 
     56    /// Create a new computed [`Color`] from a given color-mix, simplifying it to an absolute color
     57    /// if possible.
     58    pub fn from_color_mix(color_mix: ColorMix) -> Self {
     59        if let Some(absolute) = color_mix.mix_to_absolute() {
     60            Self::Absolute(absolute)
     61        } else {
     62            Self::ColorMix(Box::new(color_mix))
     63        }
     64    }
     65 
     66    /// Combine this complex color with the given foreground color into an
     67    /// absolute color.
     68    pub fn resolve_to_absolute(&self, current_color: &AbsoluteColor) -> AbsoluteColor {
     69        use crate::values::specified::percentage::ToPercentage;
     70 
     71        match *self {
     72            Self::Absolute(c) => c,
     73            Self::ColorFunction(ref color_function) => {
     74                color_function.resolve_to_absolute(current_color)
     75            },
     76            Self::CurrentColor => *current_color,
     77            Self::ColorMix(ref mix) => {
     78                let left = mix.left.resolve_to_absolute(current_color);
     79                let right = mix.right.resolve_to_absolute(current_color);
     80                crate::color::mix::mix(
     81                    mix.interpolation,
     82                    &left,
     83                    mix.left_percentage.to_percentage(),
     84                    &right,
     85                    mix.right_percentage.to_percentage(),
     86                    mix.flags,
     87                )
     88            },
     89            Self::ContrastColor(ref c) => {
     90                let bg_color = c.resolve_to_absolute(current_color);
     91                if Self::contrast_ratio(&bg_color, &AbsoluteColor::BLACK)
     92                    > Self::contrast_ratio(&bg_color, &AbsoluteColor::WHITE)
     93                {
     94                    AbsoluteColor::BLACK
     95                } else {
     96                    AbsoluteColor::WHITE
     97                }
     98            },
     99        }
    100    }
    101 
    102    fn contrast_ratio(a: &AbsoluteColor, b: &AbsoluteColor) -> f32 {
    103        // TODO: This just implements the WCAG 2.1 algorithm,
    104        // https://www.w3.org/TR/WCAG21/#dfn-contrast-ratio
    105        // Consider using a more sophisticated contrast algorithm, e.g. see
    106        // https://apcacontrast.com
    107        let compute = |c| -> f32 {
    108            if c <= 0.04045 {
    109                c / 12.92
    110            } else {
    111                f32::powf((c + 0.055) / 1.055, 2.4)
    112            }
    113        };
    114        let luminance = |r, g, b| -> f32 { 0.2126 * r + 0.7152 * g + 0.0722 * b };
    115        let a = a.into_srgb_legacy();
    116        let b = b.into_srgb_legacy();
    117        let a = a.raw_components();
    118        let b = b.raw_components();
    119        let la = luminance(compute(a[0]), compute(a[1]), compute(a[2])) + 0.05;
    120        let lb = luminance(compute(b[0]), compute(b[1]), compute(b[2])) + 0.05;
    121        if la > lb {
    122            la / lb
    123        } else {
    124            lb / la
    125        }
    126    }
    127 }
    128 
    129 impl ToAnimatedZero for AbsoluteColor {
    130    fn to_animated_zero(&self) -> Result<Self, ()> {
    131        Ok(Self::TRANSPARENT_BLACK)
    132    }
    133 }
    134 
    135 /// auto | <color>
    136 pub type ColorOrAuto = GenericColorOrAuto<Color>;
    137 
    138 /// caret-color
    139 pub type CaretColor = GenericCaretColor<Color>;