mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-10-31 07:57:27 +03:00 
			
		
		
		
	Merge branch 'main' into comment_correct_placement
This commit is contained in:
		
						commit
						db76eaa12c
					
				
							
								
								
									
										12
									
								
								.github/workflows/test-docker.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								.github/workflows/test-docker.yml
									
									
									
									
										vendored
									
									
								
							|  | @ -11,9 +11,9 @@ jobs: | |||
|       matrix: | ||||
|         docker: [ | ||||
|           # Run slower jobs first to give them a headstart and reduce waiting time | ||||
|           ubuntu-20.04-focal-arm64v8, | ||||
|           ubuntu-20.04-focal-ppc64le, | ||||
|           ubuntu-20.04-focal-s390x, | ||||
|           ubuntu-22.04-jammy-arm64v8, | ||||
|           ubuntu-22.04-jammy-ppc64le, | ||||
|           ubuntu-22.04-jammy-s390x, | ||||
|           # Then run the remainder | ||||
|           alpine, | ||||
|           amazon-2-amd64, | ||||
|  | @ -32,11 +32,11 @@ jobs: | |||
|         ] | ||||
|         dockerTag: [main] | ||||
|         include: | ||||
|           - docker: "ubuntu-20.04-focal-arm64v8" | ||||
|           - docker: "ubuntu-22.04-jammy-arm64v8" | ||||
|             qemu-arch: "aarch64" | ||||
|           - docker: "ubuntu-20.04-focal-ppc64le" | ||||
|           - docker: "ubuntu-22.04-jammy-ppc64le" | ||||
|             qemu-arch: "ppc64le" | ||||
|           - docker: "ubuntu-20.04-focal-s390x" | ||||
|           - docker: "ubuntu-22.04-jammy-s390x" | ||||
|             qemu-arch: "s390x" | ||||
| 
 | ||||
|     name: ${{ matrix.docker }} | ||||
|  |  | |||
							
								
								
									
										30
									
								
								CHANGES.rst
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								CHANGES.rst
									
									
									
									
									
								
							|  | @ -5,6 +5,24 @@ Changelog (Pillow) | |||
| 9.2.0 (unreleased) | ||||
| ------------------ | ||||
| 
 | ||||
| - Separate multiple GIF comment blocks with newlines #6294 | ||||
|   [raygard, radarhere] | ||||
| 
 | ||||
| - Always use GIF89a for comments #6292 | ||||
|   [raygard, radarhere] | ||||
| 
 | ||||
| - Ignore compression value from BMP info dictionary when saving as TIFF #6231 | ||||
|   [radarhere] | ||||
| 
 | ||||
| - If font is file-like object, do not re-read from object to get variant #6234 | ||||
|   [radarhere] | ||||
| 
 | ||||
| - Raise ValueError when trying to access internal fp after close #6213 | ||||
|   [radarhere] | ||||
| 
 | ||||
| - Support more affine expression forms in im.point() #6254 | ||||
|   [benrg, radarhere] | ||||
| 
 | ||||
| - Populate Python palette in fromarray() #6283 | ||||
|   [radarhere] | ||||
| 
 | ||||
|  | @ -17,9 +35,6 @@ Changelog (Pillow) | |||
| - Adjust BITSPERSAMPLE to match SAMPLESPERPIXEL when opening TIFFs #6270 | ||||
|   [radarhere] | ||||
| 
 | ||||
| - Do not open images with zero or negative height #6269 | ||||
|   [radarhere] | ||||
| 
 | ||||
| - Search pkgconf system libs/cflags #6138 | ||||
|   [jameshilliard, radarhere] | ||||
| 
 | ||||
|  | @ -50,6 +65,15 @@ Changelog (Pillow) | |||
| - Deprecated PhotoImage.paste() box parameter #6178 | ||||
|   [radarhere] | ||||
| 
 | ||||
| 9.1.1 (2022-05-17) | ||||
| ------------------ | ||||
| 
 | ||||
| - When reading past the end of a TGA scan line, reduce bytes left. CVE-2022-30595 | ||||
|   [radarhere] | ||||
| 
 | ||||
| - Do not open images with zero or negative height #6269 | ||||
|   [radarhere] | ||||
| 
 | ||||
| 9.1.0 (2022-04-01) | ||||
| ------------------ | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							|  | @ -85,6 +85,8 @@ release-test: | |||
| sdist: | ||||
| 	python3 -m build --help > /dev/null 2>&1 || python3 -m pip install build | ||||
| 	python3 -m build --sdist | ||||
| 	python3 -m twine --help > /dev/null 2>&1 || python3 -m pip install twine | ||||
| 	python3 -m twine check --strict dist/* | ||||
| 
 | ||||
| .PHONY: test | ||||
| test: | ||||
|  |  | |||
|  | @ -24,7 +24,6 @@ Released quarterly on January 2nd, April 1st, July 1st and October 15th. | |||
| * [ ] Create and check source distribution: | ||||
|   ```bash | ||||
|   make sdist | ||||
|   python3 -m twine check --strict dist/* | ||||
|   ``` | ||||
| * [ ] Create [binary distributions](https://github.com/python-pillow/Pillow/blob/main/RELEASING.md#binary-distributions) | ||||
| * [ ] Check and upload all binaries and source distributions e.g.: | ||||
|  | @ -61,7 +60,6 @@ Released as needed for security, installation or critical bug fixes. | |||
| * [ ] Create and check source distribution: | ||||
|   ```bash | ||||
|   make sdist | ||||
|   python3 -m twine check --strict dist/* | ||||
|   ``` | ||||
| * [ ] Create [binary distributions](https://github.com/python-pillow/Pillow/blob/main/RELEASING.md#binary-distributions) | ||||
| * [ ] Check and upload all binaries and source distributions e.g.: | ||||
|  | @ -91,7 +89,6 @@ Released as needed privately to individual vendors for critical security-related | |||
| * [ ] Create and check source distribution: | ||||
|   ```bash | ||||
|   make sdist | ||||
|   python3 -m twine check --strict dist/* | ||||
|   ``` | ||||
| * [ ] Create [binary distributions](https://github.com/python-pillow/Pillow/blob/main/RELEASING.md#binary-distributions) | ||||
| * [ ] Publish the [release on GitHub](https://github.com/python-pillow/Pillow/releases) | ||||
|  |  | |||
							
								
								
									
										
											BIN
										
									
								
								Tests/images/cross_scan_line_truncated.tga
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Tests/images/cross_scan_line_truncated.tga
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								Tests/images/multiple_comments.gif
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Tests/images/multiple_comments.gif
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 1.5 KiB | 
|  | @ -637,6 +637,15 @@ def test_apng_save_blend(tmp_path): | |||
|         assert im.getpixel((0, 0)) == (0, 255, 0, 255) | ||||
| 
 | ||||
| 
 | ||||
| def test_seek_after_close(): | ||||
|     im = Image.open("Tests/images/apng/delay.png") | ||||
|     im.seek(1) | ||||
|     im.close() | ||||
| 
 | ||||
|     with pytest.raises(ValueError): | ||||
|         im.seek(0) | ||||
| 
 | ||||
| 
 | ||||
| def test_constants_deprecation(): | ||||
|     for enum, prefix in { | ||||
|         PngImagePlugin.Disposal: "APNG_DISPOSE_", | ||||
|  |  | |||
|  | @ -46,6 +46,15 @@ def test_closed_file(): | |||
|         im.close() | ||||
| 
 | ||||
| 
 | ||||
| def test_seek_after_close(): | ||||
|     im = Image.open(animated_test_file) | ||||
|     im.seek(1) | ||||
|     im.close() | ||||
| 
 | ||||
|     with pytest.raises(ValueError): | ||||
|         im.seek(0) | ||||
| 
 | ||||
| 
 | ||||
| def test_context_manager(): | ||||
|     with warnings.catch_warnings(): | ||||
|         with Image.open(static_test_file) as im: | ||||
|  |  | |||
|  | @ -46,6 +46,19 @@ def test_closed_file(): | |||
|         im.close() | ||||
| 
 | ||||
| 
 | ||||
| def test_seek_after_close(): | ||||
|     im = Image.open("Tests/images/iss634.gif") | ||||
|     im.load() | ||||
|     im.close() | ||||
| 
 | ||||
|     with pytest.raises(ValueError): | ||||
|         im.is_animated | ||||
|     with pytest.raises(ValueError): | ||||
|         im.n_frames | ||||
|     with pytest.raises(ValueError): | ||||
|         im.seek(1) | ||||
| 
 | ||||
| 
 | ||||
| def test_context_manager(): | ||||
|     with warnings.catch_warnings(): | ||||
|         with Image.open(TEST_GIF) as im: | ||||
|  | @ -794,6 +807,9 @@ def test_comment(tmp_path): | |||
|     with Image.open(out) as reread: | ||||
|         assert reread.info["comment"] == im.info["comment"].encode() | ||||
| 
 | ||||
|         # Test that GIF89a is used for comments | ||||
|         assert reread.info["version"] == b"GIF89a" | ||||
| 
 | ||||
| 
 | ||||
| def test_comment_over_255(tmp_path): | ||||
|     out = str(tmp_path / "temp.gif") | ||||
|  | @ -804,15 +820,23 @@ def test_comment_over_255(tmp_path): | |||
|     im.info["comment"] = comment | ||||
|     im.save(out) | ||||
|     with Image.open(out) as reread: | ||||
| 
 | ||||
|         assert reread.info["comment"] == comment | ||||
| 
 | ||||
|         # Test that GIF89a is used for comments | ||||
|         assert reread.info["version"] == b"GIF89a" | ||||
| 
 | ||||
| 
 | ||||
| def test_zero_comment_subblocks(): | ||||
|     with Image.open("Tests/images/hopper_zero_comment_subblocks.gif") as im: | ||||
|         assert_image_equal_tofile(im, TEST_GIF) | ||||
| 
 | ||||
| 
 | ||||
| def test_read_multiple_comment_blocks(): | ||||
|     with Image.open("Tests/images/multiple_comments.gif") as im: | ||||
|         # Multiple comment blocks in a frame are separated not concatenated | ||||
|         assert im.info["comment"] == b"Test comment 1\nTest comment 2" | ||||
| 
 | ||||
| 
 | ||||
| def test_write_comment(tmp_path): | ||||
|     out = str(tmp_path / "temp.gif") | ||||
|     with Image.open("Tests/images/dispose_prev.gif") as im: | ||||
|  |  | |||
|  | @ -18,6 +18,7 @@ from .helper import ( | |||
|     hopper, | ||||
|     mark_if_feature_version, | ||||
|     skip_unless_feature, | ||||
|     skip_unless_feature_version, | ||||
| ) | ||||
| 
 | ||||
| 
 | ||||
|  | @ -991,6 +992,7 @@ class TestFileLibTiff(LibTiffTestCase): | |||
|         with Image.open(out) as im: | ||||
|             im.load() | ||||
| 
 | ||||
|     @skip_unless_feature_version("libtiff", "4.0.4") | ||||
|     def test_realloc_overflow(self): | ||||
|         TiffImagePlugin.READ_LIBTIFF = True | ||||
|         with Image.open("Tests/images/tiff_overflow_rows_per_strip.tif") as im: | ||||
|  |  | |||
|  | @ -48,6 +48,14 @@ def test_closed_file(): | |||
|         im.close() | ||||
| 
 | ||||
| 
 | ||||
| def test_seek_after_close(): | ||||
|     im = Image.open(test_files[0]) | ||||
|     im.close() | ||||
| 
 | ||||
|     with pytest.raises(ValueError): | ||||
|         im.seek(1) | ||||
| 
 | ||||
| 
 | ||||
| def test_context_manager(): | ||||
|     with warnings.catch_warnings(): | ||||
|         with Image.open(test_files[0]) as im: | ||||
|  |  | |||
|  | @ -101,6 +101,10 @@ def test_cross_scan_line(): | |||
|     with Image.open("Tests/images/cross_scan_line.tga") as im: | ||||
|         assert_image_equal_tofile(im, "Tests/images/cross_scan_line.png") | ||||
| 
 | ||||
|     with Image.open("Tests/images/cross_scan_line_truncated.tga") as im: | ||||
|         with pytest.raises(OSError): | ||||
|             im.load() | ||||
| 
 | ||||
| 
 | ||||
| def test_save(tmp_path): | ||||
|     test_file = "Tests/images/tga_id_field.tga" | ||||
|  |  | |||
|  | @ -70,6 +70,15 @@ class TestFileTiff: | |||
|             im.load() | ||||
|             im.close() | ||||
| 
 | ||||
|     def test_seek_after_close(self): | ||||
|         im = Image.open("Tests/images/multipage.tiff") | ||||
|         im.close() | ||||
| 
 | ||||
|         with pytest.raises(ValueError): | ||||
|             im.n_frames | ||||
|         with pytest.raises(ValueError): | ||||
|             im.seek(1) | ||||
| 
 | ||||
|     def test_context_manager(self): | ||||
|         with warnings.catch_warnings(): | ||||
|             with Image.open("Tests/images/multipage.tiff") as im: | ||||
|  | @ -706,6 +715,13 @@ class TestFileTiff: | |||
|         with Image.open(outfile) as reloaded: | ||||
|             assert reloaded.info["icc_profile"] == icc_profile | ||||
| 
 | ||||
|     def test_save_bmp_compression(self, tmp_path): | ||||
|         with Image.open("Tests/images/hopper.bmp") as im: | ||||
|             assert im.info["compression"] == 0 | ||||
| 
 | ||||
|             outfile = str(tmp_path / "temp.tif") | ||||
|             im.save(outfile) | ||||
| 
 | ||||
|     def test_discard_icc_profile(self, tmp_path): | ||||
|         outfile = str(tmp_path / "temp.tif") | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,7 @@ | |||
| import pytest | ||||
| 
 | ||||
| from PIL import Image | ||||
| 
 | ||||
| from .helper import assert_image_equal, hopper | ||||
| 
 | ||||
| 
 | ||||
|  | @ -17,11 +19,24 @@ def test_sanity(): | |||
|         im.point(list(range(256))) | ||||
|     im.point(lambda x: x * 1) | ||||
|     im.point(lambda x: x + 1) | ||||
|     im.point(lambda x: x - 1) | ||||
|     im.point(lambda x: x * 1 + 1) | ||||
|     im.point(lambda x: 0.1 + 0.2 * x) | ||||
|     im.point(lambda x: -x) | ||||
|     im.point(lambda x: x - 0.5) | ||||
|     im.point(lambda x: 1 - x / 2) | ||||
|     im.point(lambda x: (2 + x) / 3) | ||||
|     im.point(lambda x: 0.5) | ||||
|     im.point(lambda x: x / 1) | ||||
|     im.point(lambda x: x + x) | ||||
|     with pytest.raises(TypeError): | ||||
|         im.point(lambda x: x - 1) | ||||
|         im.point(lambda x: x * x) | ||||
|     with pytest.raises(TypeError): | ||||
|         im.point(lambda x: x / 1) | ||||
|         im.point(lambda x: x / x) | ||||
|     with pytest.raises(TypeError): | ||||
|         im.point(lambda x: 1 / x) | ||||
|     with pytest.raises(TypeError): | ||||
|         im.point(lambda x: x // 2) | ||||
| 
 | ||||
| 
 | ||||
| def test_16bit_lut(): | ||||
|  | @ -47,3 +62,8 @@ def test_f_mode(): | |||
|     im = hopper("F") | ||||
|     with pytest.raises(ValueError): | ||||
|         im.point(None) | ||||
| 
 | ||||
| 
 | ||||
| def test_coerce_e_deprecation(): | ||||
|     with pytest.warns(DeprecationWarning): | ||||
|         assert Image.coerce_e(2).data == 2 | ||||
|  |  | |||
|  | @ -65,9 +65,12 @@ class TestImageFont: | |||
|         return font_bytes | ||||
| 
 | ||||
|     def test_font_with_filelike(self): | ||||
|         ImageFont.truetype( | ||||
|         ttf = ImageFont.truetype( | ||||
|             self._font_as_bytes(), FONT_SIZE, layout_engine=self.LAYOUT_ENGINE | ||||
|         ) | ||||
|         ttf_copy = ttf.font_variant() | ||||
|         assert ttf_copy.font_bytes == ttf.font_bytes | ||||
| 
 | ||||
|         self._render(self._font_as_bytes()) | ||||
|         # Usage note:  making two fonts from the same buffer fails. | ||||
|         # shared_bytes = self._font_as_bytes() | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| #!/bin/bash | ||||
| # install openjpeg | ||||
| 
 | ||||
| archive=openjpeg-2.4.0 | ||||
| archive=openjpeg-2.5.0 | ||||
| 
 | ||||
| ./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/main/$archive.tar.gz | ||||
| 
 | ||||
|  |  | |||
|  | @ -170,6 +170,14 @@ in Pillow 10 (2023-07-01). Upgrade to | |||
| `PyQt6 <https://www.riverbankcomputing.com/static/Docs/PyQt6/>`_ or | ||||
| `PySide6 <https://doc.qt.io/qtforpython/>`_ instead. | ||||
| 
 | ||||
| Image.coerce_e | ||||
| ~~~~~~~~~~~~~~ | ||||
| 
 | ||||
| .. deprecated:: 9.2.0 | ||||
| 
 | ||||
| This undocumented method has been deprecated and will be removed in Pillow 10 | ||||
| (2023-07-01). | ||||
| 
 | ||||
| Removed features | ||||
| ---------------- | ||||
| 
 | ||||
|  |  | |||
|  | @ -181,7 +181,8 @@ Many of Pillow's features require external libraries: | |||
| 
 | ||||
| * **openjpeg** provides JPEG 2000 functionality. | ||||
| 
 | ||||
|   * Pillow has been tested with openjpeg **2.0.0**, **2.1.0**, **2.3.1** and **2.4.0**. | ||||
|   * Pillow has been tested with openjpeg **2.0.0**, **2.1.0**, **2.3.1**, | ||||
|     **2.4.0** and **2.5.0**. | ||||
|   * Pillow does **not** support the earlier **1.5** series which ships | ||||
|     with Debian Jessie. | ||||
| 
 | ||||
|  | @ -474,11 +475,9 @@ These platforms are built and tested for every change. | |||
| +----------------------------------+----------------------------+---------------------+ | ||||
| | Ubuntu Linux 20.04 LTS (Focal)   | 3.7, 3.8, 3.9, 3.10, 3.11, | x86-64              | | ||||
| |                                  | PyPy3                      |                     | | ||||
| |                                  +----------------------------+---------------------+ | ||||
| |                                  | 3.8                        | arm64v8, ppc64le,   | | ||||
| |                                  |                            | s390x               | | ||||
| +----------------------------------+----------------------------+---------------------+ | ||||
| | Ubuntu Linux 22.04 LTS (Jammy)   | 3.10                       | x86-64              | | ||||
| | Ubuntu Linux 22.04 LTS (Jammy)   | 3.10                       | arm64v8, ppc64le,   | | ||||
| |                                  |                            | s390x, x86-64       | | ||||
| +----------------------------------+----------------------------+---------------------+ | ||||
| | Windows Server 2016              | 3.7                        | x86-64              | | ||||
| +----------------------------------+----------------------------+---------------------+ | ||||
|  |  | |||
|  | @ -174,7 +174,7 @@ Previously, if a BMP file was too large, an ``OSError`` would be raised. Now, | |||
| Dark theme for docs | ||||
| ^^^^^^^^^^^^^^^^^^^ | ||||
| 
 | ||||
| The https://pillow.readthedocs.io documentation will use a dark theme if the the user has requested the system use one. Uses the ``prefers-color-scheme`` CSS media query. | ||||
| The https://pillow.readthedocs.io documentation will use a dark theme if the user has requested the system use one. Uses the ``prefers-color-scheme`` CSS media query. | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										16
									
								
								docs/releasenotes/9.1.1.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								docs/releasenotes/9.1.1.rst
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | |||
| 9.1.1 | ||||
| ----- | ||||
| 
 | ||||
| Security | ||||
| ======== | ||||
| 
 | ||||
| This release addresses several security problems. | ||||
| 
 | ||||
| :cve:`CVE-2022-30595`: When reading a TGA file with RLE packets that cross scan lines, | ||||
| Pillow reads the information past the end of the first line without deducting that | ||||
| from the length of the remaining file data. This vulnerability was introduced in Pillow | ||||
| 9.1.0, and can cause a heap buffer overflow. | ||||
| 
 | ||||
| Opening an image with a zero or negative height has been found to bypass a | ||||
| decompression bomb check. This will now raise a :py:exc:`SyntaxError` instead, in turn | ||||
| raising a ``PIL.UnidentifiedImageError``. | ||||
|  | @ -31,6 +31,14 @@ FreeTypeFont.getmask2 fill parameter | |||
| The undocumented ``fill`` parameter of :py:meth:`.FreeTypeFont.getmask2` | ||||
| has been deprecated and will be removed in Pillow 10 (2023-07-01). | ||||
| 
 | ||||
| Image.coerce_e | ||||
| ~~~~~~~~~~~~~~ | ||||
| 
 | ||||
| .. deprecated:: 9.2.0 | ||||
| 
 | ||||
| This undocumented method has been deprecated and will be removed in Pillow 10 | ||||
| (2023-07-01). | ||||
| 
 | ||||
| API Changes | ||||
| =========== | ||||
| 
 | ||||
|  |  | |||
|  | @ -15,6 +15,7 @@ expected to be backported to earlier versions. | |||
|   :maxdepth: 2 | ||||
| 
 | ||||
|   9.2.0 | ||||
|   9.1.1 | ||||
|   9.1.0 | ||||
|   9.0.1 | ||||
|   9.0.0 | ||||
|  |  | |||
|  | @ -57,7 +57,7 @@ class DcxImageFile(PcxImageFile): | |||
|                 break | ||||
|             self._offset.append(offset) | ||||
| 
 | ||||
|         self.__fp = self.fp | ||||
|         self._fp = self.fp | ||||
|         self.frame = None | ||||
|         self.n_frames = len(self._offset) | ||||
|         self.is_animated = self.n_frames > 1 | ||||
|  | @ -67,22 +67,13 @@ class DcxImageFile(PcxImageFile): | |||
|         if not self._seek_check(frame): | ||||
|             return | ||||
|         self.frame = frame | ||||
|         self.fp = self.__fp | ||||
|         self.fp = self._fp | ||||
|         self.fp.seek(self._offset[frame]) | ||||
|         PcxImageFile._open(self) | ||||
| 
 | ||||
|     def tell(self): | ||||
|         return self.frame | ||||
| 
 | ||||
|     def _close__fp(self): | ||||
|         try: | ||||
|             if self.__fp != self.fp: | ||||
|                 self.__fp.close() | ||||
|         except AttributeError: | ||||
|             pass | ||||
|         finally: | ||||
|             self.__fp = None | ||||
| 
 | ||||
| 
 | ||||
| Image.register_open(DcxImageFile.format, DcxImageFile, _accept) | ||||
| 
 | ||||
|  |  | |||
|  | @ -91,7 +91,7 @@ class FliImageFile(ImageFile.ImageFile): | |||
| 
 | ||||
|         # set things up to decode first frame | ||||
|         self.__frame = -1 | ||||
|         self.__fp = self.fp | ||||
|         self._fp = self.fp | ||||
|         self.__rewind = self.fp.tell() | ||||
|         self.seek(0) | ||||
| 
 | ||||
|  | @ -125,7 +125,7 @@ class FliImageFile(ImageFile.ImageFile): | |||
|     def _seek(self, frame): | ||||
|         if frame == 0: | ||||
|             self.__frame = -1 | ||||
|             self.__fp.seek(self.__rewind) | ||||
|             self._fp.seek(self.__rewind) | ||||
|             self.__offset = 128 | ||||
|         else: | ||||
|             # ensure that the previous frame was loaded | ||||
|  | @ -136,7 +136,7 @@ class FliImageFile(ImageFile.ImageFile): | |||
|         self.__frame = frame | ||||
| 
 | ||||
|         # move to next frame | ||||
|         self.fp = self.__fp | ||||
|         self.fp = self._fp | ||||
|         self.fp.seek(self.__offset) | ||||
| 
 | ||||
|         s = self.fp.read(4) | ||||
|  | @ -153,15 +153,6 @@ class FliImageFile(ImageFile.ImageFile): | |||
|     def tell(self): | ||||
|         return self.__frame | ||||
| 
 | ||||
|     def _close__fp(self): | ||||
|         try: | ||||
|             if self.__fp != self.fp: | ||||
|                 self.__fp.close() | ||||
|         except AttributeError: | ||||
|             pass | ||||
|         finally: | ||||
|             self.__fp = None | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # registry | ||||
|  |  | |||
|  | @ -102,7 +102,7 @@ class GifImageFile(ImageFile.ImageFile): | |||
|                 p = ImagePalette.raw("RGB", p) | ||||
|                 self.global_palette = self.palette = p | ||||
| 
 | ||||
|         self.__fp = self.fp  # FIXME: hack | ||||
|         self._fp = self.fp  # FIXME: hack | ||||
|         self.__rewind = self.fp.tell() | ||||
|         self._n_frames = None | ||||
|         self._is_animated = None | ||||
|  | @ -161,7 +161,7 @@ class GifImageFile(ImageFile.ImageFile): | |||
|             self.__offset = 0 | ||||
|             self.dispose = None | ||||
|             self.__frame = -1 | ||||
|             self.__fp.seek(self.__rewind) | ||||
|             self._fp.seek(self.__rewind) | ||||
|             self.disposal_method = 0 | ||||
|         else: | ||||
|             # ensure that the previous frame was loaded | ||||
|  | @ -171,7 +171,7 @@ class GifImageFile(ImageFile.ImageFile): | |||
|         if frame != self.__frame + 1: | ||||
|             raise ValueError(f"cannot seek to frame {frame}") | ||||
| 
 | ||||
|         self.fp = self.__fp | ||||
|         self.fp = self._fp | ||||
|         if self.__offset: | ||||
|             # backup to last frame | ||||
|             self.fp.seek(self.__offset) | ||||
|  | @ -228,12 +228,18 @@ class GifImageFile(ImageFile.ImageFile): | |||
|                     # | ||||
|                     # comment extension | ||||
|                     # | ||||
|                     comment = b"" | ||||
| 
 | ||||
|                     # Collect one comment block | ||||
|                     while block: | ||||
|                         if "comment" in info: | ||||
|                             info["comment"] += block | ||||
|                         else: | ||||
|                             info["comment"] = block | ||||
|                         comment += block | ||||
|                         block = self.data() | ||||
| 
 | ||||
|                     if "comment" in info: | ||||
|                         # If multiple comment blocks in frame, separate with \n | ||||
|                         info["comment"] += b"\n" + comment | ||||
|                     else: | ||||
|                         info["comment"] = comment | ||||
|                     s = None | ||||
|                     continue | ||||
|                 elif s[0] == 255: | ||||
|  | @ -281,7 +287,7 @@ class GifImageFile(ImageFile.ImageFile): | |||
|             s = None | ||||
| 
 | ||||
|         if interlace is None: | ||||
|             # self.__fp = None | ||||
|             # self._fp = None | ||||
|             raise EOFError | ||||
|         if not update_image: | ||||
|             return | ||||
|  | @ -443,15 +449,6 @@ class GifImageFile(ImageFile.ImageFile): | |||
|     def tell(self): | ||||
|         return self.__frame | ||||
| 
 | ||||
|     def _close__fp(self): | ||||
|         try: | ||||
|             if self.__fp != self.fp: | ||||
|                 self.__fp.close() | ||||
|         except AttributeError: | ||||
|             pass | ||||
|         finally: | ||||
|             self.__fp = None | ||||
| 
 | ||||
| 
 | ||||
| # -------------------------------------------------------------------- | ||||
| # Write GIF files | ||||
|  | @ -903,17 +900,16 @@ def _get_global_header(im, info): | |||
|     # https://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp | ||||
| 
 | ||||
|     version = b"87a" | ||||
|     for extensionKey in ["transparency", "duration", "loop", "comment"]: | ||||
|         if info and extensionKey in info: | ||||
|             if (extensionKey == "duration" and info[extensionKey] == 0) or ( | ||||
|                 extensionKey == "comment" and not (1 <= len(info[extensionKey]) <= 255) | ||||
|             ): | ||||
|                 continue | ||||
|             version = b"89a" | ||||
|             break | ||||
|     else: | ||||
|         if im.info.get("version") == b"89a": | ||||
|             version = b"89a" | ||||
|     if im.info.get("version") == b"89a" or ( | ||||
|         info | ||||
|         and ( | ||||
|             "transparency" in info | ||||
|             or "loop" in info | ||||
|             or info.get("duration") | ||||
|             or info.get("comment") | ||||
|         ) | ||||
|     ): | ||||
|         version = b"89a" | ||||
| 
 | ||||
|     background = _get_background(im, info.get("background")) | ||||
| 
 | ||||
|  |  | |||
|  | @ -245,7 +245,7 @@ class ImImageFile(ImageFile.ImageFile): | |||
| 
 | ||||
|         self.__offset = offs = self.fp.tell() | ||||
| 
 | ||||
|         self.__fp = self.fp  # FIXME: hack | ||||
|         self._fp = self.fp  # FIXME: hack | ||||
| 
 | ||||
|         if self.rawmode[:2] == "F;": | ||||
| 
 | ||||
|  | @ -294,22 +294,13 @@ class ImImageFile(ImageFile.ImageFile): | |||
|         size = ((self.size[0] * bits + 7) // 8) * self.size[1] | ||||
|         offs = self.__offset + frame * size | ||||
| 
 | ||||
|         self.fp = self.__fp | ||||
|         self.fp = self._fp | ||||
| 
 | ||||
|         self.tile = [("raw", (0, 0) + self.size, offs, (self.rawmode, 0, -1))] | ||||
| 
 | ||||
|     def tell(self): | ||||
|         return self.frame | ||||
| 
 | ||||
|     def _close__fp(self): | ||||
|         try: | ||||
|             if self.__fp != self.fp: | ||||
|                 self.__fp.close() | ||||
|         except AttributeError: | ||||
|             pass | ||||
|         finally: | ||||
|             self.__fp = None | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # -------------------------------------------------------------------- | ||||
|  |  | |||
|  | @ -29,7 +29,6 @@ import builtins | |||
| import io | ||||
| import logging | ||||
| import math | ||||
| import numbers | ||||
| import os | ||||
| import re | ||||
| import struct | ||||
|  | @ -432,44 +431,50 @@ def _getencoder(mode, encoder_name, args, extra=()): | |||
| 
 | ||||
| 
 | ||||
| def coerce_e(value): | ||||
|     return value if isinstance(value, _E) else _E(value) | ||||
|     deprecate("coerce_e", 10) | ||||
|     return value if isinstance(value, _E) else _E(1, value) | ||||
| 
 | ||||
| 
 | ||||
| # _E(scale, offset) represents the affine transformation scale * x + offset. | ||||
| # The "data" field is named for compatibility with the old implementation, | ||||
| # and should be renamed once coerce_e is removed. | ||||
| class _E: | ||||
|     def __init__(self, data): | ||||
|     def __init__(self, scale, data): | ||||
|         self.scale = scale | ||||
|         self.data = data | ||||
| 
 | ||||
|     def __neg__(self): | ||||
|         return _E(-self.scale, -self.data) | ||||
| 
 | ||||
|     def __add__(self, other): | ||||
|         return _E((self.data, "__add__", coerce_e(other).data)) | ||||
|         if isinstance(other, _E): | ||||
|             return _E(self.scale + other.scale, self.data + other.data) | ||||
|         return _E(self.scale, self.data + other) | ||||
| 
 | ||||
|     __radd__ = __add__ | ||||
| 
 | ||||
|     def __sub__(self, other): | ||||
|         return self + -other | ||||
| 
 | ||||
|     def __rsub__(self, other): | ||||
|         return other + -self | ||||
| 
 | ||||
|     def __mul__(self, other): | ||||
|         return _E((self.data, "__mul__", coerce_e(other).data)) | ||||
|         if isinstance(other, _E): | ||||
|             return NotImplemented | ||||
|         return _E(self.scale * other, self.data * other) | ||||
| 
 | ||||
|     __rmul__ = __mul__ | ||||
| 
 | ||||
|     def __truediv__(self, other): | ||||
|         if isinstance(other, _E): | ||||
|             return NotImplemented | ||||
|         return _E(self.scale / other, self.data / other) | ||||
| 
 | ||||
| 
 | ||||
| def _getscaleoffset(expr): | ||||
|     stub = ["stub"] | ||||
|     data = expr(_E(stub)).data | ||||
|     try: | ||||
|         (a, b, c) = data  # simplified syntax | ||||
|         if a is stub and b == "__mul__" and isinstance(c, numbers.Number): | ||||
|             return c, 0.0 | ||||
|         if a is stub and b == "__add__" and isinstance(c, numbers.Number): | ||||
|             return 1.0, c | ||||
|     except TypeError: | ||||
|         pass | ||||
|     try: | ||||
|         ((a, b, c), d, e) = data  # full syntax | ||||
|         if ( | ||||
|             a is stub | ||||
|             and b == "__mul__" | ||||
|             and isinstance(c, numbers.Number) | ||||
|             and d == "__add__" | ||||
|             and isinstance(e, numbers.Number) | ||||
|         ): | ||||
|             return c, e | ||||
|     except TypeError: | ||||
|         pass | ||||
|     raise ValueError("illegal expression") | ||||
|     a = expr(_E(1, 0)) | ||||
|     return (a.scale, a.data) if isinstance(a, _E) else (0, a) | ||||
| 
 | ||||
| 
 | ||||
| # -------------------------------------------------------------------- | ||||
|  | @ -544,8 +549,10 @@ class Image: | |||
| 
 | ||||
|     def __exit__(self, *args): | ||||
|         if hasattr(self, "fp") and getattr(self, "_exclusive_fp", False): | ||||
|             if hasattr(self, "_close__fp"): | ||||
|                 self._close__fp() | ||||
|             if getattr(self, "_fp", False): | ||||
|                 if self._fp != self.fp: | ||||
|                     self._fp.close() | ||||
|                 self._fp = DeferredError(ValueError("Operation on closed image")) | ||||
|             if self.fp: | ||||
|                 self.fp.close() | ||||
|         self.fp = None | ||||
|  | @ -563,8 +570,10 @@ class Image: | |||
|         more information. | ||||
|         """ | ||||
|         try: | ||||
|             if hasattr(self, "_close__fp"): | ||||
|                 self._close__fp() | ||||
|             if getattr(self, "_fp", False): | ||||
|                 if self._fp != self.fp: | ||||
|                     self._fp.close() | ||||
|                 self._fp = DeferredError(ValueError("Operation on closed image")) | ||||
|             if self.fp: | ||||
|                 self.fp.close() | ||||
|             self.fp = None | ||||
|  | @ -1324,7 +1333,7 @@ class Image: | |||
| 
 | ||||
|     def getextrema(self): | ||||
|         """ | ||||
|         Gets the the minimum and maximum pixel values for each band in | ||||
|         Gets the minimum and maximum pixel values for each band in | ||||
|         the image. | ||||
| 
 | ||||
|         :returns: For a single-band image, a 2-tuple containing the | ||||
|  |  | |||
|  | @ -711,8 +711,13 @@ class FreeTypeFont: | |||
| 
 | ||||
|         :return: A FreeTypeFont object. | ||||
|         """ | ||||
|         if font is None: | ||||
|             try: | ||||
|                 font = BytesIO(self.font_bytes) | ||||
|             except AttributeError: | ||||
|                 font = self.path | ||||
|         return FreeTypeFont( | ||||
|             font=self.path if font is None else font, | ||||
|             font=font, | ||||
|             size=self.size if size is None else size, | ||||
|             index=self.index if index is None else index, | ||||
|             encoding=self.encoding if encoding is None else encoding, | ||||
|  |  | |||
|  | @ -62,7 +62,6 @@ class MicImageFile(TiffImagePlugin.TiffImageFile): | |||
|         if not self.images: | ||||
|             raise SyntaxError("not an MIC file; no image entries") | ||||
| 
 | ||||
|         self.__fp = self.fp | ||||
|         self.frame = None | ||||
|         self._n_frames = len(self.images) | ||||
|         self.is_animated = self._n_frames > 1 | ||||
|  | @ -89,15 +88,6 @@ class MicImageFile(TiffImagePlugin.TiffImageFile): | |||
|     def tell(self): | ||||
|         return self.frame | ||||
| 
 | ||||
|     def _close__fp(self): | ||||
|         try: | ||||
|             if self.__fp != self.fp: | ||||
|                 self.__fp.close() | ||||
|         except AttributeError: | ||||
|             pass | ||||
|         finally: | ||||
|             self.__fp = None | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # -------------------------------------------------------------------- | ||||
|  |  | |||
|  | @ -58,20 +58,20 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile): | |||
|         assert self.n_frames == len(self.__mpoffsets) | ||||
|         del self.info["mpoffset"]  # no longer needed | ||||
|         self.is_animated = self.n_frames > 1 | ||||
|         self.__fp = self.fp  # FIXME: hack | ||||
|         self.__fp.seek(self.__mpoffsets[0])  # get ready to read first frame | ||||
|         self._fp = self.fp  # FIXME: hack | ||||
|         self._fp.seek(self.__mpoffsets[0])  # get ready to read first frame | ||||
|         self.__frame = 0 | ||||
|         self.offset = 0 | ||||
|         # for now we can only handle reading and individual frame extraction | ||||
|         self.readonly = 1 | ||||
| 
 | ||||
|     def load_seek(self, pos): | ||||
|         self.__fp.seek(pos) | ||||
|         self._fp.seek(pos) | ||||
| 
 | ||||
|     def seek(self, frame): | ||||
|         if not self._seek_check(frame): | ||||
|             return | ||||
|         self.fp = self.__fp | ||||
|         self.fp = self._fp | ||||
|         self.offset = self.__mpoffsets[frame] | ||||
| 
 | ||||
|         self.fp.seek(self.offset + 2)  # skip SOI marker | ||||
|  | @ -97,15 +97,6 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile): | |||
|     def tell(self): | ||||
|         return self.__frame | ||||
| 
 | ||||
|     def _close__fp(self): | ||||
|         try: | ||||
|             if self.__fp != self.fp: | ||||
|                 self.__fp.close() | ||||
|         except AttributeError: | ||||
|             pass | ||||
|         finally: | ||||
|             self.__fp = None | ||||
| 
 | ||||
|     @staticmethod | ||||
|     def adopt(jpeg_instance, mpheader=None): | ||||
|         """ | ||||
|  |  | |||
|  | @ -710,7 +710,7 @@ class PngImageFile(ImageFile.ImageFile): | |||
| 
 | ||||
|         if not _accept(self.fp.read(8)): | ||||
|             raise SyntaxError("not a PNG file") | ||||
|         self.__fp = self.fp | ||||
|         self._fp = self.fp | ||||
|         self.__frame = 0 | ||||
| 
 | ||||
|         # | ||||
|  | @ -767,7 +767,7 @@ class PngImageFile(ImageFile.ImageFile): | |||
|             self._close_exclusive_fp_after_loading = False | ||||
|             self.png.save_rewind() | ||||
|             self.__rewind_idat = self.__prepare_idat | ||||
|             self.__rewind = self.__fp.tell() | ||||
|             self.__rewind = self._fp.tell() | ||||
|             if self.default_image: | ||||
|                 # IDAT chunk contains default image and not first animation frame | ||||
|                 self.n_frames += 1 | ||||
|  | @ -822,7 +822,7 @@ class PngImageFile(ImageFile.ImageFile): | |||
|     def _seek(self, frame, rewind=False): | ||||
|         if frame == 0: | ||||
|             if rewind: | ||||
|                 self.__fp.seek(self.__rewind) | ||||
|                 self._fp.seek(self.__rewind) | ||||
|                 self.png.rewind() | ||||
|                 self.__prepare_idat = self.__rewind_idat | ||||
|                 self.im = None | ||||
|  | @ -830,7 +830,7 @@ class PngImageFile(ImageFile.ImageFile): | |||
|                     self.pyaccess = None | ||||
|                 self.info = self.png.im_info | ||||
|                 self.tile = self.png.im_tile | ||||
|                 self.fp = self.__fp | ||||
|                 self.fp = self._fp | ||||
|             self._prev_im = None | ||||
|             self.dispose = None | ||||
|             self.default_image = self.info.get("default_image", False) | ||||
|  | @ -849,7 +849,7 @@ class PngImageFile(ImageFile.ImageFile): | |||
|                 self.im.paste(self.dispose, self.dispose_extent) | ||||
|             self._prev_im = self.im.copy() | ||||
| 
 | ||||
|             self.fp = self.__fp | ||||
|             self.fp = self._fp | ||||
| 
 | ||||
|             # advance to the next frame | ||||
|             if self.__prepare_idat: | ||||
|  | @ -1027,15 +1027,6 @@ class PngImageFile(ImageFile.ImageFile): | |||
|             else {} | ||||
|         ) | ||||
| 
 | ||||
|     def _close__fp(self): | ||||
|         try: | ||||
|             if self.__fp != self.fp: | ||||
|                 self.__fp.close() | ||||
|         except AttributeError: | ||||
|             pass | ||||
|         finally: | ||||
|             self.__fp = None | ||||
| 
 | ||||
| 
 | ||||
| # -------------------------------------------------------------------- | ||||
| # PNG writer | ||||
|  |  | |||
|  | @ -132,7 +132,7 @@ class PsdImageFile(ImageFile.ImageFile): | |||
|         self.tile = _maketile(self.fp, mode, (0, 0) + self.size, channels) | ||||
| 
 | ||||
|         # keep the file open | ||||
|         self.__fp = self.fp | ||||
|         self._fp = self.fp | ||||
|         self.frame = 1 | ||||
|         self._min_frame = 1 | ||||
| 
 | ||||
|  | @ -146,7 +146,7 @@ class PsdImageFile(ImageFile.ImageFile): | |||
|             self.mode = mode | ||||
|             self.tile = tile | ||||
|             self.frame = layer | ||||
|             self.fp = self.__fp | ||||
|             self.fp = self._fp | ||||
|             return name, bbox | ||||
|         except IndexError as e: | ||||
|             raise EOFError("no such layer") from e | ||||
|  | @ -155,15 +155,6 @@ class PsdImageFile(ImageFile.ImageFile): | |||
|         # return layer number (0=image, 1..max=layers) | ||||
|         return self.frame | ||||
| 
 | ||||
|     def _close__fp(self): | ||||
|         try: | ||||
|             if self.__fp != self.fp: | ||||
|                 self.__fp.close() | ||||
|         except AttributeError: | ||||
|             pass | ||||
|         finally: | ||||
|             self.__fp = None | ||||
| 
 | ||||
| 
 | ||||
| def _layerinfo(fp, ct_bytes): | ||||
|     # read layerinfo block | ||||
|  |  | |||
|  | @ -15,7 +15,7 @@ | |||
| # | ||||
| 
 | ||||
| ## | ||||
| # Image plugin for the Spider image format.  This format is is used | ||||
| # Image plugin for the Spider image format. This format is used | ||||
| # by the SPIDER software, in processing image data from electron | ||||
| # microscopy and tomography. | ||||
| ## | ||||
|  | @ -149,7 +149,7 @@ class SpiderImageFile(ImageFile.ImageFile): | |||
|         self.mode = "F" | ||||
| 
 | ||||
|         self.tile = [("raw", (0, 0) + self.size, offset, (self.rawmode, 0, 1))] | ||||
|         self.__fp = self.fp  # FIXME: hack | ||||
|         self._fp = self.fp  # FIXME: hack | ||||
| 
 | ||||
|     @property | ||||
|     def n_frames(self): | ||||
|  | @ -172,7 +172,7 @@ class SpiderImageFile(ImageFile.ImageFile): | |||
|         if not self._seek_check(frame): | ||||
|             return | ||||
|         self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes) | ||||
|         self.fp = self.__fp | ||||
|         self.fp = self._fp | ||||
|         self.fp.seek(self.stkoffset) | ||||
|         self._open() | ||||
| 
 | ||||
|  | @ -191,15 +191,6 @@ class SpiderImageFile(ImageFile.ImageFile): | |||
| 
 | ||||
|         return ImageTk.PhotoImage(self.convert2byte(), palette=256) | ||||
| 
 | ||||
|     def _close__fp(self): | ||||
|         try: | ||||
|             if self.__fp != self.fp: | ||||
|                 self.__fp.close() | ||||
|         except AttributeError: | ||||
|             pass | ||||
|         finally: | ||||
|             self.__fp = None | ||||
| 
 | ||||
| 
 | ||||
| # -------------------------------------------------------------------- | ||||
| # Image series | ||||
|  |  | |||
|  | @ -1073,7 +1073,7 @@ class TiffImageFile(ImageFile.ImageFile): | |||
|         # setup frame pointers | ||||
|         self.__first = self.__next = self.tag_v2.next | ||||
|         self.__frame = -1 | ||||
|         self.__fp = self.fp | ||||
|         self._fp = self.fp | ||||
|         self._frame_pos = [] | ||||
|         self._n_frames = None | ||||
| 
 | ||||
|  | @ -1106,7 +1106,7 @@ class TiffImageFile(ImageFile.ImageFile): | |||
|         self.im = Image.core.new(self.mode, self.size) | ||||
| 
 | ||||
|     def _seek(self, frame): | ||||
|         self.fp = self.__fp | ||||
|         self.fp = self._fp | ||||
| 
 | ||||
|         # reset buffered io handle in case fp | ||||
|         # was passed to libtiff, invalidating the buffer | ||||
|  | @ -1515,15 +1515,6 @@ class TiffImageFile(ImageFile.ImageFile): | |||
| 
 | ||||
|         self._tile_orientation = self.tag_v2.get(0x0112) | ||||
| 
 | ||||
|     def _close__fp(self): | ||||
|         try: | ||||
|             if self.__fp != self.fp: | ||||
|                 self.__fp.close() | ||||
|         except AttributeError: | ||||
|             pass | ||||
|         finally: | ||||
|             self.__fp = None | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # -------------------------------------------------------------------- | ||||
|  | @ -1568,7 +1559,13 @@ def _save(im, fp, filename): | |||
| 
 | ||||
|     encoderinfo = im.encoderinfo | ||||
|     encoderconfig = im.encoderconfig | ||||
|     compression = encoderinfo.get("compression", im.info.get("compression")) | ||||
|     try: | ||||
|         compression = encoderinfo["compression"] | ||||
|     except KeyError: | ||||
|         compression = im.info.get("compression") | ||||
|         if isinstance(compression, int): | ||||
|             # compression value may be from BMP. Ignore it | ||||
|             compression = None | ||||
|     if compression is None: | ||||
|         compression = "raw" | ||||
|     elif compression == "tiff_jpeg": | ||||
|  |  | |||
|  | @ -125,7 +125,7 @@ ImagingGifDecode(Imaging im, ImagingCodecState state, UINT8 *buffer, Py_ssize_t | |||
| 
 | ||||
|                     context->blocksize--; | ||||
| 
 | ||||
|                     /* New bits are shifted in from from the left. */ | ||||
|                     /* New bits are shifted in from the left. */ | ||||
|                     context->bitbuffer |= (INT32)c << context->bitcount; | ||||
|                     context->bitcount += 8; | ||||
| 
 | ||||
|  |  | |||
|  | @ -1519,7 +1519,7 @@ error_0: | |||
| 
 | ||||
| typedef struct { | ||||
|     Pixel new; | ||||
|     Pixel furthest; | ||||
|     uint32_t furthestV; | ||||
|     uint32_t furthestDistance; | ||||
|     int secondPixel; | ||||
| } DistanceData; | ||||
|  | @ -1536,7 +1536,7 @@ compute_distances(const HashTable *h, const Pixel pixel, uint32_t *dist, void *u | |||
|     } | ||||
|     if (oldDist > data->furthestDistance) { | ||||
|         data->furthestDistance = oldDist; | ||||
|         data->furthest.v = pixel.v; | ||||
|         data->furthestV = pixel.v; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -1577,10 +1577,11 @@ quantize2( | |||
|     data.new.c.b = (int)(.5 + (double)mean[2] / (double)nPixels); | ||||
|     for (i = 0; i < nQuantPixels; i++) { | ||||
|         data.furthestDistance = 0; | ||||
|         data.furthestV = pixelData[0].v; | ||||
|         data.secondPixel = (i == 1) ? 1 : 0; | ||||
|         hashtable_foreach_update(h, compute_distances, &data); | ||||
|         p[i].v = data.furthest.v; | ||||
|         data.new.v = data.furthest.v; | ||||
|         p[i].v = data.furthestV; | ||||
|         data.new.v = data.furthestV; | ||||
|     } | ||||
|     hashtable_free(h); | ||||
| 
 | ||||
|  |  | |||
|  | @ -120,6 +120,7 @@ ImagingTgaRleDecode(Imaging im, ImagingCodecState state, UINT8 *buf, Py_ssize_t | |||
|             } | ||||
|             memcpy(state->buffer + state->x, ptr, n); | ||||
|             ptr += n; | ||||
|             bytes -= n; | ||||
|             extra_bytes -= n; | ||||
|         } | ||||
|     } | ||||
|  |  | |||
|  | @ -246,15 +246,15 @@ deps = { | |||
|         "libs": [r"Lib\MS\*.lib"], | ||||
|     }, | ||||
|     "openjpeg": { | ||||
|         "url": "https://github.com/uclouvain/openjpeg/archive/v2.4.0.tar.gz", | ||||
|         "filename": "openjpeg-2.4.0.tar.gz", | ||||
|         "dir": "openjpeg-2.4.0", | ||||
|         "url": "https://github.com/uclouvain/openjpeg/archive/v2.5.0.tar.gz", | ||||
|         "filename": "openjpeg-2.5.0.tar.gz", | ||||
|         "dir": "openjpeg-2.5.0", | ||||
|         "build": [ | ||||
|             cmd_cmake(("-DBUILD_THIRDPARTY:BOOL=OFF", "-DBUILD_SHARED_LIBS:BOOL=OFF")), | ||||
|             cmd_nmake(target="clean"), | ||||
|             cmd_nmake(target="openjp2"), | ||||
|             cmd_mkdir(r"{inc_dir}\openjpeg-2.4.0"), | ||||
|             cmd_copy(r"src\lib\openjp2\*.h", r"{inc_dir}\openjpeg-2.4.0"), | ||||
|             cmd_mkdir(r"{inc_dir}\openjpeg-2.5.0"), | ||||
|             cmd_copy(r"src\lib\openjp2\*.h", r"{inc_dir}\openjpeg-2.5.0"), | ||||
|         ], | ||||
|         "libs": [r"bin\*.lib"], | ||||
|     }, | ||||
|  | @ -280,9 +280,9 @@ deps = { | |||
|         "libs": [r"imagequant.lib"], | ||||
|     }, | ||||
|     "harfbuzz": { | ||||
|         "url": "https://github.com/harfbuzz/harfbuzz/archive/4.2.1.zip", | ||||
|         "filename": "harfbuzz-4.2.1.zip", | ||||
|         "dir": "harfbuzz-4.2.1", | ||||
|         "url": "https://github.com/harfbuzz/harfbuzz/archive/4.3.0.zip", | ||||
|         "filename": "harfbuzz-4.3.0.zip", | ||||
|         "dir": "harfbuzz-4.3.0", | ||||
|         "build": [ | ||||
|             cmd_cmake("-DHB_HAVE_FREETYPE:BOOL=TRUE"), | ||||
|             cmd_nmake(target="clean"), | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user