matrix-ops-bot/ops_bot/util/contrast.py
2022-12-01 14:20:37 +00:00

62 lines
1.7 KiB
Python

# Based on https://github.com/gsnedders/wcag-contrast-ratio
# Copyright (c) 2015 Geoffrey Sneddon
# Copyright (c) 2019 Tulir Asokan
# MIT license
from typing import Tuple
RGB = Tuple[float, float, float]
def hex_to_rgb(color: str) -> RGB:
color = color.lstrip("#")
if len(color) != 3 and len(color) != 6:
raise ValueError("Invalid hex length")
step = 1 if len(color) == 3 else 2
try:
r = int(color[0:step], 16)
g = int(color[step : 2 * step], 16)
b = int(color[2 * step : 3 * step], 16)
except ValueError as e:
raise ValueError("Invalid hex value") from e
return r / 255, g / 255, b / 255
def rgb_to_hex(rgb: RGB) -> str:
r, g, b = rgb
r = int(r * 255)
g = int(g * 255)
b = int(b * 255)
return f"{r:02x}{g:02x}{b:02x}"
def contrast(rgb1: RGB, rgb2: RGB) -> float:
for r, g, b in (rgb1, rgb2):
if not 0.0 <= r <= 1.0:
raise ValueError(f"r {r} is out of valid range (0.0 - 1.0)")
if not 0.0 <= g <= 1.0:
raise ValueError(f"g {g} is out of valid range (0.0 - 1.0)")
if not 0.0 <= b <= 1.0:
raise ValueError(f"b {b} is out of valid range (0.0 - 1.0)")
l1 = _relative_luminance(*rgb1)
l2 = _relative_luminance(*rgb2)
if l1 > l2:
return (l1 + 0.05) / (l2 + 0.05)
else:
return (l2 + 0.05) / (l1 + 0.05)
def _relative_luminance(r: float, g: float, b: float) -> float:
r = _linearize(r)
g = _linearize(g)
b = _linearize(b)
return 0.2126 * r + 0.7152 * g + 0.0722 * b
def _linearize(v: float) -> float:
if v <= 0.03928:
return v / 12.92
else:
return float(((v + 0.055) / 1.055) ** 2.4)