Merge branch 'main' into eps_plugin_perf

This commit is contained in:
Yay295 2023-02-06 17:50:18 -06:00
commit 35463e8ca3
128 changed files with 149 additions and 365 deletions

View File

@ -34,18 +34,34 @@ jobs:
with: with:
platform: x86_64 platform: x86_64
packages: > packages: >
ImageMagick gcc-g++ ghostscript jpeg libfreetype-devel gcc-g++
libimagequant-devel libjpeg-devel liblapack-devel ghostscript
liblcms2-devel libopenjp2-devel libraqm-devel ImageMagick
libtiff-devel libwebp-devel libxcb-devel libxcb-xinerama0 jpeg
make netpbm perl libfreetype-devel
libimagequant-devel
libjpeg-devel
liblapack-devel
liblcms2-devel
libopenjp2-devel
libraqm-devel
libtiff-devel
libwebp-devel
libxcb-devel
libxcb-xinerama0
make
netpbm
perl
python3${{ matrix.python-minor-version }}-cffi python3${{ matrix.python-minor-version }}-cffi
python3${{ matrix.python-minor-version }}-cython python3${{ matrix.python-minor-version }}-cython
python3${{ matrix.python-minor-version }}-devel python3${{ matrix.python-minor-version }}-devel
python3${{ matrix.python-minor-version }}-numpy python3${{ matrix.python-minor-version }}-numpy
python3${{ matrix.python-minor-version }}-sip python3${{ matrix.python-minor-version }}-sip
python3${{ matrix.python-minor-version }}-tkinter python3${{ matrix.python-minor-version }}-tkinter
qt5-devel-tools subversion xorg-server-extra zlib-devel qt5-devel-tools
subversion
xorg-server-extra
zlib-devel
- name: Add Lapack to PATH - name: Add Lapack to PATH
uses: egor-tensin/cleanup-path@v3 uses: egor-tensin/cleanup-path@v3

View File

@ -45,11 +45,6 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: | run: |
pacman -S --noconfirm \ pacman -S --noconfirm \
${{ matrix.package }}-python3-cffi \
${{ matrix.package }}-python3-numpy \
${{ matrix.package }}-python3-olefile \
${{ matrix.package }}-python3-pip \
${{ matrix.package }}-python3-setuptools \
${{ matrix.package }}-freetype \ ${{ matrix.package }}-freetype \
${{ matrix.package }}-gcc \ ${{ matrix.package }}-gcc \
${{ matrix.package }}-ghostscript \ ${{ matrix.package }}-ghostscript \
@ -60,6 +55,11 @@ jobs:
${{ matrix.package }}-libtiff \ ${{ matrix.package }}-libtiff \
${{ matrix.package }}-libwebp \ ${{ matrix.package }}-libwebp \
${{ matrix.package }}-openjpeg2 \ ${{ matrix.package }}-openjpeg2 \
${{ matrix.package }}-python3-cffi \
${{ matrix.package }}-python3-numpy \
${{ matrix.package }}-python3-olefile \
${{ matrix.package }}-python3-pip \
${{ matrix.package }}-python3-setuptools \
subversion subversion
if [ ${{ matrix.package }} == "mingw-w64-x86_64" ]; then if [ ${{ matrix.package }} == "mingw-w64-x86_64" ]; then

View File

@ -1,6 +1,6 @@
repos: repos:
- repo: https://github.com/psf/black - repo: https://github.com/psf/black
rev: 22.12.0 rev: 23.1.0
hooks: hooks:
- id: black - id: black
args: [--target-version=py37] args: [--target-version=py37]
@ -9,7 +9,7 @@ repos:
types: [] types: []
- repo: https://github.com/PyCQA/isort - repo: https://github.com/PyCQA/isort
rev: 5.11.4 rev: 5.12.0
hooks: hooks:
- id: isort - id: isort
@ -26,7 +26,7 @@ repos:
- id: yesqa - id: yesqa
- repo: https://github.com/Lucas-C/pre-commit-hooks - repo: https://github.com/Lucas-C/pre-commit-hooks
rev: v1.3.1 rev: v1.4.2
hooks: hooks:
- id: remove-tabs - id: remove-tabs
exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.opt$) exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.opt$)
@ -39,7 +39,7 @@ repos:
[flake8-2020, flake8-errmsg, flake8-implicit-str-concat] [flake8-2020, flake8-errmsg, flake8-implicit-str-concat]
- repo: https://github.com/pre-commit/pygrep-hooks - repo: https://github.com/pre-commit/pygrep-hooks
rev: v1.9.0 rev: v1.10.0
hooks: hooks:
- id: python-check-blanket-noqa - id: python-check-blanket-noqa
- id: rst-backticks - id: rst-backticks
@ -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: 0.5.2 rev: 0.6.1
hooks: hooks:
- id: tox-ini-fmt - id: tox-ini-fmt

View File

@ -5,6 +5,24 @@ Changelog (Pillow)
9.5.0 (unreleased) 9.5.0 (unreleased)
------------------ ------------------
- Handle more than one directory returned by pkg-config #6896
[sebastic, radarhere]
- Do not retry past formats when loading all formats for the first time #6902
[radarhere]
- Do not retry specified formats if they failed when opening #6893
[radarhere]
- Do not unintentionally load TIFF format at first #6892
[radarhere]
- Stop reading when EPS line becomes too long #6897
[radarhere]
- Allow writing IFDRational to BYTE tag #6890
[radarhere]
- Raise ValueError for BoxBlur filter with negative radius #6874 - Raise ValueError for BoxBlur filter with negative radius #6874
[hugovk, radarhere] [hugovk, radarhere]

View File

@ -5,7 +5,7 @@ The Python Imaging Library (PIL) is
Pillow is the friendly PIL fork. It is Pillow is the friendly PIL fork. It is
Copyright © 2010-2023 by Alex Clark and contributors Copyright © 2010-2023 by Jeffrey A. Clark (Alex) and contributors.
Like PIL, Pillow is licensed under the open source HPND License: Like PIL, Pillow is licensed under the open source HPND License:

View File

@ -6,8 +6,8 @@
## Python Imaging Library (Fork) ## Python Imaging Library (Fork)
Pillow is the friendly PIL fork by [Alex Clark and Pillow is the friendly PIL fork by [Jeffrey A. Clark (Alex) and
Contributors](https://github.com/python-pillow/Pillow/graphs/contributors). contributors](https://github.com/python-pillow/Pillow/graphs/contributors).
PIL is the Python Imaging Library by Fredrik Lundh and Contributors. PIL is the Python Imaging Library by Fredrik Lundh and Contributors.
As of 2019, Pillow development is As of 2019, Pillow development is
[supported by Tidelift](https://tidelift.com/subscription/pkg/pypi-pillow?utm_source=pypi-pillow&utm_medium=readme&utm_campaign=enterprise). [supported by Tidelift](https://tidelift.com/subscription/pkg/pypi-pillow?utm_source=pypi-pillow&utm_medium=readme&utm_campaign=enterprise).

View File

@ -4,7 +4,6 @@ TEST_FILE = "Tests/images/fli_overflow.fli"
def test_fli_overflow(): def test_fli_overflow():
# this should not crash with a malloc error or access violation # this should not crash with a malloc error or access violation
with Image.open(TEST_FILE) as im: with Image.open(TEST_FILE) as im:
im.load() im.load()

View File

@ -23,7 +23,6 @@ def test_ignore_dos_text():
def test_dos_text(): def test_dos_text():
try: try:
im = Image.open(TEST_FILE) im = Image.open(TEST_FILE)
im.load() im.load()

View File

@ -18,7 +18,6 @@ def test_bad():
"""These shouldn't crash/dos, but they shouldn't return anything """These shouldn't crash/dos, but they shouldn't return anything
either""" either"""
for f in get_files("b"): for f in get_files("b"):
# Assert that there is no unclosed file warning # Assert that there is no unclosed file warning
with warnings.catch_warnings(): with warnings.catch_warnings():
try: try:

View File

@ -11,6 +11,11 @@ from PIL import _deprecate
"Old thing is deprecated and will be removed in Pillow 10 " "Old thing is deprecated and will be removed in Pillow 10 "
r"\(2023-07-01\)\. Use new thing instead\.", r"\(2023-07-01\)\. Use new thing instead\.",
), ),
(
11,
"Old thing is deprecated and will be removed in Pillow 11 "
r"\(2024-10-15\)\. Use new thing instead\.",
),
( (
None, None,
r"Old thing is deprecated and will be removed in a future version\. " r"Old thing is deprecated and will be removed in a future version\. "

View File

@ -141,7 +141,6 @@ def test_rgba_bitfields():
# This test image has been manually hexedited # This test image has been manually hexedited
# to change the bitfield compression in the header from XBGR to RGBA # to change the bitfield compression in the header from XBGR to RGBA
with Image.open("Tests/images/rgb32bf-rgba.bmp") as im: with Image.open("Tests/images/rgb32bf-rgba.bmp") as im:
# So before the comparing the image, swap the channels # So before the comparing the image, swap the channels
b, g, r = im.split()[1:] b, g, r = im.split()[1:]
im = Image.merge("RGB", (r, g, b)) im = Image.merge("RGB", (r, g, b))

View File

@ -10,7 +10,6 @@ TEST_FILE = "Tests/images/gfs.t06z.rassda.tm00.bufr_d"
def test_open(): def test_open():
# Act # Act
with Image.open(TEST_FILE) as im: with Image.open(TEST_FILE) as im:
# Assert # Assert
assert im.format == "BUFR" assert im.format == "BUFR"
@ -31,7 +30,6 @@ def test_invalid_file():
def test_load(): def test_load():
# Arrange # Arrange
with Image.open(TEST_FILE) as im: with Image.open(TEST_FILE) as im:
# Act / Assert: stub cannot load without an implemented handler # Act / Assert: stub cannot load without an implemented handler
with pytest.raises(OSError): with pytest.raises(OSError):
im.load() im.load()

View File

@ -15,7 +15,6 @@ def test_sanity():
# Act # Act
with Image.open(TEST_FILE) as im: with Image.open(TEST_FILE) as im:
# Assert # Assert
assert im.size == (128, 128) assert im.size == (128, 128)
assert isinstance(im, DcxImagePlugin.DcxImageFile) assert isinstance(im, DcxImagePlugin.DcxImageFile)
@ -54,7 +53,6 @@ def test_invalid_file():
def test_tell(): def test_tell():
# Arrange # Arrange
with Image.open(TEST_FILE) as im: with Image.open(TEST_FILE) as im:
# Act # Act
frame = im.tell() frame = im.tell()

View File

@ -12,7 +12,6 @@ TEST_FILE = "Tests/images/hopper.fits"
def test_open(): def test_open():
# Act # Act
with Image.open(TEST_FILE) as im: with Image.open(TEST_FILE) as im:
# Assert # Assert
assert im.format == "FITS" assert im.format == "FITS"
assert im.size == (128, 128) assert im.size == (128, 128)

View File

@ -64,7 +64,6 @@ def test_context_manager():
def test_tell(): def test_tell():
# Arrange # Arrange
with Image.open(static_test_file) as im: with Image.open(static_test_file) as im:
# Act # Act
frame = im.tell() frame = im.tell()
@ -110,7 +109,6 @@ def test_eoferror():
def test_seek_tell(): def test_seek_tell():
with Image.open(animated_test_file) as im: with Image.open(animated_test_file) as im:
layer_number = im.tell() layer_number = im.tell()
assert layer_number == 0 assert layer_number == 0

View File

@ -209,7 +209,7 @@ def test_optimize_if_palette_can_be_reduced_by_half():
im = im.resize((591, 443)) im = im.resize((591, 443))
im_rgb = im.convert("RGB") im_rgb = im.convert("RGB")
for (optimize, colors) in ((False, 256), (True, 8)): for optimize, colors in ((False, 256), (True, 8)):
out = BytesIO() out = BytesIO()
im_rgb.save(out, "GIF", optimize=optimize) im_rgb.save(out, "GIF", optimize=optimize)
with Image.open(out) as reloaded: with Image.open(out) as reloaded:
@ -221,7 +221,6 @@ def test_roundtrip(tmp_path):
im = hopper() im = hopper()
im.save(out) im.save(out)
with Image.open(out) as reread: with Image.open(out) as reread:
assert_image_similar(reread.convert("RGB"), im, 50) assert_image_similar(reread.convert("RGB"), im, 50)
@ -232,7 +231,6 @@ def test_roundtrip2(tmp_path):
im2 = im.copy() im2 = im.copy()
im2.save(out) im2.save(out)
with Image.open(out) as reread: with Image.open(out) as reread:
assert_image_similar(reread.convert("RGB"), hopper(), 50) assert_image_similar(reread.convert("RGB"), hopper(), 50)
@ -242,7 +240,6 @@ def test_roundtrip_save_all(tmp_path):
im = hopper() im = hopper()
im.save(out, save_all=True) im.save(out, save_all=True)
with Image.open(out) as reread: with Image.open(out) as reread:
assert_image_similar(reread.convert("RGB"), im, 50) assert_image_similar(reread.convert("RGB"), im, 50)
# Multiframe image # Multiframe image
@ -284,13 +281,11 @@ def test_headers_saving_for_animated_gifs(tmp_path):
important_headers = ["background", "version", "duration", "loop"] important_headers = ["background", "version", "duration", "loop"]
# Multiframe image # Multiframe image
with Image.open("Tests/images/dispose_bgnd.gif") as im: with Image.open("Tests/images/dispose_bgnd.gif") as im:
info = im.info.copy() info = im.info.copy()
out = str(tmp_path / "temp.gif") out = str(tmp_path / "temp.gif")
im.save(out, save_all=True) im.save(out, save_all=True)
with Image.open(out) as reread: with Image.open(out) as reread:
for header in important_headers: for header in important_headers:
assert info[header] == reread.info[header] assert info[header] == reread.info[header]
@ -308,7 +303,6 @@ def test_palette_handling(tmp_path):
im2.save(f, optimize=True) im2.save(f, optimize=True)
with Image.open(f) as reloaded: with Image.open(f) as reloaded:
assert_image_similar(im, reloaded.convert("RGB"), 10) assert_image_similar(im, reloaded.convert("RGB"), 10)
@ -324,7 +318,6 @@ def test_palette_434(tmp_path):
orig = "Tests/images/test.colors.gif" orig = "Tests/images/test.colors.gif"
with Image.open(orig) as im: with Image.open(orig) as im:
with roundtrip(im) as reloaded: with roundtrip(im) as reloaded:
assert_image_similar(im, reloaded, 1) assert_image_similar(im, reloaded, 1)
with roundtrip(im, optimize=True) as reloaded: with roundtrip(im, optimize=True) as reloaded:
@ -575,7 +568,6 @@ def test_save_dispose(tmp_path):
) )
with Image.open(out) as img: with Image.open(out) as img:
for i in range(2): for i in range(2):
img.seek(img.tell() + 1) img.seek(img.tell() + 1)
assert img.disposal_method == i + 1 assert img.disposal_method == i + 1
@ -773,7 +765,6 @@ def test_multiple_duration(tmp_path):
out, save_all=True, append_images=im_list[1:], duration=duration_list out, save_all=True, append_images=im_list[1:], duration=duration_list
) )
with Image.open(out) as reread: with Image.open(out) as reread:
for duration in duration_list: for duration in duration_list:
assert reread.info["duration"] == duration assert reread.info["duration"] == duration
try: try:
@ -786,7 +777,6 @@ def test_multiple_duration(tmp_path):
out, save_all=True, append_images=im_list[1:], duration=tuple(duration_list) out, save_all=True, append_images=im_list[1:], duration=tuple(duration_list)
) )
with Image.open(out) as reread: with Image.open(out) as reread:
for duration in duration_list: for duration in duration_list:
assert reread.info["duration"] == duration assert reread.info["duration"] == duration
try: try:
@ -844,7 +834,6 @@ def test_identical_frames(tmp_path):
out, save_all=True, append_images=im_list[1:], duration=duration_list out, save_all=True, append_images=im_list[1:], duration=duration_list
) )
with Image.open(out) as reread: with Image.open(out) as reread:
# Assert that the first three frames were combined # Assert that the first three frames were combined
assert reread.n_frames == 2 assert reread.n_frames == 2

View File

@ -10,7 +10,6 @@ TEST_FILE = "Tests/images/WAlaska.wind.7days.grb"
def test_open(): def test_open():
# Act # Act
with Image.open(TEST_FILE) as im: with Image.open(TEST_FILE) as im:
# Assert # Assert
assert im.format == "GRIB" assert im.format == "GRIB"
@ -31,7 +30,6 @@ def test_invalid_file():
def test_load(): def test_load():
# Arrange # Arrange
with Image.open(TEST_FILE) as im: with Image.open(TEST_FILE) as im:
# Act / Assert: stub cannot load without an implemented handler # Act / Assert: stub cannot load without an implemented handler
with pytest.raises(OSError): with pytest.raises(OSError):
im.load() im.load()

View File

@ -8,7 +8,6 @@ TEST_FILE = "Tests/images/hdf5.h5"
def test_open(): def test_open():
# Act # Act
with Image.open(TEST_FILE) as im: with Image.open(TEST_FILE) as im:
# Assert # Assert
assert im.format == "HDF5" assert im.format == "HDF5"
@ -29,7 +28,6 @@ def test_invalid_file():
def test_load(): def test_load():
# Arrange # Arrange
with Image.open(TEST_FILE) as im: with Image.open(TEST_FILE) as im:
# Act / Assert: stub cannot load without an implemented handler # Act / Assert: stub cannot load without an implemented handler
with pytest.raises(OSError): with pytest.raises(OSError):
im.load() im.load()

View File

@ -16,7 +16,6 @@ def test_sanity():
# Loading this icon by default should result in the largest size # Loading this icon by default should result in the largest size
# (512x512@2x) being loaded # (512x512@2x) being loaded
with Image.open(TEST_FILE) as im: with Image.open(TEST_FILE) as im:
# Assert that there is no unclosed file warning # Assert that there is no unclosed file warning
with warnings.catch_warnings(): with warnings.catch_warnings():
im.load() im.load()

View File

@ -175,7 +175,6 @@ def test_save_256x256(tmp_path):
# Act # Act
im.save(outfile) im.save(outfile)
with Image.open(outfile) as im_saved: with Image.open(outfile) as im_saved:
# Assert # Assert
assert im_saved.size == (256, 256) assert im_saved.size == (256, 256)

View File

@ -51,7 +51,6 @@ def test_context_manager():
def test_tell(): def test_tell():
# Arrange # Arrange
with Image.open(TEST_IM) as im: with Image.open(TEST_IM) as im:
# Act # Act
frame = im.tell() frame = im.tell()

View File

@ -11,7 +11,6 @@ TEST_FILE = "Tests/images/iptc.jpg"
def test_getiptcinfo_jpg_none(): def test_getiptcinfo_jpg_none():
# Arrange # Arrange
with hopper() as im: with hopper() as im:
# Act # Act
iptc = IptcImagePlugin.getiptcinfo(im) iptc = IptcImagePlugin.getiptcinfo(im)
@ -22,7 +21,6 @@ def test_getiptcinfo_jpg_none():
def test_getiptcinfo_jpg_found(): def test_getiptcinfo_jpg_found():
# Arrange # Arrange
with Image.open(TEST_FILE) as im: with Image.open(TEST_FILE) as im:
# Act # Act
iptc = IptcImagePlugin.getiptcinfo(im) iptc = IptcImagePlugin.getiptcinfo(im)
@ -35,7 +33,6 @@ def test_getiptcinfo_jpg_found():
def test_getiptcinfo_tiff_none(): def test_getiptcinfo_tiff_none():
# Arrange # Arrange
with Image.open("Tests/images/hopper.tif") as im: with Image.open("Tests/images/hopper.tif") as im:
# Act # Act
iptc = IptcImagePlugin.getiptcinfo(im) iptc = IptcImagePlugin.getiptcinfo(im)

View File

@ -57,7 +57,6 @@ class TestFileJpeg:
return Image.frombytes(mode, size, os.urandom(size[0] * size[1] * len(mode))) return Image.frombytes(mode, size, os.urandom(size[0] * size[1] * len(mode)))
def test_sanity(self): def test_sanity(self):
# internal version number # internal version number
assert re.search(r"\d+\.\d+$", features.version_codec("jpg")) assert re.search(r"\d+\.\d+$", features.version_codec("jpg"))
@ -368,7 +367,6 @@ class TestFileJpeg:
def test_exif_gps_typeerror(self): def test_exif_gps_typeerror(self):
with Image.open("Tests/images/exif_gps_typeerror.jpg") as im: with Image.open("Tests/images/exif_gps_typeerror.jpg") as im:
# Should not raise a TypeError # Should not raise a TypeError
im._getexif() im._getexif()
@ -682,7 +680,6 @@ class TestFileJpeg:
# Shouldn't raise error # Shouldn't raise error
fn = "Tests/images/sugarshack_bad_mpo_header.jpg" fn = "Tests/images/sugarshack_bad_mpo_header.jpg"
with pytest.warns(UserWarning, Image.open, fn) as im: with pytest.warns(UserWarning, Image.open, fn) as im:
# Assert # Assert
assert im.format == "JPEG" assert im.format == "JPEG"
@ -704,7 +701,6 @@ class TestFileJpeg:
# Arrange # Arrange
outfile = str(tmp_path / "temp.tif") outfile = str(tmp_path / "temp.tif")
with Image.open("Tests/images/hopper.tif") as im: with Image.open("Tests/images/hopper.tif") as im:
# Act # Act
im.save(outfile, "JPEG", dpi=im.info["dpi"]) im.save(outfile, "JPEG", dpi=im.info["dpi"])
@ -731,7 +727,6 @@ class TestFileJpeg:
# This Photoshop CC 2017 image has DPI in EXIF not metadata # This Photoshop CC 2017 image has DPI in EXIF not metadata
# EXIF XResolution is (2000000, 10000) # EXIF XResolution is (2000000, 10000)
with Image.open("Tests/images/photoshop-200dpi.jpg") as im: with Image.open("Tests/images/photoshop-200dpi.jpg") as im:
# Act / Assert # Act / Assert
assert im.info.get("dpi") == (200, 200) assert im.info.get("dpi") == (200, 200)
@ -740,7 +735,6 @@ class TestFileJpeg:
# This image has DPI in EXIF not metadata # This image has DPI in EXIF not metadata
# EXIF XResolution is 72 # EXIF XResolution is 72
with Image.open("Tests/images/exif-72dpi-int.jpg") as im: with Image.open("Tests/images/exif-72dpi-int.jpg") as im:
# Act / Assert # Act / Assert
assert im.info.get("dpi") == (72, 72) assert im.info.get("dpi") == (72, 72)
@ -749,7 +743,6 @@ class TestFileJpeg:
# This is photoshop-200dpi.jpg with EXIF resolution unit set to cm: # This is photoshop-200dpi.jpg with EXIF resolution unit set to cm:
# exiftool -exif:ResolutionUnit=cm photoshop-200dpi.jpg # exiftool -exif:ResolutionUnit=cm photoshop-200dpi.jpg
with Image.open("Tests/images/exif-200dpcm.jpg") as im: with Image.open("Tests/images/exif-200dpcm.jpg") as im:
# Act / Assert # Act / Assert
assert im.info.get("dpi") == (508, 508) assert im.info.get("dpi") == (508, 508)
@ -758,7 +751,6 @@ class TestFileJpeg:
# This is photoshop-200dpi.jpg with EXIF resolution set to 0/0: # This is photoshop-200dpi.jpg with EXIF resolution set to 0/0:
# exiftool -XResolution=0/0 -YResolution=0/0 photoshop-200dpi.jpg # exiftool -XResolution=0/0 -YResolution=0/0 photoshop-200dpi.jpg
with Image.open("Tests/images/exif-dpi-zerodivision.jpg") as im: with Image.open("Tests/images/exif-dpi-zerodivision.jpg") as im:
# Act / Assert # Act / Assert
# This should return the default, and not raise a ZeroDivisionError # This should return the default, and not raise a ZeroDivisionError
assert im.info.get("dpi") == (72, 72) assert im.info.get("dpi") == (72, 72)
@ -767,7 +759,6 @@ class TestFileJpeg:
# Arrange # Arrange
# 0x011A tag in this exif contains string '300300\x02' # 0x011A tag in this exif contains string '300300\x02'
with Image.open("Tests/images/broken_exif_dpi.jpg") as im: with Image.open("Tests/images/broken_exif_dpi.jpg") as im:
# Act / Assert # Act / Assert
# This should return the default # This should return the default
assert im.info.get("dpi") == (72, 72) assert im.info.get("dpi") == (72, 72)
@ -777,7 +768,6 @@ class TestFileJpeg:
# This is photoshop-200dpi.jpg with resolution removed from EXIF: # This is photoshop-200dpi.jpg with resolution removed from EXIF:
# exiftool "-*resolution*"= photoshop-200dpi.jpg # exiftool "-*resolution*"= photoshop-200dpi.jpg
with Image.open("Tests/images/no-dpi-in-exif.jpg") as im: with Image.open("Tests/images/no-dpi-in-exif.jpg") as im:
# Act / Assert # Act / Assert
# "When the image resolution is unknown, 72 [dpi] is designated." # "When the image resolution is unknown, 72 [dpi] is designated."
# https://exiv2.org/tags.html # https://exiv2.org/tags.html
@ -787,7 +777,6 @@ class TestFileJpeg:
# This is no-dpi-in-exif with the tiff header of the exif block # This is no-dpi-in-exif with the tiff header of the exif block
# hexedited from MM * to FF FF FF FF # hexedited from MM * to FF FF FF FF
with Image.open("Tests/images/invalid-exif.jpg") as im: with Image.open("Tests/images/invalid-exif.jpg") as im:
# This should return the default, and not a SyntaxError or # This should return the default, and not a SyntaxError or
# OSError for unidentified image. # OSError for unidentified image.
assert im.info.get("dpi") == (72, 72) assert im.info.get("dpi") == (72, 72)
@ -810,7 +799,6 @@ class TestFileJpeg:
def test_invalid_exif_x_resolution(self): def test_invalid_exif_x_resolution(self):
# When no x or y resolution is defined in EXIF # When no x or y resolution is defined in EXIF
with Image.open("Tests/images/invalid-exif-without-x-resolution.jpg") as im: with Image.open("Tests/images/invalid-exif-without-x-resolution.jpg") as im:
# This should return the default, and not a ValueError or # This should return the default, and not a ValueError or
# OSError for an unidentified image. # OSError for an unidentified image.
assert im.info.get("dpi") == (72, 72) assert im.info.get("dpi") == (72, 72)
@ -820,7 +808,6 @@ class TestFileJpeg:
# This image has been manually hexedited to have an IFD offset of 10, # This image has been manually hexedited to have an IFD offset of 10,
# in contrast to normal 8 # in contrast to normal 8
with Image.open("Tests/images/exif-ifd-offset.jpg") as im: with Image.open("Tests/images/exif-ifd-offset.jpg") as im:
# Act / Assert # Act / Assert
assert im._getexif()[306] == "2017:03:13 23:03:09" assert im._getexif()[306] == "2017:03:13 23:03:09"

View File

@ -270,7 +270,6 @@ def test_rgba():
# Arrange # Arrange
with Image.open("Tests/images/rgb_trns_ycbc.j2k") as j2k: with Image.open("Tests/images/rgb_trns_ycbc.j2k") as j2k:
with Image.open("Tests/images/rgb_trns_ycbc.jp2") as jp2: with Image.open("Tests/images/rgb_trns_ycbc.jp2") as jp2:
# Act # Act
j2k.load() j2k.load()
jp2.load() jp2.load()

View File

@ -645,7 +645,6 @@ class TestFileLibTiff(LibTiffTestCase):
pilim = hopper() pilim = hopper()
def save_bytesio(compression=None): def save_bytesio(compression=None):
buffer_io = io.BytesIO() buffer_io = io.BytesIO()
pilim.save(buffer_io, format="tiff", compression=compression) pilim.save(buffer_io, format="tiff", compression=compression)
buffer_io.seek(0) buffer_io.seek(0)
@ -740,7 +739,6 @@ class TestFileLibTiff(LibTiffTestCase):
def test_multipage_compression(self): def test_multipage_compression(self):
with Image.open("Tests/images/compression.tif") as im: with Image.open("Tests/images/compression.tif") as im:
im.seek(0) im.seek(0)
assert im._compression == "tiff_ccitt" assert im._compression == "tiff_ccitt"
assert im.size == (10, 10) assert im.size == (10, 10)

View File

@ -44,7 +44,6 @@ def test_open_windows_v1():
# Arrange # Arrange
# Act # Act
with Image.open(TEST_FILE) as im: with Image.open(TEST_FILE) as im:
# Assert # Assert
assert_image_equal(im, hopper("1")) assert_image_equal(im, hopper("1"))
assert isinstance(im, MspImagePlugin.MspImageFile) assert isinstance(im, MspImagePlugin.MspImageFile)
@ -59,7 +58,6 @@ def _assert_file_image_equal(source_path, target_path):
not os.path.exists(EXTRA_DIR), reason="Extra image files not installed" not os.path.exists(EXTRA_DIR), reason="Extra image files not installed"
) )
def test_open_windows_v2(): def test_open_windows_v2():
files = ( files = (
os.path.join(EXTRA_DIR, f) os.path.join(EXTRA_DIR, f)
for f in os.listdir(EXTRA_DIR) for f in os.listdir(EXTRA_DIR)

View File

@ -89,7 +89,6 @@ def test_save_all(tmp_path):
# Multiframe image # Multiframe image
with Image.open("Tests/images/dispose_bgnd.gif") as im: with Image.open("Tests/images/dispose_bgnd.gif") as im:
outfile = str(tmp_path / "temp.pdf") outfile = str(tmp_path / "temp.pdf")
im.save(outfile, save_all=True) im.save(outfile, save_all=True)
@ -123,7 +122,6 @@ def test_save_all(tmp_path):
def test_multiframe_normal_save(tmp_path): def test_multiframe_normal_save(tmp_path):
# Test saving a multiframe image without save_all # Test saving a multiframe image without save_all
with Image.open("Tests/images/dispose_bgnd.gif") as im: with Image.open("Tests/images/dispose_bgnd.gif") as im:
outfile = str(tmp_path / "temp.pdf") outfile = str(tmp_path / "temp.pdf")
im.save(outfile) im.save(outfile)

View File

@ -78,7 +78,6 @@ class TestFilePng:
return chunks return chunks
def test_sanity(self, tmp_path): def test_sanity(self, tmp_path):
# internal version number # internal version number
assert re.search(r"\d+\.\d+\.\d+(\.\d+)?$", features.version_codec("zlib")) assert re.search(r"\d+\.\d+\.\d+(\.\d+)?$", features.version_codec("zlib"))
@ -156,7 +155,6 @@ class TestFilePng:
assert im.info == {"spam": "egg"} assert im.info == {"spam": "egg"}
def test_bad_itxt(self): def test_bad_itxt(self):
im = load(HEAD + chunk(b"iTXt") + TAIL) im = load(HEAD + chunk(b"iTXt") + TAIL)
assert im.info == {} assert im.info == {}
@ -201,7 +199,6 @@ class TestFilePng:
assert im.info["spam"].tkey == "Spam" assert im.info["spam"].tkey == "Spam"
def test_interlace(self): def test_interlace(self):
test_file = "Tests/images/pil123p.png" test_file = "Tests/images/pil123p.png"
with Image.open(test_file) as im: with Image.open(test_file) as im:
assert_image(im, "P", (162, 150)) assert_image(im, "P", (162, 150))
@ -495,7 +492,6 @@ class TestFilePng:
# Check reading images with null tRNS value, issue #1239 # Check reading images with null tRNS value, issue #1239
test_file = "Tests/images/tRNS_null_1x1.png" test_file = "Tests/images/tRNS_null_1x1.png"
with Image.open(test_file) as im: with Image.open(test_file) as im:
assert im.info["transparency"] == 0 assert im.info["transparency"] == 0
def test_save_icc_profile(self): def test_save_icc_profile(self):

View File

@ -77,7 +77,6 @@ def test_eoferror():
def test_seek_tell(): def test_seek_tell():
with Image.open(test_file) as im: with Image.open(test_file) as im:
layer_number = im.tell() layer_number = im.tell()
assert layer_number == 1 assert layer_number == 1
@ -95,7 +94,6 @@ def test_seek_tell():
def test_seek_eoferror(): def test_seek_eoferror():
with Image.open(test_file) as im: with Image.open(test_file) as im:
with pytest.raises(EOFError): with pytest.raises(EOFError):
im.seek(-1) im.seek(-1)

View File

@ -79,7 +79,6 @@ def test_is_spider_image():
def test_tell(): def test_tell():
# Arrange # Arrange
with Image.open(TEST_FILE) as im: with Image.open(TEST_FILE) as im:
# Act # Act
index = im.tell() index = im.tell()

View File

@ -16,7 +16,6 @@ def test_sanity():
# Act # Act
with Image.open(test_file) as im: with Image.open(test_file) as im:
# Assert # Assert
assert im.size == (128, 128) assert im.size == (128, 128)

View File

@ -10,18 +10,21 @@ from .helper import is_pypy
TEST_TAR_FILE = "Tests/images/hopper.tar" TEST_TAR_FILE = "Tests/images/hopper.tar"
def test_sanity(): @pytest.mark.parametrize(
for codec, test_path, format in [ "codec, test_path, format",
["zlib", "hopper.png", "PNG"], (
["jpg", "hopper.jpg", "JPEG"], ("zlib", "hopper.png", "PNG"),
]: ("jpg", "hopper.jpg", "JPEG"),
if features.check(codec): ),
with TarIO.TarIO(TEST_TAR_FILE, test_path) as tar: )
with Image.open(tar) as im: def test_sanity(codec, test_path, format):
im.load() if features.check(codec):
assert im.mode == "RGB" with TarIO.TarIO(TEST_TAR_FILE, test_path) as tar:
assert im.size == (128, 128) with Image.open(tar) as im:
assert im.format == format im.load()
assert im.mode == "RGB"
assert im.size == (128, 128)
assert im.format == format
@pytest.mark.skipif(is_pypy(), reason="Requires CPython") @pytest.mark.skipif(is_pypy(), reason="Requires CPython")

View File

@ -78,7 +78,6 @@ def test_id_field():
# Act # Act
with Image.open(test_file) as im: with Image.open(test_file) as im:
# Assert # Assert
assert im.size == (100, 100) assert im.size == (100, 100)
@ -89,7 +88,6 @@ def test_id_field_rle():
# Act # Act
with Image.open(test_file) as im: with Image.open(test_file) as im:
# Assert # Assert
assert im.size == (199, 199) assert im.size == (199, 199)
@ -171,7 +169,6 @@ def test_save_id_section(tmp_path):
test_file = "Tests/images/tga_id_field.tga" test_file = "Tests/images/tga_id_field.tga"
with Image.open(test_file) as im: with Image.open(test_file) as im:
# Save with no id section # Save with no id section
im.save(out, id_section="") im.save(out, id_section="")
with Image.open(out) as test_im: with Image.open(out) as test_im:

View File

@ -25,7 +25,6 @@ except ImportError:
class TestFileTiff: class TestFileTiff:
def test_sanity(self, tmp_path): def test_sanity(self, tmp_path):
filename = str(tmp_path / "temp.tif") filename = str(tmp_path / "temp.tif")
hopper("RGB").save(filename) hopper("RGB").save(filename)
@ -157,7 +156,6 @@ class TestFileTiff:
def test_xyres_tiff(self): def test_xyres_tiff(self):
filename = "Tests/images/pil168.tif" filename = "Tests/images/pil168.tif"
with Image.open(filename) as im: with Image.open(filename) as im:
# legacy api # legacy api
assert isinstance(im.tag[X_RESOLUTION][0], tuple) assert isinstance(im.tag[X_RESOLUTION][0], tuple)
assert isinstance(im.tag[Y_RESOLUTION][0], tuple) assert isinstance(im.tag[Y_RESOLUTION][0], tuple)
@ -171,7 +169,6 @@ class TestFileTiff:
def test_xyres_fallback_tiff(self): def test_xyres_fallback_tiff(self):
filename = "Tests/images/compression.tif" filename = "Tests/images/compression.tif"
with Image.open(filename) as im: with Image.open(filename) as im:
# v2 api # v2 api
assert isinstance(im.tag_v2[X_RESOLUTION], TiffImagePlugin.IFDRational) assert isinstance(im.tag_v2[X_RESOLUTION], TiffImagePlugin.IFDRational)
assert isinstance(im.tag_v2[Y_RESOLUTION], TiffImagePlugin.IFDRational) assert isinstance(im.tag_v2[Y_RESOLUTION], TiffImagePlugin.IFDRational)
@ -186,7 +183,6 @@ class TestFileTiff:
def test_int_resolution(self): def test_int_resolution(self):
filename = "Tests/images/pil168.tif" filename = "Tests/images/pil168.tif"
with Image.open(filename) as im: with Image.open(filename) as im:
# Try to read a file where X,Y_RESOLUTION are ints # Try to read a file where X,Y_RESOLUTION are ints
im.tag_v2[X_RESOLUTION] = 71 im.tag_v2[X_RESOLUTION] = 71
im.tag_v2[Y_RESOLUTION] = 71 im.tag_v2[Y_RESOLUTION] = 71
@ -381,7 +377,6 @@ class TestFileTiff:
def test___str__(self): def test___str__(self):
filename = "Tests/images/pil136.tiff" filename = "Tests/images/pil136.tiff"
with Image.open(filename) as im: with Image.open(filename) as im:
# Act # Act
ret = str(im.ifd) ret = str(im.ifd)
@ -392,7 +387,6 @@ class TestFileTiff:
# Arrange # Arrange
filename = "Tests/images/pil136.tiff" filename = "Tests/images/pil136.tiff"
with Image.open(filename) as im: with Image.open(filename) as im:
# v2 interface # v2 interface
v2_tags = { v2_tags = {
256: 55, 256: 55,
@ -630,7 +624,6 @@ class TestFileTiff:
filename = str(tmp_path / "temp.tif") filename = str(tmp_path / "temp.tif")
hopper("RGB").save(filename, **kwargs) hopper("RGB").save(filename, **kwargs)
with Image.open(filename) as im: with Image.open(filename) as im:
# legacy interface # legacy interface
assert im.tag[X_RESOLUTION][0][0] == 72 assert im.tag[X_RESOLUTION][0][0] == 72
assert im.tag[Y_RESOLUTION][0][0] == 36 assert im.tag[Y_RESOLUTION][0][0] == 36

View File

@ -54,7 +54,6 @@ def test_rt_metadata(tmp_path):
img.save(f, tiffinfo=info) img.save(f, tiffinfo=info)
with Image.open(f) as loaded: with Image.open(f) as loaded:
assert loaded.tag[ImageJMetaDataByteCounts] == (len(bin_data),) assert loaded.tag[ImageJMetaDataByteCounts] == (len(bin_data),)
assert loaded.tag_v2[ImageJMetaDataByteCounts] == (len(bin_data),) assert loaded.tag_v2[ImageJMetaDataByteCounts] == (len(bin_data),)
@ -74,14 +73,12 @@ def test_rt_metadata(tmp_path):
info[ImageJMetaDataByteCounts] = (8, len(bin_data) - 8) info[ImageJMetaDataByteCounts] = (8, len(bin_data) - 8)
img.save(f, tiffinfo=info) img.save(f, tiffinfo=info)
with Image.open(f) as loaded: with Image.open(f) as loaded:
assert loaded.tag[ImageJMetaDataByteCounts] == (8, len(bin_data) - 8) assert loaded.tag[ImageJMetaDataByteCounts] == (8, len(bin_data) - 8)
assert loaded.tag_v2[ImageJMetaDataByteCounts] == (8, len(bin_data) - 8) assert loaded.tag_v2[ImageJMetaDataByteCounts] == (8, len(bin_data) - 8)
def test_read_metadata(): def test_read_metadata():
with Image.open("Tests/images/hopper_g4.tif") as img: with Image.open("Tests/images/hopper_g4.tif") as img:
assert { assert {
"YResolution": IFDRational(4294967295, 113653537), "YResolution": IFDRational(4294967295, 113653537),
"PlanarConfiguration": 1, "PlanarConfiguration": 1,
@ -202,14 +199,15 @@ def test_writing_other_types_to_ascii(value, expected, tmp_path):
assert reloaded.tag_v2[271] == expected assert reloaded.tag_v2[271] == expected
def test_writing_int_to_bytes(tmp_path): @pytest.mark.parametrize("value", (1, IFDRational(1)))
def test_writing_other_types_to_bytes(value, tmp_path):
im = hopper() im = hopper()
info = TiffImagePlugin.ImageFileDirectory_v2() info = TiffImagePlugin.ImageFileDirectory_v2()
tag = TiffTags.TAGS_V2[700] tag = TiffTags.TAGS_V2[700]
assert tag.type == TiffTags.BYTE assert tag.type == TiffTags.BYTE
info[700] = 1 info[700] = value
out = str(tmp_path / "temp.tiff") out = str(tmp_path / "temp.tiff")
im.save(out, tiffinfo=info) im.save(out, tiffinfo=info)

View File

@ -18,10 +18,8 @@ except ImportError:
def test_read_exif_metadata(): def test_read_exif_metadata():
file_path = "Tests/images/flower.webp" file_path = "Tests/images/flower.webp"
with Image.open(file_path) as image: with Image.open(file_path) as image:
assert image.format == "WEBP" assert image.format == "WEBP"
exif_data = image.info.get("exif", None) exif_data = image.info.get("exif", None)
assert exif_data assert exif_data
@ -64,10 +62,8 @@ def test_write_exif_metadata():
def test_read_icc_profile(): def test_read_icc_profile():
file_path = "Tests/images/flower2.webp" file_path = "Tests/images/flower2.webp"
with Image.open(file_path) as image: with Image.open(file_path) as image:
assert image.format == "WEBP" assert image.format == "WEBP"
assert image.info.get("icc_profile", None) assert image.info.get("icc_profile", None)

View File

@ -6,7 +6,6 @@ from .helper import assert_image_similar_tofile, hopper
def test_load_raw(): def test_load_raw():
# Test basic EMF open and rendering # Test basic EMF open and rendering
with Image.open("Tests/images/drawing.emf") as im: with Image.open("Tests/images/drawing.emf") as im:
if hasattr(Image.core, "drawwmf"): if hasattr(Image.core, "drawwmf"):

View File

@ -44,7 +44,6 @@ def test_open():
# Act # Act
with Image.open(filename) as im: with Image.open(filename) as im:
# Assert # Assert
assert im.mode == "1" assert im.mode == "1"
assert im.size == (128, 128) assert im.size == (128, 128)
@ -57,7 +56,6 @@ def test_open_filename_with_underscore():
# Act # Act
with Image.open(filename) as im: with Image.open(filename) as im:
# Assert # Assert
assert im.mode == "1" assert im.mode == "1"
assert im.size == (128, 128) assert im.size == (128, 128)

View File

@ -10,7 +10,6 @@ TEST_FILE = "Tests/images/hopper.p7"
def test_open(): def test_open():
# Act # Act
with Image.open(TEST_FILE) as im: with Image.open(TEST_FILE) as im:
# Assert # Assert
assert im.format == "XVThumb" assert im.format == "XVThumb"

View File

@ -69,7 +69,6 @@ class TestImage:
assert issubclass(UnidentifiedImageError, OSError) assert issubclass(UnidentifiedImageError, OSError)
def test_sanity(self): def test_sanity(self):
im = Image.new("L", (100, 100)) im = Image.new("L", (100, 100))
assert repr(im)[:45] == "<PIL.Image.Image image mode=L size=100x100 at" assert repr(im)[:45] == "<PIL.Image.Image image mode=L size=100x100 at"
assert im.mode == "L" assert im.mode == "L"
@ -398,6 +397,17 @@ class TestImage:
with pytest.raises(ValueError): with pytest.raises(ValueError):
source.alpha_composite(over, (0, 0), (0, -1)) source.alpha_composite(over, (0, 0), (0, -1))
def test_register_open_duplicates(self):
# Arrange
factory, accept = Image.OPEN["JPEG"]
id_length = len(Image.ID)
# Act
Image.register_open("JPEG", factory, accept)
# Assert
assert len(Image.ID) == id_length
def test_registered_extensions_uninitialized(self): def test_registered_extensions_uninitialized(self):
# Arrange # Arrange
Image._initialized = 0 Image._initialized = 0
@ -996,7 +1006,6 @@ def mock_encode(*args):
class TestRegistry: class TestRegistry:
def test_encode_registry(self): def test_encode_registry(self):
Image.register_encoder("MOCK", mock_encode) Image.register_encoder("MOCK", mock_encode)
assert "MOCK" in Image.ENCODERS assert "MOCK" in Image.ENCODERS

View File

@ -45,7 +45,6 @@ def test_unsupported_conversion():
def test_default(): def test_default():
im = hopper("P") im = hopper("P")
assert im.mode == "P" assert im.mode == "P"
converted_im = im.convert() converted_im = im.convert()

View File

@ -86,7 +86,6 @@ def test_crop_crash():
def test_crop_zero(): def test_crop_zero():
im = Image.new("RGB", (0, 0), "white") im = Image.new("RGB", (0, 0), "white")
cropped = im.crop((0, 0, 0, 0)) cropped = im.crop((0, 0, 0, 0))

View File

@ -4,7 +4,6 @@ from .helper import hopper
def test_sanity(): def test_sanity():
bbox = hopper().getbbox() bbox = hopper().getbbox()
assert isinstance(bbox, tuple) assert isinstance(bbox, tuple)

View File

@ -6,7 +6,6 @@ from .helper import hopper
def test_sanity(): def test_sanity():
with hopper() as im: with hopper() as im:
im.mode im.mode

View File

@ -52,7 +52,7 @@ def test_resample():
# >>> im.save('Tests/images/hopper_45.png') # >>> im.save('Tests/images/hopper_45.png')
with Image.open("Tests/images/hopper_45.png") as target: with Image.open("Tests/images/hopper_45.png") as target:
for (resample, epsilon) in ( for resample, epsilon in (
(Image.Resampling.NEAREST, 10), (Image.Resampling.NEAREST, 10),
(Image.Resampling.BILINEAR, 5), (Image.Resampling.BILINEAR, 5),
(Image.Resampling.BICUBIC, 0), (Image.Resampling.BICUBIC, 0),

View File

@ -4,7 +4,6 @@ from .helper import assert_image_equal, fromstring, hopper
def test_sanity(): def test_sanity():
with pytest.raises(ValueError): with pytest.raises(ValueError):
hopper().tobitmap() hopper().tobitmap()

View File

@ -50,7 +50,6 @@ def test_add():
# Arrange # Arrange
with Image.open("Tests/images/imagedraw_ellipse_RGB.png") as im1: with Image.open("Tests/images/imagedraw_ellipse_RGB.png") as im1:
with Image.open("Tests/images/imagedraw_floodfill_RGB.png") as im2: with Image.open("Tests/images/imagedraw_floodfill_RGB.png") as im2:
# Act # Act
new = ImageChops.add(im1, im2) new = ImageChops.add(im1, im2)
@ -63,7 +62,6 @@ def test_add_scale_offset():
# Arrange # Arrange
with Image.open("Tests/images/imagedraw_ellipse_RGB.png") as im1: with Image.open("Tests/images/imagedraw_ellipse_RGB.png") as im1:
with Image.open("Tests/images/imagedraw_floodfill_RGB.png") as im2: with Image.open("Tests/images/imagedraw_floodfill_RGB.png") as im2:
# Act # Act
new = ImageChops.add(im1, im2, scale=2.5, offset=100) new = ImageChops.add(im1, im2, scale=2.5, offset=100)
@ -87,7 +85,6 @@ def test_add_modulo():
# Arrange # Arrange
with Image.open("Tests/images/imagedraw_ellipse_RGB.png") as im1: with Image.open("Tests/images/imagedraw_ellipse_RGB.png") as im1:
with Image.open("Tests/images/imagedraw_floodfill_RGB.png") as im2: with Image.open("Tests/images/imagedraw_floodfill_RGB.png") as im2:
# Act # Act
new = ImageChops.add_modulo(im1, im2) new = ImageChops.add_modulo(im1, im2)
@ -111,7 +108,6 @@ def test_blend():
# Arrange # Arrange
with Image.open("Tests/images/imagedraw_ellipse_RGB.png") as im1: with Image.open("Tests/images/imagedraw_ellipse_RGB.png") as im1:
with Image.open("Tests/images/imagedraw_floodfill_RGB.png") as im2: with Image.open("Tests/images/imagedraw_floodfill_RGB.png") as im2:
# Act # Act
new = ImageChops.blend(im1, im2, 0.5) new = ImageChops.blend(im1, im2, 0.5)
@ -137,7 +133,6 @@ def test_darker_image():
# Arrange # Arrange
with Image.open("Tests/images/imagedraw_chord_RGB.png") as im1: with Image.open("Tests/images/imagedraw_chord_RGB.png") as im1:
with Image.open("Tests/images/imagedraw_outline_chord_RGB.png") as im2: with Image.open("Tests/images/imagedraw_outline_chord_RGB.png") as im2:
# Act # Act
new = ImageChops.darker(im1, im2) new = ImageChops.darker(im1, im2)
@ -149,7 +144,6 @@ def test_darker_pixel():
# Arrange # Arrange
im1 = hopper() im1 = hopper()
with Image.open("Tests/images/imagedraw_chord_RGB.png") as im2: with Image.open("Tests/images/imagedraw_chord_RGB.png") as im2:
# Act # Act
new = ImageChops.darker(im1, im2) new = ImageChops.darker(im1, im2)
@ -161,7 +155,6 @@ def test_difference():
# Arrange # Arrange
with Image.open("Tests/images/imagedraw_arc_end_le_start.png") as im1: with Image.open("Tests/images/imagedraw_arc_end_le_start.png") as im1:
with Image.open("Tests/images/imagedraw_arc_no_loops.png") as im2: with Image.open("Tests/images/imagedraw_arc_no_loops.png") as im2:
# Act # Act
new = ImageChops.difference(im1, im2) new = ImageChops.difference(im1, im2)
@ -173,7 +166,6 @@ def test_difference_pixel():
# Arrange # Arrange
im1 = hopper() im1 = hopper()
with Image.open("Tests/images/imagedraw_polygon_kite_RGB.png") as im2: with Image.open("Tests/images/imagedraw_polygon_kite_RGB.png") as im2:
# Act # Act
new = ImageChops.difference(im1, im2) new = ImageChops.difference(im1, im2)
@ -195,7 +187,6 @@ def test_duplicate():
def test_invert(): def test_invert():
# Arrange # Arrange
with Image.open("Tests/images/imagedraw_floodfill_RGB.png") as im: with Image.open("Tests/images/imagedraw_floodfill_RGB.png") as im:
# Act # Act
new = ImageChops.invert(im) new = ImageChops.invert(im)
@ -209,7 +200,6 @@ def test_lighter_image():
# Arrange # Arrange
with Image.open("Tests/images/imagedraw_chord_RGB.png") as im1: with Image.open("Tests/images/imagedraw_chord_RGB.png") as im1:
with Image.open("Tests/images/imagedraw_outline_chord_RGB.png") as im2: with Image.open("Tests/images/imagedraw_outline_chord_RGB.png") as im2:
# Act # Act
new = ImageChops.lighter(im1, im2) new = ImageChops.lighter(im1, im2)
@ -221,7 +211,6 @@ def test_lighter_pixel():
# Arrange # Arrange
im1 = hopper() im1 = hopper()
with Image.open("Tests/images/imagedraw_chord_RGB.png") as im2: with Image.open("Tests/images/imagedraw_chord_RGB.png") as im2:
# Act # Act
new = ImageChops.lighter(im1, im2) new = ImageChops.lighter(im1, im2)
@ -275,7 +264,6 @@ def test_offset():
xoffset = 45 xoffset = 45
yoffset = 20 yoffset = 20
with Image.open("Tests/images/imagedraw_ellipse_RGB.png") as im: with Image.open("Tests/images/imagedraw_ellipse_RGB.png") as im:
# Act # Act
new = ImageChops.offset(im, xoffset, yoffset) new = ImageChops.offset(im, xoffset, yoffset)
@ -292,7 +280,6 @@ def test_screen():
# Arrange # Arrange
with Image.open("Tests/images/imagedraw_ellipse_RGB.png") as im1: with Image.open("Tests/images/imagedraw_ellipse_RGB.png") as im1:
with Image.open("Tests/images/imagedraw_floodfill_RGB.png") as im2: with Image.open("Tests/images/imagedraw_floodfill_RGB.png") as im2:
# Act # Act
new = ImageChops.screen(im1, im2) new = ImageChops.screen(im1, im2)
@ -305,7 +292,6 @@ def test_subtract():
# Arrange # Arrange
with Image.open("Tests/images/imagedraw_chord_RGB.png") as im1: with Image.open("Tests/images/imagedraw_chord_RGB.png") as im1:
with Image.open("Tests/images/imagedraw_outline_chord_RGB.png") as im2: with Image.open("Tests/images/imagedraw_outline_chord_RGB.png") as im2:
# Act # Act
new = ImageChops.subtract(im1, im2) new = ImageChops.subtract(im1, im2)
@ -319,7 +305,6 @@ def test_subtract_scale_offset():
# Arrange # Arrange
with Image.open("Tests/images/imagedraw_chord_RGB.png") as im1: with Image.open("Tests/images/imagedraw_chord_RGB.png") as im1:
with Image.open("Tests/images/imagedraw_outline_chord_RGB.png") as im2: with Image.open("Tests/images/imagedraw_outline_chord_RGB.png") as im2:
# Act # Act
new = ImageChops.subtract(im1, im2, scale=2.5, offset=100) new = ImageChops.subtract(im1, im2, scale=2.5, offset=100)
@ -332,7 +317,6 @@ def test_subtract_clip():
# Arrange # Arrange
im1 = hopper() im1 = hopper()
with Image.open("Tests/images/imagedraw_chord_RGB.png") as im2: with Image.open("Tests/images/imagedraw_chord_RGB.png") as im2:
# Act # Act
new = ImageChops.subtract(im1, im2) new = ImageChops.subtract(im1, im2)
@ -344,7 +328,6 @@ def test_subtract_modulo():
# Arrange # Arrange
with Image.open("Tests/images/imagedraw_chord_RGB.png") as im1: with Image.open("Tests/images/imagedraw_chord_RGB.png") as im1:
with Image.open("Tests/images/imagedraw_outline_chord_RGB.png") as im2: with Image.open("Tests/images/imagedraw_outline_chord_RGB.png") as im2:
# Act # Act
new = ImageChops.subtract_modulo(im1, im2) new = ImageChops.subtract_modulo(im1, im2)
@ -358,7 +341,6 @@ def test_subtract_modulo_no_clip():
# Arrange # Arrange
im1 = hopper() im1 = hopper()
with Image.open("Tests/images/imagedraw_chord_RGB.png") as im2: with Image.open("Tests/images/imagedraw_chord_RGB.png") as im2:
# Act # Act
new = ImageChops.subtract_modulo(im1, im2) new = ImageChops.subtract_modulo(im1, im2)
@ -370,7 +352,6 @@ def test_soft_light():
# Arrange # Arrange
with Image.open("Tests/images/hopper.png") as im1: with Image.open("Tests/images/hopper.png") as im1:
with Image.open("Tests/images/hopper-XYZ.png") as im2: with Image.open("Tests/images/hopper-XYZ.png") as im2:
# Act # Act
new = ImageChops.soft_light(im1, im2) new = ImageChops.soft_light(im1, im2)
@ -383,7 +364,6 @@ def test_hard_light():
# Arrange # Arrange
with Image.open("Tests/images/hopper.png") as im1: with Image.open("Tests/images/hopper.png") as im1:
with Image.open("Tests/images/hopper-XYZ.png") as im2: with Image.open("Tests/images/hopper-XYZ.png") as im2:
# Act # Act
new = ImageChops.hard_light(im1, im2) new = ImageChops.hard_light(im1, im2)
@ -396,7 +376,6 @@ def test_overlay():
# Arrange # Arrange
with Image.open("Tests/images/hopper.png") as im1: with Image.open("Tests/images/hopper.png") as im1:
with Image.open("Tests/images/hopper-XYZ.png") as im2: with Image.open("Tests/images/hopper-XYZ.png") as im2:
# Act # Act
new = ImageChops.overlay(im1, im2) new = ImageChops.overlay(im1, im2)

View File

@ -52,7 +52,6 @@ def test_sanity():
def test_valueerror(): def test_valueerror():
with Image.open("Tests/images/chi.gif") as im: with Image.open("Tests/images/chi.gif") as im:
draw = ImageDraw.Draw(im) draw = ImageDraw.Draw(im)
draw.line((0, 0), fill=(0, 0, 0)) draw.line((0, 0), fill=(0, 0, 0))

View File

@ -30,7 +30,6 @@ SAFEBLOCK = ImageFile.SAFEBLOCK
class TestImageFile: class TestImageFile:
def test_parser(self): def test_parser(self):
def roundtrip(format): def roundtrip(format):
im = hopper("L").resize((1000, 1000), Image.Resampling.NEAREST) im = hopper("L").resize((1000, 1000), Image.Resampling.NEAREST)
if format in ("MSP", "XBM"): if format in ("MSP", "XBM"):
im = im.convert("1") im = im.convert("1")

View File

@ -21,7 +21,6 @@ deformer = Deformer()
def test_sanity(): def test_sanity():
ImageOps.autocontrast(hopper("L")) ImageOps.autocontrast(hopper("L"))
ImageOps.autocontrast(hopper("RGB")) ImageOps.autocontrast(hopper("RGB"))
@ -419,7 +418,6 @@ def test_autocontrast_cutoff():
def test_autocontrast_mask_toy_input(): def test_autocontrast_mask_toy_input():
# Test the mask argument of autocontrast # Test the mask argument of autocontrast
with Image.open("Tests/images/bw_gradient.png") as img: with Image.open("Tests/images/bw_gradient.png") as img:
rect_mask = Image.new("L", img.size, 0) rect_mask = Image.new("L", img.size, 0)
draw = ImageDraw.Draw(rect_mask) draw = ImageDraw.Draw(rect_mask)
x0 = img.size[0] // 4 x0 = img.size[0] // 4
@ -439,7 +437,6 @@ def test_autocontrast_mask_toy_input():
def test_autocontrast_mask_real_input(): def test_autocontrast_mask_real_input():
# Test the autocontrast with a rectangular mask # Test the autocontrast with a rectangular mask
with Image.open("Tests/images/iptc.jpg") as img: with Image.open("Tests/images/iptc.jpg") as img:
rect_mask = Image.new("L", img.size, 0) rect_mask = Image.new("L", img.size, 0)
draw = ImageDraw.Draw(rect_mask) draw = ImageDraw.Draw(rect_mask)
x0, y0 = img.size[0] // 2, img.size[1] // 2 x0, y0 = img.size[0] // 2, img.size[1] // 2

View File

@ -6,7 +6,6 @@ from .helper import assert_image_equal, assert_image_equal_tofile
def test_sanity(): def test_sanity():
palette = ImagePalette.ImagePalette("RGB", list(range(256)) * 3) palette = ImagePalette.ImagePalette("RGB", list(range(256)) * 3)
assert len(palette.colors) == 256 assert len(palette.colors) == 256
@ -23,7 +22,6 @@ def test_reload():
def test_getcolor(): def test_getcolor():
palette = ImagePalette.ImagePalette() palette = ImagePalette.ImagePalette()
assert len(palette.palette) == 0 assert len(palette.palette) == 0
assert len(palette.colors) == 0 assert len(palette.colors) == 0
@ -84,7 +82,6 @@ def test_getcolor_not_special(index, palette):
def test_file(tmp_path): def test_file(tmp_path):
palette = ImagePalette.ImagePalette("RGB", list(range(256)) * 3) palette = ImagePalette.ImagePalette("RGB", list(range(256)) * 3)
f = str(tmp_path / "temp.lut") f = str(tmp_path / "temp.lut")

View File

@ -8,7 +8,6 @@ from PIL import Image, ImagePath
def test_path(): def test_path():
p = ImagePath.Path(list(range(10))) p = ImagePath.Path(list(range(10)))
# sequence interface # sequence interface

View File

@ -6,7 +6,6 @@ from .helper import assert_image_equal, hopper, skip_unless_feature
def test_sanity(tmp_path): def test_sanity(tmp_path):
test_file = str(tmp_path / "temp.im") test_file = str(tmp_path / "temp.im")
im = hopper("RGB") im = hopper("RGB")

View File

@ -6,7 +6,6 @@ from .helper import hopper
def test_sanity(): def test_sanity():
im = hopper() im = hopper()
st = ImageStat.Stat(im) st = ImageStat.Stat(im)
@ -31,7 +30,6 @@ def test_sanity():
def test_hopper(): def test_hopper():
im = hopper() im = hopper()
st = ImageStat.Stat(im) st = ImageStat.Stat(im)
@ -45,7 +43,6 @@ def test_hopper():
def test_constant(): def test_constant():
im = Image.new("L", (128, 128), 128) im = Image.new("L", (128, 128), 128)
st = ImageStat.Stat(im) st = ImageStat.Stat(im)

View File

@ -4,7 +4,6 @@ from PIL import Image
def test_setmode(): def test_setmode():
im = Image.new("L", (1, 1), 255) im = Image.new("L", (1, 1), 255)
im.im.setmode("1") im.im.setmode("1")
assert im.im.getpixel((0, 0)) == 255 assert im.im.getpixel((0, 0)) == 255

View File

@ -42,7 +42,6 @@ def test_basic(tmp_path, mode):
im_in.save(filename) im_in.save(filename)
with Image.open(filename) as im_out: with Image.open(filename) as im_out:
verify(im_in) verify(im_in)
verify(im_out) verify(im_out)
@ -87,7 +86,6 @@ def test_tobytes():
def test_convert(): def test_convert():
im = original.copy() im = original.copy()
verify(im.convert("I;16")) verify(im.convert("I;16"))

View File

@ -235,7 +235,6 @@ def test_no_resource_warning_for_numpy_array():
test_file = "Tests/images/hopper.png" test_file = "Tests/images/hopper.png"
with Image.open(test_file) as im: with Image.open(test_file) as im:
# Act/Assert # Act/Assert
with warnings.catch_warnings(): with warnings.catch_warnings():
array(im) array(im)

View File

@ -89,7 +89,6 @@ def test_pickle_la_mode_with_palette(tmp_path):
def test_pickle_tell(): def test_pickle_tell():
# Arrange # Arrange
with Image.open("Tests/images/hopper.webp") as image: with Image.open("Tests/images/hopper.webp") as image:
# Act: roundtrip # Act: roundtrip
unpickled_image = pickle.loads(pickle.dumps(image)) unpickled_image = pickle.loads(pickle.dumps(image))

View File

@ -6,7 +6,7 @@ with warnings.catch_warnings():
warnings.simplefilter("ignore", category=DeprecationWarning) warnings.simplefilter("ignore", category=DeprecationWarning)
from PIL import ImageQt from PIL import ImageQt
from .helper import assert_image_equal, assert_image_equal_tofile, hopper from .helper import assert_image_equal_tofile, assert_image_similar, hopper
if ImageQt.qt_is_installed: if ImageQt.qt_is_installed:
from PIL.ImageQt import QPixmap from PIL.ImageQt import QPixmap
@ -48,7 +48,7 @@ if ImageQt.qt_is_installed:
def roundtrip(expected): def roundtrip(expected):
result = ImageQt.fromqpixmap(ImageQt.toqpixmap(expected)) result = ImageQt.fromqpixmap(ImageQt.toqpixmap(expected))
# Qt saves all pixmaps as rgb # Qt saves all pixmaps as rgb
assert_image_equal(result, expected.convert("RGB")) assert_image_similar(result, expected.convert("RGB"), 0.3)
@pytest.mark.skipif(not ImageQt.qt_is_installed, reason="Qt bindings are not installed") @pytest.mark.skipif(not ImageQt.qt_is_installed, reason="Qt bindings are not installed")

View File

@ -7,7 +7,6 @@ from .helper import hopper
def _test_equal(num, denom, target): def _test_equal(num, denom, target):
t = IFDRational(num, denom) t = IFDRational(num, denom)
assert target == t assert target == t
@ -15,7 +14,6 @@ def _test_equal(num, denom, target):
def test_sanity(): def test_sanity():
_test_equal(1, 1, 1) _test_equal(1, 1, 1)
_test_equal(1, 1, Fraction(1, 1)) _test_equal(1, 1, Fraction(1, 1))

View File

@ -9,7 +9,6 @@ test_file = "Tests/images/hopper.webp"
@skip_unless_feature("webp") @skip_unless_feature("webp")
class TestWebPLeaks(PillowLeakTestCase): class TestWebPLeaks(PillowLeakTestCase):
mem_limit = 3 * 1024 # kb mem_limit = 3 * 1024 # kb
iterations = 100 iterations = 100

View File

@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
# install libimagequant # install libimagequant
archive=libimagequant-4.0.4 archive=libimagequant-4.1.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

View File

@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
# install webp # install webp
archive=libwebp-1.2.4 archive=libwebp-1.3.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

View File

@ -5,7 +5,7 @@ The Python Imaging Library (PIL) is
Pillow is the friendly PIL fork. It is Pillow is the friendly PIL fork. It is
Copyright © 2010-2023 by Alex Clark and contributors Copyright © 2010-2023 by Jeffrey A. Clark (Alex) and contributors
Like PIL, Pillow is licensed under the open source PIL Like PIL, Pillow is licensed under the open source PIL
Software License: Software License:

View File

@ -52,8 +52,10 @@ master_doc = "index"
# General information about the project. # General information about the project.
project = "Pillow (PIL Fork)" project = "Pillow (PIL Fork)"
copyright = "1995-2011 Fredrik Lundh, 2010-2023 Alex Clark and Contributors" copyright = (
author = "Fredrik Lundh, Alex Clark and Contributors" "1995-2011 Fredrik Lundh, 2010-2023 Jeffrey A. Clark (Alex) and contributors"
)
author = "Fredrik Lundh, Jeffrey A. Clark (Alex), contributors"
# The version info for the project you're documenting, acts as replacement for # The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the # |version| and |release|, also used in various other places throughout the
@ -243,7 +245,7 @@ latex_documents = [
master_doc, master_doc,
"PillowPILFork.tex", "PillowPILFork.tex",
"Pillow (PIL Fork) Documentation", "Pillow (PIL Fork) Documentation",
"Alex Clark", "Jeffrey A. Clark (Alex)",
"manual", "manual",
) )
] ]
@ -293,7 +295,7 @@ texinfo_documents = [
"Pillow (PIL Fork) Documentation", "Pillow (PIL Fork) Documentation",
author, author,
"PillowPILFork", "PillowPILFork",
"Pillow is the friendly PIL fork by Alex Clark and Contributors.", "Pillow is the friendly PIL fork by Jeffrey A. Clark (Alex) and contributors.",
"Miscellaneous", "Miscellaneous",
) )
] ]

View File

@ -31,7 +31,7 @@ INT32 and a 32-bit floating point pixel has the range of FLOAT32. The current re
supports the following standard modes: supports the following standard modes:
* ``1`` (1-bit pixels, black and white, stored with one pixel per byte) * ``1`` (1-bit pixels, black and white, stored with one pixel per byte)
* ``L`` (8-bit pixels, black and white) * ``L`` (8-bit pixels, grayscale)
* ``P`` (8-bit pixels, mapped to any other mode using a color palette) * ``P`` (8-bit pixels, mapped to any other mode using a color palette)
* ``RGB`` (3x8-bit pixels, true color) * ``RGB`` (3x8-bit pixels, true color)
* ``RGBA`` (4x8-bit pixels, true color with transparency mask) * ``RGBA`` (4x8-bit pixels, true color with transparency mask)

View File

@ -1,7 +1,7 @@
Pillow Pillow
====== ======
Pillow is the friendly PIL fork by `Alex Clark and Contributors <https://github.com/python-pillow/Pillow/graphs/contributors>`_. PIL is the Python Imaging Library by Fredrik Lundh and Contributors. Pillow is the friendly PIL fork by `Jeffrey A. Clark (Alex) and contributors <https://github.com/python-pillow/Pillow/graphs/contributors>`_. PIL is the Python Imaging Library by Fredrik Lundh and contributors.
Pillow for enterprise is available via the Tidelift Subscription. `Learn more <https://tidelift.com/subscription/pkg/pypi-pillow?utm_source=pypi-pillow&utm_medium=docs&utm_campaign=enterprise>`_. Pillow for enterprise is available via the Tidelift Subscription. `Learn more <https://tidelift.com/subscription/pkg/pypi-pillow?utm_source=pypi-pillow&utm_medium=docs&utm_campaign=enterprise>`_.

View File

@ -169,7 +169,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.0.4** * Pillow has been tested with libimagequant **2.6-4.1**
* 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.

View File

@ -4,8 +4,8 @@ description = Python Imaging Library (Fork)
long_description = file: README.md long_description = file: README.md
long_description_content_type = text/markdown long_description_content_type = text/markdown
url = https://python-pillow.org url = https://python-pillow.org
author = Alex Clark (PIL Fork Author) author = Jeffrey A. Clark (Alex)
author_email = aclark@python-pillow.org author_email = aclark@aclark.net
license = HPND license = HPND
classifiers = classifiers =
Development Status :: 6 - Mature Development Status :: 6 - Mature

View File

@ -263,18 +263,18 @@ def _pkg_config(name):
if not DEBUG: if not DEBUG:
command_libs.append("--silence-errors") command_libs.append("--silence-errors")
command_cflags.append("--silence-errors") command_cflags.append("--silence-errors")
libs = ( libs = re.split(
r"(^|\s+)-L",
subprocess.check_output(command_libs, stderr=stderr) subprocess.check_output(command_libs, stderr=stderr)
.decode("utf8") .decode("utf8")
.strip() .strip(),
.replace("-L", "") )[::2][1:]
) cflags = re.split(
cflags = ( r"(^|\s+)-I",
subprocess.check_output(command_cflags) subprocess.check_output(command_cflags, stderr=stderr)
.decode("utf8") .decode("utf8")
.strip() .strip(),
.replace("-I", "") )[::2][1:]
)
return libs, cflags return libs, cflags
except Exception: except Exception:
pass pass
@ -430,7 +430,6 @@ class pil_build_ext(build_ext):
return sdk_path return sdk_path
def build_extensions(self): def build_extensions(self):
library_dirs = [] library_dirs = []
include_dirs = [] include_dirs = []
@ -473,8 +472,12 @@ class pil_build_ext(build_ext):
else: else:
lib_root = include_root = root lib_root = include_root = root
_add_directory(library_dirs, lib_root) if lib_root is not None:
_add_directory(include_dirs, include_root) for lib_dir in lib_root:
_add_directory(library_dirs, lib_dir)
if include_root is not None:
for include_dir in include_root:
_add_directory(include_dirs, include_dir)
# respect CFLAGS/CPPFLAGS/LDFLAGS # respect CFLAGS/CPPFLAGS/LDFLAGS
for k in ("CFLAGS", "CPPFLAGS", "LDFLAGS"): for k in ("CFLAGS", "CPPFLAGS", "LDFLAGS"):
@ -913,7 +916,6 @@ class pil_build_ext(build_ext):
self.summary_report(feature) self.summary_report(feature)
def summary_report(self, feature): def summary_report(self, feature):
print("-" * 68) print("-" * 68)
print("PIL SETUP SUMMARY") print("PIL SETUP SUMMARY")
print("-" * 68) print("-" * 68)

View File

@ -223,7 +223,6 @@ class BmpImageFile(ImageFile.ImageFile):
# --------------- Once the header is processed, process the palette/LUT # --------------- Once the header is processed, process the palette/LUT
if self.mode == "P": # Paletted for 1, 4 and 8 bit images if self.mode == "P": # Paletted for 1, 4 and 8 bit images
# ---------------------------------------------------- 1-bit images # ---------------------------------------------------- 1-bit images
if not (0 < file_info["colors"] <= 65536): if not (0 < file_info["colors"] <= 65536):
msg = f"Unsupported BMP Palette size ({file_info['colors']})" msg = f"Unsupported BMP Palette size ({file_info['colors']})"
@ -360,7 +359,6 @@ class BmpRleDecoder(ImageFile.PyDecoder):
# Image plugin for the DIB format (BMP alias) # Image plugin for the DIB format (BMP alias)
# ============================================================================= # =============================================================================
class DibImageFile(BmpImageFile): class DibImageFile(BmpImageFile):
format = "DIB" format = "DIB"
format_description = "Windows Bitmap" format_description = "Windows Bitmap"

View File

@ -33,12 +33,10 @@ def _accept(prefix):
class BufrStubImageFile(ImageFile.StubImageFile): class BufrStubImageFile(ImageFile.StubImageFile):
format = "BUFR" format = "BUFR"
format_description = "BUFR" format_description = "BUFR"
def _open(self): def _open(self):
offset = self.fp.tell() offset = self.fp.tell()
if not _accept(self.fp.read(4)): if not _accept(self.fp.read(4)):

View File

@ -32,12 +32,10 @@ def _accept(prefix):
class CurImageFile(BmpImagePlugin.BmpImageFile): class CurImageFile(BmpImagePlugin.BmpImageFile):
format = "CUR" format = "CUR"
format_description = "Windows Cursor" format_description = "Windows Cursor"
def _open(self): def _open(self):
offset = self.fp.tell() offset = self.fp.tell()
# check magic # check magic

View File

@ -37,13 +37,11 @@ def _accept(prefix):
class DcxImageFile(PcxImageFile): class DcxImageFile(PcxImageFile):
format = "DCX" format = "DCX"
format_description = "Intel DCX" format_description = "Intel DCX"
_close_exclusive_fp_after_loading = False _close_exclusive_fp_after_loading = False
def _open(self): def _open(self):
# Header # Header
s = self.fp.read(4) s = self.fp.read(4)
if not _accept(s): if not _accept(s):

View File

@ -19,7 +19,6 @@ def _accept(prefix):
class FitsImageFile(ImageFile.ImageFile): class FitsImageFile(ImageFile.ImageFile):
format = "FITS" format = "FITS"
format_description = "FITS" format_description = "FITS"

View File

@ -44,7 +44,6 @@ def register_handler(handler):
class FITSStubImageFile(ImageFile.StubImageFile): class FITSStubImageFile(ImageFile.StubImageFile):
format = FitsImagePlugin.FitsImageFile.format format = FitsImagePlugin.FitsImageFile.format
format_description = FitsImagePlugin.FitsImageFile.format_description format_description = FitsImagePlugin.FitsImageFile.format_description

View File

@ -40,13 +40,11 @@ def _accept(prefix):
class FliImageFile(ImageFile.ImageFile): class FliImageFile(ImageFile.ImageFile):
format = "FLI" format = "FLI"
format_description = "Autodesk FLI/FLC Animation" format_description = "Autodesk FLI/FLC Animation"
_close_exclusive_fp_after_loading = False _close_exclusive_fp_after_loading = False
def _open(self): def _open(self):
# HEAD # HEAD
s = self.fp.read(128) s = self.fp.read(128)
if not (_accept(s) and s[20:22] == b"\x00\x00"): if not (_accept(s) and s[20:22] == b"\x00\x00"):

View File

@ -36,7 +36,6 @@ class FontFile:
bitmap = None bitmap = None
def __init__(self): def __init__(self):
self.info = {} self.info = {}
self.glyph = [None] * 256 self.glyph = [None] * 256

View File

@ -48,7 +48,6 @@ def _accept(prefix):
class FpxImageFile(ImageFile.ImageFile): class FpxImageFile(ImageFile.ImageFile):
format = "FPX" format = "FPX"
format_description = "FlashPix" format_description = "FlashPix"
@ -157,7 +156,6 @@ class FpxImageFile(ImageFile.ImageFile):
self.tile = [] self.tile = []
for i in range(0, len(s), length): for i in range(0, len(s), length):
x1 = min(xsize, x + xtile) x1 = min(xsize, x + xtile)
y1 = min(ysize, y + ytile) y1 = min(ysize, y + ytile)
@ -174,7 +172,6 @@ class FpxImageFile(ImageFile.ImageFile):
) )
elif compression == 1: elif compression == 1:
# FIXME: the fill decoder is not implemented # FIXME: the fill decoder is not implemented
self.tile.append( self.tile.append(
( (
@ -186,7 +183,6 @@ class FpxImageFile(ImageFile.ImageFile):
) )
elif compression == 2: elif compression == 2:
internal_color_conversion = s[14] internal_color_conversion = s[14]
jpeg_tables = s[15] jpeg_tables = s[15]
rawmode = self.rawmode rawmode = self.rawmode
@ -234,7 +230,6 @@ class FpxImageFile(ImageFile.ImageFile):
self.fp = None self.fp = None
def load(self): def load(self):
if not self.fp: if not self.fp:
self.fp = self.ole.openstream(self.stream[:2] + ["Subimage 0000 Data"]) self.fp = self.ole.openstream(self.stream[:2] + ["Subimage 0000 Data"])

View File

@ -37,7 +37,6 @@ def _accept(prefix):
class GbrImageFile(ImageFile.ImageFile): class GbrImageFile(ImageFile.ImageFile):
format = "GBR" format = "GBR"
format_description = "GIMP brush file" format_description = "GIMP brush file"

View File

@ -44,7 +44,6 @@ class GdImageFile(ImageFile.ImageFile):
format_description = "GD uncompressed images" format_description = "GD uncompressed images"
def _open(self): def _open(self):
# Header # Header
s = self.fp.read(1037) s = self.fp.read(1037)

View File

@ -61,7 +61,6 @@ def _accept(prefix):
class GifImageFile(ImageFile.ImageFile): class GifImageFile(ImageFile.ImageFile):
format = "GIF" format = "GIF"
format_description = "Compuserve GIF" format_description = "Compuserve GIF"
_close_exclusive_fp_after_loading = False _close_exclusive_fp_after_loading = False
@ -81,7 +80,6 @@ class GifImageFile(ImageFile.ImageFile):
return False return False
def _open(self): def _open(self):
# Screen # Screen
s = self.fp.read(13) s = self.fp.read(13)
if not _accept(s): if not _accept(s):
@ -157,7 +155,6 @@ class GifImageFile(ImageFile.ImageFile):
raise EOFError(msg) from e raise EOFError(msg) from e
def _seek(self, frame, update_image=True): def _seek(self, frame, update_image=True):
if frame == 0: if frame == 0:
# rewind # rewind
self.__offset = 0 self.__offset = 0
@ -195,7 +192,6 @@ class GifImageFile(ImageFile.ImageFile):
interlace = None interlace = None
frame_dispose_extent = None frame_dispose_extent = None
while True: while True:
if not s: if not s:
s = self.fp.read(1) s = self.fp.read(1)
if not s or s == b";": if not s or s == b";":
@ -579,7 +575,6 @@ def _getbbox(base_im, im_frame):
def _write_multiple_frames(im, fp, palette): def _write_multiple_frames(im, fp, palette):
duration = im.encoderinfo.get("duration") duration = im.encoderinfo.get("duration")
disposal = im.encoderinfo.get("disposal", im.info.get("disposal")) disposal = im.encoderinfo.get("disposal", im.info.get("disposal"))
@ -752,7 +747,6 @@ def _write_local_header(fp, im, offset, flags):
def _save_netpbm(im, fp, filename): def _save_netpbm(im, fp, filename):
# Unused by default. # Unused by default.
# To use, uncomment the register_save call at the end of the file. # To use, uncomment the register_save call at the end of the file.
# #

View File

@ -64,18 +64,15 @@ SEGMENTS = [linear, curved, sine, sphere_increasing, sphere_decreasing]
class GradientFile: class GradientFile:
gradient = None gradient = None
def getpalette(self, entries=256): def getpalette(self, entries=256):
palette = [] palette = []
ix = 0 ix = 0
x0, x1, xm, rgb0, rgb1, segment = self.gradient[ix] x0, x1, xm, rgb0, rgb1, segment = self.gradient[ix]
for i in range(entries): for i in range(entries):
x = i / (entries - 1) x = i / (entries - 1)
while x1 < x: while x1 < x:
@ -105,7 +102,6 @@ class GimpGradientFile(GradientFile):
"""File handler for GIMP's gradient format.""" """File handler for GIMP's gradient format."""
def __init__(self, fp): def __init__(self, fp):
if fp.readline()[:13] != b"GIMP Gradient": if fp.readline()[:13] != b"GIMP Gradient":
msg = "not a GIMP gradient file" msg = "not a GIMP gradient file"
raise SyntaxError(msg) raise SyntaxError(msg)
@ -121,7 +117,6 @@ class GimpGradientFile(GradientFile):
gradient = [] gradient = []
for i in range(count): for i in range(count):
s = fp.readline().split() s = fp.readline().split()
w = [float(x) for x in s[:11]] w = [float(x) for x in s[:11]]

View File

@ -25,7 +25,6 @@ class GimpPaletteFile:
rawmode = "RGB" rawmode = "RGB"
def __init__(self, fp): def __init__(self, fp):
self.palette = [o8(i) * 3 for i in range(256)] self.palette = [o8(i) * 3 for i in range(256)]
if fp.readline()[:12] != b"GIMP Palette": if fp.readline()[:12] != b"GIMP Palette":
@ -33,7 +32,6 @@ class GimpPaletteFile:
raise SyntaxError(msg) raise SyntaxError(msg)
for i in range(256): for i in range(256):
s = fp.readline() s = fp.readline()
if not s: if not s:
break break
@ -55,5 +53,4 @@ class GimpPaletteFile:
self.palette = b"".join(self.palette) self.palette = b"".join(self.palette)
def getpalette(self): def getpalette(self):
return self.palette, self.rawmode return self.palette, self.rawmode

View File

@ -33,12 +33,10 @@ def _accept(prefix):
class GribStubImageFile(ImageFile.StubImageFile): class GribStubImageFile(ImageFile.StubImageFile):
format = "GRIB" format = "GRIB"
format_description = "GRIB" format_description = "GRIB"
def _open(self): def _open(self):
offset = self.fp.tell() offset = self.fp.tell()
if not _accept(self.fp.read(8)): if not _accept(self.fp.read(8)):

View File

@ -33,12 +33,10 @@ def _accept(prefix):
class HDF5StubImageFile(ImageFile.StubImageFile): class HDF5StubImageFile(ImageFile.StubImageFile):
format = "HDF5" format = "HDF5"
format_description = "HDF5" format_description = "HDF5"
def _open(self): def _open(self):
offset = self.fp.tell() offset = self.fp.tell()
if not _accept(self.fp.read(8)): if not _accept(self.fp.read(8)):

View File

@ -135,7 +135,6 @@ def read_png_or_jpeg2000(fobj, start_length, size):
class IcnsFile: class IcnsFile:
SIZES = { SIZES = {
(512, 512, 2): [(b"ic10", read_png_or_jpeg2000)], (512, 512, 2): [(b"ic10", read_png_or_jpeg2000)],
(512, 512, 1): [(b"ic09", read_png_or_jpeg2000)], (512, 512, 1): [(b"ic09", read_png_or_jpeg2000)],
@ -189,7 +188,7 @@ class IcnsFile:
def itersizes(self): def itersizes(self):
sizes = [] sizes = []
for size, fmts in self.SIZES.items(): for size, fmts in self.SIZES.items():
for (fmt, reader) in fmts: for fmt, reader in fmts:
if fmt in self.dct: if fmt in self.dct:
sizes.append(size) sizes.append(size)
break break

View File

@ -185,7 +185,7 @@ class IcoFile:
return {(h["width"], h["height"]) for h in self.entry} return {(h["width"], h["height"]) for h in self.entry}
def getentryindex(self, size, bpp=False): def getentryindex(self, size, bpp=False):
for (i, h) in enumerate(self.entry): for i, h in enumerate(self.entry):
if size == h["dim"] and (bpp is False or bpp == h["color_depth"]): if size == h["dim"] and (bpp is False or bpp == h["color_depth"]):
return i return i
return 0 return 0

View File

@ -115,13 +115,11 @@ def number(s):
class ImImageFile(ImageFile.ImageFile): class ImImageFile(ImageFile.ImageFile):
format = "IM" format = "IM"
format_description = "IFUNC Image Memory" format_description = "IFUNC Image Memory"
_close_exclusive_fp_after_loading = False _close_exclusive_fp_after_loading = False
def _open(self): def _open(self):
# Quick rejection: if there's not an LF among the first # Quick rejection: if there's not an LF among the first
# 100 bytes, this is (probably) not a text header. # 100 bytes, this is (probably) not a text header.
@ -140,7 +138,6 @@ class ImImageFile(ImageFile.ImageFile):
self.rawmode = "L" self.rawmode = "L"
while True: while True:
s = self.fp.read(1) s = self.fp.read(1)
# Some versions of IFUNC uses \n\r instead of \r\n... # Some versions of IFUNC uses \n\r instead of \r\n...
@ -169,7 +166,6 @@ class ImImageFile(ImageFile.ImageFile):
raise SyntaxError(msg) from e raise SyntaxError(msg) from e
if m: if m:
k, v = m.group(1, 2) k, v = m.group(1, 2)
# Don't know if this is the correct encoding, # Don't know if this is the correct encoding,
@ -200,7 +196,6 @@ class ImImageFile(ImageFile.ImageFile):
n += 1 n += 1
else: else:
msg = "Syntax error in IM header: " + s.decode("ascii", "replace") msg = "Syntax error in IM header: " + s.decode("ascii", "replace")
raise SyntaxError(msg) raise SyntaxError(msg)
@ -252,7 +247,6 @@ class ImImageFile(ImageFile.ImageFile):
self._fp = self.fp # FIXME: hack self._fp = self.fp # FIXME: hack
if self.rawmode[:2] == "F;": if self.rawmode[:2] == "F;":
# ifunc95 formats # ifunc95 formats
try: try:
# use bit decoder (if necessary) # use bit decoder (if necessary)
@ -332,7 +326,6 @@ SAVE = {
def _save(im, fp, filename): def _save(im, fp, filename):
try: try:
image_type, rawmode = SAVE[im.mode] image_type, rawmode = SAVE[im.mode]
except KeyError as e: except KeyError as e:

View File

@ -153,6 +153,7 @@ def isImageType(t):
# #
# Constants # Constants
# transpose # transpose
class Transpose(IntEnum): class Transpose(IntEnum):
FLIP_LEFT_RIGHT = 0 FLIP_LEFT_RIGHT = 0
@ -391,7 +392,6 @@ def init():
def _getdecoder(mode, decoder_name, args, extra=()): def _getdecoder(mode, decoder_name, args, extra=()):
# tweak arguments # tweak arguments
if args is None: if args is None:
args = () args = ()
@ -415,7 +415,6 @@ def _getdecoder(mode, decoder_name, args, extra=()):
def _getencoder(mode, encoder_name, args, extra=()): def _getencoder(mode, encoder_name, args, extra=()):
# tweak arguments # tweak arguments
if args is None: if args is None:
args = () args = ()
@ -3267,9 +3266,15 @@ def open(fp, mode="r", formats=None):
im = _open_core(fp, filename, prefix, formats) im = _open_core(fp, filename, prefix, formats)
if im is None: if im is None and formats is ID:
checked_formats = formats.copy()
if init(): if init():
im = _open_core(fp, filename, prefix, formats) im = _open_core(
fp,
filename,
prefix,
tuple(format for format in formats if format not in checked_formats),
)
if im: if im:
im._exclusive_fp = exclusive_fp im._exclusive_fp = exclusive_fp
@ -3400,7 +3405,8 @@ def register_open(id, factory, accept=None):
reject images having another format. reject images having another format.
""" """
id = id.upper() id = id.upper()
ID.append(id) if id not in ID:
ID.append(id)
OPEN[id] = factory, accept OPEN[id] = factory, accept

View File

@ -928,8 +928,8 @@ def floodfill(image, xy, value, border=None, thresh=0):
full_edge = set() full_edge = set()
while edge: while edge:
new_edge = set() new_edge = set()
for (x, y) in edge: # 4 adjacent method for x, y in edge: # 4 adjacent method
for (s, t) in ((x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)): for s, t in ((x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)):
# If already processed, or if a coordinate is negative, skip # If already processed, or if a coordinate is negative, skip
if (s, t) in full_edge or s < 0 or t < 0: if (s, t) in full_edge or s < 0 or t < 0:
continue continue

View File

@ -395,7 +395,6 @@ class Parser:
# parse what we have # parse what we have
if self.decoder: if self.decoder:
if self.offset > 0: if self.offset > 0:
# skip header # skip header
skip = min(len(self.data), self.offset) skip = min(len(self.data), self.offset)
@ -420,14 +419,12 @@ class Parser:
self.data = self.data[n:] self.data = self.data[n:]
elif self.image: elif self.image:
# if we end up here with no decoder, this file cannot # if we end up here with no decoder, this file cannot
# be incrementally parsed. wait until we've gotten all # be incrementally parsed. wait until we've gotten all
# available data # available data
pass pass
else: else:
# attempt to open this file # attempt to open this file
try: try:
with io.BytesIO(self.data) as fp: with io.BytesIO(self.data) as fp:

View File

@ -90,7 +90,6 @@ class ImageFont:
"""PIL font wrapper""" """PIL font wrapper"""
def _load_pilfont(self, filename): def _load_pilfont(self, filename):
with open(filename, "rb") as fp: with open(filename, "rb") as fp:
image = None image = None
for ext in (".png", ".gif", ".pbm"): for ext in (".png", ".gif", ".pbm"):
@ -116,7 +115,6 @@ class ImageFont:
image.close() image.close()
def _load_pilfont_data(self, file, image): def _load_pilfont_data(self, file, image):
# read PILfont header # read PILfont header
if file.readline() != b"PILfont\n": if file.readline() != b"PILfont\n":
msg = "Not a PILfont file" msg = "Not a PILfont file"

View File

@ -205,7 +205,6 @@ def colorize(image, black, white, mid=None, blackpoint=0, whitepoint=255, midpoi
# Create the mapping (2-color) # Create the mapping (2-color)
if mid is None: if mid is None:
range_map = range(0, whitepoint - blackpoint) range_map = range(0, whitepoint - blackpoint)
for i in range_map: for i in range_map:
@ -215,7 +214,6 @@ def colorize(image, black, white, mid=None, blackpoint=0, whitepoint=255, midpoi
# Create the mapping (3-color) # Create the mapping (3-color)
else: else:
range_map1 = range(0, midpoint - blackpoint) range_map1 = range(0, midpoint - blackpoint)
range_map2 = range(0, whitepoint - midpoint) range_map2 = range(0, whitepoint - midpoint)

View File

@ -248,11 +248,9 @@ def wedge(mode="RGB"):
def load(filename): def load(filename):
# FIXME: supports GIMP gradients only # FIXME: supports GIMP gradients only
with open(filename, "rb") as fp: with open(filename, "rb") as fp:
for paletteHandler in [ for paletteHandler in [
GimpPaletteFile.GimpPaletteFile, GimpPaletteFile.GimpPaletteFile,
GimpGradientFile.GimpGradientFile, GimpGradientFile.GimpGradientFile,

View File

@ -390,7 +390,6 @@ else:
if __name__ == "__main__": if __name__ == "__main__":
if len(sys.argv) < 2: if len(sys.argv) < 2:
print("Syntax: python3 ImageShow.py imagefile [title]") print("Syntax: python3 ImageShow.py imagefile [title]")
sys.exit() sys.exit()

View File

@ -97,7 +97,6 @@ class PhotoImage:
""" """
def __init__(self, image=None, size=None, **kw): def __init__(self, image=None, size=None, **kw):
# Tk compatibility: file or data # Tk compatibility: file or data
if image is None: if image is None:
image = _get_image_from_kw(kw) image = _get_image_from_kw(kw)
@ -209,7 +208,6 @@ class BitmapImage:
""" """
def __init__(self, image=None, **kw): def __init__(self, image=None, **kw):
# Tk compatibility: file or data # Tk compatibility: file or data
if image is None: if image is None:
image = _get_image_from_kw(kw) image = _get_image_from_kw(kw)

View File

@ -30,12 +30,10 @@ field = re.compile(rb"([a-z]*) ([^ \r\n]*)")
class ImtImageFile(ImageFile.ImageFile): class ImtImageFile(ImageFile.ImageFile):
format = "IMT" format = "IMT"
format_description = "IM Tools" format_description = "IM Tools"
def _open(self): def _open(self):
# Quick rejection: if there's not a LF among the first # Quick rejection: if there's not a LF among the first
# 100 bytes, this is (probably) not a text header. # 100 bytes, this is (probably) not a text header.
@ -47,7 +45,6 @@ class ImtImageFile(ImageFile.ImageFile):
xsize = ysize = 0 xsize = ysize = 0
while True: while True:
if buffer: if buffer:
s = buffer[:1] s = buffer[:1]
buffer = buffer[1:] buffer = buffer[1:]
@ -57,7 +54,6 @@ class ImtImageFile(ImageFile.ImageFile):
break break
if s == b"\x0C": if s == b"\x0C":
# image data begins # image data begins
self.tile = [ self.tile = [
( (
@ -71,7 +67,6 @@ class ImtImageFile(ImageFile.ImageFile):
break break
else: else:
# read key/value pair # read key/value pair
if b"\n" not in buffer: if b"\n" not in buffer:
buffer += self.fp.read(100) buffer += self.fp.read(100)

View File

@ -48,7 +48,6 @@ def dump(c):
class IptcImageFile(ImageFile.ImageFile): class IptcImageFile(ImageFile.ImageFile):
format = "IPTC" format = "IPTC"
format_description = "IPTC/NAA" format_description = "IPTC/NAA"
@ -84,7 +83,6 @@ class IptcImageFile(ImageFile.ImageFile):
return tag, size return tag, size
def _open(self): def _open(self):
# load descriptive fields # load descriptive fields
while True: while True:
offset = self.fp.tell() offset = self.fp.tell()
@ -134,7 +132,6 @@ class IptcImageFile(ImageFile.ImageFile):
] ]
def load(self): def load(self):
if len(self.tile) != 1 or self.tile[0][0] != "iptc": if len(self.tile) != 1 or self.tile[0][0] != "iptc":
return ImageFile.ImageFile.load(self) return ImageFile.ImageFile.load(self)

View File

@ -41,7 +41,7 @@ import sys
import tempfile import tempfile
import warnings import warnings
from . import Image, ImageFile, TiffImagePlugin from . import Image, ImageFile
from ._binary import i16be as i16 from ._binary import i16be as i16
from ._binary import i32be as i32 from ._binary import i32be as i32
from ._binary import o8 from ._binary import o8
@ -344,12 +344,10 @@ def _accept(prefix):
class JpegImageFile(ImageFile.ImageFile): class JpegImageFile(ImageFile.ImageFile):
format = "JPEG" format = "JPEG"
format_description = "JPEG (ISO 10918)" format_description = "JPEG (ISO 10918)"
def _open(self): def _open(self):
s = self.fp.read(3) s = self.fp.read(3)
if not _accept(s): if not _accept(s):
@ -370,7 +368,6 @@ class JpegImageFile(ImageFile.ImageFile):
self.icclist = [] self.icclist = []
while True: while True:
i = s[0] i = s[0]
if i == 0xFF: if i == 0xFF:
s = s + self.fp.read(1) s = s + self.fp.read(1)
@ -418,7 +415,6 @@ class JpegImageFile(ImageFile.ImageFile):
return s return s
def draft(self, mode, size): def draft(self, mode, size):
if len(self.tile) != 1: if len(self.tile) != 1:
return return
@ -455,7 +451,6 @@ class JpegImageFile(ImageFile.ImageFile):
return self.mode, box return self.mode, box
def load_djpeg(self): def load_djpeg(self):
# ALTERNATIVE: handle JPEGs via the IJG command line utilities # ALTERNATIVE: handle JPEGs via the IJG command line utilities
f, path = tempfile.mkstemp() f, path = tempfile.mkstemp()
@ -524,6 +519,8 @@ def _getmp(self):
head = file_contents.read(8) head = file_contents.read(8)
endianness = ">" if head[:4] == b"\x4d\x4d\x00\x2a" else "<" endianness = ">" if head[:4] == b"\x4d\x4d\x00\x2a" else "<"
# process dictionary # process dictionary
from . import TiffImagePlugin
try: try:
info = TiffImagePlugin.ImageFileDirectory_v2(head) info = TiffImagePlugin.ImageFileDirectory_v2(head)
file_contents.seek(info.next) file_contents.seek(info.next)

Some files were not shown because too many files have changed in this diff Show More