Skip to main content

compose_rust/
contrast.rs

1//! How far apart two colours are, in the two ways the colour roles are held to: contrast
2//! for an ink on a fill, and separation for a fill that has to be seen on another.
3//!
4//! The design system tables are tested against these, an application's palette is checked
5//! against them, and the Renderer chooses an ink with the same arithmetic.
6
7use crate::schema::{Color, ColorRole};
8
9/// The fifteen syntax inks.
10pub const SYNTAX_ROLES: [ColorRole; 15] = [
11    ColorRole::SyntaxKeyword,
12    ColorRole::SyntaxString,
13    ColorRole::SyntaxComment,
14    ColorRole::SyntaxNumber,
15    ColorRole::SyntaxConstant,
16    ColorRole::SyntaxType,
17    ColorRole::SyntaxFunction,
18    ColorRole::SyntaxVariable,
19    ColorRole::SyntaxProperty,
20    ColorRole::SyntaxOperator,
21    ColorRole::SyntaxPunctuation,
22    ColorRole::SyntaxTag,
23    ColorRole::SyntaxAttribute,
24    ColorRole::SyntaxEscape,
25    ColorRole::SyntaxMacro,
26];
27
28/// Each diff ink with its line background and its word background.
29pub const DIFF_ROLES: [(ColorRole, ColorRole, ColorRole); 2] = [
30    (
31        ColorRole::DiffAdded,
32        ColorRole::DiffAddedContainer,
33        ColorRole::DiffAddedEmphasis,
34    ),
35    (
36        ColorRole::DiffRemoved,
37        ColorRole::DiffRemovedContainer,
38        ColorRole::DiffRemovedEmphasis,
39    ),
40];
41
42/// How far apart, summed over three eight bit channels, two fills have to be before one
43/// can be seen on the other. The same figure the system tables are tested against.
44pub const MIN_SEPARATION: u32 = 24;
45
46fn channel(value: u32) -> f64 {
47    let value = f64::from(value) / 255.0;
48    if value <= 0.040_45 {
49        value / 12.92
50    } else {
51        ((value + 0.055) / 1.055).powf(2.4)
52    }
53}
54
55/// Relative luminance, as WCAG defines it.
56pub fn luminance(color: Color) -> f64 {
57    let argb = color.to_argb();
58    0.2126 * channel((argb >> 16) & 0xff)
59        + 0.7152 * channel((argb >> 8) & 0xff)
60        + 0.0722 * channel(argb & 0xff)
61}
62
63/// The WCAG contrast ratio of two opaque colours, from 1 to 21.
64pub fn contrast(first: Color, second: Color) -> f64 {
65    let (a, b) = (luminance(first), luminance(second));
66    let (high, low) = if a > b { (a, b) } else { (b, a) };
67    (high + 0.05) / (low + 0.05)
68}
69
70/// Summed channel distance in eight bit terms.
71pub fn apart(first: Color, second: Color) -> u32 {
72    [16, 8, 0]
73        .into_iter()
74        .map(|shift| {
75            let a = (first.to_argb() >> shift) & 0xff;
76            let b = (second.to_argb() >> shift) & 0xff;
77            a.abs_diff(b)
78        })
79        .sum()
80}