Skip to content

Commit

Permalink
Add AVIF plugin (using libavif)
Browse files Browse the repository at this point in the history
  • Loading branch information
fdintino committed Sep 12, 2024
1 parent 3fd6b7c commit 0ef3c88
Show file tree
Hide file tree
Showing 44 changed files with 3,195 additions and 11 deletions.
6 changes: 5 additions & 1 deletion .ci/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ if [[ $(uname) != CYGWIN* ]]; then
sudo apt-get -qq install libfreetype6-dev liblcms2-dev python3-tk\
ghostscript libjpeg-turbo-progs libopenjp2-7-dev\
cmake meson imagemagick libharfbuzz-dev libfribidi-dev\
sway wl-clipboard libopenblas-dev
sway wl-clipboard libopenblas-dev\
ninja-build build-essential nasm
fi

python3 -m pip install --upgrade pip
Expand Down Expand Up @@ -62,6 +63,9 @@ if [[ $(uname) != CYGWIN* ]]; then
# raqm
pushd depends && ./install_raqm.sh && popd

# libavif
pushd depends && ./install_libavif.sh && popd

# extra test images
pushd depends && ./install_extra_test_images.sh && popd
else
Expand Down
9 changes: 8 additions & 1 deletion .github/workflows/macos-install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ brew install \
libtiff \
little-cms2 \
openjpeg \
webp
webp \
dav1d \
aom \
rav1e \
ninja
if [[ "$ImageOS" == "macos13" ]]; then
brew install --ignore-dependencies libraqm
else
Expand All @@ -31,5 +35,8 @@ python3 -m pip install -U pytest-timeout
python3 -m pip install pyroma
python3 -m pip install numpy

# libavif
pushd depends && ./install_libavif.sh && popd

# extra test images
pushd depends && ./install_extra_test_images.sh && popd
1 change: 1 addition & 0 deletions .github/workflows/test-mingw.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ jobs:
mingw-w64-x86_64-libimagequant \
mingw-w64-x86_64-libjpeg-turbo \
mingw-w64-x86_64-libraqm \
mingw-w64-x86_64-libavif \
mingw-w64-x86_64-libtiff \
mingw-w64-x86_64-libwebp \
mingw-w64-x86_64-openjpeg2 \
Expand Down
14 changes: 14 additions & 0 deletions .github/workflows/test-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ jobs:
choco install nasm --no-progress
echo "C:\Program Files\NASM" >> $env:GITHUB_PATH
python -m pip install meson
choco install ghostscript --version=10.3.1 --no-progress
echo "C:\Program Files\gs\gs10.03.1\bin" >> $env:GITHUB_PATH
Expand Down Expand Up @@ -137,6 +139,18 @@ jobs:
if: steps.build-cache.outputs.cache-hit != 'true'
run: "& winbuild\\build\\build_dep_libpng.cmd"

- name: Build dependencies / rav1e
if: steps.build-cache.outputs.cache-hit != 'true'
run: "& winbuild\\build\\build_dep_rav1e.cmd"

- name: Build dependencies / meson
if: steps.build-cache.outputs.cache-hit != 'true'
run: "& winbuild\\build\\install_meson.cmd"

- name: Build dependencies / libavif
if: steps.build-cache.outputs.cache-hit != 'true'
run: "& winbuild\\build\\build_dep_libavif.cmd"

# for FreeType WOFF2 font support
- name: Build dependencies / brotli
if: steps.build-cache.outputs.cache-hit != 'true'
Expand Down
82 changes: 80 additions & 2 deletions .github/workflows/wheels-dependencies.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ if [ -z "$IS_MACOS" ]; then
export MB_ML_LIBC=${AUDITWHEEL_POLICY::9}
export MB_ML_VER=${AUDITWHEEL_POLICY:9}
fi
export PLAT=$CIBW_ARCHS
export PLAT="${CIBW_ARCHS:-$AUDITWHEEL_ARCH}"
source wheels/multibuild/common_utils.sh
source wheels/multibuild/library_builders.sh
if [ -z "$IS_MACOS" ]; then
Expand Down Expand Up @@ -41,6 +41,8 @@ LIBWEBP_VERSION=1.4.0
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

if [[ -n "$IS_MACOS" ]] && [[ "$CIBW_ARCHS" == "x86_64" ]]; then
function build_openjpeg {
Expand Down Expand Up @@ -83,6 +85,71 @@ function build_harfbuzz {
fi
}

function install_rav1e {
if [[ -n "$IS_MACOS" ]] && [[ "$PLAT" == "arm64" ]]; then
librav1e_tgz=librav1e-${RAV1E_VERSION}-macos-aarch64.tar.gz
elif [ -n "$IS_MACOS" ]; then
librav1e_tgz=librav1e-${RAV1E_VERSION}-macos.tar.gz
elif [ "$PLAT" == "aarch64" ]; then
librav1e_tgz=librav1e-${RAV1E_VERSION}-linux-aarch64.tar.gz
else
librav1e_tgz=librav1e-${RAV1E_VERSION}-linux-generic.tar.gz
fi

curl -sLo - \
https://github.com/xiph/rav1e/releases/download/v$RAV1E_VERSION/$librav1e_tgz \
| tar -C $BUILD_PREFIX --exclude LICENSE --exclude LICENSE --exclude '*.so' --exclude '*.dylib' -zxf -

if [ ! -n "$IS_MACOS" ]; then
sed -i 's/-lgcc_s/-lgcc_eh/g' "${BUILD_PREFIX}/lib/pkgconfig/rav1e.pc"
fi

# 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
}

function build_libavif {
install_rav1e
python -m pip install meson ninja

if [[ "$PLAT" == "x86_64" ]]; then
build_simple nasm 2.15.05 https://www.nasm.us/pub/nasm/releasebuilds/2.15.05/
fi

local cmake=$(get_modern_cmake)
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 \
-DCMAKE_INSTALL_PREFIX=$BUILD_PREFIX \
-DCMAKE_INSTALL_NAME_DIR=$BUILD_PREFIX/lib \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_SHARED_LIBS=OFF \
-DAVIF_LIBSHARPYUV=LOCAL \
-DAVIF_LIBYUV=LOCAL \
-DAVIF_CODEC_RAV1E=SYSTEM \
-DAVIF_CODEC_AOM=LOCAL \
-DAVIF_CODEC_DAV1D=LOCAL \
-DAVIF_CODEC_SVT=LOCAL \
-DENABLE_NASM=ON \
-DCMAKE_MODULE_PATH=/tmp/cmake/Modules \
. \
&& make install)

if [[ "$MB_ML_LIBC" == "manylinux" ]]; then
cp /usr/local/lib64/libavif.a /usr/local/lib
cp /usr/local/lib64/pkgconfig/libavif.pc /usr/local/lib/pkgconfig
fi
}

function build {
if [[ -n "$IS_MACOS" ]] && [[ "$CIBW_ARCHS" == "arm64" ]]; then
sudo chown -R runner /usr/local
Expand All @@ -93,6 +160,13 @@ function build {
fi
build_new_zlib

ORIGINAL_LDFLAGS=$LDFLAGS
if [[ -n "$IS_MACOS" ]] && [[ "$CIBW_ARCHS" == "arm64" ]]; then
LDFLAGS="${LDFLAGS} -ld64"
fi
build_libavif
LDFLAGS=$ORIGINAL_LDFLAGS

build_simple xcb-proto 1.17.0 https://xorg.freedesktop.org/archive/individual/proto
if [ -n "$IS_MACOS" ]; then
build_simple xorgproto 2024.1 https://www.x.org/pub/individual/proto
Expand Down Expand Up @@ -147,15 +221,19 @@ if [[ -n "$IS_MACOS" ]]; then
# remove lcms2 and libpng to fix building openjpeg on arm64
# remove jpeg-turbo to avoid inclusion on arm64
# remove webp and zstd to avoid inclusion on x86_64
# remove aom and libavif to fix building on arm64
# curl from brew requires zstd, use system curl
brew remove --ignore-dependencies libpng libtiff libxcb libxau libxdmcp curl cairo lcms2 zstd
if [[ "$CIBW_ARCHS" == "arm64" ]]; then
brew remove --ignore-dependencies jpeg-turbo
else
brew remove --ignore-dependencies webp
brew remove --ignore-dependencies webp aom libavif
fi

brew install meson pkg-config

# clear bash path cache for curl
hash -d curl
elif [[ "$MB_ML_LIBC" == "manylinux" ]]; then
if [[ "$HARFBUZZ_VERSION" != 8.5.0 ]]; then
yum install -y meson
Expand Down
44 changes: 44 additions & 0 deletions Tests/check_avif_leaks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from __future__ import annotations

from io import BytesIO

import pytest

from PIL import Image

from .helper import is_win32, skip_unless_feature

# Limits for testing the leak
mem_limit = 1024 * 1048576
stack_size = 8 * 1048576
iterations = int((mem_limit / stack_size) * 2)
test_file = "Tests/images/avif/hopper.avif"

pytestmark = [
pytest.mark.skipif(is_win32(), reason="requires Unix or macOS"),
skip_unless_feature("avif"),
]


def test_leak_load():
from resource import RLIMIT_AS, RLIMIT_STACK, setrlimit

setrlimit(RLIMIT_STACK, (stack_size, stack_size))
setrlimit(RLIMIT_AS, (mem_limit, mem_limit))
for _ in range(iterations):
with Image.open(test_file) as im:
im.load()


def test_leak_save():
from resource import RLIMIT_AS, RLIMIT_STACK, setrlimit

setrlimit(RLIMIT_STACK, (stack_size, stack_size))
setrlimit(RLIMIT_AS, (mem_limit, mem_limit))
for _ in range(iterations):
with Image.open(test_file) as im:
im.load()
test_output = BytesIO()
im.save(test_output, "AVIF")
test_output.seek(0)
test_output.read()
9 changes: 8 additions & 1 deletion Tests/check_wheel.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
from __future__ import annotations

import platform
import struct
import sys

from PIL import features


def test_wheel_modules() -> None:
expected_modules = {"pil", "tkinter", "freetype2", "littlecms2", "webp"}
expected_modules = {"pil", "tkinter", "freetype2", "littlecms2", "webp", "avif"}

# tkinter is not available in cibuildwheel installed CPython on Windows
try:
Expand All @@ -16,6 +18,11 @@ def test_wheel_modules() -> None:
except ImportError:
expected_modules.remove("tkinter")

# libavif is not available on windows for x86 and ARM64 architectures
if sys.platform == "win32":
if platform.machine() == "ARM64" or struct.calcsize("P") == 4:
expected_modules.remove("avif")

assert set(features.get_supported_modules()) == expected_modules


Expand Down
Binary file added Tests/images/avif/chimera-missing-pixi.avif
Binary file not shown.
Binary file added Tests/images/avif/exif.avif
Binary file not shown.
Binary file added Tests/images/avif/hopper.avif
Binary file not shown.
Binary file added Tests/images/avif/hopper_avif_write.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Tests/images/avif/icc_profile.avif
Binary file not shown.
Binary file added Tests/images/avif/icc_profile_none.avif
Binary file not shown.
Binary file added Tests/images/avif/rgba10.heif
Binary file not shown.
Binary file added Tests/images/avif/star.avifs
Binary file not shown.
Binary file added Tests/images/avif/star.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Tests/images/avif/star.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Tests/images/avif/star180.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Tests/images/avif/star270.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Tests/images/avif/star90.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Tests/images/avif/transparency.avif
Binary file not shown.
Binary file added Tests/images/avif/xmp_tags_orientation.avif
Binary file not shown.
Loading

0 comments on commit 0ef3c88

Please sign in to comment.