0.12.0a5 add OKLab and oklch
This commit is contained in:
parent
ef645bd4da
commit
b1d0c62b44
4 changed files with 105 additions and 12 deletions
|
|
@ -1,11 +1,11 @@
|
|||
# Changelog
|
||||
|
||||
## 0.12.0
|
||||
## 0.12.0 "The Color Update"
|
||||
|
||||
* All `AuthSrc()` derivatives, deprecated and never used, have been removed.
|
||||
* New module `mat` adds a shallow reimplementation of `Matrix()` in order to implement matrix multiplication
|
||||
* Removed obsolete `configparse` implementation that has been around since 0.3 and shelved since 0.4.
|
||||
* `color`: added support for conversion from RGB to sRGB, XYZ
|
||||
* `color`: added support for conversion from RGB to sRGB, XYZ, OKLab and OKLCH.
|
||||
|
||||
## 0.11.2
|
||||
|
||||
|
|
|
|||
|
|
@ -35,20 +35,19 @@ from .strtools import PrefixIdentifier
|
|||
from .validators import matches, not_less_than, not_greater_than, yesno
|
||||
from .redact import redact_url_password
|
||||
from .http import WantsContentType
|
||||
from .color import chalk, WebColor
|
||||
from .color import OKLabColor, chalk, WebColor, RGBColor, SRGBColor, XYZColor, OKLabColor
|
||||
from .mat import Matrix
|
||||
|
||||
__version__ = "0.12.0a4"
|
||||
__version__ = "0.12.0a5"
|
||||
|
||||
__all__ = (
|
||||
'ConfigOptions', 'ConfigParserConfigSource', 'ConfigSource', 'ConfigValue',
|
||||
'DictConfigSource', 'EnvConfigSource', 'I18n', 'Incomplete', 'JsonI18n',
|
||||
'Matrix',
|
||||
'MissingConfigError', 'MissingConfigWarning', 'PrefixIdentifier',
|
||||
'RGBColor', 'SRGBColor',
|
||||
'Matrix', 'MissingConfigError', 'MissingConfigWarning', 'OKLabColor',
|
||||
'PrefixIdentifier', 'RGBColor', 'SRGBColor',
|
||||
'Siq', 'SiqCache', 'SiqGen', 'SiqType', 'Snowflake', 'SnowflakeGen',
|
||||
'StringCase', 'TimedDict', 'TomlI18n', 'UserSigner', 'Wanted', 'WantsContentType',
|
||||
'WebColor',
|
||||
'WebColor', 'XYZColor',
|
||||
'addattr', 'additem', 'age_and_days', 'alru_cache', 'b2048decode', 'b2048encode',
|
||||
'b32ldecode', 'b32lencode', 'b64encode', 'b64decode', 'cb32encode',
|
||||
'cb32decode', 'chalk', 'count_ones', 'dei_args', 'deprecated',
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ from __future__ import annotations
|
|||
|
||||
from collections import namedtuple
|
||||
from functools import lru_cache
|
||||
import math
|
||||
|
||||
from suou.mat import Matrix
|
||||
|
||||
|
|
@ -100,6 +101,9 @@ class RGBColor(namedtuple('_WebColor', 'red green blue')):
|
|||
Representation of a color in the RGB TrueColor space.
|
||||
|
||||
Useful for theming.
|
||||
|
||||
*Changed in 0.12.0*: name is now RGBColor, with WebColor being an alias.
|
||||
Added conversions to and from OKLCH, OKLab, sRGB, and XYZ.
|
||||
"""
|
||||
def lighten(self, *, factor = .75):
|
||||
"""
|
||||
|
|
@ -149,7 +153,8 @@ class RGBColor(namedtuple('_WebColor', 'red green blue')):
|
|||
(-1 if i < 0 else 1) * (((abs(i) + 0.55)) / 1.055) ** 2.4) for i in self
|
||||
))
|
||||
|
||||
|
||||
def to_oklab(self):
|
||||
return self.to_xyz().to_oklab()
|
||||
|
||||
__add__ = blend_with
|
||||
|
||||
|
|
@ -165,6 +170,11 @@ class RGBColor(namedtuple('_WebColor', 'red green blue')):
|
|||
def to_xyz(self):
|
||||
return XYZColor(*(self.RGB_TO_XYZ @ Matrix.as_column(self)).get_column())
|
||||
|
||||
def to_oklch(self):
|
||||
return self.to_xyz().to_oklch()
|
||||
|
||||
def to_oklab(self):
|
||||
return self.to_xyz().to_oklab()
|
||||
|
||||
WebColor = RGBColor
|
||||
|
||||
|
|
@ -192,12 +202,15 @@ class SRGBColor(namedtuple('_SRGBColor', 'red green blue')):
|
|||
def to_xyz(self):
|
||||
return self.to_rgb().to_xyz()
|
||||
|
||||
|
||||
def to_oklab(self):
|
||||
return self.to_rgb().to_oklab()
|
||||
|
||||
|
||||
class XYZColor(namedtuple('_XYZColor', 'x y z')):
|
||||
"""
|
||||
Represent a color in the XYZ color space.
|
||||
|
||||
*New in 0.12.0*
|
||||
"""
|
||||
|
||||
XYZ_TO_RGB = Matrix([
|
||||
|
|
@ -206,10 +219,89 @@ class XYZColor(namedtuple('_XYZColor', 'x y z')):
|
|||
[ 0.05563007969699366, -0.20397695888897652, 1.0569715142428786]
|
||||
])
|
||||
|
||||
XYZ_TO_LMS = Matrix([
|
||||
[0.8190224379967030, 0.3619062600528904, -0.1288737815209879],
|
||||
[0.0329836539323885, 0.9292868615863434, 0.0361446663506424],
|
||||
[0.0481771893596242, 0.2642395317527308, 0.6335478284694309]
|
||||
])
|
||||
|
||||
LMSG_TO_OKLAB = Matrix([
|
||||
[0.2104542683093140, 0.7936177747023054, -0.0040720430116193],
|
||||
[1.9779985324311684, -2.4285922420485799, 0.4505937096174110],
|
||||
[0.0259040424655478, 0.7827717124575296, -0.8086757549230774]
|
||||
])
|
||||
|
||||
def to_rgb(self):
|
||||
return RGBColor(*(self.XYZ_TO_RGB @ Matrix.as_column(self)).get_column())
|
||||
|
||||
def to_oklab(self):
|
||||
lms = (self.XYZ_TO_LMS @ Matrix.as_column(self)).get_column()
|
||||
lmsg = [math.cbrt(i) for i in lms]
|
||||
oklab = (self.LMSG_TO_OKLAB @ Matrix.as_column(self)).get_column()
|
||||
return OKLabColor(*oklab)
|
||||
|
||||
def to_oklch(self):
|
||||
return self.to_oklab().to_oklch()
|
||||
|
||||
|
||||
class OKLabColor(namedtuple('_OKLabColor', 'l a b')):
|
||||
"""
|
||||
Represent a color in the OKLab color space.
|
||||
|
||||
__all__ = ('chalk', 'WebColor', "RGBColor", 'SRGBColor')
|
||||
*New in 0.12.0*
|
||||
"""
|
||||
|
||||
OKLAB_TO_LMSG = Matrix([
|
||||
[1., 0.3963377773761749, 0.2158037573099136],
|
||||
[1., -0.1055613458156586, -0.0638541728258133],
|
||||
[1., -0.0894841775298119, -1.2914855480194092]
|
||||
])
|
||||
|
||||
LMS_TO_XYZ = Matrix([
|
||||
[ 1.2268798758459243, -0.5578149944602171, 0.2813910456659647],
|
||||
[-0.0405757452148008, 1.1122868032803170, -0.0717110580655164],
|
||||
[-0.0763729366746601, -0.4214933324022432, 1.5869240198367816]
|
||||
])
|
||||
|
||||
def to_xyz(self):
|
||||
lmsg = (self.OKLAB_TO_LMSG @ Matrix.as_column(self)).get_column()
|
||||
lms = [i ** 3 for i in lmsg]
|
||||
xyz = (self.LMS_TO_XYZ @ Matrix.as_column(lms)).get_column()
|
||||
return XYZColor(*xyz)
|
||||
|
||||
def to_oklch(self):
|
||||
return OKLCHColor(
|
||||
self.l,
|
||||
math.sqrt(self.a ** 2 + self.b ** 2),
|
||||
0 if abs(self.a) < .0002 and abs(self.b) < .0002 else (((math.atan2(self.b, self.a) * 180) / math.pi % 360) + 360) % 360
|
||||
)
|
||||
|
||||
def to_rgb(self):
|
||||
return self.to_xyz().to_rgb()
|
||||
|
||||
class OKLCHColor(namedtuple('_OKLCHColor', 'l c h')):
|
||||
"""
|
||||
Represent a color in the OKLCH color space.
|
||||
|
||||
*Warning*: conversion to RGB is not bound checked yet!
|
||||
|
||||
*New in 0.12.0*
|
||||
"""
|
||||
|
||||
def __str__(self):
|
||||
l, c, h = round(self.l, 4), round(self.c, 4), round(self.h, 4)
|
||||
|
||||
return f'oklch({l}, {c}, {h})'
|
||||
|
||||
|
||||
def to_oklab(self):
|
||||
return OKLabColor(
|
||||
self.l,
|
||||
self.c * math.cos(self.h * math.pi / 180),
|
||||
self.h * math.cos(self.h * math.pi / 180)
|
||||
)
|
||||
|
||||
def to_rgb(self):
|
||||
return self.to_oklab().to_rgb()
|
||||
|
||||
__all__ = ('chalk', 'WebColor', "RGBColor", 'SRGBColor', 'XYZColor', 'OKLabColor')
|
||||
|
|
|
|||
|
|
@ -24,7 +24,9 @@ _T = TypeVar('_T')
|
|||
|
||||
class Matrix(Collection[_T]):
|
||||
"""
|
||||
Shallow reimplementation of numpy's matrices in pure Python.
|
||||
Minimalist reimplementation of matrices in pure Python.
|
||||
|
||||
This to avoid adding numpy as a dependency.
|
||||
|
||||
*New in 0.12.0*
|
||||
"""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue