chromatic-python 0.4.2__tar.gz → 0.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {chromatic_python-0.4.2/chromatic_python.egg-info → chromatic_python-0.5.0}/PKG-INFO +5 -5
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/chromatic/_typing.py +17 -20
- chromatic_python-0.5.0/chromatic/_version.py +24 -0
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/chromatic/color/colorconv.py +37 -34
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/chromatic/color/core.py +430 -311
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/chromatic/color/core.pyi +148 -138
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/chromatic/color/iterators.py +2 -2
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/chromatic/color/palette.py +62 -54
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/chromatic/color/palette.pyi +8 -50
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/chromatic/data/__init__.py +1 -1
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/chromatic/data/__init__.pyi +1 -1
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/chromatic/data/_fetchers.py +1 -0
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/chromatic/data/userfont.py +3 -3
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/chromatic/demo.py +10 -10
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/chromatic/image/_array.py +95 -91
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/chromatic/image/_curses.py +10 -10
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/chromatic/image/_glyph.py +16 -14
- {chromatic_python-0.4.2 → chromatic_python-0.5.0/chromatic_python.egg-info}/PKG-INFO +5 -5
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/chromatic_python.egg-info/SOURCES.txt +0 -1
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/chromatic_python.egg-info/requires.txt +2 -2
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/pyproject.toml +12 -4
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/tests/test_color_str.py +34 -13
- chromatic_python-0.4.2/chromatic/_version.py +0 -34
- chromatic_python-0.4.2/requirements.txt +0 -53
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/.gitattributes +0 -0
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/.github/workflows/publish.yml +0 -0
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/.gitignore +0 -0
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/README.md +0 -0
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/banner.png +0 -0
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/chromatic/__init__.py +0 -0
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/chromatic/__init__.pyi +1 -1
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/chromatic/color/__init__.py +0 -0
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/chromatic/color/__init__.pyi +3 -3
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/chromatic/data/butterfly.jpg +0 -0
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/chromatic/data/escher.png +0 -0
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/chromatic/data/fonts/consolas.ttf +0 -0
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/chromatic/data/fonts/vga437.ttf +0 -0
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/chromatic/data/goblin_virus.png +0 -0
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/chromatic/data/registry.json +0 -0
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/chromatic/data/userfont.pyi +1 -1
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/chromatic/data/userfont.schema.json +0 -0
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/chromatic/image/__init__.py +0 -0
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/chromatic/image/__init__.pyi +0 -0
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/chromatic_python.egg-info/dependency_links.txt +0 -0
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/chromatic_python.egg-info/top_level.txt +0 -0
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/setup.cfg +0 -0
- {chromatic_python-0.4.2 → chromatic_python-0.5.0}/tests/__init__.py +0 -0
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: chromatic-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: ANSI art image processing and colored terminal text
|
|
5
5
|
Author: crypt0lith
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
Project-URL: repository, https://github.com/crypt0lith/chromatic.git
|
|
8
8
|
Keywords: ansi,ascii,art,font,image,terminal,parser
|
|
9
|
-
Classifier: Programming Language :: Python :: 3.
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
10
10
|
Classifier: Operating System :: OS Independent
|
|
11
11
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
12
12
|
Classifier: Typing :: Typed
|
|
13
|
-
Requires-Python:
|
|
13
|
+
Requires-Python: <4.0,>=3.13
|
|
14
14
|
Description-Content-Type: text/markdown
|
|
15
15
|
Requires-Dist: numpy<2.6,>=1.25.2
|
|
16
|
-
Requires-Dist: pillow
|
|
16
|
+
Requires-Dist: pillow>=12.2.0
|
|
17
17
|
Requires-Dist: networkx<4.0,>=3.0
|
|
18
18
|
Requires-Dist: scipy<2.0,>=1.11.4
|
|
19
19
|
Requires-Dist: opencv-python
|
|
20
20
|
Requires-Dist: scikit-image
|
|
21
21
|
Requires-Dist: scikit-learn
|
|
22
|
-
Requires-Dist: fonttools
|
|
22
|
+
Requires-Dist: fonttools>=4.60.2
|
|
23
23
|
Requires-Dist: lazy_loader
|
|
24
24
|
|
|
25
25
|

|
|
@@ -7,8 +7,8 @@ import types
|
|
|
7
7
|
from collections import OrderedDict, namedtuple
|
|
8
8
|
from collections.abc import Callable as ABC_Callable
|
|
9
9
|
from functools import reduce, wraps
|
|
10
|
-
from numbers import Number
|
|
11
10
|
from typing import (
|
|
11
|
+
TYPE_CHECKING,
|
|
12
12
|
Any,
|
|
13
13
|
Callable,
|
|
14
14
|
Concatenate,
|
|
@@ -20,13 +20,12 @@ from typing import (
|
|
|
20
20
|
ParamSpec,
|
|
21
21
|
Protocol,
|
|
22
22
|
Sequence,
|
|
23
|
-
TYPE_CHECKING,
|
|
24
23
|
Type,
|
|
25
24
|
TypeAlias,
|
|
26
25
|
TypeAliasType,
|
|
26
|
+
TypedDict,
|
|
27
27
|
TypeGuard,
|
|
28
28
|
TypeVar,
|
|
29
|
-
TypedDict,
|
|
30
29
|
Union,
|
|
31
30
|
Unpack,
|
|
32
31
|
cast,
|
|
@@ -36,19 +35,17 @@ from typing import (
|
|
|
36
35
|
runtime_checkable,
|
|
37
36
|
)
|
|
38
37
|
|
|
38
|
+
from numpy import dtype, float64, generic, ndarray, uint8
|
|
39
|
+
from numpy._typing import NDArray, _ArrayLike
|
|
39
40
|
from PIL.Image import Image
|
|
40
41
|
from PIL.ImageFont import FreeTypeFont
|
|
41
|
-
from numpy import dtype, float64, generic, ndarray, number, uint8
|
|
42
|
-
from numpy._typing import NDArray, _ArrayLike
|
|
43
42
|
|
|
44
43
|
if TYPE_CHECKING:
|
|
45
44
|
from .data import UserFont
|
|
46
45
|
|
|
47
46
|
_P = ParamSpec('_P')
|
|
48
|
-
_T = TypeVar('_T')
|
|
49
47
|
_T_co = TypeVar('_T_co', covariant=True)
|
|
50
48
|
_T_contra = TypeVar('_T_contra', contravariant=True)
|
|
51
|
-
_AnyNumber_co = TypeVar('_AnyNumber_co', number, Number, covariant=True)
|
|
52
49
|
|
|
53
50
|
type ArrayReducerFunc[_SCT: generic] = Callable[
|
|
54
51
|
Concatenate[_ArrayLike[_SCT], _P], NDArray[_SCT]
|
|
@@ -180,16 +177,16 @@ def is_matching_type(value, typ):
|
|
|
180
177
|
return False
|
|
181
178
|
|
|
182
179
|
|
|
183
|
-
def is_matching_typed_dict(
|
|
184
|
-
if TypedDict not in getattr(
|
|
185
|
-
return False, type_error_msg(
|
|
180
|
+
def is_matching_typed_dict(d: dict, /, typed_dict: type[dict]) -> tuple[bool, str]:
|
|
181
|
+
if TypedDict not in getattr(d, '__orig_bases__', ()):
|
|
182
|
+
return False, type_error_msg(d, dict)
|
|
186
183
|
expected = get_type_hints(typed_dict)
|
|
187
|
-
if unexpected :=
|
|
184
|
+
if unexpected := d.keys() - expected:
|
|
188
185
|
return False, f"unexpected keyword arguments: {unexpected}"
|
|
189
|
-
if missing := set(getattr(typed_dict, '__required_keys__', expected)) -
|
|
186
|
+
if missing := set(getattr(typed_dict, '__required_keys__', expected)) - d.keys():
|
|
190
187
|
return False, f"missing required keys: {missing}"
|
|
191
188
|
for name, typ in expected.items():
|
|
192
|
-
field =
|
|
189
|
+
field = d.get(name)
|
|
193
190
|
if field is None or is_matching_type(field, typ):
|
|
194
191
|
continue
|
|
195
192
|
return False, type_error_msg(
|
|
@@ -214,15 +211,14 @@ class SupportsUnion(Protocol[_T_contra, _T_co]):
|
|
|
214
211
|
def __or__(self, x: _T_contra, /) -> _T_co: ...
|
|
215
212
|
|
|
216
213
|
|
|
217
|
-
def unionize(
|
|
218
|
-
return reduce(op.or_,
|
|
214
|
+
def unionize(iterable: Iterable[SupportsUnion[_T_contra, _T_co]], /) -> _T_co:
|
|
215
|
+
return reduce(op.or_, iterable)
|
|
219
216
|
|
|
220
217
|
|
|
221
218
|
_GenericAlias = type(Type[...]) | types.GenericAlias
|
|
222
219
|
_UnionGenericType = type(Union[..., None])
|
|
223
220
|
_LiteralGenericType = type(L[''])
|
|
224
221
|
_CallableGenericType = type(Callable[[], ...]) | type(ABC_Callable[[], ...])
|
|
225
|
-
_CallableType = type(Callable) | ABC_Callable
|
|
226
222
|
|
|
227
223
|
|
|
228
224
|
class _BoundedDict[_KT, _VT](OrderedDict[_KT, _VT]):
|
|
@@ -346,11 +342,12 @@ def _sort_attrs(obj, tp_name, attr_names):
|
|
|
346
342
|
return attr_names, field_names
|
|
347
343
|
|
|
348
344
|
|
|
349
|
-
|
|
350
|
-
type TypeVarDict = dict[TypeVar, ...]
|
|
345
|
+
type TypeVarDict = dict[TypeVar, ...]
|
|
351
346
|
|
|
352
|
-
|
|
353
|
-
|
|
347
|
+
|
|
348
|
+
def subtype[_T](typ: _T) -> _T:
|
|
349
|
+
def _serialize(item: tuple[Any, Hashable], /):
|
|
350
|
+
key, value = item
|
|
354
351
|
return _unique_attrs(key), hash(value)
|
|
355
352
|
|
|
356
353
|
def _cache_key(tp, tvars: TypeVarDict):
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"__version__",
|
|
7
|
+
"__version_tuple__",
|
|
8
|
+
"version",
|
|
9
|
+
"version_tuple",
|
|
10
|
+
"__commit_id__",
|
|
11
|
+
"commit_id",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
__version__: str
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
20
|
+
|
|
21
|
+
__version__ = version = '0.5.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 5, 0)
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = 'g066855df5'
|
|
@@ -25,6 +25,7 @@ __all__ = [
|
|
|
25
25
|
|
|
26
26
|
from functools import lru_cache
|
|
27
27
|
from operator import mul, truediv
|
|
28
|
+
from types import MappingProxyType as mappingproxy
|
|
28
29
|
from typing import Final, Literal, SupportsInt, TypeGuard
|
|
29
30
|
|
|
30
31
|
import numpy as np
|
|
@@ -40,11 +41,11 @@ from .._typing import (
|
|
|
40
41
|
|
|
41
42
|
|
|
42
43
|
@lru_cache
|
|
43
|
-
def _supports_int(typ: type) -> TypeGuard[type[SupportsInt]]:
|
|
44
|
+
def _supports_int(typ: type, /) -> TypeGuard[type[SupportsInt]]:
|
|
44
45
|
return issubclass(typ, SupportsInt)
|
|
45
46
|
|
|
46
47
|
|
|
47
|
-
def is_u24(value, *, strict: bool = False):
|
|
48
|
+
def is_u24(value, *, strict: bool = False) -> bool:
|
|
48
49
|
"""Check if value is an unsigned 24-bit integer.
|
|
49
50
|
|
|
50
51
|
Parameters
|
|
@@ -60,44 +61,46 @@ def is_u24(value, *, strict: bool = False):
|
|
|
60
61
|
Raised when `strict=True` and value is not u24
|
|
61
62
|
"""
|
|
62
63
|
if _supports_int(value.__class__):
|
|
63
|
-
if 0 <= int(value)
|
|
64
|
+
if 0 <= int(value) < (1 << 24):
|
|
64
65
|
return True
|
|
65
66
|
elif not strict:
|
|
66
67
|
return False
|
|
67
68
|
raise ValueError(f"{value!r} is not u24")
|
|
68
69
|
|
|
69
70
|
|
|
70
|
-
def hexstr2rgb(
|
|
71
|
-
n = len(
|
|
71
|
+
def hexstr2rgb(s: str, /) -> Int3Tuple:
|
|
72
|
+
n = len(s)
|
|
72
73
|
if n % 4 == 0: # trunc alpha
|
|
73
74
|
n *= 3
|
|
74
75
|
n //= 4
|
|
75
|
-
|
|
76
|
+
s = s[:n]
|
|
76
77
|
if n == 3: # rgb -> rrggbb
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
s = ''.join(c * 2 for c in s)
|
|
79
|
+
x = int(s, 16)
|
|
80
|
+
if not 0 <= x < (1 << 24):
|
|
81
|
+
raise ValueError(f"{x:#x} is not u24")
|
|
82
|
+
return int2rgb(x)
|
|
80
83
|
|
|
81
84
|
|
|
82
|
-
def rgb2hexstr(rgb: RGBVectorLike) -> str:
|
|
85
|
+
def rgb2hexstr(rgb: RGBVectorLike, /) -> str:
|
|
83
86
|
return "%02x%02x%02x" % tuple(rgb)
|
|
84
87
|
|
|
85
88
|
|
|
86
|
-
def rgb2int(rgb: RGBVectorLike) -> int:
|
|
89
|
+
def rgb2int(rgb: RGBVectorLike, /) -> int:
|
|
87
90
|
r, g, b = map(int, rgb)
|
|
88
91
|
return r << 16 | g << 8 | b
|
|
89
92
|
|
|
90
93
|
|
|
91
|
-
def int2rgb(
|
|
94
|
+
def int2rgb(x: int, /) -> Int3Tuple:
|
|
92
95
|
try:
|
|
93
|
-
return getattr(
|
|
96
|
+
return getattr(x, 'rgb')
|
|
94
97
|
except AttributeError:
|
|
95
98
|
pass
|
|
96
|
-
x = int(
|
|
99
|
+
x = int(x) & 0xFFFFFF
|
|
97
100
|
return (x >> 16) & 0xFF, (x >> 8) & 0xFF, x & 0xFF
|
|
98
101
|
|
|
99
102
|
|
|
100
|
-
def xyz2lab(xyz: FloatSequence) -> Float3Tuple:
|
|
103
|
+
def xyz2lab(xyz: FloatSequence, /) -> Float3Tuple:
|
|
101
104
|
x, y, z = (
|
|
102
105
|
n ** (1 / 3) if n > 0.008856 else (7.787 * n) + (16 / 116)
|
|
103
106
|
for n in map(truediv, xyz, (95.047, 100.0, 108.883))
|
|
@@ -108,7 +111,7 @@ def xyz2lab(xyz: FloatSequence) -> Float3Tuple:
|
|
|
108
111
|
return L, a, b
|
|
109
112
|
|
|
110
113
|
|
|
111
|
-
def lab2xyz(lab: FloatSequence) -> Float3Tuple:
|
|
114
|
+
def lab2xyz(lab: FloatSequence, /) -> Float3Tuple:
|
|
112
115
|
L, a, b = lab
|
|
113
116
|
y = (L + 16) / 116
|
|
114
117
|
x = a / 500 + y
|
|
@@ -133,19 +136,19 @@ M_RGB2XYZ = np.array(
|
|
|
133
136
|
M_XYZ2RGB = np.linalg.inv(M_RGB2XYZ)
|
|
134
137
|
|
|
135
138
|
|
|
136
|
-
def rgb2xyz(rgb: RGBPixel) -> Float3Tuple:
|
|
139
|
+
def rgb2xyz(rgb: RGBPixel, /) -> Float3Tuple:
|
|
137
140
|
x, y, z = M_RGB2XYZ @ (np.array(rgb, dtype=np.float64) / 255.0)
|
|
138
141
|
return x, y, z
|
|
139
142
|
|
|
140
143
|
|
|
141
|
-
def xyz2rgb(xyz: ShapedNDArray[tuple[Literal[3]], np.float64]) -> Int3Tuple:
|
|
144
|
+
def xyz2rgb(xyz: ShapedNDArray[tuple[Literal[3]], np.float64], /) -> Int3Tuple:
|
|
142
145
|
r, g, b = (
|
|
143
146
|
np.clip(M_XYZ2RGB @ np.array(xyz, dtype=np.float64), 0.0, 1.0) * 255.0
|
|
144
147
|
).astype(int)
|
|
145
148
|
return r, g, b
|
|
146
149
|
|
|
147
150
|
|
|
148
|
-
def hsl2rgb(hsl: FloatSequence) -> Int3Tuple:
|
|
151
|
+
def hsl2rgb(hsl: FloatSequence, /) -> Int3Tuple:
|
|
149
152
|
h, s, L = hsl
|
|
150
153
|
h /= 360
|
|
151
154
|
h %= 1
|
|
@@ -174,7 +177,7 @@ def hsl2rgb(hsl: FloatSequence) -> Int3Tuple:
|
|
|
174
177
|
return r, g, b
|
|
175
178
|
|
|
176
179
|
|
|
177
|
-
def rgb2hsl(rgb: RGBVectorLike) -> Float3Tuple:
|
|
180
|
+
def rgb2hsl(rgb: RGBVectorLike, /) -> Float3Tuple:
|
|
178
181
|
r, g, b = (x / 255.0 for x in rgb)
|
|
179
182
|
m, _, v = sorted([r, g, b])
|
|
180
183
|
L = (m + v) / 2
|
|
@@ -196,7 +199,7 @@ def rgb2hsl(rgb: RGBVectorLike) -> Float3Tuple:
|
|
|
196
199
|
return (360 * h, s, L)
|
|
197
200
|
|
|
198
201
|
|
|
199
|
-
def hsv2rgb(hsv: FloatSequence) -> Int3Tuple:
|
|
202
|
+
def hsv2rgb(hsv: FloatSequence, /) -> Int3Tuple:
|
|
200
203
|
h, s, v = hsv
|
|
201
204
|
c = v * s
|
|
202
205
|
x = c * (1 - abs((h / 60) % 2 - 1))
|
|
@@ -220,7 +223,7 @@ def hsv2rgb(hsv: FloatSequence) -> Int3Tuple:
|
|
|
220
223
|
return r, g, b
|
|
221
224
|
|
|
222
225
|
|
|
223
|
-
def rgb2hsv(rgb: RGBVectorLike) -> Float3Tuple:
|
|
226
|
+
def rgb2hsv(rgb: RGBVectorLike, /) -> Float3Tuple:
|
|
224
227
|
r, g, b = (x / 255.0 for x in rgb)
|
|
225
228
|
m, v = sorted([r, g, b])[::2]
|
|
226
229
|
delta = v - m
|
|
@@ -239,17 +242,17 @@ def rgb2hsv(rgb: RGBVectorLike) -> Float3Tuple:
|
|
|
239
242
|
return h, s, v
|
|
240
243
|
|
|
241
244
|
|
|
242
|
-
def lab2rgb(lab: FloatSequence) -> Int3Tuple:
|
|
245
|
+
def lab2rgb(lab: FloatSequence, /) -> Int3Tuple:
|
|
243
246
|
xyz = lab2xyz(lab)
|
|
244
247
|
return xyz2rgb(np.array(xyz, dtype=np.float64))
|
|
245
248
|
|
|
246
249
|
|
|
247
|
-
def rgb2lab(rgb: RGBVectorLike) -> Float3Tuple:
|
|
250
|
+
def rgb2lab(rgb: RGBVectorLike, /) -> Float3Tuple:
|
|
248
251
|
xyz = rgb2xyz(rgb)
|
|
249
252
|
return xyz2lab(xyz)
|
|
250
253
|
|
|
251
254
|
|
|
252
|
-
ANSI_4BIT_RGB: Final[
|
|
255
|
+
ANSI_4BIT_RGB: Final[tuple[Int3Tuple, ...]] = (
|
|
253
256
|
(0, 0, 0), # black
|
|
254
257
|
(170, 0, 0), # red
|
|
255
258
|
(0, 170, 0), # green
|
|
@@ -266,10 +269,10 @@ ANSI_4BIT_RGB: Final[list[Int3Tuple]] = [
|
|
|
266
269
|
(255, 85, 255), # bright magenta
|
|
267
270
|
(85, 255, 255), # bright cyan
|
|
268
271
|
(255, 255, 255), # bright white
|
|
269
|
-
|
|
272
|
+
)
|
|
270
273
|
|
|
271
274
|
|
|
272
|
-
def ansi_4bit_to_rgb(value: int):
|
|
275
|
+
def ansi_4bit_to_rgb(value: int, /):
|
|
273
276
|
offset = 0
|
|
274
277
|
if value > 37:
|
|
275
278
|
if value <= 47:
|
|
@@ -304,26 +307,26 @@ def _4b_lookup() -> dict[Int3Tuple, Int3Tuple]:
|
|
|
304
307
|
return table
|
|
305
308
|
|
|
306
309
|
|
|
307
|
-
ANSI_4BIT_RGB_MAP = _4b_lookup()
|
|
310
|
+
ANSI_4BIT_RGB_MAP = mappingproxy(_4b_lookup())
|
|
308
311
|
|
|
309
312
|
|
|
310
|
-
def _quantize_rgb(rgb: RGBVectorLike):
|
|
313
|
+
def _quantize_rgb(rgb: RGBVectorLike, /):
|
|
311
314
|
r, g, b = rgb
|
|
312
315
|
return min(r >> 3, 0x1F), min(g >> 3, 0x1F), min(b >> 3, 0x1F)
|
|
313
316
|
|
|
314
317
|
|
|
315
|
-
def nearest_ansi_4bit_rgb(value: RGBVectorLike) -> Int3Tuple:
|
|
318
|
+
def nearest_ansi_4bit_rgb(value: RGBVectorLike, /) -> Int3Tuple:
|
|
316
319
|
return ANSI_4BIT_RGB_MAP[_quantize_rgb(value)]
|
|
317
320
|
|
|
318
321
|
|
|
319
|
-
def nearest_ansi_8bit_rgb(value: RGBVectorLike) -> Int3Tuple:
|
|
322
|
+
def nearest_ansi_8bit_rgb(value: RGBVectorLike, /) -> Int3Tuple:
|
|
320
323
|
try:
|
|
321
324
|
return ansi_8bit_to_rgb(rgb_to_ansi_8bit(value))
|
|
322
325
|
except ValueError:
|
|
323
326
|
raise ValueError(f"invalid RGB value: {value!r}") from None
|
|
324
327
|
|
|
325
328
|
|
|
326
|
-
def ansi_8bit_to_rgb(value: int):
|
|
329
|
+
def ansi_8bit_to_rgb(value: int, /):
|
|
327
330
|
if 0 <= value < 16:
|
|
328
331
|
return ANSI_4BIT_RGB[value]
|
|
329
332
|
elif value < 232:
|
|
@@ -335,7 +338,7 @@ def ansi_8bit_to_rgb(value: int):
|
|
|
335
338
|
raise ValueError(f"expected an unsigned 8-bit integer, got {value}")
|
|
336
339
|
|
|
337
340
|
|
|
338
|
-
def rgb_to_ansi_8bit(rgb: RGBVectorLike) -> int:
|
|
341
|
+
def rgb_to_ansi_8bit(rgb: RGBVectorLike, /) -> int:
|
|
339
342
|
if len(set(rgb)) == 1:
|
|
340
343
|
c = rgb[0]
|
|
341
344
|
if c < 8:
|
|
@@ -347,6 +350,6 @@ def rgb_to_ansi_8bit(rgb: RGBVectorLike) -> int:
|
|
|
347
350
|
return 16 + (36 * r) + (6 * g) + b
|
|
348
351
|
|
|
349
352
|
|
|
350
|
-
def rgb_diff(rgb1: Int3Tuple, rgb2: Int3Tuple) -> Int3Tuple:
|
|
353
|
+
def rgb_diff(rgb1: Int3Tuple, rgb2: Int3Tuple, /) -> Int3Tuple:
|
|
351
354
|
lab1, lab2 = map(rgb2lab, (rgb1, rgb2))
|
|
352
355
|
return lab2rgb([(i + j) / 2 for i, j in zip(lab1, lab2)])
|