mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-02-05 22:20:54 +03:00
Skip building libavif on 32-bit Windows (#16)
* Corrected comment * Reduced difference * Generate rotated images * Build rav1e * Skip building libavif on 32-bit * Fixed building libavif on oss-fuzz * Removed unnecessary converts --------- Co-authored-by: Andrew Murray <radarhere@users.noreply.github.com>
This commit is contained in:
parent
ce6bf21f15
commit
4b29af49fd
2
.github/workflows/test-windows.yml
vendored
2
.github/workflows/test-windows.yml
vendored
|
@ -146,7 +146,7 @@ jobs:
|
|||
run: "& winbuild\\build\\build_dep_libpng.cmd"
|
||||
|
||||
- name: Build dependencies / libavif
|
||||
if: steps.build-cache.outputs.cache-hit != 'true'
|
||||
if: steps.build-cache.outputs.cache-hit != 'true' && matrix.architecture == 'x64'
|
||||
run: "& winbuild\\build\\build_dep_libavif.cmd"
|
||||
|
||||
# for FreeType WOFF2 font support
|
||||
|
|
53
.github/workflows/wheels-dependencies.sh
vendored
53
.github/workflows/wheels-dependencies.sh
vendored
|
@ -51,7 +51,6 @@ BZIP2_VERSION=1.0.8
|
|||
LIBXCB_VERSION=1.17.0
|
||||
BROTLI_VERSION=1.1.0
|
||||
LIBAVIF_VERSION=1.1.1
|
||||
RAV1E_VERSION=0.7.1
|
||||
|
||||
function build_pkg_config {
|
||||
if [ -e pkg-config-stamp ]; then return; fi
|
||||
|
@ -101,50 +100,22 @@ function build_harfbuzz {
|
|||
function build_libavif {
|
||||
if [ -e libavif-stamp ]; then return; fi
|
||||
|
||||
if [[ "$PLAT" == "aarch64" ]]; then
|
||||
# Once GitHub Actions supports aarch64 without emulation, this will no longer needed as building will be faster
|
||||
if [[ "$PLAT" == "aarch64" ]]; then
|
||||
suffix="aarch64"
|
||||
else
|
||||
suffix="generic"
|
||||
fi
|
||||
|
||||
curl -sLo - \
|
||||
https://github.com/xiph/rav1e/releases/download/v$RAV1E_VERSION/librav1e-$RAV1E_VERSION-linux-$suffix.tar.gz \
|
||||
| tar -C $BUILD_PREFIX -zxf -
|
||||
|
||||
# Force libavif to treat system rav1e as if it were local
|
||||
mkdir -p /tmp/cmake/Modules
|
||||
cat <<EOF > /tmp/cmake/Modules/Findrav1e.cmake
|
||||
add_library(rav1e::rav1e STATIC IMPORTED GLOBAL)
|
||||
set_target_properties(rav1e::rav1e PROPERTIES
|
||||
IMPORTED_LOCATION "$BUILD_PREFIX/lib/librav1e.a"
|
||||
AVIF_LOCAL ON
|
||||
INTERFACE_INCLUDE_DIRECTORIES "$BUILD_PREFIX/include/rav1e"
|
||||
)
|
||||
EOF
|
||||
|
||||
rav1e=SYSTEM
|
||||
else
|
||||
curl https://sh.rustup.rs -sSf | sh -s -- -y
|
||||
. "$HOME/.cargo/env"
|
||||
|
||||
if [ -z "$IS_ALPINE" ] && [ -z "$IS_MACOS" ]; then
|
||||
yum install -y perl
|
||||
if [[ "$MB_ML_VER" == 2014 ]]; then
|
||||
yum install -y perl-IPC-Cmd
|
||||
fi
|
||||
fi
|
||||
|
||||
rav1e=LOCAL
|
||||
fi
|
||||
|
||||
python3 -m pip install meson ninja
|
||||
|
||||
if [[ "$PLAT" == "x86_64" ]]; then
|
||||
if [[ "$PLAT" == "x86_64" ]] || [ -n "$SANITIZER" ]; then
|
||||
build_simple nasm 2.16.03 https://www.nasm.us/pub/nasm/releasebuilds/2.16.03
|
||||
fi
|
||||
|
||||
# For rav1e
|
||||
curl https://sh.rustup.rs -sSf | sh -s -- -y
|
||||
. "$HOME/.cargo/env"
|
||||
if [ -z "$IS_ALPINE" ] && [ -z "$SANITIZER" ] && [ -z "$IS_MACOS" ]; then
|
||||
yum install -y perl
|
||||
if [[ "$MB_ML_VER" == 2014 ]]; then
|
||||
yum install -y perl-IPC-Cmd
|
||||
fi
|
||||
fi
|
||||
|
||||
local out_dir=$(fetch_unpack https://github.com/AOMediaCodec/libavif/archive/refs/tags/v$LIBAVIF_VERSION.tar.gz libavif-$LIBAVIF_VERSION.tar.gz)
|
||||
(cd $out_dir \
|
||||
&& cmake \
|
||||
|
@ -154,7 +125,7 @@ EOF
|
|||
-DBUILD_SHARED_LIBS=OFF \
|
||||
-DAVIF_LIBSHARPYUV=LOCAL \
|
||||
-DAVIF_LIBYUV=LOCAL \
|
||||
-DAVIF_CODEC_RAV1E=$rav1e \
|
||||
-DAVIF_CODEC_RAV1E=LOCAL \
|
||||
-DAVIF_CODEC_AOM=LOCAL \
|
||||
-DAVIF_CODEC_DAV1D=LOCAL \
|
||||
-DAVIF_CODEC_SVT=LOCAL \
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 9.0 KiB |
Binary file not shown.
Before Width: | Height: | Size: 9.2 KiB |
Binary file not shown.
Before Width: | Height: | Size: 9.1 KiB |
|
@ -218,7 +218,7 @@ class TestFileAvif:
|
|||
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(3)])
|
||||
assert difference < 5
|
||||
assert difference <= 3
|
||||
|
||||
def test_save_single_frame(self, tmp_path: Path) -> None:
|
||||
temp_file = str(tmp_path / "temp.avif")
|
||||
|
@ -255,10 +255,10 @@ class TestFileAvif:
|
|||
|
||||
def test_save_icc_profile(self) -> None:
|
||||
with Image.open("Tests/images/avif/icc_profile_none.avif") as im:
|
||||
assert im.info.get("icc_profile") is None
|
||||
assert "icc_profile" not in im.info
|
||||
|
||||
with Image.open("Tests/images/avif/icc_profile.avif") as with_icc:
|
||||
expected_icc = with_icc.info.get("icc_profile")
|
||||
expected_icc = with_icc.info["icc_profile"]
|
||||
assert expected_icc is not None
|
||||
|
||||
im = roundtrip(im, icc_profile=expected_icc)
|
||||
|
@ -278,7 +278,7 @@ class TestFileAvif:
|
|||
|
||||
def test_roundtrip_no_icc_profile(self) -> None:
|
||||
with Image.open("Tests/images/avif/icc_profile_none.avif") as im:
|
||||
assert im.info.get("icc_profile") is None
|
||||
assert "icc_profile" not in im.info
|
||||
|
||||
im = roundtrip(im)
|
||||
assert "icc_profile" not in im.info
|
||||
|
@ -470,14 +470,14 @@ class TestFileAvif:
|
|||
|
||||
@skip_unless_avif_encoder("aom")
|
||||
@skip_unless_feature("avif")
|
||||
@pytest.mark.parametrize("val", [{"foo": "bar"}, 1234])
|
||||
@pytest.mark.parametrize("advanced", [{"foo": "bar"}, 1234])
|
||||
def test_encoder_advanced_codec_options_invalid(
|
||||
self, tmp_path: Path, val: dict[str, str] | int
|
||||
self, tmp_path: Path, advanced: dict[str, str] | int
|
||||
) -> None:
|
||||
with Image.open(TEST_AVIF_FILE) as im:
|
||||
test_file = str(tmp_path / "temp.avif")
|
||||
with pytest.raises(ValueError):
|
||||
im.save(test_file, codec="aom", advanced=val)
|
||||
im.save(test_file, codec="aom", advanced=advanced)
|
||||
|
||||
@skip_unless_avif_decoder("aom")
|
||||
@skip_unless_feature("avif")
|
||||
|
@ -545,20 +545,20 @@ class TestFileAvif:
|
|||
def test_decoder_codec_available_invalid(self) -> None:
|
||||
assert _avif.decoder_codec_available("foo") is False
|
||||
|
||||
def test_p_mode_transparency(self) -> None:
|
||||
def test_p_mode_transparency(self, tmp_path: Path) -> None:
|
||||
im = Image.new("P", size=(64, 64))
|
||||
draw = ImageDraw.Draw(im)
|
||||
draw.rectangle(xy=[(0, 0), (32, 32)], fill=255)
|
||||
draw.rectangle(xy=[(32, 32), (64, 64)], fill=255)
|
||||
|
||||
buf_png = BytesIO()
|
||||
im.save(buf_png, format="PNG", transparency=0)
|
||||
im_png = Image.open(buf_png)
|
||||
buf_out = BytesIO()
|
||||
im_png.save(buf_out, format="AVIF", quality=100)
|
||||
out_png = str(tmp_path / "temp.png")
|
||||
im.save(out_png, transparency=0)
|
||||
with Image.open(out_png) as im_png:
|
||||
out_avif = str(tmp_path / "temp.avif")
|
||||
im_png.save(out_avif, quality=100)
|
||||
|
||||
with Image.open(buf_out) as expected:
|
||||
assert_image_similar(im_png.convert("RGBA"), expected, 0.17)
|
||||
with Image.open(out_avif) as expected:
|
||||
assert_image_similar(im_png.convert("RGBA"), expected, 0.17)
|
||||
|
||||
def test_decoder_strict_flags(self) -> None:
|
||||
# This would fail if full avif strictFlags were enabled
|
||||
|
@ -566,27 +566,22 @@ class TestFileAvif:
|
|||
assert im.size == (480, 270)
|
||||
|
||||
@skip_unless_avif_encoder("aom")
|
||||
def test_aom_optimizations(self) -> None:
|
||||
im = hopper("RGB")
|
||||
buf = BytesIO()
|
||||
im.save(buf, format="AVIF", codec="aom", speed=1)
|
||||
def test_aom_optimizations(self, tmp_path: Path) -> None:
|
||||
test_file = str(tmp_path / "temp.avif")
|
||||
hopper().save(test_file, codec="aom", speed=1)
|
||||
|
||||
@skip_unless_avif_encoder("svt")
|
||||
def test_svt_optimizations(self) -> None:
|
||||
im = hopper("RGB")
|
||||
buf = BytesIO()
|
||||
im.save(buf, format="AVIF", codec="svt", speed=1)
|
||||
def test_svt_optimizations(self, tmp_path: Path) -> None:
|
||||
test_file = str(tmp_path / "temp.avif")
|
||||
hopper().save(test_file, codec="svt", speed=1)
|
||||
|
||||
|
||||
@skip_unless_feature("avif")
|
||||
class TestAvifAnimation:
|
||||
@contextmanager
|
||||
def star_frames(self) -> Generator[list[ImageFile.ImageFile], None, None]:
|
||||
with Image.open("Tests/images/avif/star.png") as f1:
|
||||
with Image.open("Tests/images/avif/star90.png") as f2:
|
||||
with Image.open("Tests/images/avif/star180.png") as f3:
|
||||
with Image.open("Tests/images/avif/star270.png") as f4:
|
||||
yield [f1, f2, f3, f4]
|
||||
with Image.open("Tests/images/avif/star.png") as f:
|
||||
yield [f, f.rotate(90), f.rotate(180), f.rotate(270)]
|
||||
|
||||
def test_n_frames(self) -> None:
|
||||
"""
|
||||
|
@ -602,10 +597,10 @@ class TestAvifAnimation:
|
|||
assert im.n_frames == 5
|
||||
assert im.is_animated
|
||||
|
||||
def test_write_animation_L(self, tmp_path: Path) -> None:
|
||||
def test_write_animation_P(self, tmp_path: Path) -> None:
|
||||
"""
|
||||
Convert an animated GIF to animated AVIF, then compare the frame
|
||||
count, and first and last frames to ensure they're visually similar.
|
||||
count, and first and second-to-last frames to ensure they're visually similar.
|
||||
"""
|
||||
|
||||
with Image.open("Tests/images/avif/star.gif") as orig:
|
||||
|
@ -616,15 +611,17 @@ class TestAvifAnimation:
|
|||
with Image.open(temp_file) as im:
|
||||
assert im.n_frames == orig.n_frames
|
||||
|
||||
# Compare first and second-to-last frames to the original animated GIF
|
||||
assert_image_similar(im.convert("RGB"), orig.convert("RGB"), 2.25)
|
||||
# Compare first frame in P mode to frame from original GIF
|
||||
assert_image_similar(im, orig.convert("RGBA"), 2)
|
||||
|
||||
# Compare second-to-last frame in RGBA mode to frame from original GIF
|
||||
orig.seek(orig.n_frames - 2)
|
||||
im.seek(im.n_frames - 2)
|
||||
assert_image_similar(im.convert("RGB"), orig.convert("RGB"), 2.54)
|
||||
assert_image_similar(im, orig, 2.54)
|
||||
|
||||
def test_write_animation_RGB(self, tmp_path: Path) -> None:
|
||||
def test_write_animation_RGBA(self, tmp_path: Path) -> None:
|
||||
"""
|
||||
Write an animated AVIF from RGB frames, and ensure the frames
|
||||
Write an animated AVIF from RGBA frames, and ensure the frames
|
||||
are visually similar to the originals.
|
||||
"""
|
||||
|
||||
|
@ -633,11 +630,11 @@ class TestAvifAnimation:
|
|||
assert im.n_frames == 4
|
||||
|
||||
# Compare first frame to original
|
||||
assert_image_similar(im, frame1.convert("RGBA"), 2.7)
|
||||
assert_image_similar(im, frame1, 2.7)
|
||||
|
||||
# Compare second frame to original
|
||||
im.seek(1)
|
||||
assert_image_similar(im, frame2.convert("RGBA"), 4.1)
|
||||
assert_image_similar(im, frame2, 4.1)
|
||||
|
||||
with self.star_frames() as frames:
|
||||
frame1 = frames[0]
|
||||
|
@ -646,7 +643,7 @@ class TestAvifAnimation:
|
|||
frames[0].copy().save(temp_file1, save_all=True, append_images=frames[1:])
|
||||
check(temp_file1)
|
||||
|
||||
# Tests appending using a generator
|
||||
# Test appending using a generator
|
||||
def imGenerator(
|
||||
ims: list[ImageFile.ImageFile],
|
||||
) -> Generator[ImageFile.ImageFile, None, None]:
|
||||
|
|
Loading…
Reference in New Issue
Block a user