From 5859b29f9bda0271f593829fe870fdf85177b712 Mon Sep 17 00:00:00 2001 From: Alexander Piskun <13381981+bigcat88@users.noreply.github.com> Date: Thu, 1 Feb 2024 20:29:22 +0300 Subject: [PATCH] Enable Libheif Plugin System (#205) * DENABLE_PLUGIN_LOADING=ON * CI runners: macos-11 -> macos-12 Signed-off-by: Alexander Piskun --------- Signed-off-by: Alexander Piskun --- .github/workflows/analysis-coverage.yml | 8 ++++-- .github/workflows/test-src-build-macos.yml | 12 ++++----- .github/workflows/test-wheels-pi_heif.yml | 4 +-- .github/workflows/test-wheels.yml | 4 +-- .github/workflows/wheels-pi_heif.yml | 3 ++- .github/workflows/wheels-pillow_heif.yml | 3 ++- CHANGELOG.md | 3 ++- libheif/linux_build_libs.py | 25 +++++++++++++++-- libheif/macos/libheif.rb | 19 +++++++++++-- libheif/windows/mingw-w64-libheif/PKGBUILD | 20 ++++++++++++-- pi-heif/libheif/macos/libheif.rb | 22 ++++++++++++--- .../windows/mingw-w64-libheif/PKGBUILD | 22 ++++++++++++--- pillow_heif/__init__.py | 2 +- pillow_heif/_pillow_heif.c | 27 +++++++++++++++++++ pillow_heif/misc.py | 7 ++++- tests/basic_test.py | 7 +++++ 16 files changed, 157 insertions(+), 31 deletions(-) diff --git a/.github/workflows/analysis-coverage.yml b/.github/workflows/analysis-coverage.yml index 2f52ca07..98d20bb5 100644 --- a/.github/workflows/analysis-coverage.yml +++ b/.github/workflows/analysis-coverage.yml @@ -59,7 +59,9 @@ jobs: run: python3 -c "import pillow_heif; print(pillow_heif.libheif_info())" - name: Generate coverage report - run: coverage run -m pytest && coverage xml && coverage html + run: | + TEST_PLUGIN_LOAD="$(whereis libheif | awk '{print $2}')/plugins/libheif-x265.so" coverage run -m pytest + coverage xml && coverage html - name: HTML coverage to artifacts uses: actions/upload-artifact@v3 @@ -104,7 +106,9 @@ jobs: run: python3 -c "import pillow_heif; print(pillow_heif.libheif_info())" - name: Generate coverage report - run: coverage run -m pytest && coverage xml && coverage html + run: | + TEST_PLUGIN_LOAD="$(whereis libheif | awk '{print $2}')/plugins/libheif-x265.so" coverage run -m pytest + coverage xml && coverage html - name: HTML coverage to artifacts uses: actions/upload-artifact@v3 diff --git a/.github/workflows/test-src-build-macos.yml b/.github/workflows/test-src-build-macos.yml index f268ffe8..d4bdac24 100644 --- a/.github/workflows/test-src-build-macos.yml +++ b/.github/workflows/test-src-build-macos.yml @@ -28,9 +28,9 @@ concurrency: cancel-in-progress: true jobs: - full_macos_11: - name: macOS:11-x86_64 - runs-on: macos-11 + full_macos_12: + name: macOS:12-x86_64 + runs-on: macos-12 env: TEST_DECODE_THREADS: 0 # This test fails on GitHub on macOS. We have such enabled test on Cirrus. PH_FULL_ACTION: 1 @@ -52,9 +52,9 @@ jobs: - name: Perform tests run: python3 -m pytest - lite_macos_11: - name: macOS:11-x86_64(Pi-Heif) - runs-on: macos-11 + lite_macos_12: + name: macOS:12-x86_64(Pi-Heif) + runs-on: macos-12 env: TEST_DECODE_THREADS: 0 PH_LIGHT_ACTION: 1 diff --git a/.github/workflows/test-wheels-pi_heif.yml b/.github/workflows/test-wheels-pi_heif.yml index e12d22c6..fb9b3bbd 100644 --- a/.github/workflows/test-wheels-pi_heif.yml +++ b/.github/workflows/test-wheels-pi_heif.yml @@ -104,8 +104,8 @@ jobs: cd .. && python3 -m pytest pillow_heif macos-wheels: - name: macOS • 11 • ${{ matrix.python-version }} - runs-on: macos-11 + name: macOS • 12 • ${{ matrix.python-version }} + runs-on: macos-12 strategy: fail-fast: false matrix: diff --git a/.github/workflows/test-wheels.yml b/.github/workflows/test-wheels.yml index abb96839..71df84e2 100644 --- a/.github/workflows/test-wheels.yml +++ b/.github/workflows/test-wheels.yml @@ -99,8 +99,8 @@ jobs: run: cd .. && python3 -m pytest pillow_heif macos-wheels: - name: macOS • 11 • ${{ matrix.python-version }} - runs-on: macos-11 + name: macOS • 12 • ${{ matrix.python-version }} + runs-on: macos-12 strategy: matrix: python-version: ["pypy-3.8", "pypy-3.9", "pypy-3.10", "3.8", "3.9", "3.10", "3.11", "3.12"] diff --git a/.github/workflows/wheels-pi_heif.yml b/.github/workflows/wheels-pi_heif.yml index 8fd411c2..f10ebd5f 100644 --- a/.github/workflows/wheels-pi_heif.yml +++ b/.github/workflows/wheels-pi_heif.yml @@ -93,7 +93,7 @@ jobs: wheels_macos: name: macosx • x86_64 - runs-on: macos-11 + runs-on: macos-12 steps: - uses: actions/checkout@v4 @@ -109,6 +109,7 @@ jobs: env: CIBW_ARCHS: "x86_64" CIBW_ENVIRONMENT_MACOS: PH_LIGHT_ACTION=1 TEST_DECODE_THREADS=0 + MACOSX_DEPLOYMENT_TARGET: "10.10" - name: Checking built wheels run: | diff --git a/.github/workflows/wheels-pillow_heif.yml b/.github/workflows/wheels-pillow_heif.yml index 60ecc388..7ab0da52 100644 --- a/.github/workflows/wheels-pillow_heif.yml +++ b/.github/workflows/wheels-pillow_heif.yml @@ -89,7 +89,7 @@ jobs: wheels_macos: name: macosx • x86_64 - runs-on: macos-11 + runs-on: macos-12 steps: - uses: actions/checkout@v4 @@ -100,6 +100,7 @@ jobs: env: CIBW_ARCHS: "x86_64" CIBW_ENVIRONMENT_MACOS: PH_FULL_ACTION=1 TEST_DECODE_THREADS=0 + MACOSX_DEPLOYMENT_TARGET: "10.10" - name: Check built wheels run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 4247dee0..3e0fc1de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,13 @@ All notable changes to this project will be documented in this file. -## [0.15.0 - 2024-0x-xx] +## [0.15.0 - 2024-02-02] ### Added - `libheif_info` function: added `encoders` and `decoders` keys to the result, for future libheif plugins support. #189 - `options.PREFERRED_ENCODER` - to use `encoder` different from the default one. #192 - `options.PREFERRED_DECODER` - to use `decoder` different from the default one. #193 +- New `load_plugin` function. `ENABLE_PLUGIN_LOADING` option now is `ON` in the libheif configuration. #154 ### Changed diff --git a/libheif/linux_build_libs.py b/libheif/linux_build_libs.py index a1cd0623..6caf1cfa 100644 --- a/libheif/linux_build_libs.py +++ b/libheif/linux_build_libs.py @@ -189,8 +189,29 @@ def build_lib_linux(url: str, name: str): cmake_args += ["-DCMAKE_BUILD_TYPE=Release"] if name == "libheif": cmake_args += ( - "-DWITH_EXAMPLES=OFF -DWITH_RAV1E=OFF -DWITH_DAV1D=OFF -DWITH_SvtEnc=OFF" - " -DWITH_LIBSHARPYUV=OFF -DENABLE_PLUGIN_LOADING=OFF".split() + "-DWITH_LIBDE265=ON " + "-DWITH_LIBDE265_PLUGIN=OFF " + "-DWITH_X265=ON " + "-DWITH_X265_PLUGIN=OFF " + "-DWITH_AOM_DECODER=ON " + "-DWITH_AOM_DECODER_PLUGIN=OFF " + "-DWITH_AOM_ENCODER=ON " + "-DWITH_AOM_ENCODER_PLUGIN=OFF " + "-DWITH_RAV1E=OFF " + "-DWITH_RAV1E_PLUGIN=OFF " + "-DWITH_DAV1D=OFF " + "-DWITH_DAV1D_PLUGIN=OFF " + "-DWITH_SvtEnc=OFF " + "-DWITH_SvtEnc_PLUGIN=OFF " + "-DWITH_KVAZAAR=OFF " + "-DWITH_KVAZAAR_PLUGIN=OFF " + "-DWITH_JPEG_DECODER=OFF " + "-DWITH_JPEG_ENCODER=OFF " + "-DWITH_OpenJPEG_DECODER=OFF " + "-DWITH_OpenJPEG_ENCODER=OFF " + "-DENABLE_PLUGIN_LOADING=ON " + "-DWITH_LIBSHARPYUV=OFF " + "-DWITH_EXAMPLES=OFF".split() ) _hide_build_process = False if is_musllinux(): diff --git a/libheif/macos/libheif.rb b/libheif/macos/libheif.rb index 67a78bc4..0ce78dae 100644 --- a/libheif/macos/libheif.rb +++ b/libheif/macos/libheif.rb @@ -20,15 +20,30 @@ class Libheif < Formula def install args = %W[ + -DWITH_LIBDE265=ON + -DWITH_LIBDE265_PLUGIN=OFF + -DWITH_X265=ON + -DWITH_X265_PLUGIN=OFF + -DWITH_AOM_DECODER=ON + -DWITH_AOM_DECODER_PLUGIN=OFF + -DWITH_AOM_ENCODER=ON + -DWITH_AOM_ENCODER_PLUGIN=OFF -DWITH_RAV1E=OFF + -DWITH_RAV1E_PLUGIN=OFF -DWITH_DAV1D=OFF + -DWITH_DAV1D_PLUGIN=OFF -DWITH_SvtEnc=OFF - -DWITH_LIBSHARPYUV=OFF - -DENABLE_PLUGIN_LOADING=OFF + -DWITH_SvtEnc_PLUGIN=OFF + -DWITH_KVAZAAR=OFF + -DWITH_KVAZAAR_PLUGIN=OFF + -DWITH_FFMPEG_DECODER=OFF + -DWITH_FFMPEG_DECODER_PLUGIN=OFF -DWITH_JPEG_DECODER=OFF -DWITH_JPEG_ENCODER=OFF -DWITH_OpenJPEG_DECODER=OFF -DWITH_OpenJPEG_ENCODER=OFF + -DENABLE_PLUGIN_LOADING=ON + -DWITH_LIBSHARPYUV=OFF -DCMAKE_INSTALL_RPATH=#{rpath} ] system "cmake", "-S", ".", "-B", "build", *args, *std_cmake_args diff --git a/libheif/windows/mingw-w64-libheif/PKGBUILD b/libheif/windows/mingw-w64-libheif/PKGBUILD index 9e58e863..140f4774 100644 --- a/libheif/windows/mingw-w64-libheif/PKGBUILD +++ b/libheif/windows/mingw-w64-libheif/PKGBUILD @@ -35,15 +35,31 @@ build() { -DCMAKE_INSTALL_PREFIX=${MINGW_PREFIX} \ "${extra_config[@]}" \ -DBUILD_SHARED_LIBS=ON \ + -DWITH_LIBDE265=ON \ + -DWITH_LIBDE265_PLUGIN=OFF \ + -DWITH_X265=ON \ + -DWITH_X265_PLUGIN=OFF \ + -DWITH_AOM_DECODER=ON \ + -DWITH_AOM_DECODER_PLUGIN=OFF \ + -DWITH_AOM_ENCODER=ON \ + -DWITH_AOM_ENCODER_PLUGIN=OFF \ -DWITH_RAV1E=OFF \ + -DWITH_RAV1E_PLUGIN=OFF \ -DWITH_DAV1D=OFF \ + -DWITH_DAV1D_PLUGIN=OFF \ -DWITH_SvtEnc=OFF \ - -DWITH_LIBSHARPYUV=OFF \ - -DENABLE_PLUGIN_LOADING=OFF \ + -DWITH_SvtEnc_PLUGIN=OFF \ + -DWITH_KVAZAAR=OFF \ + -DWITH_KVAZAAR_PLUGIN=OFF \ + -DWITH_FFMPEG_DECODER=OFF \ + -DWITH_FFMPEG_DECODER_PLUGIN=OFF \ -DWITH_JPEG_DECODER=OFF \ -DWITH_JPEG_ENCODER=OFF \ -DWITH_OpenJPEG_DECODER=OFF \ -DWITH_OpenJPEG_ENCODER=OFF \ + -DENABLE_PLUGIN_LOADING=ON \ + -DWITH_LIBSHARPYUV=OFF \ + -DWITH_EXAMPLES=OFF \ -DX265_CFLAGS="-DX265_API_IMPORTS" \ ../${_realname}-${pkgver} diff --git a/pi-heif/libheif/macos/libheif.rb b/pi-heif/libheif/macos/libheif.rb index e07bdd2d..e62056f5 100644 --- a/pi-heif/libheif/macos/libheif.rb +++ b/pi-heif/libheif/macos/libheif.rb @@ -15,17 +15,31 @@ class Libheif < Formula def install args = %W[ + -DWITH_LIBDE265=ON + -DWITH_LIBDE265_PLUGIN=OFF + -DWITH_X265=OFF + -DWITH_X265_PLUGIN=OFF + -DWITH_AOM_DECODER=OFF + -DWITH_AOM_DECODER_PLUGIN=OFF + -DWITH_AOM_ENCODER=OFF + -DWITH_AOM_ENCODER_PLUGIN=OFF -DWITH_RAV1E=OFF + -DWITH_RAV1E_PLUGIN=OFF -DWITH_DAV1D=OFF + -DWITH_DAV1D_PLUGIN=OFF -DWITH_SvtEnc=OFF - -DWITH_AOM=OFF - -DWITH_X265=OFF - -DWITH_LIBSHARPYUV=OFF - -DENABLE_PLUGIN_LOADING=OFF + -DWITH_SvtEnc_PLUGIN=OFF + -DWITH_KVAZAAR=OFF + -DWITH_KVAZAAR_PLUGIN=OFF + -DWITH_FFMPEG_DECODER=OFF + -DWITH_FFMPEG_DECODER_PLUGIN=OFF -DWITH_JPEG_DECODER=OFF -DWITH_JPEG_ENCODER=OFF -DWITH_OpenJPEG_DECODER=OFF -DWITH_OpenJPEG_ENCODER=OFF + -DENABLE_PLUGIN_LOADING=ON + -DWITH_LIBSHARPYUV=OFF + -DWITH_EXAMPLES=OFF -DCMAKE_INSTALL_RPATH=#{rpath} ] system "cmake", "-S", ".", "-B", "build", *args, *std_cmake_args diff --git a/pi-heif/libheif/windows/mingw-w64-libheif/PKGBUILD b/pi-heif/libheif/windows/mingw-w64-libheif/PKGBUILD index c59bf091..48291edb 100644 --- a/pi-heif/libheif/windows/mingw-w64-libheif/PKGBUILD +++ b/pi-heif/libheif/windows/mingw-w64-libheif/PKGBUILD @@ -32,17 +32,31 @@ build() { -DCMAKE_INSTALL_PREFIX=${MINGW_PREFIX} \ "${extra_config[@]}" \ -DBUILD_SHARED_LIBS=ON \ + -DWITH_LIBDE265=ON \ + -DWITH_LIBDE265_PLUGIN=OFF \ + -DWITH_X265=OFF \ + -DWITH_X265_PLUGIN=OFF \ + -DWITH_AOM_DECODER=OFF \ + -DWITH_AOM_DECODER_PLUGIN=OFF \ + -DWITH_AOM_ENCODER=OFF \ + -DWITH_AOM_ENCODER_PLUGIN=OFF \ -DWITH_RAV1E=OFF \ + -DWITH_RAV1E_PLUGIN=OFF \ -DWITH_DAV1D=OFF \ - -DWITH_AOM=OFF \ - -DWITH_X265=OFF \ + -DWITH_DAV1D_PLUGIN=OFF \ -DWITH_SvtEnc=OFF \ - -DWITH_LIBSHARPYUV=OFF \ - -DENABLE_PLUGIN_LOADING=OFF \ + -DWITH_SvtEnc_PLUGIN=OFF \ + -DWITH_KVAZAAR=OFF \ + -DWITH_KVAZAAR_PLUGIN=OFF \ + -DWITH_FFMPEG_DECODER=OFF \ + -DWITH_FFMPEG_DECODER_PLUGIN=OFF \ -DWITH_JPEG_DECODER=OFF \ -DWITH_JPEG_ENCODER=OFF \ -DWITH_OpenJPEG_DECODER=OFF \ -DWITH_OpenJPEG_ENCODER=OFF \ + -DENABLE_PLUGIN_LOADING=ON \ + -DWITH_LIBSHARPYUV=OFF \ + -DWITH_EXAMPLES=OFF \ ../${_realname}-${pkgver} ${MINGW_PREFIX}/bin/cmake --build . diff --git a/pillow_heif/__init__.py b/pillow_heif/__init__.py index 408774e6..35ffa8e2 100644 --- a/pillow_heif/__init__.py +++ b/pillow_heif/__init__.py @@ -26,4 +26,4 @@ open_heif, read_heif, ) -from .misc import get_file_mimetype, set_orientation +from .misc import get_file_mimetype, load_plugin, set_orientation diff --git a/pillow_heif/_pillow_heif.c b/pillow_heif/_pillow_heif.c index 372af7a9..7a122ab7 100644 --- a/pillow_heif/_pillow_heif.c +++ b/pillow_heif/_pillow_heif.c @@ -1328,12 +1328,39 @@ static PyObject* _get_lib_info(PyObject* self) { return lib_info_dict; } +static PyObject* _load_plugins(PyObject* self, PyObject* args) { + const char *plugins_directory; + if (!PyArg_ParseTuple(args, "s", &plugins_directory)) + return NULL; + + struct heif_error error = heif_load_plugins(plugins_directory, NULL, NULL, 0); + if (check_error(error)) { + return NULL; + } + RETURN_NONE +} + +static PyObject* _load_plugin(PyObject* self, PyObject* args) { + const char *plugin_path; + if (!PyArg_ParseTuple(args, "s", &plugin_path)) + return NULL; + + const struct heif_plugin_info* info = NULL; + struct heif_error error = heif_load_plugin(plugin_path, &info); + if (check_error(error)) { + return NULL; + } + RETURN_NONE +} + /* =========== Module =========== */ static PyMethodDef heifMethods[] = { {"CtxWrite", (PyCFunction)_CtxWrite, METH_VARARGS}, {"load_file", (PyCFunction)_load_file, METH_VARARGS}, {"get_lib_info", (PyCFunction)_get_lib_info, METH_NOARGS}, + {"load_plugins", (PyCFunction)_load_plugins, METH_VARARGS}, + {"load_plugin", (PyCFunction)_load_plugin, METH_VARARGS}, {NULL, NULL} }; diff --git a/pillow_heif/misc.py b/pillow_heif/misc.py index d62a9729..47631384 100644 --- a/pillow_heif/misc.py +++ b/pillow_heif/misc.py @@ -10,7 +10,7 @@ from math import ceil from pathlib import Path from struct import pack, unpack -from typing import List, Optional +from typing import List, Optional, Union from PIL import Image @@ -446,3 +446,8 @@ def size_mode(self): def bit_depth(self) -> int: """Return bit-depth based on image mode.""" return MODE_INFO[self.mode][1] + + +def load_plugin(plugin_path: Union[str, Path]) -> None: + """Load specified LibHeif plugin.""" + _pillow_heif.load_plugin(plugin_path) diff --git a/tests/basic_test.py b/tests/basic_test.py index 5523424c..e8ae5525 100644 --- a/tests/basic_test.py +++ b/tests/basic_test.py @@ -126,3 +126,10 @@ def test_light_build(): expected_version = os.getenv("EXP_PH_LIBHEIF_VERSION", "1.17.6") if expected_version: assert info["libheif"] == expected_version + + +@pytest.mark.skipif(not os.getenv("TEST_PLUGIN_LOAD"), reason="Only when plugins present") +def test_load_plugin(): + pillow_heif.load_plugin(os.environ["TEST_PLUGIN_LOAD"]) + with pytest.raises(RuntimeError): + pillow_heif.load_plugin("invalid path")