mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-07-11 08:42:35 +03:00
Merge 7eacae2c43
into 3e5df07b34
This commit is contained in:
commit
4daa8a5573
|
@ -101,6 +101,7 @@ def assert_image_equal_tofile(
|
||||||
msg: str | None = None,
|
msg: str | None = None,
|
||||||
mode: str | None = None,
|
mode: str | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
img: Image.Image
|
||||||
with Image.open(filename) as img:
|
with Image.open(filename) as img:
|
||||||
if mode:
|
if mode:
|
||||||
img = img.convert(mode)
|
img = img.convert(mode)
|
||||||
|
@ -144,6 +145,7 @@ def assert_image_similar_tofile(
|
||||||
epsilon: float,
|
epsilon: float,
|
||||||
msg: str | None = None,
|
msg: str | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
img: Image.Image
|
||||||
with Image.open(filename) as img:
|
with Image.open(filename) as img:
|
||||||
assert_image_similar(a, img, epsilon, msg)
|
assert_image_similar(a, img, epsilon, msg)
|
||||||
|
|
||||||
|
|
|
@ -94,8 +94,11 @@ def test_good() -> None:
|
||||||
|
|
||||||
for f in get_files("g"):
|
for f in get_files("g"):
|
||||||
try:
|
try:
|
||||||
|
im: Image.Image
|
||||||
with Image.open(f) as im:
|
with Image.open(f) as im:
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
|
compare: Image.Image
|
||||||
with Image.open(get_compare(f)) as compare:
|
with Image.open(get_compare(f)) as compare:
|
||||||
compare.load()
|
compare.load()
|
||||||
if im.mode == "P":
|
if im.mode == "P":
|
||||||
|
|
|
@ -277,25 +277,25 @@ def test_apng_mode() -> None:
|
||||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||||
assert im.mode == "P"
|
assert im.mode == "P"
|
||||||
im.seek(im.n_frames - 1)
|
im.seek(im.n_frames - 1)
|
||||||
im = im.convert("RGB")
|
rgb_im = im.convert("RGB")
|
||||||
assert im.getpixel((0, 0)) == (0, 255, 0)
|
assert rgb_im.getpixel((0, 0)) == (0, 255, 0)
|
||||||
assert im.getpixel((64, 32)) == (0, 255, 0)
|
assert rgb_im.getpixel((64, 32)) == (0, 255, 0)
|
||||||
|
|
||||||
with Image.open("Tests/images/apng/mode_palette_alpha.png") as im:
|
with Image.open("Tests/images/apng/mode_palette_alpha.png") as im:
|
||||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||||
assert im.mode == "P"
|
assert im.mode == "P"
|
||||||
im.seek(im.n_frames - 1)
|
im.seek(im.n_frames - 1)
|
||||||
im = im.convert("RGBA")
|
rgb_im = im.convert("RGBA")
|
||||||
assert im.getpixel((0, 0)) == (0, 255, 0, 255)
|
assert rgb_im.getpixel((0, 0)) == (0, 255, 0, 255)
|
||||||
assert im.getpixel((64, 32)) == (0, 255, 0, 255)
|
assert rgb_im.getpixel((64, 32)) == (0, 255, 0, 255)
|
||||||
|
|
||||||
with Image.open("Tests/images/apng/mode_palette_1bit_alpha.png") as im:
|
with Image.open("Tests/images/apng/mode_palette_1bit_alpha.png") as im:
|
||||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||||
assert im.mode == "P"
|
assert im.mode == "P"
|
||||||
im.seek(im.n_frames - 1)
|
im.seek(im.n_frames - 1)
|
||||||
im = im.convert("RGBA")
|
rgb_im = im.convert("RGBA")
|
||||||
assert im.getpixel((0, 0)) == (0, 0, 255, 128)
|
assert rgb_im.getpixel((0, 0)) == (0, 0, 255, 128)
|
||||||
assert im.getpixel((64, 32)) == (0, 0, 255, 128)
|
assert rgb_im.getpixel((64, 32)) == (0, 0, 255, 128)
|
||||||
|
|
||||||
|
|
||||||
def test_apng_chunk_errors() -> None:
|
def test_apng_chunk_errors() -> None:
|
||||||
|
|
|
@ -14,6 +14,7 @@ import pytest
|
||||||
|
|
||||||
from PIL import (
|
from PIL import (
|
||||||
AvifImagePlugin,
|
AvifImagePlugin,
|
||||||
|
GifImagePlugin,
|
||||||
Image,
|
Image,
|
||||||
ImageDraw,
|
ImageDraw,
|
||||||
ImageFile,
|
ImageFile,
|
||||||
|
@ -220,6 +221,7 @@ class TestFileAvif:
|
||||||
def test_background_from_gif(self, tmp_path: Path) -> None:
|
def test_background_from_gif(self, tmp_path: Path) -> None:
|
||||||
with Image.open("Tests/images/chi.gif") as im:
|
with Image.open("Tests/images/chi.gif") as im:
|
||||||
original_value = im.convert("RGB").getpixel((1, 1))
|
original_value = im.convert("RGB").getpixel((1, 1))
|
||||||
|
assert isinstance(original_value, tuple)
|
||||||
|
|
||||||
# Save as AVIF
|
# Save as AVIF
|
||||||
out_avif = tmp_path / "temp.avif"
|
out_avif = tmp_path / "temp.avif"
|
||||||
|
@ -232,6 +234,7 @@ class TestFileAvif:
|
||||||
|
|
||||||
with Image.open(out_gif) as reread:
|
with Image.open(out_gif) as reread:
|
||||||
reread_value = reread.convert("RGB").getpixel((1, 1))
|
reread_value = reread.convert("RGB").getpixel((1, 1))
|
||||||
|
assert isinstance(reread_value, tuple)
|
||||||
difference = sum([abs(original_value[i] - reread_value[i]) for i in range(3)])
|
difference = sum([abs(original_value[i] - reread_value[i]) for i in range(3)])
|
||||||
assert difference <= 6
|
assert difference <= 6
|
||||||
|
|
||||||
|
@ -240,6 +243,7 @@ class TestFileAvif:
|
||||||
with Image.open("Tests/images/chi.gif") as im:
|
with Image.open("Tests/images/chi.gif") as im:
|
||||||
im.save(temp_file)
|
im.save(temp_file)
|
||||||
with Image.open(temp_file) as im:
|
with Image.open(temp_file) as im:
|
||||||
|
assert isinstance(im, AvifImagePlugin.AvifImageFile)
|
||||||
assert im.n_frames == 1
|
assert im.n_frames == 1
|
||||||
|
|
||||||
def test_invalid_file(self) -> None:
|
def test_invalid_file(self) -> None:
|
||||||
|
@ -598,10 +602,12 @@ class TestAvifAnimation:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
with Image.open(TEST_AVIF_FILE) as im:
|
with Image.open(TEST_AVIF_FILE) as im:
|
||||||
|
assert isinstance(im, AvifImagePlugin.AvifImageFile)
|
||||||
assert im.n_frames == 1
|
assert im.n_frames == 1
|
||||||
assert not im.is_animated
|
assert not im.is_animated
|
||||||
|
|
||||||
with Image.open("Tests/images/avif/star.avifs") as im:
|
with Image.open("Tests/images/avif/star.avifs") as im:
|
||||||
|
assert isinstance(im, AvifImagePlugin.AvifImageFile)
|
||||||
assert im.n_frames == 5
|
assert im.n_frames == 5
|
||||||
assert im.is_animated
|
assert im.is_animated
|
||||||
|
|
||||||
|
@ -612,11 +618,13 @@ class TestAvifAnimation:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
with Image.open("Tests/images/avif/star.gif") as original:
|
with Image.open("Tests/images/avif/star.gif") as original:
|
||||||
|
assert isinstance(original, GifImagePlugin.GifImageFile)
|
||||||
assert original.n_frames > 1
|
assert original.n_frames > 1
|
||||||
|
|
||||||
temp_file = tmp_path / "temp.avif"
|
temp_file = tmp_path / "temp.avif"
|
||||||
original.save(temp_file, save_all=True)
|
original.save(temp_file, save_all=True)
|
||||||
with Image.open(temp_file) as im:
|
with Image.open(temp_file) as im:
|
||||||
|
assert isinstance(im, AvifImagePlugin.AvifImageFile)
|
||||||
assert im.n_frames == original.n_frames
|
assert im.n_frames == original.n_frames
|
||||||
|
|
||||||
# Compare first frame in P mode to frame from original GIF
|
# Compare first frame in P mode to frame from original GIF
|
||||||
|
@ -636,6 +644,7 @@ class TestAvifAnimation:
|
||||||
|
|
||||||
def check(temp_file: Path) -> None:
|
def check(temp_file: Path) -> None:
|
||||||
with Image.open(temp_file) as im:
|
with Image.open(temp_file) as im:
|
||||||
|
assert isinstance(im, AvifImagePlugin.AvifImageFile)
|
||||||
assert im.n_frames == 4
|
assert im.n_frames == 4
|
||||||
|
|
||||||
# Compare first frame to original
|
# Compare first frame to original
|
||||||
|
@ -708,6 +717,7 @@ class TestAvifAnimation:
|
||||||
)
|
)
|
||||||
|
|
||||||
with Image.open(temp_file) as im:
|
with Image.open(temp_file) as im:
|
||||||
|
assert isinstance(im, AvifImagePlugin.AvifImageFile)
|
||||||
assert im.n_frames == 5
|
assert im.n_frames == 5
|
||||||
assert im.is_animated
|
assert im.is_animated
|
||||||
|
|
||||||
|
@ -737,6 +747,7 @@ class TestAvifAnimation:
|
||||||
)
|
)
|
||||||
|
|
||||||
with Image.open(temp_file) as im:
|
with Image.open(temp_file) as im:
|
||||||
|
assert isinstance(im, AvifImagePlugin.AvifImageFile)
|
||||||
assert im.n_frames == 5
|
assert im.n_frames == 5
|
||||||
assert im.is_animated
|
assert im.is_animated
|
||||||
|
|
||||||
|
|
|
@ -160,9 +160,9 @@ def test_save_dib(tmp_path: Path) -> None:
|
||||||
def test_rgba_bitfields() -> None:
|
def test_rgba_bitfields() -> None:
|
||||||
# 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 bmp_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 = bmp_im.split()[1:]
|
||||||
im = Image.merge("RGB", (r, g, b))
|
im = Image.merge("RGB", (r, g, b))
|
||||||
|
|
||||||
assert_image_equal_tofile(im, "Tests/images/bmp/q/rgb32bf-xbgr.bmp")
|
assert_image_equal_tofile(im, "Tests/images/bmp/q/rgb32bf-xbgr.bmp")
|
||||||
|
|
|
@ -61,6 +61,7 @@ def test_handler(tmp_path: Path) -> None:
|
||||||
|
|
||||||
def load(self, im: ImageFile.StubImageFile) -> Image.Image:
|
def load(self, im: ImageFile.StubImageFile) -> Image.Image:
|
||||||
self.loaded = True
|
self.loaded = True
|
||||||
|
assert im.fp is not None
|
||||||
im.fp.close()
|
im.fp.close()
|
||||||
return Image.new("RGB", (1, 1))
|
return Image.new("RGB", (1, 1))
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ def test_invalid_file() -> None:
|
||||||
no_cursors_file = "Tests/images/no_cursors.cur"
|
no_cursors_file = "Tests/images/no_cursors.cur"
|
||||||
|
|
||||||
cur = CurImagePlugin.CurImageFile(TEST_FILE)
|
cur = CurImagePlugin.CurImageFile(TEST_FILE)
|
||||||
|
assert cur.fp is not None
|
||||||
cur.fp.close()
|
cur.fp.close()
|
||||||
with open(no_cursors_file, "rb") as cur.fp:
|
with open(no_cursors_file, "rb") as cur.fp:
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
|
|
|
@ -56,6 +56,7 @@ TEST_FILE_UNCOMPRESSED_RGB_WITH_ALPHA = "Tests/images/uncompressed_rgb.dds"
|
||||||
)
|
)
|
||||||
def test_sanity_dxt1_bc1(image_path: str) -> None:
|
def test_sanity_dxt1_bc1(image_path: str) -> None:
|
||||||
"""Check DXT1 and BC1 images can be opened"""
|
"""Check DXT1 and BC1 images can be opened"""
|
||||||
|
target: Image.Image
|
||||||
with Image.open(TEST_FILE_DXT1.replace(".dds", ".png")) as target:
|
with Image.open(TEST_FILE_DXT1.replace(".dds", ".png")) as target:
|
||||||
target = target.convert("RGBA")
|
target = target.convert("RGBA")
|
||||||
with Image.open(image_path) as im:
|
with Image.open(image_path) as im:
|
||||||
|
@ -504,9 +505,9 @@ def test_save_dxt5(tmp_path: Path) -> None:
|
||||||
|
|
||||||
def test_save_dx10_bc5(tmp_path: Path) -> None:
|
def test_save_dx10_bc5(tmp_path: Path) -> None:
|
||||||
out = tmp_path / "temp.dds"
|
out = tmp_path / "temp.dds"
|
||||||
with Image.open(TEST_FILE_DX10_BC5_TYPELESS) as im:
|
with Image.open(TEST_FILE_DX10_BC5_TYPELESS) as img:
|
||||||
im.save(out, pixel_format="BC5")
|
img.save(out, pixel_format="BC5")
|
||||||
assert_image_similar_tofile(im, out, 9.56)
|
assert_image_similar_tofile(img, out, 9.56)
|
||||||
|
|
||||||
im = hopper("L")
|
im = hopper("L")
|
||||||
with pytest.raises(OSError, match="only RGB mode can be written as BC5"):
|
with pytest.raises(OSError, match="only RGB mode can be written as BC5"):
|
||||||
|
|
|
@ -256,8 +256,8 @@ def test_bytesio_object() -> None:
|
||||||
with Image.open(img_bytes) as img:
|
with Image.open(img_bytes) as img:
|
||||||
img.load()
|
img.load()
|
||||||
|
|
||||||
with Image.open(FILE1_COMPARE) as image1_scale1_compare:
|
with Image.open(FILE1_COMPARE) as im:
|
||||||
image1_scale1_compare = image1_scale1_compare.convert("RGB")
|
image1_scale1_compare = im.convert("RGB")
|
||||||
image1_scale1_compare.load()
|
image1_scale1_compare.load()
|
||||||
assert_image_similar(img, image1_scale1_compare, 5)
|
assert_image_similar(img, image1_scale1_compare, 5)
|
||||||
|
|
||||||
|
@ -292,16 +292,16 @@ def test_render_scale1() -> None:
|
||||||
# Zero bounding box
|
# Zero bounding box
|
||||||
with Image.open(FILE1) as image1_scale1:
|
with Image.open(FILE1) as image1_scale1:
|
||||||
image1_scale1.load()
|
image1_scale1.load()
|
||||||
with Image.open(FILE1_COMPARE) as image1_scale1_compare:
|
with Image.open(FILE1_COMPARE) as im:
|
||||||
image1_scale1_compare = image1_scale1_compare.convert("RGB")
|
image1_scale1_compare = im.convert("RGB")
|
||||||
image1_scale1_compare.load()
|
image1_scale1_compare.load()
|
||||||
assert_image_similar(image1_scale1, image1_scale1_compare, 5)
|
assert_image_similar(image1_scale1, image1_scale1_compare, 5)
|
||||||
|
|
||||||
# Non-zero bounding box
|
# Non-zero bounding box
|
||||||
with Image.open(FILE2) as image2_scale1:
|
with Image.open(FILE2) as image2_scale1:
|
||||||
image2_scale1.load()
|
image2_scale1.load()
|
||||||
with Image.open(FILE2_COMPARE) as image2_scale1_compare:
|
with Image.open(FILE2_COMPARE) as im:
|
||||||
image2_scale1_compare = image2_scale1_compare.convert("RGB")
|
image2_scale1_compare = im.convert("RGB")
|
||||||
image2_scale1_compare.load()
|
image2_scale1_compare.load()
|
||||||
assert_image_similar(image2_scale1, image2_scale1_compare, 10)
|
assert_image_similar(image2_scale1, image2_scale1_compare, 10)
|
||||||
|
|
||||||
|
@ -315,8 +315,8 @@ def test_render_scale2() -> None:
|
||||||
with Image.open(FILE1) as image1_scale2:
|
with Image.open(FILE1) as image1_scale2:
|
||||||
assert isinstance(image1_scale2, EpsImagePlugin.EpsImageFile)
|
assert isinstance(image1_scale2, EpsImagePlugin.EpsImageFile)
|
||||||
image1_scale2.load(scale=2)
|
image1_scale2.load(scale=2)
|
||||||
with Image.open(FILE1_COMPARE_SCALE2) as image1_scale2_compare:
|
with Image.open(FILE1_COMPARE_SCALE2) as im:
|
||||||
image1_scale2_compare = image1_scale2_compare.convert("RGB")
|
image1_scale2_compare = im.convert("RGB")
|
||||||
image1_scale2_compare.load()
|
image1_scale2_compare.load()
|
||||||
assert_image_similar(image1_scale2, image1_scale2_compare, 5)
|
assert_image_similar(image1_scale2, image1_scale2_compare, 5)
|
||||||
|
|
||||||
|
@ -324,8 +324,8 @@ def test_render_scale2() -> None:
|
||||||
with Image.open(FILE2) as image2_scale2:
|
with Image.open(FILE2) as image2_scale2:
|
||||||
assert isinstance(image2_scale2, EpsImagePlugin.EpsImageFile)
|
assert isinstance(image2_scale2, EpsImagePlugin.EpsImageFile)
|
||||||
image2_scale2.load(scale=2)
|
image2_scale2.load(scale=2)
|
||||||
with Image.open(FILE2_COMPARE_SCALE2) as image2_scale2_compare:
|
with Image.open(FILE2_COMPARE_SCALE2) as im:
|
||||||
image2_scale2_compare = image2_scale2_compare.convert("RGB")
|
image2_scale2_compare = im.convert("RGB")
|
||||||
image2_scale2_compare.load()
|
image2_scale2_compare.load()
|
||||||
assert_image_similar(image2_scale2, image2_scale2_compare, 10)
|
assert_image_similar(image2_scale2, image2_scale2_compare, 10)
|
||||||
|
|
||||||
|
@ -335,9 +335,9 @@ def test_render_scale2() -> None:
|
||||||
"filename", (FILE1, FILE2, "Tests/images/eps/illu10_preview.eps")
|
"filename", (FILE1, FILE2, "Tests/images/eps/illu10_preview.eps")
|
||||||
)
|
)
|
||||||
def test_resize(filename: str) -> None:
|
def test_resize(filename: str) -> None:
|
||||||
with Image.open(filename) as im:
|
with Image.open(filename) as img:
|
||||||
new_size = (100, 100)
|
new_size = (100, 100)
|
||||||
im = im.resize(new_size)
|
im = img.resize(new_size)
|
||||||
assert im.size == new_size
|
assert im.size == new_size
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,8 @@ def test_sanity() -> None:
|
||||||
def test_prefix_chunk(monkeypatch: pytest.MonkeyPatch) -> None:
|
def test_prefix_chunk(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
monkeypatch.setattr(ImageFile, "LOAD_TRUNCATED_IMAGES", True)
|
monkeypatch.setattr(ImageFile, "LOAD_TRUNCATED_IMAGES", True)
|
||||||
with Image.open(animated_test_file_with_prefix_chunk) as im:
|
with Image.open(animated_test_file_with_prefix_chunk) as im:
|
||||||
|
assert isinstance(im, FliImagePlugin.FliImageFile)
|
||||||
|
|
||||||
assert im.mode == "P"
|
assert im.mode == "P"
|
||||||
assert im.size == (320, 200)
|
assert im.size == (320, 200)
|
||||||
assert im.format == "FLI"
|
assert im.format == "FLI"
|
||||||
|
@ -55,6 +57,7 @@ def test_prefix_chunk(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
assert im.is_animated
|
assert im.is_animated
|
||||||
|
|
||||||
palette = im.getpalette()
|
palette = im.getpalette()
|
||||||
|
assert palette is not None
|
||||||
assert palette[3:6] == [255, 255, 255]
|
assert palette[3:6] == [255, 255, 255]
|
||||||
assert palette[381:384] == [204, 204, 12]
|
assert palette[381:384] == [204, 204, 12]
|
||||||
assert palette[765:] == [252, 0, 0]
|
assert palette[765:] == [252, 0, 0]
|
||||||
|
|
|
@ -293,6 +293,7 @@ def test_roundtrip_save_all(tmp_path: Path) -> None:
|
||||||
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 isinstance(reread, GifImagePlugin.GifImageFile)
|
||||||
assert reread.n_frames == 5
|
assert reread.n_frames == 5
|
||||||
|
|
||||||
|
|
||||||
|
@ -318,6 +319,7 @@ def test_roundtrip_save_all_1(tmp_path: Path) -> None:
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
def test_loading_multiple_palettes(path: str, mode: str) -> None:
|
def test_loading_multiple_palettes(path: str, mode: str) -> None:
|
||||||
|
im: Image.Image
|
||||||
with Image.open(path) as im:
|
with Image.open(path) as im:
|
||||||
assert im.mode == "P"
|
assert im.mode == "P"
|
||||||
assert im.palette is not None
|
assert im.palette is not None
|
||||||
|
@ -351,7 +353,7 @@ def test_headers_saving_for_animated_gifs(tmp_path: Path) -> None:
|
||||||
|
|
||||||
def test_palette_handling(tmp_path: Path) -> None:
|
def test_palette_handling(tmp_path: Path) -> None:
|
||||||
# see https://github.com/python-pillow/Pillow/issues/513
|
# see https://github.com/python-pillow/Pillow/issues/513
|
||||||
|
im: Image.Image
|
||||||
with Image.open(TEST_GIF) as im:
|
with Image.open(TEST_GIF) as im:
|
||||||
im = im.convert("RGB")
|
im = im.convert("RGB")
|
||||||
|
|
||||||
|
@ -375,8 +377,8 @@ def test_palette_434(tmp_path: Path) -> None:
|
||||||
|
|
||||||
return reloaded
|
return reloaded
|
||||||
|
|
||||||
orig = "Tests/images/test.colors.gif"
|
im: Image.Image
|
||||||
with Image.open(orig) as im:
|
with Image.open("Tests/images/test.colors.gif") 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:
|
||||||
|
@ -391,6 +393,7 @@ def test_palette_434(tmp_path: Path) -> None:
|
||||||
|
|
||||||
@pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available")
|
@pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available")
|
||||||
def test_save_netpbm_bmp_mode(tmp_path: Path) -> None:
|
def test_save_netpbm_bmp_mode(tmp_path: Path) -> None:
|
||||||
|
img: Image.Image
|
||||||
with Image.open(TEST_GIF) as img:
|
with Image.open(TEST_GIF) as img:
|
||||||
img = img.convert("RGB")
|
img = img.convert("RGB")
|
||||||
|
|
||||||
|
@ -403,6 +406,7 @@ def test_save_netpbm_bmp_mode(tmp_path: Path) -> None:
|
||||||
|
|
||||||
@pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available")
|
@pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available")
|
||||||
def test_save_netpbm_l_mode(tmp_path: Path) -> None:
|
def test_save_netpbm_l_mode(tmp_path: Path) -> None:
|
||||||
|
img: Image.Image
|
||||||
with Image.open(TEST_GIF) as img:
|
with Image.open(TEST_GIF) as img:
|
||||||
img = img.convert("L")
|
img = img.convert("L")
|
||||||
|
|
||||||
|
@ -1032,9 +1036,9 @@ def test_webp_background(tmp_path: Path) -> None:
|
||||||
|
|
||||||
# Test opaque WebP background
|
# Test opaque WebP background
|
||||||
if features.check("webp"):
|
if features.check("webp"):
|
||||||
with Image.open("Tests/images/hopper.webp") as im:
|
with Image.open("Tests/images/hopper.webp") as img:
|
||||||
assert im.info["background"] == (255, 255, 255, 255)
|
assert img.info["background"] == (255, 255, 255, 255)
|
||||||
im.save(out)
|
img.save(out)
|
||||||
|
|
||||||
# Test non-opaque WebP background
|
# Test non-opaque WebP background
|
||||||
im = Image.new("L", (100, 100), "#000")
|
im = Image.new("L", (100, 100), "#000")
|
||||||
|
@ -1043,6 +1047,7 @@ def test_webp_background(tmp_path: Path) -> None:
|
||||||
|
|
||||||
|
|
||||||
def test_comment(tmp_path: Path) -> None:
|
def test_comment(tmp_path: Path) -> None:
|
||||||
|
im: Image.Image
|
||||||
with Image.open(TEST_GIF) as im:
|
with Image.open(TEST_GIF) as im:
|
||||||
assert im.info["comment"] == b"File written by Adobe Photoshop\xa8 4.0"
|
assert im.info["comment"] == b"File written by Adobe Photoshop\xa8 4.0"
|
||||||
|
|
||||||
|
@ -1375,6 +1380,7 @@ def test_palette_save_all_P(tmp_path: Path) -> None:
|
||||||
with Image.open(out) as im:
|
with Image.open(out) as im:
|
||||||
# Assert that the frames are correct, and each frame has the same palette
|
# Assert that the frames are correct, and each frame has the same palette
|
||||||
assert_image_equal(im.convert("RGB"), frames[0].convert("RGB"))
|
assert_image_equal(im.convert("RGB"), frames[0].convert("RGB"))
|
||||||
|
assert isinstance(im, GifImagePlugin.GifImageFile)
|
||||||
assert im.palette is not None
|
assert im.palette is not None
|
||||||
assert im.global_palette is not None
|
assert im.global_palette is not None
|
||||||
assert im.palette.palette == im.global_palette.palette
|
assert im.palette.palette == im.global_palette.palette
|
||||||
|
|
|
@ -59,8 +59,9 @@ def test_handler(tmp_path: Path) -> None:
|
||||||
def open(self, im: Image.Image) -> None:
|
def open(self, im: Image.Image) -> None:
|
||||||
self.opened = True
|
self.opened = True
|
||||||
|
|
||||||
def load(self, im: Image.Image) -> Image.Image:
|
def load(self, im: ImageFile.ImageFile) -> Image.Image:
|
||||||
self.loaded = True
|
self.loaded = True
|
||||||
|
assert im.fp is not None
|
||||||
im.fp.close()
|
im.fp.close()
|
||||||
return Image.new("RGB", (1, 1))
|
return Image.new("RGB", (1, 1))
|
||||||
|
|
||||||
|
|
|
@ -61,8 +61,9 @@ def test_handler(tmp_path: Path) -> None:
|
||||||
def open(self, im: Image.Image) -> None:
|
def open(self, im: Image.Image) -> None:
|
||||||
self.opened = True
|
self.opened = True
|
||||||
|
|
||||||
def load(self, im: Image.Image) -> Image.Image:
|
def load(self, im: ImageFile.ImageFile) -> Image.Image:
|
||||||
self.loaded = True
|
self.loaded = True
|
||||||
|
assert im.fp is not None
|
||||||
im.fp.close()
|
im.fp.close()
|
||||||
return Image.new("RGB", (1, 1))
|
return Image.new("RGB", (1, 1))
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ def test_getiptcinfo_jpg_none() -> None:
|
||||||
# Arrange
|
# Arrange
|
||||||
with hopper() as im:
|
with hopper() as im:
|
||||||
# Act
|
# Act
|
||||||
iptc = IptcImagePlugin.getiptcinfo(im)
|
iptc = IptcImagePlugin.getiptcinfo(im) # type: ignore[arg-type]
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert iptc is None
|
assert iptc is None
|
||||||
|
|
|
@ -331,8 +331,10 @@ class TestFileJpeg:
|
||||||
|
|
||||||
# Reading
|
# Reading
|
||||||
with Image.open("Tests/images/exif_gps.jpg") as im:
|
with Image.open("Tests/images/exif_gps.jpg") as im:
|
||||||
exif = im._getexif()
|
assert isinstance(im, JpegImagePlugin.JpegImageFile)
|
||||||
assert exif[gps_index] == expected_exif_gps
|
exif_data = im._getexif()
|
||||||
|
assert exif_data is not None
|
||||||
|
assert exif_data[gps_index] == expected_exif_gps
|
||||||
|
|
||||||
# Writing
|
# Writing
|
||||||
f = tmp_path / "temp.jpg"
|
f = tmp_path / "temp.jpg"
|
||||||
|
@ -341,8 +343,10 @@ class TestFileJpeg:
|
||||||
hopper().save(f, exif=exif)
|
hopper().save(f, exif=exif)
|
||||||
|
|
||||||
with Image.open(f) as reloaded:
|
with Image.open(f) as reloaded:
|
||||||
exif = reloaded._getexif()
|
assert isinstance(reloaded, JpegImagePlugin.JpegImageFile)
|
||||||
assert exif[gps_index] == expected_exif_gps
|
exif_data = reloaded._getexif()
|
||||||
|
assert exif_data is not None
|
||||||
|
assert exif_data[gps_index] == expected_exif_gps
|
||||||
|
|
||||||
def test_empty_exif_gps(self) -> None:
|
def test_empty_exif_gps(self) -> None:
|
||||||
with Image.open("Tests/images/empty_gps_ifd.jpg") as im:
|
with Image.open("Tests/images/empty_gps_ifd.jpg") as im:
|
||||||
|
@ -369,6 +373,7 @@ class TestFileJpeg:
|
||||||
exifs = []
|
exifs = []
|
||||||
for i in range(2):
|
for i in range(2):
|
||||||
with Image.open("Tests/images/exif-200dpcm.jpg") as im:
|
with Image.open("Tests/images/exif-200dpcm.jpg") as im:
|
||||||
|
assert isinstance(im, JpegImagePlugin.JpegImageFile)
|
||||||
exifs.append(im._getexif())
|
exifs.append(im._getexif())
|
||||||
assert exifs[0] == exifs[1]
|
assert exifs[0] == exifs[1]
|
||||||
|
|
||||||
|
@ -402,13 +407,18 @@ class TestFileJpeg:
|
||||||
}
|
}
|
||||||
|
|
||||||
with Image.open("Tests/images/exif_gps.jpg") as im:
|
with Image.open("Tests/images/exif_gps.jpg") as im:
|
||||||
|
assert isinstance(im, JpegImagePlugin.JpegImageFile)
|
||||||
|
|
||||||
exif = im._getexif()
|
exif = im._getexif()
|
||||||
|
assert exif is not None
|
||||||
|
|
||||||
for tag, value in expected_exif.items():
|
for tag, value in expected_exif.items():
|
||||||
assert value == exif[tag]
|
assert value == exif[tag]
|
||||||
|
|
||||||
def test_exif_gps_typeerror(self) -> None:
|
def test_exif_gps_typeerror(self) -> None:
|
||||||
with Image.open("Tests/images/exif_gps_typeerror.jpg") as im:
|
with Image.open("Tests/images/exif_gps_typeerror.jpg") as im:
|
||||||
|
assert isinstance(im, JpegImagePlugin.JpegImageFile)
|
||||||
|
|
||||||
# Should not raise a TypeError
|
# Should not raise a TypeError
|
||||||
im._getexif()
|
im._getexif()
|
||||||
|
|
||||||
|
@ -488,7 +498,9 @@ class TestFileJpeg:
|
||||||
|
|
||||||
def test_exif(self) -> None:
|
def test_exif(self) -> None:
|
||||||
with Image.open("Tests/images/pil_sample_rgb.jpg") as im:
|
with Image.open("Tests/images/pil_sample_rgb.jpg") as im:
|
||||||
|
assert isinstance(im, JpegImagePlugin.JpegImageFile)
|
||||||
info = im._getexif()
|
info = im._getexif()
|
||||||
|
assert info is not None
|
||||||
assert info[305] == "Adobe Photoshop CS Macintosh"
|
assert info[305] == "Adobe Photoshop CS Macintosh"
|
||||||
|
|
||||||
def test_get_child_images(self) -> None:
|
def test_get_child_images(self) -> None:
|
||||||
|
@ -692,10 +704,12 @@ class TestFileJpeg:
|
||||||
def test_save_multiple_16bit_qtables(self) -> None:
|
def test_save_multiple_16bit_qtables(self) -> None:
|
||||||
with Image.open("Tests/images/hopper_16bit_qtables.jpg") as im:
|
with Image.open("Tests/images/hopper_16bit_qtables.jpg") as im:
|
||||||
im2 = self.roundtrip(im, qtables="keep")
|
im2 = self.roundtrip(im, qtables="keep")
|
||||||
|
assert isinstance(im, JpegImagePlugin.JpegImageFile)
|
||||||
assert im.quantization == im2.quantization
|
assert im.quantization == im2.quantization
|
||||||
|
|
||||||
def test_save_single_16bit_qtable(self) -> None:
|
def test_save_single_16bit_qtable(self) -> None:
|
||||||
with Image.open("Tests/images/hopper_16bit_qtables.jpg") as im:
|
with Image.open("Tests/images/hopper_16bit_qtables.jpg") as im:
|
||||||
|
assert isinstance(im, JpegImagePlugin.JpegImageFile)
|
||||||
im2 = self.roundtrip(im, qtables={0: im.quantization[0]})
|
im2 = self.roundtrip(im, qtables={0: im.quantization[0]})
|
||||||
assert len(im2.quantization) == 1
|
assert len(im2.quantization) == 1
|
||||||
assert im2.quantization[0] == im.quantization[0]
|
assert im2.quantization[0] == im.quantization[0]
|
||||||
|
@ -907,7 +921,10 @@ class TestFileJpeg:
|
||||||
# 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 isinstance(im, JpegImagePlugin.JpegImageFile)
|
||||||
|
exif = im._getexif()
|
||||||
|
assert exif is not None
|
||||||
|
assert exif[306] == "2017:03:13 23:03:09"
|
||||||
|
|
||||||
def test_multiple_exif(self) -> None:
|
def test_multiple_exif(self) -> None:
|
||||||
with Image.open("Tests/images/multiple_exif.jpg") as im:
|
with Image.open("Tests/images/multiple_exif.jpg") as im:
|
||||||
|
@ -1126,8 +1143,9 @@ class TestFileCloseW32:
|
||||||
im.save(tmpfile)
|
im.save(tmpfile)
|
||||||
|
|
||||||
im = Image.open(tmpfile)
|
im = Image.open(tmpfile)
|
||||||
|
assert im.fp is not None
|
||||||
|
assert not im.fp.closed
|
||||||
fp = im.fp
|
fp = im.fp
|
||||||
assert not fp.closed
|
|
||||||
with pytest.raises(OSError):
|
with pytest.raises(OSError):
|
||||||
os.remove(tmpfile)
|
os.remove(tmpfile)
|
||||||
im.load()
|
im.load()
|
||||||
|
|
|
@ -164,7 +164,7 @@ def test_reduce() -> None:
|
||||||
with Image.open("Tests/images/test-card-lossless.jp2") as im:
|
with Image.open("Tests/images/test-card-lossless.jp2") as im:
|
||||||
assert callable(im.reduce)
|
assert callable(im.reduce)
|
||||||
|
|
||||||
im.reduce = 2
|
im.reduce = 2 # type: ignore[method-assign, assignment]
|
||||||
assert im.reduce == 2
|
assert im.reduce == 2
|
||||||
|
|
||||||
im.load()
|
im.load()
|
||||||
|
|
|
@ -11,7 +11,15 @@ from typing import Any, NamedTuple
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, ImageFilter, ImageOps, TiffImagePlugin, TiffTags, features
|
from PIL import (
|
||||||
|
Image,
|
||||||
|
ImageFile,
|
||||||
|
ImageFilter,
|
||||||
|
ImageOps,
|
||||||
|
TiffImagePlugin,
|
||||||
|
TiffTags,
|
||||||
|
features,
|
||||||
|
)
|
||||||
from PIL.TiffImagePlugin import OSUBFILETYPE, SAMPLEFORMAT, STRIPOFFSETS, SUBIFD
|
from PIL.TiffImagePlugin import OSUBFILETYPE, SAMPLEFORMAT, STRIPOFFSETS, SUBIFD
|
||||||
|
|
||||||
from .helper import (
|
from .helper import (
|
||||||
|
@ -27,7 +35,7 @@ from .helper import (
|
||||||
|
|
||||||
@skip_unless_feature("libtiff")
|
@skip_unless_feature("libtiff")
|
||||||
class LibTiffTestCase:
|
class LibTiffTestCase:
|
||||||
def _assert_noerr(self, tmp_path: Path, im: TiffImagePlugin.TiffImageFile) -> None:
|
def _assert_noerr(self, tmp_path: Path, im: ImageFile.ImageFile) -> None:
|
||||||
"""Helper tests that assert basic sanity about the g4 tiff reading"""
|
"""Helper tests that assert basic sanity about the g4 tiff reading"""
|
||||||
# 1 bit
|
# 1 bit
|
||||||
assert im.mode == "1"
|
assert im.mode == "1"
|
||||||
|
@ -429,7 +437,6 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
assert isinstance(orig, TiffImagePlugin.TiffImageFile)
|
assert isinstance(orig, TiffImagePlugin.TiffImageFile)
|
||||||
|
|
||||||
out = tmp_path / "temp.tif"
|
out = tmp_path / "temp.tif"
|
||||||
|
|
||||||
orig.tag[269] = "temp.tif"
|
orig.tag[269] = "temp.tif"
|
||||||
orig.save(out)
|
orig.save(out)
|
||||||
|
|
||||||
|
@ -457,8 +464,8 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
# test case from irc, how to do blur on b/w image
|
# test case from irc, how to do blur on b/w image
|
||||||
# and save to compressed tif.
|
# and save to compressed tif.
|
||||||
out = tmp_path / "temp.tif"
|
out = tmp_path / "temp.tif"
|
||||||
with Image.open("Tests/images/pport_g4.tif") as im:
|
with Image.open("Tests/images/pport_g4.tif") as img:
|
||||||
im = im.convert("L")
|
im = img.convert("L")
|
||||||
|
|
||||||
im = im.filter(ImageFilter.GaussianBlur(4))
|
im = im.filter(ImageFilter.GaussianBlur(4))
|
||||||
im.save(out, compression="tiff_adobe_deflate")
|
im.save(out, compression="tiff_adobe_deflate")
|
||||||
|
@ -552,8 +559,9 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
im.save(out, compression=compression)
|
im.save(out, compression=compression)
|
||||||
|
|
||||||
def test_fp_leak(self) -> None:
|
def test_fp_leak(self) -> None:
|
||||||
im: Image.Image | None = Image.open("Tests/images/hopper_g4_500.tif")
|
im: ImageFile.ImageFile | None = Image.open("Tests/images/hopper_g4_500.tif")
|
||||||
assert im is not None
|
assert im is not None
|
||||||
|
assert im.fp is not None
|
||||||
fn = im.fp.fileno()
|
fn = im.fp.fileno()
|
||||||
|
|
||||||
os.fstat(fn)
|
os.fstat(fn)
|
||||||
|
@ -1028,8 +1036,8 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
# Set EXIF Orientation to 2
|
# Set EXIF Orientation to 2
|
||||||
data = data[:102] + b"\x02" + data[103:]
|
data = data[:102] + b"\x02" + data[103:]
|
||||||
|
|
||||||
with Image.open(io.BytesIO(data)) as im:
|
with Image.open(io.BytesIO(data)) as img:
|
||||||
im = im.transpose(Image.Transpose.FLIP_LEFT_RIGHT)
|
im = img.transpose(Image.Transpose.FLIP_LEFT_RIGHT)
|
||||||
assert_image_equal_tofile(im, "Tests/images/old-style-jpeg-compression.png")
|
assert_image_equal_tofile(im, "Tests/images/old-style-jpeg-compression.png")
|
||||||
|
|
||||||
def test_open_missing_samplesperpixel(self) -> None:
|
def test_open_missing_samplesperpixel(self) -> None:
|
||||||
|
@ -1097,9 +1105,8 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
with Image.open("Tests/images/g4_orientation_1.tif") as base_im:
|
with Image.open("Tests/images/g4_orientation_1.tif") as base_im:
|
||||||
for i in range(2, 9):
|
for i in range(2, 9):
|
||||||
with Image.open("Tests/images/g4_orientation_" + str(i) + ".tif") as im:
|
with Image.open("Tests/images/g4_orientation_" + str(i) + ".tif") as im:
|
||||||
im = ImageOps.exif_transpose(im)
|
transposed_im = ImageOps.exif_transpose(im)
|
||||||
|
assert_image_similar(base_im, transposed_im, 0.7)
|
||||||
assert_image_similar(base_im, im, 0.7)
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"test_file",
|
"test_file",
|
||||||
|
|
|
@ -22,10 +22,10 @@ def test_sanity() -> None:
|
||||||
|
|
||||||
# Adjust for the gamma of 2.2 encoded into the file
|
# Adjust for the gamma of 2.2 encoded into the file
|
||||||
lut = ImagePalette.make_gamma_lut(1 / 2.2)
|
lut = ImagePalette.make_gamma_lut(1 / 2.2)
|
||||||
im = Image.merge("RGBA", [chan.point(lut) for chan in im.split()])
|
im1 = Image.merge("RGBA", [chan.point(lut) for chan in im.split()])
|
||||||
|
|
||||||
im2 = hopper("RGBA")
|
im2 = hopper("RGBA")
|
||||||
assert_image_similar(im, im2, 10)
|
assert_image_similar(im1, im2, 10)
|
||||||
|
|
||||||
|
|
||||||
def test_n_frames() -> None:
|
def test_n_frames() -> None:
|
||||||
|
|
|
@ -120,9 +120,11 @@ def test_ignore_frame_size() -> None:
|
||||||
# Ignore the different size of the second frame
|
# Ignore the different size of the second frame
|
||||||
# since this is not a "Large Thumbnail" image
|
# since this is not a "Large Thumbnail" image
|
||||||
with Image.open("Tests/images/ignore_frame_size.mpo") as im:
|
with Image.open("Tests/images/ignore_frame_size.mpo") as im:
|
||||||
|
assert isinstance(im, MpoImagePlugin.MpoImageFile)
|
||||||
assert im.size == (64, 64)
|
assert im.size == (64, 64)
|
||||||
|
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
|
assert im.mpinfo is not None
|
||||||
assert (
|
assert (
|
||||||
im.mpinfo[0xB002][1]["Attribute"]["MPType"]
|
im.mpinfo[0xB002][1]["Attribute"]["MPType"]
|
||||||
== "Multi-Frame Image: (Disparity)"
|
== "Multi-Frame Image: (Disparity)"
|
||||||
|
@ -155,6 +157,7 @@ def test_reload_exif_after_seek() -> None:
|
||||||
@pytest.mark.parametrize("test_file", test_files)
|
@pytest.mark.parametrize("test_file", test_files)
|
||||||
def test_mp(test_file: str) -> None:
|
def test_mp(test_file: str) -> None:
|
||||||
with Image.open(test_file) as im:
|
with Image.open(test_file) as im:
|
||||||
|
assert isinstance(im, MpoImagePlugin.MpoImageFile)
|
||||||
mpinfo = im._getmp()
|
mpinfo = im._getmp()
|
||||||
assert mpinfo is not None
|
assert mpinfo is not None
|
||||||
assert mpinfo[45056] == b"0100"
|
assert mpinfo[45056] == b"0100"
|
||||||
|
@ -165,6 +168,7 @@ def test_mp_offset() -> None:
|
||||||
# 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 APP2 data, in contrast to normal 8
|
# in APP2 data, in contrast to normal 8
|
||||||
with Image.open("Tests/images/sugarshack_ifd_offset.mpo") as im:
|
with Image.open("Tests/images/sugarshack_ifd_offset.mpo") as im:
|
||||||
|
assert isinstance(im, MpoImagePlugin.MpoImageFile)
|
||||||
mpinfo = im._getmp()
|
mpinfo = im._getmp()
|
||||||
assert mpinfo is not None
|
assert mpinfo is not None
|
||||||
assert mpinfo[45056] == b"0100"
|
assert mpinfo[45056] == b"0100"
|
||||||
|
@ -182,6 +186,7 @@ def test_mp_no_data() -> None:
|
||||||
@pytest.mark.parametrize("test_file", test_files)
|
@pytest.mark.parametrize("test_file", test_files)
|
||||||
def test_mp_attribute(test_file: str) -> None:
|
def test_mp_attribute(test_file: str) -> None:
|
||||||
with Image.open(test_file) as im:
|
with Image.open(test_file) as im:
|
||||||
|
assert isinstance(im, MpoImagePlugin.MpoImageFile)
|
||||||
mpinfo = im._getmp()
|
mpinfo = im._getmp()
|
||||||
assert mpinfo is not None
|
assert mpinfo is not None
|
||||||
for frame_number, mpentry in enumerate(mpinfo[0xB002]):
|
for frame_number, mpentry in enumerate(mpinfo[0xB002]):
|
||||||
|
@ -284,6 +289,7 @@ def test_save(test_file: str) -> None:
|
||||||
|
|
||||||
|
|
||||||
def test_save_all() -> None:
|
def test_save_all() -> None:
|
||||||
|
im: Image.Image
|
||||||
for test_file in test_files:
|
for test_file in test_files:
|
||||||
with Image.open(test_file) as im:
|
with Image.open(test_file) as im:
|
||||||
im_reloaded = roundtrip(im, save_all=True)
|
im_reloaded = roundtrip(im, save_all=True)
|
||||||
|
|
|
@ -93,6 +93,7 @@ class TestFilePng:
|
||||||
|
|
||||||
hopper("RGB").save(test_file)
|
hopper("RGB").save(test_file)
|
||||||
|
|
||||||
|
im: Image.Image
|
||||||
with Image.open(test_file) as im:
|
with Image.open(test_file) as im:
|
||||||
im.load()
|
im.load()
|
||||||
assert im.mode == "RGB"
|
assert im.mode == "RGB"
|
||||||
|
@ -103,6 +104,8 @@ class TestFilePng:
|
||||||
for mode in ["1", "L", "P", "RGB", "I;16", "I;16B"]:
|
for mode in ["1", "L", "P", "RGB", "I;16", "I;16B"]:
|
||||||
im = hopper(mode)
|
im = hopper(mode)
|
||||||
im.save(test_file)
|
im.save(test_file)
|
||||||
|
|
||||||
|
reloaded: Image.Image
|
||||||
with Image.open(test_file) as reloaded:
|
with Image.open(test_file) as reloaded:
|
||||||
if mode == "I;16B":
|
if mode == "I;16B":
|
||||||
reloaded = reloaded.convert(mode)
|
reloaded = reloaded.convert(mode)
|
||||||
|
@ -225,11 +228,13 @@ class TestFilePng:
|
||||||
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))
|
||||||
im = im.convert("RGBA")
|
rgba_im = im.convert("RGBA")
|
||||||
assert_image(im, "RGBA", (162, 150))
|
assert_image(rgba_im, "RGBA", (162, 150))
|
||||||
|
|
||||||
# image has 124 unique alpha values
|
# image has 124 unique alpha values
|
||||||
assert len(im.getchannel("A").getcolors()) == 124
|
colors = rgba_im.getchannel("A").getcolors()
|
||||||
|
assert colors is not None
|
||||||
|
assert len(colors) == 124
|
||||||
|
|
||||||
def test_load_transparent_rgb(self) -> None:
|
def test_load_transparent_rgb(self) -> None:
|
||||||
test_file = "Tests/images/rgb_trns.png"
|
test_file = "Tests/images/rgb_trns.png"
|
||||||
|
@ -237,11 +242,13 @@ class TestFilePng:
|
||||||
assert im.info["transparency"] == (0, 255, 52)
|
assert im.info["transparency"] == (0, 255, 52)
|
||||||
|
|
||||||
assert_image(im, "RGB", (64, 64))
|
assert_image(im, "RGB", (64, 64))
|
||||||
im = im.convert("RGBA")
|
rgba_im = im.convert("RGBA")
|
||||||
assert_image(im, "RGBA", (64, 64))
|
assert_image(rgba_im, "RGBA", (64, 64))
|
||||||
|
|
||||||
# image has 876 transparent pixels
|
# image has 876 transparent pixels
|
||||||
assert im.getchannel("A").getcolors()[0][0] == 876
|
colors = rgba_im.getchannel("A").getcolors()
|
||||||
|
assert colors is not None
|
||||||
|
assert colors[0][0] == 876
|
||||||
|
|
||||||
def test_save_p_transparent_palette(self, tmp_path: Path) -> None:
|
def test_save_p_transparent_palette(self, tmp_path: Path) -> None:
|
||||||
in_file = "Tests/images/pil123p.png"
|
in_file = "Tests/images/pil123p.png"
|
||||||
|
@ -258,11 +265,13 @@ class TestFilePng:
|
||||||
assert len(im.info["transparency"]) == 256
|
assert len(im.info["transparency"]) == 256
|
||||||
|
|
||||||
assert_image(im, "P", (162, 150))
|
assert_image(im, "P", (162, 150))
|
||||||
im = im.convert("RGBA")
|
rgba_im = im.convert("RGBA")
|
||||||
assert_image(im, "RGBA", (162, 150))
|
assert_image(rgba_im, "RGBA", (162, 150))
|
||||||
|
|
||||||
# image has 124 unique alpha values
|
# image has 124 unique alpha values
|
||||||
assert len(im.getchannel("A").getcolors()) == 124
|
colors = rgba_im.getchannel("A").getcolors()
|
||||||
|
assert colors is not None
|
||||||
|
assert len(colors) == 124
|
||||||
|
|
||||||
def test_save_p_single_transparency(self, tmp_path: Path) -> None:
|
def test_save_p_single_transparency(self, tmp_path: Path) -> None:
|
||||||
in_file = "Tests/images/p_trns_single.png"
|
in_file = "Tests/images/p_trns_single.png"
|
||||||
|
@ -279,13 +288,15 @@ class TestFilePng:
|
||||||
assert im.info["transparency"] == 164
|
assert im.info["transparency"] == 164
|
||||||
assert im.getpixel((31, 31)) == 164
|
assert im.getpixel((31, 31)) == 164
|
||||||
assert_image(im, "P", (64, 64))
|
assert_image(im, "P", (64, 64))
|
||||||
im = im.convert("RGBA")
|
rgba_im = im.convert("RGBA")
|
||||||
assert_image(im, "RGBA", (64, 64))
|
assert_image(rgba_im, "RGBA", (64, 64))
|
||||||
|
|
||||||
assert im.getpixel((31, 31)) == (0, 255, 52, 0)
|
assert rgba_im.getpixel((31, 31)) == (0, 255, 52, 0)
|
||||||
|
|
||||||
# image has 876 transparent pixels
|
# image has 876 transparent pixels
|
||||||
assert im.getchannel("A").getcolors()[0][0] == 876
|
colors = rgba_im.getchannel("A").getcolors()
|
||||||
|
assert colors is not None
|
||||||
|
assert colors[0][0] == 876
|
||||||
|
|
||||||
def test_save_p_transparent_black(self, tmp_path: Path) -> None:
|
def test_save_p_transparent_black(self, tmp_path: Path) -> None:
|
||||||
# check if solid black image with full transparency
|
# check if solid black image with full transparency
|
||||||
|
@ -313,7 +324,9 @@ class TestFilePng:
|
||||||
assert im.info["transparency"] == 255
|
assert im.info["transparency"] == 255
|
||||||
|
|
||||||
im_rgba = im.convert("RGBA")
|
im_rgba = im.convert("RGBA")
|
||||||
assert im_rgba.getchannel("A").getcolors()[0][0] == num_transparent
|
colors = im_rgba.getchannel("A").getcolors()
|
||||||
|
assert colors is not None
|
||||||
|
assert colors[0][0] == num_transparent
|
||||||
|
|
||||||
test_file = tmp_path / "temp.png"
|
test_file = tmp_path / "temp.png"
|
||||||
im.save(test_file)
|
im.save(test_file)
|
||||||
|
@ -324,7 +337,9 @@ class TestFilePng:
|
||||||
assert_image_equal(im, test_im)
|
assert_image_equal(im, test_im)
|
||||||
|
|
||||||
test_im_rgba = test_im.convert("RGBA")
|
test_im_rgba = test_im.convert("RGBA")
|
||||||
assert test_im_rgba.getchannel("A").getcolors()[0][0] == num_transparent
|
colors = test_im_rgba.getchannel("A").getcolors()
|
||||||
|
assert colors is not None
|
||||||
|
assert colors[0][0] == num_transparent
|
||||||
|
|
||||||
def test_save_rgb_single_transparency(self, tmp_path: Path) -> None:
|
def test_save_rgb_single_transparency(self, tmp_path: Path) -> None:
|
||||||
in_file = "Tests/images/caption_6_33_22.png"
|
in_file = "Tests/images/caption_6_33_22.png"
|
||||||
|
@ -671,6 +686,9 @@ class TestFilePng:
|
||||||
im.save(out, bits=4, save_all=save_all)
|
im.save(out, bits=4, save_all=save_all)
|
||||||
|
|
||||||
with Image.open(out) as reloaded:
|
with Image.open(out) as reloaded:
|
||||||
|
assert isinstance(reloaded, PngImagePlugin.PngImageFile)
|
||||||
|
assert reloaded.png is not None
|
||||||
|
assert reloaded.png.im_palette is not None
|
||||||
assert len(reloaded.png.im_palette[1]) == 48
|
assert len(reloaded.png.im_palette[1]) == 48
|
||||||
|
|
||||||
def test_plte_length(self, tmp_path: Path) -> None:
|
def test_plte_length(self, tmp_path: Path) -> None:
|
||||||
|
@ -681,6 +699,9 @@ class TestFilePng:
|
||||||
im.save(out)
|
im.save(out)
|
||||||
|
|
||||||
with Image.open(out) as reloaded:
|
with Image.open(out) as reloaded:
|
||||||
|
assert isinstance(reloaded, PngImagePlugin.PngImageFile)
|
||||||
|
assert reloaded.png is not None
|
||||||
|
assert reloaded.png.im_palette is not None
|
||||||
assert len(reloaded.png.im_palette[1]) == 3
|
assert len(reloaded.png.im_palette[1]) == 3
|
||||||
|
|
||||||
def test_getxmp(self) -> None:
|
def test_getxmp(self) -> None:
|
||||||
|
@ -702,13 +723,17 @@ class TestFilePng:
|
||||||
def test_exif(self) -> None:
|
def test_exif(self) -> None:
|
||||||
# With an EXIF chunk
|
# With an EXIF chunk
|
||||||
with Image.open("Tests/images/exif.png") as im:
|
with Image.open("Tests/images/exif.png") as im:
|
||||||
exif = im._getexif()
|
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||||
assert exif[274] == 1
|
exif_data = im._getexif()
|
||||||
|
assert exif_data is not None
|
||||||
|
assert exif_data[274] == 1
|
||||||
|
|
||||||
# With an ImageMagick zTXt chunk
|
# With an ImageMagick zTXt chunk
|
||||||
with Image.open("Tests/images/exif_imagemagick.png") as im:
|
with Image.open("Tests/images/exif_imagemagick.png") as im:
|
||||||
exif = im._getexif()
|
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||||
assert exif[274] == 1
|
exif_data = im._getexif()
|
||||||
|
assert exif_data is not None
|
||||||
|
assert exif_data[274] == 1
|
||||||
|
|
||||||
# Assert that info still can be extracted
|
# Assert that info still can be extracted
|
||||||
# when the image is no longer a PngImageFile instance
|
# when the image is no longer a PngImageFile instance
|
||||||
|
@ -717,8 +742,10 @@ class TestFilePng:
|
||||||
|
|
||||||
# With a tEXt chunk
|
# With a tEXt chunk
|
||||||
with Image.open("Tests/images/exif_text.png") as im:
|
with Image.open("Tests/images/exif_text.png") as im:
|
||||||
exif = im._getexif()
|
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||||
assert exif[274] == 1
|
exif_data = im._getexif()
|
||||||
|
assert exif_data is not None
|
||||||
|
assert exif_data[274] == 1
|
||||||
|
|
||||||
# With XMP tags
|
# With XMP tags
|
||||||
with Image.open("Tests/images/xmp_tags_orientation.png") as im:
|
with Image.open("Tests/images/xmp_tags_orientation.png") as im:
|
||||||
|
@ -740,7 +767,9 @@ class TestFilePng:
|
||||||
im.save(test_file, exif=im.getexif())
|
im.save(test_file, exif=im.getexif())
|
||||||
|
|
||||||
with Image.open(test_file) as reloaded:
|
with Image.open(test_file) as reloaded:
|
||||||
|
assert isinstance(reloaded, PngImagePlugin.PngImageFile)
|
||||||
exif = reloaded._getexif()
|
exif = reloaded._getexif()
|
||||||
|
assert exif is not None
|
||||||
assert exif[274] == 1
|
assert exif[274] == 1
|
||||||
|
|
||||||
@mark_if_feature_version(
|
@mark_if_feature_version(
|
||||||
|
@ -752,7 +781,9 @@ class TestFilePng:
|
||||||
im.save(test_file, exif=im.getexif())
|
im.save(test_file, exif=im.getexif())
|
||||||
|
|
||||||
with Image.open(test_file) as reloaded:
|
with Image.open(test_file) as reloaded:
|
||||||
|
assert isinstance(reloaded, PngImagePlugin.PngImageFile)
|
||||||
exif = reloaded._getexif()
|
exif = reloaded._getexif()
|
||||||
|
assert exif is not None
|
||||||
assert exif[305] == "Adobe Photoshop CS Macintosh"
|
assert exif[305] == "Adobe Photoshop CS Macintosh"
|
||||||
|
|
||||||
def test_exif_argument(self, tmp_path: Path) -> None:
|
def test_exif_argument(self, tmp_path: Path) -> None:
|
||||||
|
@ -776,7 +807,6 @@ class TestFilePng:
|
||||||
|
|
||||||
@pytest.mark.parametrize("buffer", (True, False))
|
@pytest.mark.parametrize("buffer", (True, False))
|
||||||
def test_save_stdout(self, buffer: bool, monkeypatch: pytest.MonkeyPatch) -> None:
|
def test_save_stdout(self, buffer: bool, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
|
|
||||||
class MyStdOut:
|
class MyStdOut:
|
||||||
buffer = BytesIO()
|
buffer = BytesIO()
|
||||||
|
|
||||||
|
@ -785,7 +815,7 @@ class TestFilePng:
|
||||||
monkeypatch.setattr(sys, "stdout", mystdout)
|
monkeypatch.setattr(sys, "stdout", mystdout)
|
||||||
|
|
||||||
with Image.open(TEST_PNG_FILE) as im:
|
with Image.open(TEST_PNG_FILE) as im:
|
||||||
im.save(sys.stdout, "PNG")
|
im.save(sys.stdout, "PNG") # type: ignore[arg-type]
|
||||||
|
|
||||||
if isinstance(mystdout, MyStdOut):
|
if isinstance(mystdout, MyStdOut):
|
||||||
mystdout = mystdout.buffer
|
mystdout = mystdout.buffer
|
||||||
|
|
|
@ -367,7 +367,6 @@ def test_mimetypes(tmp_path: Path) -> None:
|
||||||
|
|
||||||
@pytest.mark.parametrize("buffer", (True, False))
|
@pytest.mark.parametrize("buffer", (True, False))
|
||||||
def test_save_stdout(buffer: bool, monkeypatch: pytest.MonkeyPatch) -> None:
|
def test_save_stdout(buffer: bool, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
|
|
||||||
class MyStdOut:
|
class MyStdOut:
|
||||||
buffer = BytesIO()
|
buffer = BytesIO()
|
||||||
|
|
||||||
|
@ -376,7 +375,7 @@ def test_save_stdout(buffer: bool, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
monkeypatch.setattr(sys, "stdout", mystdout)
|
monkeypatch.setattr(sys, "stdout", mystdout)
|
||||||
|
|
||||||
with Image.open(TEST_FILE) as im:
|
with Image.open(TEST_FILE) as im:
|
||||||
im.save(sys.stdout, "PPM")
|
im.save(sys.stdout, "PPM") # type: ignore[arg-type]
|
||||||
|
|
||||||
if isinstance(mystdout, MyStdOut):
|
if isinstance(mystdout, MyStdOut):
|
||||||
mystdout = mystdout.buffer
|
mystdout = mystdout.buffer
|
||||||
|
|
|
@ -84,8 +84,8 @@ def test_rgbx() -> None:
|
||||||
|
|
||||||
with Image.open(io.BytesIO(data)) as im:
|
with Image.open(io.BytesIO(data)) as im:
|
||||||
r, g, b = im.split()
|
r, g, b = im.split()
|
||||||
im = Image.merge("RGB", (b, g, r))
|
im_bgr = Image.merge("RGB", (b, g, r))
|
||||||
assert_image_equal_tofile(im, os.path.join(EXTRA_DIR, "32bpp.png"))
|
assert_image_equal_tofile(im_bgr, os.path.join(EXTRA_DIR, "32bpp.png"))
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(
|
@pytest.mark.skipif(
|
||||||
|
|
|
@ -274,13 +274,17 @@ def test_save_l_transparency(tmp_path: Path) -> None:
|
||||||
in_file = "Tests/images/la.tga"
|
in_file = "Tests/images/la.tga"
|
||||||
with Image.open(in_file) as im:
|
with Image.open(in_file) as im:
|
||||||
assert im.mode == "LA"
|
assert im.mode == "LA"
|
||||||
assert im.getchannel("A").getcolors()[0][0] == num_transparent
|
colors = im.getchannel("A").getcolors()
|
||||||
|
assert colors is not None
|
||||||
|
assert colors[0][0] == num_transparent
|
||||||
|
|
||||||
out = tmp_path / "temp.tga"
|
out = tmp_path / "temp.tga"
|
||||||
im.save(out)
|
im.save(out)
|
||||||
|
|
||||||
with Image.open(out) as test_im:
|
with Image.open(out) as test_im:
|
||||||
assert test_im.mode == "LA"
|
assert test_im.mode == "LA"
|
||||||
assert test_im.getchannel("A").getcolors()[0][0] == num_transparent
|
colors = test_im.getchannel("A").getcolors()
|
||||||
|
assert colors is not None
|
||||||
|
assert colors[0][0] == num_transparent
|
||||||
|
|
||||||
assert_image_equal(im, test_im)
|
assert_image_equal(im, test_im)
|
||||||
|
|
|
@ -382,6 +382,7 @@ class TestFileTiff:
|
||||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
|
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||||
ret = str(im.ifd)
|
ret = str(im.ifd)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
|
@ -754,13 +755,13 @@ class TestFileTiff:
|
||||||
|
|
||||||
def test_tiff_save_all(self) -> None:
|
def test_tiff_save_all(self) -> None:
|
||||||
mp = BytesIO()
|
mp = BytesIO()
|
||||||
with Image.open("Tests/images/multipage.tiff") as im:
|
with Image.open("Tests/images/multipage.tiff") as img:
|
||||||
im.save(mp, format="tiff", save_all=True)
|
img.save(mp, format="tiff", save_all=True)
|
||||||
|
|
||||||
mp.seek(0, os.SEEK_SET)
|
mp.seek(0, os.SEEK_SET)
|
||||||
with Image.open(mp) as im:
|
with Image.open(mp) as img:
|
||||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
assert isinstance(img, TiffImagePlugin.TiffImageFile)
|
||||||
assert im.n_frames == 3
|
assert img.n_frames == 3
|
||||||
|
|
||||||
# Test appending images
|
# Test appending images
|
||||||
mp = BytesIO()
|
mp = BytesIO()
|
||||||
|
@ -971,6 +972,7 @@ class TestFileTiff:
|
||||||
|
|
||||||
im = Image.open(tmpfile)
|
im = Image.open(tmpfile)
|
||||||
fp = im.fp
|
fp = im.fp
|
||||||
|
assert fp is not None
|
||||||
assert not fp.closed
|
assert not fp.closed
|
||||||
im.load()
|
im.load()
|
||||||
assert fp.closed
|
assert fp.closed
|
||||||
|
@ -984,6 +986,7 @@ class TestFileTiff:
|
||||||
with open(tmpfile, "rb") as f:
|
with open(tmpfile, "rb") as f:
|
||||||
im = Image.open(f)
|
im = Image.open(f)
|
||||||
fp = im.fp
|
fp = im.fp
|
||||||
|
assert fp is not None
|
||||||
assert not fp.closed
|
assert not fp.closed
|
||||||
im.load()
|
im.load()
|
||||||
assert not fp.closed
|
assert not fp.closed
|
||||||
|
@ -1034,8 +1037,9 @@ class TestFileTiffW32:
|
||||||
im.save(tmpfile)
|
im.save(tmpfile)
|
||||||
|
|
||||||
im = Image.open(tmpfile)
|
im = Image.open(tmpfile)
|
||||||
|
assert im.fp is not None
|
||||||
|
assert not im.fp.closed
|
||||||
fp = im.fp
|
fp = im.fp
|
||||||
assert not fp.closed
|
|
||||||
with pytest.raises(OSError):
|
with pytest.raises(OSError):
|
||||||
os.remove(tmpfile)
|
os.remove(tmpfile)
|
||||||
im.load()
|
im.load()
|
||||||
|
|
|
@ -175,13 +175,13 @@ def test_change_stripbytecounts_tag_type(tmp_path: Path) -> None:
|
||||||
del info[278]
|
del info[278]
|
||||||
|
|
||||||
# Resize the image so that STRIPBYTECOUNTS will be larger than a SHORT
|
# Resize the image so that STRIPBYTECOUNTS will be larger than a SHORT
|
||||||
im = im.resize((500, 500))
|
resized_im = im.resize((500, 500))
|
||||||
info[TiffImagePlugin.IMAGEWIDTH] = im.width
|
info[TiffImagePlugin.IMAGEWIDTH] = resized_im.width
|
||||||
|
|
||||||
# STRIPBYTECOUNTS can be a SHORT or a LONG
|
# STRIPBYTECOUNTS can be a SHORT or a LONG
|
||||||
info.tagtype[TiffImagePlugin.STRIPBYTECOUNTS] = TiffTags.SHORT
|
info.tagtype[TiffImagePlugin.STRIPBYTECOUNTS] = TiffTags.SHORT
|
||||||
|
|
||||||
im.save(out, tiffinfo=info)
|
resized_im.save(out, tiffinfo=info)
|
||||||
|
|
||||||
with Image.open(out) as reloaded:
|
with Image.open(out) as reloaded:
|
||||||
assert isinstance(reloaded, TiffImagePlugin.TiffImageFile)
|
assert isinstance(reloaded, TiffImagePlugin.TiffImageFile)
|
||||||
|
|
|
@ -22,11 +22,13 @@ except ImportError:
|
||||||
def test_read_exif_metadata() -> None:
|
def test_read_exif_metadata() -> None:
|
||||||
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 isinstance(image, WebPImagePlugin.WebPImageFile)
|
||||||
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
|
||||||
|
|
||||||
exif = image._getexif()
|
exif = image._getexif()
|
||||||
|
assert exif is not None
|
||||||
|
|
||||||
# Camera make
|
# Camera make
|
||||||
assert exif[271] == "Canon"
|
assert exif[271] == "Canon"
|
||||||
|
|
|
@ -607,8 +607,8 @@ class TestImage:
|
||||||
assert im.mode == mode
|
assert im.mode == mode
|
||||||
assert im.getpixel((0, 0)) == 0
|
assert im.getpixel((0, 0)) == 0
|
||||||
assert im.getpixel((255, 255)) == 255
|
assert im.getpixel((255, 255)) == 255
|
||||||
with Image.open(target_file) as target:
|
with Image.open(target_file) as img:
|
||||||
target = target.convert(mode)
|
target = img.convert(mode)
|
||||||
assert_image_equal(im, target)
|
assert_image_equal(im, target)
|
||||||
|
|
||||||
def test_radial_gradient_wrong_mode(self) -> None:
|
def test_radial_gradient_wrong_mode(self) -> None:
|
||||||
|
@ -632,8 +632,8 @@ class TestImage:
|
||||||
assert im.mode == mode
|
assert im.mode == mode
|
||||||
assert im.getpixel((0, 0)) == 255
|
assert im.getpixel((0, 0)) == 255
|
||||||
assert im.getpixel((128, 128)) == 0
|
assert im.getpixel((128, 128)) == 0
|
||||||
with Image.open(target_file) as target:
|
with Image.open(target_file) as img:
|
||||||
target = target.convert(mode)
|
target = img.convert(mode)
|
||||||
assert_image_equal(im, target)
|
assert_image_equal(im, target)
|
||||||
|
|
||||||
def test_register_extensions(self) -> None:
|
def test_register_extensions(self) -> None:
|
||||||
|
@ -654,8 +654,8 @@ class TestImage:
|
||||||
|
|
||||||
def test_remap_palette(self) -> None:
|
def test_remap_palette(self) -> None:
|
||||||
# Test identity transform
|
# Test identity transform
|
||||||
with Image.open("Tests/images/hopper.gif") as im:
|
with Image.open("Tests/images/hopper.gif") as img:
|
||||||
assert_image_equal(im, im.remap_palette(list(range(256))))
|
assert_image_equal(img, img.remap_palette(list(range(256))))
|
||||||
|
|
||||||
# Test identity transform with an RGBA palette
|
# Test identity transform with an RGBA palette
|
||||||
im = Image.new("P", (256, 1))
|
im = Image.new("P", (256, 1))
|
||||||
|
|
|
@ -76,8 +76,8 @@ def test_8bit() -> None:
|
||||||
|
|
||||||
|
|
||||||
def test_16bit() -> None:
|
def test_16bit() -> None:
|
||||||
with Image.open("Tests/images/16bit.cropped.tif") as im:
|
with Image.open("Tests/images/16bit.cropped.tif") as img:
|
||||||
_test_float_conversion(im)
|
_test_float_conversion(img)
|
||||||
|
|
||||||
for color in (65535, 65536):
|
for color in (65535, 65536):
|
||||||
im = Image.new("I", (1, 1), color)
|
im = Image.new("I", (1, 1), color)
|
||||||
|
|
|
@ -78,13 +78,13 @@ def test_crop_crash() -> None:
|
||||||
extents = (1, 1, 10, 10)
|
extents = (1, 1, 10, 10)
|
||||||
# works prepatch
|
# works prepatch
|
||||||
with Image.open(test_img) as img:
|
with Image.open(test_img) as img:
|
||||||
img2 = img.crop(extents)
|
img1 = img.crop(extents)
|
||||||
img2.load()
|
img1.load()
|
||||||
|
|
||||||
# fail prepatch
|
# fail prepatch
|
||||||
with Image.open(test_img) as img:
|
with Image.open(test_img) as img:
|
||||||
img = img.crop(extents)
|
img2 = img.crop(extents)
|
||||||
img.load()
|
img2.load()
|
||||||
|
|
||||||
|
|
||||||
def test_crop_zero() -> None:
|
def test_crop_zero() -> None:
|
||||||
|
|
|
@ -38,6 +38,7 @@ def test_close_after_load(caplog: pytest.LogCaptureFixture) -> None:
|
||||||
def test_contextmanager() -> None:
|
def test_contextmanager() -> None:
|
||||||
fn = None
|
fn = None
|
||||||
with Image.open("Tests/images/hopper.gif") as im:
|
with Image.open("Tests/images/hopper.gif") as im:
|
||||||
|
assert im.fp is not None
|
||||||
fn = im.fp.fileno()
|
fn = im.fp.fileno()
|
||||||
os.fstat(fn)
|
os.fstat(fn)
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,7 @@ def test_putpalette_with_alpha_values() -> None:
|
||||||
expected = im.convert("RGBA")
|
expected = im.convert("RGBA")
|
||||||
|
|
||||||
palette = im.getpalette()
|
palette = im.getpalette()
|
||||||
|
assert palette is not None
|
||||||
transparency = im.info.pop("transparency")
|
transparency = im.info.pop("transparency")
|
||||||
|
|
||||||
palette_with_alpha_values = []
|
palette_with_alpha_values = []
|
||||||
|
|
|
@ -56,18 +56,18 @@ def test_rgba_quantize() -> None:
|
||||||
|
|
||||||
def test_quantize() -> None:
|
def test_quantize() -> None:
|
||||||
with Image.open("Tests/images/caption_6_33_22.png") as image:
|
with Image.open("Tests/images/caption_6_33_22.png") as image:
|
||||||
image = image.convert("RGB")
|
converted = image.convert("RGB")
|
||||||
converted = image.quantize()
|
converted = converted.quantize()
|
||||||
assert converted.mode == "P"
|
assert converted.mode == "P"
|
||||||
assert_image_similar(converted.convert("RGB"), image, 1)
|
assert_image_similar(converted.convert("RGB"), image, 1)
|
||||||
|
|
||||||
|
|
||||||
def test_quantize_no_dither() -> None:
|
def test_quantize_no_dither() -> None:
|
||||||
image = hopper()
|
palette: Image.Image
|
||||||
with Image.open("Tests/images/caption_6_33_22.png") as palette:
|
with Image.open("Tests/images/caption_6_33_22.png") as palette:
|
||||||
palette = palette.convert("P")
|
palette = palette.convert("P")
|
||||||
|
|
||||||
converted = image.quantize(dither=Image.Dither.NONE, palette=palette)
|
converted = hopper().quantize(dither=Image.Dither.NONE, palette=palette)
|
||||||
assert converted.mode == "P"
|
assert converted.mode == "P"
|
||||||
assert converted.palette is not None
|
assert converted.palette is not None
|
||||||
assert palette.palette is not None
|
assert palette.palette is not None
|
||||||
|
@ -94,8 +94,8 @@ def test_quantize_no_dither2() -> None:
|
||||||
|
|
||||||
def test_quantize_dither_diff() -> None:
|
def test_quantize_dither_diff() -> None:
|
||||||
image = hopper()
|
image = hopper()
|
||||||
with Image.open("Tests/images/caption_6_33_22.png") as palette:
|
with Image.open("Tests/images/caption_6_33_22.png") as im:
|
||||||
palette = palette.convert("P")
|
palette = im.convert("P")
|
||||||
|
|
||||||
dither = image.quantize(dither=Image.Dither.FLOYDSTEINBERG, palette=palette)
|
dither = image.quantize(dither=Image.Dither.FLOYDSTEINBERG, palette=palette)
|
||||||
nodither = image.quantize(dither=Image.Dither.NONE, palette=palette)
|
nodither = image.quantize(dither=Image.Dither.NONE, palette=palette)
|
||||||
|
|
|
@ -313,8 +313,8 @@ class TestImageResize:
|
||||||
|
|
||||||
@skip_unless_feature("libtiff")
|
@skip_unless_feature("libtiff")
|
||||||
def test_transposed(self) -> None:
|
def test_transposed(self) -> None:
|
||||||
with Image.open("Tests/images/g4_orientation_5.tif") as im:
|
with Image.open("Tests/images/g4_orientation_5.tif") as img:
|
||||||
im = im.resize((64, 64))
|
im = img.resize((64, 64))
|
||||||
assert im.size == (64, 64)
|
assert im.size == (64, 64)
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
|
|
@ -40,8 +40,8 @@ def test_mode(mode: str) -> None:
|
||||||
|
|
||||||
@pytest.mark.parametrize("angle", (0, 90, 180, 270))
|
@pytest.mark.parametrize("angle", (0, 90, 180, 270))
|
||||||
def test_angle(angle: int) -> None:
|
def test_angle(angle: int) -> None:
|
||||||
with Image.open("Tests/images/test-card.png") as im:
|
with Image.open("Tests/images/test-card.png") as img:
|
||||||
rotate(im, im.mode, angle)
|
rotate(img, img.mode, angle)
|
||||||
|
|
||||||
im = hopper()
|
im = hopper()
|
||||||
assert_image_equal(im.rotate(angle), im.rotate(angle, expand=1))
|
assert_image_equal(im.rotate(angle), im.rotate(angle, expand=1))
|
||||||
|
@ -74,6 +74,7 @@ def test_center_0() -> None:
|
||||||
im = hopper()
|
im = hopper()
|
||||||
im = im.rotate(45, center=(0, 0), resample=Image.Resampling.BICUBIC)
|
im = im.rotate(45, center=(0, 0), resample=Image.Resampling.BICUBIC)
|
||||||
|
|
||||||
|
target: Image.Image
|
||||||
with Image.open("Tests/images/hopper_45.png") as target:
|
with Image.open("Tests/images/hopper_45.png") as target:
|
||||||
target_origin = target.size[1] / 2
|
target_origin = target.size[1] / 2
|
||||||
target = target.crop((0, target_origin, 128, target_origin + 128))
|
target = target.crop((0, target_origin, 128, target_origin + 128))
|
||||||
|
@ -85,6 +86,7 @@ def test_center_14() -> None:
|
||||||
im = hopper()
|
im = hopper()
|
||||||
im = im.rotate(45, center=(14, 14), resample=Image.Resampling.BICUBIC)
|
im = im.rotate(45, center=(14, 14), resample=Image.Resampling.BICUBIC)
|
||||||
|
|
||||||
|
target: Image.Image
|
||||||
with Image.open("Tests/images/hopper_45.png") as target:
|
with Image.open("Tests/images/hopper_45.png") as target:
|
||||||
target_origin = target.size[1] / 2 - 14
|
target_origin = target.size[1] / 2 - 14
|
||||||
target = target.crop((6, target_origin, 128 + 6, target_origin + 128))
|
target = target.crop((6, target_origin, 128 + 6, target_origin + 128))
|
||||||
|
@ -94,6 +96,8 @@ def test_center_14() -> None:
|
||||||
|
|
||||||
def test_translate() -> None:
|
def test_translate() -> None:
|
||||||
im = hopper()
|
im = hopper()
|
||||||
|
|
||||||
|
target: Image.Image
|
||||||
with Image.open("Tests/images/hopper_45.png") as target:
|
with Image.open("Tests/images/hopper_45.png") as target:
|
||||||
target_origin = (target.size[1] / 2 - 64) - 5
|
target_origin = (target.size[1] / 2 - 64) - 5
|
||||||
target = target.crop(
|
target = target.crop(
|
||||||
|
|
|
@ -156,6 +156,7 @@ def test_reducing_gap_values() -> None:
|
||||||
|
|
||||||
|
|
||||||
def test_reducing_gap_for_DCT_scaling() -> None:
|
def test_reducing_gap_for_DCT_scaling() -> None:
|
||||||
|
ref: Image.Image
|
||||||
with Image.open("Tests/images/hopper.jpg") as ref:
|
with Image.open("Tests/images/hopper.jpg") as ref:
|
||||||
# thumbnail should call draft with reducing_gap scale
|
# thumbnail should call draft with reducing_gap scale
|
||||||
ref.draft(None, (18 * 3, 18 * 3))
|
ref.draft(None, (18 * 3, 18 * 3))
|
||||||
|
|
|
@ -247,14 +247,14 @@ class TestImageTransform:
|
||||||
def test_missing_method_data(self) -> None:
|
def test_missing_method_data(self) -> None:
|
||||||
with hopper() as im:
|
with hopper() as im:
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
im.transform((100, 100), None)
|
im.transform((100, 100), None) # type: ignore[arg-type]
|
||||||
|
|
||||||
@pytest.mark.parametrize("resample", (Image.Resampling.BOX, "unknown"))
|
@pytest.mark.parametrize("resample", (Image.Resampling.BOX, "unknown"))
|
||||||
def test_unknown_resampling_filter(self, resample: Image.Resampling | str) -> None:
|
def test_unknown_resampling_filter(self, resample: Image.Resampling | str) -> None:
|
||||||
with hopper() as im:
|
with hopper() as im:
|
||||||
(w, h) = im.size
|
(w, h) = im.size
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
im.transform((100, 100), Image.Transform.EXTENT, (0, 0, w, h), resample)
|
im.transform((100, 100), Image.Transform.EXTENT, (0, 0, w, h), resample) # type: ignore[arg-type]
|
||||||
|
|
||||||
|
|
||||||
class TestImageTransformAffine:
|
class TestImageTransformAffine:
|
||||||
|
|
|
@ -194,6 +194,7 @@ def test_bitmap() -> None:
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (W, H))
|
im = Image.new("RGB", (W, H))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
|
small: Image.Image
|
||||||
with Image.open("Tests/images/pil123rgba.png") as small:
|
with Image.open("Tests/images/pil123rgba.png") as small:
|
||||||
small = small.resize((50, 50), Image.Resampling.NEAREST)
|
small = small.resize((50, 50), Image.Resampling.NEAREST)
|
||||||
|
|
||||||
|
|
|
@ -245,8 +245,8 @@ def test_colorize_2color() -> None:
|
||||||
# Test the colorizing function with 2-color functionality
|
# Test the colorizing function with 2-color functionality
|
||||||
|
|
||||||
# Open test image (256px by 10px, black to white)
|
# Open test image (256px by 10px, black to white)
|
||||||
with Image.open("Tests/images/bw_gradient.png") as im:
|
with Image.open("Tests/images/bw_gradient.png") as img:
|
||||||
im = im.convert("L")
|
im = img.convert("L")
|
||||||
|
|
||||||
# Create image with original 2-color functionality
|
# Create image with original 2-color functionality
|
||||||
im_test = ImageOps.colorize(im, "red", "green")
|
im_test = ImageOps.colorize(im, "red", "green")
|
||||||
|
@ -285,8 +285,8 @@ def test_colorize_2color_offset() -> None:
|
||||||
# Test the colorizing function with 2-color functionality and offset
|
# Test the colorizing function with 2-color functionality and offset
|
||||||
|
|
||||||
# Open test image (256px by 10px, black to white)
|
# Open test image (256px by 10px, black to white)
|
||||||
with Image.open("Tests/images/bw_gradient.png") as im:
|
with Image.open("Tests/images/bw_gradient.png") as img:
|
||||||
im = im.convert("L")
|
im = img.convert("L")
|
||||||
|
|
||||||
# Create image with original 2-color functionality with offsets
|
# Create image with original 2-color functionality with offsets
|
||||||
im_test = ImageOps.colorize(
|
im_test = ImageOps.colorize(
|
||||||
|
@ -327,8 +327,8 @@ def test_colorize_3color_offset() -> None:
|
||||||
# Test the colorizing function with 3-color functionality and offset
|
# Test the colorizing function with 3-color functionality and offset
|
||||||
|
|
||||||
# Open test image (256px by 10px, black to white)
|
# Open test image (256px by 10px, black to white)
|
||||||
with Image.open("Tests/images/bw_gradient.png") as im:
|
with Image.open("Tests/images/bw_gradient.png") as img:
|
||||||
im = im.convert("L")
|
im = img.convert("L")
|
||||||
|
|
||||||
# Create image with new three color functionality with offsets
|
# Create image with new three color functionality with offsets
|
||||||
im_test = ImageOps.colorize(
|
im_test = ImageOps.colorize(
|
||||||
|
@ -422,6 +422,7 @@ def test_exif_transpose() -> None:
|
||||||
check(orientation_im)
|
check(orientation_im)
|
||||||
|
|
||||||
# Orientation from "XML:com.adobe.xmp" info key
|
# Orientation from "XML:com.adobe.xmp" info key
|
||||||
|
im: Image.Image
|
||||||
for suffix in ("", "_exiftool"):
|
for suffix in ("", "_exiftool"):
|
||||||
with Image.open("Tests/images/xmp_tags_orientation" + suffix + ".png") as im:
|
with Image.open("Tests/images/xmp_tags_orientation" + suffix + ".png") as im:
|
||||||
assert im.getexif()[0x0112] == 3
|
assert im.getexif()[0x0112] == 3
|
||||||
|
|
|
@ -76,9 +76,14 @@ def test_consecutive() -> None:
|
||||||
def test_palette_mmap() -> None:
|
def test_palette_mmap() -> None:
|
||||||
# Using mmap in ImageFile can require to reload the palette.
|
# Using mmap in ImageFile can require to reload the palette.
|
||||||
with Image.open("Tests/images/multipage-mmap.tiff") as im:
|
with Image.open("Tests/images/multipage-mmap.tiff") as im:
|
||||||
color1 = im.getpalette()[:3]
|
palette = im.getpalette()
|
||||||
|
assert palette is not None
|
||||||
|
color1 = palette[:3]
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
color2 = im.getpalette()[:3]
|
|
||||||
|
palette = im.getpalette()
|
||||||
|
assert palette is not None
|
||||||
|
color2 = palette[:3]
|
||||||
assert color1 == color2
|
assert color1 == color2
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ def helper_pickle_file(
|
||||||
tmp_path: Path, protocol: int, test_file: str, mode: str | None
|
tmp_path: Path, protocol: int, test_file: str, mode: str | None
|
||||||
) -> None:
|
) -> None:
|
||||||
# Arrange
|
# Arrange
|
||||||
|
im: Image.Image
|
||||||
with Image.open(test_file) as im:
|
with Image.open(test_file) as im:
|
||||||
filename = tmp_path / "temp.pkl"
|
filename = tmp_path / "temp.pkl"
|
||||||
if mode:
|
if mode:
|
||||||
|
@ -33,6 +34,7 @@ def helper_pickle_file(
|
||||||
|
|
||||||
|
|
||||||
def helper_pickle_string(protocol: int, test_file: str, mode: str | None) -> None:
|
def helper_pickle_string(protocol: int, test_file: str, mode: str | None) -> None:
|
||||||
|
im: Image.Image
|
||||||
with Image.open(test_file) as im:
|
with Image.open(test_file) as im:
|
||||||
if mode:
|
if mode:
|
||||||
im = im.convert(mode)
|
im = im.convert(mode)
|
||||||
|
@ -89,8 +91,8 @@ def test_pickle_jpeg() -> None:
|
||||||
def test_pickle_la_mode_with_palette(tmp_path: Path) -> None:
|
def test_pickle_la_mode_with_palette(tmp_path: Path) -> None:
|
||||||
# Arrange
|
# Arrange
|
||||||
filename = tmp_path / "temp.pkl"
|
filename = tmp_path / "temp.pkl"
|
||||||
with Image.open("Tests/images/hopper.jpg") as im:
|
with Image.open("Tests/images/hopper.jpg") as img:
|
||||||
im = im.convert("PA")
|
im = img.convert("PA")
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
for protocol in range(pickle.HIGHEST_PROTOCOL + 1):
|
for protocol in range(pickle.HIGHEST_PROTOCOL + 1):
|
||||||
|
|
|
@ -50,11 +50,13 @@ class TestShellInjection:
|
||||||
@pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available")
|
@pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available")
|
||||||
def test_save_netpbm_filename_bmp_mode(self, tmp_path: Path) -> None:
|
def test_save_netpbm_filename_bmp_mode(self, tmp_path: Path) -> None:
|
||||||
with Image.open(TEST_GIF) as im:
|
with Image.open(TEST_GIF) as im:
|
||||||
im = im.convert("RGB")
|
im_rgb = im.convert("RGB")
|
||||||
self.assert_save_filename_check(tmp_path, im, GifImagePlugin._save_netpbm)
|
self.assert_save_filename_check(
|
||||||
|
tmp_path, im_rgb, GifImagePlugin._save_netpbm
|
||||||
|
)
|
||||||
|
|
||||||
@pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available")
|
@pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available")
|
||||||
def test_save_netpbm_filename_l_mode(self, tmp_path: Path) -> None:
|
def test_save_netpbm_filename_l_mode(self, tmp_path: Path) -> None:
|
||||||
with Image.open(TEST_GIF) as im:
|
with Image.open(TEST_GIF) as im:
|
||||||
im = im.convert("L")
|
im_l = im.convert("L")
|
||||||
self.assert_save_filename_check(tmp_path, im, GifImagePlugin._save_netpbm)
|
self.assert_save_filename_check(tmp_path, im_l, GifImagePlugin._save_netpbm)
|
||||||
|
|
|
@ -213,6 +213,7 @@ class DdsImageFile(ImageFile.ImageFile):
|
||||||
format_description = "DirectDraw Surface"
|
format_description = "DirectDraw Surface"
|
||||||
|
|
||||||
def _open(self) -> None:
|
def _open(self) -> None:
|
||||||
|
assert self.fp is not None
|
||||||
if not _accept(self.fp.read(4)):
|
if not _accept(self.fp.read(4)):
|
||||||
msg = "not a DDS file"
|
msg = "not a DDS file"
|
||||||
raise SyntaxError(msg)
|
raise SyntaxError(msg)
|
||||||
|
|
|
@ -217,6 +217,7 @@ testpaths = [
|
||||||
python_version = "3.9"
|
python_version = "3.9"
|
||||||
pretty = true
|
pretty = true
|
||||||
disallow_any_generics = true
|
disallow_any_generics = true
|
||||||
|
disallow_untyped_defs = true
|
||||||
enable_error_code = "ignore-without-code"
|
enable_error_code = "ignore-without-code"
|
||||||
extra_checks = true
|
extra_checks = true
|
||||||
follow_imports = "silent"
|
follow_imports = "silent"
|
||||||
|
|
|
@ -77,6 +77,8 @@ class AvifImageFile(ImageFile.ImageFile):
|
||||||
):
|
):
|
||||||
msg = "Invalid opening codec"
|
msg = "Invalid opening codec"
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
|
|
||||||
|
assert self.fp is not None
|
||||||
self._decoder = _avif.AvifDecoder(
|
self._decoder = _avif.AvifDecoder(
|
||||||
self.fp.read(),
|
self.fp.read(),
|
||||||
DECODE_CODEC_CHOICE,
|
DECODE_CODEC_CHOICE,
|
||||||
|
|
|
@ -258,6 +258,7 @@ class BlpImageFile(ImageFile.ImageFile):
|
||||||
format_description = "Blizzard Mipmap Format"
|
format_description = "Blizzard Mipmap Format"
|
||||||
|
|
||||||
def _open(self) -> None:
|
def _open(self) -> None:
|
||||||
|
assert self.fp is not None
|
||||||
self.magic = self.fp.read(4)
|
self.magic = self.fp.read(4)
|
||||||
if not _accept(self.magic):
|
if not _accept(self.magic):
|
||||||
msg = f"Bad BLP magic {repr(self.magic)}"
|
msg = f"Bad BLP magic {repr(self.magic)}"
|
||||||
|
|
|
@ -76,6 +76,7 @@ class BmpImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
def _bitmap(self, header: int = 0, offset: int = 0) -> None:
|
def _bitmap(self, header: int = 0, offset: int = 0) -> None:
|
||||||
"""Read relevant info about the BMP"""
|
"""Read relevant info about the BMP"""
|
||||||
|
assert self.fp is not None
|
||||||
read, seek = self.fp.read, self.fp.seek
|
read, seek = self.fp.read, self.fp.seek
|
||||||
if header:
|
if header:
|
||||||
seek(header)
|
seek(header)
|
||||||
|
@ -311,6 +312,7 @@ class BmpImageFile(ImageFile.ImageFile):
|
||||||
def _open(self) -> None:
|
def _open(self) -> None:
|
||||||
"""Open file, check magic number and read header"""
|
"""Open file, check magic number and read header"""
|
||||||
# read 14 bytes: magic number, filesize, reserved, header final offset
|
# read 14 bytes: magic number, filesize, reserved, header final offset
|
||||||
|
assert self.fp is not None
|
||||||
head_data = self.fp.read(14)
|
head_data = self.fp.read(14)
|
||||||
# choke if the file does not have the required magic bytes
|
# choke if the file does not have the required magic bytes
|
||||||
if not _accept(head_data):
|
if not _accept(head_data):
|
||||||
|
|
|
@ -41,6 +41,7 @@ class BufrStubImageFile(ImageFile.StubImageFile):
|
||||||
format_description = "BUFR"
|
format_description = "BUFR"
|
||||||
|
|
||||||
def _open(self) -> None:
|
def _open(self) -> None:
|
||||||
|
assert self.fp is not None
|
||||||
if not _accept(self.fp.read(4)):
|
if not _accept(self.fp.read(4)):
|
||||||
msg = "Not a BUFR file"
|
msg = "Not a BUFR file"
|
||||||
raise SyntaxError(msg)
|
raise SyntaxError(msg)
|
||||||
|
|
|
@ -38,6 +38,7 @@ class CurImageFile(BmpImagePlugin.BmpImageFile):
|
||||||
format_description = "Windows Cursor"
|
format_description = "Windows Cursor"
|
||||||
|
|
||||||
def _open(self) -> None:
|
def _open(self) -> None:
|
||||||
|
assert self.fp is not None
|
||||||
offset = self.fp.tell()
|
offset = self.fp.tell()
|
||||||
|
|
||||||
# check magic
|
# check magic
|
||||||
|
|
|
@ -45,6 +45,7 @@ class DcxImageFile(PcxImageFile):
|
||||||
|
|
||||||
def _open(self) -> None:
|
def _open(self) -> None:
|
||||||
# Header
|
# Header
|
||||||
|
assert self.fp is not None
|
||||||
s = self.fp.read(4)
|
s = self.fp.read(4)
|
||||||
if not _accept(s):
|
if not _accept(s):
|
||||||
msg = "not a DCX file"
|
msg = "not a DCX file"
|
||||||
|
|
|
@ -333,6 +333,7 @@ class DdsImageFile(ImageFile.ImageFile):
|
||||||
format_description = "DirectDraw Surface"
|
format_description = "DirectDraw Surface"
|
||||||
|
|
||||||
def _open(self) -> None:
|
def _open(self) -> None:
|
||||||
|
assert self.fp is not None
|
||||||
if not _accept(self.fp.read(4)):
|
if not _accept(self.fp.read(4)):
|
||||||
msg = "not a DDS file"
|
msg = "not a DDS file"
|
||||||
raise SyntaxError(msg)
|
raise SyntaxError(msg)
|
||||||
|
|
|
@ -189,6 +189,7 @@ class EpsImageFile(ImageFile.ImageFile):
|
||||||
mode_map = {1: "L", 2: "LAB", 3: "RGB", 4: "CMYK"}
|
mode_map = {1: "L", 2: "LAB", 3: "RGB", 4: "CMYK"}
|
||||||
|
|
||||||
def _open(self) -> None:
|
def _open(self) -> None:
|
||||||
|
assert self.fp is not None
|
||||||
(length, offset) = self._find_offset(self.fp)
|
(length, offset) = self._find_offset(self.fp)
|
||||||
|
|
||||||
# go to offset - start of "%!PS"
|
# go to offset - start of "%!PS"
|
||||||
|
@ -400,6 +401,7 @@ class EpsImageFile(ImageFile.ImageFile):
|
||||||
) -> Image.core.PixelAccess | None:
|
) -> Image.core.PixelAccess | None:
|
||||||
# Load EPS via Ghostscript
|
# Load EPS via Ghostscript
|
||||||
if self.tile:
|
if self.tile:
|
||||||
|
assert self.fp is not None
|
||||||
self.im = Ghostscript(self.tile, self.size, self.fp, scale, transparency)
|
self.im = Ghostscript(self.tile, self.size, self.fp, scale, transparency)
|
||||||
self._mode = self.im.mode
|
self._mode = self.im.mode
|
||||||
self._size = self.im.size
|
self._size = self.im.size
|
||||||
|
|
|
@ -48,6 +48,7 @@ class FliImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
def _open(self) -> None:
|
def _open(self) -> None:
|
||||||
# HEAD
|
# HEAD
|
||||||
|
assert self.fp is not None
|
||||||
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"):
|
||||||
msg = "not an FLI/FLC file"
|
msg = "not an FLI/FLC file"
|
||||||
|
@ -111,6 +112,7 @@ class FliImageFile(ImageFile.ImageFile):
|
||||||
# load palette
|
# load palette
|
||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
|
assert self.fp is not None
|
||||||
for e in range(i16(self.fp.read(2))):
|
for e in range(i16(self.fp.read(2))):
|
||||||
s = self.fp.read(2)
|
s = self.fp.read(2)
|
||||||
i = i + s[0]
|
i = i + s[0]
|
||||||
|
|
|
@ -58,6 +58,7 @@ class FpxImageFile(ImageFile.ImageFile):
|
||||||
# read the OLE directory and see if this is a likely
|
# read the OLE directory and see if this is a likely
|
||||||
# to be a FlashPix file
|
# to be a FlashPix file
|
||||||
|
|
||||||
|
assert self.fp is not None
|
||||||
try:
|
try:
|
||||||
self.ole = olefile.OleFileIO(self.fp)
|
self.ole = olefile.OleFileIO(self.fp)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
|
@ -229,6 +230,7 @@ class FpxImageFile(ImageFile.ImageFile):
|
||||||
if y >= ysize:
|
if y >= ysize:
|
||||||
break # isn't really required
|
break # isn't really required
|
||||||
|
|
||||||
|
assert self.fp is not None
|
||||||
self.stream = stream
|
self.stream = stream
|
||||||
self._fp = self.fp
|
self._fp = self.fp
|
||||||
self.fp = None
|
self.fp = None
|
||||||
|
|
|
@ -72,6 +72,7 @@ class FtexImageFile(ImageFile.ImageFile):
|
||||||
format_description = "Texture File Format (IW2:EOC)"
|
format_description = "Texture File Format (IW2:EOC)"
|
||||||
|
|
||||||
def _open(self) -> None:
|
def _open(self) -> None:
|
||||||
|
assert self.fp is not None
|
||||||
if not _accept(self.fp.read(4)):
|
if not _accept(self.fp.read(4)):
|
||||||
msg = "not an FTEX file"
|
msg = "not an FTEX file"
|
||||||
raise SyntaxError(msg)
|
raise SyntaxError(msg)
|
||||||
|
|
|
@ -42,6 +42,7 @@ class GbrImageFile(ImageFile.ImageFile):
|
||||||
format_description = "GIMP brush file"
|
format_description = "GIMP brush file"
|
||||||
|
|
||||||
def _open(self) -> None:
|
def _open(self) -> None:
|
||||||
|
assert self.fp is not None
|
||||||
header_size = i32(self.fp.read(4))
|
header_size = i32(self.fp.read(4))
|
||||||
if header_size < 20:
|
if header_size < 20:
|
||||||
msg = "not a GIMP brush"
|
msg = "not a GIMP brush"
|
||||||
|
@ -91,6 +92,8 @@ class GbrImageFile(ImageFile.ImageFile):
|
||||||
def load(self) -> Image.core.PixelAccess | None:
|
def load(self) -> Image.core.PixelAccess | None:
|
||||||
if self._im is None:
|
if self._im is None:
|
||||||
self.im = Image.core.new(self.mode, self.size)
|
self.im = Image.core.new(self.mode, self.size)
|
||||||
|
|
||||||
|
assert self.fp is not None
|
||||||
self.frombytes(self.fp.read(self._data_size))
|
self.frombytes(self.fp.read(self._data_size))
|
||||||
return Image.Image.load(self)
|
return Image.Image.load(self)
|
||||||
|
|
||||||
|
|
|
@ -85,6 +85,7 @@ class GifImageFile(ImageFile.ImageFile):
|
||||||
global_palette = None
|
global_palette = None
|
||||||
|
|
||||||
def data(self) -> bytes | None:
|
def data(self) -> bytes | None:
|
||||||
|
assert self.fp is not None
|
||||||
s = self.fp.read(1)
|
s = self.fp.read(1)
|
||||||
if s and s[0]:
|
if s and s[0]:
|
||||||
return self.fp.read(s[0])
|
return self.fp.read(s[0])
|
||||||
|
@ -98,6 +99,7 @@ class GifImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
def _open(self) -> None:
|
def _open(self) -> None:
|
||||||
# Screen
|
# Screen
|
||||||
|
assert self.fp is not None
|
||||||
s = self.fp.read(13)
|
s = self.fp.read(13)
|
||||||
if not _accept(s):
|
if not _accept(s):
|
||||||
msg = "not a GIF file"
|
msg = "not a GIF file"
|
||||||
|
@ -114,8 +116,8 @@ class GifImageFile(ImageFile.ImageFile):
|
||||||
# check if palette contains colour indices
|
# check if palette contains colour indices
|
||||||
p = self.fp.read(3 << bits)
|
p = self.fp.read(3 << bits)
|
||||||
if self._is_palette_needed(p):
|
if self._is_palette_needed(p):
|
||||||
p = ImagePalette.raw("RGB", p)
|
palette = ImagePalette.raw("RGB", p)
|
||||||
self.global_palette = self.palette = p
|
self.global_palette = self.palette = palette
|
||||||
|
|
||||||
self._fp = self.fp # FIXME: hack
|
self._fp = self.fp # FIXME: hack
|
||||||
self.__rewind = self.fp.tell()
|
self.__rewind = self.fp.tell()
|
||||||
|
@ -254,7 +256,7 @@ class GifImageFile(ImageFile.ImageFile):
|
||||||
info["comment"] += b"\n" + comment
|
info["comment"] += b"\n" + comment
|
||||||
else:
|
else:
|
||||||
info["comment"] = comment
|
info["comment"] = comment
|
||||||
s = None
|
s = b""
|
||||||
continue
|
continue
|
||||||
elif s[0] == 255 and frame == 0 and block is not None:
|
elif s[0] == 255 and frame == 0 and block is not None:
|
||||||
#
|
#
|
||||||
|
@ -297,7 +299,7 @@ class GifImageFile(ImageFile.ImageFile):
|
||||||
bits = self.fp.read(1)[0]
|
bits = self.fp.read(1)[0]
|
||||||
self.__offset = self.fp.tell()
|
self.__offset = self.fp.tell()
|
||||||
break
|
break
|
||||||
s = None
|
s = b""
|
||||||
|
|
||||||
if interlace is None:
|
if interlace is None:
|
||||||
msg = "image not found in GIF frame"
|
msg = "image not found in GIF frame"
|
||||||
|
|
|
@ -41,6 +41,7 @@ class GribStubImageFile(ImageFile.StubImageFile):
|
||||||
format_description = "GRIB"
|
format_description = "GRIB"
|
||||||
|
|
||||||
def _open(self) -> None:
|
def _open(self) -> None:
|
||||||
|
assert self.fp is not None
|
||||||
if not _accept(self.fp.read(8)):
|
if not _accept(self.fp.read(8)):
|
||||||
msg = "Not a GRIB file"
|
msg = "Not a GRIB file"
|
||||||
raise SyntaxError(msg)
|
raise SyntaxError(msg)
|
||||||
|
|
|
@ -41,6 +41,7 @@ class HDF5StubImageFile(ImageFile.StubImageFile):
|
||||||
format_description = "HDF5"
|
format_description = "HDF5"
|
||||||
|
|
||||||
def _open(self) -> None:
|
def _open(self) -> None:
|
||||||
|
assert self.fp is not None
|
||||||
if not _accept(self.fp.read(8)):
|
if not _accept(self.fp.read(8)):
|
||||||
msg = "Not an HDF file"
|
msg = "Not an HDF file"
|
||||||
raise SyntaxError(msg)
|
raise SyntaxError(msg)
|
||||||
|
|
|
@ -265,6 +265,7 @@ class IcnsImageFile(ImageFile.ImageFile):
|
||||||
format_description = "Mac OS icns resource"
|
format_description = "Mac OS icns resource"
|
||||||
|
|
||||||
def _open(self) -> None:
|
def _open(self) -> None:
|
||||||
|
assert self.fp is not None
|
||||||
self.icns = IcnsFile(self.fp)
|
self.icns = IcnsFile(self.fp)
|
||||||
self._mode = "RGBA"
|
self._mode = "RGBA"
|
||||||
self.info["sizes"] = self.icns.itersizes()
|
self.info["sizes"] = self.icns.itersizes()
|
||||||
|
|
|
@ -326,6 +326,7 @@ class IcoImageFile(ImageFile.ImageFile):
|
||||||
format_description = "Windows Icon"
|
format_description = "Windows Icon"
|
||||||
|
|
||||||
def _open(self) -> None:
|
def _open(self) -> None:
|
||||||
|
assert self.fp is not None
|
||||||
self.ico = IcoFile(self.fp)
|
self.ico = IcoFile(self.fp)
|
||||||
self.info["sizes"] = self.ico.sizes()
|
self.info["sizes"] = self.ico.sizes()
|
||||||
self.size = self.ico.entry[0].dim
|
self.size = self.ico.entry[0].dim
|
||||||
|
|
|
@ -125,6 +125,7 @@ class ImImageFile(ImageFile.ImageFile):
|
||||||
# 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.
|
||||||
|
|
||||||
|
assert self.fp is not None
|
||||||
if b"\n" not in self.fp.read(100):
|
if b"\n" not in self.fp.read(100):
|
||||||
msg = "not an IM file"
|
msg = "not an IM file"
|
||||||
raise SyntaxError(msg)
|
raise SyntaxError(msg)
|
||||||
|
@ -304,6 +305,8 @@ class ImImageFile(ImageFile.ImageFile):
|
||||||
size = ((self.size[0] * bits + 7) // 8) * self.size[1]
|
size = ((self.size[0] * bits + 7) // 8) * self.size[1]
|
||||||
offs = self.__offset + frame * size
|
offs = self.__offset + frame * size
|
||||||
|
|
||||||
|
if isinstance(self._fp, DeferredError):
|
||||||
|
raise self._fp.ex
|
||||||
self.fp = self._fp
|
self.fp = self._fp
|
||||||
|
|
||||||
self.tile = [
|
self.tile = [
|
||||||
|
|
|
@ -586,16 +586,11 @@ class Image:
|
||||||
return new
|
return new
|
||||||
|
|
||||||
# Context manager support
|
# Context manager support
|
||||||
def __enter__(self):
|
def __enter__(self) -> Image:
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __exit__(self, *args):
|
def __exit__(self, *args: object) -> None:
|
||||||
from . import ImageFile
|
pass
|
||||||
|
|
||||||
if isinstance(self, ImageFile.ImageFile):
|
|
||||||
if getattr(self, "_exclusive_fp", False):
|
|
||||||
self._close_fp()
|
|
||||||
self.fp = None
|
|
||||||
|
|
||||||
def close(self) -> None:
|
def close(self) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -1514,9 +1509,13 @@ class Image:
|
||||||
exif_info = bytes.fromhex(
|
exif_info = bytes.fromhex(
|
||||||
"".join(self.info["Raw profile type exif"].split("\n")[3:])
|
"".join(self.info["Raw profile type exif"].split("\n")[3:])
|
||||||
)
|
)
|
||||||
elif hasattr(self, "tag_v2"):
|
else:
|
||||||
|
from . import TiffImagePlugin
|
||||||
|
|
||||||
|
if isinstance(self, TiffImagePlugin.TiffImageFile):
|
||||||
self._exif.bigtiff = self.tag_v2._bigtiff
|
self._exif.bigtiff = self.tag_v2._bigtiff
|
||||||
self._exif.endian = self.tag_v2._endian
|
self._exif.endian = self.tag_v2._endian
|
||||||
|
assert self.fp is not None
|
||||||
self._exif.load_from_fp(self.fp, self.tag_v2._offset)
|
self._exif.load_from_fp(self.fp, self.tag_v2._offset)
|
||||||
if exif_info is not None:
|
if exif_info is not None:
|
||||||
self._exif.load(exif_info)
|
self._exif.load(exif_info)
|
||||||
|
|
|
@ -119,6 +119,8 @@ class ImageFile(Image.Image):
|
||||||
self.decoderconfig: tuple[Any, ...] = ()
|
self.decoderconfig: tuple[Any, ...] = ()
|
||||||
self.decodermaxblock = MAXBLOCK
|
self.decodermaxblock = MAXBLOCK
|
||||||
|
|
||||||
|
self.fp: IO[bytes] | None
|
||||||
|
self._fp: IO[bytes] | DeferredError
|
||||||
if is_path(fp):
|
if is_path(fp):
|
||||||
# filename
|
# filename
|
||||||
self.fp = open(fp, "rb")
|
self.fp = open(fp, "rb")
|
||||||
|
@ -155,14 +157,23 @@ class ImageFile(Image.Image):
|
||||||
def _open(self) -> None:
|
def _open(self) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _close_fp(self):
|
# Context manager support
|
||||||
if getattr(self, "_fp", False) and not isinstance(self._fp, DeferredError):
|
def __enter__(self) -> ImageFile:
|
||||||
|
return self
|
||||||
|
|
||||||
|
def _close_fp(self) -> None:
|
||||||
|
if hasattr(self, "_fp") and not isinstance(self._fp, DeferredError):
|
||||||
if self._fp != self.fp:
|
if self._fp != self.fp:
|
||||||
self._fp.close()
|
self._fp.close()
|
||||||
self._fp = DeferredError(ValueError("Operation on closed image"))
|
self._fp = DeferredError(ValueError("Operation on closed image"))
|
||||||
if self.fp:
|
if self.fp:
|
||||||
self.fp.close()
|
self.fp.close()
|
||||||
|
|
||||||
|
def __exit__(self, *args: object) -> None:
|
||||||
|
if getattr(self, "_exclusive_fp", False):
|
||||||
|
self._close_fp()
|
||||||
|
self.fp = None
|
||||||
|
|
||||||
def close(self) -> None:
|
def close(self) -> None:
|
||||||
"""
|
"""
|
||||||
Closes the file pointer, if possible.
|
Closes the file pointer, if possible.
|
||||||
|
@ -255,7 +266,7 @@ class ImageFile(Image.Image):
|
||||||
|
|
||||||
# raise exception if something's wrong. must be called
|
# raise exception if something's wrong. must be called
|
||||||
# directly after open, and closes file when finished.
|
# directly after open, and closes file when finished.
|
||||||
if self._exclusive_fp:
|
if self._exclusive_fp and self.fp:
|
||||||
self.fp.close()
|
self.fp.close()
|
||||||
self.fp = None
|
self.fp = None
|
||||||
|
|
||||||
|
@ -273,6 +284,7 @@ class ImageFile(Image.Image):
|
||||||
self.map: mmap.mmap | None = None
|
self.map: mmap.mmap | None = None
|
||||||
use_mmap = self.filename and len(self.tile) == 1
|
use_mmap = self.filename and len(self.tile) == 1
|
||||||
|
|
||||||
|
assert self.fp is not None
|
||||||
readonly = 0
|
readonly = 0
|
||||||
|
|
||||||
# look for read/seek overrides
|
# look for read/seek overrides
|
||||||
|
|
|
@ -53,6 +53,7 @@ class IptcImageFile(ImageFile.ImageFile):
|
||||||
def field(self) -> tuple[tuple[int, int] | None, int]:
|
def field(self) -> tuple[tuple[int, int] | None, int]:
|
||||||
#
|
#
|
||||||
# get a IPTC field header
|
# get a IPTC field header
|
||||||
|
assert self.fp is not None
|
||||||
s = self.fp.read(5)
|
s = self.fp.read(5)
|
||||||
if not s.strip(b"\x00"):
|
if not s.strip(b"\x00"):
|
||||||
return None, 0
|
return None, 0
|
||||||
|
@ -80,6 +81,7 @@ class IptcImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
def _open(self) -> None:
|
def _open(self) -> None:
|
||||||
# load descriptive fields
|
# load descriptive fields
|
||||||
|
assert self.fp is not None
|
||||||
while True:
|
while True:
|
||||||
offset = self.fp.tell()
|
offset = self.fp.tell()
|
||||||
tag, size = self.field()
|
tag, size = self.field()
|
||||||
|
@ -133,6 +135,7 @@ class IptcImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
offset, compression = self.tile[0][2:]
|
offset, compression = self.tile[0][2:]
|
||||||
|
|
||||||
|
assert self.fp is not None
|
||||||
self.fp.seek(offset)
|
self.fp.seek(offset)
|
||||||
|
|
||||||
# Copy image data to temporary file
|
# Copy image data to temporary file
|
||||||
|
@ -141,6 +144,7 @@ class IptcImageFile(ImageFile.ImageFile):
|
||||||
# To simplify access to the extracted file,
|
# To simplify access to the extracted file,
|
||||||
# prepend a PPM header
|
# prepend a PPM header
|
||||||
o.write(b"P5\n%d %d\n255\n" % self.size)
|
o.write(b"P5\n%d %d\n255\n" % self.size)
|
||||||
|
assert self.fp is not None
|
||||||
while True:
|
while True:
|
||||||
type, size = self.field()
|
type, size = self.field()
|
||||||
if type != (8, 10):
|
if type != (8, 10):
|
||||||
|
|
|
@ -248,6 +248,7 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
|
||||||
format_description = "JPEG 2000 (ISO 15444)"
|
format_description = "JPEG 2000 (ISO 15444)"
|
||||||
|
|
||||||
def _open(self) -> None:
|
def _open(self) -> None:
|
||||||
|
assert self.fp is not None
|
||||||
sig = self.fp.read(4)
|
sig = self.fp.read(4)
|
||||||
if sig == b"\xff\x4f\xff\x51":
|
if sig == b"\xff\x4f\xff\x51":
|
||||||
self.codec = "j2k"
|
self.codec = "j2k"
|
||||||
|
@ -300,6 +301,7 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
|
||||||
]
|
]
|
||||||
|
|
||||||
def _parse_comment(self) -> None:
|
def _parse_comment(self) -> None:
|
||||||
|
assert self.fp is not None
|
||||||
while True:
|
while True:
|
||||||
marker = self.fp.read(2)
|
marker = self.fp.read(2)
|
||||||
if not marker:
|
if not marker:
|
||||||
|
|
|
@ -60,6 +60,7 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
|
|
||||||
def Skip(self: JpegImageFile, marker: int) -> None:
|
def Skip(self: JpegImageFile, marker: int) -> None:
|
||||||
|
assert self.fp is not None
|
||||||
n = i16(self.fp.read(2)) - 2
|
n = i16(self.fp.read(2)) - 2
|
||||||
ImageFile._safe_read(self.fp, n)
|
ImageFile._safe_read(self.fp, n)
|
||||||
|
|
||||||
|
@ -69,6 +70,7 @@ def APP(self: JpegImageFile, marker: int) -> None:
|
||||||
# Application marker. Store these in the APP dictionary.
|
# Application marker. Store these in the APP dictionary.
|
||||||
# Also look for well-known application markers.
|
# Also look for well-known application markers.
|
||||||
|
|
||||||
|
assert self.fp is not None
|
||||||
n = i16(self.fp.read(2)) - 2
|
n = i16(self.fp.read(2)) - 2
|
||||||
s = ImageFile._safe_read(self.fp, n)
|
s = ImageFile._safe_read(self.fp, n)
|
||||||
|
|
||||||
|
@ -173,6 +175,7 @@ def APP(self: JpegImageFile, marker: int) -> None:
|
||||||
def COM(self: JpegImageFile, marker: int) -> None:
|
def COM(self: JpegImageFile, marker: int) -> None:
|
||||||
#
|
#
|
||||||
# Comment marker. Store these in the APP dictionary.
|
# Comment marker. Store these in the APP dictionary.
|
||||||
|
assert self.fp is not None
|
||||||
n = i16(self.fp.read(2)) - 2
|
n = i16(self.fp.read(2)) - 2
|
||||||
s = ImageFile._safe_read(self.fp, n)
|
s = ImageFile._safe_read(self.fp, n)
|
||||||
|
|
||||||
|
@ -189,6 +192,7 @@ def SOF(self: JpegImageFile, marker: int) -> None:
|
||||||
# mode. Note that this could be made a bit brighter, by
|
# mode. Note that this could be made a bit brighter, by
|
||||||
# looking for JFIF and Adobe APP markers.
|
# looking for JFIF and Adobe APP markers.
|
||||||
|
|
||||||
|
assert self.fp is not None
|
||||||
n = i16(self.fp.read(2)) - 2
|
n = i16(self.fp.read(2)) - 2
|
||||||
s = ImageFile._safe_read(self.fp, n)
|
s = ImageFile._safe_read(self.fp, n)
|
||||||
self._size = i16(s, 3), i16(s, 1)
|
self._size = i16(s, 3), i16(s, 1)
|
||||||
|
@ -237,6 +241,7 @@ def DQT(self: JpegImageFile, marker: int) -> None:
|
||||||
# FIXME: The quantization tables can be used to estimate the
|
# FIXME: The quantization tables can be used to estimate the
|
||||||
# compression quality.
|
# compression quality.
|
||||||
|
|
||||||
|
assert self.fp is not None
|
||||||
n = i16(self.fp.read(2)) - 2
|
n = i16(self.fp.read(2)) - 2
|
||||||
s = ImageFile._safe_read(self.fp, n)
|
s = ImageFile._safe_read(self.fp, n)
|
||||||
while len(s):
|
while len(s):
|
||||||
|
@ -337,6 +342,7 @@ class JpegImageFile(ImageFile.ImageFile):
|
||||||
format_description = "JPEG (ISO 10918)"
|
format_description = "JPEG (ISO 10918)"
|
||||||
|
|
||||||
def _open(self) -> None:
|
def _open(self) -> None:
|
||||||
|
assert self.fp is not None
|
||||||
s = self.fp.read(3)
|
s = self.fp.read(3)
|
||||||
|
|
||||||
if not _accept(s):
|
if not _accept(s):
|
||||||
|
@ -405,6 +411,7 @@ class JpegImageFile(ImageFile.ImageFile):
|
||||||
For premature EOF and LOAD_TRUNCATED_IMAGES adds EOI marker
|
For premature EOF and LOAD_TRUNCATED_IMAGES adds EOI marker
|
||||||
so libjpeg can finish decoding
|
so libjpeg can finish decoding
|
||||||
"""
|
"""
|
||||||
|
assert self.fp is not None
|
||||||
s = self.fp.read(read_bytes)
|
s = self.fp.read(read_bytes)
|
||||||
|
|
||||||
if not s and ImageFile.LOAD_TRUNCATED_IMAGES and not hasattr(self, "_ended"):
|
if not s and ImageFile.LOAD_TRUNCATED_IMAGES and not hasattr(self, "_ended"):
|
||||||
|
|
|
@ -67,6 +67,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
|
||||||
self._n_frames = len(self.images)
|
self._n_frames = len(self.images)
|
||||||
self.is_animated = self._n_frames > 1
|
self.is_animated = self._n_frames > 1
|
||||||
|
|
||||||
|
assert self.fp is not None
|
||||||
self.__fp = self.fp
|
self.__fp = self.fp
|
||||||
self.seek(0)
|
self.seek(0)
|
||||||
|
|
||||||
|
|
|
@ -106,6 +106,7 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile):
|
||||||
_close_exclusive_fp_after_loading = False
|
_close_exclusive_fp_after_loading = False
|
||||||
|
|
||||||
def _open(self) -> None:
|
def _open(self) -> None:
|
||||||
|
assert self.fp is not None
|
||||||
self.fp.seek(0) # prep the fp in order to pass the JPEG test
|
self.fp.seek(0) # prep the fp in order to pass the JPEG test
|
||||||
JpegImagePlugin.JpegImageFile._open(self)
|
JpegImagePlugin.JpegImageFile._open(self)
|
||||||
self._after_jpeg_open()
|
self._after_jpeg_open()
|
||||||
|
@ -125,6 +126,7 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile):
|
||||||
assert self.n_frames == len(self.__mpoffsets)
|
assert self.n_frames == len(self.__mpoffsets)
|
||||||
del self.info["mpoffset"] # no longer needed
|
del self.info["mpoffset"] # no longer needed
|
||||||
self.is_animated = self.n_frames > 1
|
self.is_animated = self.n_frames > 1
|
||||||
|
assert self.fp is not None
|
||||||
self._fp = self.fp # FIXME: hack
|
self._fp = self.fp # FIXME: hack
|
||||||
self._fp.seek(self.__mpoffsets[0]) # get ready to read first frame
|
self._fp.seek(self.__mpoffsets[0]) # get ready to read first frame
|
||||||
self.__frame = 0
|
self.__frame = 0
|
||||||
|
|
|
@ -755,6 +755,7 @@ class PngImageFile(ImageFile.ImageFile):
|
||||||
format_description = "Portable network graphics"
|
format_description = "Portable network graphics"
|
||||||
|
|
||||||
def _open(self) -> None:
|
def _open(self) -> None:
|
||||||
|
assert self.fp is not None
|
||||||
if not _accept(self.fp.read(8)):
|
if not _accept(self.fp.read(8)):
|
||||||
msg = "not a PNG file"
|
msg = "not a PNG file"
|
||||||
raise SyntaxError(msg)
|
raise SyntaxError(msg)
|
||||||
|
@ -986,6 +987,7 @@ class PngImageFile(ImageFile.ImageFile):
|
||||||
"""internal: read more image data"""
|
"""internal: read more image data"""
|
||||||
|
|
||||||
assert self.png is not None
|
assert self.png is not None
|
||||||
|
assert self.fp is not None
|
||||||
while self.__idat == 0:
|
while self.__idat == 0:
|
||||||
# end of chunk, skip forward to next one
|
# end of chunk, skip forward to next one
|
||||||
|
|
||||||
|
@ -1019,6 +1021,7 @@ class PngImageFile(ImageFile.ImageFile):
|
||||||
def load_end(self) -> None:
|
def load_end(self) -> None:
|
||||||
"""internal: finished reading image data"""
|
"""internal: finished reading image data"""
|
||||||
assert self.png is not None
|
assert self.png is not None
|
||||||
|
assert self.fp is not None
|
||||||
if self.__idat != 0:
|
if self.__idat != 0:
|
||||||
self.fp.read(self.__idat)
|
self.fp.read(self.__idat)
|
||||||
while True:
|
while True:
|
||||||
|
|
|
@ -61,6 +61,7 @@ class PsdImageFile(ImageFile.ImageFile):
|
||||||
_close_exclusive_fp_after_loading = False
|
_close_exclusive_fp_after_loading = False
|
||||||
|
|
||||||
def _open(self) -> None:
|
def _open(self) -> None:
|
||||||
|
assert self.fp is not None
|
||||||
read = self.fp.read
|
read = self.fp.read
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -178,6 +179,8 @@ class PsdImageFile(ImageFile.ImageFile):
|
||||||
self._mode = mode
|
self._mode = mode
|
||||||
self.tile = tile
|
self.tile = tile
|
||||||
self.frame = layer
|
self.frame = layer
|
||||||
|
if isinstance(self._fp, DeferredError):
|
||||||
|
raise self._fp.ex
|
||||||
self.fp = self._fp
|
self.fp = self._fp
|
||||||
|
|
||||||
def tell(self) -> int:
|
def tell(self) -> int:
|
||||||
|
|
|
@ -25,6 +25,7 @@ class QoiImageFile(ImageFile.ImageFile):
|
||||||
format_description = "Quite OK Image"
|
format_description = "Quite OK Image"
|
||||||
|
|
||||||
def _open(self) -> None:
|
def _open(self) -> None:
|
||||||
|
assert self.fp is not None
|
||||||
if not _accept(self.fp.read(4)):
|
if not _accept(self.fp.read(4)):
|
||||||
msg = "not a QOI file"
|
msg = "not a QOI file"
|
||||||
raise SyntaxError(msg)
|
raise SyntaxError(msg)
|
||||||
|
|
|
@ -103,6 +103,7 @@ class SpiderImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
def _open(self) -> None:
|
def _open(self) -> None:
|
||||||
# check header
|
# check header
|
||||||
|
assert self.fp is not None
|
||||||
n = 27 * 4 # read 27 float values
|
n = 27 * 4 # read 27 float values
|
||||||
f = self.fp.read(n)
|
f = self.fp.read(n)
|
||||||
|
|
||||||
|
@ -184,6 +185,9 @@ class SpiderImageFile(ImageFile.ImageFile):
|
||||||
if isinstance(self._fp, DeferredError):
|
if isinstance(self._fp, DeferredError):
|
||||||
raise self._fp.ex
|
raise self._fp.ex
|
||||||
self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes)
|
self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes)
|
||||||
|
|
||||||
|
if isinstance(self._fp, DeferredError):
|
||||||
|
raise self._fp.ex
|
||||||
self.fp = self._fp
|
self.fp = self._fp
|
||||||
self.fp.seek(self.stkoffset)
|
self.fp.seek(self.stkoffset)
|
||||||
self._open()
|
self._open()
|
||||||
|
@ -323,9 +327,9 @@ if __name__ == "__main__":
|
||||||
outfile = sys.argv[2]
|
outfile = sys.argv[2]
|
||||||
|
|
||||||
# perform some image operation
|
# perform some image operation
|
||||||
im = im.transpose(Image.Transpose.FLIP_LEFT_RIGHT)
|
transposed_im = im.transpose(Image.Transpose.FLIP_LEFT_RIGHT)
|
||||||
print(
|
print(
|
||||||
f"saving a flipped version of {os.path.basename(filename)} "
|
f"saving a flipped version of {os.path.basename(filename)} "
|
||||||
f"as {outfile} "
|
f"as {outfile} "
|
||||||
)
|
)
|
||||||
im.save(outfile, SpiderImageFile.format)
|
transposed_im.save(outfile, SpiderImageFile.format)
|
||||||
|
|
|
@ -1175,6 +1175,7 @@ class TiffImageFile(ImageFile.ImageFile):
|
||||||
"""Open the first image in a TIFF file"""
|
"""Open the first image in a TIFF file"""
|
||||||
|
|
||||||
# Header
|
# Header
|
||||||
|
assert self.fp is not None
|
||||||
ifh = self.fp.read(8)
|
ifh = self.fp.read(8)
|
||||||
if ifh[2] == 43:
|
if ifh[2] == 43:
|
||||||
ifh += self.fp.read(8)
|
ifh += self.fp.read(8)
|
||||||
|
@ -1341,6 +1342,7 @@ class TiffImageFile(ImageFile.ImageFile):
|
||||||
# To be nice on memory footprint, if there's a
|
# To be nice on memory footprint, if there's a
|
||||||
# file descriptor, use that instead of reading
|
# file descriptor, use that instead of reading
|
||||||
# into a string in python.
|
# into a string in python.
|
||||||
|
assert self.fp is not None
|
||||||
try:
|
try:
|
||||||
fp = hasattr(self.fp, "fileno") and self.fp.fileno()
|
fp = hasattr(self.fp, "fileno") and self.fp.fileno()
|
||||||
# flush the file descriptor, prevents error on pypy 2.4+
|
# flush the file descriptor, prevents error on pypy 2.4+
|
||||||
|
|
|
@ -39,6 +39,7 @@ class WalImageFile(ImageFile.ImageFile):
|
||||||
self._mode = "P"
|
self._mode = "P"
|
||||||
|
|
||||||
# read header fields
|
# read header fields
|
||||||
|
assert self.fp is not None
|
||||||
header = self.fp.read(32 + 24 + 32 + 12)
|
header = self.fp.read(32 + 24 + 32 + 12)
|
||||||
self._size = i32(header, 32), i32(header, 36)
|
self._size = i32(header, 32), i32(header, 36)
|
||||||
Image._decompression_bomb_check(self.size)
|
Image._decompression_bomb_check(self.size)
|
||||||
|
@ -55,6 +56,7 @@ class WalImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
def load(self) -> Image.core.PixelAccess | None:
|
def load(self) -> Image.core.PixelAccess | None:
|
||||||
if self._im is None:
|
if self._im is None:
|
||||||
|
assert self.fp is not None
|
||||||
self.im = Image.core.new(self.mode, self.size)
|
self.im = Image.core.new(self.mode, self.size)
|
||||||
self.frombytes(self.fp.read(self.size[0] * self.size[1]))
|
self.frombytes(self.fp.read(self.size[0] * self.size[1]))
|
||||||
self.putpalette(quake2palette)
|
self.putpalette(quake2palette)
|
||||||
|
|
|
@ -43,6 +43,7 @@ class WebPImageFile(ImageFile.ImageFile):
|
||||||
def _open(self) -> None:
|
def _open(self) -> None:
|
||||||
# Use the newer AnimDecoder API to parse the (possibly) animated file,
|
# Use the newer AnimDecoder API to parse the (possibly) animated file,
|
||||||
# and access muxed chunks like ICC/EXIF/XMP.
|
# and access muxed chunks like ICC/EXIF/XMP.
|
||||||
|
assert self.fp is not None
|
||||||
self._decoder = _webp.WebPAnimDecoder(self.fp.read())
|
self._decoder = _webp.WebPAnimDecoder(self.fp.read())
|
||||||
|
|
||||||
# Get info from decoder
|
# Get info from decoder
|
||||||
|
|
|
@ -49,6 +49,7 @@ if hasattr(Image.core, "drawwmf"):
|
||||||
self.bbox = im.info["wmf_bbox"]
|
self.bbox = im.info["wmf_bbox"]
|
||||||
|
|
||||||
def load(self, im: ImageFile.StubImageFile) -> Image.Image:
|
def load(self, im: ImageFile.StubImageFile) -> Image.Image:
|
||||||
|
assert im.fp is not None
|
||||||
im.fp.seek(0) # rewind
|
im.fp.seek(0) # rewind
|
||||||
return Image.frombytes(
|
return Image.frombytes(
|
||||||
"RGB",
|
"RGB",
|
||||||
|
@ -81,6 +82,7 @@ class WmfStubImageFile(ImageFile.StubImageFile):
|
||||||
|
|
||||||
def _open(self) -> None:
|
def _open(self) -> None:
|
||||||
# check placable header
|
# check placable header
|
||||||
|
assert self.fp is not None
|
||||||
s = self.fp.read(44)
|
s = self.fp.read(44)
|
||||||
|
|
||||||
if s.startswith(b"\xd7\xcd\xc6\x9a\x00\x00"):
|
if s.startswith(b"\xd7\xcd\xc6\x9a\x00\x00"):
|
||||||
|
|
|
@ -58,15 +58,15 @@ class XVThumbImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
# skip info comments
|
# skip info comments
|
||||||
while True:
|
while True:
|
||||||
s = self.fp.readline()
|
line = self.fp.readline()
|
||||||
if not s:
|
if not line:
|
||||||
msg = "Unexpected EOF reading XV thumbnail file"
|
msg = "Unexpected EOF reading XV thumbnail file"
|
||||||
raise SyntaxError(msg)
|
raise SyntaxError(msg)
|
||||||
if s[0] != 35: # ie. when not a comment: '#'
|
if line[0] != 35: # ie. when not a comment: '#'
|
||||||
break
|
break
|
||||||
|
|
||||||
# parse header line (already read)
|
# parse header line (already read)
|
||||||
s = s.strip().split()
|
s = line.strip().split()
|
||||||
|
|
||||||
self._mode = "P"
|
self._mode = "P"
|
||||||
self._size = int(s[0]), int(s[1])
|
self._size = int(s[0]), int(s[1])
|
||||||
|
|
Loading…
Reference in New Issue
Block a user