imagequant 1.1.6__cp315-cp315-win32.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.
imagequant/__init__.py ADDED
@@ -0,0 +1,207 @@
1
+ # The library can be used without PIL/Pillow
2
+ try:
3
+ from PIL import Image
4
+ except ImportError:
5
+ Image = None
6
+
7
+ from ._libimagequant import lib, ffi
8
+
9
+
10
+ def _get_error_msg(code):
11
+ """Get error string from libimagequant return codes.
12
+
13
+ :param int code: The code returned by libimagequant.
14
+
15
+ :rtype: str
16
+ """
17
+ if code == lib.LIQ_QUALITY_TOO_LOW:
18
+ return "Quality too low"
19
+ elif code == lib.LIQ_VALUE_OUT_OF_RANGE:
20
+ return "Value out of range"
21
+ elif code == lib.LIQ_OUT_OF_MEMORY:
22
+ return "Out of memory"
23
+ elif code == lib.LIQ_ABORTED:
24
+ return "Aborted"
25
+ elif code == lib.LIQ_BITMAP_NOT_AVAILABLE:
26
+ return "Bitmap not available"
27
+ elif code == lib.LIQ_BUFFER_TOO_SMALL:
28
+ return "Buffer too small"
29
+ elif code == lib.LIQ_INVALID_POINTER:
30
+ return "Invalid pointer"
31
+ elif code == lib.LIQ_UNSUPPORTED:
32
+ return "Unsupported"
33
+ else:
34
+ return "Unknown error"
35
+
36
+
37
+ def _pil_image_to_raw_bytes(image):
38
+ """Get raw bytes from a PIL/Pillow image.
39
+
40
+ :param PIL.Image.Image image: The image.
41
+
42
+ :rtype: bytes
43
+ :return: The image as raw bytes (``b"RGBARGBARGBA..."``).
44
+ """
45
+ return bytes([comp for pix in image.getdata() for comp in pix])
46
+
47
+
48
+ def _liq_palette_to_raw_palette(liq_palette):
49
+ """Converts a liq_palette to a raw RGBA palette usable with PIL/Pillow image.
50
+
51
+ :param liq_palette: The palette de convert.
52
+
53
+ :rtype: list
54
+ :return: The raw RGBA palette (``[r0, g0, b0, a0, r1, g1, b1, a1,...]``).
55
+ """
56
+ return sum(
57
+ [[color.r, color.g, color.b, color.a] for color in liq_palette.entries], []
58
+ )
59
+
60
+
61
+ def quantize_raw_rgba_bytes(
62
+ image_data,
63
+ width,
64
+ height,
65
+ dithering_level=1.0,
66
+ max_colors=256,
67
+ min_quality=0,
68
+ max_quality=100,
69
+ ):
70
+ """Converts an RGBA image to an optimized 8bit paletted image.
71
+
72
+ :param bytes image_data: Raw RGBA image data (format: ``b"RGBARGBARGBA..."``).
73
+ :param int width: Image's width.
74
+ :param int height: Image's height.
75
+ :param float dithering_level: The dithering level, from ``0.0`` (no
76
+ dithering) to ``1.0`` (default: ``1.0``).
77
+ :param int max_colors: Maximum number of color to use in the palette, from
78
+ ``1`` to ``256`` (default: ``256``).
79
+ :param int min_quality: Minimum acceptable quality. If the minimum quality
80
+ can’t be met, the quantization will be aborted and
81
+ a ``RuntimeError`` will be raised. From ``0`` to
82
+ ``100`` (default: ``0``).
83
+ :param int max_quality: The target quality, from ``0`` to ``100``
84
+ (default: ``100``).
85
+
86
+ :rtype: tuple(bytes, list)
87
+ :return: The processed image bytes (each byte represents a pixel and its
88
+ value is the index of a color from the palette), and the palette
89
+ (format: ``[r0, g0, b0, a0, r1, g1, b1, a1,...]``).
90
+
91
+ >>> import imagequant
92
+ >>> imagequant.quantize_raw_rgba_bytes(
93
+ ... b"\\xFF\\x00\\x00\\xFF"
94
+ ... b"\\x00\\xFF\\x00\\xFF"
95
+ ... b"\\x00\\x00\\xFF\\xFF"
96
+ ... b"\\xFF\\xFF\\xFF\\xFF",
97
+ ... 2, 2
98
+ ... )
99
+ (b'...', [...])
100
+ """
101
+ if len(image_data) != 4 * width * height:
102
+ raise ValueError("image_data length do not match the given width and height")
103
+
104
+ if not 0.0 <= dithering_level <= 1.0:
105
+ raise ValueError("dithering_level must be a float between 0.0 and 1.0")
106
+
107
+ if not 1 <= max_colors <= 256:
108
+ raise ValueError("max_colors must be an integer between 1 and 256")
109
+
110
+ if not 0 <= min_quality <= 100:
111
+ raise ValueError("min_quality must be an integer between 0 and 100")
112
+
113
+ if not 0 <= max_quality <= 100:
114
+ raise ValueError("max_quality must be an integer between 0 and 100")
115
+
116
+ if min_quality > max_quality:
117
+ raise ValueError("min_quality must be lower or equal to max_quality")
118
+
119
+ liq_attr = lib.liq_attr_create()
120
+ liq_attr.max_colors = max_colors
121
+ lib.liq_set_quality(liq_attr, min_quality, max_quality)
122
+
123
+ liq_image = lib.liq_image_create_rgba(liq_attr, image_data, width, height, 0)
124
+
125
+ liq_result_p = ffi.new("liq_result**")
126
+ code = lib.liq_image_quantize(liq_image, liq_attr, liq_result_p)
127
+ if code != lib.LIQ_OK:
128
+ lib.liq_result_destroy(liq_result_p[0])
129
+ lib.liq_image_destroy(liq_image)
130
+ lib.liq_attr_destroy(liq_attr)
131
+ raise RuntimeError(_get_error_msg(code))
132
+ lib.liq_set_dithering_level(liq_result_p[0], dithering_level)
133
+
134
+ raw_8bit_pixels = ffi.new("char[]", width * height)
135
+ lib.liq_write_remapped_image(
136
+ liq_result_p[0], liq_image, raw_8bit_pixels, width * height
137
+ )
138
+
139
+ pal = lib.liq_get_palette(liq_result_p[0])
140
+
141
+ output_image_data = ffi.unpack(raw_8bit_pixels, width * height)
142
+ output_palette = _liq_palette_to_raw_palette(pal)
143
+
144
+ lib.liq_result_destroy(liq_result_p[0])
145
+ lib.liq_image_destroy(liq_image)
146
+ lib.liq_attr_destroy(liq_attr)
147
+ ffi.release(raw_8bit_pixels)
148
+
149
+ return output_image_data, output_palette
150
+
151
+
152
+ def quantize_pil_image(
153
+ image,
154
+ dithering_level=1.0,
155
+ max_colors=256,
156
+ min_quality=0,
157
+ max_quality=100,
158
+ ):
159
+ """Converts an RGBA image to an optimized 8bit paletted image.
160
+
161
+ :param PIL.Image.Image image: The image to process.
162
+ :param float dithering_level: The dithering level, from ``0.0`` (no
163
+ dithering) to ``1.0`` (default: ``1.0``).
164
+ :param int max_colors: Maximum number of color to use in the palette, from
165
+ ``1`` to ``256`` (default: ``256``).
166
+ :param int min_quality: Minimum acceptable quality. If the minimum quality
167
+ can’t be met, the quantization will be aborted and
168
+ a ``RuntimeError`` will be raised. From ``0`` to
169
+ ``100`` (default: ``0``).
170
+ :param int max_quality: The target quality, from ``0`` to ``100``
171
+ (default: ``100``).
172
+
173
+ :rtype: PIL.Image.Image
174
+ :return: The processed image as a PIL/Pillow image.
175
+
176
+ >>> import imagequant
177
+ >>> input_image = Image.open("./example.png")
178
+ >>> imagequant.quantize_pil_image(input_image)
179
+ <PIL.Image.Image image mode=P size=... at ...>
180
+ """
181
+ if Image is None:
182
+ raise ImportError("PIL or Pillow is required to use this function.")
183
+
184
+ if image.mode != "RGBA":
185
+ image = image.convert(mode="RGBA")
186
+
187
+ input_image_data = _pil_image_to_raw_bytes(image)
188
+
189
+ output_image_data, output_palette = quantize_raw_rgba_bytes(
190
+ input_image_data,
191
+ image.width,
192
+ image.height,
193
+ dithering_level=dithering_level,
194
+ max_colors=max_colors,
195
+ min_quality=min_quality,
196
+ max_quality=max_quality,
197
+ )
198
+
199
+ output_image = Image.frombytes(
200
+ "P",
201
+ [image.width, image.height],
202
+ output_image_data,
203
+ decoder_name="raw",
204
+ )
205
+ output_image.putpalette(output_palette, rawmode="RGBA")
206
+
207
+ return output_image
Binary file
@@ -0,0 +1,36 @@
1
+ import os
2
+
3
+ from cffi import FFI
4
+ from distutils import ccompiler
5
+
6
+ _ROOT = os.path.abspath(os.path.dirname(__file__))
7
+ _LIBIMAGEQUANT_H = os.path.join(_ROOT, "libimagequant.h")
8
+ _LIBIMAGEQUANT_SRC = [
9
+ "blur.c",
10
+ "kmeans.c",
11
+ "mediancut.c",
12
+ "mempool.c",
13
+ "nearest.c",
14
+ "pam.c",
15
+ "libimagequant.c",
16
+ ]
17
+
18
+ CC_ARGS = []
19
+ if ccompiler.get_default_compiler() == "unix":
20
+ CC_ARGS = ["-Wno-incompatible-pointer-types"]
21
+
22
+
23
+ ffibuilder = FFI()
24
+ ffibuilder.set_source(
25
+ "imagequant._libimagequant",
26
+ "\n".join(['#include "%s"' % c for c in _LIBIMAGEQUANT_SRC]),
27
+ include_dirs=[
28
+ os.path.join(_ROOT, "..", "libimagequant"),
29
+ ],
30
+ extra_compile_args=CC_ARGS,
31
+ )
32
+ ffibuilder.cdef(open(_LIBIMAGEQUANT_H, "r").read())
33
+
34
+
35
+ if __name__ == "__main__":
36
+ ffibuilder.compile(verbose=True)
@@ -0,0 +1,249 @@
1
+ Metadata-Version: 2.4
2
+ Name: imagequant
3
+ Version: 1.1.6
4
+ Summary: Image Quantization Library
5
+ Home-page: https://github.com/wanadev/imagequant-python
6
+ Author: Wanadev
7
+ Author-email: contact@wanadev.fr
8
+ Maintainer: Fabien LOISON
9
+ License: BSD-3-Clause
10
+ Project-URL: Source Code, https://github.com/wanadev/imagequant-python
11
+ Project-URL: Documentation, https://github.com/wanadev/imagequant-python#usage
12
+ Project-URL: Changelog, https://github.com/wanadev/imagequant-python#changelog
13
+ Project-URL: Issues, https://github.com/wanadev/imagequant-python/issues
14
+ Project-URL: Chat, https://discord.gg/BmUkEdMuFp
15
+ Keywords: image libimagequant pngquant cffi
16
+ License-File: LICENSE
17
+ Requires-Dist: cffi>=1.0.0
18
+ Provides-Extra: dev
19
+ Requires-Dist: nox; extra == "dev"
20
+ Requires-Dist: flake8; extra == "dev"
21
+ Requires-Dist: black; extra == "dev"
22
+ Requires-Dist: pytest; extra == "dev"
23
+ Provides-Extra: pil
24
+ Requires-Dist: pillow; extra == "pil"
25
+ Dynamic: author
26
+ Dynamic: author-email
27
+ Dynamic: description
28
+ Dynamic: home-page
29
+ Dynamic: keywords
30
+ Dynamic: license
31
+ Dynamic: license-file
32
+ Dynamic: maintainer
33
+ Dynamic: project-url
34
+ Dynamic: provides-extra
35
+ Dynamic: requires-dist
36
+ Dynamic: summary
37
+
38
+ Imagequant Python - Python Bindings for libimagequant
39
+ =====================================================
40
+
41
+ |Github| |Discord| |PYPI Version| |Build Status| |Black| |License|
42
+
43
+ **Imagequant Python** are bindings to allow using libimagequant_ from Python.
44
+
45
+ **Libimagequant** is a small, portable C library for high-quality conversion of RGBA images to 8-bit indexed-color (palette) images.
46
+
47
+ .. _libimagequant: https://github.com/ImageOptim/libimagequant
48
+
49
+
50
+ Install
51
+ -------
52
+
53
+ From PyPI::
54
+
55
+ pip3 install imagequant
56
+
57
+ NOTE: you may require compilation tools to build the library if you system is not suitable for the precompiled wheels. On Debian / Ubuntu you can install the build dependencies with the following command::
58
+
59
+ sudo apt install build-essential python3-dev
60
+
61
+
62
+ Usage
63
+ -----
64
+
65
+ With PIL / Pillow
66
+ ~~~~~~~~~~~~~~~~~
67
+
68
+ .. code-block:: python
69
+
70
+ from PIL import Image
71
+ import imagequant
72
+
73
+ input_image = Image.open("./example.png")
74
+ output_image = imagequant.quantize_pil_image(
75
+ input_image,
76
+ dithering_level=1.0, # from 0.0 to 1.0
77
+ max_colors=256, # from 1 to 256
78
+ min_quality=0, # from 0 to 100
79
+ max_quality=100, # from 0 to 100
80
+ )
81
+ output_image.save("./out.png", format="PNG")
82
+
83
+ |input_image| → |output_image|
84
+
85
+ .. |input_image| image:: ./example.png
86
+ .. |output_image| image:: ./example_out.png
87
+
88
+
89
+ With Raw Data
90
+ ~~~~~~~~~~~~~
91
+
92
+ .. code-block:: python
93
+
94
+ import imagequant
95
+
96
+ # 2×2px image
97
+ IMAGE_DATA = (
98
+ # | R | G | B | A |
99
+ b"\xFF\x00\x00\xFF" # red
100
+ b"\x00\xFF\x00\xFF" # lime
101
+ b"\x00\x00\xFF\xFF" # blue
102
+ b"\xFF\xFF\xFF\xFF" # white
103
+ )
104
+
105
+ output_image_data, output_palette = imagequant.quantize_raw_rgba_bytes(
106
+ IMAGE_DATA, # RGBA image data
107
+ 2, 2, # width, height
108
+ dithering_level=1.0, # from 0.0 to 1.0
109
+ max_colors=256, # from 1 to 256
110
+ min_quality=0, # from 0 to 100
111
+ max_quality=100, # from 0 to 100
112
+ )
113
+
114
+ # you can now encode image data and the palette in any image format...
115
+
116
+ Example ``output_image_data``:
117
+
118
+ .. code-block:: python
119
+
120
+ b'\x02\x03\x00\x01'
121
+
122
+ Example ``output_palette``:
123
+
124
+ .. code-block:: python
125
+
126
+ [0, 0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 0, 255, 0, 255, 0, 0, 0, 0, ...]
127
+ # color 0 | color 1 | color 2 | color 3 | color 4 | ...
128
+
129
+
130
+ Development of the Bindings
131
+ ---------------------------
132
+
133
+ Clone the repository and get the submodules::
134
+
135
+ git clone https://github.com/wanadev/imagequant-python.git
136
+ cd imagequant-python
137
+ git submodule init
138
+ git submodule update
139
+
140
+ Install some dependencies (preferably in a virtualenv)::
141
+
142
+ pip3 install nox cffi pillow
143
+
144
+ Build the binary part of the lib::
145
+
146
+ python imagequant/libimagequant_build.py
147
+
148
+ A ``.so`` file (or a ``.pyd`` file on Windows, or a ``.dylib`` file on MacOS) shoud now be present in the ``imagequant/`` folder. You will not need to run this command again until you change something in ``imagequant/libimagequant.h`` or in ``libimagequant/*.{c,h}``.
149
+
150
+ To check the coding style, you can run the lint with the following command::
151
+
152
+ nox -s lint
153
+
154
+ To run the tests, use the following command::
155
+
156
+ nox -s test
157
+
158
+
159
+ License
160
+ -------
161
+
162
+ **Imagequant Python** is licensed under the BSD 3 Clause. See the LICENSE_ file for more information.
163
+
164
+ **Libimagequant** is dual-licensed:
165
+
166
+ * For Free/Libre Open Source Software it's available under GPL v3 or later with additional copyright notices for older parts of the code.
167
+
168
+ * For use in closed-source software, AppStore distribution, and other non-GPL uses, you can obtain a commercial license.
169
+
170
+ Read its `license terms <https://github.com/ImageOptim/libimagequant#license>`_ for more information.
171
+
172
+ .. _LICENSE: https://github.com/wanadev/imagequant-python/blob/master/LICENSE
173
+
174
+
175
+ Changelog
176
+ ---------
177
+
178
+ * **[NEXT]** (changes on ``master`` that have not been released yet):
179
+
180
+ * Nothing yet ;)
181
+
182
+ * **v1.1.6:**
183
+
184
+ * misc: Added Python 3.15 support (@flozz)
185
+ * misc!: Removed Python 3.10 support (@flozz)
186
+ * misc!: Dropped Linux i686 support (@flozz)
187
+
188
+ * **v1.1.5:**
189
+
190
+ * misc: Added support for Python 3.14 (@flozz)
191
+ * misc: Dropped support of x86 (32bits) architecture for Python >= 3.14 (@flozz)
192
+ * misc!: Removed support for Python 3.9 (@flozz)
193
+
194
+ * **v1.1.4:**
195
+
196
+ * fix: Cleanup before throwing RuntimeError to avoid memory leak (@laggykiller, #26)
197
+
198
+ * **v1.1.3:**
199
+
200
+ * misc(deps): Fixed PyPy builds on Windows platform (@flozz)
201
+
202
+ * **v1.1.2:**
203
+
204
+ * misc: Removed PyPy wheels for Windows AMD64 as they won't build (@flozz)
205
+ * misc: Fixed libimagequant build on newer GCC versions (@flozz)
206
+ * misc: Added Python 3.13 support (@flozz)
207
+ * misc!: Removed Python 3.8 support (@flozz)
208
+
209
+ * **v1.1.1:**
210
+
211
+ * dist: Build and publish arm64 wheels for Linux and Windows (@laggykiller, #9, #10)
212
+
213
+ * **v1.1.0:**
214
+
215
+ * Added options to set minimal and target (maximal) quality (@injet-zhou, #4)
216
+
217
+ * **v1.0.5:**
218
+
219
+ * Added Python 3.12 support (@flozz)
220
+ * Removed Python 3.7 support (@flozz)
221
+
222
+ * **v1.0.4:**
223
+
224
+ * Added Python 3.11 support
225
+
226
+ * **v1.0.3:**
227
+
228
+ * ``arm64`` and ``universal2`` wheels for macOS M1
229
+ * ``x86`` and ``x68_64`` wheels for musl-based Linux distro (Alpine,...)
230
+ * ``x86`` wheels for Windows (``x86_64`` were already available)
231
+
232
+ * **v1.0.2:** Python 3.10 support and wheels
233
+ * **v1.0.1:** Fix encoding while reading the README in setup.py
234
+ * **v1.0.0:** Initial release with a minimal API.
235
+
236
+
237
+
238
+ .. |Github| image:: https://img.shields.io/github/stars/wanadev/imagequant-python?label=Github&logo=github
239
+ :target: https://github.com/wanadev/imagequant-python
240
+ .. |Discord| image:: https://img.shields.io/badge/chat-Discord-8c9eff?logo=discord&logoColor=ffffff
241
+ :target: https://discord.gg/BmUkEdMuFp
242
+ .. |PYPI Version| image:: https://img.shields.io/pypi/v/imagequant.svg
243
+ :target: https://pypi.python.org/pypi/imagequant
244
+ .. |Build Status| image:: https://github.com/wanadev/imagequant-python/actions/workflows/python-ci.yml/badge.svg
245
+ :target: https://github.com/wanadev/imagequant-python/actions
246
+ .. |Black| image:: https://img.shields.io/badge/code%20style-black-000000.svg
247
+ :target: https://black.readthedocs.io/en/stable/
248
+ .. |License| image:: https://img.shields.io/pypi/l/imagequant.svg
249
+ :target: https://github.com/wanadev/imagequant-python/blob/master/LICENSE
@@ -0,0 +1,8 @@
1
+ imagequant/__init__.py,sha256=vKQzx50YlbF6T0ea0O8g_4MRmOQkT-JaxUmCOnsXpKc,7240
2
+ imagequant/_libimagequant.pyd,sha256=Td0FkPXsbjv_7uJv4s__jwamEPLFb7nldjmwH3VXR4Y,69632
3
+ imagequant/libimagequant_build.py,sha256=Nr4ae1eIkHEiJdr_3FvFrLZOLRJLgwGu-Abm2HJq8tU,838
4
+ imagequant-1.1.6.dist-info/licenses/LICENSE,sha256=Q3H61ezfZicJMgnz9EXuNj44EL7xdQfCITFsaUwNe8M,1526
5
+ imagequant-1.1.6.dist-info/METADATA,sha256=q6z99i4UZraNcx6X79me-w1hwdoyMZnriDf3-9KJ9VM,7764
6
+ imagequant-1.1.6.dist-info/WHEEL,sha256=hw69xxiYuOcOqid9WoScsymTbZ3eDs5HQ4gUZZtdDhs,97
7
+ imagequant-1.1.6.dist-info/top_level.txt,sha256=9AzQ9NgvAz4fDBTQwr6nG2LtlMnV88Dm3IGIkx0ZfGo,11
8
+ imagequant-1.1.6.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.2.1)
3
+ Root-Is-Purelib: false
4
+ Tag: cp315-cp315-win32
5
+
@@ -0,0 +1,24 @@
1
+ Copyright (c) 2021-2026, Wanadev <http://www.wanadev.fr/>
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+ * Redistributions of source code must retain the above copyright
7
+ notice, this list of conditions and the following disclaimer.
8
+ * Redistributions in binary form must reproduce the above copyright
9
+ notice, this list of conditions and the following disclaimer in the
10
+ documentation and/or other materials provided with the distribution.
11
+ * Neither the name of Wanadev nor the
12
+ names of its contributors may be used to endorse or promote products
13
+ derived from this software without specific prior written permission.
14
+
15
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
+ DISCLAIMED. IN NO EVENT SHALL WANADEV BE LIABLE FOR ANY
19
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1 @@
1
+ imagequant