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:
Andrew Murray 2025-01-21 15:39:42 +11:00 committed by GitHub
parent ce6bf21f15
commit 4b29af49fd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 48 additions and 80 deletions

View File

@ -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

View File

@ -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

View File

@ -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]: