yta-colors 0.0.1__py3-none-any.whl
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.
- yta_colors/__init__.py +198 -0
- yta_colors/converter.py +152 -0
- yta_colors/utils.py +493 -0
- yta_colors-0.0.1.dist-info/LICENSE +19 -0
- yta_colors-0.0.1.dist-info/METADATA +16 -0
- yta_colors-0.0.1.dist-info/RECORD +7 -0
- yta_colors-0.0.1.dist-info/WHEEL +4 -0
yta_colors/__init__.py
ADDED
@@ -0,0 +1,198 @@
|
|
1
|
+
from yta_colors.converter import ColorConverter
|
2
|
+
from yta_colors.utils import parse_color
|
3
|
+
from yta_validation import PythonValidator
|
4
|
+
from typing import Union
|
5
|
+
|
6
|
+
|
7
|
+
class Color:
|
8
|
+
"""
|
9
|
+
Class that represents a color, stored as RGBA, makes
|
10
|
+
easy the way we interact with color and provide them as
|
11
|
+
parameters and simplify the color conversion. The color
|
12
|
+
is stored as a not-normalized color, but values can be
|
13
|
+
normalized through the methods that allow it (those
|
14
|
+
including the 'normalized' bool parameter).
|
15
|
+
|
16
|
+
Any attribute has to be initialized with a value between
|
17
|
+
0 and 255. Alpha as 255 means full opaque.
|
18
|
+
|
19
|
+
TODO: Please confirm alpha 255 is opaque.
|
20
|
+
"""
|
21
|
+
|
22
|
+
r: int
|
23
|
+
"""
|
24
|
+
Red color, from 0 to 255, where 0 is no value and 255
|
25
|
+
is everything.
|
26
|
+
"""
|
27
|
+
g: int
|
28
|
+
"""
|
29
|
+
Green color, from 0 to 255, where 0 is no value and 255
|
30
|
+
is everything.
|
31
|
+
"""
|
32
|
+
b: int
|
33
|
+
"""
|
34
|
+
Blue color, from 0 to 255, where 0 is no value and 255
|
35
|
+
is everything.
|
36
|
+
"""
|
37
|
+
a: int
|
38
|
+
"""
|
39
|
+
Alpha (transparency), from 0 to 255, where 0 is no
|
40
|
+
value and 255 is everything.
|
41
|
+
"""
|
42
|
+
|
43
|
+
def __init__(self, r, g, b, a):
|
44
|
+
self.r, self.g, self.b, self.a = r, g, b, a
|
45
|
+
# TODO: Precalculate and store all the values
|
46
|
+
# internally so we don't need to calculate them
|
47
|
+
# later (?)
|
48
|
+
|
49
|
+
@property
|
50
|
+
def rgb_not_normalized(self):
|
51
|
+
"""
|
52
|
+
Get the color as a tuple of the 3 RGB values that
|
53
|
+
are, in order: red, green, blue. These values are
|
54
|
+
not normalized, so each value is in the interval
|
55
|
+
[0, 255].
|
56
|
+
"""
|
57
|
+
return self.r, self.g, self.b
|
58
|
+
|
59
|
+
@property
|
60
|
+
def rgb_normalized(self):
|
61
|
+
"""
|
62
|
+
Get the color as a tuple of the 3 RGB values that
|
63
|
+
are, in order: red, green, blue. These values are
|
64
|
+
normalized, so each value is in the interval [0, 1].
|
65
|
+
"""
|
66
|
+
return self.r / 255.0, self.g / 255.0, self.b / 255.0
|
67
|
+
|
68
|
+
@property
|
69
|
+
def rgba_not_normalized(self):
|
70
|
+
"""
|
71
|
+
Get the color as a tuple of the 3 RGB values and
|
72
|
+
a 4th value representing the transparency, that
|
73
|
+
are, in order: red, green, blue, alpha. These
|
74
|
+
values are not normalized, so each value is in
|
75
|
+
the interval [0, 255].
|
76
|
+
"""
|
77
|
+
self.r, self.g, self.b, self.a
|
78
|
+
|
79
|
+
@property
|
80
|
+
def rgba_normalized(self):
|
81
|
+
"""
|
82
|
+
Get the color as a tuple of the 3 RGB values and
|
83
|
+
a 4th value representing the transparency, that
|
84
|
+
are, in order: red, green, blue, alpha. These
|
85
|
+
values are normalized, so each value is in the
|
86
|
+
interval [0, 1].
|
87
|
+
"""
|
88
|
+
return self.r / 255.0, self.g / 255.0, self.b / 255.0, self.a / 255.0
|
89
|
+
|
90
|
+
@property
|
91
|
+
def rgb_array_not_normalized(self):
|
92
|
+
"""
|
93
|
+
Get the color as an array of the 3 RGB values that
|
94
|
+
are, in order: red, green, blue. These values are
|
95
|
+
not normalized, so each value is in the interval
|
96
|
+
[0, 255].
|
97
|
+
"""
|
98
|
+
return [*self.rgba_not_normalized]
|
99
|
+
|
100
|
+
@property
|
101
|
+
def rgb_array_normalized(self):
|
102
|
+
"""
|
103
|
+
Get the color as an array of the 3 RGB values that
|
104
|
+
are, in order: red, green, blue. These values are
|
105
|
+
normalized, so each value is in the interval [0, 1].
|
106
|
+
"""
|
107
|
+
return [*self.rgba_normalized]
|
108
|
+
|
109
|
+
@property
|
110
|
+
def rgba_array_not_normalized(self):
|
111
|
+
"""
|
112
|
+
Get the color as an array of the 3 RGB values and
|
113
|
+
a 4th value representing the transparency, that
|
114
|
+
are, in order: red, green, blue, alpha. These
|
115
|
+
values are not normalized, so each value is in
|
116
|
+
the interval [0, 255].
|
117
|
+
"""
|
118
|
+
return [*self.rgba_not_normalized]
|
119
|
+
|
120
|
+
@property
|
121
|
+
def rgba_array_normalized(self):
|
122
|
+
"""
|
123
|
+
Get the color as an array of the 3 RGB values and
|
124
|
+
a 4th value representing the transparency, that
|
125
|
+
are, in order: red, green, blue, alpha. These
|
126
|
+
values are normalized, so each value is in the
|
127
|
+
interval [0, 1].
|
128
|
+
"""
|
129
|
+
return [*self.rgba_normalized]
|
130
|
+
|
131
|
+
@property
|
132
|
+
def hex_with_alpha(self):
|
133
|
+
"""
|
134
|
+
Get the color as a string representing it in
|
135
|
+
hexadecimal value. The result will be #RRGGBBAA
|
136
|
+
because it includes the alpha value in the last
|
137
|
+
position.
|
138
|
+
"""
|
139
|
+
return ColorConverter.rgba_to_hex(self.rgba_not_normalized, True)
|
140
|
+
|
141
|
+
@property
|
142
|
+
def hex_without_alpha(self):
|
143
|
+
"""
|
144
|
+
Get the color as a string representing it in
|
145
|
+
hexadecimal value. The result will be #RRGGBB
|
146
|
+
because it doesn't include the alpha value.
|
147
|
+
"""
|
148
|
+
return ColorConverter.rgba_to_hex(self.rgba_not_normalized, False)
|
149
|
+
|
150
|
+
@property
|
151
|
+
def hsl(self):
|
152
|
+
"""
|
153
|
+
Get the color as an HSL color.
|
154
|
+
"""
|
155
|
+
return ColorConverter.rgba_to_hsl(self.rgba_not_normalized)
|
156
|
+
|
157
|
+
@property
|
158
|
+
def cymk(self):
|
159
|
+
"""
|
160
|
+
Get the color as an CYMK color.
|
161
|
+
"""
|
162
|
+
return ColorConverter.rgba_to_cymk(self.rgba_not_normalized)
|
163
|
+
|
164
|
+
@property
|
165
|
+
def hsv(self):
|
166
|
+
"""
|
167
|
+
Get the color as a HSV color.
|
168
|
+
"""
|
169
|
+
return ColorConverter.rgba_to_hsv(self.rgba_not_normalized)
|
170
|
+
|
171
|
+
# TODO: Use the cv2 library to make other changes
|
172
|
+
@staticmethod
|
173
|
+
def parse(color: Union[list, tuple, str, 'ColorString', 'Color']):
|
174
|
+
"""
|
175
|
+
Parse the provided 'color' parameter and return the
|
176
|
+
color as r,g,b,a values or raises an Exception if it
|
177
|
+
is not a valid and parseable color.
|
178
|
+
|
179
|
+
This method accepts string colors (if names are
|
180
|
+
registered in our system), hexadecimal colors (than
|
181
|
+
can also include alpha value), RGB array or tuples
|
182
|
+
(that can be normalized, with float values between
|
183
|
+
0.0 and 1.0, or not normalized, with int values
|
184
|
+
between 0 and 255), or RGBA array or tuples, similar
|
185
|
+
to RGB but including a 4h alpha value.
|
186
|
+
"""
|
187
|
+
# TODO: Work on this method to accept other arguments as
|
188
|
+
# I've been working on the 'parse_hexadecimal_color'
|
189
|
+
# method which is in the 'utils.py' file.
|
190
|
+
if PythonValidator.is_instance(color, Color):
|
191
|
+
return color
|
192
|
+
|
193
|
+
color = parse_color(color)
|
194
|
+
|
195
|
+
if color is None:
|
196
|
+
raise Exception(f'The provided "color" parameter is not parseable.')
|
197
|
+
|
198
|
+
return Color(*color)
|
yta_colors/converter.py
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
from yta_colors.utils import is_hexadecimal_color, parse_rgba_color, rgba_to_hex, hex_to_rgba, rgb_to_hex, rgba_to_hex, rgb_to_hsl, rgb_to_cymk, rgb_to_hsv, hsv_to_rgb, rgba_to_hsv, parse_color, hsv_to_rgba
|
2
|
+
from yta_validation import PythonValidator
|
3
|
+
from typing import Union
|
4
|
+
|
5
|
+
|
6
|
+
class ColorConverter:
|
7
|
+
"""
|
8
|
+
Class to simplify and encapsulate the functionality
|
9
|
+
related to color conversion.
|
10
|
+
"""
|
11
|
+
|
12
|
+
@staticmethod
|
13
|
+
def rgb_to_hex(red, green, blue):
|
14
|
+
"""
|
15
|
+
Returns the provided RGB color as a hex color. The 'red', 'green' and
|
16
|
+
'blue' parameters must be between 0 and 255.
|
17
|
+
"""
|
18
|
+
return rgba_to_hex(red, green, blue)
|
19
|
+
|
20
|
+
@staticmethod
|
21
|
+
def hex_to_rgb(color: str):
|
22
|
+
"""
|
23
|
+
Parse the provided hexadecimal 'color' parameter and
|
24
|
+
turn it into an RGB color (returned as r,g,b) or
|
25
|
+
raises an Exception if not.
|
26
|
+
"""
|
27
|
+
r, g, b, _ = ColorConverter.hex_to_rgba(color)
|
28
|
+
|
29
|
+
return r, g, b
|
30
|
+
|
31
|
+
@staticmethod
|
32
|
+
def hex_to_rgba(color: str):
|
33
|
+
if not is_hexadecimal_color(color):
|
34
|
+
raise Exception(f'The provided "color" parameter "{str(color)}" is not an hexadecimal color.')
|
35
|
+
|
36
|
+
return hex_to_rgba(color)
|
37
|
+
|
38
|
+
@staticmethod
|
39
|
+
def rgb_to_hex(color: Union[tuple, list], do_include_alpha: bool = False):
|
40
|
+
"""
|
41
|
+
Parse the provided RGB 'color' parameter and turn it to
|
42
|
+
a hexadecimal color if valid or raises an Exception if
|
43
|
+
not. The result will be #RRGGBB if 'do_include_alpha' is
|
44
|
+
False, or #RRGGBBAA if 'do_include_alpha' is True.
|
45
|
+
"""
|
46
|
+
validate_color(color)
|
47
|
+
|
48
|
+
return rgb_to_hex(color, do_include_alpha)
|
49
|
+
|
50
|
+
@staticmethod
|
51
|
+
def rgba_to_hex(color: Union[tuple, list], do_include_alpha: bool = False):
|
52
|
+
"""
|
53
|
+
Parse the provided RGBA 'color' parameter and turn it to
|
54
|
+
a hexadecimal color if valid or raises an Exception if
|
55
|
+
not. The result will be #RRGGBB if 'do_include_alpha' is
|
56
|
+
False, or #RRGGBBAA if 'do_include_alpha' is True.
|
57
|
+
"""
|
58
|
+
validate_color(color)
|
59
|
+
|
60
|
+
return rgba_to_hex(color, do_include_alpha)
|
61
|
+
|
62
|
+
@staticmethod
|
63
|
+
def rgba_to_hsl(color: Union[tuple, list]):
|
64
|
+
# TODO: Explain
|
65
|
+
validate_color(color)
|
66
|
+
|
67
|
+
_, _, _, a = parse_rgba_color(color)
|
68
|
+
|
69
|
+
return *ColorConverter.rgb_to_hsl(color), a
|
70
|
+
|
71
|
+
@staticmethod
|
72
|
+
def rgb_to_hsl(color: Union[tuple, list]):
|
73
|
+
# TODO: Explain
|
74
|
+
validate_color(color)
|
75
|
+
|
76
|
+
return rgb_to_hsl(color)
|
77
|
+
|
78
|
+
@staticmethod
|
79
|
+
def rgba_to_cymk(color: Union[tuple, list]):
|
80
|
+
# TODO: Explain
|
81
|
+
validate_color(color)
|
82
|
+
|
83
|
+
color = color[:3]
|
84
|
+
|
85
|
+
# TODO: Is there a way to handle alpha transparency
|
86
|
+
# with a cymk (?)
|
87
|
+
return ColorConverter.rgb_to_cymk(color)
|
88
|
+
|
89
|
+
@staticmethod
|
90
|
+
def rgb_to_cymk(color: Union[tuple, list]):
|
91
|
+
# TODO: Explain
|
92
|
+
# It looks like you need to know the color profile before
|
93
|
+
# any conversion from RGB or RGBA
|
94
|
+
# https://www.reddit.com/r/AdobeIllustrator/comments/17vpbod/different_cmyk_values_for_same_hex_code_which/?rdt=55781#:~:text=A%20hex%20code%20is%20an,information%2C%20like%20a%20colour%20profile.
|
95
|
+
validate_color(color)
|
96
|
+
|
97
|
+
return rgb_to_cymk(color)
|
98
|
+
|
99
|
+
@staticmethod
|
100
|
+
def rgb_to_hsv(color: Union[tuple, list]):
|
101
|
+
"""
|
102
|
+
Turn the provided RGB 'color' into a HSV color.
|
103
|
+
"""
|
104
|
+
validate_color(color)
|
105
|
+
|
106
|
+
return rgb_to_hsv(color)
|
107
|
+
|
108
|
+
@staticmethod
|
109
|
+
def rgba_to_hsv(color: Union[tuple, list]):
|
110
|
+
"""
|
111
|
+
Turn the provided RGBA 'color' into a HSV color.
|
112
|
+
The HSV color doesn't pay attention to the alpha
|
113
|
+
layer so this method is the same as using the
|
114
|
+
'rgb_to_hsv' method for this 'color'.
|
115
|
+
"""
|
116
|
+
validate_color(color)
|
117
|
+
|
118
|
+
return rgba_to_hsv(color)
|
119
|
+
|
120
|
+
@staticmethod
|
121
|
+
def hsv_to_rgb(color: Union[tuple, list]):
|
122
|
+
"""
|
123
|
+
Turn the provided HSV 'color' into a RGB color.
|
124
|
+
"""
|
125
|
+
validate_color(color)
|
126
|
+
|
127
|
+
h, s, v = color
|
128
|
+
|
129
|
+
return hsv_to_rgb(h, s, v)
|
130
|
+
|
131
|
+
@staticmethod
|
132
|
+
def hsv_to_rgba(color: Union[tuple, list]):
|
133
|
+
"""
|
134
|
+
Turn the provided HSV 'color' into a RGBA color.
|
135
|
+
The HSV color doesn't pay attention to the alpha
|
136
|
+
layer so it will be always one.
|
137
|
+
"""
|
138
|
+
validate_color(color)
|
139
|
+
|
140
|
+
h, s, v = color
|
141
|
+
|
142
|
+
return hsv_to_rgba(h, s, v)
|
143
|
+
|
144
|
+
def validate_color(color: Union[tuple, list]):
|
145
|
+
"""
|
146
|
+
Validate the provided 'color' as a tuple or list of 3
|
147
|
+
or 4 elements.
|
148
|
+
"""
|
149
|
+
if not PythonValidator.is_instance(color, 'Color') and parse_color(color) is None:
|
150
|
+
raise Exception('The provided "color" is not a parsable color.')
|
151
|
+
|
152
|
+
return True
|
yta_colors/utils.py
ADDED
@@ -0,0 +1,493 @@
|
|
1
|
+
from yta_constants.color import ColorString
|
2
|
+
from yta_constants.regex import ColorRegularExpression
|
3
|
+
from yta_validation import PythonValidator
|
4
|
+
from colorsys import rgb_to_hsv as _rgb_to_hsv, hsv_to_rgb as _hsv_to_rgb
|
5
|
+
from typing import Union
|
6
|
+
|
7
|
+
|
8
|
+
def is_hexadecimal_color(
|
9
|
+
color: str
|
10
|
+
) -> bool:
|
11
|
+
"""
|
12
|
+
Check that the 'color' parameter is an hexadecimal
|
13
|
+
color.
|
14
|
+
"""
|
15
|
+
return ColorRegularExpression.HEX.parse(color)
|
16
|
+
|
17
|
+
def is_string_color(
|
18
|
+
color: str
|
19
|
+
) -> bool:
|
20
|
+
"""
|
21
|
+
Check that the 'color' parameter is an string
|
22
|
+
color accepted by our system, whose value is an
|
23
|
+
hexadecimal value.
|
24
|
+
"""
|
25
|
+
return ColorString.is_valid(color)
|
26
|
+
|
27
|
+
def is_array_or_tuple_without_alpha_normalized(
|
28
|
+
color: str
|
29
|
+
):
|
30
|
+
"""
|
31
|
+
Check that the 'color' parameter is an array or a
|
32
|
+
tuple of 3 elements that are float values between
|
33
|
+
0 and 1 (normalized value).
|
34
|
+
"""
|
35
|
+
return (
|
36
|
+
is_array_or_tuple_without_alpha and
|
37
|
+
all(
|
38
|
+
PythonValidator.is_instance(c, float) and
|
39
|
+
0 <= c <= 1
|
40
|
+
for c in color
|
41
|
+
)
|
42
|
+
)
|
43
|
+
|
44
|
+
def is_array_or_tuple_with_alpha_normalized(
|
45
|
+
color: str
|
46
|
+
):
|
47
|
+
"""
|
48
|
+
Check that the 'color' parameter is an array or a
|
49
|
+
tuple of 4 elements that are float values between
|
50
|
+
0 and 1 (normalized value).
|
51
|
+
"""
|
52
|
+
return (
|
53
|
+
is_array_or_tuple_with_alpha and
|
54
|
+
all(
|
55
|
+
PythonValidator.is_instance(c, float) and
|
56
|
+
0 <= c <= 1
|
57
|
+
for c in color
|
58
|
+
)
|
59
|
+
)
|
60
|
+
|
61
|
+
def is_array_or_tuple_without_alpha(
|
62
|
+
color: str
|
63
|
+
):
|
64
|
+
"""
|
65
|
+
Check that the 'color' parameter is an array or a
|
66
|
+
tuple of 3 elements that are int values between 0
|
67
|
+
and 255.
|
68
|
+
"""
|
69
|
+
return (
|
70
|
+
PythonValidator.is_instance(color, [tuple, list]) and
|
71
|
+
len(color) == 3 and
|
72
|
+
all(
|
73
|
+
PythonValidator.is_instance(c, int) and
|
74
|
+
0 <= c <= 255
|
75
|
+
for c in color
|
76
|
+
)
|
77
|
+
)
|
78
|
+
|
79
|
+
def is_array_or_tuple_with_alpha(
|
80
|
+
color: Union[list, tuple]
|
81
|
+
) -> bool:
|
82
|
+
"""
|
83
|
+
Check that the 'color' parameter is an array or a
|
84
|
+
tuple of 4 elements that are int values between 0
|
85
|
+
and 255.
|
86
|
+
"""
|
87
|
+
return (
|
88
|
+
PythonValidator.is_instance(color, [tuple, list]) and
|
89
|
+
len(color) == 4 and
|
90
|
+
all(
|
91
|
+
PythonValidator.is_instance(c, int) and
|
92
|
+
0 <= c <= 255
|
93
|
+
for c in color
|
94
|
+
)
|
95
|
+
)
|
96
|
+
|
97
|
+
def parse_rgb_color(
|
98
|
+
color: str
|
99
|
+
) -> list:
|
100
|
+
"""
|
101
|
+
Parse the provided 'color' as RGB and returns it as
|
102
|
+
r,g,b values.
|
103
|
+
"""
|
104
|
+
if is_array_or_tuple_without_alpha_normalized(color):
|
105
|
+
return color[0] * 255, color[1] * 255, color[2] * 255
|
106
|
+
elif is_array_or_tuple_without_alpha(color):
|
107
|
+
return color[0], color[1], color[2]
|
108
|
+
else:
|
109
|
+
raise Exception(f'The provided "color" parameter is not an RGB color.')
|
110
|
+
|
111
|
+
def parse_rgba_color(
|
112
|
+
color: str
|
113
|
+
) -> list:
|
114
|
+
"""
|
115
|
+
Parse the provided 'color' as RGBA and returns it as
|
116
|
+
r,g,b,a values.
|
117
|
+
"""
|
118
|
+
if is_array_or_tuple_with_alpha_normalized(color):
|
119
|
+
return color[0] * 255, color[1] * 255, color[2] * 255, color[3] * 255
|
120
|
+
elif is_array_or_tuple_with_alpha(color):
|
121
|
+
return color[0], color[1], color[2], color[3]
|
122
|
+
else:
|
123
|
+
raise Exception(f'The provided "color" parameter is not an RGBA color.')
|
124
|
+
|
125
|
+
def parse_color(
|
126
|
+
color: Union[str, list, tuple]
|
127
|
+
) -> Union[list, None]:
|
128
|
+
"""
|
129
|
+
Tries to parse the provided 'color' and returns it
|
130
|
+
as an RGBA if parseable, or None if not.
|
131
|
+
"""
|
132
|
+
# As string color
|
133
|
+
string_color = None
|
134
|
+
try:
|
135
|
+
string_color = ColorString.to_enum(color)
|
136
|
+
except:
|
137
|
+
pass
|
138
|
+
|
139
|
+
color_array = None
|
140
|
+
if string_color is not None:
|
141
|
+
color_array = hex_to_rgba(string_color.value)
|
142
|
+
# A hexadecimal string
|
143
|
+
elif PythonValidator.is_string(color) and is_hexadecimal_color(color):
|
144
|
+
color_array = hex_to_rgba(color)
|
145
|
+
else:
|
146
|
+
# RGBA color
|
147
|
+
try:
|
148
|
+
color_array = parse_rgba_color(color)
|
149
|
+
except:
|
150
|
+
pass
|
151
|
+
|
152
|
+
# RGB color
|
153
|
+
try:
|
154
|
+
color_array = *parse_rgb_color(color), 0
|
155
|
+
except:
|
156
|
+
pass
|
157
|
+
|
158
|
+
# TODO: What about HSL, CYMK, etc. (?)
|
159
|
+
|
160
|
+
return color_array
|
161
|
+
|
162
|
+
# These methods below are just actioners, they don't
|
163
|
+
# check anything as they will be used by a class that
|
164
|
+
# validates everything before using these methods.
|
165
|
+
def hex_to_rgba(
|
166
|
+
hex_color: str
|
167
|
+
) -> list:
|
168
|
+
"""
|
169
|
+
Return a tuple containing the color in RGBA order.
|
170
|
+
"""
|
171
|
+
# Hex can start with '0x', '0X' or '#'
|
172
|
+
hex = hex_color.lstrip('#').lstrip('0x').lstrip('0X')
|
173
|
+
if len(hex) == 8:
|
174
|
+
# hex with alpha
|
175
|
+
r, g, b, a = (int(hex[i:i+2], 16) for i in (0, 2, 4, 6))
|
176
|
+
elif len(hex) == 6:
|
177
|
+
# hex without alpha
|
178
|
+
r, g, b, a = *(int(hex[i:i+2], 16) for i in (0, 2, 4)), 0
|
179
|
+
|
180
|
+
return r, g, b, a
|
181
|
+
|
182
|
+
def hex_to_rgb(
|
183
|
+
hex_color: str
|
184
|
+
) -> list:
|
185
|
+
"""
|
186
|
+
Return a tuple containing the color in RGB order.
|
187
|
+
"""
|
188
|
+
r, g, b, _ = hex_to_rgba(hex_color)
|
189
|
+
|
190
|
+
return r, g, b
|
191
|
+
|
192
|
+
def rgb_to_hex(
|
193
|
+
rgb_color: list,
|
194
|
+
do_include_alpha: bool = False
|
195
|
+
) -> list:
|
196
|
+
"""
|
197
|
+
Return a tuple containing the color in RGB or RGBA
|
198
|
+
order (according to the given 'do_include_alpha'
|
199
|
+
parameter value).
|
200
|
+
"""
|
201
|
+
r, g, b = parse_rgb_color(rgb_color)
|
202
|
+
|
203
|
+
return rgba_to_hex([r, g, b, 255], do_include_alpha)
|
204
|
+
|
205
|
+
def rgba_to_hex(
|
206
|
+
rgba_color: list,
|
207
|
+
do_include_alpha: bool = False
|
208
|
+
) -> list:
|
209
|
+
"""
|
210
|
+
Return a tuple containing the color in HEX mode
|
211
|
+
(including the alpha value if the given
|
212
|
+
'do_include_alpha' parameter is True).
|
213
|
+
"""
|
214
|
+
r, g, b, a = parse_rgba_color(rgba_color)
|
215
|
+
|
216
|
+
return (
|
217
|
+
"#{:02x}{:02x}{:02x}{:02x}".format(r, g, b, a)
|
218
|
+
if do_include_alpha else
|
219
|
+
"#{:02x}{:02x}{:02x}".format(r, g, b)
|
220
|
+
)
|
221
|
+
|
222
|
+
def rgba_to_hsl(
|
223
|
+
rgba_color: list
|
224
|
+
) -> list:
|
225
|
+
r, g, b, _ = parse_rgba_color(rgba_color)
|
226
|
+
|
227
|
+
return rgb_to_hsl([r, g, b])
|
228
|
+
|
229
|
+
def rgb_to_hsl(
|
230
|
+
rgb_color: list
|
231
|
+
) -> list:
|
232
|
+
r, g, b = parse_rgb_color(rgb_color)
|
233
|
+
|
234
|
+
# Values normalization
|
235
|
+
r /= 255.0
|
236
|
+
g /= 255.0
|
237
|
+
b /= 255.0
|
238
|
+
|
239
|
+
# Max and minimum values for RGB
|
240
|
+
cmax = max(r, g, b)
|
241
|
+
cmin = min(r, g, b)
|
242
|
+
delta = cmax - cmin
|
243
|
+
|
244
|
+
# Tone (H)
|
245
|
+
h = (
|
246
|
+
0 # No difference => undefined (gray) tone
|
247
|
+
if delta == 0 else
|
248
|
+
(60 * ((g - b) / delta) + 360) % 360
|
249
|
+
if cmax == r else
|
250
|
+
(60 * ((b - r) / delta) + 120) % 360
|
251
|
+
if cmax == g else
|
252
|
+
(60 * ((r - g) / delta) + 240) % 360 # cmax == b
|
253
|
+
)
|
254
|
+
|
255
|
+
# Luminosity (L)
|
256
|
+
l = (cmax + cmin) / 2
|
257
|
+
|
258
|
+
# Saturation (S)
|
259
|
+
s = (
|
260
|
+
0 # No difference => saturation is 0
|
261
|
+
if delta == 0 else
|
262
|
+
delta / (1 - abs(2 * l - 1)) if l != 0 and l != 1 else delta / (2 - (cmax + cmin))
|
263
|
+
)
|
264
|
+
|
265
|
+
# TODO: I saw in some online solutions that they offer
|
266
|
+
# the results without decimal figures
|
267
|
+
return round(h, 2), round(s * 100, 2), round(l * 100, 2)
|
268
|
+
|
269
|
+
# TODO: Add 'hsl_to_rgb'
|
270
|
+
# TODO: Add 'hsl_to_rgba'
|
271
|
+
|
272
|
+
def rgb_to_cymk(
|
273
|
+
rgb_color: list
|
274
|
+
) -> list:
|
275
|
+
r, g, b = parse_rgb_color(rgb_color)
|
276
|
+
|
277
|
+
r, g, b = r / 255.0, g / 255.0, b / 255.0
|
278
|
+
|
279
|
+
k = 1 - max(r, g, b)
|
280
|
+
|
281
|
+
if k == 1:
|
282
|
+
c = m = y = 0
|
283
|
+
else:
|
284
|
+
c = (1 - r - k) / (1 - k)
|
285
|
+
m = (1 - g - k) / (1 - k)
|
286
|
+
y = (1 - b - k) / (1 - k)
|
287
|
+
|
288
|
+
# TODO: I saw in some online solutions that they offer
|
289
|
+
# the results without decimal figures
|
290
|
+
return round(c * 100, 2), round(m * 100, 2), round(y * 100, 2), round(k * 100, 2)
|
291
|
+
|
292
|
+
def rgb_to_hsv(
|
293
|
+
rgb_color
|
294
|
+
) -> list:
|
295
|
+
r, g, b = parse_rgb_color(rgb_color)
|
296
|
+
|
297
|
+
# TODO: Assume this is not normalized
|
298
|
+
return _rgb_to_hsv(r, g, b)
|
299
|
+
|
300
|
+
def rgba_to_hsv(
|
301
|
+
rgba_color
|
302
|
+
) -> list:
|
303
|
+
r, g, b, _ = parse_rgba_color(rgba_color)
|
304
|
+
|
305
|
+
# TODO: Assume this is not normalized
|
306
|
+
return _rgb_to_hsv(r, g, b)
|
307
|
+
|
308
|
+
def hsv_to_rgb(
|
309
|
+
h,
|
310
|
+
s,
|
311
|
+
v
|
312
|
+
):
|
313
|
+
# TODO: Assume this is not normalized
|
314
|
+
return _hsv_to_rgb(h, s, v)
|
315
|
+
|
316
|
+
def hsv_to_rgba(
|
317
|
+
h,
|
318
|
+
s,
|
319
|
+
v
|
320
|
+
):
|
321
|
+
# TODO: Assume this is not normalized
|
322
|
+
return *hsv_to_rgb(h, s, v), 255
|
323
|
+
|
324
|
+
|
325
|
+
|
326
|
+
# TODO: We need to accept colors in different formats
|
327
|
+
# 1. string representing the color ('red', 'blue')
|
328
|
+
# 2. string representing hexa color ('#334455', '0x334455', '0X334455')
|
329
|
+
# 3. array, list or tuple containing 3 or 4 colors, each color can be only 1 number/letter (f means ff)
|
330
|
+
def parse_hexadecimal_color(
|
331
|
+
color: Union[str, list, tuple]
|
332
|
+
) -> Union[list, None]:
|
333
|
+
"""
|
334
|
+
Parse a hexadecimal color. It returns None if unparseable
|
335
|
+
or an array containing [r, g, b, a].
|
336
|
+
|
337
|
+
This method accepts a string color (such as 'black' or
|
338
|
+
'white'), an hexadecimal value (such as '#ffffff' or
|
339
|
+
'0x000000') and an array containing the RGB(A) values.
|
340
|
+
|
341
|
+
TODO: Please, refactor this code.
|
342
|
+
"""
|
343
|
+
if (
|
344
|
+
not is_array_or_tuple_with_alpha(color) and
|
345
|
+
not is_array_or_tuple_without_alpha(color) and
|
346
|
+
not PythonValidator.is_string(color)
|
347
|
+
):
|
348
|
+
# TODO: Raise an Exception or return None here?
|
349
|
+
return None
|
350
|
+
|
351
|
+
alpha = None
|
352
|
+
if is_array_or_tuple_with_alpha(color):
|
353
|
+
color = ''.join(f"{c:02X}" for c in color)
|
354
|
+
alpha = color[-2:]
|
355
|
+
color = color[:-2]
|
356
|
+
#return color
|
357
|
+
if is_array_or_tuple_without_alpha(color):
|
358
|
+
color = f'{"".join(f"{c:02X}" for c in color)}'
|
359
|
+
#return color.append(0)
|
360
|
+
|
361
|
+
color = color.lower()
|
362
|
+
|
363
|
+
# 1. Parse a string color name and turn into its hexadecimal value
|
364
|
+
tmp_color = ColorString.from_color_string(color)
|
365
|
+
if tmp_color is not None:
|
366
|
+
color = tmp_color.value
|
367
|
+
#return tmp_color.as_hex_array.append(0)
|
368
|
+
|
369
|
+
from yta_constants.regex import ColorRegularExpression
|
370
|
+
|
371
|
+
try:
|
372
|
+
# Transform the string to its hexadecimal value '#xxxxxx'
|
373
|
+
color = f'#{ColorString.to_enum(color.replace("#", "").replace("0x", "")).value}'
|
374
|
+
except:
|
375
|
+
pass
|
376
|
+
|
377
|
+
if not ColorRegularExpression.HEXADECIMAL.is_valid(color):
|
378
|
+
# TODO: Raise an Exception or return None here?
|
379
|
+
return None
|
380
|
+
|
381
|
+
# It is a valid hexadecimal string
|
382
|
+
if (
|
383
|
+
ColorRegularExpression.HEXADECIMAL_3_CHARACTERS_NO_ALPHA.is_valid(color) or
|
384
|
+
ColorRegularExpression.HEXADECIMAL_4_CHARACTERS_ALPHA.is_valid(color)
|
385
|
+
):
|
386
|
+
# We duplicate it to make it have 6 or 8 characters
|
387
|
+
# duplicating not the '#' or '0x' flag.
|
388
|
+
color = (
|
389
|
+
f'#{"".join(c * 2 for c in color[1:])}'
|
390
|
+
if color.startswith('#') else
|
391
|
+
f'0x{"".join(c * 2 for c in color[2:])}'
|
392
|
+
if color.startswith('0x') else
|
393
|
+
''.join(c * 2 for c in color)
|
394
|
+
)
|
395
|
+
|
396
|
+
if ColorRegularExpression.HEXADECIMAL_8_CHARACTERS_ALPHA.is_valid(color):
|
397
|
+
#alpha = int(color[-2:], 16)
|
398
|
+
alpha = color[-2:]
|
399
|
+
color = color[:-2]
|
400
|
+
|
401
|
+
alpha = (
|
402
|
+
alpha.lower()
|
403
|
+
if PythonValidator.is_string(alpha) else
|
404
|
+
alpha
|
405
|
+
)
|
406
|
+
|
407
|
+
# String is like '#ffffff' or '0xffffff' here
|
408
|
+
return [
|
409
|
+
color[-6:-4],
|
410
|
+
color[-4:-2],
|
411
|
+
color[-2:],
|
412
|
+
alpha
|
413
|
+
]
|
414
|
+
|
415
|
+
|
416
|
+
|
417
|
+
# TODO: This class has to be different, having '#FFFFFF' is not a good choice
|
418
|
+
from yta_constants.enum import YTAEnum as Enum
|
419
|
+
class ColorString(Enum):
|
420
|
+
"""
|
421
|
+
The hexadecimal string that corresponds to the color.
|
422
|
+
"""
|
423
|
+
|
424
|
+
BLACK = 'ffffff'
|
425
|
+
WHITE = '000000'
|
426
|
+
|
427
|
+
@property
|
428
|
+
def as_color_string(
|
429
|
+
self
|
430
|
+
) -> str:
|
431
|
+
return self.name.lower()
|
432
|
+
|
433
|
+
@property
|
434
|
+
def as_hex_str(
|
435
|
+
self
|
436
|
+
) -> str:
|
437
|
+
"""
|
438
|
+
Get the color as a string in the '#ffffff' format.
|
439
|
+
"""
|
440
|
+
return f'#{self.value.lower()}'
|
441
|
+
|
442
|
+
@property
|
443
|
+
def as_hex_0x_str(
|
444
|
+
self
|
445
|
+
) -> str:
|
446
|
+
"""
|
447
|
+
Get the color as a string in the '0xffffff' format.
|
448
|
+
"""
|
449
|
+
return f'0x{self.value.lower()}'
|
450
|
+
|
451
|
+
@property
|
452
|
+
def as_hex_array(
|
453
|
+
self
|
454
|
+
) -> list:
|
455
|
+
"""
|
456
|
+
Get the color as an array in the [red, green, blue] format
|
457
|
+
"""
|
458
|
+
return [
|
459
|
+
self.value[-6:-4],
|
460
|
+
self.value[-4:-2],
|
461
|
+
self.value[-2:]
|
462
|
+
]
|
463
|
+
|
464
|
+
@property
|
465
|
+
def as_hex_int_array(
|
466
|
+
self
|
467
|
+
) -> list:
|
468
|
+
"""
|
469
|
+
Get the color as an array in the [red, green, blue] format
|
470
|
+
but with integer values.
|
471
|
+
"""
|
472
|
+
return [
|
473
|
+
int(self.value[-6:-4], 16),
|
474
|
+
int(self.value[-4:-2], 16),
|
475
|
+
int(self.value[-2:], 16)
|
476
|
+
]
|
477
|
+
|
478
|
+
@staticmethod
|
479
|
+
def from_color_string(
|
480
|
+
color: str
|
481
|
+
) -> Union[ColorString, None]:
|
482
|
+
"""
|
483
|
+
Parse the provided 'color' string as an Enum instance or
|
484
|
+
get None if not.
|
485
|
+
"""
|
486
|
+
try:
|
487
|
+
return ColorString[color.upper()]
|
488
|
+
except KeyError:
|
489
|
+
return None
|
490
|
+
# TODO: Maybe better an Exception?
|
491
|
+
raise Exception('The given "color" is not valid.')
|
492
|
+
|
493
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2018 The Python Packaging Authority
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
|
+
SOFTWARE.
|
@@ -0,0 +1,16 @@
|
|
1
|
+
Metadata-Version: 2.3
|
2
|
+
Name: yta-colors
|
3
|
+
Version: 0.0.1
|
4
|
+
Summary:
|
5
|
+
Author: danialcala94
|
6
|
+
Author-email: danielalcalavalera@gmail.com
|
7
|
+
Requires-Python: ==3.9
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
9
|
+
Classifier: Programming Language :: Python :: 3.9
|
10
|
+
Requires-Dist: yta_constants (>=0.0.1,<1.0.0)
|
11
|
+
Requires-Dist: yta_validation (>=0.0.1,<1.0.0)
|
12
|
+
Description-Content-Type: text/markdown
|
13
|
+
|
14
|
+
# Youtube Autonomous Colors add-on
|
15
|
+
|
16
|
+
The way to handle and validate colors.
|
@@ -0,0 +1,7 @@
|
|
1
|
+
yta_colors/__init__.py,sha256=Gk5tQF7zwqJy_DjtOQIysUG-ZQPu8AhK7krGhVLGI4A,6590
|
2
|
+
yta_colors/converter.py,sha256=mGOopt8VoRTPyYrcnZ19RErxg69kYJYrxzkKG2zuitY,4934
|
3
|
+
yta_colors/utils.py,sha256=2fAPZaQrBs7617sW3WmcUAi6YOjnCrO3kXpOkCH4zxo,13252
|
4
|
+
yta_colors-0.0.1.dist-info/LICENSE,sha256=6kbiFSfobTZ7beWiKnHpN902HgBx-Jzgcme0SvKqhKY,1091
|
5
|
+
yta_colors-0.0.1.dist-info/METADATA,sha256=YBzkS_uwC7nFS92g9Rd9ml_BTkK0OmYBR_RXnmAlgVM,458
|
6
|
+
yta_colors-0.0.1.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
|
7
|
+
yta_colors-0.0.1.dist-info/RECORD,,
|