mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-04-07 19:04:13 +03:00
Merge 5406def0d2
into cda26be10e
This commit is contained in:
commit
d1505153ee
|
@ -101,6 +101,7 @@ def assert_image_equal_tofile(
|
|||
msg: str | None = None,
|
||||
mode: str | None = None,
|
||||
) -> None:
|
||||
img: Image.Image
|
||||
with Image.open(filename) as img:
|
||||
if mode:
|
||||
img = img.convert(mode)
|
||||
|
@ -144,6 +145,7 @@ def assert_image_similar_tofile(
|
|||
epsilon: float,
|
||||
msg: str | None = None,
|
||||
) -> None:
|
||||
img: Image.Image
|
||||
with Image.open(filename) as img:
|
||||
assert_image_similar(a, img, epsilon, msg)
|
||||
|
||||
|
|
|
@ -94,8 +94,11 @@ def test_good() -> None:
|
|||
|
||||
for f in get_files("g"):
|
||||
try:
|
||||
im: Image.Image
|
||||
with Image.open(f) as im:
|
||||
im.load()
|
||||
|
||||
compare: Image.Image
|
||||
with Image.open(get_compare(f)) as compare:
|
||||
compare.load()
|
||||
if im.mode == "P":
|
||||
|
|
|
@ -277,25 +277,25 @@ def test_apng_mode() -> None:
|
|||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
assert im.mode == "P"
|
||||
im.seek(im.n_frames - 1)
|
||||
im = im.convert("RGB")
|
||||
assert im.getpixel((0, 0)) == (0, 255, 0)
|
||||
assert im.getpixel((64, 32)) == (0, 255, 0)
|
||||
rgb_im = im.convert("RGB")
|
||||
assert rgb_im.getpixel((0, 0)) == (0, 255, 0)
|
||||
assert rgb_im.getpixel((64, 32)) == (0, 255, 0)
|
||||
|
||||
with Image.open("Tests/images/apng/mode_palette_alpha.png") as im:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
assert im.mode == "P"
|
||||
im.seek(im.n_frames - 1)
|
||||
im = im.convert("RGBA")
|
||||
assert im.getpixel((0, 0)) == (0, 255, 0, 255)
|
||||
assert im.getpixel((64, 32)) == (0, 255, 0, 255)
|
||||
rgb_im = im.convert("RGBA")
|
||||
assert rgb_im.getpixel((0, 0)) == (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:
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
assert im.mode == "P"
|
||||
im.seek(im.n_frames - 1)
|
||||
im = im.convert("RGBA")
|
||||
assert im.getpixel((0, 0)) == (0, 0, 255, 128)
|
||||
assert im.getpixel((64, 32)) == (0, 0, 255, 128)
|
||||
rgb_im = im.convert("RGBA")
|
||||
assert rgb_im.getpixel((0, 0)) == (0, 0, 255, 128)
|
||||
assert rgb_im.getpixel((64, 32)) == (0, 0, 255, 128)
|
||||
|
||||
|
||||
def test_apng_chunk_errors() -> None:
|
||||
|
|
|
@ -14,6 +14,7 @@ import pytest
|
|||
|
||||
from PIL import (
|
||||
AvifImagePlugin,
|
||||
GifImagePlugin,
|
||||
Image,
|
||||
ImageDraw,
|
||||
ImageFile,
|
||||
|
@ -220,6 +221,7 @@ class TestFileAvif:
|
|||
def test_background_from_gif(self, tmp_path: Path) -> None:
|
||||
with Image.open("Tests/images/chi.gif") as im:
|
||||
original_value = im.convert("RGB").getpixel((1, 1))
|
||||
assert isinstance(original_value, tuple)
|
||||
|
||||
# Save as AVIF
|
||||
out_avif = tmp_path / "temp.avif"
|
||||
|
@ -232,6 +234,7 @@ class TestFileAvif:
|
|||
|
||||
with Image.open(out_gif) as reread:
|
||||
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)])
|
||||
assert difference <= 3
|
||||
|
||||
|
@ -240,6 +243,7 @@ class TestFileAvif:
|
|||
with Image.open("Tests/images/chi.gif") as im:
|
||||
im.save(temp_file)
|
||||
with Image.open(temp_file) as im:
|
||||
assert isinstance(im, AvifImagePlugin.AvifImageFile)
|
||||
assert im.n_frames == 1
|
||||
|
||||
def test_invalid_file(self) -> None:
|
||||
|
@ -254,7 +258,9 @@ class TestFileAvif:
|
|||
assert_image(im, "RGBA", (64, 64))
|
||||
|
||||
# image has 876 transparent pixels
|
||||
assert im.getchannel("A").getcolors()[0] == (876, 0)
|
||||
colors = im.getchannel("A").getcolors()
|
||||
assert colors is not None
|
||||
assert colors[0] == (876, 0)
|
||||
|
||||
def test_save_transparent(self, tmp_path: Path) -> None:
|
||||
im = Image.new("RGBA", (10, 10), (0, 0, 0, 0))
|
||||
|
@ -596,10 +602,12 @@ class TestAvifAnimation:
|
|||
"""
|
||||
|
||||
with Image.open(TEST_AVIF_FILE) as im:
|
||||
assert isinstance(im, AvifImagePlugin.AvifImageFile)
|
||||
assert im.n_frames == 1
|
||||
assert not im.is_animated
|
||||
|
||||
with Image.open("Tests/images/avif/star.avifs") as im:
|
||||
assert isinstance(im, AvifImagePlugin.AvifImageFile)
|
||||
assert im.n_frames == 5
|
||||
assert im.is_animated
|
||||
|
||||
|
@ -610,11 +618,13 @@ class TestAvifAnimation:
|
|||
"""
|
||||
|
||||
with Image.open("Tests/images/avif/star.gif") as original:
|
||||
assert isinstance(original, GifImagePlugin.GifImageFile)
|
||||
assert original.n_frames > 1
|
||||
|
||||
temp_file = tmp_path / "temp.avif"
|
||||
original.save(temp_file, save_all=True)
|
||||
with Image.open(temp_file) as im:
|
||||
assert isinstance(im, AvifImagePlugin.AvifImageFile)
|
||||
assert im.n_frames == original.n_frames
|
||||
|
||||
# Compare first frame in P mode to frame from original GIF
|
||||
|
@ -634,6 +644,7 @@ class TestAvifAnimation:
|
|||
|
||||
def check(temp_file: Path) -> None:
|
||||
with Image.open(temp_file) as im:
|
||||
assert isinstance(im, AvifImagePlugin.AvifImageFile)
|
||||
assert im.n_frames == 4
|
||||
|
||||
# Compare first frame to original
|
||||
|
@ -706,6 +717,7 @@ class TestAvifAnimation:
|
|||
)
|
||||
|
||||
with Image.open(temp_file) as im:
|
||||
assert isinstance(im, AvifImagePlugin.AvifImageFile)
|
||||
assert im.n_frames == 5
|
||||
assert im.is_animated
|
||||
|
||||
|
@ -735,6 +747,7 @@ class TestAvifAnimation:
|
|||
)
|
||||
|
||||
with Image.open(temp_file) as im:
|
||||
assert isinstance(im, AvifImagePlugin.AvifImageFile)
|
||||
assert im.n_frames == 5
|
||||
assert im.is_animated
|
||||
|
||||
|
|
|
@ -160,9 +160,9 @@ def test_save_dib(tmp_path: Path) -> None:
|
|||
def test_rgba_bitfields() -> None:
|
||||
# This test image has been manually hexedited
|
||||
# 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
|
||||
b, g, r = im.split()[1:]
|
||||
b, g, r = bmp_im.split()[1:]
|
||||
im = Image.merge("RGB", (r, g, b))
|
||||
|
||||
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:
|
||||
self.loaded = True
|
||||
assert im.fp is not None
|
||||
im.fp.close()
|
||||
return Image.new("RGB", (1, 1))
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ def test_invalid_file() -> None:
|
|||
no_cursors_file = "Tests/images/no_cursors.cur"
|
||||
|
||||
cur = CurImagePlugin.CurImageFile(TEST_FILE)
|
||||
assert cur.fp is not None
|
||||
cur.fp.close()
|
||||
with open(no_cursors_file, "rb") as cur.fp:
|
||||
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:
|
||||
"""Check DXT1 and BC1 images can be opened"""
|
||||
target: Image.Image
|
||||
with Image.open(TEST_FILE_DXT1.replace(".dds", ".png")) as target:
|
||||
target = target.convert("RGBA")
|
||||
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:
|
||||
out = tmp_path / "temp.dds"
|
||||
with Image.open(TEST_FILE_DX10_BC5_TYPELESS) as im:
|
||||
im.save(out, pixel_format="BC5")
|
||||
assert_image_similar_tofile(im, out, 9.56)
|
||||
with Image.open(TEST_FILE_DX10_BC5_TYPELESS) as img:
|
||||
img.save(out, pixel_format="BC5")
|
||||
assert_image_similar_tofile(img, out, 9.56)
|
||||
|
||||
im = hopper("L")
|
||||
with pytest.raises(OSError, match="only RGB mode can be written as BC5"):
|
||||
|
|
|
@ -255,8 +255,8 @@ def test_bytesio_object() -> None:
|
|||
with Image.open(img_bytes) as img:
|
||||
img.load()
|
||||
|
||||
with Image.open(FILE1_COMPARE) as image1_scale1_compare:
|
||||
image1_scale1_compare = image1_scale1_compare.convert("RGB")
|
||||
with Image.open(FILE1_COMPARE) as im:
|
||||
image1_scale1_compare = im.convert("RGB")
|
||||
image1_scale1_compare.load()
|
||||
assert_image_similar(img, image1_scale1_compare, 5)
|
||||
|
||||
|
@ -291,16 +291,16 @@ def test_render_scale1() -> None:
|
|||
# Zero bounding box
|
||||
with Image.open(FILE1) as image1_scale1:
|
||||
image1_scale1.load()
|
||||
with Image.open(FILE1_COMPARE) as image1_scale1_compare:
|
||||
image1_scale1_compare = image1_scale1_compare.convert("RGB")
|
||||
with Image.open(FILE1_COMPARE) as im:
|
||||
image1_scale1_compare = im.convert("RGB")
|
||||
image1_scale1_compare.load()
|
||||
assert_image_similar(image1_scale1, image1_scale1_compare, 5)
|
||||
|
||||
# Non-zero bounding box
|
||||
with Image.open(FILE2) as image2_scale1:
|
||||
image2_scale1.load()
|
||||
with Image.open(FILE2_COMPARE) as image2_scale1_compare:
|
||||
image2_scale1_compare = image2_scale1_compare.convert("RGB")
|
||||
with Image.open(FILE2_COMPARE) as im:
|
||||
image2_scale1_compare = im.convert("RGB")
|
||||
image2_scale1_compare.load()
|
||||
assert_image_similar(image2_scale1, image2_scale1_compare, 10)
|
||||
|
||||
|
@ -314,8 +314,8 @@ def test_render_scale2() -> None:
|
|||
with Image.open(FILE1) as image1_scale2:
|
||||
assert isinstance(image1_scale2, EpsImagePlugin.EpsImageFile)
|
||||
image1_scale2.load(scale=2)
|
||||
with Image.open(FILE1_COMPARE_SCALE2) as image1_scale2_compare:
|
||||
image1_scale2_compare = image1_scale2_compare.convert("RGB")
|
||||
with Image.open(FILE1_COMPARE_SCALE2) as im:
|
||||
image1_scale2_compare = im.convert("RGB")
|
||||
image1_scale2_compare.load()
|
||||
assert_image_similar(image1_scale2, image1_scale2_compare, 5)
|
||||
|
||||
|
@ -323,8 +323,8 @@ def test_render_scale2() -> None:
|
|||
with Image.open(FILE2) as image2_scale2:
|
||||
assert isinstance(image2_scale2, EpsImagePlugin.EpsImageFile)
|
||||
image2_scale2.load(scale=2)
|
||||
with Image.open(FILE2_COMPARE_SCALE2) as image2_scale2_compare:
|
||||
image2_scale2_compare = image2_scale2_compare.convert("RGB")
|
||||
with Image.open(FILE2_COMPARE_SCALE2) as im:
|
||||
image2_scale2_compare = im.convert("RGB")
|
||||
image2_scale2_compare.load()
|
||||
assert_image_similar(image2_scale2, image2_scale2_compare, 10)
|
||||
|
||||
|
@ -334,9 +334,9 @@ def test_render_scale2() -> None:
|
|||
"filename", (FILE1, FILE2, "Tests/images/eps/illu10_preview.eps")
|
||||
)
|
||||
def test_resize(filename: str) -> None:
|
||||
with Image.open(filename) as im:
|
||||
with Image.open(filename) as img:
|
||||
new_size = (100, 100)
|
||||
im = im.resize(new_size)
|
||||
im = img.resize(new_size)
|
||||
assert im.size == new_size
|
||||
|
||||
|
||||
|
|
|
@ -43,6 +43,8 @@ def test_sanity() -> None:
|
|||
def test_prefix_chunk(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setattr(ImageFile, "LOAD_TRUNCATED_IMAGES", True)
|
||||
with Image.open(animated_test_file_with_prefix_chunk) as im:
|
||||
assert isinstance(im, FliImagePlugin.FliImageFile)
|
||||
|
||||
assert im.mode == "P"
|
||||
assert im.size == (320, 200)
|
||||
assert im.format == "FLI"
|
||||
|
@ -50,6 +52,7 @@ def test_prefix_chunk(monkeypatch: pytest.MonkeyPatch) -> None:
|
|||
assert im.is_animated
|
||||
|
||||
palette = im.getpalette()
|
||||
assert palette is not None
|
||||
assert palette[3:6] == [255, 255, 255]
|
||||
assert palette[381:384] == [204, 204, 12]
|
||||
assert palette[765:] == [252, 0, 0]
|
||||
|
|
|
@ -224,6 +224,7 @@ def test_optimize_if_palette_can_be_reduced_by_half() -> None:
|
|||
out = BytesIO()
|
||||
im.save(out, "GIF", optimize=optimize)
|
||||
with Image.open(out) as reloaded:
|
||||
assert reloaded.palette is not None
|
||||
assert len(reloaded.palette.palette) // 3 == colors
|
||||
|
||||
|
||||
|
@ -280,6 +281,7 @@ def test_roundtrip_save_all(tmp_path: Path) -> None:
|
|||
im.save(out, save_all=True)
|
||||
|
||||
with Image.open(out) as reread:
|
||||
assert isinstance(reread, GifImagePlugin.GifImageFile)
|
||||
assert reread.n_frames == 5
|
||||
|
||||
|
||||
|
@ -305,6 +307,7 @@ def test_roundtrip_save_all_1(tmp_path: Path) -> None:
|
|||
),
|
||||
)
|
||||
def test_loading_multiple_palettes(path: str, mode: str) -> None:
|
||||
im: Image.Image
|
||||
with Image.open(path) as im:
|
||||
assert im.mode == "P"
|
||||
assert im.palette is not None
|
||||
|
@ -338,7 +341,7 @@ def test_headers_saving_for_animated_gifs(tmp_path: Path) -> None:
|
|||
|
||||
def test_palette_handling(tmp_path: Path) -> None:
|
||||
# see https://github.com/python-pillow/Pillow/issues/513
|
||||
|
||||
im: Image.Image
|
||||
with Image.open(TEST_GIF) as im:
|
||||
im = im.convert("RGB")
|
||||
|
||||
|
@ -362,8 +365,8 @@ def test_palette_434(tmp_path: Path) -> None:
|
|||
|
||||
return reloaded
|
||||
|
||||
orig = "Tests/images/test.colors.gif"
|
||||
with Image.open(orig) as im:
|
||||
im: Image.Image
|
||||
with Image.open("Tests/images/test.colors.gif") as im:
|
||||
with roundtrip(im) as reloaded:
|
||||
assert_image_similar(im, reloaded, 1)
|
||||
with roundtrip(im, optimize=True) as reloaded:
|
||||
|
@ -378,6 +381,7 @@ def test_palette_434(tmp_path: Path) -> None:
|
|||
|
||||
@pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available")
|
||||
def test_save_netpbm_bmp_mode(tmp_path: Path) -> None:
|
||||
img: Image.Image
|
||||
with Image.open(TEST_GIF) as img:
|
||||
img = img.convert("RGB")
|
||||
|
||||
|
@ -390,6 +394,7 @@ def test_save_netpbm_bmp_mode(tmp_path: Path) -> None:
|
|||
|
||||
@pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available")
|
||||
def test_save_netpbm_l_mode(tmp_path: Path) -> None:
|
||||
img: Image.Image
|
||||
with Image.open(TEST_GIF) as img:
|
||||
img = img.convert("L")
|
||||
|
||||
|
@ -540,7 +545,9 @@ def test_dispose_background_transparency() -> None:
|
|||
img.seek(2)
|
||||
px = img.load()
|
||||
assert px is not None
|
||||
assert px[35, 30][3] == 0
|
||||
value = px[35, 30]
|
||||
assert isinstance(value, tuple)
|
||||
assert value[3] == 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
@ -1017,9 +1024,9 @@ def test_webp_background(tmp_path: Path) -> None:
|
|||
|
||||
# Test opaque WebP background
|
||||
if features.check("webp"):
|
||||
with Image.open("Tests/images/hopper.webp") as im:
|
||||
assert im.info["background"] == (255, 255, 255, 255)
|
||||
im.save(out)
|
||||
with Image.open("Tests/images/hopper.webp") as img:
|
||||
assert img.info["background"] == (255, 255, 255, 255)
|
||||
img.save(out)
|
||||
|
||||
# Test non-opaque WebP background
|
||||
im = Image.new("L", (100, 100), "#000")
|
||||
|
@ -1028,6 +1035,7 @@ def test_webp_background(tmp_path: Path) -> None:
|
|||
|
||||
|
||||
def test_comment(tmp_path: Path) -> None:
|
||||
im: Image.Image
|
||||
with Image.open(TEST_GIF) as im:
|
||||
assert im.info["comment"] == b"File written by Adobe Photoshop\xa8 4.0"
|
||||
|
||||
|
@ -1358,7 +1366,9 @@ def test_palette_save_all_P(tmp_path: Path) -> None:
|
|||
with Image.open(out) as im:
|
||||
# Assert that the frames are correct, and each frame has the same palette
|
||||
assert_image_equal(im.convert("RGB"), frames[0].convert("RGB"))
|
||||
assert isinstance(im, GifImagePlugin.GifImageFile)
|
||||
assert im.palette is not None
|
||||
assert im.global_palette is not None
|
||||
assert im.palette.palette == im.global_palette.palette
|
||||
|
||||
im.seek(1)
|
||||
|
@ -1422,7 +1432,9 @@ def test_getdata(monkeypatch: pytest.MonkeyPatch) -> None:
|
|||
def test_lzw_bits() -> None:
|
||||
# see https://github.com/python-pillow/Pillow/issues/2811
|
||||
with Image.open("Tests/images/issue_2811.gif") as im:
|
||||
assert im.tile[0][3][0] == 11 # LZW bits
|
||||
args = im.tile[0][3]
|
||||
assert isinstance(args, tuple)
|
||||
assert args[0] == 11 # LZW bits
|
||||
# codec error prepatch
|
||||
im.load()
|
||||
|
||||
|
@ -1477,7 +1489,11 @@ def test_saving_rgba(tmp_path: Path) -> None:
|
|||
|
||||
with Image.open(out) as reloaded:
|
||||
reloaded_rgba = reloaded.convert("RGBA")
|
||||
assert reloaded_rgba.load()[0, 0][3] == 0
|
||||
px = reloaded_rgba.load()
|
||||
assert px is not None
|
||||
value = px[0, 0]
|
||||
assert isinstance(value, tuple)
|
||||
assert value[3] == 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize("params", ({}, {"disposal": 2, "optimize": False}))
|
||||
|
|
|
@ -59,8 +59,9 @@ def test_handler(tmp_path: Path) -> None:
|
|||
def open(self, im: Image.Image) -> None:
|
||||
self.opened = True
|
||||
|
||||
def load(self, im: Image.Image) -> Image.Image:
|
||||
def load(self, im: ImageFile.ImageFile) -> Image.Image:
|
||||
self.loaded = True
|
||||
assert im.fp is not None
|
||||
im.fp.close()
|
||||
return Image.new("RGB", (1, 1))
|
||||
|
||||
|
|
|
@ -61,8 +61,9 @@ def test_handler(tmp_path: Path) -> None:
|
|||
def open(self, im: Image.Image) -> None:
|
||||
self.opened = True
|
||||
|
||||
def load(self, im: Image.Image) -> Image.Image:
|
||||
def load(self, im: ImageFile.ImageFile) -> Image.Image:
|
||||
self.loaded = True
|
||||
assert im.fp is not None
|
||||
im.fp.close()
|
||||
return Image.new("RGB", (1, 1))
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ def test_getiptcinfo_jpg_none() -> None:
|
|||
# Arrange
|
||||
with hopper() as im:
|
||||
# Act
|
||||
iptc = IptcImagePlugin.getiptcinfo(im)
|
||||
iptc = IptcImagePlugin.getiptcinfo(im) # type: ignore[arg-type]
|
||||
|
||||
# Assert
|
||||
assert iptc is None
|
||||
|
|
|
@ -132,26 +132,30 @@ class TestFileJpeg:
|
|||
f = "Tests/images/pil_sample_cmyk.jpg"
|
||||
with Image.open(f) as im:
|
||||
# the source image has red pixels in the upper left corner.
|
||||
c, m, y, k = (x / 255.0 for x in im.getpixel((0, 0)))
|
||||
value = im.getpixel((0, 0))
|
||||
assert isinstance(value, tuple)
|
||||
c, m, y, k = (x / 255.0 for x in value)
|
||||
assert c == 0.0
|
||||
assert m > 0.8
|
||||
assert y > 0.8
|
||||
assert k == 0.0
|
||||
# the opposite corner is black
|
||||
c, m, y, k = (
|
||||
x / 255.0 for x in im.getpixel((im.size[0] - 1, im.size[1] - 1))
|
||||
)
|
||||
value = im.getpixel((im.size[0] - 1, im.size[1] - 1))
|
||||
assert isinstance(value, tuple)
|
||||
c, m, y, k = (x / 255.0 for x in value)
|
||||
assert k > 0.9
|
||||
# roundtrip, and check again
|
||||
im = self.roundtrip(im)
|
||||
c, m, y, k = (x / 255.0 for x in im.getpixel((0, 0)))
|
||||
value = im.getpixel((0, 0))
|
||||
assert isinstance(value, tuple)
|
||||
c, m, y, k = (x / 255.0 for x in value)
|
||||
assert c == 0.0
|
||||
assert m > 0.8
|
||||
assert y > 0.8
|
||||
assert k == 0.0
|
||||
c, m, y, k = (
|
||||
x / 255.0 for x in im.getpixel((im.size[0] - 1, im.size[1] - 1))
|
||||
)
|
||||
value = im.getpixel((im.size[0] - 1, im.size[1] - 1))
|
||||
assert isinstance(value, tuple)
|
||||
c, m, y, k = (x / 255.0 for x in value)
|
||||
assert k > 0.9
|
||||
|
||||
def test_rgb(self) -> None:
|
||||
|
@ -334,8 +338,10 @@ class TestFileJpeg:
|
|||
|
||||
# Reading
|
||||
with Image.open("Tests/images/exif_gps.jpg") as im:
|
||||
exif = im._getexif()
|
||||
assert exif[gps_index] == expected_exif_gps
|
||||
assert isinstance(im, JpegImagePlugin.JpegImageFile)
|
||||
exif_data = im._getexif()
|
||||
assert exif_data is not None
|
||||
assert exif_data[gps_index] == expected_exif_gps
|
||||
|
||||
# Writing
|
||||
f = tmp_path / "temp.jpg"
|
||||
|
@ -344,8 +350,10 @@ class TestFileJpeg:
|
|||
hopper().save(f, exif=exif)
|
||||
|
||||
with Image.open(f) as reloaded:
|
||||
exif = reloaded._getexif()
|
||||
assert exif[gps_index] == expected_exif_gps
|
||||
assert isinstance(reloaded, JpegImagePlugin.JpegImageFile)
|
||||
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:
|
||||
with Image.open("Tests/images/empty_gps_ifd.jpg") as im:
|
||||
|
@ -372,6 +380,7 @@ class TestFileJpeg:
|
|||
exifs = []
|
||||
for i in range(2):
|
||||
with Image.open("Tests/images/exif-200dpcm.jpg") as im:
|
||||
assert isinstance(im, JpegImagePlugin.JpegImageFile)
|
||||
exifs.append(im._getexif())
|
||||
assert exifs[0] == exifs[1]
|
||||
|
||||
|
@ -405,13 +414,18 @@ class TestFileJpeg:
|
|||
}
|
||||
|
||||
with Image.open("Tests/images/exif_gps.jpg") as im:
|
||||
assert isinstance(im, JpegImagePlugin.JpegImageFile)
|
||||
|
||||
exif = im._getexif()
|
||||
assert exif is not None
|
||||
|
||||
for tag, value in expected_exif.items():
|
||||
assert value == exif[tag]
|
||||
|
||||
def test_exif_gps_typeerror(self) -> None:
|
||||
with Image.open("Tests/images/exif_gps_typeerror.jpg") as im:
|
||||
assert isinstance(im, JpegImagePlugin.JpegImageFile)
|
||||
|
||||
# Should not raise a TypeError
|
||||
im._getexif()
|
||||
|
||||
|
@ -491,7 +505,9 @@ class TestFileJpeg:
|
|||
|
||||
def test_exif(self) -> None:
|
||||
with Image.open("Tests/images/pil_sample_rgb.jpg") as im:
|
||||
assert isinstance(im, JpegImagePlugin.JpegImageFile)
|
||||
info = im._getexif()
|
||||
assert info is not None
|
||||
assert info[305] == "Adobe Photoshop CS Macintosh"
|
||||
|
||||
def test_get_child_images(self) -> None:
|
||||
|
@ -677,10 +693,12 @@ class TestFileJpeg:
|
|||
def test_save_multiple_16bit_qtables(self) -> None:
|
||||
with Image.open("Tests/images/hopper_16bit_qtables.jpg") as im:
|
||||
im2 = self.roundtrip(im, qtables="keep")
|
||||
assert isinstance(im, JpegImagePlugin.JpegImageFile)
|
||||
assert im.quantization == im2.quantization
|
||||
|
||||
def test_save_single_16bit_qtable(self) -> None:
|
||||
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]})
|
||||
assert len(im2.quantization) == 1
|
||||
assert im2.quantization[0] == im.quantization[0]
|
||||
|
@ -889,7 +907,10 @@ class TestFileJpeg:
|
|||
# in contrast to normal 8
|
||||
with Image.open("Tests/images/exif-ifd-offset.jpg") as im:
|
||||
# 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:
|
||||
with Image.open("Tests/images/multiple_exif.jpg") as im:
|
||||
|
@ -1110,8 +1131,9 @@ class TestFileCloseW32:
|
|||
im.save(tmpfile)
|
||||
|
||||
im = Image.open(tmpfile)
|
||||
assert im.fp is not None
|
||||
assert not im.fp.closed
|
||||
fp = im.fp
|
||||
assert not fp.closed
|
||||
with pytest.raises(OSError):
|
||||
os.remove(tmpfile)
|
||||
im.load()
|
||||
|
|
|
@ -164,7 +164,7 @@ def test_reduce() -> None:
|
|||
with Image.open("Tests/images/test-card-lossless.jp2") as im:
|
||||
assert callable(im.reduce)
|
||||
|
||||
im.reduce = 2
|
||||
im.reduce = 2 # type: ignore[method-assign, assignment]
|
||||
assert im.reduce == 2
|
||||
|
||||
im.load()
|
||||
|
|
|
@ -11,7 +11,15 @@ from typing import Any, NamedTuple
|
|||
|
||||
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 .helper import (
|
||||
|
@ -27,7 +35,7 @@ from .helper import (
|
|||
|
||||
@skip_unless_feature("libtiff")
|
||||
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"""
|
||||
# 1 bit
|
||||
assert im.mode == "1"
|
||||
|
@ -441,7 +449,6 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
assert isinstance(orig, TiffImagePlugin.TiffImageFile)
|
||||
|
||||
out = tmp_path / "temp.tif"
|
||||
|
||||
orig.tag[269] = "temp.tif"
|
||||
orig.save(out)
|
||||
|
||||
|
@ -469,8 +476,8 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
# test case from irc, how to do blur on b/w image
|
||||
# and save to compressed tif.
|
||||
out = tmp_path / "temp.tif"
|
||||
with Image.open("Tests/images/pport_g4.tif") as im:
|
||||
im = im.convert("L")
|
||||
with Image.open("Tests/images/pport_g4.tif") as img:
|
||||
im = img.convert("L")
|
||||
|
||||
im = im.filter(ImageFilter.GaussianBlur(4))
|
||||
im.save(out, compression="tiff_adobe_deflate")
|
||||
|
@ -564,8 +571,9 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
im.save(out, compression=compression)
|
||||
|
||||
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.fp is not None
|
||||
fn = im.fp.fileno()
|
||||
|
||||
os.fstat(fn)
|
||||
|
@ -1053,8 +1061,8 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
# Set EXIF Orientation to 2
|
||||
data = data[:102] + b"\x02" + data[103:]
|
||||
|
||||
with Image.open(io.BytesIO(data)) as im:
|
||||
im = im.transpose(Image.Transpose.FLIP_LEFT_RIGHT)
|
||||
with Image.open(io.BytesIO(data)) as img:
|
||||
im = img.transpose(Image.Transpose.FLIP_LEFT_RIGHT)
|
||||
assert_image_equal_tofile(im, "Tests/images/old-style-jpeg-compression.png")
|
||||
|
||||
def test_open_missing_samplesperpixel(self) -> None:
|
||||
|
@ -1122,9 +1130,8 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
with Image.open("Tests/images/g4_orientation_1.tif") as base_im:
|
||||
for i in range(2, 9):
|
||||
with Image.open("Tests/images/g4_orientation_" + str(i) + ".tif") as im:
|
||||
im = ImageOps.exif_transpose(im)
|
||||
|
||||
assert_image_similar(base_im, im, 0.7)
|
||||
transposed_im = ImageOps.exif_transpose(im)
|
||||
assert_image_similar(base_im, transposed_im, 0.7)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"test_file",
|
||||
|
|
|
@ -22,10 +22,10 @@ def test_sanity() -> None:
|
|||
|
||||
# Adjust for the gamma of 2.2 encoded into the file
|
||||
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")
|
||||
assert_image_similar(im, im2, 10)
|
||||
assert_image_similar(im1, im2, 10)
|
||||
|
||||
|
||||
def test_n_frames() -> None:
|
||||
|
|
|
@ -120,9 +120,11 @@ def test_ignore_frame_size() -> None:
|
|||
# Ignore the different size of the second frame
|
||||
# since this is not a "Large Thumbnail" image
|
||||
with Image.open("Tests/images/ignore_frame_size.mpo") as im:
|
||||
assert isinstance(im, MpoImagePlugin.MpoImageFile)
|
||||
assert im.size == (64, 64)
|
||||
|
||||
im.seek(1)
|
||||
assert im.mpinfo is not None
|
||||
assert (
|
||||
im.mpinfo[0xB002][1]["Attribute"]["MPType"]
|
||||
== "Multi-Frame Image: (Disparity)"
|
||||
|
@ -155,7 +157,9 @@ def test_reload_exif_after_seek() -> None:
|
|||
@pytest.mark.parametrize("test_file", test_files)
|
||||
def test_mp(test_file: str) -> None:
|
||||
with Image.open(test_file) as im:
|
||||
assert isinstance(im, MpoImagePlugin.MpoImageFile)
|
||||
mpinfo = im._getmp()
|
||||
assert mpinfo is not None
|
||||
assert mpinfo[45056] == b"0100"
|
||||
assert mpinfo[45057] == 2
|
||||
|
||||
|
@ -164,7 +168,9 @@ def test_mp_offset() -> None:
|
|||
# This image has been manually hexedited to have an IFD offset of 10
|
||||
# in APP2 data, in contrast to normal 8
|
||||
with Image.open("Tests/images/sugarshack_ifd_offset.mpo") as im:
|
||||
assert isinstance(im, MpoImagePlugin.MpoImageFile)
|
||||
mpinfo = im._getmp()
|
||||
assert mpinfo is not None
|
||||
assert mpinfo[45056] == b"0100"
|
||||
assert mpinfo[45057] == 2
|
||||
|
||||
|
@ -180,7 +186,9 @@ def test_mp_no_data() -> None:
|
|||
@pytest.mark.parametrize("test_file", test_files)
|
||||
def test_mp_attribute(test_file: str) -> None:
|
||||
with Image.open(test_file) as im:
|
||||
assert isinstance(im, MpoImagePlugin.MpoImageFile)
|
||||
mpinfo = im._getmp()
|
||||
assert mpinfo is not None
|
||||
for frame_number, mpentry in enumerate(mpinfo[0xB002]):
|
||||
mpattr = mpentry["Attribute"]
|
||||
if frame_number:
|
||||
|
@ -281,6 +289,7 @@ def test_save(test_file: str) -> None:
|
|||
|
||||
|
||||
def test_save_all() -> None:
|
||||
im: Image.Image
|
||||
for test_file in test_files:
|
||||
with Image.open(test_file) as im:
|
||||
im_reloaded = roundtrip(im, save_all=True)
|
||||
|
|
|
@ -93,6 +93,7 @@ class TestFilePng:
|
|||
|
||||
hopper("RGB").save(test_file)
|
||||
|
||||
im: Image.Image
|
||||
with Image.open(test_file) as im:
|
||||
im.load()
|
||||
assert im.mode == "RGB"
|
||||
|
@ -103,6 +104,8 @@ class TestFilePng:
|
|||
for mode in ["1", "L", "P", "RGB", "I", "I;16", "I;16B"]:
|
||||
im = hopper(mode)
|
||||
im.save(test_file)
|
||||
|
||||
reloaded: Image.Image
|
||||
with Image.open(test_file) as reloaded:
|
||||
if mode in ("I", "I;16B"):
|
||||
reloaded = reloaded.convert(mode)
|
||||
|
@ -225,11 +228,13 @@ class TestFilePng:
|
|||
test_file = "Tests/images/pil123p.png"
|
||||
with Image.open(test_file) as im:
|
||||
assert_image(im, "P", (162, 150))
|
||||
im = im.convert("RGBA")
|
||||
assert_image(im, "RGBA", (162, 150))
|
||||
rgba_im = im.convert("RGBA")
|
||||
assert_image(rgba_im, "RGBA", (162, 150))
|
||||
|
||||
# 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:
|
||||
test_file = "Tests/images/rgb_trns.png"
|
||||
|
@ -237,11 +242,13 @@ class TestFilePng:
|
|||
assert im.info["transparency"] == (0, 255, 52)
|
||||
|
||||
assert_image(im, "RGB", (64, 64))
|
||||
im = im.convert("RGBA")
|
||||
assert_image(im, "RGBA", (64, 64))
|
||||
rgba_im = im.convert("RGBA")
|
||||
assert_image(rgba_im, "RGBA", (64, 64))
|
||||
|
||||
# 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:
|
||||
in_file = "Tests/images/pil123p.png"
|
||||
|
@ -258,11 +265,13 @@ class TestFilePng:
|
|||
assert len(im.info["transparency"]) == 256
|
||||
|
||||
assert_image(im, "P", (162, 150))
|
||||
im = im.convert("RGBA")
|
||||
assert_image(im, "RGBA", (162, 150))
|
||||
rgba_im = im.convert("RGBA")
|
||||
assert_image(rgba_im, "RGBA", (162, 150))
|
||||
|
||||
# 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:
|
||||
in_file = "Tests/images/p_trns_single.png"
|
||||
|
@ -279,13 +288,15 @@ class TestFilePng:
|
|||
assert im.info["transparency"] == 164
|
||||
assert im.getpixel((31, 31)) == 164
|
||||
assert_image(im, "P", (64, 64))
|
||||
im = im.convert("RGBA")
|
||||
assert_image(im, "RGBA", (64, 64))
|
||||
rgba_im = im.convert("RGBA")
|
||||
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
|
||||
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:
|
||||
# check if solid black image with full transparency
|
||||
|
@ -313,7 +324,9 @@ class TestFilePng:
|
|||
assert im.info["transparency"] == 255
|
||||
|
||||
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"
|
||||
im.save(test_file)
|
||||
|
@ -324,7 +337,9 @@ class TestFilePng:
|
|||
assert_image_equal(im, test_im)
|
||||
|
||||
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:
|
||||
in_file = "Tests/images/caption_6_33_22.png"
|
||||
|
@ -671,6 +686,9 @@ class TestFilePng:
|
|||
im.save(out, bits=4, save_all=save_all)
|
||||
|
||||
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
|
||||
|
||||
def test_plte_length(self, tmp_path: Path) -> None:
|
||||
|
@ -681,6 +699,9 @@ class TestFilePng:
|
|||
im.save(out)
|
||||
|
||||
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
|
||||
|
||||
def test_getxmp(self) -> None:
|
||||
|
@ -702,13 +723,17 @@ class TestFilePng:
|
|||
def test_exif(self) -> None:
|
||||
# With an EXIF chunk
|
||||
with Image.open("Tests/images/exif.png") as im:
|
||||
exif = im._getexif()
|
||||
assert exif[274] == 1
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
exif_data = im._getexif()
|
||||
assert exif_data is not None
|
||||
assert exif_data[274] == 1
|
||||
|
||||
# With an ImageMagick zTXt chunk
|
||||
with Image.open("Tests/images/exif_imagemagick.png") as im:
|
||||
exif = im._getexif()
|
||||
assert exif[274] == 1
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
exif_data = im._getexif()
|
||||
assert exif_data is not None
|
||||
assert exif_data[274] == 1
|
||||
|
||||
# Assert that info still can be extracted
|
||||
# when the image is no longer a PngImageFile instance
|
||||
|
@ -717,8 +742,10 @@ class TestFilePng:
|
|||
|
||||
# With a tEXt chunk
|
||||
with Image.open("Tests/images/exif_text.png") as im:
|
||||
exif = im._getexif()
|
||||
assert exif[274] == 1
|
||||
assert isinstance(im, PngImagePlugin.PngImageFile)
|
||||
exif_data = im._getexif()
|
||||
assert exif_data is not None
|
||||
assert exif_data[274] == 1
|
||||
|
||||
# With XMP tags
|
||||
with Image.open("Tests/images/xmp_tags_orientation.png") as im:
|
||||
|
@ -740,7 +767,9 @@ class TestFilePng:
|
|||
im.save(test_file, exif=im.getexif())
|
||||
|
||||
with Image.open(test_file) as reloaded:
|
||||
assert isinstance(reloaded, PngImagePlugin.PngImageFile)
|
||||
exif = reloaded._getexif()
|
||||
assert exif is not None
|
||||
assert exif[274] == 1
|
||||
|
||||
@mark_if_feature_version(
|
||||
|
@ -752,7 +781,9 @@ class TestFilePng:
|
|||
im.save(test_file, exif=im.getexif())
|
||||
|
||||
with Image.open(test_file) as reloaded:
|
||||
assert isinstance(reloaded, PngImagePlugin.PngImageFile)
|
||||
exif = reloaded._getexif()
|
||||
assert exif is not None
|
||||
assert exif[305] == "Adobe Photoshop CS Macintosh"
|
||||
|
||||
def test_exif_argument(self, tmp_path: Path) -> None:
|
||||
|
@ -776,7 +807,6 @@ class TestFilePng:
|
|||
|
||||
@pytest.mark.parametrize("buffer", (True, False))
|
||||
def test_save_stdout(self, buffer: bool, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
|
||||
class MyStdOut:
|
||||
buffer = BytesIO()
|
||||
|
||||
|
@ -785,7 +815,7 @@ class TestFilePng:
|
|||
monkeypatch.setattr(sys, "stdout", mystdout)
|
||||
|
||||
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):
|
||||
mystdout = mystdout.buffer
|
||||
|
|
|
@ -365,7 +365,6 @@ def test_mimetypes(tmp_path: Path) -> None:
|
|||
|
||||
@pytest.mark.parametrize("buffer", (True, False))
|
||||
def test_save_stdout(buffer: bool, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
|
||||
class MyStdOut:
|
||||
buffer = BytesIO()
|
||||
|
||||
|
@ -374,7 +373,7 @@ def test_save_stdout(buffer: bool, monkeypatch: pytest.MonkeyPatch) -> None:
|
|||
monkeypatch.setattr(sys, "stdout", mystdout)
|
||||
|
||||
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):
|
||||
mystdout = mystdout.buffer
|
||||
|
|
|
@ -84,8 +84,8 @@ def test_rgbx() -> None:
|
|||
|
||||
with Image.open(io.BytesIO(data)) as im:
|
||||
r, g, b = im.split()
|
||||
im = Image.merge("RGB", (b, g, r))
|
||||
assert_image_equal_tofile(im, os.path.join(EXTRA_DIR, "32bpp.png"))
|
||||
im_bgr = Image.merge("RGB", (b, g, r))
|
||||
assert_image_equal_tofile(im_bgr, os.path.join(EXTRA_DIR, "32bpp.png"))
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
|
|
|
@ -220,12 +220,16 @@ def test_horizontal_orientations() -> None:
|
|||
with Image.open("Tests/images/rgb32rle_top_right.tga") as im:
|
||||
px = im.load()
|
||||
assert px is not None
|
||||
assert px[90, 90][:3] == (0, 0, 0)
|
||||
value = px[90, 90]
|
||||
assert isinstance(value, tuple)
|
||||
assert value[:3] == (0, 0, 0)
|
||||
|
||||
with Image.open("Tests/images/rgb32rle_bottom_right.tga") as im:
|
||||
px = im.load()
|
||||
assert px is not None
|
||||
assert px[90, 90][:3] == (0, 255, 0)
|
||||
value = px[90, 90]
|
||||
assert isinstance(value, tuple)
|
||||
assert value[:3] == (0, 255, 0)
|
||||
|
||||
|
||||
def test_save_rle(tmp_path: Path) -> None:
|
||||
|
@ -268,13 +272,17 @@ def test_save_l_transparency(tmp_path: Path) -> None:
|
|||
in_file = "Tests/images/la.tga"
|
||||
with Image.open(in_file) as im:
|
||||
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"
|
||||
im.save(out)
|
||||
|
||||
with Image.open(out) as test_im:
|
||||
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)
|
||||
|
|
|
@ -395,6 +395,7 @@ class TestFileTiff:
|
|||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
|
||||
# Act
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
ret = str(im.ifd)
|
||||
|
||||
# Assert
|
||||
|
@ -762,13 +763,13 @@ class TestFileTiff:
|
|||
|
||||
def test_tiff_save_all(self) -> None:
|
||||
mp = BytesIO()
|
||||
with Image.open("Tests/images/multipage.tiff") as im:
|
||||
im.save(mp, format="tiff", save_all=True)
|
||||
with Image.open("Tests/images/multipage.tiff") as img:
|
||||
img.save(mp, format="tiff", save_all=True)
|
||||
|
||||
mp.seek(0, os.SEEK_SET)
|
||||
with Image.open(mp) as im:
|
||||
assert isinstance(im, TiffImagePlugin.TiffImageFile)
|
||||
assert im.n_frames == 3
|
||||
with Image.open(mp) as img:
|
||||
assert isinstance(img, TiffImagePlugin.TiffImageFile)
|
||||
assert img.n_frames == 3
|
||||
|
||||
# Test appending images
|
||||
mp = BytesIO()
|
||||
|
@ -956,6 +957,7 @@ class TestFileTiff:
|
|||
|
||||
im = Image.open(tmpfile)
|
||||
fp = im.fp
|
||||
assert fp is not None
|
||||
assert not fp.closed
|
||||
im.load()
|
||||
assert fp.closed
|
||||
|
@ -969,6 +971,7 @@ class TestFileTiff:
|
|||
with open(tmpfile, "rb") as f:
|
||||
im = Image.open(f)
|
||||
fp = im.fp
|
||||
assert fp is not None
|
||||
assert not fp.closed
|
||||
im.load()
|
||||
assert not fp.closed
|
||||
|
@ -1019,8 +1022,9 @@ class TestFileTiffW32:
|
|||
im.save(tmpfile)
|
||||
|
||||
im = Image.open(tmpfile)
|
||||
assert im.fp is not None
|
||||
assert not im.fp.closed
|
||||
fp = im.fp
|
||||
assert not fp.closed
|
||||
with pytest.raises(OSError):
|
||||
os.remove(tmpfile)
|
||||
im.load()
|
||||
|
|
|
@ -175,13 +175,13 @@ def test_change_stripbytecounts_tag_type(tmp_path: Path) -> None:
|
|||
del info[278]
|
||||
|
||||
# Resize the image so that STRIPBYTECOUNTS will be larger than a SHORT
|
||||
im = im.resize((500, 500))
|
||||
info[TiffImagePlugin.IMAGEWIDTH] = im.width
|
||||
resized_im = im.resize((500, 500))
|
||||
info[TiffImagePlugin.IMAGEWIDTH] = resized_im.width
|
||||
|
||||
# STRIPBYTECOUNTS can be a SHORT or a LONG
|
||||
info.tagtype[TiffImagePlugin.STRIPBYTECOUNTS] = TiffTags.SHORT
|
||||
|
||||
im.save(out, tiffinfo=info)
|
||||
resized_im.save(out, tiffinfo=info)
|
||||
|
||||
with Image.open(out) as reloaded:
|
||||
assert isinstance(reloaded, TiffImagePlugin.TiffImageFile)
|
||||
|
|
|
@ -219,6 +219,7 @@ class TestFileWebp:
|
|||
# Save P mode GIF with background
|
||||
with Image.open("Tests/images/chi.gif") as im:
|
||||
original_value = im.convert("RGB").getpixel((1, 1))
|
||||
assert isinstance(original_value, tuple)
|
||||
|
||||
# Save as WEBP
|
||||
im.save(out_webp, save_all=True)
|
||||
|
@ -230,6 +231,7 @@ class TestFileWebp:
|
|||
|
||||
with Image.open(out_gif) as reread:
|
||||
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))
|
||||
assert difference < 5
|
||||
|
||||
|
|
|
@ -22,11 +22,13 @@ except ImportError:
|
|||
def test_read_exif_metadata() -> None:
|
||||
file_path = "Tests/images/flower.webp"
|
||||
with Image.open(file_path) as image:
|
||||
assert isinstance(image, WebPImagePlugin.WebPImageFile)
|
||||
assert image.format == "WEBP"
|
||||
exif_data = image.info.get("exif", None)
|
||||
assert exif_data
|
||||
|
||||
exif = image._getexif()
|
||||
assert exif is not None
|
||||
|
||||
# Camera make
|
||||
assert exif[271] == "Canon"
|
||||
|
|
|
@ -615,8 +615,8 @@ class TestImage:
|
|||
assert im.mode == mode
|
||||
assert im.getpixel((0, 0)) == 0
|
||||
assert im.getpixel((255, 255)) == 255
|
||||
with Image.open(target_file) as target:
|
||||
target = target.convert(mode)
|
||||
with Image.open(target_file) as img:
|
||||
target = img.convert(mode)
|
||||
assert_image_equal(im, target)
|
||||
|
||||
def test_radial_gradient_wrong_mode(self) -> None:
|
||||
|
@ -640,8 +640,8 @@ class TestImage:
|
|||
assert im.mode == mode
|
||||
assert im.getpixel((0, 0)) == 255
|
||||
assert im.getpixel((128, 128)) == 0
|
||||
with Image.open(target_file) as target:
|
||||
target = target.convert(mode)
|
||||
with Image.open(target_file) as img:
|
||||
target = img.convert(mode)
|
||||
assert_image_equal(im, target)
|
||||
|
||||
def test_register_extensions(self) -> None:
|
||||
|
@ -662,8 +662,8 @@ class TestImage:
|
|||
|
||||
def test_remap_palette(self) -> None:
|
||||
# Test identity transform
|
||||
with Image.open("Tests/images/hopper.gif") as im:
|
||||
assert_image_equal(im, im.remap_palette(list(range(256))))
|
||||
with Image.open("Tests/images/hopper.gif") as img:
|
||||
assert_image_equal(img, img.remap_palette(list(range(256))))
|
||||
|
||||
# Test identity transform with an RGBA palette
|
||||
im = Image.new("P", (256, 1))
|
||||
|
@ -673,6 +673,7 @@ class TestImage:
|
|||
im_remapped = im.remap_palette(list(range(256)))
|
||||
assert_image_equal(im, im_remapped)
|
||||
assert im.palette is not None
|
||||
assert im_remapped.palette is not None
|
||||
assert im.palette.palette == im_remapped.palette.palette
|
||||
|
||||
# Test illegal image mode
|
||||
|
|
|
@ -76,8 +76,8 @@ def test_8bit() -> None:
|
|||
|
||||
|
||||
def test_16bit() -> None:
|
||||
with Image.open("Tests/images/16bit.cropped.tif") as im:
|
||||
_test_float_conversion(im)
|
||||
with Image.open("Tests/images/16bit.cropped.tif") as img:
|
||||
_test_float_conversion(img)
|
||||
|
||||
for color in (65535, 65536):
|
||||
im = Image.new("I", (1, 1), color)
|
||||
|
|
|
@ -78,13 +78,13 @@ def test_crop_crash() -> None:
|
|||
extents = (1, 1, 10, 10)
|
||||
# works prepatch
|
||||
with Image.open(test_img) as img:
|
||||
img2 = img.crop(extents)
|
||||
img2.load()
|
||||
img1 = img.crop(extents)
|
||||
img1.load()
|
||||
|
||||
# fail prepatch
|
||||
with Image.open(test_img) as img:
|
||||
img = img.crop(extents)
|
||||
img.load()
|
||||
img2 = img.crop(extents)
|
||||
img2.load()
|
||||
|
||||
|
||||
def test_crop_zero() -> None:
|
||||
|
|
|
@ -38,6 +38,7 @@ def test_close_after_load(caplog: pytest.LogCaptureFixture) -> None:
|
|||
def test_contextmanager() -> None:
|
||||
fn = None
|
||||
with Image.open("Tests/images/hopper.gif") as im:
|
||||
assert im.fp is not None
|
||||
fn = im.fp.fileno()
|
||||
os.fstat(fn)
|
||||
|
||||
|
|
|
@ -62,6 +62,7 @@ def test_putpalette_with_alpha_values() -> None:
|
|||
expected = im.convert("RGBA")
|
||||
|
||||
palette = im.getpalette()
|
||||
assert palette is not None
|
||||
transparency = im.info.pop("transparency")
|
||||
|
||||
palette_with_alpha_values = []
|
||||
|
|
|
@ -56,20 +56,21 @@ def test_rgba_quantize() -> None:
|
|||
|
||||
def test_quantize() -> None:
|
||||
with Image.open("Tests/images/caption_6_33_22.png") as image:
|
||||
image = image.convert("RGB")
|
||||
converted = image.quantize()
|
||||
converted = image.convert("RGB")
|
||||
converted = converted.quantize()
|
||||
assert converted.mode == "P"
|
||||
assert_image_similar(converted.convert("RGB"), image, 1)
|
||||
|
||||
|
||||
def test_quantize_no_dither() -> None:
|
||||
image = hopper()
|
||||
palette: Image.Image
|
||||
with Image.open("Tests/images/caption_6_33_22.png") as palette:
|
||||
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.palette is not None
|
||||
assert palette.palette is not None
|
||||
assert converted.palette.palette == palette.palette.palette
|
||||
|
||||
|
||||
|
@ -93,8 +94,8 @@ def test_quantize_no_dither2() -> None:
|
|||
|
||||
def test_quantize_dither_diff() -> None:
|
||||
image = hopper()
|
||||
with Image.open("Tests/images/caption_6_33_22.png") as palette:
|
||||
palette = palette.convert("P")
|
||||
with Image.open("Tests/images/caption_6_33_22.png") as im:
|
||||
palette = im.convert("P")
|
||||
|
||||
dither = image.quantize(dither=Image.Dither.FLOYDSTEINBERG, palette=palette)
|
||||
nodither = image.quantize(dither=Image.Dither.NONE, palette=palette)
|
||||
|
|
|
@ -313,8 +313,8 @@ class TestImageResize:
|
|||
|
||||
@skip_unless_feature("libtiff")
|
||||
def test_transposed(self) -> None:
|
||||
with Image.open("Tests/images/g4_orientation_5.tif") as im:
|
||||
im = im.resize((64, 64))
|
||||
with Image.open("Tests/images/g4_orientation_5.tif") as img:
|
||||
im = img.resize((64, 64))
|
||||
assert im.size == (64, 64)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
|
|
@ -40,8 +40,8 @@ def test_mode(mode: str) -> None:
|
|||
|
||||
@pytest.mark.parametrize("angle", (0, 90, 180, 270))
|
||||
def test_angle(angle: int) -> None:
|
||||
with Image.open("Tests/images/test-card.png") as im:
|
||||
rotate(im, im.mode, angle)
|
||||
with Image.open("Tests/images/test-card.png") as img:
|
||||
rotate(img, img.mode, angle)
|
||||
|
||||
im = hopper()
|
||||
assert_image_equal(im.rotate(angle), im.rotate(angle, expand=1))
|
||||
|
@ -74,6 +74,7 @@ def test_center_0() -> None:
|
|||
im = hopper()
|
||||
im = im.rotate(45, center=(0, 0), resample=Image.Resampling.BICUBIC)
|
||||
|
||||
target: Image.Image
|
||||
with Image.open("Tests/images/hopper_45.png") as target:
|
||||
target_origin = target.size[1] / 2
|
||||
target = target.crop((0, target_origin, 128, target_origin + 128))
|
||||
|
@ -85,6 +86,7 @@ def test_center_14() -> None:
|
|||
im = hopper()
|
||||
im = im.rotate(45, center=(14, 14), resample=Image.Resampling.BICUBIC)
|
||||
|
||||
target: Image.Image
|
||||
with Image.open("Tests/images/hopper_45.png") as target:
|
||||
target_origin = target.size[1] / 2 - 14
|
||||
target = target.crop((6, target_origin, 128 + 6, target_origin + 128))
|
||||
|
@ -94,6 +96,8 @@ def test_center_14() -> None:
|
|||
|
||||
def test_translate() -> None:
|
||||
im = hopper()
|
||||
|
||||
target: Image.Image
|
||||
with Image.open("Tests/images/hopper_45.png") as target:
|
||||
target_origin = (target.size[1] / 2 - 64) - 5
|
||||
target = target.crop(
|
||||
|
|
|
@ -156,6 +156,7 @@ def test_reducing_gap_values() -> None:
|
|||
|
||||
|
||||
def test_reducing_gap_for_DCT_scaling() -> None:
|
||||
ref: Image.Image
|
||||
with Image.open("Tests/images/hopper.jpg") as ref:
|
||||
# thumbnail should call draft with reducing_gap scale
|
||||
ref.draft(None, (18 * 3, 18 * 3))
|
||||
|
|
|
@ -48,6 +48,7 @@ class TestImageTransform:
|
|||
im.size, Image.Transform.AFFINE, [1, 0, 0, 0, 1, 0]
|
||||
)
|
||||
assert im.palette is not None
|
||||
assert transformed.palette is not None
|
||||
assert im.palette.palette == transformed.palette.palette
|
||||
|
||||
def test_extent(self) -> None:
|
||||
|
@ -246,14 +247,14 @@ class TestImageTransform:
|
|||
def test_missing_method_data(self) -> None:
|
||||
with hopper() as im:
|
||||
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"))
|
||||
def test_unknown_resampling_filter(self, resample: Image.Resampling | str) -> None:
|
||||
with hopper() as im:
|
||||
(w, h) = im.size
|
||||
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:
|
||||
|
|
|
@ -194,6 +194,7 @@ def test_bitmap() -> None:
|
|||
# Arrange
|
||||
im = Image.new("RGB", (W, H))
|
||||
draw = ImageDraw.Draw(im)
|
||||
small: Image.Image
|
||||
with Image.open("Tests/images/pil123rgba.png") as small:
|
||||
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
|
||||
|
||||
# Open test image (256px by 10px, black to white)
|
||||
with Image.open("Tests/images/bw_gradient.png") as im:
|
||||
im = im.convert("L")
|
||||
with Image.open("Tests/images/bw_gradient.png") as img:
|
||||
im = img.convert("L")
|
||||
|
||||
# Create image with original 2-color functionality
|
||||
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
|
||||
|
||||
# Open test image (256px by 10px, black to white)
|
||||
with Image.open("Tests/images/bw_gradient.png") as im:
|
||||
im = im.convert("L")
|
||||
with Image.open("Tests/images/bw_gradient.png") as img:
|
||||
im = img.convert("L")
|
||||
|
||||
# Create image with original 2-color functionality with offsets
|
||||
im_test = ImageOps.colorize(
|
||||
|
@ -327,8 +327,8 @@ def test_colorize_3color_offset() -> None:
|
|||
# Test the colorizing function with 3-color functionality and offset
|
||||
|
||||
# Open test image (256px by 10px, black to white)
|
||||
with Image.open("Tests/images/bw_gradient.png") as im:
|
||||
im = im.convert("L")
|
||||
with Image.open("Tests/images/bw_gradient.png") as img:
|
||||
im = img.convert("L")
|
||||
|
||||
# Create image with new three color functionality with offsets
|
||||
im_test = ImageOps.colorize(
|
||||
|
@ -422,6 +422,7 @@ def test_exif_transpose() -> None:
|
|||
check(orientation_im)
|
||||
|
||||
# Orientation from "XML:com.adobe.xmp" info key
|
||||
im: Image.Image
|
||||
for suffix in ("", "_exiftool"):
|
||||
with Image.open("Tests/images/xmp_tags_orientation" + suffix + ".png") as im:
|
||||
assert im.getexif()[0x0112] == 3
|
||||
|
|
|
@ -76,9 +76,14 @@ def test_consecutive() -> None:
|
|||
def test_palette_mmap() -> None:
|
||||
# Using mmap in ImageFile can require to reload the palette.
|
||||
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)
|
||||
color2 = im.getpalette()[:3]
|
||||
|
||||
palette = im.getpalette()
|
||||
assert palette is not None
|
||||
color2 = palette[:3]
|
||||
assert color1 == color2
|
||||
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ def helper_pickle_file(
|
|||
tmp_path: Path, protocol: int, test_file: str, mode: str | None
|
||||
) -> None:
|
||||
# Arrange
|
||||
im: Image.Image
|
||||
with Image.open(test_file) as im:
|
||||
filename = tmp_path / "temp.pkl"
|
||||
if mode:
|
||||
|
@ -33,6 +34,7 @@ def helper_pickle_file(
|
|||
|
||||
|
||||
def helper_pickle_string(protocol: int, test_file: str, mode: str | None) -> None:
|
||||
im: Image.Image
|
||||
with Image.open(test_file) as im:
|
||||
if 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:
|
||||
# Arrange
|
||||
filename = tmp_path / "temp.pkl"
|
||||
with Image.open("Tests/images/hopper.jpg") as im:
|
||||
im = im.convert("PA")
|
||||
with Image.open("Tests/images/hopper.jpg") as img:
|
||||
im = img.convert("PA")
|
||||
|
||||
# Act / Assert
|
||||
for protocol in range(pickle.HIGHEST_PROTOCOL + 1):
|
||||
|
|
|
@ -50,11 +50,13 @@ class TestShellInjection:
|
|||
@pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available")
|
||||
def test_save_netpbm_filename_bmp_mode(self, tmp_path: Path) -> None:
|
||||
with Image.open(TEST_GIF) as im:
|
||||
im = im.convert("RGB")
|
||||
self.assert_save_filename_check(tmp_path, im, GifImagePlugin._save_netpbm)
|
||||
im_rgb = im.convert("RGB")
|
||||
self.assert_save_filename_check(
|
||||
tmp_path, im_rgb, GifImagePlugin._save_netpbm
|
||||
)
|
||||
|
||||
@pytest.mark.skipif(not netpbm_available(), reason="Netpbm not available")
|
||||
def test_save_netpbm_filename_l_mode(self, tmp_path: Path) -> None:
|
||||
with Image.open(TEST_GIF) as im:
|
||||
im = im.convert("L")
|
||||
self.assert_save_filename_check(tmp_path, im, GifImagePlugin._save_netpbm)
|
||||
im_l = im.convert("L")
|
||||
self.assert_save_filename_check(tmp_path, im_l, GifImagePlugin._save_netpbm)
|
||||
|
|
|
@ -213,6 +213,7 @@ class DdsImageFile(ImageFile.ImageFile):
|
|||
format_description = "DirectDraw Surface"
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
if not _accept(self.fp.read(4)):
|
||||
msg = "not a DDS file"
|
||||
raise SyntaxError(msg)
|
||||
|
|
|
@ -175,6 +175,7 @@ testpaths = [
|
|||
python_version = "3.9"
|
||||
pretty = true
|
||||
disallow_any_generics = true
|
||||
disallow_untyped_defs = true
|
||||
enable_error_code = "ignore-without-code"
|
||||
extra_checks = true
|
||||
follow_imports = "silent"
|
||||
|
|
|
@ -78,6 +78,8 @@ class AvifImageFile(ImageFile.ImageFile):
|
|||
):
|
||||
msg = "Invalid opening codec"
|
||||
raise ValueError(msg)
|
||||
|
||||
assert self.fp is not None
|
||||
self._decoder = _avif.AvifDecoder(
|
||||
self.fp.read(),
|
||||
DECODE_CODEC_CHOICE,
|
||||
|
|
|
@ -258,6 +258,7 @@ class BlpImageFile(ImageFile.ImageFile):
|
|||
format_description = "Blizzard Mipmap Format"
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
self.magic = self.fp.read(4)
|
||||
if not _accept(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:
|
||||
"""Read relevant info about the BMP"""
|
||||
assert self.fp is not None
|
||||
read, seek = self.fp.read, self.fp.seek
|
||||
if header:
|
||||
seek(header)
|
||||
|
@ -311,6 +312,7 @@ class BmpImageFile(ImageFile.ImageFile):
|
|||
def _open(self) -> None:
|
||||
"""Open file, check magic number and read header"""
|
||||
# read 14 bytes: magic number, filesize, reserved, header final offset
|
||||
assert self.fp is not None
|
||||
head_data = self.fp.read(14)
|
||||
# choke if the file does not have the required magic bytes
|
||||
if not _accept(head_data):
|
||||
|
|
|
@ -41,6 +41,7 @@ class BufrStubImageFile(ImageFile.StubImageFile):
|
|||
format_description = "BUFR"
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
if not _accept(self.fp.read(4)):
|
||||
msg = "Not a BUFR file"
|
||||
raise SyntaxError(msg)
|
||||
|
|
|
@ -38,6 +38,7 @@ class CurImageFile(BmpImagePlugin.BmpImageFile):
|
|||
format_description = "Windows Cursor"
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
offset = self.fp.tell()
|
||||
|
||||
# check magic
|
||||
|
|
|
@ -45,6 +45,7 @@ class DcxImageFile(PcxImageFile):
|
|||
|
||||
def _open(self) -> None:
|
||||
# Header
|
||||
assert self.fp is not None
|
||||
s = self.fp.read(4)
|
||||
if not _accept(s):
|
||||
msg = "not a DCX file"
|
||||
|
|
|
@ -333,6 +333,7 @@ class DdsImageFile(ImageFile.ImageFile):
|
|||
format_description = "DirectDraw Surface"
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
if not _accept(self.fp.read(4)):
|
||||
msg = "not a DDS file"
|
||||
raise SyntaxError(msg)
|
||||
|
|
|
@ -189,6 +189,7 @@ class EpsImageFile(ImageFile.ImageFile):
|
|||
mode_map = {1: "L", 2: "LAB", 3: "RGB", 4: "CMYK"}
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
(length, offset) = self._find_offset(self.fp)
|
||||
|
||||
# go to offset - start of "%!PS"
|
||||
|
@ -400,6 +401,7 @@ class EpsImageFile(ImageFile.ImageFile):
|
|||
) -> Image.core.PixelAccess | None:
|
||||
# Load EPS via Ghostscript
|
||||
if self.tile:
|
||||
assert self.fp is not None
|
||||
self.im = Ghostscript(self.tile, self.size, self.fp, scale, transparency)
|
||||
self._mode = self.im.mode
|
||||
self._size = self.im.size
|
||||
|
|
|
@ -48,6 +48,7 @@ class FliImageFile(ImageFile.ImageFile):
|
|||
|
||||
def _open(self) -> None:
|
||||
# HEAD
|
||||
assert self.fp is not None
|
||||
s = self.fp.read(128)
|
||||
if not (_accept(s) and s[20:22] == b"\x00\x00"):
|
||||
msg = "not an FLI/FLC file"
|
||||
|
@ -111,6 +112,7 @@ class FliImageFile(ImageFile.ImageFile):
|
|||
# load palette
|
||||
|
||||
i = 0
|
||||
assert self.fp is not None
|
||||
for e in range(i16(self.fp.read(2))):
|
||||
s = self.fp.read(2)
|
||||
i = i + s[0]
|
||||
|
|
|
@ -58,6 +58,7 @@ class FpxImageFile(ImageFile.ImageFile):
|
|||
# read the OLE directory and see if this is a likely
|
||||
# to be a FlashPix file
|
||||
|
||||
assert self.fp is not None
|
||||
try:
|
||||
self.ole = olefile.OleFileIO(self.fp)
|
||||
except OSError as e:
|
||||
|
@ -229,6 +230,7 @@ class FpxImageFile(ImageFile.ImageFile):
|
|||
if y >= ysize:
|
||||
break # isn't really required
|
||||
|
||||
assert self.fp is not None
|
||||
self.stream = stream
|
||||
self._fp = self.fp
|
||||
self.fp = None
|
||||
|
|
|
@ -72,6 +72,7 @@ class FtexImageFile(ImageFile.ImageFile):
|
|||
format_description = "Texture File Format (IW2:EOC)"
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
if not _accept(self.fp.read(4)):
|
||||
msg = "not an FTEX file"
|
||||
raise SyntaxError(msg)
|
||||
|
|
|
@ -42,6 +42,7 @@ class GbrImageFile(ImageFile.ImageFile):
|
|||
format_description = "GIMP brush file"
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
header_size = i32(self.fp.read(4))
|
||||
if header_size < 20:
|
||||
msg = "not a GIMP brush"
|
||||
|
@ -91,6 +92,8 @@ class GbrImageFile(ImageFile.ImageFile):
|
|||
def load(self) -> Image.core.PixelAccess | None:
|
||||
if self._im is None:
|
||||
self.im = Image.core.new(self.mode, self.size)
|
||||
|
||||
assert self.fp is not None
|
||||
self.frombytes(self.fp.read(self._data_size))
|
||||
return Image.Image.load(self)
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ import os
|
|||
import subprocess
|
||||
from enum import IntEnum
|
||||
from functools import cached_property
|
||||
from typing import IO, Any, Literal, NamedTuple, Union
|
||||
from typing import IO, Any, Literal, NamedTuple, Union, cast
|
||||
|
||||
from . import (
|
||||
Image,
|
||||
|
@ -85,6 +85,7 @@ class GifImageFile(ImageFile.ImageFile):
|
|||
global_palette = None
|
||||
|
||||
def data(self) -> bytes | None:
|
||||
assert self.fp is not None
|
||||
s = self.fp.read(1)
|
||||
if s and s[0]:
|
||||
return self.fp.read(s[0])
|
||||
|
@ -98,6 +99,7 @@ class GifImageFile(ImageFile.ImageFile):
|
|||
|
||||
def _open(self) -> None:
|
||||
# Screen
|
||||
assert self.fp is not None
|
||||
s = self.fp.read(13)
|
||||
if not _accept(s):
|
||||
msg = "not a GIF file"
|
||||
|
@ -114,8 +116,8 @@ class GifImageFile(ImageFile.ImageFile):
|
|||
# check if palette contains colour indices
|
||||
p = self.fp.read(3 << bits)
|
||||
if self._is_palette_needed(p):
|
||||
p = ImagePalette.raw("RGB", p)
|
||||
self.global_palette = self.palette = p
|
||||
palette = ImagePalette.raw("RGB", p)
|
||||
self.global_palette = self.palette = palette
|
||||
|
||||
self._fp = self.fp # FIXME: hack
|
||||
self.__rewind = self.fp.tell()
|
||||
|
@ -254,7 +256,7 @@ class GifImageFile(ImageFile.ImageFile):
|
|||
info["comment"] += b"\n" + comment
|
||||
else:
|
||||
info["comment"] = comment
|
||||
s = None
|
||||
s = b""
|
||||
continue
|
||||
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]
|
||||
self.__offset = self.fp.tell()
|
||||
break
|
||||
s = None
|
||||
s = b""
|
||||
|
||||
if interlace is None:
|
||||
msg = "image not found in GIF frame"
|
||||
|
@ -350,7 +352,10 @@ class GifImageFile(ImageFile.ImageFile):
|
|||
if self._frame_palette:
|
||||
if color * 3 + 3 > len(self._frame_palette.palette):
|
||||
color = 0
|
||||
return tuple(self._frame_palette.palette[color * 3 : color * 3 + 3])
|
||||
return cast(
|
||||
tuple[int, int, int],
|
||||
tuple(self._frame_palette.palette[color * 3 : color * 3 + 3]),
|
||||
)
|
||||
else:
|
||||
return (color, color, color)
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ class GribStubImageFile(ImageFile.StubImageFile):
|
|||
format_description = "GRIB"
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
if not _accept(self.fp.read(8)):
|
||||
msg = "Not a GRIB file"
|
||||
raise SyntaxError(msg)
|
||||
|
|
|
@ -41,6 +41,7 @@ class HDF5StubImageFile(ImageFile.StubImageFile):
|
|||
format_description = "HDF5"
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
if not _accept(self.fp.read(8)):
|
||||
msg = "Not an HDF file"
|
||||
raise SyntaxError(msg)
|
||||
|
|
|
@ -266,6 +266,7 @@ class IcnsImageFile(ImageFile.ImageFile):
|
|||
format_description = "Mac OS icns resource"
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
self.icns = IcnsFile(self.fp)
|
||||
self._mode = "RGBA"
|
||||
self.info["sizes"] = self.icns.itersizes()
|
||||
|
|
|
@ -326,6 +326,7 @@ class IcoImageFile(ImageFile.ImageFile):
|
|||
format_description = "Windows Icon"
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
self.ico = IcoFile(self.fp)
|
||||
self.info["sizes"] = self.ico.sizes()
|
||||
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
|
||||
# 100 bytes, this is (probably) not a text header.
|
||||
|
||||
assert self.fp is not None
|
||||
if b"\n" not in self.fp.read(100):
|
||||
msg = "not an IM file"
|
||||
raise SyntaxError(msg)
|
||||
|
@ -304,6 +305,8 @@ class ImImageFile(ImageFile.ImageFile):
|
|||
size = ((self.size[0] * bits + 7) // 8) * self.size[1]
|
||||
offs = self.__offset + frame * size
|
||||
|
||||
if isinstance(self._fp, DeferredError):
|
||||
raise self._fp.ex
|
||||
self.fp = self._fp
|
||||
|
||||
self.tile = [
|
||||
|
|
|
@ -601,16 +601,11 @@ class Image:
|
|||
return new
|
||||
|
||||
# Context manager support
|
||||
def __enter__(self):
|
||||
def __enter__(self) -> Image:
|
||||
return self
|
||||
|
||||
def __exit__(self, *args):
|
||||
from . import ImageFile
|
||||
|
||||
if isinstance(self, ImageFile.ImageFile):
|
||||
if getattr(self, "_exclusive_fp", False):
|
||||
self._close_fp()
|
||||
self.fp = None
|
||||
def __exit__(self, *args: object) -> None:
|
||||
pass
|
||||
|
||||
def close(self) -> None:
|
||||
"""
|
||||
|
@ -1528,10 +1523,14 @@ class Image:
|
|||
exif_info = bytes.fromhex(
|
||||
"".join(self.info["Raw profile type exif"].split("\n")[3:])
|
||||
)
|
||||
elif hasattr(self, "tag_v2"):
|
||||
self._exif.bigtiff = self.tag_v2._bigtiff
|
||||
self._exif.endian = self.tag_v2._endian
|
||||
self._exif.load_from_fp(self.fp, self.tag_v2._offset)
|
||||
else:
|
||||
from . import TiffImagePlugin
|
||||
|
||||
if isinstance(self, TiffImagePlugin.TiffImageFile):
|
||||
self._exif.bigtiff = self.tag_v2._bigtiff
|
||||
self._exif.endian = self.tag_v2._endian
|
||||
assert self.fp is not None
|
||||
self._exif.load_from_fp(self.fp, self.tag_v2._offset)
|
||||
if exif_info is not None:
|
||||
self._exif.load(exif_info)
|
||||
|
||||
|
|
|
@ -130,6 +130,8 @@ class ImageFile(Image.Image):
|
|||
self.decoderconfig: tuple[Any, ...] = ()
|
||||
self.decodermaxblock = MAXBLOCK
|
||||
|
||||
self.fp: IO[bytes] | None
|
||||
self._fp: IO[bytes] | DeferredError
|
||||
if is_path(fp):
|
||||
# filename
|
||||
self.fp = open(fp, "rb")
|
||||
|
@ -166,14 +168,23 @@ class ImageFile(Image.Image):
|
|||
def _open(self) -> None:
|
||||
pass
|
||||
|
||||
def _close_fp(self):
|
||||
if getattr(self, "_fp", False) and not isinstance(self._fp, DeferredError):
|
||||
# Context manager support
|
||||
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:
|
||||
self._fp.close()
|
||||
self._fp = DeferredError(ValueError("Operation on closed image"))
|
||||
if self.fp:
|
||||
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:
|
||||
"""
|
||||
Closes the file pointer, if possible.
|
||||
|
@ -265,7 +276,7 @@ class ImageFile(Image.Image):
|
|||
|
||||
# raise exception if something's wrong. must be called
|
||||
# directly after open, and closes file when finished.
|
||||
if self._exclusive_fp:
|
||||
if self._exclusive_fp and self.fp:
|
||||
self.fp.close()
|
||||
self.fp = None
|
||||
|
||||
|
@ -283,6 +294,7 @@ class ImageFile(Image.Image):
|
|||
self.map: mmap.mmap | None = None
|
||||
use_mmap = self.filename and len(self.tile) == 1
|
||||
|
||||
assert self.fp is not None
|
||||
readonly = 0
|
||||
|
||||
# look for read/seek overrides
|
||||
|
|
|
@ -77,6 +77,7 @@ class IptcImageFile(ImageFile.ImageFile):
|
|||
def field(self) -> tuple[tuple[int, int] | None, int]:
|
||||
#
|
||||
# get a IPTC field header
|
||||
assert self.fp is not None
|
||||
s = self.fp.read(5)
|
||||
if not s.strip(b"\x00"):
|
||||
return None, 0
|
||||
|
@ -104,6 +105,7 @@ class IptcImageFile(ImageFile.ImageFile):
|
|||
|
||||
def _open(self) -> None:
|
||||
# load descriptive fields
|
||||
assert self.fp is not None
|
||||
while True:
|
||||
offset = self.fp.tell()
|
||||
tag, size = self.field()
|
||||
|
@ -157,6 +159,7 @@ class IptcImageFile(ImageFile.ImageFile):
|
|||
|
||||
offset, compression = self.tile[0][2:]
|
||||
|
||||
assert self.fp is not None
|
||||
self.fp.seek(offset)
|
||||
|
||||
# Copy image data to temporary file
|
||||
|
@ -165,6 +168,7 @@ class IptcImageFile(ImageFile.ImageFile):
|
|||
# To simplify access to the extracted file,
|
||||
# prepend a PPM header
|
||||
o.write(b"P5\n%d %d\n255\n" % self.size)
|
||||
assert self.fp is not None
|
||||
while True:
|
||||
type, size = self.field()
|
||||
if type != (8, 10):
|
||||
|
|
|
@ -248,6 +248,7 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
|
|||
format_description = "JPEG 2000 (ISO 15444)"
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
sig = self.fp.read(4)
|
||||
if sig == b"\xff\x4f\xff\x51":
|
||||
self.codec = "j2k"
|
||||
|
@ -300,6 +301,7 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
|
|||
]
|
||||
|
||||
def _parse_comment(self) -> None:
|
||||
assert self.fp is not None
|
||||
while True:
|
||||
marker = self.fp.read(2)
|
||||
if not marker:
|
||||
|
|
|
@ -61,6 +61,7 @@ if TYPE_CHECKING:
|
|||
|
||||
|
||||
def Skip(self: JpegImageFile, marker: int) -> None:
|
||||
assert self.fp is not None
|
||||
n = i16(self.fp.read(2)) - 2
|
||||
ImageFile._safe_read(self.fp, n)
|
||||
|
||||
|
@ -70,6 +71,7 @@ def APP(self: JpegImageFile, marker: int) -> None:
|
|||
# Application marker. Store these in the APP dictionary.
|
||||
# Also look for well-known application markers.
|
||||
|
||||
assert self.fp is not None
|
||||
n = i16(self.fp.read(2)) - 2
|
||||
s = ImageFile._safe_read(self.fp, n)
|
||||
|
||||
|
@ -174,6 +176,7 @@ def APP(self: JpegImageFile, marker: int) -> None:
|
|||
def COM(self: JpegImageFile, marker: int) -> None:
|
||||
#
|
||||
# Comment marker. Store these in the APP dictionary.
|
||||
assert self.fp is not None
|
||||
n = i16(self.fp.read(2)) - 2
|
||||
s = ImageFile._safe_read(self.fp, n)
|
||||
|
||||
|
@ -190,6 +193,7 @@ def SOF(self: JpegImageFile, marker: int) -> None:
|
|||
# mode. Note that this could be made a bit brighter, by
|
||||
# looking for JFIF and Adobe APP markers.
|
||||
|
||||
assert self.fp is not None
|
||||
n = i16(self.fp.read(2)) - 2
|
||||
s = ImageFile._safe_read(self.fp, n)
|
||||
self._size = i16(s, 3), i16(s, 1)
|
||||
|
@ -238,6 +242,7 @@ def DQT(self: JpegImageFile, marker: int) -> None:
|
|||
# FIXME: The quantization tables can be used to estimate the
|
||||
# compression quality.
|
||||
|
||||
assert self.fp is not None
|
||||
n = i16(self.fp.read(2)) - 2
|
||||
s = ImageFile._safe_read(self.fp, n)
|
||||
while len(s):
|
||||
|
@ -338,6 +343,7 @@ class JpegImageFile(ImageFile.ImageFile):
|
|||
format_description = "JPEG (ISO 10918)"
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
s = self.fp.read(3)
|
||||
|
||||
if not _accept(s):
|
||||
|
@ -412,6 +418,7 @@ class JpegImageFile(ImageFile.ImageFile):
|
|||
For premature EOF and LOAD_TRUNCATED_IMAGES adds EOI marker
|
||||
so libjpeg can finish decoding
|
||||
"""
|
||||
assert self.fp is not None
|
||||
s = self.fp.read(read_bytes)
|
||||
|
||||
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.is_animated = self._n_frames > 1
|
||||
|
||||
assert self.fp is not None
|
||||
self.__fp = self.fp
|
||||
self.seek(0)
|
||||
|
||||
|
|
|
@ -99,6 +99,7 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile):
|
|||
_close_exclusive_fp_after_loading = False
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
self.fp.seek(0) # prep the fp in order to pass the JPEG test
|
||||
JpegImagePlugin.JpegImageFile._open(self)
|
||||
self._after_jpeg_open()
|
||||
|
@ -118,6 +119,7 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile):
|
|||
assert self.n_frames == len(self.__mpoffsets)
|
||||
del self.info["mpoffset"] # no longer needed
|
||||
self.is_animated = self.n_frames > 1
|
||||
assert self.fp is not None
|
||||
self._fp = self.fp # FIXME: hack
|
||||
self._fp.seek(self.__mpoffsets[0]) # get ready to read first frame
|
||||
self.__frame = 0
|
||||
|
|
|
@ -754,6 +754,7 @@ class PngImageFile(ImageFile.ImageFile):
|
|||
format_description = "Portable network graphics"
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
if not _accept(self.fp.read(8)):
|
||||
msg = "not a PNG file"
|
||||
raise SyntaxError(msg)
|
||||
|
@ -985,6 +986,7 @@ class PngImageFile(ImageFile.ImageFile):
|
|||
"""internal: read more image data"""
|
||||
|
||||
assert self.png is not None
|
||||
assert self.fp is not None
|
||||
while self.__idat == 0:
|
||||
# end of chunk, skip forward to next one
|
||||
|
||||
|
@ -1018,6 +1020,7 @@ class PngImageFile(ImageFile.ImageFile):
|
|||
def load_end(self) -> None:
|
||||
"""internal: finished reading image data"""
|
||||
assert self.png is not None
|
||||
assert self.fp is not None
|
||||
if self.__idat != 0:
|
||||
self.fp.read(self.__idat)
|
||||
while True:
|
||||
|
|
|
@ -61,6 +61,7 @@ class PsdImageFile(ImageFile.ImageFile):
|
|||
_close_exclusive_fp_after_loading = False
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
read = self.fp.read
|
||||
|
||||
#
|
||||
|
@ -178,6 +179,8 @@ class PsdImageFile(ImageFile.ImageFile):
|
|||
self._mode = mode
|
||||
self.tile = tile
|
||||
self.frame = layer
|
||||
if isinstance(self._fp, DeferredError):
|
||||
raise self._fp.ex
|
||||
self.fp = self._fp
|
||||
|
||||
def tell(self) -> int:
|
||||
|
|
|
@ -22,6 +22,7 @@ class QoiImageFile(ImageFile.ImageFile):
|
|||
format_description = "Quite OK Image"
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
if not _accept(self.fp.read(4)):
|
||||
msg = "not a QOI file"
|
||||
raise SyntaxError(msg)
|
||||
|
|
|
@ -103,6 +103,7 @@ class SpiderImageFile(ImageFile.ImageFile):
|
|||
|
||||
def _open(self) -> None:
|
||||
# check header
|
||||
assert self.fp is not None
|
||||
n = 27 * 4 # read 27 float values
|
||||
f = self.fp.read(n)
|
||||
|
||||
|
@ -184,6 +185,9 @@ class SpiderImageFile(ImageFile.ImageFile):
|
|||
if isinstance(self._fp, DeferredError):
|
||||
raise self._fp.ex
|
||||
self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes)
|
||||
|
||||
if isinstance(self._fp, DeferredError):
|
||||
raise self._fp.ex
|
||||
self.fp = self._fp
|
||||
self.fp.seek(self.stkoffset)
|
||||
self._open()
|
||||
|
@ -323,9 +327,9 @@ if __name__ == "__main__":
|
|||
outfile = sys.argv[2]
|
||||
|
||||
# perform some image operation
|
||||
im = im.transpose(Image.Transpose.FLIP_LEFT_RIGHT)
|
||||
transposed_im = im.transpose(Image.Transpose.FLIP_LEFT_RIGHT)
|
||||
print(
|
||||
f"saving a flipped version of {os.path.basename(filename)} "
|
||||
f"as {outfile} "
|
||||
)
|
||||
im.save(outfile, SpiderImageFile.format)
|
||||
transposed_im.save(outfile, SpiderImageFile.format)
|
||||
|
|
|
@ -1179,6 +1179,7 @@ class TiffImageFile(ImageFile.ImageFile):
|
|||
"""Open the first image in a TIFF file"""
|
||||
|
||||
# Header
|
||||
assert self.fp is not None
|
||||
ifh = self.fp.read(8)
|
||||
if ifh[2] == 43:
|
||||
ifh += self.fp.read(8)
|
||||
|
@ -1341,6 +1342,7 @@ class TiffImageFile(ImageFile.ImageFile):
|
|||
# To be nice on memory footprint, if there's a
|
||||
# file descriptor, use that instead of reading
|
||||
# into a string in python.
|
||||
assert self.fp is not None
|
||||
try:
|
||||
fp = hasattr(self.fp, "fileno") and self.fp.fileno()
|
||||
# flush the file descriptor, prevents error on pypy 2.4+
|
||||
|
|
|
@ -39,6 +39,7 @@ class WalImageFile(ImageFile.ImageFile):
|
|||
self._mode = "P"
|
||||
|
||||
# read header fields
|
||||
assert self.fp is not None
|
||||
header = self.fp.read(32 + 24 + 32 + 12)
|
||||
self._size = i32(header, 32), i32(header, 36)
|
||||
Image._decompression_bomb_check(self.size)
|
||||
|
@ -55,6 +56,7 @@ class WalImageFile(ImageFile.ImageFile):
|
|||
|
||||
def load(self) -> Image.core.PixelAccess | None:
|
||||
if self._im is None:
|
||||
assert self.fp is not None
|
||||
self.im = Image.core.new(self.mode, self.size)
|
||||
self.frombytes(self.fp.read(self.size[0] * self.size[1]))
|
||||
self.putpalette(quake2palette)
|
||||
|
|
|
@ -43,6 +43,7 @@ class WebPImageFile(ImageFile.ImageFile):
|
|||
def _open(self) -> None:
|
||||
# Use the newer AnimDecoder API to parse the (possibly) animated file,
|
||||
# and access muxed chunks like ICC/EXIF/XMP.
|
||||
assert self.fp is not None
|
||||
self._decoder = _webp.WebPAnimDecoder(self.fp.read())
|
||||
|
||||
# Get info from decoder
|
||||
|
|
|
@ -49,6 +49,7 @@ if hasattr(Image.core, "drawwmf"):
|
|||
self.bbox = im.info["wmf_bbox"]
|
||||
|
||||
def load(self, im: ImageFile.StubImageFile) -> Image.Image:
|
||||
assert im.fp is not None
|
||||
im.fp.seek(0) # rewind
|
||||
return Image.frombytes(
|
||||
"RGB",
|
||||
|
@ -81,6 +82,7 @@ class WmfStubImageFile(ImageFile.StubImageFile):
|
|||
|
||||
def _open(self) -> None:
|
||||
# check placable header
|
||||
assert self.fp is not None
|
||||
s = self.fp.read(80)
|
||||
|
||||
if s.startswith(b"\xd7\xcd\xc6\x9a\x00\x00"):
|
||||
|
|
|
@ -58,15 +58,15 @@ class XVThumbImageFile(ImageFile.ImageFile):
|
|||
|
||||
# skip info comments
|
||||
while True:
|
||||
s = self.fp.readline()
|
||||
if not s:
|
||||
line = self.fp.readline()
|
||||
if not line:
|
||||
msg = "Unexpected EOF reading XV thumbnail file"
|
||||
raise SyntaxError(msg)
|
||||
if s[0] != 35: # ie. when not a comment: '#'
|
||||
if line[0] != 35: # ie. when not a comment: '#'
|
||||
break
|
||||
|
||||
# parse header line (already read)
|
||||
s = s.strip().split()
|
||||
s = line.strip().split()
|
||||
|
||||
self._mode = "P"
|
||||
self._size = int(s[0]), int(s[1])
|
||||
|
|
|
@ -37,17 +37,18 @@ class XpmImageFile(ImageFile.ImageFile):
|
|||
format_description = "X11 Pixel Map"
|
||||
|
||||
def _open(self) -> None:
|
||||
assert self.fp is not None
|
||||
if not _accept(self.fp.read(9)):
|
||||
msg = "not an XPM file"
|
||||
raise SyntaxError(msg)
|
||||
|
||||
# skip forward to next string
|
||||
while True:
|
||||
s = self.fp.readline()
|
||||
if not s:
|
||||
line = self.fp.readline()
|
||||
if not line:
|
||||
msg = "broken XPM file"
|
||||
raise SyntaxError(msg)
|
||||
m = xpm_head.match(s)
|
||||
m = xpm_head.match(line)
|
||||
if m:
|
||||
break
|
||||
|
||||
|
@ -66,14 +67,14 @@ class XpmImageFile(ImageFile.ImageFile):
|
|||
palette = [b"\0\0\0"] * 256
|
||||
|
||||
for _ in range(pal):
|
||||
s = self.fp.readline()
|
||||
if s.endswith(b"\r\n"):
|
||||
s = s[:-2]
|
||||
elif s.endswith((b"\r", b"\n")):
|
||||
s = s[:-1]
|
||||
line = self.fp.readline()
|
||||
if line.endswith(b"\r\n"):
|
||||
line = line[:-2]
|
||||
elif line.endswith((b"\r", b"\n")):
|
||||
line = line[:-1]
|
||||
|
||||
c = s[1]
|
||||
s = s[2:-2].split()
|
||||
c = line[1]
|
||||
s = line[2:-2].split()
|
||||
|
||||
for i in range(0, len(s), 2):
|
||||
if s[i] == b"c":
|
||||
|
@ -83,9 +84,11 @@ class XpmImageFile(ImageFile.ImageFile):
|
|||
self.info["transparency"] = c
|
||||
elif rgb.startswith(b"#"):
|
||||
# FIXME: handle colour names (see ImagePalette.py)
|
||||
rgb = int(rgb[1:], 16)
|
||||
rgb_int = int(rgb[1:], 16)
|
||||
palette[c] = (
|
||||
o8((rgb >> 16) & 255) + o8((rgb >> 8) & 255) + o8(rgb & 255)
|
||||
o8((rgb_int >> 16) & 255)
|
||||
+ o8((rgb_int >> 8) & 255)
|
||||
+ o8(rgb_int & 255)
|
||||
)
|
||||
else:
|
||||
# unknown colour
|
||||
|
@ -109,6 +112,7 @@ class XpmImageFile(ImageFile.ImageFile):
|
|||
|
||||
xsize, ysize = self.size
|
||||
|
||||
assert self.fp is not None
|
||||
s = [self.fp.readline()[1 : xsize + 1].ljust(xsize) for i in range(ysize)]
|
||||
|
||||
return b"".join(s)
|
||||
|
|
Loading…
Reference in New Issue
Block a user