Skip to content

Commit 0da9f0c

Browse files
committed
Release v0.1.1
Use numpy instead of standard library `math` module in `css_filter`
1 parent 642bbc9 commit 0da9f0c

File tree

2 files changed

+110
-88
lines changed

2 files changed

+110
-88
lines changed

pkg/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ namespaces = true
1515

1616
# ----------------------------------------- Project Metadata -------------------------------------
1717
[project]
18-
version = "0.1.0"
18+
version = "0.1.1"
1919
name = "PyColorIT"
2020
requires-python = ">=3.10"
2121
dependencies = [

pkg/src/pycolorit/css_filter.py

Lines changed: 109 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from typing import TYPE_CHECKING as _TYPE_CHECKING
1515
import math
1616
import random
17+
import numpy as np
1718

1819
if _TYPE_CHECKING:
1920
from pycolorit.color import RGBColor
@@ -27,78 +28,109 @@ def generate(target: RGBColor) -> tuple[list[float], float, str]:
2728
class Color:
2829
def __init__(self, r, g, b):
2930
self.set(r, g, b)
31+
return
3032

3133
def __str__(self):
3234
return f"rgb({round(self.r)}, {round(self.g)}, {round(self.b)})"
3335

3436
def set(self, r, g, b):
35-
self.r = self.clamp(r)
36-
self.g = self.clamp(g)
37-
self.b = self.clamp(b)
37+
self.rgb = np.clip(np.array([r, g, b], dtype=np.float64), 0, 255)
38+
self.r, self.g, self.b = self.rgb
39+
return
3840

3941
def hue_rotate(self, angle=0):
40-
angle = angle / 180 * math.pi
41-
sin = math.sin(angle)
42-
cos = math.cos(angle)
43-
44-
self.multiply(
42+
angle = np.radians(angle)
43+
sin, cos = np.sin(angle), np.cos(angle)
44+
matrix = np.array(
4545
[
46-
0.213 + cos * 0.787 - sin * 0.213,
47-
0.715 - cos * 0.715 - sin * 0.715,
48-
0.072 - cos * 0.072 + sin * 0.928,
49-
0.213 - cos * 0.213 + sin * 0.143,
50-
0.715 + cos * 0.285 + sin * 0.140,
51-
0.072 - cos * 0.072 - sin * 0.283,
52-
0.213 - cos * 0.213 - sin * 0.787,
53-
0.715 - cos * 0.715 + sin * 0.715,
54-
0.072 + cos * 0.928 + sin * 0.072,
46+
[
47+
0.213 + cos * 0.787 - sin * 0.213,
48+
0.715 - cos * 0.715 - sin * 0.715,
49+
0.072 - cos * 0.072 + sin * 0.928,
50+
],
51+
[
52+
0.213 - cos * 0.213 + sin * 0.143,
53+
0.715 + cos * 0.285 + sin * 0.140,
54+
0.072 - cos * 0.072 - sin * 0.283,
55+
],
56+
[
57+
0.213 - cos * 0.213 - sin * 0.787,
58+
0.715 - cos * 0.715 + sin * 0.715,
59+
0.072 + cos * 0.928 + sin * 0.072,
60+
],
5561
]
5662
)
63+
self.multiply(matrix)
64+
return
5765

5866
def grayscale(self, x=1):
59-
self.multiply(
67+
matrix = np.array(
6068
[
61-
0.2126 + 0.7874 * (1 - x),
62-
0.7152 - 0.7152 * (1 - x),
63-
0.0722 - 0.0722 * (1 - x),
64-
0.2126 - 0.2126 * (1 - x),
65-
0.7152 + 0.2848 * (1 - x),
66-
0.0722 - 0.0722 * (1 - x),
67-
0.2126 - 0.2126 * (1 - x),
68-
0.7152 - 0.7152 * (1 - x),
69-
0.0722 + 0.9278 * (1 - x),
70-
]
69+
[
70+
0.2126 + 0.7874 * (1 - x),
71+
0.7152 - 0.7152 * (1 - x),
72+
0.0722 - 0.0722 * (1 - x),
73+
],
74+
[
75+
0.2126 - 0.2126 * (1 - x),
76+
0.7152 + 0.2848 * (1 - x),
77+
0.0722 - 0.0722 * (1 - x),
78+
],
79+
[
80+
0.2126 - 0.2126 * (1 - x),
81+
0.7152 - 0.7152 * (1 - x),
82+
0.0722 + 0.9278 * (1 - x),
83+
],
84+
],
7185
)
86+
self.multiply(matrix)
87+
return
7288

7389
def sepia(self, x=1):
74-
self.multiply(
90+
matrix = np.array(
7591
[
76-
0.393 + 0.607 * (1 - x),
77-
0.769 - 0.769 * (1 - x),
78-
0.189 - 0.189 * (1 - x),
79-
0.349 - 0.349 * (1 - x),
80-
0.686 + 0.314 * (1 - x),
81-
0.168 - 0.168 * (1 - x),
82-
0.272 - 0.272 * (1 - x),
83-
0.534 - 0.534 * (1 - x),
84-
0.131 + 0.869 * (1 - x),
85-
]
92+
[
93+
0.393 + 0.607 * (1 - x),
94+
0.769 - 0.769 * (1 - x),
95+
0.189 - 0.189 * (1 - x),
96+
],
97+
[
98+
0.349 - 0.349 * (1 - x),
99+
0.686 + 0.314 * (1 - x),
100+
0.168 - 0.168 * (1 - x),
101+
],
102+
[
103+
0.272 - 0.272 * (1 - x),
104+
0.534 - 0.534 * (1 - x),
105+
0.131 + 0.869 * (1 - x),
106+
],
107+
],
86108
)
109+
self.multiply(matrix)
110+
return
87111

88112
def saturate(self, x=1):
89-
self.multiply(
113+
matrix = np.array(
90114
[
91-
0.213 + 0.787 * x,
92-
0.715 - 0.715 * x,
93-
0.072 - 0.072 * x,
94-
0.213 - 0.213 * x,
95-
0.715 + 0.285 * x,
96-
0.072 - 0.072 * x,
97-
0.213 - 0.213 * x,
98-
0.715 - 0.715 * x,
99-
0.072 + 0.928 * x,
100-
]
115+
[
116+
0.213 + 0.787 * x,
117+
0.715 - 0.715 * x,
118+
0.072 - 0.072 * x,
119+
],
120+
[
121+
0.213 - 0.213 * x,
122+
0.715 + 0.285 * x,
123+
0.072 - 0.072 * x,
124+
],
125+
[
126+
0.213 - 0.213 * x,
127+
0.715 - 0.715 * x,
128+
0.072 + 0.928 * x,
129+
],
130+
],
101131
)
132+
self.multiply(matrix)
133+
return
102134

103135
def brightness(self, x=1):
104136
"""
@@ -109,12 +141,9 @@ def brightness(self, x=1):
109141
x : int >= 0
110142
Linear multiplier, with 0 creating a completely black color, 1 (i.e. 100%) having no effect,
111143
and values over 1 brightening the color.
112-
113-
Returns
114-
-------
115-
116144
"""
117-
self.multiply([x, 0, 0, 0, x, 0, 0, 0, x])
145+
self.multiply(np.diag([x, x, x]))
146+
return
118147

119148
def contrast(self, x=1):
120149
"""
@@ -124,45 +153,38 @@ def contrast(self, x=1):
124153
----------
125154
x : int >= 0
126155
A value of 0 makes the color grey, 1 (i.e. 100%) has no effect, and values over 1 create a contrast.
127-
128-
Returns
129-
-------
130-
131156
"""
132-
intercept = tuple(127.5 * (1 - x) for i in range(3))
133-
self.multiply([x, 0, 0, 0, x, 0, 0, 0, x], intercept)
134-
135-
def clamp(self, value):
136-
return min(255, max(0, value))
157+
intercept = np.array([127.5 * (1 - x)] * 3)
158+
self.multiply(np.diag([x, x, x]), intercept)
159+
return
137160

138-
def multiply(self, matrix, vector=(0, 0, 0)):
139-
r = self.r * matrix[0] + self.g * matrix[1] + self.b * matrix[2] + vector[0]
140-
g = self.r * matrix[3] + self.g * matrix[4] + self.b * matrix[5] + vector[1]
141-
b = self.r * matrix[6] + self.g * matrix[7] + self.b * matrix[8] + vector[2]
142-
self.set(r, g, b)
161+
def multiply(self, matrix, vector=None):
162+
result = np.dot(matrix, self.rgb)
163+
if vector is not None:
164+
result += vector
165+
self.set(*result)
166+
return
143167

144168
def invert(self, x=1):
145-
r = (x + self.r / 255 * (1 - 2 * x)) * 255
146-
g = (x + self.g / 255 * (1 - 2 * x)) * 255
147-
b = (x + self.b / 255 * (1 - 2 * x)) * 255
148-
self.set(r, g, b)
169+
self.rgb = (x + self.rgb / 255 * (1 - 2 * x)) * 255
170+
self.set(*self.rgb)
149171
return
150172

151173
def rgb_to_hsl(self):
152-
r, g, b = self.r / 255, self.g / 255, self.b / 255
153-
max_value = max(r, g, b)
154-
min_value = min(r, g, b)
155-
h, s, l = 0, 0, (max_value + min_value) / 2
156-
if max_value != min_value:
157-
d = max_value - min_value
158-
s = d / (2 - max_value - min_value) if l > 0.5 else d / (max_value + min_value)
159-
if max_value == r:
160-
h = (g - b) / d + (6 if g < b else 0)
161-
elif max_value == g:
162-
h = (b - r) / d + 2
163-
else:
164-
h = (r - g) / d + 4
165-
h /= 6
174+
rgb = self.rgb / 255
175+
max_val, min_val = np.max(rgb), np.min(rgb)
176+
l = (max_val + min_val) / 2
177+
if max_val == min_val:
178+
h = s = 0
179+
else:
180+
d = max_val - min_val
181+
s = d / (2 - max_val - min_val) if l > 0.5 else d / (max_val + min_val)
182+
idx = np.argmax(rgb)
183+
h = [
184+
(rgb[1] - rgb[2]) / d + (6 if rgb[1] < rgb[2] else 0),
185+
(rgb[2] - rgb[0]) / d + 2,
186+
(rgb[0] - rgb[1]) / d + 4
187+
][idx] / 6
166188
return h * 100, s * 100, l * 100
167189

168190

0 commit comments

Comments
 (0)