Last active
February 18, 2024 22:34
-
-
Save facelessuser/9ed4c9b719404e8ae338484c92fc1f7a to your computer and use it in GitHub Desktop.
Scale LH and tones
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# pragma: init | |
from coloraide.gamut import Fit | |
from coloraide.spaces import RGBish | |
class OkLChScale(Fit): | |
""" | |
Gamut mapping by scaling. | |
Expected gamut mapping spaces are RGB type spaces. | |
For best results, linear light RGB spaces are preferred. | |
""" | |
NAME = "oklch-scale" | |
SPACE = "oklch" | |
def fit(self, color, space, **kwargs): | |
"""Scale the color within its gamut but preserve L and h as much as possible.""" | |
# Requires an RGB-ish space, preferably a linear space. | |
if not isinstance(color.CS_MAP[space], RGBish): | |
raise ValueError("Scaling only works in an RGBish color space, not {}".format(type(color.CS_MAP[space]))) | |
# For now, if a non-linear CSS variant is specified, just use the linear form. | |
if space in {'srgb', 'display-p3', 'rec2020', 'a98-rgb', 'prophoto-rgb'}: | |
space += '-linear' | |
orig = color.space() | |
mapcolor = color.convert(self.SPACE, norm=False) if orig != self.SPACE else color.clone().normalize(nans=False) | |
gamutcolor = color.convert(space, norm=False) if orig != space else color.clone().normalize(nans=False) | |
self.scale(gamutcolor) | |
gamutcolor.set( | |
{ | |
self.SPACE + '.l': mapcolor['l'], | |
self.SPACE + '.h': mapcolor['h'] | |
} | |
) | |
self.scale(gamutcolor) | |
color.update(gamutcolor) | |
def scale(self, color): | |
"""Scale the RGB color within its gamut.""" | |
deltas = [c - 0.5 for c in color[:-1]] | |
max_distance = max(abs(c) for c in deltas) | |
scalingFactor = max_distance / 0.5; | |
color[:-1] = [c / scalingFactor + 0.5 for c in deltas] | |
from coloraide.everything import ColorAll as Base | |
class Color(Base): ... | |
Color.register(OkLChScale()) | |
# pragma: init | |
K_1 = 0.173 | |
K_2 = 0.004 | |
K_3 = (1.0 + K_1) / (1.0 + K_2) | |
def toe_inv(x: float) -> float: | |
"""Inverse toe function for L_r.""" | |
return (x ** 2 + K_1 * x) / (K_3 * (x + K_2)) | |
def reduce_oklch_tonal_palette(c): | |
c = Color(c).convert('oklch') | |
tones = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 100] | |
return [c.clone().set('l', toe_inv(tone / 100)).fit('srgb', method='oklch-chroma') for tone in tones] | |
def scale_oklch_tonal_palette(c): | |
c = Color(c).convert('oklch') | |
tones = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 100] | |
return [c.clone().set('l', tone / 100).fit('srgb', method='oklch-scale') for tone in tones] | |
Steps(scale_oklch_tonal_palette('red')) | |
Steps(reduce_oklch_tonal_palette('red')) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment