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>;