62 lines
1.7 KiB
Python
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)
|