pyimagecuda 0.0.5__cp310-cp310-win_amd64.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.
- pyimagecuda/__init__.py +64 -0
- pyimagecuda/adjust.py +101 -0
- pyimagecuda/blend.py +273 -0
- pyimagecuda/effect.py +164 -0
- pyimagecuda/fill.py +263 -0
- pyimagecuda/filter.py +168 -0
- pyimagecuda/image.py +95 -0
- pyimagecuda/io.py +158 -0
- pyimagecuda/pyimagecuda_internal.cp310-win_amd64.pyd +0 -0
- pyimagecuda/resize.py +97 -0
- pyimagecuda/transform.py +186 -0
- pyimagecuda/utils.py +17 -0
- pyimagecuda-0.0.5.dist-info/METADATA +99 -0
- pyimagecuda-0.0.5.dist-info/RECORD +16 -0
- pyimagecuda-0.0.5.dist-info/WHEEL +5 -0
- pyimagecuda-0.0.5.dist-info/licenses/LICENSE +21 -0
pyimagecuda/__init__.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import ctypes
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
__version__ = "0.0.5"
|
|
5
|
+
|
|
6
|
+
def _check_nvidia_driver():
|
|
7
|
+
try:
|
|
8
|
+
ctypes.windll.LoadLibrary("nvcuda.dll")
|
|
9
|
+
return True
|
|
10
|
+
except OSError:
|
|
11
|
+
return False
|
|
12
|
+
|
|
13
|
+
_INTERNAL_LOADED = False
|
|
14
|
+
|
|
15
|
+
try:
|
|
16
|
+
from . import pyimagecuda_internal
|
|
17
|
+
_INTERNAL_LOADED = True
|
|
18
|
+
except ImportError as e:
|
|
19
|
+
_INTERNAL_LOADED = False
|
|
20
|
+
error_msg = str(e).lower()
|
|
21
|
+
|
|
22
|
+
if "dll load failed" in error_msg:
|
|
23
|
+
print("\n" + "!" * 75)
|
|
24
|
+
print(" [CRITICAL ERROR] Failed to load pyimagecuda backend.")
|
|
25
|
+
|
|
26
|
+
if not _check_nvidia_driver():
|
|
27
|
+
print(" CAUSE: NVIDIA Drivers are missing or not detected.")
|
|
28
|
+
print(" SOLUTION: Install latest drivers from: https://www.nvidia.com/Download/index.aspx")
|
|
29
|
+
else:
|
|
30
|
+
print(" CAUSE: Microsoft Visual C++ Redistributable is missing.")
|
|
31
|
+
print(" SOLUTION: Install it from: https://aka.ms/vs/17/release/vc_redist.x64.exe")
|
|
32
|
+
|
|
33
|
+
print("!" * 75 + "\n")
|
|
34
|
+
else:
|
|
35
|
+
print(f"Error loading internal module: {e}")
|
|
36
|
+
|
|
37
|
+
if _INTERNAL_LOADED:
|
|
38
|
+
try:
|
|
39
|
+
from .image import Image, ImageU8
|
|
40
|
+
from .io import upload, download, copy, save, load, convert_float_to_u8, convert_u8_to_float
|
|
41
|
+
from .fill import Fill
|
|
42
|
+
from .resize import Resize
|
|
43
|
+
from .blend import Blend
|
|
44
|
+
from .filter import Filter
|
|
45
|
+
from .effect import Effect
|
|
46
|
+
from .adjust import Adjust
|
|
47
|
+
from .transform import Transform
|
|
48
|
+
except ImportError as e:
|
|
49
|
+
print(f"Warning: Error importing Python wrappers: {e}")
|
|
50
|
+
|
|
51
|
+
def check_system():
|
|
52
|
+
print("--- PYIMAGECUDA DIAGNOSTIC ---")
|
|
53
|
+
|
|
54
|
+
if not _INTERNAL_LOADED:
|
|
55
|
+
print("❌ Backend C++ NOT loaded. See errors above.")
|
|
56
|
+
return False
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
pyimagecuda_internal.cuda_sync()
|
|
60
|
+
print("✅ SYSTEM OK. GPU Ready & Libraries Loaded.")
|
|
61
|
+
return True
|
|
62
|
+
except Exception as e:
|
|
63
|
+
print(f"❌ Backend loaded but GPU runtime failed: {e}")
|
|
64
|
+
return False
|
pyimagecuda/adjust.py
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
from .image import Image
|
|
2
|
+
from .pyimagecuda_internal import ( #type: ignore
|
|
3
|
+
adjust_brightness_f32,
|
|
4
|
+
adjust_contrast_f32,
|
|
5
|
+
adjust_saturation_f32,
|
|
6
|
+
adjust_gamma_f32,
|
|
7
|
+
adjust_opacity_f32
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Adjust:
|
|
12
|
+
|
|
13
|
+
@staticmethod
|
|
14
|
+
def brightness(image: Image, factor: float) -> None:
|
|
15
|
+
"""
|
|
16
|
+
Adjusts image brightness by adding a factor (in-place).
|
|
17
|
+
|
|
18
|
+
Positive factor brightens, negative darkens.
|
|
19
|
+
|
|
20
|
+
Docs & Examples: https://offerrall.github.io/pyimagecuda/adjust/#brightness
|
|
21
|
+
"""
|
|
22
|
+
adjust_brightness_f32(
|
|
23
|
+
image._buffer._handle,
|
|
24
|
+
image.width,
|
|
25
|
+
image.height,
|
|
26
|
+
float(factor)
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
@staticmethod
|
|
30
|
+
def contrast(image: Image, factor: float) -> None:
|
|
31
|
+
"""
|
|
32
|
+
Adjusts image contrast relative to middle gray (in-place).
|
|
33
|
+
|
|
34
|
+
factor > 1.0 increases contrast.
|
|
35
|
+
factor < 1.0 decreases contrast.
|
|
36
|
+
|
|
37
|
+
Docs & Examples: https://offerrall.github.io/pyimagecuda/adjust/#contrast
|
|
38
|
+
"""
|
|
39
|
+
adjust_contrast_f32(
|
|
40
|
+
image._buffer._handle,
|
|
41
|
+
image.width,
|
|
42
|
+
image.height,
|
|
43
|
+
float(factor)
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
@staticmethod
|
|
47
|
+
def saturation(image: Image, factor: float) -> None:
|
|
48
|
+
"""
|
|
49
|
+
Adjusts color intensity (in-place).
|
|
50
|
+
|
|
51
|
+
factor 0.0 = Grayscale
|
|
52
|
+
factor 1.0 = Original
|
|
53
|
+
factor > 1.0 = More vibrant
|
|
54
|
+
|
|
55
|
+
Docs & Examples: https://offerrall.github.io/pyimagecuda/adjust/#saturation
|
|
56
|
+
"""
|
|
57
|
+
adjust_saturation_f32(
|
|
58
|
+
image._buffer._handle,
|
|
59
|
+
image.width,
|
|
60
|
+
image.height,
|
|
61
|
+
float(factor)
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
@staticmethod
|
|
65
|
+
def gamma(image: Image, gamma: float) -> None:
|
|
66
|
+
"""
|
|
67
|
+
Adjusts gamma correction (non-linear brightness) (in-place).
|
|
68
|
+
|
|
69
|
+
Useful for brightening shadows without washing out highlights.
|
|
70
|
+
|
|
71
|
+
gamma > 1.0: Brightens midtones.
|
|
72
|
+
gamma < 1.0: Darkens midtones.
|
|
73
|
+
|
|
74
|
+
Docs & Examples: https://offerrall.github.io/pyimagecuda/adjust/#gamma
|
|
75
|
+
"""
|
|
76
|
+
if gamma <= 0:
|
|
77
|
+
raise ValueError("Gamma must be positive")
|
|
78
|
+
|
|
79
|
+
adjust_gamma_f32(
|
|
80
|
+
image._buffer._handle,
|
|
81
|
+
image.width,
|
|
82
|
+
image.height,
|
|
83
|
+
float(gamma)
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
@staticmethod
|
|
87
|
+
def opacity(image: Image, factor: float) -> None:
|
|
88
|
+
"""
|
|
89
|
+
Multiplies the alpha channel by a factor (In-Place).
|
|
90
|
+
|
|
91
|
+
- factor: 0.0 (Fully Transparent) to 1.0 (No change).
|
|
92
|
+
|
|
93
|
+
Docs & Examples: https://offerrall.github.io/pyimagecuda/adjust/#opacity
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
adjust_opacity_f32(
|
|
97
|
+
image._buffer._handle,
|
|
98
|
+
image.width,
|
|
99
|
+
image.height,
|
|
100
|
+
float(factor)
|
|
101
|
+
)
|
pyimagecuda/blend.py
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
from .image import Image
|
|
3
|
+
from .pyimagecuda_internal import blend_f32, blend_mask_f32 # type: ignore
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _calculate_position(
|
|
7
|
+
base_width: int,
|
|
8
|
+
base_height: int,
|
|
9
|
+
overlay_width: int,
|
|
10
|
+
overlay_height: int,
|
|
11
|
+
anchor: str,
|
|
12
|
+
offset_x: int,
|
|
13
|
+
offset_y: int
|
|
14
|
+
) -> tuple[int, int]:
|
|
15
|
+
|
|
16
|
+
pos_x = offset_x
|
|
17
|
+
pos_y = offset_y
|
|
18
|
+
|
|
19
|
+
if 'center' in anchor and anchor in ['top-center', 'center', 'bottom-center']:
|
|
20
|
+
pos_x += (base_width - overlay_width) // 2
|
|
21
|
+
elif 'right' in anchor:
|
|
22
|
+
pos_x += base_width - overlay_width
|
|
23
|
+
|
|
24
|
+
if 'center' in anchor and anchor in ['center-left', 'center', 'center-right']:
|
|
25
|
+
pos_y += (base_height - overlay_height) // 2
|
|
26
|
+
elif 'bottom' in anchor:
|
|
27
|
+
pos_y += base_height - overlay_height
|
|
28
|
+
|
|
29
|
+
return pos_x, pos_y
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Blend:
|
|
33
|
+
|
|
34
|
+
@staticmethod
|
|
35
|
+
def normal(
|
|
36
|
+
base: Image,
|
|
37
|
+
overlay: Image,
|
|
38
|
+
anchor: Literal['top-left', 'top-center', 'top-right', 'center-left', 'center', 'center-right', 'bottom-left', 'bottom-center', 'bottom-right'] = 'top-left',
|
|
39
|
+
offset_x: int = 0,
|
|
40
|
+
offset_y: int = 0,
|
|
41
|
+
opacity: float = 1.0
|
|
42
|
+
) -> None:
|
|
43
|
+
"""
|
|
44
|
+
Standard alpha blending (in-place).
|
|
45
|
+
|
|
46
|
+
Docs & Examples: https://offerrall.github.io/pyimagecuda/blend/#normal
|
|
47
|
+
"""
|
|
48
|
+
pos_x, pos_y = _calculate_position(
|
|
49
|
+
base.width, base.height,
|
|
50
|
+
overlay.width, overlay.height,
|
|
51
|
+
anchor, offset_x, offset_y
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
blend_f32(
|
|
55
|
+
base._buffer._handle,
|
|
56
|
+
overlay._buffer._handle,
|
|
57
|
+
base.width, base.height,
|
|
58
|
+
overlay.width, overlay.height,
|
|
59
|
+
pos_x, pos_y,
|
|
60
|
+
0,
|
|
61
|
+
opacity
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
@staticmethod
|
|
65
|
+
def multiply(
|
|
66
|
+
base: Image,
|
|
67
|
+
overlay: Image,
|
|
68
|
+
anchor: Literal['top-left', 'top-center', 'top-right', 'center-left', 'center', 'center-right', 'bottom-left', 'bottom-center', 'bottom-right'] = 'top-left',
|
|
69
|
+
offset_x: int = 0,
|
|
70
|
+
offset_y: int = 0,
|
|
71
|
+
opacity: float = 1.0
|
|
72
|
+
) -> None:
|
|
73
|
+
"""
|
|
74
|
+
Multiplies color values, darkening the image (in-place).
|
|
75
|
+
|
|
76
|
+
Docs & Examples: https://offerrall.github.io/pyimagecuda/blend/#multiply
|
|
77
|
+
"""
|
|
78
|
+
pos_x, pos_y = _calculate_position(
|
|
79
|
+
base.width, base.height,
|
|
80
|
+
overlay.width, overlay.height,
|
|
81
|
+
anchor, offset_x, offset_y
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
blend_f32(
|
|
85
|
+
base._buffer._handle,
|
|
86
|
+
overlay._buffer._handle,
|
|
87
|
+
base.width, base.height,
|
|
88
|
+
overlay.width, overlay.height,
|
|
89
|
+
pos_x, pos_y,
|
|
90
|
+
1,
|
|
91
|
+
opacity
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
@staticmethod
|
|
95
|
+
def screen(
|
|
96
|
+
base: Image,
|
|
97
|
+
overlay: Image,
|
|
98
|
+
anchor: Literal['top-left', 'top-center', 'top-right', 'center-left', 'center', 'center-right', 'bottom-left', 'bottom-center', 'bottom-right'] = 'top-left',
|
|
99
|
+
offset_x: int = 0,
|
|
100
|
+
offset_y: int = 0,
|
|
101
|
+
opacity: float = 1.0
|
|
102
|
+
) -> None:
|
|
103
|
+
"""
|
|
104
|
+
Inverted multiply, lightening the image (in-place).
|
|
105
|
+
|
|
106
|
+
Docs & Examples: https://offerrall.github.io/pyimagecuda/blend/#screen
|
|
107
|
+
"""
|
|
108
|
+
pos_x, pos_y = _calculate_position(
|
|
109
|
+
base.width, base.height,
|
|
110
|
+
overlay.width, overlay.height,
|
|
111
|
+
anchor, offset_x, offset_y
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
blend_f32(
|
|
115
|
+
base._buffer._handle,
|
|
116
|
+
overlay._buffer._handle,
|
|
117
|
+
base.width, base.height,
|
|
118
|
+
overlay.width, overlay.height,
|
|
119
|
+
pos_x, pos_y,
|
|
120
|
+
2,
|
|
121
|
+
opacity
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
@staticmethod
|
|
125
|
+
def add(
|
|
126
|
+
base: Image,
|
|
127
|
+
overlay: Image,
|
|
128
|
+
anchor: Literal['top-left', 'top-center', 'top-right', 'center-left', 'center', 'center-right', 'bottom-left', 'bottom-center', 'bottom-right'] = 'top-left',
|
|
129
|
+
offset_x: int = 0,
|
|
130
|
+
offset_y: int = 0,
|
|
131
|
+
opacity: float = 1.0
|
|
132
|
+
) -> None:
|
|
133
|
+
"""
|
|
134
|
+
Additive blending, useful for light effects (in-place).
|
|
135
|
+
|
|
136
|
+
Docs & Examples: https://offerrall.github.io/pyimagecuda/blend/#add
|
|
137
|
+
"""
|
|
138
|
+
pos_x, pos_y = _calculate_position(
|
|
139
|
+
base.width, base.height,
|
|
140
|
+
overlay.width, overlay.height,
|
|
141
|
+
anchor, offset_x, offset_y
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
blend_f32(
|
|
145
|
+
base._buffer._handle,
|
|
146
|
+
overlay._buffer._handle,
|
|
147
|
+
base.width, base.height,
|
|
148
|
+
overlay.width, overlay.height,
|
|
149
|
+
pos_x, pos_y,
|
|
150
|
+
3,
|
|
151
|
+
opacity
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
@staticmethod
|
|
155
|
+
def overlay(
|
|
156
|
+
base: Image,
|
|
157
|
+
overlay: Image,
|
|
158
|
+
anchor: Literal['top-left', 'top-center', 'top-right', 'center-left', 'center', 'center-right', 'bottom-left', 'bottom-center', 'bottom-right'] = 'top-left',
|
|
159
|
+
offset_x: int = 0,
|
|
160
|
+
offset_y: int = 0,
|
|
161
|
+
opacity: float = 1.0
|
|
162
|
+
) -> None:
|
|
163
|
+
"""
|
|
164
|
+
Combines Multiply and Screen. Increases contrast (in-place).
|
|
165
|
+
|
|
166
|
+
Docs & Examples: https://offerrall.github.io/pyimagecuda/blend/#overlay
|
|
167
|
+
"""
|
|
168
|
+
pos_x, pos_y = _calculate_position(
|
|
169
|
+
base.width, base.height,
|
|
170
|
+
overlay.width, overlay.height,
|
|
171
|
+
anchor, offset_x, offset_y
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
blend_f32(
|
|
175
|
+
base._buffer._handle,
|
|
176
|
+
overlay._buffer._handle,
|
|
177
|
+
base.width, base.height,
|
|
178
|
+
overlay.width, overlay.height,
|
|
179
|
+
pos_x, pos_y,
|
|
180
|
+
4,
|
|
181
|
+
opacity
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
@staticmethod
|
|
185
|
+
def soft_light(
|
|
186
|
+
base: Image,
|
|
187
|
+
overlay: Image,
|
|
188
|
+
anchor: Literal['top-left', 'top-center', 'top-right', 'center-left', 'center', 'center-right', 'bottom-left', 'bottom-center', 'bottom-right'] = 'top-left',
|
|
189
|
+
offset_x: int = 0,
|
|
190
|
+
offset_y: int = 0,
|
|
191
|
+
opacity: float = 1.0
|
|
192
|
+
) -> None:
|
|
193
|
+
"""
|
|
194
|
+
Gentle lighting effect, like a diffuse spotlight (in-place).
|
|
195
|
+
|
|
196
|
+
Docs & Examples: https://offerrall.github.io/pyimagecuda/blend/#soft-light
|
|
197
|
+
"""
|
|
198
|
+
pos_x, pos_y = _calculate_position(
|
|
199
|
+
base.width, base.height,
|
|
200
|
+
overlay.width, overlay.height,
|
|
201
|
+
anchor, offset_x, offset_y
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
blend_f32(
|
|
205
|
+
base._buffer._handle,
|
|
206
|
+
overlay._buffer._handle,
|
|
207
|
+
base.width, base.height,
|
|
208
|
+
overlay.width, overlay.height,
|
|
209
|
+
pos_x, pos_y,
|
|
210
|
+
5,
|
|
211
|
+
opacity
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
@staticmethod
|
|
215
|
+
def hard_light(
|
|
216
|
+
base: Image,
|
|
217
|
+
overlay: Image,
|
|
218
|
+
anchor: Literal['top-left', 'top-center', 'top-right', 'center-left', 'center', 'center-right', 'bottom-left', 'bottom-center', 'bottom-right'] = 'top-left',
|
|
219
|
+
offset_x: int = 0,
|
|
220
|
+
offset_y: int = 0,
|
|
221
|
+
opacity: float = 1.0
|
|
222
|
+
) -> None:
|
|
223
|
+
"""
|
|
224
|
+
Strong lighting effect, like a harsh spotlight (in-place).
|
|
225
|
+
|
|
226
|
+
Docs & Examples: https://offerrall.github.io/pyimagecuda/blend/#hard-light
|
|
227
|
+
"""
|
|
228
|
+
pos_x, pos_y = _calculate_position(
|
|
229
|
+
base.width, base.height,
|
|
230
|
+
overlay.width, overlay.height,
|
|
231
|
+
anchor, offset_x, offset_y
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
blend_f32(
|
|
235
|
+
base._buffer._handle,
|
|
236
|
+
overlay._buffer._handle,
|
|
237
|
+
base.width, base.height,
|
|
238
|
+
overlay.width, overlay.height,
|
|
239
|
+
pos_x, pos_y,
|
|
240
|
+
6,
|
|
241
|
+
opacity
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
@staticmethod
|
|
245
|
+
def mask(
|
|
246
|
+
base: Image,
|
|
247
|
+
mask: Image,
|
|
248
|
+
anchor: Literal['top-left', 'top-center', 'top-right', 'center-left', 'center', 'center-right', 'bottom-left', 'bottom-center', 'bottom-right'] = 'top-left',
|
|
249
|
+
offset_x: int = 0,
|
|
250
|
+
offset_y: int = 0,
|
|
251
|
+
mode: Literal['alpha', 'luminance'] = 'luminance'
|
|
252
|
+
) -> None:
|
|
253
|
+
"""
|
|
254
|
+
Applies an image as an alpha mask to the base image (in-place).
|
|
255
|
+
|
|
256
|
+
Docs & Examples: https://offerrall.github.io/pyimagecuda/blend/#mask
|
|
257
|
+
"""
|
|
258
|
+
pos_x, pos_y = _calculate_position(
|
|
259
|
+
base.width, base.height,
|
|
260
|
+
mask.width, mask.height,
|
|
261
|
+
anchor, offset_x, offset_y
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
mode_int = 1 if mode == 'luminance' else 0
|
|
265
|
+
|
|
266
|
+
blend_mask_f32(
|
|
267
|
+
base._buffer._handle,
|
|
268
|
+
mask._buffer._handle,
|
|
269
|
+
base.width, base.height,
|
|
270
|
+
mask.width, mask.height,
|
|
271
|
+
pos_x, pos_y,
|
|
272
|
+
mode_int
|
|
273
|
+
)
|
pyimagecuda/effect.py
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
from .image import Image
|
|
4
|
+
from .filter import Filter
|
|
5
|
+
from .blend import Blend
|
|
6
|
+
from .fill import Fill
|
|
7
|
+
from .utils import ensure_capacity
|
|
8
|
+
from .pyimagecuda_internal import ( #type: ignore
|
|
9
|
+
rounded_corners_f32,
|
|
10
|
+
extract_alpha_f32,
|
|
11
|
+
colorize_alpha_mask_f32,
|
|
12
|
+
compute_distance_field_f32,
|
|
13
|
+
generate_stroke_composite_f32,
|
|
14
|
+
effect_vignette_f32
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Effect:
|
|
19
|
+
|
|
20
|
+
@staticmethod
|
|
21
|
+
def rounded_corners(image: Image, radius: float) -> None:
|
|
22
|
+
"""
|
|
23
|
+
Applies rounded corners to the image (in-place).
|
|
24
|
+
|
|
25
|
+
Docs & Examples: https://offerrall.github.io/pyimagecuda/effect/#rounded_corners
|
|
26
|
+
"""
|
|
27
|
+
max_radius = min(image.width, image.height) / 2.0
|
|
28
|
+
if radius < 0: raise ValueError("Radius must be non-negative")
|
|
29
|
+
if radius > max_radius: radius = max_radius
|
|
30
|
+
|
|
31
|
+
rounded_corners_f32(image._buffer._handle, image.width, image.height, float(radius))
|
|
32
|
+
|
|
33
|
+
@staticmethod
|
|
34
|
+
def drop_shadow(
|
|
35
|
+
image: Image, offset_x: int = 10,
|
|
36
|
+
offset_y: int = 10,
|
|
37
|
+
blur: int = 20,
|
|
38
|
+
color: tuple[float, float, float, float] = (0.0, 0.0, 0.0, 0.5),
|
|
39
|
+
expand: bool = True,
|
|
40
|
+
dst_buffer: Image | None = None,
|
|
41
|
+
shadow_buffer: Image | None = None,
|
|
42
|
+
temp_buffer: Image | None = None
|
|
43
|
+
) -> Image | None:
|
|
44
|
+
"""
|
|
45
|
+
Adds a drop shadow to the image.
|
|
46
|
+
|
|
47
|
+
Docs & Examples: https://offerrall.github.io/pyimagecuda/effect/#drop_shadow
|
|
48
|
+
"""
|
|
49
|
+
if expand:
|
|
50
|
+
pad_l = blur + max(0, -offset_x)
|
|
51
|
+
pad_r = blur + max(0, offset_x)
|
|
52
|
+
pad_t = blur + max(0, -offset_y)
|
|
53
|
+
pad_b = blur + max(0, offset_y)
|
|
54
|
+
res_w, res_h = image.width + pad_l + pad_r, image.height + pad_t + pad_b
|
|
55
|
+
img_x, img_y = pad_l, pad_t
|
|
56
|
+
else:
|
|
57
|
+
res_w, res_h = image.width, image.height
|
|
58
|
+
img_x, img_y = 0, 0
|
|
59
|
+
|
|
60
|
+
if dst_buffer is None:
|
|
61
|
+
result = Image(res_w, res_h); ret = True
|
|
62
|
+
else:
|
|
63
|
+
ensure_capacity(dst_buffer, res_w, res_h); result = dst_buffer; ret = False
|
|
64
|
+
|
|
65
|
+
if shadow_buffer is None:
|
|
66
|
+
shadow = Image(res_w, res_h); own_shadow = True
|
|
67
|
+
else:
|
|
68
|
+
ensure_capacity(shadow_buffer, res_w, res_h); shadow = shadow_buffer; own_shadow = False
|
|
69
|
+
|
|
70
|
+
if expand:
|
|
71
|
+
if temp_buffer is None: temp = Image(res_w, res_h)
|
|
72
|
+
else: ensure_capacity(temp_buffer, res_w, res_h); temp = temp_buffer
|
|
73
|
+
Fill.color(shadow, (0,0,0,0))
|
|
74
|
+
temp.width, temp.height = image.width, image.height
|
|
75
|
+
extract_alpha_f32(image._buffer._handle, temp._buffer._handle, image.width, image.height)
|
|
76
|
+
Blend.normal(shadow, temp, anchor='top-left', offset_x=pad_l, offset_y=pad_t)
|
|
77
|
+
if temp_buffer is None: temp.free()
|
|
78
|
+
else:
|
|
79
|
+
extract_alpha_f32(image._buffer._handle, shadow._buffer._handle, image.width, image.height)
|
|
80
|
+
|
|
81
|
+
if blur > 0:
|
|
82
|
+
Filter.gaussian_blur(shadow, radius=blur, sigma=blur/3.0, dst_buffer=shadow)
|
|
83
|
+
|
|
84
|
+
colorize_alpha_mask_f32(shadow._buffer._handle, shadow.width, shadow.height, color)
|
|
85
|
+
Fill.color(result, (0,0,0,0))
|
|
86
|
+
Blend.normal(result, shadow, anchor='top-left', offset_x=offset_x, offset_y=offset_y)
|
|
87
|
+
Blend.normal(result, image, anchor='top-left', offset_x=img_x, offset_y=img_y)
|
|
88
|
+
|
|
89
|
+
if own_shadow: shadow.free()
|
|
90
|
+
return result if ret else None
|
|
91
|
+
|
|
92
|
+
@staticmethod
|
|
93
|
+
def stroke(
|
|
94
|
+
image: Image,
|
|
95
|
+
width: int = 2,
|
|
96
|
+
color: tuple[float, float, float, float] = (0.0, 0.0, 0.0, 1.0),
|
|
97
|
+
position: Literal['outside', 'inside'] = 'outside',
|
|
98
|
+
expand: bool = True,
|
|
99
|
+
dst_buffer: Image | None = None,
|
|
100
|
+
distance_buffer: Image | None = None,
|
|
101
|
+
) -> Image | None:
|
|
102
|
+
"""
|
|
103
|
+
Adds a stroke (outline) to the image using distance field method.
|
|
104
|
+
|
|
105
|
+
Docs & Examples: https://offerrall.github.io/pyimagecuda/effect/#stroke
|
|
106
|
+
"""
|
|
107
|
+
if width < 1 or width > 1000: raise ValueError("Invalid stroke width")
|
|
108
|
+
if position not in ('outside', 'inside'): raise ValueError("Invalid position")
|
|
109
|
+
|
|
110
|
+
if position == 'outside' and expand:
|
|
111
|
+
res_w, res_h = image.width + width * 2, image.height + width * 2
|
|
112
|
+
off_x, off_y = width, width
|
|
113
|
+
else:
|
|
114
|
+
res_w, res_h = image.width, image.height
|
|
115
|
+
off_x, off_y = 0, 0
|
|
116
|
+
|
|
117
|
+
if dst_buffer is None:
|
|
118
|
+
result = Image(res_w, res_h); ret = True
|
|
119
|
+
else:
|
|
120
|
+
ensure_capacity(dst_buffer, res_w, res_h); result = dst_buffer; ret = False
|
|
121
|
+
|
|
122
|
+
if distance_buffer is None:
|
|
123
|
+
distance = Image(res_w, res_h); own_dist = True
|
|
124
|
+
else:
|
|
125
|
+
ensure_capacity(distance_buffer, res_w, res_h); distance = distance_buffer; own_dist = False
|
|
126
|
+
|
|
127
|
+
compute_distance_field_f32(
|
|
128
|
+
image._buffer._handle, distance._buffer._handle,
|
|
129
|
+
image.width, image.height, res_w, res_h, off_x, off_y
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
generate_stroke_composite_f32(
|
|
133
|
+
image._buffer._handle, distance._buffer._handle, result._buffer._handle,
|
|
134
|
+
image.width, image.height, res_w, res_h, off_x, off_y,
|
|
135
|
+
float(width), color, 0 if position == 'outside' else 1
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
if own_dist: distance.free()
|
|
139
|
+
|
|
140
|
+
return result if ret else None
|
|
141
|
+
|
|
142
|
+
@staticmethod
|
|
143
|
+
def vignette(
|
|
144
|
+
image: Image,
|
|
145
|
+
radius: float = 0.9,
|
|
146
|
+
softness: float = 1.0,
|
|
147
|
+
color: tuple[float, float, float, float] = (0.0, 0.0, 0.0, 1.0)
|
|
148
|
+
) -> None:
|
|
149
|
+
"""
|
|
150
|
+
Applies a vignette effect (darkening edges) (in-place).
|
|
151
|
+
|
|
152
|
+
Docs & Examples: https://offerrall.github.io/pyimagecuda/effect/#vignette
|
|
153
|
+
"""
|
|
154
|
+
if radius < 0: radius = 0.0
|
|
155
|
+
if softness < 0: softness = 0.0
|
|
156
|
+
|
|
157
|
+
effect_vignette_f32(
|
|
158
|
+
image._buffer._handle,
|
|
159
|
+
image.width,
|
|
160
|
+
image.height,
|
|
161
|
+
float(radius),
|
|
162
|
+
float(softness),
|
|
163
|
+
color
|
|
164
|
+
)
|