colopresso 12.2.0__cp310-abi3-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.
colopresso/core.py ADDED
@@ -0,0 +1,266 @@
1
+ # SPDX-License-Identifier: GPL-3.0-or-later
2
+ #
3
+ # This file is part of colopresso
4
+ #
5
+ # Copyright (C) 2025-2026 COLOPL, Inc.
6
+ #
7
+ # Author: Go Kudo <g-kudo@colopl.co.jp>
8
+ # Developed with AI (LLM) code assistance. See `NOTICE` for details.
9
+
10
+ """
11
+ Core Python bindings for colopresso (stable ABI wrapper)
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ from dataclasses import dataclass, field, asdict
17
+ from enum import IntEnum
18
+ from typing import List, Optional, Tuple
19
+
20
+ from . import _colopresso
21
+
22
+
23
+ class PngxLossyType(IntEnum):
24
+ """PNGX lossy compression type"""
25
+ PALETTE256 = _colopresso.PNGX_LOSSY_TYPE_PALETTE256
26
+ LIMITED_RGBA4444 = _colopresso.PNGX_LOSSY_TYPE_LIMITED_RGBA4444
27
+ REDUCED_RGBA32 = _colopresso.PNGX_LOSSY_TYPE_REDUCED_RGBA32
28
+
29
+
30
+ class ColopressoError(Exception):
31
+ """Exception raised for colopresso errors"""
32
+
33
+ ERROR_CODES = {
34
+ 0: "OK",
35
+ 1: "File not found",
36
+ 2: "Invalid PNG",
37
+ 3: "Invalid format",
38
+ 4: "Out of memory",
39
+ 5: "Encode failed",
40
+ 6: "Decode failed",
41
+ 7: "IO error",
42
+ 8: "Invalid parameter",
43
+ 9: "Output not smaller",
44
+ }
45
+
46
+ def __init__(self, code: int, message: Optional[str] = None):
47
+ self.code = code
48
+ self.message = message or self.ERROR_CODES.get(code, f"Unknown error ({code})")
49
+ super().__init__(self.message)
50
+
51
+
52
+ @dataclass
53
+ class Config:
54
+ """Configuration for colopresso encoders"""
55
+
56
+ # WebP
57
+ webp_quality: float = 80.0
58
+ webp_lossless: bool = False
59
+ webp_method: int = 6
60
+ webp_target_size: int = 0
61
+ webp_target_psnr: float = 0.0
62
+ webp_segments: int = 4
63
+ webp_sns_strength: int = 50
64
+ webp_filter_strength: int = 60
65
+ webp_filter_sharpness: int = 0
66
+ webp_filter_type: int = 1
67
+ webp_autofilter: bool = True
68
+ webp_alpha_compression: bool = True
69
+ webp_alpha_filtering: int = 1
70
+ webp_alpha_quality: int = 100
71
+ webp_pass: int = 1
72
+ webp_preprocessing: int = 0
73
+ webp_partitions: int = 0
74
+ webp_partition_limit: int = 0
75
+ webp_emulate_jpeg_size: bool = False
76
+ webp_thread_level: int = 0
77
+ webp_low_memory: bool = False
78
+ webp_near_lossless: int = 100
79
+ webp_exact: bool = False
80
+ webp_use_delta_palette: bool = False
81
+ webp_use_sharp_yuv: bool = False
82
+
83
+ # AVIF
84
+ avif_quality: float = 50.0
85
+ avif_alpha_quality: int = 100
86
+ avif_lossless: bool = False
87
+ avif_speed: int = 6
88
+ avif_threads: int = 1
89
+
90
+ # PNGX (PNG)
91
+ pngx_level: int = 5
92
+ pngx_strip_safe: bool = True
93
+ pngx_optimize_alpha: bool = True
94
+ pngx_lossy_enable: bool = True
95
+ pngx_lossy_type: int = 0 # PngxLossyType.PALETTE256
96
+ pngx_lossy_max_colors: int = 256
97
+ pngx_lossy_reduced_colors: int = -1
98
+ pngx_lossy_reduced_bits_rgb: int = 4
99
+ pngx_lossy_reduced_alpha_bits: int = 4
100
+ pngx_lossy_quality_min: int = 80
101
+ pngx_lossy_quality_max: int = 95
102
+ pngx_lossy_speed: int = 3
103
+ pngx_lossy_dither_level: float = 0.6
104
+ pngx_saliency_map_enable: bool = True
105
+ pngx_chroma_anchor_enable: bool = True
106
+ pngx_adaptive_dither_enable: bool = True
107
+ pngx_gradient_boost_enable: bool = True
108
+ pngx_chroma_weight_enable: bool = True
109
+ pngx_postprocess_smooth_enable: bool = True
110
+ pngx_postprocess_smooth_importance_cutoff: float = 0.6
111
+ pngx_palette256_gradient_profile_enable: bool = True
112
+ pngx_palette256_gradient_dither_floor: float = 0.78
113
+ pngx_palette256_alpha_bleed_enable: bool = False
114
+ pngx_palette256_alpha_bleed_max_distance: int = 64
115
+ pngx_palette256_alpha_bleed_opaque_threshold: int = 248
116
+ pngx_palette256_alpha_bleed_soft_limit: int = 160
117
+ pngx_palette256_profile_opaque_ratio_threshold: float = 0.90
118
+ pngx_palette256_profile_gradient_mean_max: float = 0.16
119
+ pngx_palette256_profile_saturation_mean_max: float = 0.42
120
+ pngx_palette256_tune_opaque_ratio_threshold: float = 0.90
121
+ pngx_palette256_tune_gradient_mean_max: float = 0.14
122
+ pngx_palette256_tune_saturation_mean_max: float = 0.35
123
+ pngx_palette256_tune_speed_max: int = 1
124
+ pngx_palette256_tune_quality_min_floor: int = 90
125
+ pngx_palette256_tune_quality_max_target: int = 100
126
+ pngx_threads: int = 1
127
+ pngx_protected_colors: Optional[List[Tuple[int, int, int, int]]] = None
128
+
129
+ def _to_dict(self) -> dict:
130
+ """Convert to dictionary for C extension"""
131
+ d = asdict(self)
132
+ if isinstance(d.get("pngx_lossy_type"), PngxLossyType):
133
+ d["pngx_lossy_type"] = int(d["pngx_lossy_type"])
134
+ return d
135
+
136
+
137
+ def _wrap_error(func):
138
+ """Wrap C extension errors into ColopressoError"""
139
+ def wrapper(*args, **kwargs):
140
+ try:
141
+ return func(*args, **kwargs)
142
+ except _colopresso.ColopressoError as e:
143
+ code = e.args[0] if e.args else -1
144
+ message = e.args[1] if len(e.args) > 1 else None
145
+ raise ColopressoError(code, message) from None
146
+ return wrapper
147
+
148
+
149
+ @_wrap_error
150
+ def encode_webp(png_data: bytes, config: Optional[Config] = None) -> bytes:
151
+ """
152
+ Encode PNG data to WebP format.
153
+
154
+ Args:
155
+ png_data: Raw PNG file data
156
+ config: Optional configuration (uses defaults if not provided)
157
+
158
+ Returns:
159
+ WebP encoded data
160
+
161
+ Raises:
162
+ ColopressoError: If encoding fails
163
+ """
164
+ config_dict = config._to_dict() if config else None
165
+ return _colopresso.encode_webp(png_data, config_dict)
166
+
167
+
168
+ @_wrap_error
169
+ def encode_avif(png_data: bytes, config: Optional[Config] = None) -> bytes:
170
+ """
171
+ Encode PNG data to AVIF format.
172
+
173
+ Args:
174
+ png_data: Raw PNG file data
175
+ config: Optional configuration (uses defaults if not provided)
176
+
177
+ Returns:
178
+ AVIF encoded data
179
+
180
+ Raises:
181
+ ColopressoError: If encoding fails
182
+ """
183
+ config_dict = config._to_dict() if config else None
184
+ return _colopresso.encode_avif(png_data, config_dict)
185
+
186
+
187
+ @_wrap_error
188
+ def encode_pngx(png_data: bytes, config: Optional[Config] = None) -> bytes:
189
+ """
190
+ Optimize PNG data using PNGX encoder.
191
+
192
+ Args:
193
+ png_data: Raw PNG file data
194
+ config: Optional configuration (uses defaults if not provided)
195
+ For PALETTE256 mode, you can specify protected colors using
196
+ config.pngx_protected_colors as a list of (r, g, b, a) tuples.
197
+ These colors will always be included in the palette.
198
+
199
+ Returns:
200
+ Optimized PNG data
201
+
202
+ Raises:
203
+ ColopressoError: If encoding fails
204
+ """
205
+ config_dict = config._to_dict() if config else None
206
+ return _colopresso.encode_pngx(png_data, config_dict)
207
+
208
+
209
+ def get_version() -> int:
210
+ """Get colopresso version number"""
211
+ return _colopresso.get_version()
212
+
213
+
214
+ def get_libwebp_version() -> int:
215
+ """Get libwebp version number"""
216
+ return _colopresso.get_libwebp_version()
217
+
218
+
219
+ def get_libpng_version() -> int:
220
+ """Get libpng version number"""
221
+ return _colopresso.get_libpng_version()
222
+
223
+
224
+ def get_libavif_version() -> int:
225
+ """Get libavif version number"""
226
+ return _colopresso.get_libavif_version()
227
+
228
+
229
+ def get_pngx_oxipng_version() -> int:
230
+ """Get oxipng version number"""
231
+ return _colopresso.get_pngx_oxipng_version()
232
+
233
+
234
+ def get_pngx_libimagequant_version() -> int:
235
+ """Get libimagequant version number"""
236
+ return _colopresso.get_pngx_libimagequant_version()
237
+
238
+
239
+ def get_buildtime() -> int:
240
+ """Get build timestamp"""
241
+ return _colopresso.get_buildtime()
242
+
243
+
244
+ def get_compiler_version_string() -> str:
245
+ """Get compiler version string"""
246
+ return _colopresso.get_compiler_version_string()
247
+
248
+
249
+ def get_rust_version_string() -> str:
250
+ """Get Rust version string"""
251
+ return _colopresso.get_rust_version_string()
252
+
253
+
254
+ def is_threads_enabled() -> bool:
255
+ """Check if threading is enabled"""
256
+ return _colopresso.is_threads_enabled()
257
+
258
+
259
+ def get_default_thread_count() -> int:
260
+ """Get default thread count"""
261
+ return _colopresso.get_default_thread_count()
262
+
263
+
264
+ def get_max_thread_count() -> int:
265
+ """Get maximum thread count"""
266
+ return _colopresso.get_max_thread_count()
colopresso/py.typed ADDED
@@ -0,0 +1,65 @@
1
+ # SPDX-License-Identifier: GPL-3.0-or-later
2
+ #
3
+ # This file is part of colopresso
4
+ #
5
+ # Copyright (C) 2025-2026 COLOPL, Inc.
6
+ #
7
+ # Author: Go Kudo <g-kudo@colopl.co.jp>
8
+ # Developed with AI (LLM) code assistance. See `NOTICE` for details.
9
+
10
+ """
11
+ Type stubs for colopresso
12
+ """
13
+
14
+ from dataclasses import dataclass
15
+ from enum import IntEnum
16
+ from typing import Optional
17
+
18
+ class PngxLossyType(IntEnum):
19
+ PALETTE256: int
20
+ LIMITED_RGBA4444: int
21
+ REDUCED_RGBA32: int
22
+
23
+ class ColopressoError(Exception):
24
+ code: int
25
+ message: str
26
+ def __init__(self, code: int, message: Optional[str] = None) -> None: ...
27
+
28
+ @dataclass
29
+ class Config:
30
+ webp_quality: float
31
+ webp_lossless: bool
32
+ webp_method: int
33
+ avif_quality: float
34
+ avif_alpha_quality: int
35
+ avif_lossless: bool
36
+ avif_speed: int
37
+ avif_threads: int
38
+ pngx_level: int
39
+ pngx_strip_safe: bool
40
+ pngx_optimize_alpha: bool
41
+ pngx_lossy_enable: bool
42
+ pngx_lossy_type: PngxLossyType
43
+ pngx_lossy_max_colors: int
44
+ pngx_lossy_quality_min: int
45
+ pngx_lossy_quality_max: int
46
+ pngx_lossy_speed: int
47
+ pngx_lossy_dither_level: float
48
+ pngx_threads: int
49
+
50
+ def encode_webp(png_data: bytes, config: Optional[Config] = None) -> bytes: ...
51
+ def encode_avif(png_data: bytes, config: Optional[Config] = None) -> bytes: ...
52
+ def encode_pngx(png_data: bytes, config: Optional[Config] = None) -> bytes: ...
53
+
54
+ def get_version() -> int: ...
55
+ def get_libwebp_version() -> int: ...
56
+ def get_libpng_version() -> int: ...
57
+ def get_libavif_version() -> int: ...
58
+ def get_pngx_oxipng_version() -> int: ...
59
+ def get_pngx_libimagequant_version() -> int: ...
60
+ def get_buildtime() -> int: ...
61
+ def get_compiler_version_string() -> str: ...
62
+ def get_rust_version_string() -> str: ...
63
+ def is_threads_enabled() -> bool: ...
64
+ def get_default_thread_count() -> int: ...
65
+ def get_max_thread_count() -> int: ...