chromatic-python 0.2.1__tar.gz → 0.3.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.2.1 → chromatic_python-0.3.0}/.gitignore +2 -2
- {chromatic_python-0.2.1/chromatic_python.egg-info → chromatic_python-0.3.0}/PKG-INFO +9 -8
- chromatic_python-0.3.0/chromatic/__init__.py +8 -0
- chromatic_python-0.3.0/chromatic/__init__.pyi +74 -0
- {chromatic_python-0.2.1 → chromatic_python-0.3.0}/chromatic/_typing.py +45 -24
- {chromatic_python-0.2.1 → chromatic_python-0.3.0}/chromatic/_version.py +2 -2
- chromatic_python-0.3.0/chromatic/color/__init__.py +3 -0
- chromatic_python-0.3.0/chromatic/color/__init__.pyi +86 -0
- {chromatic_python-0.2.1 → chromatic_python-0.3.0}/chromatic/color/colorconv.py +59 -42
- chromatic_python-0.3.0/chromatic/color/core.py +1652 -0
- chromatic_python-0.3.0/chromatic/color/core.pyi +455 -0
- {chromatic_python-0.2.1 → chromatic_python-0.3.0}/chromatic/color/iterators.py +3 -3
- chromatic_python-0.3.0/chromatic/color/palette.py +561 -0
- chromatic_python-0.3.0/chromatic/color/palette.pyi +286 -0
- chromatic_python-0.3.0/chromatic/data/__init__.py +26 -0
- chromatic_python-0.3.0/chromatic/data/__init__.pyi +9 -0
- chromatic_python-0.3.0/chromatic/data/_fetchers.py +55 -0
- chromatic_python-0.3.0/chromatic/data/registry.json +5 -0
- chromatic_python-0.3.0/chromatic/data/userfont.py +132 -0
- chromatic_python-0.3.0/chromatic/data/userfont.pyi +28 -0
- chromatic_python-0.3.0/chromatic/data/userfont.schema.json +28 -0
- {chromatic_python-0.2.1 → chromatic_python-0.3.0}/chromatic/demo.py +152 -131
- chromatic_python-0.3.0/chromatic/image/__init__.pyi +70 -0
- {chromatic_python-0.2.1/chromatic/ascii → chromatic_python-0.3.0/chromatic/image}/_array.py +185 -81
- {chromatic_python-0.2.1/chromatic/ascii → chromatic_python-0.3.0/chromatic/image}/_curses.py +8 -4
- {chromatic_python-0.2.1/chromatic/ascii → chromatic_python-0.3.0/chromatic/image}/_glyph_proc.py +9 -3
- {chromatic_python-0.2.1 → chromatic_python-0.3.0/chromatic_python.egg-info}/PKG-INFO +9 -8
- {chromatic_python-0.2.1 → chromatic_python-0.3.0}/chromatic_python.egg-info/SOURCES.txt +17 -9
- chromatic_python-0.3.0/chromatic_python.egg-info/requires.txt +9 -0
- {chromatic_python-0.2.1 → chromatic_python-0.3.0}/pyproject.toml +12 -23
- chromatic_python-0.3.0/requirements.txt +9 -0
- {chromatic_python-0.2.1 → chromatic_python-0.3.0}/tests/test_color_str.py +32 -43
- chromatic_python-0.2.1/chromatic/__init__.py +0 -42
- chromatic_python-0.2.1/chromatic/color/__init__.py +0 -6
- chromatic_python-0.2.1/chromatic/color/core.py +0 -1737
- chromatic_python-0.2.1/chromatic/color/core.pyi +0 -354
- chromatic_python-0.2.1/chromatic/color/palette.py +0 -552
- chromatic_python-0.2.1/chromatic/color/palette.pyi +0 -315
- chromatic_python-0.2.1/chromatic/data/__init__.py +0 -198
- chromatic_python-0.2.1/chromatic/data/__init__.pyi +0 -15
- chromatic_python-0.2.1/chromatic/data/images/hotdog.jpg +0 -0
- chromatic_python-0.2.1/chromatic_python.egg-info/requires.txt +0 -8
- {chromatic_python-0.2.1 → chromatic_python-0.3.0}/.gitattributes +0 -0
- {chromatic_python-0.2.1 → chromatic_python-0.3.0}/LICENSE +0 -0
- {chromatic_python-0.2.1 → chromatic_python-0.3.0}/README.md +0 -0
- {chromatic_python-0.2.1/chromatic/data/images → chromatic_python-0.3.0/chromatic/data}/butterfly.jpg +0 -0
- {chromatic_python-0.2.1/chromatic/data/images → chromatic_python-0.3.0/chromatic/data}/escher.png +0 -0
- {chromatic_python-0.2.1 → chromatic_python-0.3.0}/chromatic/data/fonts/consolas.ttf +0 -0
- /chromatic_python-0.2.1/chromatic/data/fonts/IBM_VGA_437_8x16.ttf → /chromatic_python-0.3.0/chromatic/data/fonts/vga437.ttf +0 -0
- {chromatic_python-0.2.1/chromatic/data/images → chromatic_python-0.3.0/chromatic/data}/goblin_virus.png +0 -0
- {chromatic_python-0.2.1/chromatic/ascii → chromatic_python-0.3.0/chromatic/image}/__init__.py +0 -0
- {chromatic_python-0.2.1 → chromatic_python-0.3.0}/chromatic_python.egg-info/dependency_links.txt +0 -0
- {chromatic_python-0.2.1 → chromatic_python-0.3.0}/chromatic_python.egg-info/top_level.txt +0 -0
- {chromatic_python-0.2.1 → chromatic_python-0.3.0}/logo/logo.ANS +0 -0
- {chromatic_python-0.2.1 → chromatic_python-0.3.0}/logo/logo.PNG +0 -0
- {chromatic_python-0.2.1 → chromatic_python-0.3.0}/setup.cfg +0 -0
- {chromatic_python-0.2.1 → chromatic_python-0.3.0}/tests/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: chromatic-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: ANSI art image processing and colored terminal text
|
|
5
5
|
Author: crypt0lith
|
|
6
6
|
License: MIT License
|
|
@@ -33,14 +33,15 @@ Classifier: Typing :: Typed
|
|
|
33
33
|
Requires-Python: >=3.12
|
|
34
34
|
Description-Content-Type: text/markdown
|
|
35
35
|
License-File: LICENSE
|
|
36
|
-
Requires-Dist: fonttools~=4.51.0
|
|
37
36
|
Requires-Dist: networkx~=3.4.2
|
|
38
|
-
Requires-Dist: numpy~=2.
|
|
39
|
-
Requires-Dist:
|
|
40
|
-
Requires-Dist:
|
|
41
|
-
Requires-Dist: scikit-image~=0.25.
|
|
42
|
-
Requires-Dist: scikit-learn~=1.
|
|
43
|
-
Requires-Dist:
|
|
37
|
+
Requires-Dist: numpy~=2.2.6
|
|
38
|
+
Requires-Dist: pillow~=11.2.1
|
|
39
|
+
Requires-Dist: opencv-python~=4.11.0.86
|
|
40
|
+
Requires-Dist: scikit-image~=0.25.2
|
|
41
|
+
Requires-Dist: scikit-learn~=1.6.1
|
|
42
|
+
Requires-Dist: fonttools~=4.58.0
|
|
43
|
+
Requires-Dist: scipy~=1.15.3
|
|
44
|
+
Requires-Dist: lazy_loader~=0.4
|
|
44
45
|
Dynamic: license-file
|
|
45
46
|
|
|
46
47
|

|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
__all__ = [
|
|
2
|
+
'AnsiImage',
|
|
3
|
+
'Back',
|
|
4
|
+
'Color',
|
|
5
|
+
'ColorNamespace',
|
|
6
|
+
'ColorStr',
|
|
7
|
+
'Fore',
|
|
8
|
+
'SgrParameter',
|
|
9
|
+
'Style',
|
|
10
|
+
'__version__',
|
|
11
|
+
'ansi2img',
|
|
12
|
+
'ansi_quantize',
|
|
13
|
+
'ansicolor24Bit',
|
|
14
|
+
'ansicolor4Bit',
|
|
15
|
+
'ansicolor8Bit',
|
|
16
|
+
'ansify',
|
|
17
|
+
'image',
|
|
18
|
+
'ascii2img',
|
|
19
|
+
'ascii_printable',
|
|
20
|
+
'color',
|
|
21
|
+
'colorbytes',
|
|
22
|
+
'contrast_stretch',
|
|
23
|
+
'cp437_printable',
|
|
24
|
+
'data',
|
|
25
|
+
'equalize_white_point',
|
|
26
|
+
'get_font_key',
|
|
27
|
+
'get_font_object',
|
|
28
|
+
'get_glyph_masks',
|
|
29
|
+
'img2ansi',
|
|
30
|
+
'img2ascii',
|
|
31
|
+
'named_color',
|
|
32
|
+
'read_ans',
|
|
33
|
+
'register_userfont',
|
|
34
|
+
'render_ans',
|
|
35
|
+
'reshape_ansi',
|
|
36
|
+
'to_sgr_array',
|
|
37
|
+
]
|
|
38
|
+
from . import color, data, image
|
|
39
|
+
from ._version import version as __version__
|
|
40
|
+
from .color import (
|
|
41
|
+
Back,
|
|
42
|
+
Color,
|
|
43
|
+
ColorNamespace,
|
|
44
|
+
ColorStr,
|
|
45
|
+
Fore,
|
|
46
|
+
SgrParameter,
|
|
47
|
+
Style,
|
|
48
|
+
ansicolor24Bit,
|
|
49
|
+
ansicolor4Bit,
|
|
50
|
+
ansicolor8Bit,
|
|
51
|
+
colorbytes,
|
|
52
|
+
named_color,
|
|
53
|
+
)
|
|
54
|
+
from .data import register_userfont
|
|
55
|
+
from .image import (
|
|
56
|
+
AnsiImage,
|
|
57
|
+
ansi2img,
|
|
58
|
+
ansi_quantize,
|
|
59
|
+
ansify,
|
|
60
|
+
ascii2img,
|
|
61
|
+
ascii_printable,
|
|
62
|
+
contrast_stretch,
|
|
63
|
+
cp437_printable,
|
|
64
|
+
equalize_white_point,
|
|
65
|
+
get_font_key,
|
|
66
|
+
get_font_object,
|
|
67
|
+
img2ansi,
|
|
68
|
+
img2ascii,
|
|
69
|
+
read_ans,
|
|
70
|
+
render_ans,
|
|
71
|
+
reshape_ansi,
|
|
72
|
+
to_sgr_array,
|
|
73
|
+
)
|
|
74
|
+
from .image._glyph_proc import get_glyph_masks
|
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import inspect
|
|
4
|
+
import operator as op
|
|
4
5
|
import re
|
|
5
6
|
import types
|
|
6
7
|
from collections import OrderedDict, namedtuple
|
|
7
8
|
from collections.abc import Callable as ABC_Callable
|
|
8
9
|
from functools import reduce, wraps
|
|
9
10
|
from numbers import Number
|
|
10
|
-
from operator import attrgetter, or_ as bitwise_or
|
|
11
11
|
from typing import (
|
|
12
12
|
Any,
|
|
13
13
|
Callable,
|
|
14
14
|
Concatenate,
|
|
15
15
|
Hashable,
|
|
16
16
|
Iterable,
|
|
17
|
-
Literal,
|
|
17
|
+
Literal as L,
|
|
18
18
|
NamedTuple,
|
|
19
19
|
Optional,
|
|
20
20
|
ParamSpec,
|
|
21
21
|
Protocol,
|
|
22
22
|
Sequence,
|
|
23
|
+
TYPE_CHECKING,
|
|
23
24
|
Type,
|
|
24
25
|
TypeAlias,
|
|
25
26
|
TypeAliasType,
|
|
@@ -40,7 +41,8 @@ from PIL.ImageFont import FreeTypeFont
|
|
|
40
41
|
from numpy import dtype, float64, generic, ndarray, number, uint8
|
|
41
42
|
from numpy._typing import NDArray, _ArrayLike
|
|
42
43
|
|
|
43
|
-
|
|
44
|
+
if TYPE_CHECKING:
|
|
45
|
+
from .data import UserFont
|
|
44
46
|
|
|
45
47
|
_P = ParamSpec('_P')
|
|
46
48
|
_T = TypeVar('_T')
|
|
@@ -49,10 +51,10 @@ _T_contra = TypeVar('_T_contra', contravariant=True)
|
|
|
49
51
|
_AnyNumber_co = TypeVar('_AnyNumber_co', number, Number, covariant=True)
|
|
50
52
|
|
|
51
53
|
type ArrayReducerFunc[_SCT: generic] = Callable[Concatenate[_ArrayLike[_SCT], _P], NDArray[_SCT]]
|
|
52
|
-
type ShapedNDArray[_Shape: tuple[int, ...], _SCT
|
|
54
|
+
type ShapedNDArray[_Shape: tuple[int, ...], _SCT = generic] = ndarray[_Shape, dtype[_SCT]]
|
|
53
55
|
type MatrixLike[_SCT: generic] = ShapedNDArray[TupleOf2[int], _SCT]
|
|
54
56
|
type SquareMatrix[_I: int, _SCT: generic] = ShapedNDArray[TupleOf2[_I], _SCT]
|
|
55
|
-
type GlyphArray[_SCT: generic] = SquareMatrix[
|
|
57
|
+
type GlyphArray[_SCT: generic] = SquareMatrix[L[24], _SCT]
|
|
56
58
|
type TupleOf2[_T] = tuple[_T, _T]
|
|
57
59
|
type TupleOf3[_T] = tuple[_T, _T, _T]
|
|
58
60
|
|
|
@@ -64,25 +66,41 @@ GlyphBitmask: TypeAlias = GlyphArray[bool]
|
|
|
64
66
|
Bitmask: TypeAlias = MatrixLike[bool]
|
|
65
67
|
GreyscaleGlyphArray: TypeAlias = GlyphArray[float64]
|
|
66
68
|
GreyscaleArray: TypeAlias = MatrixLike[float64]
|
|
67
|
-
RGBArray: TypeAlias = ShapedNDArray[tuple[int, int,
|
|
68
|
-
RGBPixel: TypeAlias = ShapedNDArray[tuple[
|
|
69
|
+
RGBArray: TypeAlias = ShapedNDArray[tuple[int, int, L[3]], uint8]
|
|
70
|
+
RGBPixel: TypeAlias = ShapedNDArray[tuple[L[3]], uint8]
|
|
69
71
|
|
|
70
72
|
RGBImageLike: TypeAlias = Image | RGBArray
|
|
71
|
-
RGBVectorLike: TypeAlias =
|
|
72
|
-
ColorDictKeys =
|
|
73
|
-
Ansi4BitAlias =
|
|
74
|
-
Ansi8BitAlias =
|
|
75
|
-
Ansi24BitAlias =
|
|
73
|
+
RGBVectorLike: TypeAlias = IntSequence | RGBPixel
|
|
74
|
+
ColorDictKeys = L['fg', 'bg']
|
|
75
|
+
Ansi4BitAlias = L['4b']
|
|
76
|
+
Ansi8BitAlias = L['8b']
|
|
77
|
+
Ansi24BitAlias = L['24b']
|
|
76
78
|
AnsiColorAlias = Ansi4BitAlias | Ansi8BitAlias | Ansi24BitAlias
|
|
77
|
-
FontArgType: TypeAlias = FreeTypeFont | UserFont | str
|
|
79
|
+
FontArgType: TypeAlias = 'FreeTypeFont | UserFont | str'
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def eval_annotation(annotation: str, **kwargs) -> Any:
|
|
83
|
+
globals_ = kwargs.get('globals', {}) | globals()
|
|
84
|
+
locals_ = kwargs.get('locals', {})
|
|
85
|
+
try:
|
|
86
|
+
return subtype(eval(annotation, globals_.copy(), locals_.copy()))
|
|
87
|
+
except NameError as e:
|
|
88
|
+
try:
|
|
89
|
+
import typing
|
|
90
|
+
|
|
91
|
+
globals_[e.name] = getattr(typing, e.name)
|
|
92
|
+
return eval_annotation(annotation, globals=globals_, locals=locals_)
|
|
93
|
+
except AttributeError:
|
|
94
|
+
pass
|
|
95
|
+
raise
|
|
78
96
|
|
|
79
97
|
|
|
80
98
|
def type_error_msg(err_obj, *expected, context: str = '', obj_repr=False):
|
|
81
99
|
n_expected = len(expected)
|
|
82
|
-
name_slots = [
|
|
83
|
-
if
|
|
100
|
+
name_slots = ["{%d.__name__!r}" % n for n in range(n_expected)]
|
|
101
|
+
if n_expected > 1:
|
|
84
102
|
name_slots[-1] = f"or {name_slots[-1]}"
|
|
85
|
-
names = (', ' if n_expected > 2 else ' ').join([context.strip()
|
|
103
|
+
names = (', ' if n_expected > 2 else ' ').join([context.strip(), *name_slots]).format(*expected)
|
|
86
104
|
if not obj_repr:
|
|
87
105
|
if not isinstance(err_obj, type):
|
|
88
106
|
err_obj = type(err_obj)
|
|
@@ -100,7 +118,7 @@ def is_matching_type(value, typ):
|
|
|
100
118
|
origin, args = deconstruct_type(typ)
|
|
101
119
|
if origin is Union:
|
|
102
120
|
return any(is_matching_type(value, arg) for arg in args)
|
|
103
|
-
elif origin is
|
|
121
|
+
elif origin is L:
|
|
104
122
|
return value in args
|
|
105
123
|
elif isinstance(typ, TypeVar):
|
|
106
124
|
if typ.__constraints__:
|
|
@@ -183,25 +201,25 @@ class SupportsUnion(Protocol[_T_contra, _T_co]):
|
|
|
183
201
|
|
|
184
202
|
|
|
185
203
|
def unionize(__iterable: Iterable[SupportsUnion[_T_contra, _T_co]]) -> _T_co:
|
|
186
|
-
return reduce(
|
|
204
|
+
return reduce(op.or_, __iterable)
|
|
187
205
|
|
|
188
206
|
|
|
189
207
|
_GenericAlias = type(Type[...]) | types.GenericAlias
|
|
190
208
|
_UnionGenericType = type(Union[..., None])
|
|
191
|
-
_LiteralGenericType = type(
|
|
209
|
+
_LiteralGenericType = type(L[''])
|
|
192
210
|
_CallableGenericType = type(Callable[[], ...]) | type(ABC_Callable[[], ...])
|
|
193
211
|
_CallableType = type(Callable) | ABC_Callable
|
|
194
212
|
|
|
195
213
|
|
|
196
214
|
class _BoundedDict[_KT, _VT](OrderedDict[_KT, _VT]):
|
|
215
|
+
"""Bounded OrderedDict, mimics FIFO behavior of `functools.lru_cache`"""
|
|
197
216
|
|
|
198
217
|
def __init__(self, *, maxsize: Optional[int] = 128):
|
|
199
|
-
"""Bounded OrderedDict, mimics FIFO behavior of `functools.lru_cache`"""
|
|
200
218
|
super().__init__()
|
|
201
219
|
if maxsize is not None:
|
|
202
220
|
maxsize = max(maxsize, 0)
|
|
203
221
|
|
|
204
|
-
@wraps(
|
|
222
|
+
@wraps(_BoundedDict.__setitem__)
|
|
205
223
|
def _fifo(_, key, value):
|
|
206
224
|
if maxsize <= len(self):
|
|
207
225
|
self.popitem(last=True)
|
|
@@ -213,7 +231,7 @@ class _BoundedDict[_KT, _VT](OrderedDict[_KT, _VT]):
|
|
|
213
231
|
|
|
214
232
|
|
|
215
233
|
_SUBTYPE_CACHE: _BoundedDict[int, ...] = _BoundedDict()
|
|
216
|
-
_ATTR_GETTERS: _BoundedDict[..., tuple[Callable[[Iterable], NamedTuple], attrgetter]] = (
|
|
234
|
+
_ATTR_GETTERS: _BoundedDict[..., tuple[Callable[[Iterable], NamedTuple], op.attrgetter]] = (
|
|
217
235
|
_BoundedDict()
|
|
218
236
|
)
|
|
219
237
|
|
|
@@ -247,7 +265,10 @@ def _unique_attrs(obj) -> Optional['NamedTuple']:
|
|
|
247
265
|
+ 'Attrs'
|
|
248
266
|
).strip('_')
|
|
249
267
|
UniqueAttrs = cast(NamedTuple, namedtuple(tup_name, field_names))
|
|
250
|
-
_ATTR_GETTERS[tp] = constructor, getter =
|
|
268
|
+
_ATTR_GETTERS[tp] = [constructor, getter] = [
|
|
269
|
+
UniqueAttrs._make,
|
|
270
|
+
op.attrgetter(*attr_names), # noqa
|
|
271
|
+
]
|
|
251
272
|
return constructor(getter(obj))
|
|
252
273
|
|
|
253
274
|
|
|
@@ -342,7 +363,7 @@ def subtype[_T](typ: _T) -> _T:
|
|
|
342
363
|
return getattr(value, '__args__', ())
|
|
343
364
|
|
|
344
365
|
start = args[literals.pop(0)]
|
|
345
|
-
args_list[args_list.index(start)] =
|
|
366
|
+
args_list[args_list.index(start)] = L[
|
|
346
367
|
*start.__args__,
|
|
347
368
|
*dict.fromkeys(arg for idx in literals for arg in _next_args(idx)),
|
|
348
369
|
]
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
__all__ = [
|
|
2
|
+
'ANSI_4BIT_RGB',
|
|
3
|
+
'Back',
|
|
4
|
+
'CSI',
|
|
5
|
+
'Color',
|
|
6
|
+
'ColorNamespace',
|
|
7
|
+
'ColorStr',
|
|
8
|
+
'Fore',
|
|
9
|
+
'SGR_RESET',
|
|
10
|
+
'SgrParameter',
|
|
11
|
+
'SgrSequence',
|
|
12
|
+
'Style',
|
|
13
|
+
'ansi_4bit_to_rgb',
|
|
14
|
+
'ansi_8bit_to_rgb',
|
|
15
|
+
'ansicolor24Bit',
|
|
16
|
+
'ansicolor4Bit',
|
|
17
|
+
'ansicolor8Bit',
|
|
18
|
+
'color_chain',
|
|
19
|
+
'colorbytes',
|
|
20
|
+
'get_ansi_type',
|
|
21
|
+
'int2rgb',
|
|
22
|
+
'hexstr2rgb',
|
|
23
|
+
'hsl2rgb',
|
|
24
|
+
'hsv2rgb',
|
|
25
|
+
'is_u24',
|
|
26
|
+
'lab2rgb',
|
|
27
|
+
'lab2xyz',
|
|
28
|
+
'named_color',
|
|
29
|
+
'nearest_ansi_4bit_rgb',
|
|
30
|
+
'nearest_ansi_8bit_rgb',
|
|
31
|
+
'randcolor',
|
|
32
|
+
'rgb2ansi_escape',
|
|
33
|
+
'rgb2int',
|
|
34
|
+
'rgb2hexstr',
|
|
35
|
+
'rgb2hsl',
|
|
36
|
+
'rgb2hsv',
|
|
37
|
+
'rgb2lab',
|
|
38
|
+
'rgb2xyz',
|
|
39
|
+
'rgb_diff',
|
|
40
|
+
'rgb_dispatch',
|
|
41
|
+
'rgb_to_ansi_8bit',
|
|
42
|
+
'xyz2lab',
|
|
43
|
+
'xyz2rgb',
|
|
44
|
+
]
|
|
45
|
+
from . import colorconv, core, iterators, palette
|
|
46
|
+
from .colorconv import (
|
|
47
|
+
ANSI_4BIT_RGB,
|
|
48
|
+
ansi_4bit_to_rgb,
|
|
49
|
+
ansi_8bit_to_rgb,
|
|
50
|
+
int2rgb,
|
|
51
|
+
hexstr2rgb,
|
|
52
|
+
hsl2rgb,
|
|
53
|
+
hsv2rgb,
|
|
54
|
+
is_u24,
|
|
55
|
+
lab2rgb,
|
|
56
|
+
lab2xyz,
|
|
57
|
+
nearest_ansi_4bit_rgb,
|
|
58
|
+
nearest_ansi_8bit_rgb,
|
|
59
|
+
rgb2int,
|
|
60
|
+
rgb2hexstr,
|
|
61
|
+
rgb2hsl,
|
|
62
|
+
rgb2hsv,
|
|
63
|
+
rgb2lab,
|
|
64
|
+
rgb2xyz,
|
|
65
|
+
rgb_diff,
|
|
66
|
+
rgb_to_ansi_8bit,
|
|
67
|
+
xyz2lab,
|
|
68
|
+
xyz2rgb,
|
|
69
|
+
)
|
|
70
|
+
from .core import (
|
|
71
|
+
CSI,
|
|
72
|
+
Color,
|
|
73
|
+
ColorStr,
|
|
74
|
+
SGR_RESET,
|
|
75
|
+
SgrParameter,
|
|
76
|
+
SgrSequence,
|
|
77
|
+
ansicolor24Bit,
|
|
78
|
+
ansicolor4Bit,
|
|
79
|
+
ansicolor8Bit,
|
|
80
|
+
color_chain,
|
|
81
|
+
colorbytes,
|
|
82
|
+
get_ansi_type,
|
|
83
|
+
randcolor,
|
|
84
|
+
rgb2ansi_escape,
|
|
85
|
+
)
|
|
86
|
+
from .palette import Back, ColorNamespace, Fore, Style, named_color, rgb_dispatch
|
|
@@ -2,16 +2,16 @@ __all__ = [
|
|
|
2
2
|
'ANSI_4BIT_RGB',
|
|
3
3
|
'ansi_4bit_to_rgb',
|
|
4
4
|
'ansi_8bit_to_rgb',
|
|
5
|
-
'
|
|
5
|
+
'int2rgb',
|
|
6
6
|
'hexstr2rgb',
|
|
7
7
|
'hsl2rgb',
|
|
8
8
|
'hsv2rgb',
|
|
9
|
-
'
|
|
9
|
+
'is_u24',
|
|
10
10
|
'lab2rgb',
|
|
11
11
|
'lab2xyz',
|
|
12
12
|
'nearest_ansi_4bit_rgb',
|
|
13
13
|
'nearest_ansi_8bit_rgb',
|
|
14
|
-
'
|
|
14
|
+
'rgb2int',
|
|
15
15
|
'rgb2hexstr',
|
|
16
16
|
'rgb2hsl',
|
|
17
17
|
'rgb2hsv',
|
|
@@ -23,40 +23,69 @@ __all__ = [
|
|
|
23
23
|
'xyz2rgb',
|
|
24
24
|
]
|
|
25
25
|
|
|
26
|
+
from functools import lru_cache
|
|
26
27
|
from operator import mul, truediv
|
|
27
|
-
from typing import Final, Literal, SupportsInt,
|
|
28
|
+
from typing import Final, Literal, SupportsInt, TypeGuard
|
|
28
29
|
|
|
29
30
|
import numpy as np
|
|
30
31
|
|
|
31
32
|
from .._typing import Float3Tuple, FloatSequence, Int3Tuple, RGBPixel, RGBVectorLike, ShapedNDArray
|
|
32
33
|
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
@lru_cache
|
|
36
|
+
def _supports_int(typ: type) -> TypeGuard[type[SupportsInt]]:
|
|
37
|
+
return issubclass(typ, SupportsInt)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def is_u24(value, *, strict: bool = False):
|
|
41
|
+
"""Check if value is an unsigned 24-bit integer.
|
|
42
|
+
|
|
43
|
+
Parameters
|
|
44
|
+
---------
|
|
45
|
+
value
|
|
46
|
+
Input number
|
|
47
|
+
strict : bool
|
|
48
|
+
Whether to return False or raise ValueError on failure
|
|
49
|
+
|
|
50
|
+
Raises
|
|
51
|
+
------
|
|
52
|
+
ValueError
|
|
53
|
+
Raised when `strict=True` and value is not u24
|
|
54
|
+
"""
|
|
55
|
+
if _supports_int(type(value)):
|
|
56
|
+
if 0 <= int(value) <= 0xFFFFFF:
|
|
37
57
|
return True
|
|
38
58
|
elif not strict:
|
|
39
59
|
return False
|
|
40
|
-
raise
|
|
60
|
+
raise ValueError(f"{value!r} is not u24")
|
|
41
61
|
|
|
42
62
|
|
|
43
63
|
def hexstr2rgb(__str: str) -> Int3Tuple:
|
|
44
|
-
|
|
45
|
-
|
|
64
|
+
n = len(__str)
|
|
65
|
+
if n % 4 == 0: # trunc alpha
|
|
66
|
+
n *= 3
|
|
67
|
+
n //= 4
|
|
68
|
+
__str = __str[:n]
|
|
69
|
+
if n == 3: # rgb -> rrggbb
|
|
70
|
+
__str = ''.join(c * 2 for c in __str)
|
|
71
|
+
if is_u24(value := int(__str, 16), strict=True):
|
|
72
|
+
return int2rgb(value)
|
|
46
73
|
|
|
47
74
|
|
|
48
75
|
def rgb2hexstr(rgb: RGBVectorLike) -> str:
|
|
49
|
-
|
|
50
|
-
return f'{r:02x}{g:02x}{b:02x}'
|
|
76
|
+
return "%02x%02x%02x" % tuple(rgb)
|
|
51
77
|
|
|
52
78
|
|
|
53
|
-
def
|
|
79
|
+
def rgb2int(rgb: RGBVectorLike) -> int:
|
|
54
80
|
r, g, b = map(int, rgb)
|
|
55
81
|
return r << 16 | g << 8 | b
|
|
56
82
|
|
|
57
83
|
|
|
58
|
-
def
|
|
59
|
-
|
|
84
|
+
def int2rgb(__x: int) -> Int3Tuple:
|
|
85
|
+
try:
|
|
86
|
+
return getattr(__x, 'rgb')
|
|
87
|
+
except AttributeError:
|
|
88
|
+
return (__x >> 16) & 0xFF, (__x >> 8) & 0xFF, __x & 0xFF
|
|
60
89
|
|
|
61
90
|
|
|
62
91
|
def xyz2lab(xyz: FloatSequence) -> Float3Tuple:
|
|
@@ -78,7 +107,7 @@ def lab2xyz(lab: FloatSequence) -> Float3Tuple:
|
|
|
78
107
|
x, y, z = map(
|
|
79
108
|
mul,
|
|
80
109
|
(95.047, 100.0, 108.883),
|
|
81
|
-
map(lambda
|
|
110
|
+
map(lambda i: (lambda j: j if j > 0.008856 else (i - 16 / 116) / 7.787)(i**3), (x, y, z)),
|
|
82
111
|
)
|
|
83
112
|
return x, y, z
|
|
84
113
|
|
|
@@ -104,41 +133,30 @@ def xyz2rgb(xyz: ShapedNDArray[tuple[Literal[3]], np.float64]) -> Int3Tuple:
|
|
|
104
133
|
|
|
105
134
|
def hsl2rgb(hsl: FloatSequence) -> Int3Tuple:
|
|
106
135
|
h, s, L = hsl
|
|
107
|
-
h
|
|
136
|
+
h /= 360
|
|
137
|
+
h %= 1
|
|
108
138
|
if h < 0:
|
|
109
139
|
h += 1
|
|
110
|
-
r = g = b = L
|
|
111
140
|
v = (L * (1.0 + s)) if L <= 0.5 else (L + s - L * s)
|
|
112
|
-
if v > 0:
|
|
141
|
+
if v > 0 and 0 <= (sextant := int(h)) <= 5:
|
|
113
142
|
m = L + L - v
|
|
114
143
|
sv = (v - m) / v
|
|
115
144
|
h *= 6.0
|
|
116
|
-
|
|
117
|
-
fract = h - sextant
|
|
118
|
-
vsf = v * sv * fract
|
|
145
|
+
vsf = v * sv * (h - sextant)
|
|
119
146
|
mid1 = m + vsf
|
|
120
147
|
mid2 = v - vsf
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
elif sextant == 2:
|
|
126
|
-
r, g, b = m, v, mid1
|
|
127
|
-
elif sextant == 3:
|
|
128
|
-
r, g, b = m, mid2, v
|
|
129
|
-
elif sextant == 4:
|
|
130
|
-
r, g, b = mid1, m, v
|
|
131
|
-
elif sextant == 5:
|
|
132
|
-
r, g, b = v, m, mid2
|
|
133
|
-
r, g, b = (round(x * 255) for x in (r, g, b))
|
|
148
|
+
r, g, b = (
|
|
149
|
+
round(x * 0xFF)
|
|
150
|
+
for x in [[v, mid1, m], [mid2, v, m], [m, v, mid1], [m, mid2, v], [mid1, m, v]][sextant]
|
|
151
|
+
)
|
|
134
152
|
else:
|
|
135
|
-
r, g, b =
|
|
153
|
+
r, g, b = [round(L * 0xFF)] * 3
|
|
136
154
|
return r, g, b
|
|
137
155
|
|
|
138
156
|
|
|
139
157
|
def rgb2hsl(rgb: RGBVectorLike) -> Float3Tuple:
|
|
140
158
|
r, g, b = (x / 255.0 for x in rgb)
|
|
141
|
-
m, v = sorted([r, g, b])
|
|
159
|
+
m, _, v = sorted([r, g, b])
|
|
142
160
|
L = (m + v) / 2
|
|
143
161
|
h = s = 0
|
|
144
162
|
if L > 0:
|
|
@@ -245,7 +263,7 @@ def ansi_4bit_to_rgb(value: int):
|
|
|
245
263
|
return ANSI_4BIT_RGB[value]
|
|
246
264
|
|
|
247
265
|
|
|
248
|
-
def _4b_lookup():
|
|
266
|
+
def _4b_lookup() -> dict[Int3Tuple, Int3Tuple]:
|
|
249
267
|
def rgb_dist(rgb, ansi):
|
|
250
268
|
r_mean = (rgb[:, 0:1] + ansi[:, 0]) / 2
|
|
251
269
|
r_diff = (rgb[:, 0:1] - ansi[:, 0]) * (2 + r_mean / 256)
|
|
@@ -257,12 +275,11 @@ def _4b_lookup():
|
|
|
257
275
|
quants = np.stack(
|
|
258
276
|
np.meshgrid(*np.repeat(np.arange(32).reshape([1, -1]), 3, 0), indexing='ij'), axis=-1
|
|
259
277
|
).reshape([-1, 3])
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
table = {
|
|
278
|
+
nearest_colors = rgb_4b_arr[np.argmin(rgb_dist(quants * 8, rgb_4b_arr), axis=1)]
|
|
279
|
+
table: dict = {
|
|
263
280
|
tuple(map(int, color)): tuple(map(int, nearest_colors[i])) for i, color in enumerate(quants)
|
|
264
281
|
}
|
|
265
|
-
return
|
|
282
|
+
return table
|
|
266
283
|
|
|
267
284
|
|
|
268
285
|
ANSI_4BIT_RGB_MAP = _4b_lookup()
|