Skip to content

Instantly share code, notes, and snippets.

@facelessuser
Last active March 7, 2022 13:39
Show Gist options
  • Save facelessuser/43fc5aef3a753195167b1ca0ffe4091f to your computer and use it in GitHub Desktop.
Save facelessuser/43fc5aef3a753195167b1ca0ffe4091f to your computer and use it in GitHub Desktop.
CSS L4 OKLCH Gamut map mix
from coloraide.gamut import Fit, clip_channels
from coloraide.util import NaN
class OklchChroma(Fit):
"""Lch chroma gamut mapping class."""
NAME = "oklch-chroma"
EPSILON = 0.0001
LIMIT = 0.02
DE = "ok"
SPACE = "oklch"
SPACE_COORDINATE = "{}.chroma".format(SPACE)
MIN_LIGHTNESS = 0
MAX_LIGHTNESS = 1
@classmethod
def fit(cls, color, **kwargs):
"""
Gamut mapping via Oklch chroma.
"""
space = color.space()
mapcolor = color.convert(cls.SPACE)
lightness = mapcolor.lightness
# Return white or black if lightness is out of range
if lightness >= cls.MAX_LIGHTNESS or lightness <= cls.MIN_LIGHTNESS:
mapcolor.chroma = 0
mapcolor.hue = NaN
clip_channels(color.update(mapcolor))
return
# Set initial chroma boundaries
low = 0.0
high = mapcolor.chroma
clip_channels(color.update(mapcolor))
# Adjust chroma (using binary search).
# This helps preserve the other attributes of the color.
# Compress chroma until we are are right at the JND edge of being out of gamut.
if not mapcolor.in_gamut(space):
while True:
mapcolor.chroma = (high + low) * 0.5
if mapcolor.in_gamut(space, tolerance=0):
low = mapcolor.chroma
else:
clip_channels(color.update(mapcolor))
if mapcolor.delta_e(color, method=cls.DE) < cls.LIMIT:
break
high = mapcolor.chroma
class Color2(Color):
FIT = 'oklch-chroma'
Color2.register(OklchChroma, overwrite=True)
Color2('color(display-p3 0 1 0)').mix('rgb(0, 0, 0)', 0, space='display-p3').convert('srgb').to_string()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment