Skip to content

Commit

Permalink
Simplified code (#3)
Browse files Browse the repository at this point in the history
* Removed unnecessary meson install

* Use the same Python as the build script

* Use python3

* Simplified code

* Updated meson

---------

Co-authored-by: Andrew Murray <[email protected]>
  • Loading branch information
radarhere and radarhere authored Oct 19, 2024
1 parent 8b8bbba commit 58ef692
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 106 deletions.
2 changes: 0 additions & 2 deletions .github/workflows/test-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,6 @@ jobs:
choco install nasm --no-progress
echo "C:\Program Files\NASM" >> $env:GITHUB_PATH
python -m pip install meson
choco install ghostscript --version=10.4.0 --no-progress
echo "C:\Program Files\gs\gs10.04.0\bin" >> $env:GITHUB_PATH
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/wheels-dependencies.sh
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ EOF

function build_libavif {
install_rav1e
python -m pip install meson ninja
python3 -m pip install meson ninja

if [[ "$PLAT" == "x86_64" ]]; then
build_simple nasm 2.16.03 https://www.nasm.us/pub/nasm/releasebuilds/2.16.03/
Expand Down
158 changes: 59 additions & 99 deletions Tests/test_file_avif.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import os
import re
import warnings
import xml.etree.ElementTree
from collections.abc import Generator
from contextlib import contextmanager
from io import BytesIO
Expand Down Expand Up @@ -43,25 +42,13 @@
TEST_AVIF_FILE = "Tests/images/avif/hopper.avif"


def assert_xmp_orientation(xmp: bytes | None, expected: int) -> None:
assert isinstance(xmp, bytes)
root = xml.etree.ElementTree.fromstring(xmp)
orientation = None
for elem in root.iter():
if elem.tag.endswith("}Description"):
tag_orientation = elem.attrib.get(
"{http://ns.adobe.com/tiff/1.0/}Orientation"
)
if tag_orientation:
orientation = int(tag_orientation)
break
assert orientation == expected
def assert_xmp_orientation(xmp: bytes, expected: int) -> None:
assert int(xmp.split(b'tiff:Orientation="')[1].split(b'"')[0]) == expected


def roundtrip(im: ImageFile.ImageFile, **options: Any) -> ImageFile.ImageFile:
out = BytesIO()
im.save(out, "AVIF", **options)
out.seek(0)
return Image.open(out)


Expand All @@ -82,7 +69,7 @@ def skip_unless_avif_encoder(codec_name: str) -> pytest.MarkDecorator:
def is_docker_qemu() -> bool:
try:
init_proc_exe = os.readlink("/proc/1/exe")
except: # noqa: E722
except (FileNotFoundError, PermissionError):
return False
else:
return "qemu" in init_proc_exe

Check warning on line 75 in Tests/test_file_avif.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_avif.py#L75

Added line #L75 was not covered by tests
Expand Down Expand Up @@ -125,18 +112,16 @@ class TestUnsupportedAvif:
def test_unsupported(self, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(AvifImagePlugin, "SUPPORTED", False)

file_path = "Tests/images/avif/hopper.avif"
pytest.warns(
UserWarning,
lambda: pytest.raises(UnidentifiedImageError, Image.open, file_path),
)
with pytest.warns(UserWarning):
with pytest.raises(UnidentifiedImageError):
with Image.open(TEST_AVIF_FILE):
pass

def test_unsupported_open(self, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(AvifImagePlugin, "SUPPORTED", False)

file_path = "Tests/images/avif/hopper.avif"
with pytest.raises(SyntaxError):
AvifImagePlugin.AvifImageFile(file_path)
AvifImagePlugin.AvifImageFile(TEST_AVIF_FILE)


@skip_unless_feature("avif")
Expand All @@ -154,12 +139,11 @@ def test_read(self) -> None:
Does it have the bits we expect?
"""

with Image.open("Tests/images/avif/hopper.avif") as image:
with Image.open(TEST_AVIF_FILE) as image:
assert image.mode == "RGB"
assert image.size == (128, 128)
assert image.format == "AVIF"
assert image.get_format_mimetype() == "image/avif"
image.load()
image.getdata()

# generated with:
Expand All @@ -176,7 +160,6 @@ def _roundtrip(self, tmp_path: Path, mode: str, epsilon: float) -> None:
assert image.mode == "RGB"
assert image.size == (128, 128)
assert image.format == "AVIF"
image.load()
image.getdata()

if mode == "RGB":
Expand Down Expand Up @@ -240,10 +223,10 @@ def finish(self) -> None:
im.save(test_file)

def test_no_resource_warning(self, tmp_path: Path) -> None:
with Image.open(TEST_AVIF_FILE) as image:
with Image.open(TEST_AVIF_FILE) as im:
temp_file = str(tmp_path / "temp.avif")
with warnings.catch_warnings():
image.save(temp_file)
im.save(temp_file)

@pytest.mark.parametrize("major_brand", [b"avif", b"avis", b"mif1", b"msf1"])
def test_accept_ftyp_brands(self, major_brand: bytes) -> None:
Expand Down Expand Up @@ -272,9 +255,7 @@ def test_background_from_gif(self, tmp_path: Path) -> None:

with Image.open(out_gif) as reread:
reread_value = reread.convert("RGB").getpixel((1, 1))
difference = sum(
[abs(original_value[i] - reread_value[i]) for i in range(0, 3)]
)
difference = sum([abs(original_value[i] - reread_value[i]) for i in range(3)])
assert difference < 5

def test_save_single_frame(self, tmp_path: Path) -> None:
Expand All @@ -296,7 +277,7 @@ def test_load_transparent_rgb(self) -> None:
assert_image(im, "RGBA", (64, 64))

# image has 876 transparent pixels
assert im.getchannel("A").getcolors()[0][0] == 876
assert im.getchannel("A").getcolors()[0] == (876, 0)

def test_save_transparent(self, tmp_path: Path) -> None:
im = Image.new("RGBA", (10, 10), (0, 0, 0, 0))
Expand All @@ -305,7 +286,7 @@ def test_save_transparent(self, tmp_path: Path) -> None:
test_file = str(tmp_path / "temp.avif")
im.save(test_file)

# check if saved image contains same transparency
# check if saved image contains the same transparency
with Image.open(test_file) as im:
assert_image(im, "RGBA", (10, 10))
assert im.getcolors() == [(100, (0, 0, 0, 0))]
Expand Down Expand Up @@ -346,7 +327,7 @@ def test_exif(self) -> None:
exif = im.getexif()
assert exif[274] == 1

def test_exif_save(self, tmp_path: Path) -> None:
def test_exif_save_default(self, tmp_path: Path) -> None:
with Image.open("Tests/images/avif/exif.avif") as im:
test_file = str(tmp_path / "temp.avif")
im.save(test_file)
Expand All @@ -355,24 +336,14 @@ def test_exif_save(self, tmp_path: Path) -> None:
exif = reloaded.getexif()
assert exif[274] == 1

def test_exif_obj_argument(self, tmp_path: Path) -> None:
exif = Image.Exif()
exif[274] = 1
exif_data = exif.tobytes()
with Image.open(TEST_AVIF_FILE) as im:
test_file = str(tmp_path / "temp.avif")
im.save(test_file, exif=exif)

with Image.open(test_file) as reloaded:
assert reloaded.info["exif"] == exif_data

def test_exif_bytes_argument(self, tmp_path: Path) -> None:
@pytest.mark.parametrize("bytes", [True, False])
def test_exif_save_argument(self, tmp_path: Path, bytes: bool) -> None:
exif = Image.Exif()
exif[274] = 1
exif_data = exif.tobytes()
with Image.open(TEST_AVIF_FILE) as im:
test_file = str(tmp_path / "temp.avif")
im.save(test_file, exif=exif_data)
im.save(test_file, exif=exif_data if bytes else exif)

with Image.open(test_file) as reloaded:
assert reloaded.info["exif"] == exif_data
Expand All @@ -385,7 +356,7 @@ def test_exif_invalid(self, tmp_path: Path) -> None:

def test_xmp(self) -> None:
with Image.open("Tests/images/avif/xmp_tags_orientation.avif") as im:
xmp = im.info.get("xmp")
xmp = im.info["xmp"]
assert_xmp_orientation(xmp, 3)

def test_xmp_save(self, tmp_path: Path) -> None:
Expand All @@ -394,7 +365,7 @@ def test_xmp_save(self, tmp_path: Path) -> None:
im.save(test_file)

with Image.open(test_file) as reloaded:
xmp = reloaded.info.get("xmp")
xmp = reloaded.info["xmp"]
assert_xmp_orientation(xmp, 3)

def test_xmp_save_from_png(self, tmp_path: Path) -> None:
Expand All @@ -403,7 +374,7 @@ def test_xmp_save_from_png(self, tmp_path: Path) -> None:
im.save(test_file)

with Image.open(test_file) as reloaded:
xmp = reloaded.info.get("xmp")
xmp = reloaded.info["xmp"]
assert_xmp_orientation(xmp, 3)

def test_xmp_save_argument(self, tmp_path: Path) -> None:
Expand All @@ -420,12 +391,12 @@ def test_xmp_save_argument(self, tmp_path: Path) -> None:
'<?xpacket end="r"?>',
]
)
with Image.open("Tests/images/avif/hopper.avif") as im:
with Image.open(TEST_AVIF_FILE) as im:
test_file = str(tmp_path / "temp.avif")
im.save(test_file, xmp=xmp_arg)

with Image.open(test_file) as reloaded:
xmp = reloaded.info.get("xmp")
xmp = reloaded.info["xmp"]
assert_xmp_orientation(xmp, 1)

def test_tell(self) -> None:
Expand Down Expand Up @@ -514,33 +485,29 @@ def test_encoder_advanced_codec_options_invalid(

@skip_unless_avif_decoder("aom")
@skip_unless_feature("avif")
def test_decoder_codec_param(self) -> None:
AvifImagePlugin.DECODE_CODEC_CHOICE = "aom"
try:
with Image.open(TEST_AVIF_FILE) as im:
assert im.size == (128, 128)
finally:
AvifImagePlugin.DECODE_CODEC_CHOICE = "auto"
def test_decoder_codec_param(self, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(AvifImagePlugin, "DECODE_CODEC_CHOICE", "aom")

with Image.open(TEST_AVIF_FILE) as im:
assert im.size == (128, 128)

@skip_unless_avif_encoder("rav1e")
@skip_unless_feature("avif")
def test_decoder_codec_cannot_decode(self, tmp_path: Path) -> None:
AvifImagePlugin.DECODE_CODEC_CHOICE = "rav1e"
try:
with pytest.raises(ValueError):
with Image.open(TEST_AVIF_FILE):
pass
finally:
AvifImagePlugin.DECODE_CODEC_CHOICE = "auto"
def test_decoder_codec_cannot_decode(
self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
monkeypatch.setattr(AvifImagePlugin, "DECODE_CODEC_CHOICE", "rav1e")

def test_decoder_codec_invalid(self) -> None:
AvifImagePlugin.DECODE_CODEC_CHOICE = "foo"
try:
with pytest.raises(ValueError):
with Image.open(TEST_AVIF_FILE):
pass
finally:
AvifImagePlugin.DECODE_CODEC_CHOICE = "auto"
with pytest.raises(ValueError):
with Image.open(TEST_AVIF_FILE):
pass

def test_decoder_codec_invalid(self, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(AvifImagePlugin, "DECODE_CODEC_CHOICE", "foo")

with pytest.raises(ValueError):
with Image.open(TEST_AVIF_FILE):
pass

@skip_unless_avif_encoder("aom")
@skip_unless_feature("avif")
Expand All @@ -560,7 +527,7 @@ def test_encoder_codec_available_invalid(self) -> None:
assert _avif.encoder_codec_available("foo") is False

def test_encoder_quality_valueerror(self, tmp_path: Path) -> None:
with Image.open("Tests/images/avif/hopper.avif") as im:
with Image.open(TEST_AVIF_FILE) as im:
test_file = str(tmp_path / "temp.avif")
with pytest.raises(ValueError):
im.save(test_file, quality="invalid")
Expand All @@ -583,22 +550,20 @@ def test_decoder_codec_available_invalid(self) -> None:
assert _avif.decoder_codec_available("foo") is False

@pytest.mark.parametrize("upsampling", ["fastest", "best", "nearest", "bilinear"])
def test_decoder_upsampling(self, upsampling: str) -> None:
AvifImagePlugin.CHROMA_UPSAMPLING = upsampling
try:
def test_decoder_upsampling(
self, monkeypatch: pytest.MonkeyPatch, upsampling: str
) -> None:
monkeypatch.setattr(AvifImagePlugin, "CHROMA_UPSAMPLING", upsampling)

with Image.open(TEST_AVIF_FILE):
pass

def test_decoder_upsampling_invalid(self, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(AvifImagePlugin, "CHROMA_UPSAMPLING", "foo")

with pytest.raises(ValueError):
with Image.open(TEST_AVIF_FILE):
pass
finally:
AvifImagePlugin.CHROMA_UPSAMPLING = "auto"

def test_decoder_upsampling_invalid(self) -> None:
AvifImagePlugin.CHROMA_UPSAMPLING = "foo"
try:
with pytest.raises(ValueError):
with Image.open(TEST_AVIF_FILE):
pass
finally:
AvifImagePlugin.CHROMA_UPSAMPLING = "auto"

def test_p_mode_transparency(self) -> None:
im = Image.new("P", size=(64, 64))
Expand All @@ -612,7 +577,8 @@ def test_p_mode_transparency(self) -> None:
buf_out = BytesIO()
im_png.save(buf_out, format="AVIF", quality=100)

assert_image_similar(im_png.convert("RGBA"), Image.open(buf_out), 1)
with Image.open(buf_out) as expected:
assert_image_similar(im_png.convert("RGBA"), expected, 1)

def test_decoder_strict_flags(self) -> None:
# This would fail if full avif strictFlags were enabled
Expand Down Expand Up @@ -648,7 +614,7 @@ def test_n_frames(self) -> None:
correctly.
"""

with Image.open("Tests/images/avif/hopper.avif") as im:
with Image.open(TEST_AVIF_FILE) as im:
assert im.n_frames == 1
assert not im.is_animated

Expand All @@ -671,13 +637,9 @@ def test_write_animation_L(self, tmp_path: Path) -> None:
assert im.n_frames == orig.n_frames

# Compare first and second-to-last frames to the original animated GIF
orig.load()
im.load()
assert_image_similar(im.convert("RGB"), orig.convert("RGB"), 25.0)
orig.seek(orig.n_frames - 2)
im.seek(im.n_frames - 2)
orig.load()
im.load()
assert_image_similar(im.convert("RGB"), orig.convert("RGB"), 25.0)

def test_write_animation_RGB(self, tmp_path: Path) -> None:
Expand All @@ -691,12 +653,10 @@ def check(temp_file: str) -> None:
assert im.n_frames == 4

# Compare first frame to original
im.load()
assert_image_similar(im, frame1.convert("RGBA"), 25.0)

# Compare second frame to original
im.seek(1)
im.load()
assert_image_similar(im, frame2.convert("RGBA"), 25.0)

with self.star_frames() as frames:
Expand Down Expand Up @@ -725,7 +685,7 @@ def test_sequence_dimension_mismatch_check(self, tmp_path: Path) -> None:
frame1 = Image.new("RGB", (100, 100))
frame2 = Image.new("RGB", (150, 150))
with pytest.raises(ValueError):
frame1.save(temp_file, save_all=True, append_images=[frame2], duration=100)
frame1.save(temp_file, save_all=True, append_images=[frame2])

def test_heif_raises_unidentified_image_error(self) -> None:
with pytest.raises(UnidentifiedImageError):
Expand Down
4 changes: 2 additions & 2 deletions src/PIL/AvifImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def _open(self) -> None:
self._size = width, height
self.n_frames = n_frames
self.is_animated = self.n_frames > 1
self._mode = self.rawmode = mode
self._mode = mode

if icc:
self.info["icc_profile"] = icc
Expand Down Expand Up @@ -103,7 +103,7 @@ def load(self) -> Image.core.PixelAccess | None:
if self.fp and self._exclusive_fp:
self.fp.close()
self.fp = BytesIO(data)
self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 0, self.rawmode)]
self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 0, self.mode)]

return super().load()

Expand Down
Loading

0 comments on commit 58ef692

Please sign in to comment.