xulbux 1.6.0__py3-none-any.whl → 1.6.2__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.
Potentially problematic release.
This version of xulbux might be problematic. Click here for more details.
- xulbux/__init__.py +1 -1
- xulbux/_consts_.py +16 -16
- xulbux/xx_code.py +4 -4
- xulbux/xx_color.py +155 -144
- xulbux/xx_console.py +26 -41
- xulbux/xx_data.py +69 -66
- xulbux/xx_env_path.py +4 -4
- xulbux/xx_file.py +35 -26
- xulbux/xx_format_codes.py +122 -64
- xulbux/xx_json.py +27 -22
- xulbux/xx_path.py +2 -7
- xulbux/xx_regex.py +61 -61
- xulbux/xx_string.py +14 -13
- xulbux/xx_system.py +59 -8
- {xulbux-1.6.0.dist-info → xulbux-1.6.2.dist-info}/METADATA +3 -3
- xulbux-1.6.2.dist-info/RECORD +21 -0
- {xulbux-1.6.0.dist-info → xulbux-1.6.2.dist-info}/WHEEL +1 -1
- xulbux-1.6.0.dist-info/RECORD +0 -21
- {xulbux-1.6.0.dist-info → xulbux-1.6.2.dist-info}/LICENSE +0 -0
- {xulbux-1.6.0.dist-info → xulbux-1.6.2.dist-info}/entry_points.txt +0 -0
- {xulbux-1.6.0.dist-info → xulbux-1.6.2.dist-info}/top_level.txt +0 -0
xulbux/xx_color.py
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
`hexa`:
|
|
9
9
|
A HEX color: is a string in the format `RGB`, `RGBA`, `RRGGBB` or `RRGGBBAA` (where `R` `G` `B` `A` are hexadecimal digits).
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
-------------------------------------------------------------------------------------------------------------------------------------
|
|
12
12
|
The `Color` class, which contains all sorts of different color-related methods:
|
|
13
13
|
- validate colors:
|
|
14
14
|
- is valid rgba
|
|
@@ -37,9 +37,9 @@ import re as _re
|
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
class rgba:
|
|
40
|
-
"""An RGB/RGBA color: is a tuple of 3 integers, representing the red (`0`-`255`), green (`0`-`255`), and blue (`0`-`255`)
|
|
40
|
+
"""An RGB/RGBA color: is a tuple of 3 integers, representing the red (`0`-`255`), green (`0`-`255`), and blue (`0`-`255`).\n
|
|
41
41
|
Also includes an optional 4th param, which is a float, that represents the alpha channel (`0.0`-`1.0`).\n
|
|
42
|
-
|
|
42
|
+
-----------------------------------------------------------------------------------------------------------------------------
|
|
43
43
|
Includes methods:
|
|
44
44
|
- `to_hsla()` to convert to HSL color
|
|
45
45
|
- `to_hexa()` to convert to HEX color
|
|
@@ -59,7 +59,10 @@ class rgba:
|
|
|
59
59
|
- `with_alpha(alpha)` to create a new color with different alpha
|
|
60
60
|
- `complementary()` to get the complementary color"""
|
|
61
61
|
|
|
62
|
-
def __init__(self, r: int, g: int, b: int, a: float = None):
|
|
62
|
+
def __init__(self, r: int, g: int, b: int, a: float = None, _validate: bool = True):
|
|
63
|
+
if not _validate:
|
|
64
|
+
self.r, self.g, self.b, self.a = r, g, b, a
|
|
65
|
+
return
|
|
63
66
|
if any(isinstance(x, rgba) for x in (r, g, b)):
|
|
64
67
|
raise ValueError("Color is already a rgba() color")
|
|
65
68
|
elif not all(isinstance(x, int) and 0 <= x <= 255 for x in (r, g, b)):
|
|
@@ -68,27 +71,27 @@ class rgba:
|
|
|
68
71
|
(r, g, b),
|
|
69
72
|
)
|
|
70
73
|
elif a is not None and not (isinstance(a, (int, float)) and 0 <= a <= 1):
|
|
71
|
-
raise ValueError("Alpha channel must be a float/int in [0.0, 1.0]: got
|
|
74
|
+
raise ValueError(f"Alpha channel must be a float/int in [0.0, 1.0]: got '{a}'")
|
|
72
75
|
self.r, self.g, self.b = r, g, b
|
|
73
|
-
self.a = (1.0 if a > 1.0 else float(a))
|
|
76
|
+
self.a = None if a is None else (1.0 if a > 1.0 else float(a))
|
|
74
77
|
|
|
75
78
|
def __len__(self):
|
|
76
|
-
return
|
|
79
|
+
return 3 if self.a is None else 4
|
|
77
80
|
|
|
78
81
|
def __iter__(self):
|
|
79
|
-
return iter((self.r, self.g, self.b) + ((
|
|
82
|
+
return iter((self.r, self.g, self.b) + (() if self.a is None else (self.a,)))
|
|
80
83
|
|
|
81
84
|
def __dict__(self):
|
|
82
85
|
return self.dict()
|
|
83
86
|
|
|
84
87
|
def __getitem__(self, index):
|
|
85
|
-
return ((self.r, self.g, self.b) + ((
|
|
88
|
+
return ((self.r, self.g, self.b) + (() if self.a is None else (self.a,)))[index]
|
|
86
89
|
|
|
87
90
|
def __repr__(self):
|
|
88
|
-
return f'rgba({self.r}, {self.g}, {self.b}{
|
|
91
|
+
return f'rgba({self.r}, {self.g}, {self.b}{"" if self.a is None else f", {self.a}"})'
|
|
89
92
|
|
|
90
93
|
def __str__(self):
|
|
91
|
-
return f'({self.r}, {self.g}, {self.b}{
|
|
94
|
+
return f'({self.r}, {self.g}, {self.b}{"" if self.a is None else f", {self.a}"})'
|
|
92
95
|
|
|
93
96
|
def __eq__(self, other):
|
|
94
97
|
if not isinstance(other, rgba):
|
|
@@ -102,7 +105,7 @@ class rgba:
|
|
|
102
105
|
|
|
103
106
|
def dict(self) -> dict:
|
|
104
107
|
"""Returns the color components as a dictionary with keys `'r'`, `'g'`, `'b'` and optionally `'a'`"""
|
|
105
|
-
return dict(r=self.r, g=self.g, b=self.b
|
|
108
|
+
return dict(r=self.r, g=self.g, b=self.b) if self.a is None else dict(r=self.r, g=self.g, b=self.b, a=self.a)
|
|
106
109
|
|
|
107
110
|
def values(self) -> tuple:
|
|
108
111
|
"""Returns the color components as separate values `r, g, b, a`"""
|
|
@@ -110,11 +113,11 @@ class rgba:
|
|
|
110
113
|
|
|
111
114
|
def to_hsla(self) -> "hsla":
|
|
112
115
|
"""Returns the color as a `hsla()` color"""
|
|
113
|
-
return hsla(*self._rgb_to_hsl(self.r, self.g, self.b), self.a)
|
|
116
|
+
return hsla(*self._rgb_to_hsl(self.r, self.g, self.b), self.a, _validate=False)
|
|
114
117
|
|
|
115
118
|
def to_hexa(self) -> "hexa":
|
|
116
119
|
"""Returns the color as a `hexa()` color"""
|
|
117
|
-
return hexa(
|
|
120
|
+
return hexa("", self.r, self.g, self.b, self.a)
|
|
118
121
|
|
|
119
122
|
def has_alpha(self) -> bool:
|
|
120
123
|
"""Returns `True` if the color has an alpha channel and `False` otherwise"""
|
|
@@ -123,50 +126,50 @@ class rgba:
|
|
|
123
126
|
def lighten(self, amount: float) -> "rgba":
|
|
124
127
|
"""Increases the colors lightness by the specified amount (`0.0`-`1.0`)"""
|
|
125
128
|
self.r, self.g, self.b, self.a = self.to_hsla().lighten(amount).to_rgba().values()
|
|
126
|
-
return rgba(self.r, self.g, self.b, self.a)
|
|
129
|
+
return rgba(self.r, self.g, self.b, self.a, _validate=False)
|
|
127
130
|
|
|
128
131
|
def darken(self, amount: float) -> "rgba":
|
|
129
132
|
"""Decreases the colors lightness by the specified amount (`0.0`-`1.0`)"""
|
|
130
133
|
self.r, self.g, self.b, self.a = self.to_hsla().darken(amount).to_rgba().values()
|
|
131
|
-
return rgba(self.r, self.g, self.b, self.a)
|
|
134
|
+
return rgba(self.r, self.g, self.b, self.a, _validate=False)
|
|
132
135
|
|
|
133
136
|
def saturate(self, amount: float) -> "rgba":
|
|
134
137
|
"""Increases the colors saturation by the specified amount (`0.0`-`1.0`)"""
|
|
135
138
|
self.r, self.g, self.b, self.a = self.to_hsla().saturate(amount).to_rgba().values()
|
|
136
|
-
return rgba(self.r, self.g, self.b, self.a)
|
|
139
|
+
return rgba(self.r, self.g, self.b, self.a, _validate=False)
|
|
137
140
|
|
|
138
141
|
def desaturate(self, amount: float) -> "rgba":
|
|
139
142
|
"""Decreases the colors saturation by the specified amount (`0.0`-`1.0`)"""
|
|
140
143
|
self.r, self.g, self.b, self.a = self.to_hsla().desaturate(amount).to_rgba().values()
|
|
141
|
-
return rgba(self.r, self.g, self.b, self.a)
|
|
144
|
+
return rgba(self.r, self.g, self.b, self.a, _validate=False)
|
|
142
145
|
|
|
143
146
|
def rotate(self, degrees: int) -> "rgba":
|
|
144
147
|
"""Rotates the colors hue by the specified number of degrees"""
|
|
145
148
|
self.r, self.g, self.b, self.a = self.to_hsla().rotate(degrees).to_rgba().values()
|
|
146
|
-
return rgba(self.r, self.g, self.b, self.a)
|
|
149
|
+
return rgba(self.r, self.g, self.b, self.a, _validate=False)
|
|
147
150
|
|
|
148
151
|
def invert(self, invert_alpha: bool = False) -> "rgba":
|
|
149
152
|
"""Inverts the color by rotating hue by 180 degrees and inverting lightness"""
|
|
150
153
|
self.r, self.g, self.b = 255 - self.r, 255 - self.g, 255 - self.b
|
|
151
154
|
if invert_alpha:
|
|
152
155
|
self.a = 1 - self.a
|
|
153
|
-
return rgba(self.r, self.g, self.b, self.a)
|
|
156
|
+
return rgba(self.r, self.g, self.b, self.a, _validate=False)
|
|
154
157
|
|
|
155
158
|
def grayscale(self) -> "rgba":
|
|
156
159
|
"""Converts the color to grayscale using the luminance formula"""
|
|
157
160
|
self.r = self.g = self.b = Color.luminance(self.r, self.g, self.b)
|
|
158
|
-
return rgba(self.r, self.g, self.b, self.a)
|
|
161
|
+
return rgba(self.r, self.g, self.b, self.a, _validate=False)
|
|
159
162
|
|
|
160
163
|
def blend(self, other: "rgba", ratio: float = 0.5, additive_alpha: bool = False) -> "rgba":
|
|
161
|
-
"""Blends the current color with another color using the specified ratio (`0.0`-`1.0`)
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
164
|
+
"""Blends the current color with another color using the specified ratio (`0.0`-`1.0`):
|
|
165
|
+
- if `ratio` is `0.0` it means 100% of the current color and 0% of the `other` color (2:0 mixture)
|
|
166
|
+
- if `ratio` is `0.5` it means 50% of both colors (1:1 mixture)
|
|
167
|
+
- if `ratio` is `1.0` it means 0% of the current color and 100% of the `other` color (0:2 mixture)"""
|
|
165
168
|
if not (isinstance(ratio, (int, float)) and 0 <= ratio <= 1):
|
|
166
169
|
raise ValueError("'ratio' must be a float/int in [0.0, 1.0]")
|
|
167
170
|
elif not isinstance(other, rgba):
|
|
168
171
|
if Color.is_valid_rgba(other):
|
|
169
|
-
other = rgba(*other)
|
|
172
|
+
other = rgba(*other, _validate=False)
|
|
170
173
|
else:
|
|
171
174
|
raise TypeError("'other' must be a valid RGBA color")
|
|
172
175
|
ratio *= 2
|
|
@@ -175,7 +178,7 @@ class rgba:
|
|
|
175
178
|
self.b = max(0, min(255, int(round((self.b * (2 - ratio)) + (other.b * ratio)))))
|
|
176
179
|
none_alpha = self.a is None and (len(other) <= 3 or other[3] is None)
|
|
177
180
|
if not none_alpha:
|
|
178
|
-
self_a =
|
|
181
|
+
self_a = 1 if self.a is None else self.a
|
|
179
182
|
other_a = (other[3] if other[3] is not None else 1) if len(other) > 3 else 1
|
|
180
183
|
if additive_alpha:
|
|
181
184
|
self.a = max(0, min(1, (self_a * (2 - ratio)) + (other_a * ratio)))
|
|
@@ -189,11 +192,11 @@ class rgba:
|
|
|
189
192
|
)
|
|
190
193
|
else:
|
|
191
194
|
self.a = None
|
|
192
|
-
return rgba(self.r, self.g, self.b, None if none_alpha else self.a)
|
|
195
|
+
return rgba(self.r, self.g, self.b, None if none_alpha else self.a, _validate=False)
|
|
193
196
|
|
|
194
197
|
def is_dark(self) -> bool:
|
|
195
198
|
"""Returns `True` if the color is considered dark (`lightness < 50%`)"""
|
|
196
|
-
return
|
|
199
|
+
return self.to_hsla().is_dark()
|
|
197
200
|
|
|
198
201
|
def is_light(self) -> bool:
|
|
199
202
|
"""Returns `True` if the color is considered light (`lightness >= 50%`)"""
|
|
@@ -211,7 +214,7 @@ class rgba:
|
|
|
211
214
|
"""Returns a new color with the specified alpha value"""
|
|
212
215
|
if not (isinstance(alpha, (int, float)) and 0 <= alpha <= 1):
|
|
213
216
|
raise ValueError("'alpha' must be a float/int in [0.0, 1.0]")
|
|
214
|
-
return rgba(self.r, self.g, self.b, alpha)
|
|
217
|
+
return rgba(self.r, self.g, self.b, alpha, _validate=False)
|
|
215
218
|
|
|
216
219
|
def complementary(self) -> "rgba":
|
|
217
220
|
"""Returns the complementary color (180 degrees on the color wheel)"""
|
|
@@ -237,9 +240,9 @@ class rgba:
|
|
|
237
240
|
|
|
238
241
|
|
|
239
242
|
class hsla:
|
|
240
|
-
"""A HSL/HSLA color: is a tuple of 3 integers, representing hue (`0`-`360`), saturation (`0`-`100`), and lightness (`0`-`100`)
|
|
243
|
+
"""A HSL/HSLA color: is a tuple of 3 integers, representing hue (`0`-`360`), saturation (`0`-`100`), and lightness (`0`-`100`).\n
|
|
241
244
|
Also includes an optional 4th param, which is a float, that represents the alpha channel (`0.0`-`1.0`).\n
|
|
242
|
-
|
|
245
|
+
----------------------------------------------------------------------------------------------------------------------------------
|
|
243
246
|
Includes methods:
|
|
244
247
|
- `to_rgba()` to convert to RGB color
|
|
245
248
|
- `to_hexa()` to convert to HEX color
|
|
@@ -259,7 +262,10 @@ class hsla:
|
|
|
259
262
|
- `with_alpha(alpha)` to create a new color with different alpha
|
|
260
263
|
- `complementary()` to get the complementary color"""
|
|
261
264
|
|
|
262
|
-
def __init__(self, h: int, s: int, l: int, a: float = None):
|
|
265
|
+
def __init__(self, h: int, s: int, l: int, a: float = None, _validate: bool = True):
|
|
266
|
+
if not _validate:
|
|
267
|
+
self.h, self.s, self.l, self.a = h, s, l, a
|
|
268
|
+
return
|
|
263
269
|
if any(isinstance(x, hsla) for x in (h, s, l)):
|
|
264
270
|
raise ValueError("Color is already a hsla() color")
|
|
265
271
|
elif not (isinstance(h, int) and (0 <= h <= 360) and all(isinstance(x, int) and (0 <= x <= 100) for x in (s, l))):
|
|
@@ -268,27 +274,27 @@ class hsla:
|
|
|
268
274
|
(h, s, l),
|
|
269
275
|
)
|
|
270
276
|
elif a is not None and (not isinstance(a, (int, float)) or not 0 <= a <= 1):
|
|
271
|
-
raise ValueError("Alpha channel must be a float/int in [0.0, 1.0]: got
|
|
277
|
+
raise ValueError(f"Alpha channel must be a float/int in [0.0, 1.0]: got '{a}'")
|
|
272
278
|
self.h, self.s, self.l = h, s, l
|
|
273
|
-
self.a = (1.0 if a > 1.0 else float(a))
|
|
279
|
+
self.a = None if a is None else (1.0 if a > 1.0 else float(a))
|
|
274
280
|
|
|
275
281
|
def __len__(self):
|
|
276
|
-
return
|
|
282
|
+
return 3 if self.a is None else 4
|
|
277
283
|
|
|
278
284
|
def __iter__(self):
|
|
279
|
-
return iter((self.h, self.s, self.l) + ((
|
|
285
|
+
return iter((self.h, self.s, self.l) + (() if self.a is None else (self.a,)))
|
|
280
286
|
|
|
281
287
|
def __dict__(self):
|
|
282
288
|
return self.dict()
|
|
283
289
|
|
|
284
290
|
def __getitem__(self, index):
|
|
285
|
-
return ((self.h, self.s, self.l) + ((
|
|
291
|
+
return ((self.h, self.s, self.l) + (() if self.a is None else (self.a,)))[index]
|
|
286
292
|
|
|
287
293
|
def __repr__(self):
|
|
288
|
-
return f'hsla({self.h}, {self.s}, {self.l}{
|
|
294
|
+
return f'hsla({self.h}, {self.s}, {self.l}{"" if self.a is None else f", {self.a}"})'
|
|
289
295
|
|
|
290
296
|
def __str__(self):
|
|
291
|
-
return f'({self.h}, {self.s}, {self.l}{
|
|
297
|
+
return f'({self.h}, {self.s}, {self.l}{"" if self.a is None else f", {self.a}"})'
|
|
292
298
|
|
|
293
299
|
def __eq__(self, other):
|
|
294
300
|
if not isinstance(other, hsla):
|
|
@@ -302,7 +308,7 @@ class hsla:
|
|
|
302
308
|
|
|
303
309
|
def dict(self) -> dict:
|
|
304
310
|
"""Returns the color components as a dictionary with keys `'h'`, `'s'`, `'l'` and optionally `'a'`"""
|
|
305
|
-
return dict(h=self.h, s=self.s, l=self.l
|
|
311
|
+
return dict(h=self.h, s=self.s, l=self.l) if self.a is None else dict(h=self.h, s=self.s, l=self.l, a=self.a)
|
|
306
312
|
|
|
307
313
|
def values(self) -> tuple:
|
|
308
314
|
"""Returns the color components as separate values `h, s, l, a`"""
|
|
@@ -310,12 +316,12 @@ class hsla:
|
|
|
310
316
|
|
|
311
317
|
def to_rgba(self) -> "rgba":
|
|
312
318
|
"""Returns the color as a `rgba()` color"""
|
|
313
|
-
return rgba(*self._hsl_to_rgb(self.h, self.s, self.l), self.a)
|
|
319
|
+
return rgba(*self._hsl_to_rgb(self.h, self.s, self.l), self.a, _validate=False)
|
|
314
320
|
|
|
315
321
|
def to_hexa(self) -> "hexa":
|
|
316
322
|
"""Returns the color as a `hexa()` color"""
|
|
317
323
|
r, g, b = self._hsl_to_rgb(self.h, self.s, self.l)
|
|
318
|
-
return hexa(
|
|
324
|
+
return hexa("", r, g, b, self.a)
|
|
319
325
|
|
|
320
326
|
def has_alpha(self) -> bool:
|
|
321
327
|
"""Returns `True` if the color has an alpha channel and `False` otherwise"""
|
|
@@ -326,33 +332,33 @@ class hsla:
|
|
|
326
332
|
if not (isinstance(amount, (int, float)) and 0 <= amount <= 1):
|
|
327
333
|
raise ValueError("'amount' must be a float/int in [0.0, 1.0]")
|
|
328
334
|
self.l = int(min(100, self.l + (100 - self.l) * amount))
|
|
329
|
-
return hsla(self.h, self.s, self.l, self.a)
|
|
335
|
+
return hsla(self.h, self.s, self.l, self.a, _validate=False)
|
|
330
336
|
|
|
331
337
|
def darken(self, amount: float) -> "hsla":
|
|
332
338
|
"""Decreases the colors lightness by the specified amount (`0.0`-`1.0`)"""
|
|
333
339
|
if not (isinstance(amount, (int, float)) and 0 <= amount <= 1):
|
|
334
340
|
raise ValueError("'amount' must be a float/int in [0.0, 1.0]")
|
|
335
341
|
self.l = int(max(0, self.l * (1 - amount)))
|
|
336
|
-
return hsla(self.h, self.s, self.l, self.a)
|
|
342
|
+
return hsla(self.h, self.s, self.l, self.a, _validate=False)
|
|
337
343
|
|
|
338
344
|
def saturate(self, amount: float) -> "hsla":
|
|
339
345
|
"""Increases the colors saturation by the specified amount (`0.0`-`1.0`)"""
|
|
340
346
|
if not (isinstance(amount, (int, float)) and 0 <= amount <= 1):
|
|
341
347
|
raise ValueError("'amount' must be a float/int in [0.0, 1.0]")
|
|
342
348
|
self.s = int(min(100, self.s + (100 - self.s) * amount))
|
|
343
|
-
return hsla(self.h, self.s, self.l, self.a)
|
|
349
|
+
return hsla(self.h, self.s, self.l, self.a, _validate=False)
|
|
344
350
|
|
|
345
351
|
def desaturate(self, amount: float) -> "hsla":
|
|
346
352
|
"""Decreases the colors saturation by the specified amount (`0.0`-`1.0`)"""
|
|
347
353
|
if not (isinstance(amount, (int, float)) and 0 <= amount <= 1):
|
|
348
354
|
raise ValueError("'amount' must be a float/int in [0.0, 1.0]")
|
|
349
355
|
self.s = int(max(0, self.s * (1 - amount)))
|
|
350
|
-
return hsla(self.h, self.s, self.l, self.a)
|
|
356
|
+
return hsla(self.h, self.s, self.l, self.a, _validate=False)
|
|
351
357
|
|
|
352
358
|
def rotate(self, degrees: int) -> "hsla":
|
|
353
359
|
"""Rotates the colors hue by the specified number of degrees"""
|
|
354
360
|
self.h = (self.h + degrees) % 360
|
|
355
|
-
return hsla(self.h, self.s, self.l, self.a)
|
|
361
|
+
return hsla(self.h, self.s, self.l, self.a, _validate=False)
|
|
356
362
|
|
|
357
363
|
def invert(self, invert_alpha: bool = False) -> "hsla":
|
|
358
364
|
"""Inverts the color by rotating hue by 180 degrees and inverting lightness"""
|
|
@@ -360,21 +366,21 @@ class hsla:
|
|
|
360
366
|
self.l = 100 - self.l
|
|
361
367
|
if invert_alpha:
|
|
362
368
|
self.a = 1 - self.a
|
|
363
|
-
return hsla(self.h, self.s, self.l, self.a)
|
|
369
|
+
return hsla(self.h, self.s, self.l, self.a, _validate=False)
|
|
364
370
|
|
|
365
371
|
def grayscale(self) -> "hsla":
|
|
366
372
|
"""Converts the color to grayscale using the luminance formula"""
|
|
367
373
|
l = Color.luminance(*self._hsl_to_rgb(self.h, self.s, self.l))
|
|
368
|
-
self.h, self.s, self.l, _ = rgba(l, l, l).to_hsla().values()
|
|
369
|
-
return hsla(self.h, self.s, self.l, self.a)
|
|
374
|
+
self.h, self.s, self.l, _ = rgba(l, l, l, _validate=False).to_hsla().values()
|
|
375
|
+
return hsla(self.h, self.s, self.l, self.a, _validate=False)
|
|
370
376
|
|
|
371
377
|
def blend(self, other: "hsla", ratio: float = 0.5, additive_alpha: bool = False) -> "rgba":
|
|
372
|
-
"""Blends the current color with another color using the specified ratio (`0.0`-`1.0`)
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
378
|
+
"""Blends the current color with another color using the specified ratio (`0.0`-`1.0`):
|
|
379
|
+
- if `ratio` is `0.0` it means 100% of the current color and 0% of the `other` color (2:0 mixture)
|
|
380
|
+
- if `ratio` is `0.5` it means 50% of both colors (1:1 mixture)
|
|
381
|
+
- if `ratio` is `1.0` it means 0% of the current color and 100% of the `other` color (0:2 mixture)"""
|
|
376
382
|
self.h, self.s, self.l, self.a = self.to_rgba().blend(Color.to_rgba(other), ratio, additive_alpha).to_hsla().values()
|
|
377
|
-
return hsla(self.h, self.s, self.l, self.a)
|
|
383
|
+
return hsla(self.h, self.s, self.l, self.a, _validate=False)
|
|
378
384
|
|
|
379
385
|
def is_dark(self) -> bool:
|
|
380
386
|
"""Returns `True` if the color is considered dark (`lightness < 50%`)"""
|
|
@@ -396,11 +402,11 @@ class hsla:
|
|
|
396
402
|
"""Returns a new color with the specified alpha value"""
|
|
397
403
|
if not (isinstance(alpha, (int, float)) and 0 <= alpha <= 1):
|
|
398
404
|
raise ValueError("'alpha' must be a float/int in [0.0, 1.0]")
|
|
399
|
-
return hsla(self.h, self.s, self.l, alpha)
|
|
405
|
+
return hsla(self.h, self.s, self.l, alpha, _validate=False)
|
|
400
406
|
|
|
401
407
|
def complementary(self) -> "hsla":
|
|
402
408
|
"""Returns the complementary color (180 degrees on the color wheel)"""
|
|
403
|
-
return hsla((self.h + 180) % 360, self.s, self.l, self.a)
|
|
409
|
+
return hsla((self.h + 180) % 360, self.s, self.l, self.a, _validate=False)
|
|
404
410
|
|
|
405
411
|
def _hsl_to_rgb(self, h: int, s: int, l: int) -> tuple:
|
|
406
412
|
h, s, l = h / 360, s / 100, l / 100
|
|
@@ -432,7 +438,7 @@ class hsla:
|
|
|
432
438
|
class hexa:
|
|
433
439
|
"""A HEX color: is a string representing a hexadecimal color code with optional alpha channel.\n
|
|
434
440
|
-------------------------------------------------------------------------------------------------
|
|
435
|
-
Supports formats: RGB, RGBA, RRGGBB, RRGGBBAA (with or without prefix)
|
|
441
|
+
Supports formats: RGB, RGBA, RRGGBB, RRGGBBAA (with or without prefix)
|
|
436
442
|
Includes methods:
|
|
437
443
|
- `to_rgba()` to convert to RGB color
|
|
438
444
|
- `to_hsla()` to convert to HSL color
|
|
@@ -452,7 +458,10 @@ class hexa:
|
|
|
452
458
|
- `with_alpha(alpha)` to create a new color with different alpha
|
|
453
459
|
- `complementary()` to get the complementary color"""
|
|
454
460
|
|
|
455
|
-
def __init__(self, color: str | int):
|
|
461
|
+
def __init__(self, color: str | int, _r: int = None, _g: int = None, _b: int = None, _a: float = None):
|
|
462
|
+
if all(x is not None for x in (_r, _g, _b)):
|
|
463
|
+
self.r, self.g, self.b, self.a = _r, _g, _b, _a
|
|
464
|
+
return
|
|
456
465
|
if isinstance(color, hexa):
|
|
457
466
|
raise ValueError("Color is already a hexa() color")
|
|
458
467
|
if isinstance(color, str):
|
|
@@ -491,27 +500,31 @@ class hexa:
|
|
|
491
500
|
else:
|
|
492
501
|
raise ValueError(f"Invalid HEX format '{color}'")
|
|
493
502
|
elif isinstance(color, int):
|
|
494
|
-
self.r, self.g, self.b, self.a = Color.hex_int_to_rgba(color)
|
|
503
|
+
self.r, self.g, self.b, self.a = Color.hex_int_to_rgba(color).values()
|
|
495
504
|
else:
|
|
496
|
-
raise TypeError("HEX color must be of type 'str' or 'int': got
|
|
505
|
+
raise TypeError(f"HEX color must be of type 'str' or 'int': got '{type(color)}'")
|
|
497
506
|
|
|
498
507
|
def __len__(self):
|
|
499
|
-
return
|
|
508
|
+
return 3 if self.a is None else 4
|
|
500
509
|
|
|
501
510
|
def __iter__(self):
|
|
502
|
-
return iter(
|
|
511
|
+
return iter(
|
|
512
|
+
(f"{self.r:02X}", f"{self.g:02X}", f"{self.b:02X}") + (() if self.a is None else (f"{int(self.a * 255):02X}",))
|
|
513
|
+
)
|
|
503
514
|
|
|
504
515
|
def __dict__(self):
|
|
505
516
|
return self.dict()
|
|
506
517
|
|
|
507
518
|
def __getitem__(self, index):
|
|
508
|
-
return (
|
|
519
|
+
return (
|
|
520
|
+
(f"{self.r:02X}", f"{self.g:02X}", f"{self.b:02X}") + (() if self.a is None else (f"{int(self.a * 255):02X}",))
|
|
521
|
+
)[index]
|
|
509
522
|
|
|
510
523
|
def __repr__(self):
|
|
511
|
-
return f'hexa(#{self.r:02X}{self.g:02X}{self.b:02X}{f"{int(self.a * 255):02X}"
|
|
524
|
+
return f'hexa(#{self.r:02X}{self.g:02X}{self.b:02X}{"" if self.a is None else f"{int(self.a * 255):02X}"})'
|
|
512
525
|
|
|
513
526
|
def __str__(self):
|
|
514
|
-
return f'#{self.r:02X}{self.g:02X}{self.b:02X}{f"{int(self.a * 255):02X}"
|
|
527
|
+
return f'#{self.r:02X}{self.g:02X}{self.b:02X}{"" if self.a is None else f"{int(self.a * 255):02X}"}'
|
|
515
528
|
|
|
516
529
|
def __eq__(self, other):
|
|
517
530
|
if not isinstance(other, hexa):
|
|
@@ -526,14 +539,14 @@ class hexa:
|
|
|
526
539
|
def dict(self) -> dict:
|
|
527
540
|
"""Returns the color components as a dictionary with hex string values for keys `'r'`, `'g'`, `'b'` and optionally `'a'`"""
|
|
528
541
|
return (
|
|
529
|
-
dict(
|
|
542
|
+
dict(r=f"{self.r:02X}", g=f"{self.g:02X}", b=f"{self.b:02X}")
|
|
543
|
+
if self.a is None
|
|
544
|
+
else dict(
|
|
530
545
|
r=f"{self.r:02X}",
|
|
531
546
|
g=f"{self.g:02X}",
|
|
532
547
|
b=f"{self.b:02X}",
|
|
533
548
|
a=f"{int(self.a * 255):02X}",
|
|
534
549
|
)
|
|
535
|
-
if self.a
|
|
536
|
-
else dict(r=f"{self.r:02X}", g=f"{self.g:02X}", b=f"{self.b:02X}")
|
|
537
550
|
)
|
|
538
551
|
|
|
539
552
|
def values(self) -> tuple:
|
|
@@ -546,7 +559,8 @@ class hexa:
|
|
|
546
559
|
self.r,
|
|
547
560
|
self.g,
|
|
548
561
|
self.b,
|
|
549
|
-
(round(self.a, 2) if round_alpha else self.a)
|
|
562
|
+
None if self.a is None else (round(self.a, 2) if round_alpha else self.a),
|
|
563
|
+
_validate=False,
|
|
550
564
|
)
|
|
551
565
|
|
|
552
566
|
def to_hsla(self, round_alpha: bool = True) -> "hsla":
|
|
@@ -560,47 +574,47 @@ class hexa:
|
|
|
560
574
|
def lighten(self, amount: float) -> "hexa":
|
|
561
575
|
"""Increases the colors lightness by the specified amount (`0.0`-`1.0`)"""
|
|
562
576
|
self.r, self.g, self.b, self.a = self.to_rgba(False).lighten(amount).values()
|
|
563
|
-
return hexa(
|
|
577
|
+
return hexa("", self.r, self.g, self.b, self.a)
|
|
564
578
|
|
|
565
579
|
def darken(self, amount: float) -> "hexa":
|
|
566
580
|
"""Decreases the colors lightness by the specified amount (`0.0`-`1.0`)"""
|
|
567
581
|
self.r, self.g, self.b, self.a = self.to_rgba(False).darken(amount).values()
|
|
568
|
-
return hexa(
|
|
582
|
+
return hexa("", self.r, self.g, self.b, self.a)
|
|
569
583
|
|
|
570
584
|
def saturate(self, amount: float) -> "hexa":
|
|
571
585
|
"""Increases the colors saturation by the specified amount (`0.0`-`1.0`)"""
|
|
572
586
|
self.r, self.g, self.b, self.a = self.to_rgba(False).saturate(amount).values()
|
|
573
|
-
return hexa(
|
|
587
|
+
return hexa("", self.r, self.g, self.b, self.a)
|
|
574
588
|
|
|
575
589
|
def desaturate(self, amount: float) -> "hexa":
|
|
576
590
|
"""Decreases the colors saturation by the specified amount (`0.0`-`1.0`)"""
|
|
577
591
|
self.r, self.g, self.b, self.a = self.to_rgba(False).desaturate(amount).values()
|
|
578
|
-
return hexa(
|
|
592
|
+
return hexa("", self.r, self.g, self.b, self.a)
|
|
579
593
|
|
|
580
594
|
def rotate(self, degrees: int) -> "hexa":
|
|
581
595
|
"""Rotates the colors hue by the specified number of degrees"""
|
|
582
596
|
self.r, self.g, self.b, self.a = self.to_rgba(False).rotate(degrees).values()
|
|
583
|
-
return hexa(
|
|
597
|
+
return hexa("", self.r, self.g, self.b, self.a)
|
|
584
598
|
|
|
585
599
|
def invert(self, invert_alpha: bool = False) -> "hexa":
|
|
586
600
|
"""Inverts the color by rotating hue by 180 degrees and inverting lightness"""
|
|
587
601
|
self.r, self.g, self.b, self.a = self.to_rgba(False).invert().values()
|
|
588
602
|
if invert_alpha:
|
|
589
603
|
self.a = 1 - self.a
|
|
590
|
-
return hexa(
|
|
604
|
+
return hexa("", self.r, self.g, self.b, self.a)
|
|
591
605
|
|
|
592
606
|
def grayscale(self) -> "hexa":
|
|
593
607
|
"""Converts the color to grayscale using the luminance formula"""
|
|
594
608
|
self.r = self.g = self.b = Color.luminance(self.r, self.g, self.b)
|
|
595
|
-
return hexa(
|
|
609
|
+
return hexa("", self.r, self.g, self.b, self.a)
|
|
596
610
|
|
|
597
611
|
def blend(self, other: "hexa", ratio: float = 0.5, additive_alpha: bool = False) -> "rgba":
|
|
598
|
-
"""Blends the current color with another color using the specified ratio (`0.0`-`1.0`)
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
612
|
+
"""Blends the current color with another color using the specified ratio (`0.0`-`1.0`):
|
|
613
|
+
- if `ratio` is `0.0` it means 100% of the current color and 0% of the `other` color (2:0 mixture)
|
|
614
|
+
- if `ratio` is `0.5` it means 50% of both colors (1:1 mixture)
|
|
615
|
+
- if `ratio` is `1.0` it means 0% of the current color and 100% of the `other` color (0:2 mixture)"""
|
|
602
616
|
self.r, self.g, self.b, self.a = self.to_rgba(False).blend(Color.to_rgba(other), ratio, additive_alpha).values()
|
|
603
|
-
return hexa(
|
|
617
|
+
return hexa("", self.r, self.g, self.b, self.a)
|
|
604
618
|
|
|
605
619
|
def is_dark(self) -> bool:
|
|
606
620
|
"""Returns `True` if the color is considered dark (`lightness < 50%`)"""
|
|
@@ -608,7 +622,7 @@ class hexa:
|
|
|
608
622
|
|
|
609
623
|
def is_light(self) -> bool:
|
|
610
624
|
"""Returns `True` if the color is considered light (`lightness >= 50%`)"""
|
|
611
|
-
return self.
|
|
625
|
+
return not self.is_dark()
|
|
612
626
|
|
|
613
627
|
def is_grayscale(self) -> bool:
|
|
614
628
|
"""Returns `True` if the color is grayscale (`saturation == 0`)"""
|
|
@@ -616,13 +630,13 @@ class hexa:
|
|
|
616
630
|
|
|
617
631
|
def is_opaque(self) -> bool:
|
|
618
632
|
"""Returns `True` if the color has no transparency (`alpha == 1.0`)"""
|
|
619
|
-
return self.
|
|
633
|
+
return self.a == 1 or self.a is None
|
|
620
634
|
|
|
621
635
|
def with_alpha(self, alpha: float) -> "hexa":
|
|
622
636
|
"""Returns a new color with the specified alpha value"""
|
|
623
637
|
if not (isinstance(alpha, (int, float)) and 0 <= alpha <= 1):
|
|
624
638
|
raise ValueError("'alpha' must be in [0.0, 1.0]")
|
|
625
|
-
return hexa(
|
|
639
|
+
return hexa("", self.r, self.g, self.b, alpha)
|
|
626
640
|
|
|
627
641
|
def complementary(self) -> "hexa":
|
|
628
642
|
"""Returns the complementary color (180 degrees on the color wheel)"""
|
|
@@ -723,10 +737,9 @@ class Color:
|
|
|
723
737
|
@staticmethod
|
|
724
738
|
def has_alpha(color: rgba | hsla | hexa) -> bool:
|
|
725
739
|
"""Check if the given color has an alpha channel.\n
|
|
726
|
-
|
|
727
|
-
Input a RGBA, HSLA or HEXA color as `color
|
|
728
|
-
Returns `True` if the color has an alpha channel and `False` otherwise.
|
|
729
|
-
"""
|
|
740
|
+
---------------------------------------------------------------------------
|
|
741
|
+
Input a RGBA, HSLA or HEXA color as `color`.
|
|
742
|
+
Returns `True` if the color has an alpha channel and `False` otherwise."""
|
|
730
743
|
if isinstance(color, (rgba, hsla, hexa)):
|
|
731
744
|
return color.has_alpha()
|
|
732
745
|
if Color.is_valid_hexa(color):
|
|
@@ -749,15 +762,11 @@ class Color:
|
|
|
749
762
|
if isinstance(color, (hsla, hexa)):
|
|
750
763
|
return color.to_rgba()
|
|
751
764
|
elif Color.is_valid_hsla(color):
|
|
752
|
-
return hsla(*color
|
|
765
|
+
return hsla(*color, _validate=False).to_rgba()
|
|
753
766
|
elif Color.is_valid_hexa(color):
|
|
754
767
|
return hexa(color).to_rgba()
|
|
755
768
|
elif Color.is_valid_rgba(color):
|
|
756
|
-
return (
|
|
757
|
-
color
|
|
758
|
-
if isinstance(color, rgba)
|
|
759
|
-
else (rgba(*color) if Color.has_alpha(color) else rgba(color[0], color[1], color[2]))
|
|
760
|
-
)
|
|
769
|
+
return color if isinstance(color, rgba) else (rgba(*color, _validate=False))
|
|
761
770
|
raise ValueError(f"Invalid color format '{color}'")
|
|
762
771
|
|
|
763
772
|
@staticmethod
|
|
@@ -766,15 +775,11 @@ class Color:
|
|
|
766
775
|
if isinstance(color, (rgba, hexa)):
|
|
767
776
|
return color.to_hsla()
|
|
768
777
|
elif Color.is_valid_rgba(color):
|
|
769
|
-
return rgba(*color
|
|
778
|
+
return rgba(*color, _validate=False).to_hsla()
|
|
770
779
|
elif Color.is_valid_hexa(color):
|
|
771
780
|
return hexa(color).to_hsla()
|
|
772
781
|
elif Color.is_valid_hsla(color):
|
|
773
|
-
return (
|
|
774
|
-
color
|
|
775
|
-
if isinstance(color, hsla)
|
|
776
|
-
else (hsla(*color) if Color.has_alpha(color) else hsla(color[0], color[1], color[2]))
|
|
777
|
-
)
|
|
782
|
+
return color if isinstance(color, hsla) else (hsla(*color, _validate=False))
|
|
778
783
|
raise ValueError(f"Invalid color format '{color}'")
|
|
779
784
|
|
|
780
785
|
@staticmethod
|
|
@@ -783,18 +788,18 @@ class Color:
|
|
|
783
788
|
if isinstance(color, (rgba, hsla)):
|
|
784
789
|
return color.to_hexa()
|
|
785
790
|
elif Color.is_valid_rgba(color):
|
|
786
|
-
return rgba(*color
|
|
791
|
+
return rgba(*color, _validate=False).to_hexa()
|
|
787
792
|
elif Color.is_valid_hsla(color):
|
|
788
|
-
return hsla(*color
|
|
793
|
+
return hsla(*color, _validate=False).to_hexa()
|
|
789
794
|
elif Color.is_valid_hexa(color):
|
|
790
|
-
return color if isinstance(color, hexa) else hexa(
|
|
795
|
+
return color if isinstance(color, hexa) else hexa(color)
|
|
791
796
|
raise ValueError(f"Invalid color format '{color}'")
|
|
792
797
|
|
|
793
798
|
@staticmethod
|
|
794
799
|
def str_to_rgba(string: str, only_first: bool = False) -> rgba | list[rgba] | None:
|
|
795
|
-
"""Will try to recognize RGBA colors inside a string and output the found ones as RGBA objects
|
|
796
|
-
|
|
797
|
-
"""
|
|
800
|
+
"""Will try to recognize RGBA colors inside a string and output the found ones as RGBA objects.\n
|
|
801
|
+
--------------------------------------------------------------------------------------------------
|
|
802
|
+
If `only_first` is `True` only the first found color will be returned (not as a list)."""
|
|
798
803
|
if only_first:
|
|
799
804
|
match = _re.search(Regex.rgb_str(allow_alpha=True), string)
|
|
800
805
|
if not match:
|
|
@@ -805,6 +810,7 @@ class Color:
|
|
|
805
810
|
int(m[1]),
|
|
806
811
|
int(m[2]),
|
|
807
812
|
((int(m[3]) if "." not in m[3] else float(m[3])) if m[3] else None),
|
|
813
|
+
_validate=False,
|
|
808
814
|
)
|
|
809
815
|
else:
|
|
810
816
|
matches = _re.findall(Regex.rgb_str(allow_alpha=True), string)
|
|
@@ -816,6 +822,7 @@ class Color:
|
|
|
816
822
|
int(m[1]),
|
|
817
823
|
int(m[2]),
|
|
818
824
|
((int(m[3]) if "." not in m[3] else float(m[3])) if m[3] else None),
|
|
825
|
+
_validate=False,
|
|
819
826
|
)
|
|
820
827
|
for m in matches
|
|
821
828
|
]
|
|
@@ -829,51 +836,55 @@ class Color:
|
|
|
829
836
|
preserve_original: bool = False,
|
|
830
837
|
) -> int:
|
|
831
838
|
"""Convert RGBA channels to a HEXA integer (alpha is optional).\n
|
|
832
|
-
|
|
833
|
-
To preserve leading zeros, the function will add a `1` at the beginning, if the HEX integer
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
839
|
+
--------------------------------------------------------------------------------------------
|
|
840
|
+
To preserve leading zeros, the function will add a `1` at the beginning, if the HEX integer
|
|
841
|
+
would start with a `0`.
|
|
842
|
+
This could affect the color a little bit, but will make sure, that it won't be interpreted
|
|
843
|
+
as a completely different color, when initializing it as a `hexa()` color or changing it
|
|
844
|
+
back to RGBA using `Color.hex_int_to_rgba()`.\n
|
|
845
|
+
⇾ You can disable this behavior by setting `preserve_original` to `True`"""
|
|
837
846
|
r = max(0, min(255, int(r)))
|
|
838
847
|
g = max(0, min(255, int(g)))
|
|
839
848
|
b = max(0, min(255, int(b)))
|
|
840
|
-
if a is
|
|
849
|
+
if a is None:
|
|
850
|
+
hex_int = (r << 16) | (g << 8) | b
|
|
851
|
+
if not preserve_original and (hex_int & 0xF00000) == 0:
|
|
852
|
+
hex_int |= 0x010000
|
|
853
|
+
else:
|
|
841
854
|
a = max(0, min(255, int(a * 255)))
|
|
842
855
|
hex_int = (r << 24) | (g << 16) | (b << 8) | a
|
|
843
856
|
if not preserve_original and r == 0:
|
|
844
857
|
hex_int |= 0x01000000
|
|
845
|
-
else:
|
|
846
|
-
hex_int = (r << 16) | (g << 8) | b
|
|
847
|
-
if not preserve_original and (hex_int & 0xF00000) == 0:
|
|
848
|
-
hex_int |= 0x010000
|
|
849
858
|
return hex_int
|
|
850
859
|
|
|
851
860
|
@staticmethod
|
|
852
|
-
def hex_int_to_rgba(hex_int: int, preserve_original: bool = False) ->
|
|
861
|
+
def hex_int_to_rgba(hex_int: int, preserve_original: bool = False) -> rgba:
|
|
853
862
|
"""Convert a HEX integer to RGBA channels.\n
|
|
854
|
-
|
|
855
|
-
If the red channel is `1` after conversion, it will be set to `0`, because when converting
|
|
856
|
-
from RGBA to a HEX integer, the first `0` will be set to `1` to preserve leading zeros
|
|
863
|
+
-------------------------------------------------------------------------------------------
|
|
864
|
+
If the red channel is `1` after conversion, it will be set to `0`, because when converting
|
|
865
|
+
from RGBA to a HEX integer, the first `0` will be set to `1` to preserve leading zeros.
|
|
857
866
|
This is the correction, so the color doesn't even look slightly different.\n
|
|
858
|
-
⇾
|
|
867
|
+
⇾ You can disable this behavior by setting `preserve_original` to `True`"""
|
|
859
868
|
if not isinstance(hex_int, int):
|
|
860
869
|
raise ValueError("Input must be an integer")
|
|
861
870
|
hex_str = f"{hex_int:x}"
|
|
862
871
|
if len(hex_str) <= 6:
|
|
863
872
|
hex_str = hex_str.zfill(6)
|
|
864
|
-
return (
|
|
873
|
+
return rgba(
|
|
865
874
|
r if (r := int(hex_str[0:2], 16)) != 1 or preserve_original else 0,
|
|
866
875
|
int(hex_str[2:4], 16),
|
|
867
876
|
int(hex_str[4:6], 16),
|
|
868
877
|
None,
|
|
878
|
+
_validate=False,
|
|
869
879
|
)
|
|
870
880
|
elif len(hex_str) <= 8:
|
|
871
881
|
hex_str = hex_str.zfill(8)
|
|
872
|
-
return (
|
|
882
|
+
return rgba(
|
|
873
883
|
r if (r := int(hex_str[0:2], 16)) != 1 or preserve_original else 0,
|
|
874
884
|
int(hex_str[2:4], 16),
|
|
875
885
|
int(hex_str[4:6], 16),
|
|
876
886
|
int(hex_str[6:8], 16) / 255.0,
|
|
887
|
+
_validate=False,
|
|
877
888
|
)
|
|
878
889
|
else:
|
|
879
890
|
raise ValueError(f"Invalid HEX integer '0x{hex_str}': expected in range [0x000000, 0xFFFFFF]")
|
|
@@ -882,10 +893,10 @@ class Color:
|
|
|
882
893
|
def luminance(r: int, g: int, b: int, output_type: type = None) -> int | float:
|
|
883
894
|
"""Gets the colors luminance using the luminance formula.\n
|
|
884
895
|
------------------------------------------------------------
|
|
885
|
-
The param `output_type` can be set to
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
`None`
|
|
896
|
+
The param `output_type` can be set to:
|
|
897
|
+
- `int` =⠀integer in [0, 100]
|
|
898
|
+
- `float` =⠀float in [0.0, 1.0]
|
|
899
|
+
- `None` =⠀integer in [0, 255]"""
|
|
889
900
|
r, g, b = r / 255.0, g / 255.0, b / 255.0
|
|
890
901
|
if r < 0.03928:
|
|
891
902
|
r = r / 12.92
|
|
@@ -904,25 +915,25 @@ class Color:
|
|
|
904
915
|
|
|
905
916
|
@staticmethod
|
|
906
917
|
def text_color_for_on_bg(
|
|
907
|
-
text_bg_color: rgba | hexa =
|
|
918
|
+
text_bg_color: rgba | hexa = "#FFF",
|
|
908
919
|
) -> rgba | hexa:
|
|
909
920
|
was_hexa, was_int = Color.is_valid_hexa(text_bg_color), isinstance(text_bg_color, int)
|
|
910
921
|
text_bg_color = Color.to_rgba(text_bg_color)
|
|
911
922
|
brightness = 0.2126 * text_bg_color[0] + 0.7152 * text_bg_color[1] + 0.0722 * text_bg_color[2]
|
|
912
923
|
return (
|
|
913
|
-
(hexa("
|
|
924
|
+
(hexa("", 255, 255, 255) if was_hexa else rgba(255, 255, 255, _validate=False))
|
|
914
925
|
if brightness < 128
|
|
915
|
-
else ((0x000 if was_int else hexa("
|
|
926
|
+
else ((0x000 if was_int else hexa("", 0, 0, 0)) if was_hexa else rgba(0, 0, 0, _validate=False))
|
|
916
927
|
)
|
|
917
928
|
|
|
918
929
|
@staticmethod
|
|
919
930
|
def adjust_lightness(color: rgba | hexa, lightness_change: float) -> rgba | hexa:
|
|
920
931
|
"""In- or decrease the lightness of the input color.\n
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
932
|
+
-----------------------------------------------------------------------------------------------------
|
|
933
|
+
- color (rgba|hexa): HEX or RGBA color
|
|
934
|
+
- lightness_change (float): float between -1.0 (darken by `100%`) and 1.0 (lighten by `100%`)\n
|
|
935
|
+
-----------------------------------------------------------------------------------------------------
|
|
936
|
+
returns (rgba|hexa): the adjusted color in the format of the input color"""
|
|
926
937
|
was_hexa = Color.is_valid_hexa(color)
|
|
927
938
|
color = Color.to_hsla(color)
|
|
928
939
|
h, s, l, a = (
|
|
@@ -932,16 +943,16 @@ class Color:
|
|
|
932
943
|
color[3] if Color.has_alpha(color) else None,
|
|
933
944
|
)
|
|
934
945
|
l = int(max(0, min(100, l + lightness_change * 100)))
|
|
935
|
-
return
|
|
946
|
+
return hsla(h, s, l, a, _validate=False).to_hexa() if was_hexa else hsla(h, s, l, a, _validate=False).to_rgba()
|
|
936
947
|
|
|
937
948
|
@staticmethod
|
|
938
949
|
def adjust_saturation(color: rgba | hexa, saturation_change: float) -> rgba | hexa:
|
|
939
950
|
"""In- or decrease the saturation of the input color.\n
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
951
|
+
-----------------------------------------------------------------------------------------------------------
|
|
952
|
+
- color (rgba|hexa): HEX or RGBA color
|
|
953
|
+
- saturation_change (float): float between -1.0 (saturate by `100%`) and 1.0 (desaturate by `100%`)\n
|
|
954
|
+
-----------------------------------------------------------------------------------------------------------
|
|
955
|
+
returns (rgba|hexa): the adjusted color in the format of the input color"""
|
|
945
956
|
was_hexa = Color.is_valid_hexa(color)
|
|
946
957
|
color = Color.to_hsla(color)
|
|
947
958
|
h, s, l, a = (
|
|
@@ -951,4 +962,4 @@ class Color:
|
|
|
951
962
|
color[3] if Color.has_alpha(color) else None,
|
|
952
963
|
)
|
|
953
964
|
s = int(max(0, min(100, s + saturation_change * 100)))
|
|
954
|
-
return
|
|
965
|
+
return hsla(h, s, l, a, _validate=False).to_hexa() if was_hexa else hsla(h, s, l, a, _validate=False).to_rgba()
|