mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-11-04 01:47:47 +03:00 
			
		
		
		
	Merge branch 'main' into fix-imagegrab-with-wl-paste
This commit is contained in:
		
						commit
						c656583b84
					
				
							
								
								
									
										2
									
								
								.github/workflows/test-cygwin.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/test-cygwin.yml
									
									
									
									
										vendored
									
									
								
							| 
						 | 
					@ -39,7 +39,7 @@ jobs:
 | 
				
			||||||
        uses: actions/checkout@v3
 | 
					        uses: actions/checkout@v3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - name: Install Cygwin
 | 
					      - name: Install Cygwin
 | 
				
			||||||
        uses: cygwin/cygwin-install-action@v3
 | 
					        uses: cygwin/cygwin-install-action@v4
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          platform: x86_64
 | 
					          platform: x86_64
 | 
				
			||||||
          packages: >
 | 
					          packages: >
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										3
									
								
								.github/workflows/test-docker.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.github/workflows/test-docker.yml
									
									
									
									
										vendored
									
									
								
							| 
						 | 
					@ -39,10 +39,9 @@ jobs:
 | 
				
			||||||
          centos-stream-8-amd64,
 | 
					          centos-stream-8-amd64,
 | 
				
			||||||
          centos-stream-9-amd64,
 | 
					          centos-stream-9-amd64,
 | 
				
			||||||
          debian-11-bullseye-x86,
 | 
					          debian-11-bullseye-x86,
 | 
				
			||||||
          fedora-36-amd64,
 | 
					 | 
				
			||||||
          fedora-37-amd64,
 | 
					          fedora-37-amd64,
 | 
				
			||||||
 | 
					          fedora-38-amd64,
 | 
				
			||||||
          gentoo,
 | 
					          gentoo,
 | 
				
			||||||
          ubuntu-18.04-bionic-amd64,
 | 
					 | 
				
			||||||
          ubuntu-20.04-focal-amd64,
 | 
					          ubuntu-20.04-focal-amd64,
 | 
				
			||||||
          ubuntu-22.04-jammy-amd64,
 | 
					          ubuntu-22.04-jammy-amd64,
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										2
									
								
								.github/workflows/test-mingw.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/test-mingw.yml
									
									
									
									
										vendored
									
									
								
							| 
						 | 
					@ -80,7 +80,7 @@ jobs:
 | 
				
			||||||
          pushd depends && ./install_extra_test_images.sh && popd
 | 
					          pushd depends && ./install_extra_test_images.sh && popd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - name: Build Pillow
 | 
					      - name: Build Pillow
 | 
				
			||||||
        run: CFLAGS="-coverage" python3 -m pip install --global-option="build_ext" .
 | 
					        run: SETUPTOOLS_USE_DISTUTILS="stdlib" CFLAGS="-coverage" python3 -m pip install --global-option="build_ext" .
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - name: Test Pillow
 | 
					      - name: Test Pillow
 | 
				
			||||||
        run: |
 | 
					        run: |
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -57,7 +57,7 @@ repos:
 | 
				
			||||||
      - id: sphinx-lint
 | 
					      - id: sphinx-lint
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  - repo: https://github.com/tox-dev/tox-ini-fmt
 | 
					  - repo: https://github.com/tox-dev/tox-ini-fmt
 | 
				
			||||||
    rev: 1.0.0
 | 
					    rev: 1.3.0
 | 
				
			||||||
    hooks:
 | 
					    hooks:
 | 
				
			||||||
      - id: tox-ini-fmt
 | 
					      - id: tox-ini-fmt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,7 @@
 | 
				
			||||||
version: 2
 | 
					version: 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					formats: [pdf]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
build:
 | 
					build:
 | 
				
			||||||
  os: ubuntu-22.04
 | 
					  os: ubuntu-22.04
 | 
				
			||||||
  tools:
 | 
					  tools:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										36
									
								
								CHANGES.rst
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								CHANGES.rst
									
									
									
									
									
								
							| 
						 | 
					@ -5,6 +5,42 @@ Changelog (Pillow)
 | 
				
			||||||
10.0.0 (unreleased)
 | 
					10.0.0 (unreleased)
 | 
				
			||||||
-------------------
 | 
					-------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Added _repr_jpeg_() for IPython display_jpeg #7135
 | 
				
			||||||
 | 
					  [n3011, radarhere, nulano]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Use "/sbin/ldconfig" if ldconfig is not found #7068
 | 
				
			||||||
 | 
					  [radarhere]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Prefer screenshots using XCB over gnome-screenshot #7143
 | 
				
			||||||
 | 
					  [nulano, radarhere]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Fixed joined corners for ImageDraw rounded_rectangle() odd dimensions #7151
 | 
				
			||||||
 | 
					  [radarhere]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Support reading signed 8-bit TIFF images #7111
 | 
				
			||||||
 | 
					  [radarhere]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Added width argument to ImageDraw regular_polygon #7132
 | 
				
			||||||
 | 
					  [radarhere]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Support I mode for ImageFilter.BuiltinFilter #7108
 | 
				
			||||||
 | 
					  [radarhere]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Raise error from stderr of Linux ImageGrab.grabclipboard() command #7112
 | 
				
			||||||
 | 
					  [radarhere]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Added unpacker from I;16B to I;16 #7125
 | 
				
			||||||
 | 
					  [radarhere]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Support float font sizes #7107
 | 
				
			||||||
 | 
					  [radarhere]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Use later value for duplicate xref entries in PdfParser #7102
 | 
				
			||||||
 | 
					  [radarhere]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Load before getting size in __getstate__ #7105
 | 
				
			||||||
 | 
					  [bigcat88, radarhere]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Fixed type handling for include and lib directories #7069
 | 
					- Fixed type handling for include and lib directories #7069
 | 
				
			||||||
  [adisbladis, radarhere]
 | 
					  [adisbladis, radarhere]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -75,43 +75,42 @@ post-patch:
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_qtables_leak():
 | 
					standard_l_qtable = (
 | 
				
			||||||
 | 
					    # fmt: off
 | 
				
			||||||
 | 
					    16, 11, 10, 16,  24,  40,  51,  61,
 | 
				
			||||||
 | 
					    12, 12, 14, 19,  26,  58,  60,  55,
 | 
				
			||||||
 | 
					    14, 13, 16, 24,  40,  57,  69,  56,
 | 
				
			||||||
 | 
					    14, 17, 22, 29,  51,  87,  80,  62,
 | 
				
			||||||
 | 
					    18, 22, 37, 56,  68, 109, 103,  77,
 | 
				
			||||||
 | 
					    24, 35, 55, 64,  81, 104, 113,  92,
 | 
				
			||||||
 | 
					    49, 64, 78, 87, 103, 121, 120, 101,
 | 
				
			||||||
 | 
					    72, 92, 95, 98, 112, 100, 103,  99,
 | 
				
			||||||
 | 
					    # fmt: on
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					standard_chrominance_qtable = (
 | 
				
			||||||
 | 
					    # fmt: off
 | 
				
			||||||
 | 
					    17, 18, 24, 47, 99, 99, 99, 99,
 | 
				
			||||||
 | 
					    18, 21, 26, 66, 99, 99, 99, 99,
 | 
				
			||||||
 | 
					    24, 26, 56, 99, 99, 99, 99, 99,
 | 
				
			||||||
 | 
					    47, 66, 99, 99, 99, 99, 99, 99,
 | 
				
			||||||
 | 
					    99, 99, 99, 99, 99, 99, 99, 99,
 | 
				
			||||||
 | 
					    99, 99, 99, 99, 99, 99, 99, 99,
 | 
				
			||||||
 | 
					    99, 99, 99, 99, 99, 99, 99, 99,
 | 
				
			||||||
 | 
					    99, 99, 99, 99, 99, 99, 99, 99,
 | 
				
			||||||
 | 
					    # fmt: on
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@pytest.mark.parametrize(
 | 
				
			||||||
 | 
					    "qtables",
 | 
				
			||||||
 | 
					    (
 | 
				
			||||||
 | 
					        (standard_l_qtable, standard_chrominance_qtable),
 | 
				
			||||||
 | 
					        [standard_l_qtable, standard_chrominance_qtable],
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					def test_qtables_leak(qtables):
 | 
				
			||||||
    im = hopper("RGB")
 | 
					    im = hopper("RGB")
 | 
				
			||||||
 | 
					 | 
				
			||||||
    standard_l_qtable = [
 | 
					 | 
				
			||||||
        int(s)
 | 
					 | 
				
			||||||
        for s in """
 | 
					 | 
				
			||||||
        16  11  10  16  24  40  51  61
 | 
					 | 
				
			||||||
        12  12  14  19  26  58  60  55
 | 
					 | 
				
			||||||
        14  13  16  24  40  57  69  56
 | 
					 | 
				
			||||||
        14  17  22  29  51  87  80  62
 | 
					 | 
				
			||||||
        18  22  37  56  68 109 103  77
 | 
					 | 
				
			||||||
        24  35  55  64  81 104 113  92
 | 
					 | 
				
			||||||
        49  64  78  87 103 121 120 101
 | 
					 | 
				
			||||||
        72  92  95  98 112 100 103  99
 | 
					 | 
				
			||||||
        """.split(
 | 
					 | 
				
			||||||
            None
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
    ]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    standard_chrominance_qtable = [
 | 
					 | 
				
			||||||
        int(s)
 | 
					 | 
				
			||||||
        for s in """
 | 
					 | 
				
			||||||
        17  18  24  47  99  99  99  99
 | 
					 | 
				
			||||||
        18  21  26  66  99  99  99  99
 | 
					 | 
				
			||||||
        24  26  56  99  99  99  99  99
 | 
					 | 
				
			||||||
        47  66  99  99  99  99  99  99
 | 
					 | 
				
			||||||
        99  99  99  99  99  99  99  99
 | 
					 | 
				
			||||||
        99  99  99  99  99  99  99  99
 | 
					 | 
				
			||||||
        99  99  99  99  99  99  99  99
 | 
					 | 
				
			||||||
        99  99  99  99  99  99  99  99
 | 
					 | 
				
			||||||
        """.split(
 | 
					 | 
				
			||||||
            None
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
    ]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    qtables = [standard_l_qtable, standard_chrominance_qtable]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for _ in range(iterations):
 | 
					    for _ in range(iterations):
 | 
				
			||||||
        test_output = BytesIO()
 | 
					        test_output = BytesIO()
 | 
				
			||||||
        im.save(test_output, "JPEG", qtables=qtables)
 | 
					        im.save(test_output, "JPEG", qtables=qtables)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								Tests/images/8bit.s.tif
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Tests/images/8bit.s.tif
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								Tests/images/duplicate_xref_entry.pdf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Tests/images/duplicate_xref_entry.pdf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								Tests/images/hopper_emboss_I.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Tests/images/hopper_emboss_I.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 13 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Tests/images/hopper_emboss_more_I.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Tests/images/hopper_emboss_more_I.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 14 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Tests/images/imagedraw_rounded_rectangle_x_odd.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Tests/images/imagedraw_rounded_rectangle_x_odd.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 565 B  | 
							
								
								
									
										
											BIN
										
									
								
								Tests/images/imagedraw_rounded_rectangle_y_odd.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Tests/images/imagedraw_rounded_rectangle_y_odd.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 527 B  | 
							
								
								
									
										
											BIN
										
									
								
								Tests/images/imagedraw_triangle_width.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Tests/images/imagedraw_triangle_width.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 499 B  | 
| 
						 | 
					@ -922,6 +922,19 @@ class TestFileJpeg:
 | 
				
			||||||
            im.load()
 | 
					            im.load()
 | 
				
			||||||
            ImageFile.LOAD_TRUNCATED_IMAGES = False
 | 
					            ImageFile.LOAD_TRUNCATED_IMAGES = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_repr_jpeg(self):
 | 
				
			||||||
 | 
					        im = hopper()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with Image.open(BytesIO(im._repr_jpeg_())) as repr_jpeg:
 | 
				
			||||||
 | 
					            assert repr_jpeg.format == "JPEG"
 | 
				
			||||||
 | 
					            assert_image_similar(im, repr_jpeg, 17)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_repr_jpeg_error(self):
 | 
				
			||||||
 | 
					        im = hopper("F")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with pytest.raises(ValueError):
 | 
				
			||||||
 | 
					            im._repr_jpeg_()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.mark.skipif(not is_win32(), reason="Windows only")
 | 
					@pytest.mark.skipif(not is_win32(), reason="Windows only")
 | 
				
			||||||
@skip_unless_feature("jpg")
 | 
					@skip_unless_feature("jpg")
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -198,6 +198,12 @@ class TestFileTiff:
 | 
				
			||||||
        with pytest.raises(OSError):
 | 
					        with pytest.raises(OSError):
 | 
				
			||||||
            im.save(outfile)
 | 
					            im.save(outfile)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_8bit_s(self):
 | 
				
			||||||
 | 
					        with Image.open("Tests/images/8bit.s.tif") as im:
 | 
				
			||||||
 | 
					            im.load()
 | 
				
			||||||
 | 
					            assert im.mode == "L"
 | 
				
			||||||
 | 
					            assert im.getpixel((50, 50)) == 184
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_little_endian(self):
 | 
					    def test_little_endian(self):
 | 
				
			||||||
        with Image.open("Tests/images/16bit.cropped.tif") as im:
 | 
					        with Image.open("Tests/images/16bit.cropped.tif") as im:
 | 
				
			||||||
            assert im.getpixel((0, 0)) == 480
 | 
					            assert im.getpixel((0, 0)) == 480
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,7 @@ import pytest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from PIL import Image
 | 
					from PIL import Image
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .helper import hopper
 | 
					from .helper import hopper, skip_unless_feature
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.mark.parametrize("mode", ("1", "P", "L", "RGB", "I", "F"))
 | 
					@pytest.mark.parametrize("mode", ("1", "P", "L", "RGB", "I", "F"))
 | 
				
			||||||
| 
						 | 
					@ -42,3 +42,10 @@ def test_copy_zero():
 | 
				
			||||||
    out = im.copy()
 | 
					    out = im.copy()
 | 
				
			||||||
    assert out.mode == im.mode
 | 
					    assert out.mode == im.mode
 | 
				
			||||||
    assert out.size == im.size
 | 
					    assert out.size == im.size
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@skip_unless_feature("libtiff")
 | 
				
			||||||
 | 
					def test_deepcopy():
 | 
				
			||||||
 | 
					    with Image.open("Tests/images/g4_orientation_5.tif") as im:
 | 
				
			||||||
 | 
					        out = copy.deepcopy(im)
 | 
				
			||||||
 | 
					    assert out.size == (590, 88)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,15 +30,16 @@ from .helper import assert_image_equal, hopper
 | 
				
			||||||
        ImageFilter.UnsharpMask(10),
 | 
					        ImageFilter.UnsharpMask(10),
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@pytest.mark.parametrize("mode", ("L", "RGB", "CMYK"))
 | 
					@pytest.mark.parametrize("mode", ("L", "I", "RGB", "CMYK"))
 | 
				
			||||||
def test_sanity(filter_to_apply, mode):
 | 
					def test_sanity(filter_to_apply, mode):
 | 
				
			||||||
    im = hopper(mode)
 | 
					    im = hopper(mode)
 | 
				
			||||||
    out = im.filter(filter_to_apply)
 | 
					    if mode != "I" or isinstance(filter_to_apply, ImageFilter.BuiltinFilter):
 | 
				
			||||||
    assert out.mode == im.mode
 | 
					        out = im.filter(filter_to_apply)
 | 
				
			||||||
    assert out.size == im.size
 | 
					        assert out.mode == im.mode
 | 
				
			||||||
 | 
					        assert out.size == im.size
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.mark.parametrize("mode", ("L", "RGB", "CMYK"))
 | 
					@pytest.mark.parametrize("mode", ("L", "I", "RGB", "CMYK"))
 | 
				
			||||||
def test_sanity_error(mode):
 | 
					def test_sanity_error(mode):
 | 
				
			||||||
    with pytest.raises(TypeError):
 | 
					    with pytest.raises(TypeError):
 | 
				
			||||||
        im = hopper(mode)
 | 
					        im = hopper(mode)
 | 
				
			||||||
| 
						 | 
					@ -130,10 +131,12 @@ def test_kernel_not_enough_coefficients():
 | 
				
			||||||
        ImageFilter.Kernel((3, 3), (0, 0))
 | 
					        ImageFilter.Kernel((3, 3), (0, 0))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.mark.parametrize("mode", ("L", "LA", "RGB", "CMYK"))
 | 
					@pytest.mark.parametrize("mode", ("L", "LA", "I", "RGB", "CMYK"))
 | 
				
			||||||
def test_consistency_3x3(mode):
 | 
					def test_consistency_3x3(mode):
 | 
				
			||||||
    with Image.open("Tests/images/hopper.bmp") as source:
 | 
					    with Image.open("Tests/images/hopper.bmp") as source:
 | 
				
			||||||
        with Image.open("Tests/images/hopper_emboss.bmp") as reference:
 | 
					        reference_name = "hopper_emboss"
 | 
				
			||||||
 | 
					        reference_name += "_I.png" if mode == "I" else ".bmp"
 | 
				
			||||||
 | 
					        with Image.open("Tests/images/" + reference_name) as reference:
 | 
				
			||||||
            kernel = ImageFilter.Kernel(
 | 
					            kernel = ImageFilter.Kernel(
 | 
				
			||||||
                (3, 3),
 | 
					                (3, 3),
 | 
				
			||||||
                # fmt: off
 | 
					                # fmt: off
 | 
				
			||||||
| 
						 | 
					@ -146,16 +149,20 @@ def test_consistency_3x3(mode):
 | 
				
			||||||
            source = source.split() * 2
 | 
					            source = source.split() * 2
 | 
				
			||||||
            reference = reference.split() * 2
 | 
					            reference = reference.split() * 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            assert_image_equal(
 | 
					            if mode == "I":
 | 
				
			||||||
                Image.merge(mode, source[: len(mode)]).filter(kernel),
 | 
					                source = source[0].convert(mode)
 | 
				
			||||||
                Image.merge(mode, reference[: len(mode)]),
 | 
					            else:
 | 
				
			||||||
            )
 | 
					                source = Image.merge(mode, source[: len(mode)])
 | 
				
			||||||
 | 
					            reference = Image.merge(mode, reference[: len(mode)])
 | 
				
			||||||
 | 
					            assert_image_equal(source.filter(kernel), reference)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.mark.parametrize("mode", ("L", "LA", "RGB", "CMYK"))
 | 
					@pytest.mark.parametrize("mode", ("L", "LA", "I", "RGB", "CMYK"))
 | 
				
			||||||
def test_consistency_5x5(mode):
 | 
					def test_consistency_5x5(mode):
 | 
				
			||||||
    with Image.open("Tests/images/hopper.bmp") as source:
 | 
					    with Image.open("Tests/images/hopper.bmp") as source:
 | 
				
			||||||
        with Image.open("Tests/images/hopper_emboss_more.bmp") as reference:
 | 
					        reference_name = "hopper_emboss_more"
 | 
				
			||||||
 | 
					        reference_name += "_I.png" if mode == "I" else ".bmp"
 | 
				
			||||||
 | 
					        with Image.open("Tests/images/" + reference_name) as reference:
 | 
				
			||||||
            kernel = ImageFilter.Kernel(
 | 
					            kernel = ImageFilter.Kernel(
 | 
				
			||||||
                (5, 5),
 | 
					                (5, 5),
 | 
				
			||||||
                # fmt: off
 | 
					                # fmt: off
 | 
				
			||||||
| 
						 | 
					@ -170,10 +177,12 @@ def test_consistency_5x5(mode):
 | 
				
			||||||
            source = source.split() * 2
 | 
					            source = source.split() * 2
 | 
				
			||||||
            reference = reference.split() * 2
 | 
					            reference = reference.split() * 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            assert_image_equal(
 | 
					            if mode == "I":
 | 
				
			||||||
                Image.merge(mode, source[: len(mode)]).filter(kernel),
 | 
					                source = source[0].convert(mode)
 | 
				
			||||||
                Image.merge(mode, reference[: len(mode)]),
 | 
					            else:
 | 
				
			||||||
            )
 | 
					                source = Image.merge(mode, source[: len(mode)])
 | 
				
			||||||
 | 
					            reference = Image.merge(mode, reference[: len(mode)])
 | 
				
			||||||
 | 
					            assert_image_equal(source.filter(kernel), reference)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_invalid_box_blur_filter():
 | 
					def test_invalid_box_blur_filter():
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,15 +27,21 @@ X1 = int(X0 * 3)
 | 
				
			||||||
Y0 = int(H / 4)
 | 
					Y0 = int(H / 4)
 | 
				
			||||||
Y1 = int(X0 * 3)
 | 
					Y1 = int(X0 * 3)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Two kinds of bounding box
 | 
					# Bounding boxes
 | 
				
			||||||
BBOX1 = [(X0, Y0), (X1, Y1)]
 | 
					BBOX = (((X0, Y0), (X1, Y1)), [(X0, Y0), (X1, Y1)], (X0, Y0, X1, Y1), [X0, Y0, X1, Y1])
 | 
				
			||||||
BBOX2 = [X0, Y0, X1, Y1]
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Two kinds of coordinate sequences
 | 
					# Coordinate sequences
 | 
				
			||||||
POINTS1 = [(10, 10), (20, 40), (30, 30)]
 | 
					POINTS = (
 | 
				
			||||||
POINTS2 = [10, 10, 20, 40, 30, 30]
 | 
					    ((10, 10), (20, 40), (30, 30)),
 | 
				
			||||||
 | 
					    [(10, 10), (20, 40), (30, 30)],
 | 
				
			||||||
 | 
					    (10, 10, 20, 40, 30, 30),
 | 
				
			||||||
 | 
					    [10, 10, 20, 40, 30, 30],
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
KITE_POINTS = [(10, 50), (70, 10), (90, 50), (70, 90), (10, 50)]
 | 
					KITE_POINTS = (
 | 
				
			||||||
 | 
					    ((10, 50), (70, 10), (90, 50), (70, 90), (10, 50)),
 | 
				
			||||||
 | 
					    [(10, 50), (70, 10), (90, 50), (70, 90), (10, 50)],
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_sanity():
 | 
					def test_sanity():
 | 
				
			||||||
| 
						 | 
					@ -63,7 +69,7 @@ def test_mode_mismatch():
 | 
				
			||||||
        ImageDraw.ImageDraw(im, mode="L")
 | 
					        ImageDraw.ImageDraw(im, mode="L")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.mark.parametrize("bbox", (BBOX1, BBOX2))
 | 
					@pytest.mark.parametrize("bbox", BBOX)
 | 
				
			||||||
@pytest.mark.parametrize("start, end", ((0, 180), (0.5, 180.4)))
 | 
					@pytest.mark.parametrize("start, end", ((0, 180), (0.5, 180.4)))
 | 
				
			||||||
def test_arc(bbox, start, end):
 | 
					def test_arc(bbox, start, end):
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
| 
						 | 
					@ -77,7 +83,8 @@ def test_arc(bbox, start, end):
 | 
				
			||||||
    assert_image_similar_tofile(im, "Tests/images/imagedraw_arc.png", 1)
 | 
					    assert_image_similar_tofile(im, "Tests/images/imagedraw_arc.png", 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_arc_end_le_start():
 | 
					@pytest.mark.parametrize("bbox", BBOX)
 | 
				
			||||||
 | 
					def test_arc_end_le_start(bbox):
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    im = Image.new("RGB", (W, H))
 | 
					    im = Image.new("RGB", (W, H))
 | 
				
			||||||
    draw = ImageDraw.Draw(im)
 | 
					    draw = ImageDraw.Draw(im)
 | 
				
			||||||
| 
						 | 
					@ -85,13 +92,14 @@ def test_arc_end_le_start():
 | 
				
			||||||
    end = 0
 | 
					    end = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Act
 | 
					    # Act
 | 
				
			||||||
    draw.arc(BBOX1, start=start, end=end)
 | 
					    draw.arc(bbox, start=start, end=end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Assert
 | 
					    # Assert
 | 
				
			||||||
    assert_image_equal_tofile(im, "Tests/images/imagedraw_arc_end_le_start.png")
 | 
					    assert_image_equal_tofile(im, "Tests/images/imagedraw_arc_end_le_start.png")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_arc_no_loops():
 | 
					@pytest.mark.parametrize("bbox", BBOX)
 | 
				
			||||||
 | 
					def test_arc_no_loops(bbox):
 | 
				
			||||||
    # No need to go in loops
 | 
					    # No need to go in loops
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    im = Image.new("RGB", (W, H))
 | 
					    im = Image.new("RGB", (W, H))
 | 
				
			||||||
| 
						 | 
					@ -100,57 +108,61 @@ def test_arc_no_loops():
 | 
				
			||||||
    end = 370
 | 
					    end = 370
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Act
 | 
					    # Act
 | 
				
			||||||
    draw.arc(BBOX1, start=start, end=end)
 | 
					    draw.arc(bbox, start=start, end=end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Assert
 | 
					    # Assert
 | 
				
			||||||
    assert_image_similar_tofile(im, "Tests/images/imagedraw_arc_no_loops.png", 1)
 | 
					    assert_image_similar_tofile(im, "Tests/images/imagedraw_arc_no_loops.png", 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_arc_width():
 | 
					@pytest.mark.parametrize("bbox", BBOX)
 | 
				
			||||||
 | 
					def test_arc_width(bbox):
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    im = Image.new("RGB", (W, H))
 | 
					    im = Image.new("RGB", (W, H))
 | 
				
			||||||
    draw = ImageDraw.Draw(im)
 | 
					    draw = ImageDraw.Draw(im)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Act
 | 
					    # Act
 | 
				
			||||||
    draw.arc(BBOX1, 10, 260, width=5)
 | 
					    draw.arc(bbox, 10, 260, width=5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Assert
 | 
					    # Assert
 | 
				
			||||||
    assert_image_similar_tofile(im, "Tests/images/imagedraw_arc_width.png", 1)
 | 
					    assert_image_similar_tofile(im, "Tests/images/imagedraw_arc_width.png", 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_arc_width_pieslice_large():
 | 
					@pytest.mark.parametrize("bbox", BBOX)
 | 
				
			||||||
 | 
					def test_arc_width_pieslice_large(bbox):
 | 
				
			||||||
    # Tests an arc with a large enough width that it is a pieslice
 | 
					    # Tests an arc with a large enough width that it is a pieslice
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    im = Image.new("RGB", (W, H))
 | 
					    im = Image.new("RGB", (W, H))
 | 
				
			||||||
    draw = ImageDraw.Draw(im)
 | 
					    draw = ImageDraw.Draw(im)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Act
 | 
					    # Act
 | 
				
			||||||
    draw.arc(BBOX1, 10, 260, fill="yellow", width=100)
 | 
					    draw.arc(bbox, 10, 260, fill="yellow", width=100)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Assert
 | 
					    # Assert
 | 
				
			||||||
    assert_image_similar_tofile(im, "Tests/images/imagedraw_arc_width_pieslice.png", 1)
 | 
					    assert_image_similar_tofile(im, "Tests/images/imagedraw_arc_width_pieslice.png", 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_arc_width_fill():
 | 
					@pytest.mark.parametrize("bbox", BBOX)
 | 
				
			||||||
 | 
					def test_arc_width_fill(bbox):
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    im = Image.new("RGB", (W, H))
 | 
					    im = Image.new("RGB", (W, H))
 | 
				
			||||||
    draw = ImageDraw.Draw(im)
 | 
					    draw = ImageDraw.Draw(im)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Act
 | 
					    # Act
 | 
				
			||||||
    draw.arc(BBOX1, 10, 260, fill="yellow", width=5)
 | 
					    draw.arc(bbox, 10, 260, fill="yellow", width=5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Assert
 | 
					    # Assert
 | 
				
			||||||
    assert_image_similar_tofile(im, "Tests/images/imagedraw_arc_width_fill.png", 1)
 | 
					    assert_image_similar_tofile(im, "Tests/images/imagedraw_arc_width_fill.png", 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_arc_width_non_whole_angle():
 | 
					@pytest.mark.parametrize("bbox", BBOX)
 | 
				
			||||||
 | 
					def test_arc_width_non_whole_angle(bbox):
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    im = Image.new("RGB", (W, H))
 | 
					    im = Image.new("RGB", (W, H))
 | 
				
			||||||
    draw = ImageDraw.Draw(im)
 | 
					    draw = ImageDraw.Draw(im)
 | 
				
			||||||
    expected = "Tests/images/imagedraw_arc_width_non_whole_angle.png"
 | 
					    expected = "Tests/images/imagedraw_arc_width_non_whole_angle.png"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Act
 | 
					    # Act
 | 
				
			||||||
    draw.arc(BBOX1, 10, 259.5, width=5)
 | 
					    draw.arc(bbox, 10, 259.5, width=5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Assert
 | 
					    # Assert
 | 
				
			||||||
    assert_image_similar_tofile(im, expected, 1)
 | 
					    assert_image_similar_tofile(im, expected, 1)
 | 
				
			||||||
| 
						 | 
					@ -184,7 +196,7 @@ def test_bitmap():
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.mark.parametrize("mode", ("RGB", "L"))
 | 
					@pytest.mark.parametrize("mode", ("RGB", "L"))
 | 
				
			||||||
@pytest.mark.parametrize("bbox", (BBOX1, BBOX2))
 | 
					@pytest.mark.parametrize("bbox", BBOX)
 | 
				
			||||||
def test_chord(mode, bbox):
 | 
					def test_chord(mode, bbox):
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    im = Image.new(mode, (W, H))
 | 
					    im = Image.new(mode, (W, H))
 | 
				
			||||||
| 
						 | 
					@ -198,37 +210,40 @@ def test_chord(mode, bbox):
 | 
				
			||||||
    assert_image_similar_tofile(im, expected, 1)
 | 
					    assert_image_similar_tofile(im, expected, 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_chord_width():
 | 
					@pytest.mark.parametrize("bbox", BBOX)
 | 
				
			||||||
 | 
					def test_chord_width(bbox):
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    im = Image.new("RGB", (W, H))
 | 
					    im = Image.new("RGB", (W, H))
 | 
				
			||||||
    draw = ImageDraw.Draw(im)
 | 
					    draw = ImageDraw.Draw(im)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Act
 | 
					    # Act
 | 
				
			||||||
    draw.chord(BBOX1, 10, 260, outline="yellow", width=5)
 | 
					    draw.chord(bbox, 10, 260, outline="yellow", width=5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Assert
 | 
					    # Assert
 | 
				
			||||||
    assert_image_similar_tofile(im, "Tests/images/imagedraw_chord_width.png", 1)
 | 
					    assert_image_similar_tofile(im, "Tests/images/imagedraw_chord_width.png", 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_chord_width_fill():
 | 
					@pytest.mark.parametrize("bbox", BBOX)
 | 
				
			||||||
 | 
					def test_chord_width_fill(bbox):
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    im = Image.new("RGB", (W, H))
 | 
					    im = Image.new("RGB", (W, H))
 | 
				
			||||||
    draw = ImageDraw.Draw(im)
 | 
					    draw = ImageDraw.Draw(im)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Act
 | 
					    # Act
 | 
				
			||||||
    draw.chord(BBOX1, 10, 260, fill="red", outline="yellow", width=5)
 | 
					    draw.chord(bbox, 10, 260, fill="red", outline="yellow", width=5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Assert
 | 
					    # Assert
 | 
				
			||||||
    assert_image_similar_tofile(im, "Tests/images/imagedraw_chord_width_fill.png", 1)
 | 
					    assert_image_similar_tofile(im, "Tests/images/imagedraw_chord_width_fill.png", 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_chord_zero_width():
 | 
					@pytest.mark.parametrize("bbox", BBOX)
 | 
				
			||||||
 | 
					def test_chord_zero_width(bbox):
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    im = Image.new("RGB", (W, H))
 | 
					    im = Image.new("RGB", (W, H))
 | 
				
			||||||
    draw = ImageDraw.Draw(im)
 | 
					    draw = ImageDraw.Draw(im)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Act
 | 
					    # Act
 | 
				
			||||||
    draw.chord(BBOX1, 10, 260, fill="red", outline="yellow", width=0)
 | 
					    draw.chord(bbox, 10, 260, fill="red", outline="yellow", width=0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Assert
 | 
					    # Assert
 | 
				
			||||||
    assert_image_equal_tofile(im, "Tests/images/imagedraw_chord_zero_width.png")
 | 
					    assert_image_equal_tofile(im, "Tests/images/imagedraw_chord_zero_width.png")
 | 
				
			||||||
| 
						 | 
					@ -247,7 +262,7 @@ def test_chord_too_fat():
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.mark.parametrize("mode", ("RGB", "L"))
 | 
					@pytest.mark.parametrize("mode", ("RGB", "L"))
 | 
				
			||||||
@pytest.mark.parametrize("bbox", (BBOX1, BBOX2))
 | 
					@pytest.mark.parametrize("bbox", BBOX)
 | 
				
			||||||
def test_ellipse(mode, bbox):
 | 
					def test_ellipse(mode, bbox):
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    im = Image.new(mode, (W, H))
 | 
					    im = Image.new(mode, (W, H))
 | 
				
			||||||
| 
						 | 
					@ -261,13 +276,14 @@ def test_ellipse(mode, bbox):
 | 
				
			||||||
    assert_image_similar_tofile(im, expected, 1)
 | 
					    assert_image_similar_tofile(im, expected, 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_ellipse_translucent():
 | 
					@pytest.mark.parametrize("bbox", BBOX)
 | 
				
			||||||
 | 
					def test_ellipse_translucent(bbox):
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    im = Image.new("RGB", (W, H))
 | 
					    im = Image.new("RGB", (W, H))
 | 
				
			||||||
    draw = ImageDraw.Draw(im, "RGBA")
 | 
					    draw = ImageDraw.Draw(im, "RGBA")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Act
 | 
					    # Act
 | 
				
			||||||
    draw.ellipse(BBOX1, fill=(0, 255, 0, 127))
 | 
					    draw.ellipse(bbox, fill=(0, 255, 0, 127))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Assert
 | 
					    # Assert
 | 
				
			||||||
    expected = "Tests/images/imagedraw_ellipse_translucent.png"
 | 
					    expected = "Tests/images/imagedraw_ellipse_translucent.png"
 | 
				
			||||||
| 
						 | 
					@ -297,13 +313,14 @@ def test_ellipse_symmetric():
 | 
				
			||||||
        assert_image_equal(im, im.transpose(Image.Transpose.FLIP_LEFT_RIGHT))
 | 
					        assert_image_equal(im, im.transpose(Image.Transpose.FLIP_LEFT_RIGHT))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_ellipse_width():
 | 
					@pytest.mark.parametrize("bbox", BBOX)
 | 
				
			||||||
 | 
					def test_ellipse_width(bbox):
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    im = Image.new("RGB", (W, H))
 | 
					    im = Image.new("RGB", (W, H))
 | 
				
			||||||
    draw = ImageDraw.Draw(im)
 | 
					    draw = ImageDraw.Draw(im)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Act
 | 
					    # Act
 | 
				
			||||||
    draw.ellipse(BBOX1, outline="blue", width=5)
 | 
					    draw.ellipse(bbox, outline="blue", width=5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Assert
 | 
					    # Assert
 | 
				
			||||||
    assert_image_similar_tofile(im, "Tests/images/imagedraw_ellipse_width.png", 1)
 | 
					    assert_image_similar_tofile(im, "Tests/images/imagedraw_ellipse_width.png", 1)
 | 
				
			||||||
| 
						 | 
					@ -321,25 +338,27 @@ def test_ellipse_width_large():
 | 
				
			||||||
    assert_image_similar_tofile(im, "Tests/images/imagedraw_ellipse_width_large.png", 1)
 | 
					    assert_image_similar_tofile(im, "Tests/images/imagedraw_ellipse_width_large.png", 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_ellipse_width_fill():
 | 
					@pytest.mark.parametrize("bbox", BBOX)
 | 
				
			||||||
 | 
					def test_ellipse_width_fill(bbox):
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    im = Image.new("RGB", (W, H))
 | 
					    im = Image.new("RGB", (W, H))
 | 
				
			||||||
    draw = ImageDraw.Draw(im)
 | 
					    draw = ImageDraw.Draw(im)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Act
 | 
					    # Act
 | 
				
			||||||
    draw.ellipse(BBOX1, fill="green", outline="blue", width=5)
 | 
					    draw.ellipse(bbox, fill="green", outline="blue", width=5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Assert
 | 
					    # Assert
 | 
				
			||||||
    assert_image_similar_tofile(im, "Tests/images/imagedraw_ellipse_width_fill.png", 1)
 | 
					    assert_image_similar_tofile(im, "Tests/images/imagedraw_ellipse_width_fill.png", 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_ellipse_zero_width():
 | 
					@pytest.mark.parametrize("bbox", BBOX)
 | 
				
			||||||
 | 
					def test_ellipse_zero_width(bbox):
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    im = Image.new("RGB", (W, H))
 | 
					    im = Image.new("RGB", (W, H))
 | 
				
			||||||
    draw = ImageDraw.Draw(im)
 | 
					    draw = ImageDraw.Draw(im)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Act
 | 
					    # Act
 | 
				
			||||||
    draw.ellipse(BBOX1, fill="green", outline="blue", width=0)
 | 
					    draw.ellipse(bbox, fill="green", outline="blue", width=0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Assert
 | 
					    # Assert
 | 
				
			||||||
    assert_image_equal_tofile(im, "Tests/images/imagedraw_ellipse_zero_width.png")
 | 
					    assert_image_equal_tofile(im, "Tests/images/imagedraw_ellipse_zero_width.png")
 | 
				
			||||||
| 
						 | 
					@ -386,7 +405,7 @@ def test_ellipse_various_sizes_filled():
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.mark.parametrize("points", (POINTS1, POINTS2))
 | 
					@pytest.mark.parametrize("points", POINTS)
 | 
				
			||||||
def test_line(points):
 | 
					def test_line(points):
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    im = Image.new("RGB", (W, H))
 | 
					    im = Image.new("RGB", (W, H))
 | 
				
			||||||
| 
						 | 
					@ -458,7 +477,7 @@ def test_transform():
 | 
				
			||||||
    assert_image_equal(im, expected)
 | 
					    assert_image_equal(im, expected)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.mark.parametrize("bbox", (BBOX1, BBOX2))
 | 
					@pytest.mark.parametrize("bbox", BBOX)
 | 
				
			||||||
@pytest.mark.parametrize("start, end", ((-92, 46), (-92.2, 46.2)))
 | 
					@pytest.mark.parametrize("start, end", ((-92, 46), (-92.2, 46.2)))
 | 
				
			||||||
def test_pieslice(bbox, start, end):
 | 
					def test_pieslice(bbox, start, end):
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
| 
						 | 
					@ -472,38 +491,41 @@ def test_pieslice(bbox, start, end):
 | 
				
			||||||
    assert_image_similar_tofile(im, "Tests/images/imagedraw_pieslice.png", 1)
 | 
					    assert_image_similar_tofile(im, "Tests/images/imagedraw_pieslice.png", 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_pieslice_width():
 | 
					@pytest.mark.parametrize("bbox", BBOX)
 | 
				
			||||||
 | 
					def test_pieslice_width(bbox):
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    im = Image.new("RGB", (W, H))
 | 
					    im = Image.new("RGB", (W, H))
 | 
				
			||||||
    draw = ImageDraw.Draw(im)
 | 
					    draw = ImageDraw.Draw(im)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Act
 | 
					    # Act
 | 
				
			||||||
    draw.pieslice(BBOX1, 10, 260, outline="blue", width=5)
 | 
					    draw.pieslice(bbox, 10, 260, outline="blue", width=5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Assert
 | 
					    # Assert
 | 
				
			||||||
    assert_image_similar_tofile(im, "Tests/images/imagedraw_pieslice_width.png", 1)
 | 
					    assert_image_similar_tofile(im, "Tests/images/imagedraw_pieslice_width.png", 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_pieslice_width_fill():
 | 
					@pytest.mark.parametrize("bbox", BBOX)
 | 
				
			||||||
 | 
					def test_pieslice_width_fill(bbox):
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    im = Image.new("RGB", (W, H))
 | 
					    im = Image.new("RGB", (W, H))
 | 
				
			||||||
    draw = ImageDraw.Draw(im)
 | 
					    draw = ImageDraw.Draw(im)
 | 
				
			||||||
    expected = "Tests/images/imagedraw_pieslice_width_fill.png"
 | 
					    expected = "Tests/images/imagedraw_pieslice_width_fill.png"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Act
 | 
					    # Act
 | 
				
			||||||
    draw.pieslice(BBOX1, 10, 260, fill="white", outline="blue", width=5)
 | 
					    draw.pieslice(bbox, 10, 260, fill="white", outline="blue", width=5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Assert
 | 
					    # Assert
 | 
				
			||||||
    assert_image_similar_tofile(im, expected, 1)
 | 
					    assert_image_similar_tofile(im, expected, 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_pieslice_zero_width():
 | 
					@pytest.mark.parametrize("bbox", BBOX)
 | 
				
			||||||
 | 
					def test_pieslice_zero_width(bbox):
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    im = Image.new("RGB", (W, H))
 | 
					    im = Image.new("RGB", (W, H))
 | 
				
			||||||
    draw = ImageDraw.Draw(im)
 | 
					    draw = ImageDraw.Draw(im)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Act
 | 
					    # Act
 | 
				
			||||||
    draw.pieslice(BBOX1, 10, 260, fill="white", outline="blue", width=0)
 | 
					    draw.pieslice(bbox, 10, 260, fill="white", outline="blue", width=0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Assert
 | 
					    # Assert
 | 
				
			||||||
    assert_image_equal_tofile(im, "Tests/images/imagedraw_pieslice_zero_width.png")
 | 
					    assert_image_equal_tofile(im, "Tests/images/imagedraw_pieslice_zero_width.png")
 | 
				
			||||||
| 
						 | 
					@ -551,7 +573,7 @@ def test_pieslice_no_spikes():
 | 
				
			||||||
    assert_image_equal(im, im_pre_erase)
 | 
					    assert_image_equal(im, im_pre_erase)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.mark.parametrize("points", (POINTS1, POINTS2))
 | 
					@pytest.mark.parametrize("points", POINTS)
 | 
				
			||||||
def test_point(points):
 | 
					def test_point(points):
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    im = Image.new("RGB", (W, H))
 | 
					    im = Image.new("RGB", (W, H))
 | 
				
			||||||
| 
						 | 
					@ -564,7 +586,7 @@ def test_point(points):
 | 
				
			||||||
    assert_image_equal_tofile(im, "Tests/images/imagedraw_point.png")
 | 
					    assert_image_equal_tofile(im, "Tests/images/imagedraw_point.png")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.mark.parametrize("points", (POINTS1, POINTS2))
 | 
					@pytest.mark.parametrize("points", POINTS)
 | 
				
			||||||
def test_polygon(points):
 | 
					def test_polygon(points):
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    im = Image.new("RGB", (W, H))
 | 
					    im = Image.new("RGB", (W, H))
 | 
				
			||||||
| 
						 | 
					@ -578,7 +600,8 @@ def test_polygon(points):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.mark.parametrize("mode", ("RGB", "L"))
 | 
					@pytest.mark.parametrize("mode", ("RGB", "L"))
 | 
				
			||||||
def test_polygon_kite(mode):
 | 
					@pytest.mark.parametrize("kite_points", KITE_POINTS)
 | 
				
			||||||
 | 
					def test_polygon_kite(mode, kite_points):
 | 
				
			||||||
    # Test drawing lines of different gradients (dx>dy, dy>dx) and
 | 
					    # Test drawing lines of different gradients (dx>dy, dy>dx) and
 | 
				
			||||||
    # vertical (dx==0) and horizontal (dy==0) lines
 | 
					    # vertical (dx==0) and horizontal (dy==0) lines
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
| 
						 | 
					@ -587,7 +610,7 @@ def test_polygon_kite(mode):
 | 
				
			||||||
    expected = f"Tests/images/imagedraw_polygon_kite_{mode}.png"
 | 
					    expected = f"Tests/images/imagedraw_polygon_kite_{mode}.png"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Act
 | 
					    # Act
 | 
				
			||||||
    draw.polygon(KITE_POINTS, fill="blue", outline="yellow")
 | 
					    draw.polygon(kite_points, fill="blue", outline="yellow")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Assert
 | 
					    # Assert
 | 
				
			||||||
    assert_image_equal_tofile(im, expected)
 | 
					    assert_image_equal_tofile(im, expected)
 | 
				
			||||||
| 
						 | 
					@ -634,7 +657,7 @@ def test_polygon_translucent():
 | 
				
			||||||
    assert_image_equal_tofile(im, expected)
 | 
					    assert_image_equal_tofile(im, expected)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.mark.parametrize("bbox", (BBOX1, BBOX2))
 | 
					@pytest.mark.parametrize("bbox", BBOX)
 | 
				
			||||||
def test_rectangle(bbox):
 | 
					def test_rectangle(bbox):
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    im = Image.new("RGB", (W, H))
 | 
					    im = Image.new("RGB", (W, H))
 | 
				
			||||||
| 
						 | 
					@ -661,63 +684,68 @@ def test_big_rectangle():
 | 
				
			||||||
    assert_image_similar_tofile(im, "Tests/images/imagedraw_big_rectangle.png", 1)
 | 
					    assert_image_similar_tofile(im, "Tests/images/imagedraw_big_rectangle.png", 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_rectangle_width():
 | 
					@pytest.mark.parametrize("bbox", BBOX)
 | 
				
			||||||
 | 
					def test_rectangle_width(bbox):
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    im = Image.new("RGB", (W, H))
 | 
					    im = Image.new("RGB", (W, H))
 | 
				
			||||||
    draw = ImageDraw.Draw(im)
 | 
					    draw = ImageDraw.Draw(im)
 | 
				
			||||||
    expected = "Tests/images/imagedraw_rectangle_width.png"
 | 
					    expected = "Tests/images/imagedraw_rectangle_width.png"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Act
 | 
					    # Act
 | 
				
			||||||
    draw.rectangle(BBOX1, outline="green", width=5)
 | 
					    draw.rectangle(bbox, outline="green", width=5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Assert
 | 
					    # Assert
 | 
				
			||||||
    assert_image_equal_tofile(im, expected)
 | 
					    assert_image_equal_tofile(im, expected)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_rectangle_width_fill():
 | 
					@pytest.mark.parametrize("bbox", BBOX)
 | 
				
			||||||
 | 
					def test_rectangle_width_fill(bbox):
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    im = Image.new("RGB", (W, H))
 | 
					    im = Image.new("RGB", (W, H))
 | 
				
			||||||
    draw = ImageDraw.Draw(im)
 | 
					    draw = ImageDraw.Draw(im)
 | 
				
			||||||
    expected = "Tests/images/imagedraw_rectangle_width_fill.png"
 | 
					    expected = "Tests/images/imagedraw_rectangle_width_fill.png"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Act
 | 
					    # Act
 | 
				
			||||||
    draw.rectangle(BBOX1, fill="blue", outline="green", width=5)
 | 
					    draw.rectangle(bbox, fill="blue", outline="green", width=5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Assert
 | 
					    # Assert
 | 
				
			||||||
    assert_image_equal_tofile(im, expected)
 | 
					    assert_image_equal_tofile(im, expected)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_rectangle_zero_width():
 | 
					@pytest.mark.parametrize("bbox", BBOX)
 | 
				
			||||||
 | 
					def test_rectangle_zero_width(bbox):
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    im = Image.new("RGB", (W, H))
 | 
					    im = Image.new("RGB", (W, H))
 | 
				
			||||||
    draw = ImageDraw.Draw(im)
 | 
					    draw = ImageDraw.Draw(im)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Act
 | 
					    # Act
 | 
				
			||||||
    draw.rectangle(BBOX1, fill="blue", outline="green", width=0)
 | 
					    draw.rectangle(bbox, fill="blue", outline="green", width=0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Assert
 | 
					    # Assert
 | 
				
			||||||
    assert_image_equal_tofile(im, "Tests/images/imagedraw_rectangle_zero_width.png")
 | 
					    assert_image_equal_tofile(im, "Tests/images/imagedraw_rectangle_zero_width.png")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_rectangle_I16():
 | 
					@pytest.mark.parametrize("bbox", BBOX)
 | 
				
			||||||
 | 
					def test_rectangle_I16(bbox):
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    im = Image.new("I;16", (W, H))
 | 
					    im = Image.new("I;16", (W, H))
 | 
				
			||||||
    draw = ImageDraw.Draw(im)
 | 
					    draw = ImageDraw.Draw(im)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Act
 | 
					    # Act
 | 
				
			||||||
    draw.rectangle(BBOX1, fill="black", outline="green")
 | 
					    draw.rectangle(bbox, fill="black", outline="green")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Assert
 | 
					    # Assert
 | 
				
			||||||
    assert_image_equal_tofile(im.convert("I"), "Tests/images/imagedraw_rectangle_I.png")
 | 
					    assert_image_equal_tofile(im.convert("I"), "Tests/images/imagedraw_rectangle_I.png")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_rectangle_translucent_outline():
 | 
					@pytest.mark.parametrize("bbox", BBOX)
 | 
				
			||||||
 | 
					def test_rectangle_translucent_outline(bbox):
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    im = Image.new("RGB", (W, H))
 | 
					    im = Image.new("RGB", (W, H))
 | 
				
			||||||
    draw = ImageDraw.Draw(im, "RGBA")
 | 
					    draw = ImageDraw.Draw(im, "RGBA")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Act
 | 
					    # Act
 | 
				
			||||||
    draw.rectangle(BBOX1, fill="black", outline=(0, 255, 0, 127), width=5)
 | 
					    draw.rectangle(bbox, fill="black", outline=(0, 255, 0, 127), width=5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Assert
 | 
					    # Assert
 | 
				
			||||||
    assert_image_equal_tofile(
 | 
					    assert_image_equal_tofile(
 | 
				
			||||||
| 
						 | 
					@ -794,13 +822,14 @@ def test_rounded_rectangle_non_integer_radius(xy, radius, type):
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_rounded_rectangle_zero_radius():
 | 
					@pytest.mark.parametrize("bbox", BBOX)
 | 
				
			||||||
 | 
					def test_rounded_rectangle_zero_radius(bbox):
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    im = Image.new("RGB", (W, H))
 | 
					    im = Image.new("RGB", (W, H))
 | 
				
			||||||
    draw = ImageDraw.Draw(im)
 | 
					    draw = ImageDraw.Draw(im)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Act
 | 
					    # Act
 | 
				
			||||||
    draw.rounded_rectangle(BBOX1, 0, fill="blue", outline="green", width=5)
 | 
					    draw.rounded_rectangle(bbox, 0, fill="blue", outline="green", width=5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Assert
 | 
					    # Assert
 | 
				
			||||||
    assert_image_equal_tofile(im, "Tests/images/imagedraw_rectangle_width_fill.png")
 | 
					    assert_image_equal_tofile(im, "Tests/images/imagedraw_rectangle_width_fill.png")
 | 
				
			||||||
| 
						 | 
					@ -810,7 +839,9 @@ def test_rounded_rectangle_zero_radius():
 | 
				
			||||||
    "xy, suffix",
 | 
					    "xy, suffix",
 | 
				
			||||||
    [
 | 
					    [
 | 
				
			||||||
        ((20, 10, 80, 90), "x"),
 | 
					        ((20, 10, 80, 90), "x"),
 | 
				
			||||||
 | 
					        ((20, 10, 81, 90), "x_odd"),
 | 
				
			||||||
        ((10, 20, 90, 80), "y"),
 | 
					        ((10, 20, 90, 80), "y"),
 | 
				
			||||||
 | 
					        ((10, 20, 90, 81), "y_odd"),
 | 
				
			||||||
        ((20, 20, 80, 80), "both"),
 | 
					        ((20, 20, 80, 80), "both"),
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
| 
						 | 
					@ -830,14 +861,15 @@ def test_rounded_rectangle_translucent(xy, suffix):
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_floodfill():
 | 
					@pytest.mark.parametrize("bbox", BBOX)
 | 
				
			||||||
 | 
					def test_floodfill(bbox):
 | 
				
			||||||
    red = ImageColor.getrgb("red")
 | 
					    red = ImageColor.getrgb("red")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for mode, value in [("L", 1), ("RGBA", (255, 0, 0, 0)), ("RGB", red)]:
 | 
					    for mode, value in [("L", 1), ("RGBA", (255, 0, 0, 0)), ("RGB", red)]:
 | 
				
			||||||
        # Arrange
 | 
					        # Arrange
 | 
				
			||||||
        im = Image.new(mode, (W, H))
 | 
					        im = Image.new(mode, (W, H))
 | 
				
			||||||
        draw = ImageDraw.Draw(im)
 | 
					        draw = ImageDraw.Draw(im)
 | 
				
			||||||
        draw.rectangle(BBOX2, outline="yellow", fill="green")
 | 
					        draw.rectangle(bbox, outline="yellow", fill="green")
 | 
				
			||||||
        centre_point = (int(W / 2), int(H / 2))
 | 
					        centre_point = (int(W / 2), int(H / 2))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Act
 | 
					        # Act
 | 
				
			||||||
| 
						 | 
					@ -862,13 +894,14 @@ def test_floodfill():
 | 
				
			||||||
    assert_image_equal(im, Image.new("RGB", (1, 1), red))
 | 
					    assert_image_equal(im, Image.new("RGB", (1, 1), red))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_floodfill_border():
 | 
					@pytest.mark.parametrize("bbox", BBOX)
 | 
				
			||||||
 | 
					def test_floodfill_border(bbox):
 | 
				
			||||||
    # floodfill() is experimental
 | 
					    # floodfill() is experimental
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    im = Image.new("RGB", (W, H))
 | 
					    im = Image.new("RGB", (W, H))
 | 
				
			||||||
    draw = ImageDraw.Draw(im)
 | 
					    draw = ImageDraw.Draw(im)
 | 
				
			||||||
    draw.rectangle(BBOX2, outline="yellow", fill="green")
 | 
					    draw.rectangle(bbox, outline="yellow", fill="green")
 | 
				
			||||||
    centre_point = (int(W / 2), int(H / 2))
 | 
					    centre_point = (int(W / 2), int(H / 2))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Act
 | 
					    # Act
 | 
				
			||||||
| 
						 | 
					@ -883,13 +916,14 @@ def test_floodfill_border():
 | 
				
			||||||
    assert_image_equal_tofile(im, "Tests/images/imagedraw_floodfill2.png")
 | 
					    assert_image_equal_tofile(im, "Tests/images/imagedraw_floodfill2.png")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_floodfill_thresh():
 | 
					@pytest.mark.parametrize("bbox", BBOX)
 | 
				
			||||||
 | 
					def test_floodfill_thresh(bbox):
 | 
				
			||||||
    # floodfill() is experimental
 | 
					    # floodfill() is experimental
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    im = Image.new("RGB", (W, H))
 | 
					    im = Image.new("RGB", (W, H))
 | 
				
			||||||
    draw = ImageDraw.Draw(im)
 | 
					    draw = ImageDraw.Draw(im)
 | 
				
			||||||
    draw.rectangle(BBOX2, outline="darkgreen", fill="green")
 | 
					    draw.rectangle(bbox, outline="darkgreen", fill="green")
 | 
				
			||||||
    centre_point = (int(W / 2), int(H / 2))
 | 
					    centre_point = (int(W / 2), int(H / 2))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Act
 | 
					    # Act
 | 
				
			||||||
| 
						 | 
					@ -1309,7 +1343,8 @@ def test_setting_default_font():
 | 
				
			||||||
        assert isinstance(draw.getfont(), ImageFont.ImageFont)
 | 
					        assert isinstance(draw.getfont(), ImageFont.ImageFont)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_same_color_outline():
 | 
					@pytest.mark.parametrize("bbox", BBOX)
 | 
				
			||||||
 | 
					def test_same_color_outline(bbox):
 | 
				
			||||||
    # Prepare shape
 | 
					    # Prepare shape
 | 
				
			||||||
    x0, y0 = 5, 5
 | 
					    x0, y0 = 5, 5
 | 
				
			||||||
    x1, y1 = 5, 50
 | 
					    x1, y1 = 5, 50
 | 
				
			||||||
| 
						 | 
					@ -1325,12 +1360,12 @@ def test_same_color_outline():
 | 
				
			||||||
    for mode in ["RGB", "L"]:
 | 
					    for mode in ["RGB", "L"]:
 | 
				
			||||||
        for fill, outline in [["red", None], ["red", "red"], ["red", "#f00"]]:
 | 
					        for fill, outline in [["red", None], ["red", "red"], ["red", "#f00"]]:
 | 
				
			||||||
            for operation, args in {
 | 
					            for operation, args in {
 | 
				
			||||||
                "chord": [BBOX1, 0, 180],
 | 
					                "chord": [bbox, 0, 180],
 | 
				
			||||||
                "ellipse": [BBOX1],
 | 
					                "ellipse": [bbox],
 | 
				
			||||||
                "shape": [s],
 | 
					                "shape": [s],
 | 
				
			||||||
                "pieslice": [BBOX1, -90, 45],
 | 
					                "pieslice": [bbox, -90, 45],
 | 
				
			||||||
                "polygon": [[(18, 30), (85, 30), (60, 72)]],
 | 
					                "polygon": [[(18, 30), (85, 30), (60, 72)]],
 | 
				
			||||||
                "rectangle": [BBOX1],
 | 
					                "rectangle": [bbox],
 | 
				
			||||||
            }.items():
 | 
					            }.items():
 | 
				
			||||||
                # Arrange
 | 
					                # Arrange
 | 
				
			||||||
                im = Image.new(mode, (W, H))
 | 
					                im = Image.new(mode, (W, H))
 | 
				
			||||||
| 
						 | 
					@ -1347,20 +1382,20 @@ def test_same_color_outline():
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.mark.parametrize(
 | 
					@pytest.mark.parametrize(
 | 
				
			||||||
    "n_sides, rotation, polygon_name",
 | 
					    "n_sides, polygon_name, args",
 | 
				
			||||||
    [(4, 0, "square"), (8, 0, "regular_octagon"), (4, 45, "square")],
 | 
					    [
 | 
				
			||||||
 | 
					        (4, "square", {}),
 | 
				
			||||||
 | 
					        (8, "regular_octagon", {}),
 | 
				
			||||||
 | 
					        (4, "square_rotate_45", {"rotation": 45}),
 | 
				
			||||||
 | 
					        (3, "triangle_width", {"width": 5, "outline": "yellow"}),
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
def test_draw_regular_polygon(n_sides, rotation, polygon_name):
 | 
					def test_draw_regular_polygon(n_sides, polygon_name, args):
 | 
				
			||||||
    im = Image.new("RGBA", size=(W, H), color=(255, 0, 0, 0))
 | 
					    im = Image.new("RGBA", size=(W, H), color=(255, 0, 0, 0))
 | 
				
			||||||
    filename_base = f"Tests/images/imagedraw_{polygon_name}"
 | 
					    filename = f"Tests/images/imagedraw_{polygon_name}.png"
 | 
				
			||||||
    filename = (
 | 
					 | 
				
			||||||
        f"{filename_base}.png"
 | 
					 | 
				
			||||||
        if rotation == 0
 | 
					 | 
				
			||||||
        else f"{filename_base}_rotate_{rotation}.png"
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    draw = ImageDraw.Draw(im)
 | 
					    draw = ImageDraw.Draw(im)
 | 
				
			||||||
    bounding_circle = ((W // 2, H // 2), 25)
 | 
					    bounding_circle = ((W // 2, H // 2), 25)
 | 
				
			||||||
    draw.regular_polygon(bounding_circle, n_sides, rotation=rotation, fill="red")
 | 
					    draw.regular_polygon(bounding_circle, n_sides, fill="red", **args)
 | 
				
			||||||
    assert_image_equal_tofile(im, filename)
 | 
					    assert_image_equal_tofile(im, filename)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,15 +27,16 @@ X1 = int(X0 * 3)
 | 
				
			||||||
Y0 = int(H / 4)
 | 
					Y0 = int(H / 4)
 | 
				
			||||||
Y1 = int(X0 * 3)
 | 
					Y1 = int(X0 * 3)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Two kinds of bounding box
 | 
					# Bounding boxes
 | 
				
			||||||
BBOX1 = [(X0, Y0), (X1, Y1)]
 | 
					BBOX = (((X0, Y0), (X1, Y1)), [(X0, Y0), (X1, Y1)], (X0, Y0, X1, Y1), [X0, Y0, X1, Y1])
 | 
				
			||||||
BBOX2 = [X0, Y0, X1, Y1]
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Two kinds of coordinate sequences
 | 
					# Coordinate sequences
 | 
				
			||||||
POINTS1 = [(10, 10), (20, 40), (30, 30)]
 | 
					POINTS = (
 | 
				
			||||||
POINTS2 = [10, 10, 20, 40, 30, 30]
 | 
					    ((10, 10), (20, 40), (30, 30)),
 | 
				
			||||||
 | 
					    [(10, 10), (20, 40), (30, 30)],
 | 
				
			||||||
KITE_POINTS = [(10, 50), (70, 10), (90, 50), (70, 90), (10, 50)]
 | 
					    (10, 10, 20, 40, 30, 30),
 | 
				
			||||||
 | 
					    [10, 10, 20, 40, 30, 30],
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
FONT_PATH = "Tests/fonts/FreeMono.ttf"
 | 
					FONT_PATH = "Tests/fonts/FreeMono.ttf"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -52,7 +53,7 @@ def test_sanity():
 | 
				
			||||||
    draw.line(list(range(10)), pen)
 | 
					    draw.line(list(range(10)), pen)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.mark.parametrize("bbox", (BBOX1, BBOX2))
 | 
					@pytest.mark.parametrize("bbox", BBOX)
 | 
				
			||||||
def test_ellipse(bbox):
 | 
					def test_ellipse(bbox):
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    im = Image.new("RGB", (W, H))
 | 
					    im = Image.new("RGB", (W, H))
 | 
				
			||||||
| 
						 | 
					@ -80,7 +81,7 @@ def test_ellipse_edge():
 | 
				
			||||||
    assert_image_similar_tofile(im, "Tests/images/imagedraw_ellipse_edge.png", 1)
 | 
					    assert_image_similar_tofile(im, "Tests/images/imagedraw_ellipse_edge.png", 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.mark.parametrize("points", (POINTS1, POINTS2))
 | 
					@pytest.mark.parametrize("points", POINTS)
 | 
				
			||||||
def test_line(points):
 | 
					def test_line(points):
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    im = Image.new("RGB", (W, H))
 | 
					    im = Image.new("RGB", (W, H))
 | 
				
			||||||
| 
						 | 
					@ -94,7 +95,8 @@ def test_line(points):
 | 
				
			||||||
    assert_image_equal_tofile(im, "Tests/images/imagedraw_line.png")
 | 
					    assert_image_equal_tofile(im, "Tests/images/imagedraw_line.png")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_line_pen_as_brush():
 | 
					@pytest.mark.parametrize("points", POINTS)
 | 
				
			||||||
 | 
					def test_line_pen_as_brush(points):
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    im = Image.new("RGB", (W, H))
 | 
					    im = Image.new("RGB", (W, H))
 | 
				
			||||||
    draw = ImageDraw2.Draw(im)
 | 
					    draw = ImageDraw2.Draw(im)
 | 
				
			||||||
| 
						 | 
					@ -103,13 +105,13 @@ def test_line_pen_as_brush():
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Act
 | 
					    # Act
 | 
				
			||||||
    # Pass in the pen as the brush parameter
 | 
					    # Pass in the pen as the brush parameter
 | 
				
			||||||
    draw.line(POINTS1, pen, brush)
 | 
					    draw.line(points, pen, brush)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Assert
 | 
					    # Assert
 | 
				
			||||||
    assert_image_equal_tofile(im, "Tests/images/imagedraw_line.png")
 | 
					    assert_image_equal_tofile(im, "Tests/images/imagedraw_line.png")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.mark.parametrize("points", (POINTS1, POINTS2))
 | 
					@pytest.mark.parametrize("points", POINTS)
 | 
				
			||||||
def test_polygon(points):
 | 
					def test_polygon(points):
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    im = Image.new("RGB", (W, H))
 | 
					    im = Image.new("RGB", (W, H))
 | 
				
			||||||
| 
						 | 
					@ -124,7 +126,7 @@ def test_polygon(points):
 | 
				
			||||||
    assert_image_equal_tofile(im, "Tests/images/imagedraw_polygon.png")
 | 
					    assert_image_equal_tofile(im, "Tests/images/imagedraw_polygon.png")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.mark.parametrize("bbox", (BBOX1, BBOX2))
 | 
					@pytest.mark.parametrize("bbox", BBOX)
 | 
				
			||||||
def test_rectangle(bbox):
 | 
					def test_rectangle(bbox):
 | 
				
			||||||
    # Arrange
 | 
					    # Arrange
 | 
				
			||||||
    im = Image.new("RGB", (W, H))
 | 
					    im = Image.new("RGB", (W, H))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -191,6 +191,16 @@ def test_getlength(
 | 
				
			||||||
        assert length == length_raqm
 | 
					        assert length == length_raqm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_float_size():
 | 
				
			||||||
 | 
					    lengths = []
 | 
				
			||||||
 | 
					    for size in (48, 48.5, 49):
 | 
				
			||||||
 | 
					        f = ImageFont.truetype(
 | 
				
			||||||
 | 
					            "Tests/fonts/NotoSans-Regular.ttf", size, layout_engine=layout_engine
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        lengths.append(f.getlength("text"))
 | 
				
			||||||
 | 
					    assert lengths[0] != lengths[1] != lengths[2]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_render_multiline(font):
 | 
					def test_render_multiline(font):
 | 
				
			||||||
    im = Image.new(mode="RGB", size=(300, 100))
 | 
					    im = Image.new(mode="RGB", size=(300, 100))
 | 
				
			||||||
    draw = ImageDraw.Draw(im)
 | 
					    draw = ImageDraw.Draw(im)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,7 +28,7 @@ def test_path():
 | 
				
			||||||
        (6.0, 7.0),
 | 
					        (6.0, 7.0),
 | 
				
			||||||
        (8.0, 9.0),
 | 
					        (8.0, 9.0),
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
    assert p.tolist(1) == [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
 | 
					    assert p.tolist(True) == [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    assert p.getbbox() == (0.0, 1.0, 8.0, 9.0)
 | 
					    assert p.getbbox() == (0.0, 1.0, 8.0, 9.0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -38,48 +38,65 @@ def test_path():
 | 
				
			||||||
    p.transform((1, 0, 1, 0, 1, 1))
 | 
					    p.transform((1, 0, 1, 0, 1, 1))
 | 
				
			||||||
    assert list(p) == [(1.0, 2.0), (5.0, 6.0), (9.0, 10.0)]
 | 
					    assert list(p) == [(1.0, 2.0), (5.0, 6.0), (9.0, 10.0)]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # alternative constructors
 | 
					 | 
				
			||||||
    p = ImagePath.Path([0, 1])
 | 
					 | 
				
			||||||
    assert list(p) == [(0.0, 1.0)]
 | 
					 | 
				
			||||||
    p = ImagePath.Path([0.0, 1.0])
 | 
					 | 
				
			||||||
    assert list(p) == [(0.0, 1.0)]
 | 
					 | 
				
			||||||
    p = ImagePath.Path([0, 1])
 | 
					 | 
				
			||||||
    assert list(p) == [(0.0, 1.0)]
 | 
					 | 
				
			||||||
    p = ImagePath.Path([(0, 1)])
 | 
					 | 
				
			||||||
    assert list(p) == [(0.0, 1.0)]
 | 
					 | 
				
			||||||
    p = ImagePath.Path(p)
 | 
					 | 
				
			||||||
    assert list(p) == [(0.0, 1.0)]
 | 
					 | 
				
			||||||
    p = ImagePath.Path(p.tolist(0))
 | 
					 | 
				
			||||||
    assert list(p) == [(0.0, 1.0)]
 | 
					 | 
				
			||||||
    p = ImagePath.Path(p.tolist(1))
 | 
					 | 
				
			||||||
    assert list(p) == [(0.0, 1.0)]
 | 
					 | 
				
			||||||
    p = ImagePath.Path(array.array("f", [0, 1]))
 | 
					 | 
				
			||||||
    assert list(p) == [(0.0, 1.0)]
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    arr = array.array("f", [0, 1])
 | 
					@pytest.mark.parametrize(
 | 
				
			||||||
    p = ImagePath.Path(arr.tobytes())
 | 
					    "coords",
 | 
				
			||||||
 | 
					    (
 | 
				
			||||||
 | 
					        (0, 1),
 | 
				
			||||||
 | 
					        [0, 1],
 | 
				
			||||||
 | 
					        (0.0, 1.0),
 | 
				
			||||||
 | 
					        [0.0, 1.0],
 | 
				
			||||||
 | 
					        ((0, 1),),
 | 
				
			||||||
 | 
					        [(0, 1)],
 | 
				
			||||||
 | 
					        ((0.0, 1.0),),
 | 
				
			||||||
 | 
					        [(0.0, 1.0)],
 | 
				
			||||||
 | 
					        array.array("f", [0, 1]),
 | 
				
			||||||
 | 
					        array.array("f", [0, 1]).tobytes(),
 | 
				
			||||||
 | 
					        ImagePath.Path((0, 1)),
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					def test_path_constructors(coords):
 | 
				
			||||||
 | 
					    # Arrange / Act
 | 
				
			||||||
 | 
					    p = ImagePath.Path(coords)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Assert
 | 
				
			||||||
    assert list(p) == [(0.0, 1.0)]
 | 
					    assert list(p) == [(0.0, 1.0)]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_invalid_coords():
 | 
					@pytest.mark.parametrize(
 | 
				
			||||||
    # Arrange
 | 
					    "coords",
 | 
				
			||||||
    coords = ["a", "b"]
 | 
					    (
 | 
				
			||||||
 | 
					        ("a", "b"),
 | 
				
			||||||
    # Act / Assert
 | 
					        ([0, 1],),
 | 
				
			||||||
 | 
					        [[0, 1]],
 | 
				
			||||||
 | 
					        ([0.0, 1.0],),
 | 
				
			||||||
 | 
					        [[0.0, 1.0]],
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					def test_invalid_path_constructors(coords):
 | 
				
			||||||
 | 
					    # Act
 | 
				
			||||||
    with pytest.raises(ValueError) as e:
 | 
					    with pytest.raises(ValueError) as e:
 | 
				
			||||||
        ImagePath.Path(coords)
 | 
					        ImagePath.Path(coords)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Assert
 | 
				
			||||||
    assert str(e.value) == "incorrect coordinate type"
 | 
					    assert str(e.value) == "incorrect coordinate type"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_path_odd_number_of_coordinates():
 | 
					@pytest.mark.parametrize(
 | 
				
			||||||
    # Arrange
 | 
					    "coords",
 | 
				
			||||||
    coords = [0]
 | 
					    (
 | 
				
			||||||
 | 
					        (0,),
 | 
				
			||||||
    # Act / Assert
 | 
					        [0],
 | 
				
			||||||
 | 
					        (0, 1, 2),
 | 
				
			||||||
 | 
					        [0, 1, 2],
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					def test_path_odd_number_of_coordinates(coords):
 | 
				
			||||||
 | 
					    # Act
 | 
				
			||||||
    with pytest.raises(ValueError) as e:
 | 
					    with pytest.raises(ValueError) as e:
 | 
				
			||||||
        ImagePath.Path(coords)
 | 
					        ImagePath.Path(coords)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Assert
 | 
				
			||||||
    assert str(e.value) == "wrong number of coordinates"
 | 
					    assert str(e.value) == "wrong number of coordinates"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -757,6 +757,7 @@ class TestLibUnpack:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_I16(self):
 | 
					    def test_I16(self):
 | 
				
			||||||
        self.assert_unpack("I;16", "I;16", 2, 0x0201, 0x0403, 0x0605)
 | 
					        self.assert_unpack("I;16", "I;16", 2, 0x0201, 0x0403, 0x0605)
 | 
				
			||||||
 | 
					        self.assert_unpack("I;16", "I;16B", 2, 0x0102, 0x0304, 0x0506)
 | 
				
			||||||
        self.assert_unpack("I;16B", "I;16B", 2, 0x0102, 0x0304, 0x0506)
 | 
					        self.assert_unpack("I;16B", "I;16B", 2, 0x0102, 0x0304, 0x0506)
 | 
				
			||||||
        self.assert_unpack("I;16L", "I;16L", 2, 0x0201, 0x0403, 0x0605)
 | 
					        self.assert_unpack("I;16L", "I;16L", 2, 0x0201, 0x0403, 0x0605)
 | 
				
			||||||
        self.assert_unpack("I;16", "I;12", 2, 0x0010, 0x0203, 0x0040)
 | 
					        self.assert_unpack("I;16", "I;12", 2, 0x0010, 0x0203, 0x0040)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -117,3 +117,9 @@ def test_pdf_repr():
 | 
				
			||||||
    assert pdf_repr(b"a)/b\\(c") == rb"(a\)/b\\\(c)"
 | 
					    assert pdf_repr(b"a)/b\\(c") == rb"(a\)/b\\\(c)"
 | 
				
			||||||
    assert pdf_repr([123, True, {"a": PdfName(b"b")}]) == b"[ 123 true <<\n/a /b\n>> ]"
 | 
					    assert pdf_repr([123, True, {"a": PdfName(b"b")}]) == b"[ 123 true <<\n/a /b\n>> ]"
 | 
				
			||||||
    assert pdf_repr(PdfBinary(b"\x90\x1F\xA0")) == b"<901FA0>"
 | 
					    assert pdf_repr(PdfBinary(b"\x90\x1F\xA0")) == b"<901FA0>"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_duplicate_xref_entry():
 | 
				
			||||||
 | 
					    pdf = PdfParser("Tests/images/duplicate_xref_entry.pdf")
 | 
				
			||||||
 | 
					    assert pdf.xref_table.existing_entries[6][0] == 1197
 | 
				
			||||||
 | 
					    pdf.close()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,9 @@
 | 
				
			||||||
# Documentation: https://docs.codecov.io/docs/codecov-yaml
 | 
					# Documentation: https://docs.codecov.com/docs/codecov-yaml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
codecov:
 | 
					codecov:
 | 
				
			||||||
  # Avoid "Missing base report" due to committing CHANGES.rst with "[CI skip]"
 | 
					  # Avoid "Missing base report" due to committing CHANGES.rst with "[CI skip]"
 | 
				
			||||||
  # https://github.com/codecov/support/issues/363
 | 
					  # https://github.com/codecov/support/issues/363
 | 
				
			||||||
  # https://docs.codecov.io/docs/comparing-commits
 | 
					  # https://docs.codecov.com/docs/comparing-commits
 | 
				
			||||||
  allow_coverage_offsets: true
 | 
					  allow_coverage_offsets: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
comment: false
 | 
					comment: false
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
#!/bin/bash
 | 
					#!/bin/bash
 | 
				
			||||||
# install libimagequant
 | 
					# install libimagequant
 | 
				
			||||||
 | 
					
 | 
				
			||||||
archive=libimagequant-4.1.1
 | 
					archive=libimagequant-4.2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/main/$archive.tar.gz
 | 
					./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/main/$archive.tar.gz
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										11
									
								
								docs/conf.py
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								docs/conf.py
									
									
									
									
									
								
							| 
						 | 
					@ -317,6 +317,17 @@ def setup(app):
 | 
				
			||||||
    app.add_css_file("css/dark.css")
 | 
					    app.add_css_file("css/dark.css")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					linkcheck_allowed_redirects = {
 | 
				
			||||||
 | 
					    r"https://bestpractices.coreinfrastructure.org/projects/6331": r"https://bestpractices.coreinfrastructure.org/en/.*",  # noqa: E501
 | 
				
			||||||
 | 
					    r"https://badges.gitter.im/python-pillow/Pillow.svg": r"https://badges.gitter.im/repo.svg",  # noqa: E501
 | 
				
			||||||
 | 
					    r"https://gitter.im/python-pillow/Pillow?.*": r"https://app.gitter.im/#/room/#python-pillow_Pillow:gitter.im?.*",  # noqa: E501
 | 
				
			||||||
 | 
					    r"https://pillow.readthedocs.io/?badge=latest": r"https://pillow.readthedocs.io/en/stable/?badge=latest",  # noqa: E501
 | 
				
			||||||
 | 
					    r"https://pillow.readthedocs.io": r"https://pillow.readthedocs.io/en/stable/",
 | 
				
			||||||
 | 
					    r"https://tidelift.com/badges/package/pypi/Pillow?.*": r"https://img.shields.io/badge/.*",  # noqa: E501
 | 
				
			||||||
 | 
					    r"https://zenodo.org/badge/17549/python-pillow/Pillow.svg": r"https://zenodo.org/badge/doi/[\.0-9]+/zenodo.[0-9]+.svg",  # noqa: E501
 | 
				
			||||||
 | 
					    r"https://zenodo.org/badge/latestdoi/17549/python-pillow/Pillow": r"https://zenodo.org/record/[0-9]+",  # noqa: E501
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# sphinx.ext.extlinks
 | 
					# sphinx.ext.extlinks
 | 
				
			||||||
# This config is a dictionary of external sites,
 | 
					# This config is a dictionary of external sites,
 | 
				
			||||||
# mapping unique short aliases to a base URL and a prefix.
 | 
					# mapping unique short aliases to a base URL and a prefix.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -210,7 +210,7 @@ open-source users (and will reach EOL on 2023-12-08 for commercial licence holde
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Support for PyQt5 and PySide2 has been removed from ``ImageQt``. Upgrade to
 | 
					Support for PyQt5 and PySide2 has been removed from ``ImageQt``. Upgrade to
 | 
				
			||||||
`PyQt6 <https://www.riverbankcomputing.com/static/Docs/PyQt6/>`_ or
 | 
					`PyQt6 <https://www.riverbankcomputing.com/static/Docs/PyQt6/>`_ or
 | 
				
			||||||
`PySide6 <https://doc.qt.io/qtforpython/>`_ instead.
 | 
					`PySide6 <https://doc.qt.io/qtforpython-6/>`_ instead.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Image.coerce_e
 | 
					Image.coerce_e
 | 
				
			||||||
~~~~~~~~~~~~~~
 | 
					~~~~~~~~~~~~~~
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -181,7 +181,7 @@ Many of Pillow's features require external libraries:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* **libimagequant** provides improved color quantization
 | 
					* **libimagequant** provides improved color quantization
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  * Pillow has been tested with libimagequant **2.6-4.1.1**
 | 
					  * Pillow has been tested with libimagequant **2.6-4.2**
 | 
				
			||||||
  * Libimagequant is licensed GPLv3, which is more restrictive than
 | 
					  * Libimagequant is licensed GPLv3, which is more restrictive than
 | 
				
			||||||
    the Pillow license, therefore we will not be distributing binaries
 | 
					    the Pillow license, therefore we will not be distributing binaries
 | 
				
			||||||
    with libimagequant support enabled.
 | 
					    with libimagequant support enabled.
 | 
				
			||||||
| 
						 | 
					@ -448,17 +448,15 @@ These platforms are built and tested for every change.
 | 
				
			||||||
+----------------------------------+----------------------------+---------------------+
 | 
					+----------------------------------+----------------------------+---------------------+
 | 
				
			||||||
| Debian 11 Bullseye               | 3.9                        | x86                 |
 | 
					| Debian 11 Bullseye               | 3.9                        | x86                 |
 | 
				
			||||||
+----------------------------------+----------------------------+---------------------+
 | 
					+----------------------------------+----------------------------+---------------------+
 | 
				
			||||||
| Fedora 36                        | 3.10                       | x86-64              |
 | 
					 | 
				
			||||||
+----------------------------------+----------------------------+---------------------+
 | 
					 | 
				
			||||||
| Fedora 37                        | 3.11                       | x86-64              |
 | 
					| Fedora 37                        | 3.11                       | x86-64              |
 | 
				
			||||||
+----------------------------------+----------------------------+---------------------+
 | 
					+----------------------------------+----------------------------+---------------------+
 | 
				
			||||||
 | 
					| Fedora 38                        | 3.11                       | x86-64              |
 | 
				
			||||||
 | 
					+----------------------------------+----------------------------+---------------------+
 | 
				
			||||||
| Gentoo                           | 3.9                        | x86-64              |
 | 
					| Gentoo                           | 3.9                        | x86-64              |
 | 
				
			||||||
+----------------------------------+----------------------------+---------------------+
 | 
					+----------------------------------+----------------------------+---------------------+
 | 
				
			||||||
| macOS 12 Monterey                | 3.8, 3.9, 3.10, 3.11,      | x86-64              |
 | 
					| macOS 12 Monterey                | 3.8, 3.9, 3.10, 3.11,      | x86-64              |
 | 
				
			||||||
|                                  | 3.12, PyPy3                |                     |
 | 
					|                                  | 3.12, PyPy3                |                     |
 | 
				
			||||||
+----------------------------------+----------------------------+---------------------+
 | 
					+----------------------------------+----------------------------+---------------------+
 | 
				
			||||||
| Ubuntu Linux 18.04 LTS (Bionic)  | 3.9                        | x86-64              |
 | 
					 | 
				
			||||||
+----------------------------------+----------------------------+---------------------+
 | 
					 | 
				
			||||||
| Ubuntu Linux 20.04 LTS (Focal)   | 3.8                        | x86-64              |
 | 
					| Ubuntu Linux 20.04 LTS (Focal)   | 3.8                        | x86-64              |
 | 
				
			||||||
+----------------------------------+----------------------------+---------------------+
 | 
					+----------------------------------+----------------------------+---------------------+
 | 
				
			||||||
| Ubuntu Linux 22.04 LTS (Jammy)   | 3.8, 3.9, 3.10, 3.11,      | x86-64              |
 | 
					| Ubuntu Linux 22.04 LTS (Jammy)   | 3.8, 3.9, 3.10, 3.11,      | x86-64              |
 | 
				
			||||||
| 
						 | 
					@ -492,7 +490,7 @@ These platforms have been reported to work at the versions mentioned.
 | 
				
			||||||
| Operating system                 | | Tested Python           | | Latest tested  | | Tested     |
 | 
					| Operating system                 | | Tested Python           | | Latest tested  | | Tested     |
 | 
				
			||||||
|                                  | | versions                | | Pillow version | | processors |
 | 
					|                                  | | versions                | | Pillow version | | processors |
 | 
				
			||||||
+==================================+===========================+==================+==============+
 | 
					+==================================+===========================+==================+==============+
 | 
				
			||||||
| macOS 13 Ventura                 | 3.7, 3.8, 3.9, 3.10, 3.11 | 9.4.0            |arm           |
 | 
					| macOS 13 Ventura                 | 3.7, 3.8, 3.9, 3.10, 3.11 | 9.5.0            |arm           |
 | 
				
			||||||
+----------------------------------+---------------------------+------------------+--------------+
 | 
					+----------------------------------+---------------------------+------------------+--------------+
 | 
				
			||||||
| macOS 12 Big Sur                 | 3.7, 3.8, 3.9, 3.10, 3.11 | 9.3.0            |arm           |
 | 
					| macOS 12 Big Sur                 | 3.7, 3.8, 3.9, 3.10, 3.11 | 9.3.0            |arm           |
 | 
				
			||||||
+----------------------------------+---------------------------+------------------+--------------+
 | 
					+----------------------------------+---------------------------+------------------+--------------+
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -439,7 +439,7 @@ Used to specify the dithering method to use for the
 | 
				
			||||||
Palettes
 | 
					Palettes
 | 
				
			||||||
^^^^^^^^
 | 
					^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Used to specify the pallete to use for the :meth:`~Image.convert` method.
 | 
					Used to specify the palette to use for the :meth:`~Image.convert` method.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. autoclass:: Palette
 | 
					.. autoclass:: Palette
 | 
				
			||||||
    :members:
 | 
					    :members:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -243,6 +243,7 @@ Methods
 | 
				
			||||||
.. py:method:: ImageDraw.line(xy, fill=None, width=0, joint=None)
 | 
					.. py:method:: ImageDraw.line(xy, fill=None, width=0, joint=None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Draws a line between the coordinates in the ``xy`` list.
 | 
					    Draws a line between the coordinates in the ``xy`` list.
 | 
				
			||||||
 | 
					    The coordinate pixels are included in the drawn line.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    :param xy: Sequence of either 2-tuples like ``[(x, y), (x, y), ...]`` or
 | 
					    :param xy: Sequence of either 2-tuples like ``[(x, y), (x, y), ...]`` or
 | 
				
			||||||
               numeric values like ``[x, y, x, y, ...]``.
 | 
					               numeric values like ``[x, y, x, y, ...]``.
 | 
				
			||||||
| 
						 | 
					@ -287,7 +288,7 @@ Methods
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    The polygon outline consists of straight lines between the given
 | 
					    The polygon outline consists of straight lines between the given
 | 
				
			||||||
    coordinates, plus a straight line between the last and the first
 | 
					    coordinates, plus a straight line between the last and the first
 | 
				
			||||||
    coordinate.
 | 
					    coordinate. The coordinate pixels are included in the drawn polygon.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    :param xy: Sequence of either 2-tuples like ``[(x, y), (x, y), ...]`` or
 | 
					    :param xy: Sequence of either 2-tuples like ``[(x, y), (x, y), ...]`` or
 | 
				
			||||||
               numeric values like ``[x, y, x, y, ...]``.
 | 
					               numeric values like ``[x, y, x, y, ...]``.
 | 
				
			||||||
| 
						 | 
					@ -296,7 +297,7 @@ Methods
 | 
				
			||||||
    :param width: The line width, in pixels.
 | 
					    :param width: The line width, in pixels.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. py:method:: ImageDraw.regular_polygon(bounding_circle, n_sides, rotation=0, fill=None, outline=None)
 | 
					.. py:method:: ImageDraw.regular_polygon(bounding_circle, n_sides, rotation=0, fill=None, outline=None, width=1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Draws a regular polygon inscribed in ``bounding_circle``,
 | 
					    Draws a regular polygon inscribed in ``bounding_circle``,
 | 
				
			||||||
    with ``n_sides``, and rotation of ``rotation`` degrees.
 | 
					    with ``n_sides``, and rotation of ``rotation`` degrees.
 | 
				
			||||||
| 
						 | 
					@ -311,6 +312,7 @@ Methods
 | 
				
			||||||
        (e.g. ``rotation=90``, applies a 90 degree rotation).
 | 
					        (e.g. ``rotation=90``, applies a 90 degree rotation).
 | 
				
			||||||
    :param fill: Color to use for the fill.
 | 
					    :param fill: Color to use for the fill.
 | 
				
			||||||
    :param outline: Color to use for the outline.
 | 
					    :param outline: Color to use for the outline.
 | 
				
			||||||
 | 
					    :param width: The line width, in pixels.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. py:method:: ImageDraw.rectangle(xy, fill=None, outline=None, width=1)
 | 
					.. py:method:: ImageDraw.rectangle(xy, fill=None, outline=None, width=1)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,8 +15,9 @@ or the clipboard to a PIL image memory.
 | 
				
			||||||
    returned as an "RGBA" on macOS, or an "RGB" image otherwise.
 | 
					    returned as an "RGBA" on macOS, or an "RGB" image otherwise.
 | 
				
			||||||
    If the bounding box is omitted, the entire screen is copied.
 | 
					    If the bounding box is omitted, the entire screen is copied.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    On Linux, if ``xdisplay`` is ``None`` then ``gnome-screenshot`` will be used if it
 | 
					    On Linux, if ``xdisplay`` is ``None`` and the default X11 display does not return
 | 
				
			||||||
    is installed. To capture the default X11 display instead, pass ``xdisplay=""``.
 | 
					    a snapshot of the screen, ``gnome-screenshot`` will be used as fallback if it is
 | 
				
			||||||
 | 
					    installed. To disable this behaviour, pass ``xdisplay=""`` instead.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .. versionadded:: 1.1.3 (Windows), 3.0.0 (macOS), 7.1.0 (Linux)
 | 
					    .. versionadded:: 1.1.3 (Windows), 3.0.0 (macOS), 7.1.0 (Linux)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -39,9 +40,11 @@ or the clipboard to a PIL image memory.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. py:function:: grabclipboard()
 | 
					.. py:function:: grabclipboard()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Take a snapshot of the clipboard image, if any. Only macOS and Windows are currently supported.
 | 
					    Take a snapshot of the clipboard image, if any.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .. versionadded:: 1.1.4 (Windows), 3.3.0 (macOS)
 | 
					    On Linux, ``wl-paste`` or ``xclip`` is required.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionadded:: 1.1.4 (Windows), 3.3.0 (macOS), 9.4.0 (Linux)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    :return: On Windows, an image, a list of filenames,
 | 
					    :return: On Windows, an image, a list of filenames,
 | 
				
			||||||
             or None if the clipboard does not contain image data or filenames.
 | 
					             or None if the clipboard does not contain image data or filenames.
 | 
				
			||||||
| 
						 | 
					@ -49,3 +52,5 @@ or the clipboard to a PIL image memory.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
             On Mac, an image,
 | 
					             On Mac, an image,
 | 
				
			||||||
             or None if the clipboard does not contain image data.
 | 
					             or None if the clipboard does not contain image data.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					             On Linux, an image.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -48,7 +48,7 @@ vector data. Path objects can be passed to the methods on the
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Maps the path through a function.
 | 
					    Maps the path through a function.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. py:method:: PIL.ImagePath.Path.tolist(flat=0)
 | 
					.. py:method:: PIL.ImagePath.Path.tolist(flat=False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Converts the path to a Python list [(x, y), …].
 | 
					    Converts the path to a Python list [(x, y), …].
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -117,7 +117,7 @@ open-source users (and will reach EOL on 2023-12-08 for commercial licence holde
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Support for PyQt5 and PySide2 has been removed from ``ImageQt``. Upgrade to
 | 
					Support for PyQt5 and PySide2 has been removed from ``ImageQt``. Upgrade to
 | 
				
			||||||
`PyQt6 <https://www.riverbankcomputing.com/static/Docs/PyQt6/>`_ or
 | 
					`PyQt6 <https://www.riverbankcomputing.com/static/Docs/PyQt6/>`_ or
 | 
				
			||||||
`PySide6 <https://doc.qt.io/qtforpython/>`_ instead.
 | 
					`PySide6 <https://doc.qt.io/qtforpython-6/>`_ instead.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Image.coerce_e
 | 
					Image.coerce_e
 | 
				
			||||||
^^^^^^^^^^^^^^
 | 
					^^^^^^^^^^^^^^
 | 
				
			||||||
| 
						 | 
					@ -135,10 +135,11 @@ TODO
 | 
				
			||||||
API Changes
 | 
					API Changes
 | 
				
			||||||
===========
 | 
					===========
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TODO
 | 
					Added line width parameter to ImageDraw regular_polygon
 | 
				
			||||||
^^^^
 | 
					^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TODO
 | 
					An optional line ``width`` parameter has been added to
 | 
				
			||||||
 | 
					``ImageDraw.Draw.regular_polygon``.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
API Additions
 | 
					API Additions
 | 
				
			||||||
=============
 | 
					=============
 | 
				
			||||||
| 
						 | 
					@ -159,7 +160,20 @@ TODO
 | 
				
			||||||
Other Changes
 | 
					Other Changes
 | 
				
			||||||
=============
 | 
					=============
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TODO
 | 
					Support display_jpeg() in IPython
 | 
				
			||||||
^^^^
 | 
					^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TODO
 | 
					In addition to ``display()`` and ``display_png``, ``display_jpeg()`` can now
 | 
				
			||||||
 | 
					also be used to display images in IPython::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    from PIL import Image
 | 
				
			||||||
 | 
					    from IPython.display import display_jpeg
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    im = Image.new("RGB", (100, 100), (255, 0, 0))
 | 
				
			||||||
 | 
					    display_jpeg(im)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Support reading signed 8-bit TIFF images
 | 
				
			||||||
 | 
					^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TIFF images with signed integer data, 8 bits per sample and a photometric
 | 
				
			||||||
 | 
					interpretaton of BlackIsZero can now be read.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,7 +15,7 @@ open-source users (and will reach EOL on 2023-12-08 for commercial licence holde
 | 
				
			||||||
Support for PyQt5 and PySide2 has been deprecated from ``ImageQt`` and will be removed
 | 
					Support for PyQt5 and PySide2 has been deprecated from ``ImageQt`` and will be removed
 | 
				
			||||||
in Pillow 10 (2023-07-01). Upgrade to
 | 
					in Pillow 10 (2023-07-01). Upgrade to
 | 
				
			||||||
`PyQt6 <https://www.riverbankcomputing.com/static/Docs/PyQt6/>`_ or
 | 
					`PyQt6 <https://www.riverbankcomputing.com/static/Docs/PyQt6/>`_ or
 | 
				
			||||||
`PySide6 <https://doc.qt.io/qtforpython/>`_ instead.
 | 
					`PySide6 <https://doc.qt.io/qtforpython-6/>`_ instead.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
FreeTypeFont.getmask2 fill parameter
 | 
					FreeTypeFont.getmask2 fill parameter
 | 
				
			||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
					^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										6
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								setup.py
									
									
									
									
									
								
							| 
						 | 
					@ -10,6 +10,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
import re
 | 
					import re
 | 
				
			||||||
 | 
					import shutil
 | 
				
			||||||
import struct
 | 
					import struct
 | 
				
			||||||
import subprocess
 | 
					import subprocess
 | 
				
			||||||
import sys
 | 
					import sys
 | 
				
			||||||
| 
						 | 
					@ -150,6 +151,7 @@ def _dbg(s, tp=None):
 | 
				
			||||||
def _find_library_dirs_ldconfig():
 | 
					def _find_library_dirs_ldconfig():
 | 
				
			||||||
    # Based on ctypes.util from Python 2
 | 
					    # Based on ctypes.util from Python 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ldconfig = "ldconfig" if shutil.which("ldconfig") else "/sbin/ldconfig"
 | 
				
			||||||
    if sys.platform.startswith("linux") or sys.platform.startswith("gnu"):
 | 
					    if sys.platform.startswith("linux") or sys.platform.startswith("gnu"):
 | 
				
			||||||
        if struct.calcsize("l") == 4:
 | 
					        if struct.calcsize("l") == 4:
 | 
				
			||||||
            machine = os.uname()[4] + "-32"
 | 
					            machine = os.uname()[4] + "-32"
 | 
				
			||||||
| 
						 | 
					@ -166,14 +168,14 @@ def _find_library_dirs_ldconfig():
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Assuming GLIBC's ldconfig (with option -p)
 | 
					        # Assuming GLIBC's ldconfig (with option -p)
 | 
				
			||||||
        # Alpine Linux uses musl that can't print cache
 | 
					        # Alpine Linux uses musl that can't print cache
 | 
				
			||||||
        args = ["ldconfig", "-p"]
 | 
					        args = [ldconfig, "-p"]
 | 
				
			||||||
        expr = rf".*\({abi_type}.*\) => (.*)"
 | 
					        expr = rf".*\({abi_type}.*\) => (.*)"
 | 
				
			||||||
        env = dict(os.environ)
 | 
					        env = dict(os.environ)
 | 
				
			||||||
        env["LC_ALL"] = "C"
 | 
					        env["LC_ALL"] = "C"
 | 
				
			||||||
        env["LANG"] = "C"
 | 
					        env["LANG"] = "C"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    elif sys.platform.startswith("freebsd"):
 | 
					    elif sys.platform.startswith("freebsd"):
 | 
				
			||||||
        args = ["ldconfig", "-r"]
 | 
					        args = [ldconfig, "-r"]
 | 
				
			||||||
        expr = r".* => (.*)"
 | 
					        expr = r".* => (.*)"
 | 
				
			||||||
        env = {}
 | 
					        env = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -633,19 +633,34 @@ class Image:
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _repr_png_(self):
 | 
					    def _repr_image(self, image_format):
 | 
				
			||||||
        """iPython display hook support
 | 
					        """Helper function for iPython display hook.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        :returns: png version of the image as bytes
 | 
					        :param image_format: Image format.
 | 
				
			||||||
 | 
					        :returns: image as bytes, saved into the given format.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        b = io.BytesIO()
 | 
					        b = io.BytesIO()
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            self.save(b, "PNG")
 | 
					            self.save(b, image_format)
 | 
				
			||||||
        except Exception as e:
 | 
					        except Exception as e:
 | 
				
			||||||
            msg = "Could not save to PNG for display"
 | 
					            msg = f"Could not save to {image_format} for display"
 | 
				
			||||||
            raise ValueError(msg) from e
 | 
					            raise ValueError(msg) from e
 | 
				
			||||||
        return b.getvalue()
 | 
					        return b.getvalue()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _repr_png_(self):
 | 
				
			||||||
 | 
					        """iPython display hook support for PNG format.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :returns: PNG version of the image as bytes
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        return self._repr_image("PNG")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _repr_jpeg_(self):
 | 
				
			||||||
 | 
					        """iPython display hook support for JPEG format.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :returns: JPEG version of the image as bytes
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        return self._repr_image("JPEG")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def __array_interface__(self):
 | 
					    def __array_interface__(self):
 | 
				
			||||||
        # numpy array interface support
 | 
					        # numpy array interface support
 | 
				
			||||||
| 
						 | 
					@ -672,7 +687,8 @@ class Image:
 | 
				
			||||||
        return new
 | 
					        return new
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __getstate__(self):
 | 
					    def __getstate__(self):
 | 
				
			||||||
        return [self.info, self.mode, self.size, self.getpalette(), self.tobytes()]
 | 
					        im_data = self.tobytes()  # load image first
 | 
				
			||||||
 | 
					        return [self.info, self.mode, self.size, self.getpalette(), im_data]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __setstate__(self, state):
 | 
					    def __setstate__(self, state):
 | 
				
			||||||
        Image.__init__(self)
 | 
					        Image.__init__(self)
 | 
				
			||||||
| 
						 | 
					@ -1107,7 +1123,6 @@ class Image:
 | 
				
			||||||
           Available methods are :data:`Dither.NONE` or :data:`Dither.FLOYDSTEINBERG`
 | 
					           Available methods are :data:`Dither.NONE` or :data:`Dither.FLOYDSTEINBERG`
 | 
				
			||||||
           (default).
 | 
					           (default).
 | 
				
			||||||
        :returns: A new image
 | 
					        :returns: A new image
 | 
				
			||||||
 | 
					 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.load()
 | 
					        self.load()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -185,12 +185,8 @@ class ImageCmsProfile:
 | 
				
			||||||
    def _set(self, profile, filename=None):
 | 
					    def _set(self, profile, filename=None):
 | 
				
			||||||
        self.profile = profile
 | 
					        self.profile = profile
 | 
				
			||||||
        self.filename = filename
 | 
					        self.filename = filename
 | 
				
			||||||
        if profile:
 | 
					        self.product_name = None  # profile.product_name
 | 
				
			||||||
            self.product_name = None  # profile.product_name
 | 
					        self.product_info = None  # profile.product_info
 | 
				
			||||||
            self.product_info = None  # profile.product_info
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            self.product_name = None
 | 
					 | 
				
			||||||
            self.product_info = None
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def tobytes(self):
 | 
					    def tobytes(self):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -279,11 +279,11 @@ class ImageDraw:
 | 
				
			||||||
                self.im.paste(im.im, (0, 0) + im.size, mask.im)
 | 
					                self.im.paste(im.im, (0, 0) + im.size, mask.im)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def regular_polygon(
 | 
					    def regular_polygon(
 | 
				
			||||||
        self, bounding_circle, n_sides, rotation=0, fill=None, outline=None
 | 
					        self, bounding_circle, n_sides, rotation=0, fill=None, outline=None, width=1
 | 
				
			||||||
    ):
 | 
					    ):
 | 
				
			||||||
        """Draw a regular polygon."""
 | 
					        """Draw a regular polygon."""
 | 
				
			||||||
        xy = _compute_regular_polygon_vertices(bounding_circle, n_sides, rotation)
 | 
					        xy = _compute_regular_polygon_vertices(bounding_circle, n_sides, rotation)
 | 
				
			||||||
        self.polygon(xy, fill, outline)
 | 
					        self.polygon(xy, fill, outline, width)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def rectangle(self, xy, fill=None, outline=None, width=1):
 | 
					    def rectangle(self, xy, fill=None, outline=None, width=1):
 | 
				
			||||||
        """Draw a rectangle."""
 | 
					        """Draw a rectangle."""
 | 
				
			||||||
| 
						 | 
					@ -314,11 +314,11 @@ class ImageDraw:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        full_x, full_y = False, False
 | 
					        full_x, full_y = False, False
 | 
				
			||||||
        if all(corners):
 | 
					        if all(corners):
 | 
				
			||||||
            full_x = d >= x1 - x0
 | 
					            full_x = d >= x1 - x0 - 1
 | 
				
			||||||
            if full_x:
 | 
					            if full_x:
 | 
				
			||||||
                # The two left and two right corners are joined
 | 
					                # The two left and two right corners are joined
 | 
				
			||||||
                d = x1 - x0
 | 
					                d = x1 - x0
 | 
				
			||||||
            full_y = d >= y1 - y0
 | 
					            full_y = d >= y1 - y0 - 1
 | 
				
			||||||
            if full_y:
 | 
					            if full_y:
 | 
				
			||||||
                # The two top and two bottom corners are joined
 | 
					                # The two top and two bottom corners are joined
 | 
				
			||||||
                d = y1 - y0
 | 
					                d = y1 - y0
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -61,7 +61,17 @@ def grab(bbox=None, include_layered_windows=False, all_screens=False, xdisplay=N
 | 
				
			||||||
                left, top, right, bottom = bbox
 | 
					                left, top, right, bottom = bbox
 | 
				
			||||||
                im = im.crop((left - x0, top - y0, right - x0, bottom - y0))
 | 
					                im = im.crop((left - x0, top - y0, right - x0, bottom - y0))
 | 
				
			||||||
            return im
 | 
					            return im
 | 
				
			||||||
        elif shutil.which("gnome-screenshot"):
 | 
					    try:
 | 
				
			||||||
 | 
					        if not Image.core.HAVE_XCB:
 | 
				
			||||||
 | 
					            msg = "Pillow was built without XCB support"
 | 
				
			||||||
 | 
					            raise OSError(msg)
 | 
				
			||||||
 | 
					        size, data = Image.core.grabscreen_x11(xdisplay)
 | 
				
			||||||
 | 
					    except OSError:
 | 
				
			||||||
 | 
					        if (
 | 
				
			||||||
 | 
					            xdisplay is None
 | 
				
			||||||
 | 
					            and sys.platform not in ("darwin", "win32")
 | 
				
			||||||
 | 
					            and shutil.which("gnome-screenshot")
 | 
				
			||||||
 | 
					        ):
 | 
				
			||||||
            fh, filepath = tempfile.mkstemp(".png")
 | 
					            fh, filepath = tempfile.mkstemp(".png")
 | 
				
			||||||
            os.close(fh)
 | 
					            os.close(fh)
 | 
				
			||||||
            subprocess.call(["gnome-screenshot", "-f", filepath])
 | 
					            subprocess.call(["gnome-screenshot", "-f", filepath])
 | 
				
			||||||
| 
						 | 
					@ -73,15 +83,13 @@ def grab(bbox=None, include_layered_windows=False, all_screens=False, xdisplay=N
 | 
				
			||||||
                im.close()
 | 
					                im.close()
 | 
				
			||||||
                return im_cropped
 | 
					                return im_cropped
 | 
				
			||||||
            return im
 | 
					            return im
 | 
				
			||||||
    # use xdisplay=None for default display on non-win32/macOS systems
 | 
					        else:
 | 
				
			||||||
    if not Image.core.HAVE_XCB:
 | 
					            raise
 | 
				
			||||||
        msg = "Pillow was built without XCB support"
 | 
					    else:
 | 
				
			||||||
        raise OSError(msg)
 | 
					        im = Image.frombytes("RGB", size, data, "raw", "BGRX", size[0] * 4, 1)
 | 
				
			||||||
    size, data = Image.core.grabscreen_x11(xdisplay)
 | 
					        if bbox:
 | 
				
			||||||
    im = Image.frombytes("RGB", size, data, "raw", "BGRX", size[0] * 4, 1)
 | 
					            im = im.crop(bbox)
 | 
				
			||||||
    if bbox:
 | 
					        return im
 | 
				
			||||||
        im = im.crop(bbox)
 | 
					 | 
				
			||||||
    return im
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def grabclipboard():
 | 
					def grabclipboard():
 | 
				
			||||||
| 
						 | 
					@ -156,8 +164,11 @@ def grabclipboard():
 | 
				
			||||||
            msg = "wl-paste or xclip is required for ImageGrab.grabclipboard() on Linux"
 | 
					            msg = "wl-paste or xclip is required for ImageGrab.grabclipboard() on Linux"
 | 
				
			||||||
            raise NotImplementedError(msg)
 | 
					            raise NotImplementedError(msg)
 | 
				
			||||||
        fh, filepath = tempfile.mkstemp()
 | 
					        fh, filepath = tempfile.mkstemp()
 | 
				
			||||||
        subprocess.call(args, stdout=fh)
 | 
					        err = subprocess.run(args, stdout=fh, stderr=subprocess.PIPE).stderr
 | 
				
			||||||
        os.close(fh)
 | 
					        os.close(fh)
 | 
				
			||||||
 | 
					        if err:
 | 
				
			||||||
 | 
					            msg = f"{args[0]} error: {err.strip().decode()}"
 | 
				
			||||||
 | 
					            raise ChildProcessError(msg)
 | 
				
			||||||
        im = Image.open(filepath)
 | 
					        im = Image.open(filepath)
 | 
				
			||||||
        im.load()
 | 
					        im.load()
 | 
				
			||||||
        os.unlink(filepath)
 | 
					        os.unlink(filepath)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -957,14 +957,11 @@ class PdfParser:
 | 
				
			||||||
                check_format_condition(m, "xref entry not found")
 | 
					                check_format_condition(m, "xref entry not found")
 | 
				
			||||||
                offset = m.end()
 | 
					                offset = m.end()
 | 
				
			||||||
                is_free = m.group(3) == b"f"
 | 
					                is_free = m.group(3) == b"f"
 | 
				
			||||||
                generation = int(m.group(2))
 | 
					 | 
				
			||||||
                if not is_free:
 | 
					                if not is_free:
 | 
				
			||||||
 | 
					                    generation = int(m.group(2))
 | 
				
			||||||
                    new_entry = (int(m.group(1)), generation)
 | 
					                    new_entry = (int(m.group(1)), generation)
 | 
				
			||||||
                    check_format_condition(
 | 
					                    if i not in self.xref_table:
 | 
				
			||||||
                        i not in self.xref_table or self.xref_table[i] == new_entry,
 | 
					                        self.xref_table[i] = new_entry
 | 
				
			||||||
                        "xref entry duplicated (and not identical)",
 | 
					 | 
				
			||||||
                    )
 | 
					 | 
				
			||||||
                    self.xref_table[i] = new_entry
 | 
					 | 
				
			||||||
        return offset
 | 
					        return offset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def read_indirect(self, ref, max_nesting=-1):
 | 
					    def read_indirect(self, ref, max_nesting=-1):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -170,6 +170,8 @@ OPEN_INFO = {
 | 
				
			||||||
    (MM, 0, (1,), 2, (8,), ()): ("L", "L;IR"),
 | 
					    (MM, 0, (1,), 2, (8,), ()): ("L", "L;IR"),
 | 
				
			||||||
    (II, 1, (1,), 1, (8,), ()): ("L", "L"),
 | 
					    (II, 1, (1,), 1, (8,), ()): ("L", "L"),
 | 
				
			||||||
    (MM, 1, (1,), 1, (8,), ()): ("L", "L"),
 | 
					    (MM, 1, (1,), 1, (8,), ()): ("L", "L"),
 | 
				
			||||||
 | 
					    (II, 1, (2,), 1, (8,), ()): ("L", "L"),
 | 
				
			||||||
 | 
					    (MM, 1, (2,), 1, (8,), ()): ("L", "L"),
 | 
				
			||||||
    (II, 1, (1,), 2, (8,), ()): ("L", "L;R"),
 | 
					    (II, 1, (1,), 2, (8,), ()): ("L", "L;R"),
 | 
				
			||||||
    (MM, 1, (1,), 2, (8,), ()): ("L", "L;R"),
 | 
					    (MM, 1, (1,), 2, (8,), ()): ("L", "L;R"),
 | 
				
			||||||
    (II, 1, (1,), 1, (12,), ()): ("I;16", "I;12"),
 | 
					    (II, 1, (1,), 1, (12,), ()): ("I;16", "I;12"),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -116,7 +116,9 @@ getfont(PyObject *self_, PyObject *args, PyObject *kw) {
 | 
				
			||||||
    int error = 0;
 | 
					    int error = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    char *filename = NULL;
 | 
					    char *filename = NULL;
 | 
				
			||||||
    Py_ssize_t size;
 | 
					    float size;
 | 
				
			||||||
 | 
					    FT_Size_RequestRec req;
 | 
				
			||||||
 | 
					    FT_Long width;
 | 
				
			||||||
    Py_ssize_t index = 0;
 | 
					    Py_ssize_t index = 0;
 | 
				
			||||||
    Py_ssize_t layout_engine = 0;
 | 
					    Py_ssize_t layout_engine = 0;
 | 
				
			||||||
    unsigned char *encoding;
 | 
					    unsigned char *encoding;
 | 
				
			||||||
| 
						 | 
					@ -133,7 +135,7 @@ getfont(PyObject *self_, PyObject *args, PyObject *kw) {
 | 
				
			||||||
    if (!PyArg_ParseTupleAndKeywords(
 | 
					    if (!PyArg_ParseTupleAndKeywords(
 | 
				
			||||||
            args,
 | 
					            args,
 | 
				
			||||||
            kw,
 | 
					            kw,
 | 
				
			||||||
            "etn|nsy#n",
 | 
					            "etf|nsy#n",
 | 
				
			||||||
            kwlist,
 | 
					            kwlist,
 | 
				
			||||||
            Py_FileSystemDefaultEncoding,
 | 
					            Py_FileSystemDefaultEncoding,
 | 
				
			||||||
            &filename,
 | 
					            &filename,
 | 
				
			||||||
| 
						 | 
					@ -179,7 +181,13 @@ getfont(PyObject *self_, PyObject *args, PyObject *kw) {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!error) {
 | 
					    if (!error) {
 | 
				
			||||||
        error = FT_Set_Pixel_Sizes(self->face, 0, size);
 | 
					        width = size * 64;
 | 
				
			||||||
 | 
					        req.type = FT_SIZE_REQUEST_TYPE_NOMINAL;
 | 
				
			||||||
 | 
					        req.width = width;
 | 
				
			||||||
 | 
					        req.height = width;
 | 
				
			||||||
 | 
					        req.horiResolution = 0;
 | 
				
			||||||
 | 
					        req.vertResolution = 0;
 | 
				
			||||||
 | 
					        error = FT_Request_Size(self->face, &req);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!error && encoding && strlen((char *)encoding) == 4) {
 | 
					    if (!error && encoding && strlen((char *)encoding) == 4) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -37,6 +37,17 @@ clip8(float in) {
 | 
				
			||||||
    return (UINT8)in;
 | 
					    return (UINT8)in;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline INT32
 | 
				
			||||||
 | 
					clip32(float in) {
 | 
				
			||||||
 | 
					    if (in <= 0.0) {
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (in >= pow(2, 31) - 1) {
 | 
				
			||||||
 | 
					        return pow(2, 31) - 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return (INT32)in;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Imaging
 | 
					Imaging
 | 
				
			||||||
ImagingExpand(Imaging imIn, int xmargin, int ymargin, int mode) {
 | 
					ImagingExpand(Imaging imIn, int xmargin, int ymargin, int mode) {
 | 
				
			||||||
    Imaging imOut;
 | 
					    Imaging imOut;
 | 
				
			||||||
| 
						 | 
					@ -96,8 +107,8 @@ ImagingExpand(Imaging imIn, int xmargin, int ymargin, int mode) {
 | 
				
			||||||
void
 | 
					void
 | 
				
			||||||
ImagingFilter3x3(Imaging imOut, Imaging im, const float *kernel, float offset) {
 | 
					ImagingFilter3x3(Imaging imOut, Imaging im, const float *kernel, float offset) {
 | 
				
			||||||
#define KERNEL1x3(in0, x, kernel, d)                                             \
 | 
					#define KERNEL1x3(in0, x, kernel, d)                                             \
 | 
				
			||||||
    (_i2f((UINT8)in0[x - d]) * (kernel)[0] + _i2f((UINT8)in0[x]) * (kernel)[1] + \
 | 
					    (_i2f(in0[x - d]) * (kernel)[0] + _i2f(in0[x]) * (kernel)[1] + \
 | 
				
			||||||
     _i2f((UINT8)in0[x + d]) * (kernel)[2])
 | 
					     _i2f(in0[x + d]) * (kernel)[2])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    int x = 0, y = 0;
 | 
					    int x = 0, y = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -105,21 +116,40 @@ ImagingFilter3x3(Imaging imOut, Imaging im, const float *kernel, float offset) {
 | 
				
			||||||
    if (im->bands == 1) {
 | 
					    if (im->bands == 1) {
 | 
				
			||||||
        // Add one time for rounding
 | 
					        // Add one time for rounding
 | 
				
			||||||
        offset += 0.5;
 | 
					        offset += 0.5;
 | 
				
			||||||
        for (y = 1; y < im->ysize - 1; y++) {
 | 
					        if (im->type == IMAGING_TYPE_INT32) {
 | 
				
			||||||
            UINT8 *in_1 = (UINT8 *)im->image[y - 1];
 | 
					            for (y = 1; y < im->ysize - 1; y++) {
 | 
				
			||||||
            UINT8 *in0 = (UINT8 *)im->image[y];
 | 
					                INT32 *in_1 = (INT32 *)im->image[y - 1];
 | 
				
			||||||
            UINT8 *in1 = (UINT8 *)im->image[y + 1];
 | 
					                INT32 *in0 = (INT32 *)im->image[y];
 | 
				
			||||||
            UINT8 *out = (UINT8 *)imOut->image[y];
 | 
					                INT32 *in1 = (INT32 *)im->image[y + 1];
 | 
				
			||||||
 | 
					                INT32 *out = (INT32 *)imOut->image[y];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            out[0] = in0[0];
 | 
					                out[0] = in0[0];
 | 
				
			||||||
            for (x = 1; x < im->xsize - 1; x++) {
 | 
					                for (x = 1; x < im->xsize - 1; x++) {
 | 
				
			||||||
                float ss = offset;
 | 
					                    float ss = offset;
 | 
				
			||||||
                ss += KERNEL1x3(in1, x, &kernel[0], 1);
 | 
					                    ss += KERNEL1x3(in1, x, &kernel[0], 1);
 | 
				
			||||||
                ss += KERNEL1x3(in0, x, &kernel[3], 1);
 | 
					                    ss += KERNEL1x3(in0, x, &kernel[3], 1);
 | 
				
			||||||
                ss += KERNEL1x3(in_1, x, &kernel[6], 1);
 | 
					                    ss += KERNEL1x3(in_1, x, &kernel[6], 1);
 | 
				
			||||||
                out[x] = clip8(ss);
 | 
					                    out[x] = clip32(ss);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                out[x] = in0[x];
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            for (y = 1; y < im->ysize - 1; y++) {
 | 
				
			||||||
 | 
					                UINT8 *in_1 = (UINT8 *)im->image[y - 1];
 | 
				
			||||||
 | 
					                UINT8 *in0 = (UINT8 *)im->image[y];
 | 
				
			||||||
 | 
					                UINT8 *in1 = (UINT8 *)im->image[y + 1];
 | 
				
			||||||
 | 
					                UINT8 *out = (UINT8 *)imOut->image[y];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                out[0] = in0[0];
 | 
				
			||||||
 | 
					                for (x = 1; x < im->xsize - 1; x++) {
 | 
				
			||||||
 | 
					                    float ss = offset;
 | 
				
			||||||
 | 
					                    ss += KERNEL1x3(in1, x, &kernel[0], 1);
 | 
				
			||||||
 | 
					                    ss += KERNEL1x3(in0, x, &kernel[3], 1);
 | 
				
			||||||
 | 
					                    ss += KERNEL1x3(in_1, x, &kernel[6], 1);
 | 
				
			||||||
 | 
					                    out[x] = clip8(ss);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                out[x] = in0[x];
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            out[x] = in0[x];
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        // Add one time for rounding
 | 
					        // Add one time for rounding
 | 
				
			||||||
| 
						 | 
					@ -195,10 +225,10 @@ ImagingFilter3x3(Imaging imOut, Imaging im, const float *kernel, float offset) {
 | 
				
			||||||
void
 | 
					void
 | 
				
			||||||
ImagingFilter5x5(Imaging imOut, Imaging im, const float *kernel, float offset) {
 | 
					ImagingFilter5x5(Imaging imOut, Imaging im, const float *kernel, float offset) {
 | 
				
			||||||
#define KERNEL1x5(in0, x, kernel, d)                                             \
 | 
					#define KERNEL1x5(in0, x, kernel, d)                                             \
 | 
				
			||||||
    (_i2f((UINT8)in0[x - d - d]) * (kernel)[0] +                                 \
 | 
					    (_i2f(in0[x - d - d]) * (kernel)[0] +                                 \
 | 
				
			||||||
     _i2f((UINT8)in0[x - d]) * (kernel)[1] + _i2f((UINT8)in0[x]) * (kernel)[2] + \
 | 
					     _i2f(in0[x - d]) * (kernel)[1] + _i2f(in0[x]) * (kernel)[2] + \
 | 
				
			||||||
     _i2f((UINT8)in0[x + d]) * (kernel)[3] +                                     \
 | 
					     _i2f(in0[x + d]) * (kernel)[3] +                                     \
 | 
				
			||||||
     _i2f((UINT8)in0[x + d + d]) * (kernel)[4])
 | 
					     _i2f(in0[x + d + d]) * (kernel)[4])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    int x = 0, y = 0;
 | 
					    int x = 0, y = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -207,27 +237,52 @@ ImagingFilter5x5(Imaging imOut, Imaging im, const float *kernel, float offset) {
 | 
				
			||||||
    if (im->bands == 1) {
 | 
					    if (im->bands == 1) {
 | 
				
			||||||
        // Add one time for rounding
 | 
					        // Add one time for rounding
 | 
				
			||||||
        offset += 0.5;
 | 
					        offset += 0.5;
 | 
				
			||||||
        for (y = 2; y < im->ysize - 2; y++) {
 | 
					        if (im->type == IMAGING_TYPE_INT32) {
 | 
				
			||||||
            UINT8 *in_2 = (UINT8 *)im->image[y - 2];
 | 
					            for (y = 2; y < im->ysize - 2; y++) {
 | 
				
			||||||
            UINT8 *in_1 = (UINT8 *)im->image[y - 1];
 | 
					                INT32 *in_2 = (INT32 *)im->image[y - 2];
 | 
				
			||||||
            UINT8 *in0 = (UINT8 *)im->image[y];
 | 
					                INT32 *in_1 = (INT32 *)im->image[y - 1];
 | 
				
			||||||
            UINT8 *in1 = (UINT8 *)im->image[y + 1];
 | 
					                INT32 *in0 = (INT32 *)im->image[y];
 | 
				
			||||||
            UINT8 *in2 = (UINT8 *)im->image[y + 2];
 | 
					                INT32 *in1 = (INT32 *)im->image[y + 1];
 | 
				
			||||||
            UINT8 *out = (UINT8 *)imOut->image[y];
 | 
					                INT32 *in2 = (INT32 *)im->image[y + 2];
 | 
				
			||||||
 | 
					                INT32 *out = (INT32 *)imOut->image[y];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            out[0] = in0[0];
 | 
					                out[0] = in0[0];
 | 
				
			||||||
            out[1] = in0[1];
 | 
					                out[1] = in0[1];
 | 
				
			||||||
            for (x = 2; x < im->xsize - 2; x++) {
 | 
					                for (x = 2; x < im->xsize - 2; x++) {
 | 
				
			||||||
                float ss = offset;
 | 
					                    float ss = offset;
 | 
				
			||||||
                ss += KERNEL1x5(in2, x, &kernel[0], 1);
 | 
					                    ss += KERNEL1x5(in2, x, &kernel[0], 1);
 | 
				
			||||||
                ss += KERNEL1x5(in1, x, &kernel[5], 1);
 | 
					                    ss += KERNEL1x5(in1, x, &kernel[5], 1);
 | 
				
			||||||
                ss += KERNEL1x5(in0, x, &kernel[10], 1);
 | 
					                    ss += KERNEL1x5(in0, x, &kernel[10], 1);
 | 
				
			||||||
                ss += KERNEL1x5(in_1, x, &kernel[15], 1);
 | 
					                    ss += KERNEL1x5(in_1, x, &kernel[15], 1);
 | 
				
			||||||
                ss += KERNEL1x5(in_2, x, &kernel[20], 1);
 | 
					                    ss += KERNEL1x5(in_2, x, &kernel[20], 1);
 | 
				
			||||||
                out[x] = clip8(ss);
 | 
					                    out[x] = clip32(ss);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                out[x + 0] = in0[x + 0];
 | 
				
			||||||
 | 
					                out[x + 1] = in0[x + 1];
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            for (y = 2; y < im->ysize - 2; y++) {
 | 
				
			||||||
 | 
					                UINT8 *in_2 = (UINT8 *)im->image[y - 2];
 | 
				
			||||||
 | 
					                UINT8 *in_1 = (UINT8 *)im->image[y - 1];
 | 
				
			||||||
 | 
					                UINT8 *in0 = (UINT8 *)im->image[y];
 | 
				
			||||||
 | 
					                UINT8 *in1 = (UINT8 *)im->image[y + 1];
 | 
				
			||||||
 | 
					                UINT8 *in2 = (UINT8 *)im->image[y + 2];
 | 
				
			||||||
 | 
					                UINT8 *out = (UINT8 *)imOut->image[y];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                out[0] = in0[0];
 | 
				
			||||||
 | 
					                out[1] = in0[1];
 | 
				
			||||||
 | 
					                for (x = 2; x < im->xsize - 2; x++) {
 | 
				
			||||||
 | 
					                    float ss = offset;
 | 
				
			||||||
 | 
					                    ss += KERNEL1x5(in2, x, &kernel[0], 1);
 | 
				
			||||||
 | 
					                    ss += KERNEL1x5(in1, x, &kernel[5], 1);
 | 
				
			||||||
 | 
					                    ss += KERNEL1x5(in0, x, &kernel[10], 1);
 | 
				
			||||||
 | 
					                    ss += KERNEL1x5(in_1, x, &kernel[15], 1);
 | 
				
			||||||
 | 
					                    ss += KERNEL1x5(in_2, x, &kernel[20], 1);
 | 
				
			||||||
 | 
					                    out[x] = clip8(ss);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                out[x + 0] = in0[x + 0];
 | 
				
			||||||
 | 
					                out[x + 1] = in0[x + 1];
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            out[x + 0] = in0[x + 0];
 | 
					 | 
				
			||||||
            out[x + 1] = in0[x + 1];
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        // Add one time for rounding
 | 
					        // Add one time for rounding
 | 
				
			||||||
| 
						 | 
					@ -327,7 +382,7 @@ ImagingFilter(Imaging im, int xsize, int ysize, const FLOAT32 *kernel, FLOAT32 o
 | 
				
			||||||
    Imaging imOut;
 | 
					    Imaging imOut;
 | 
				
			||||||
    ImagingSectionCookie cookie;
 | 
					    ImagingSectionCookie cookie;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!im || im->type != IMAGING_TYPE_UINT8) {
 | 
					    if (im->type != IMAGING_TYPE_UINT8 && im->type != IMAGING_TYPE_INT32) {
 | 
				
			||||||
        return (Imaging)ImagingError_ModeError();
 | 
					        return (Imaging)ImagingError_ModeError();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1149,6 +1149,16 @@ unpackI16N_I16(UINT8 *out, const UINT8 *in, int pixels) {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
 | 
					unpackI16B_I16(UINT8 *out, const UINT8 *in, int pixels) {
 | 
				
			||||||
 | 
					    int i;
 | 
				
			||||||
 | 
					    for (i = 0; i < pixels; i++) {
 | 
				
			||||||
 | 
					        out[0] = in[1];
 | 
				
			||||||
 | 
					        out[1] = in[0];
 | 
				
			||||||
 | 
					        in += 2;
 | 
				
			||||||
 | 
					        out += 2;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
unpackI16R_I16(UINT8 *out, const UINT8 *in, int pixels) {
 | 
					unpackI16R_I16(UINT8 *out, const UINT8 *in, int pixels) {
 | 
				
			||||||
    int i;
 | 
					    int i;
 | 
				
			||||||
    for (i = 0; i < pixels; i++) {
 | 
					    for (i = 0; i < pixels; i++) {
 | 
				
			||||||
| 
						 | 
					@ -1764,6 +1774,7 @@ static struct {
 | 
				
			||||||
    {"I;16L", "I;16L", 16, copy2},
 | 
					    {"I;16L", "I;16L", 16, copy2},
 | 
				
			||||||
    {"I;16N", "I;16N", 16, copy2},
 | 
					    {"I;16N", "I;16N", 16, copy2},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    {"I;16", "I;16B", 16, unpackI16B_I16},
 | 
				
			||||||
    {"I;16", "I;16N", 16, unpackI16N_I16},   // LibTiff native->image endian.
 | 
					    {"I;16", "I;16N", 16, unpackI16N_I16},   // LibTiff native->image endian.
 | 
				
			||||||
    {"I;16L", "I;16N", 16, unpackI16N_I16},  // LibTiff native->image endian.
 | 
					    {"I;16L", "I;16N", 16, unpackI16N_I16},  // LibTiff native->image endian.
 | 
				
			||||||
    {"I;16B", "I;16N", 16, unpackI16N_I16B},
 | 
					    {"I;16B", "I;16N", 16, unpackI16N_I16B},
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										7
									
								
								tox.ini
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								tox.ini
									
									
									
									
									
								
							| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
[tox]
 | 
					[tox]
 | 
				
			||||||
minversion = 1.9
 | 
					requires =
 | 
				
			||||||
envlist =
 | 
					    tox>=4.2
 | 
				
			||||||
 | 
					env_list =
 | 
				
			||||||
    lint
 | 
					    lint
 | 
				
			||||||
    py{py3, 311, 310, 39, 38}
 | 
					    py{py3, 311, 310, 39, 38}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,7 +24,7 @@ skip_install = true
 | 
				
			||||||
deps =
 | 
					deps =
 | 
				
			||||||
    check-manifest
 | 
					    check-manifest
 | 
				
			||||||
    pre-commit
 | 
					    pre-commit
 | 
				
			||||||
passenv =
 | 
					pass_env =
 | 
				
			||||||
    PRE_COMMIT_COLOR
 | 
					    PRE_COMMIT_COLOR
 | 
				
			||||||
commands =
 | 
					commands =
 | 
				
			||||||
    pre-commit run --all-files --show-diff-on-failure
 | 
					    pre-commit run --all-files --show-diff-on-failure
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -337,9 +337,9 @@ deps = {
 | 
				
			||||||
        "libs": [r"imagequant.lib"],
 | 
					        "libs": [r"imagequant.lib"],
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "harfbuzz": {
 | 
					    "harfbuzz": {
 | 
				
			||||||
        "url": "https://github.com/harfbuzz/harfbuzz/archive/7.1.0.zip",
 | 
					        "url": "https://github.com/harfbuzz/harfbuzz/archive/7.3.0.zip",
 | 
				
			||||||
        "filename": "harfbuzz-7.1.0.zip",
 | 
					        "filename": "harfbuzz-7.3.0.zip",
 | 
				
			||||||
        "dir": "harfbuzz-7.1.0",
 | 
					        "dir": "harfbuzz-7.3.0",
 | 
				
			||||||
        "license": "COPYING",
 | 
					        "license": "COPYING",
 | 
				
			||||||
        "build": [
 | 
					        "build": [
 | 
				
			||||||
            *cmds_cmake(
 | 
					            *cmds_cmake(
 | 
				
			||||||
| 
						 | 
					@ -352,12 +352,12 @@ deps = {
 | 
				
			||||||
        "libs": [r"*.lib"],
 | 
					        "libs": [r"*.lib"],
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "fribidi": {
 | 
					    "fribidi": {
 | 
				
			||||||
        "url": "https://github.com/fribidi/fribidi/archive/v1.0.12.zip",
 | 
					        "url": "https://github.com/fribidi/fribidi/archive/v1.0.13.zip",
 | 
				
			||||||
        "filename": "fribidi-1.0.12.zip",
 | 
					        "filename": "fribidi-1.0.13.zip",
 | 
				
			||||||
        "dir": "fribidi-1.0.12",
 | 
					        "dir": "fribidi-1.0.13",
 | 
				
			||||||
        "license": "COPYING",
 | 
					        "license": "COPYING",
 | 
				
			||||||
        "build": [
 | 
					        "build": [
 | 
				
			||||||
            cmd_copy(r"COPYING", r"{bin_dir}\fribidi-1.0.12-COPYING"),
 | 
					            cmd_copy(r"COPYING", r"{bin_dir}\fribidi-1.0.13-COPYING"),
 | 
				
			||||||
            cmd_copy(r"{winbuild_dir}\fribidi.cmake", r"CMakeLists.txt"),
 | 
					            cmd_copy(r"{winbuild_dir}\fribidi.cmake", r"CMakeLists.txt"),
 | 
				
			||||||
            *cmds_cmake("fribidi"),
 | 
					            *cmds_cmake("fribidi"),
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user