Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 12 additions & 10 deletions Tests/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import pytest
from packaging.version import parse as parse_version

from PIL import Image, ImageFile, ImageMath, features
from PIL import Image, ImageChops, ImageFile, ImageMath, features

TYPE_CHECKING = False
if TYPE_CHECKING:
Expand Down Expand Up @@ -60,10 +60,8 @@ def convert_to_comparable(
) -> tuple[Image.Image, Image.Image]:
new_a, new_b = a, b
if a.mode == "P":
new_a = Image.new("L", a.size)
new_b = Image.new("L", b.size)
new_a.putdata(a.get_flattened_data())
new_b.putdata(b.get_flattened_data())
new_a = Image.frombytes("L", a.size, a.tobytes())
new_b = Image.frombytes("L", b.size, b.tobytes())
elif a.mode == "I;16":
new_a = a.convert("I")
new_b = b.convert("I")
Expand Down Expand Up @@ -124,12 +122,16 @@ def assert_image_similar(

a, b = convert_to_comparable(a, b)

diff = 0
for ach, bch in zip(a.split(), b.split()):
chdiff = ImageMath.lambda_eval(
lambda args: abs(args["a"] - args["b"]), a=ach, b=bch
if a.mode in ("I", "F"):
# ImageChops.difference only supports 8bpc images,
# so this needs to be done by hand.
diff_im = ImageMath.lambda_eval(
lambda args: abs(args["a"] - args["b"]), a=a, b=b
).convert("L")
diff += sum(i * num for i, num in enumerate(chdiff.histogram()))
else:
diff_im = ImageChops.difference(a, b)

diff = sum((i % 256) * count for i, count in enumerate(diff_im.histogram()))

ave_diff = diff / (a.size[0] * a.size[1])
try:
Expand Down
27 changes: 16 additions & 11 deletions Tests/test_file_apng.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

import pytest

from PIL import Image, ImageSequence, PngImagePlugin
from PIL import Image, ImageFile, ImageSequence, PngImagePlugin

from .helper import hopper

TYPE_CHECKING = False
if TYPE_CHECKING:
Expand Down Expand Up @@ -459,20 +461,23 @@ def test_apng_save_alpha(tmp_path: Path) -> None:
assert reloaded.getpixel((0, 0)) == (255, 0, 0, 127)


def test_apng_save_split_fdat(tmp_path: Path) -> None:
def test_apng_save_split_fdat(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
# test to make sure we do not generate sequence errors when writing
# frames with image data spanning multiple fdAT chunks (in this case
# both the default image and first animation frame will span multiple
# data chunks)
# data chunks). A small block size lets a small image satisfy that premise,
# rather than needing a large image and the time spent compressing it.
monkeypatch.setattr(ImageFile, "MAXBLOCK", 1024)
test_file = tmp_path / "temp.png"
with Image.open("Tests/images/old-style-jpeg-compression.png") as im:
frames = [im.copy(), Image.new("RGBA", im.size, (255, 0, 0, 255))]
im.save(
test_file,
save_all=True,
default_image=True,
append_images=frames,
)
im = hopper("RGBA")
frames = [im.copy(), Image.new("RGBA", im.size, (255, 0, 0, 255))]
im.save(
test_file,
save_all=True,
default_image=True,
append_images=frames,
)
assert test_file.read_bytes().count(b"fdAT") > 2

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know we have a difference of opinion on this, but I still don't see minor performance improvements in the test suite as a reason to start monkeypatching. I would rather test Pillow as it is with a real image.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pillow is not being monkeypatched. MAXBLOCK is a documented public API, this just sets it temporarily.

This is the same pattern as used in e.g. test_padded_idat in test_file_png.

with Image.open(test_file) as im:
assert isinstance(im, PngImagePlugin.PngImageFile)
im.seek(im.n_frames - 1)
Expand Down
60 changes: 0 additions & 60 deletions Tests/test_imagefile.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,82 +7,22 @@

from PIL import (
BmpImagePlugin,
EpsImagePlugin,
Image,
ImageFile,
UnidentifiedImageError,
_binary,
features,
)

from .helper import (
assert_image,
assert_image_equal,
assert_image_similar,
fromstring,
hopper,
skip_unless_feature,
tostring,
)

# save original block sizes
MAXBLOCK = ImageFile.MAXBLOCK
SAFEBLOCK = ImageFile.SAFEBLOCK


class TestImageFile:
def test_parser(self, monkeypatch: pytest.MonkeyPatch) -> None:
def roundtrip(format: str) -> tuple[Image.Image, Image.Image]:
im = hopper("L").resize((1000, 1000), Image.Resampling.NEAREST)
if format in ("MSP", "XBM"):
im = im.convert("1")

test_file = BytesIO()

im.copy().save(test_file, format)

data = test_file.getvalue()

parser = ImageFile.Parser()
parser.feed(data)
im_out = parser.close()

return im, im_out

assert_image_equal(*roundtrip("BMP"))
im1, im2 = roundtrip("GIF")
assert_image_similar(im1.convert("P"), im2, 1)
with pytest.warns(DeprecationWarning, match="IM image format"):
assert_image_equal(*roundtrip("IM"))
assert_image_equal(*roundtrip("MSP"))
if features.check("zlib"):
# force multiple blocks in PNG driver
monkeypatch.setattr(ImageFile, "MAXBLOCK", 8192)
assert_image_equal(*roundtrip("PNG"))
assert_image_equal(*roundtrip("PPM"))
assert_image_equal(*roundtrip("TIFF"))
assert_image_equal(*roundtrip("XBM"))
assert_image_equal(*roundtrip("TGA"))
assert_image_equal(*roundtrip("PCX"))

if EpsImagePlugin.has_ghostscript():
im1, im2 = roundtrip("EPS")
# This test fails on Ubuntu 12.04, PPC (Bigendian) It
# appears to be a ghostscript 9.05 bug, since the
# ghostscript rendering is wonky and the file is identical
# to that written on ubuntu 12.04 x64
# md5sum: ba974835ff2d6f3f2fd0053a23521d4a

# EPS comes back in RGB:
assert_image_similar(im1, im2.convert("L"), 20)

if features.check("jpg"):
im1, im2 = roundtrip("JPEG") # lossy compression
assert_image(im1, im2.mode, im2.size)

with pytest.raises(OSError):
roundtrip("PDF")

def test_ico(self) -> None:
with open("Tests/images/python.ico", "rb") as f:
data = f.read()
Expand Down
94 changes: 94 additions & 0 deletions Tests/test_imagefile_parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
from __future__ import annotations

from io import BytesIO

import pytest

from PIL import EpsImagePlugin, Image, ImageFile
from Tests.helper import (
assert_image,
assert_image_equal,
assert_image_similar,
hopper,
skip_unless_feature,
)


@pytest.fixture(scope="module")
def hopper_l_1k() -> Image.Image:
return hopper("L").resize((1000, 1000), Image.Resampling.NEAREST)


def roundtrip(im: Image.Image, format: str) -> tuple[Image.Image, Image.Image]:
if format in ("MSP", "XBM"):
im = im.convert("1")

test_file = BytesIO()

im.copy().save(test_file, format)

data = test_file.getvalue()

parser = ImageFile.Parser()
parser.feed(data)
im_out = parser.close()

return im, im_out


@pytest.mark.parametrize(
"format",
[
"BMP",
pytest.param(
"EPS",
marks=pytest.mark.skipif(
not EpsImagePlugin.has_ghostscript(),
reason="Ghostscript not available",
),
),
"GIF",
"IM",
pytest.param("JPEG", marks=skip_unless_feature("jpg")),
"MSP",
"PCX",
pytest.param("PNG", marks=skip_unless_feature("zlib")),
"PPM",
"TGA",
"TIFF",
"XBM",
],
)
def test_parser(
monkeypatch: pytest.MonkeyPatch, hopper_l_1k: Image.Image, format: str
) -> None:
# force multiple blocks in PNG driver
monkeypatch.setattr(ImageFile, "MAXBLOCK", 8192)

if format == "IM":
with pytest.warns(DeprecationWarning, match="IM image format"):
im1, im2 = roundtrip(hopper_l_1k, format)
else:
im1, im2 = roundtrip(hopper_l_1k, format)

if format == "GIF":
assert_image_similar(im1.convert("P"), im2, 1)
elif format == "EPS":
# This test fails on Ubuntu 12.04, PPC (Bigendian) It
# appears to be a ghostscript 9.05 bug, since the
# ghostscript rendering is wonky and the file is identical
# to that written on ubuntu 12.04 x64
# md5sum: ba974835ff2d6f3f2fd0053a23521d4a

# EPS comes back in RGB:
assert_image_similar(im1, im2.convert("L"), 20)
elif format == "JPEG": # Lossy compression
assert_image(im1, im2.mode, im2.size)
else:
assert_image_equal(im1, im2)


def test_parser_pdf_roundtrip_error(hopper_l_1k: Image.Image) -> None:
# See https://github.com/python-pillow/Pillow/issues/78
with pytest.raises(OSError, match="cannot parse this image"):
roundtrip(hopper_l_1k, "PDF")
Loading