mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-02-12 01:20:53 +03:00
Improved error handling
This commit is contained in:
parent
3730bf214b
commit
c40bcbfc87
|
@ -23,8 +23,7 @@ if [[ $(uname) != CYGWIN* ]]; then
|
||||||
sudo apt-get -qq install libfreetype6-dev liblcms2-dev python3-tk\
|
sudo apt-get -qq install libfreetype6-dev liblcms2-dev python3-tk\
|
||||||
ghostscript libjpeg-turbo-progs libopenjp2-7-dev\
|
ghostscript libjpeg-turbo-progs libopenjp2-7-dev\
|
||||||
cmake meson imagemagick libharfbuzz-dev libfribidi-dev\
|
cmake meson imagemagick libharfbuzz-dev libfribidi-dev\
|
||||||
sway wl-clipboard libopenblas-dev\
|
sway wl-clipboard libopenblas-dev nasm
|
||||||
ninja-build build-essential nasm
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
python3 -m pip install --upgrade pip
|
python3 -m pip install --upgrade pip
|
||||||
|
|
15
.github/workflows/wheels-dependencies.sh
vendored
15
.github/workflows/wheels-dependencies.sh
vendored
|
@ -114,7 +114,7 @@ function install_rav1e {
|
||||||
|
|
||||||
curl -sLo - \
|
curl -sLo - \
|
||||||
https://github.com/xiph/rav1e/releases/download/v$RAV1E_VERSION/librav1e-$RAV1E_VERSION-$suffix.tar.gz \
|
https://github.com/xiph/rav1e/releases/download/v$RAV1E_VERSION/librav1e-$RAV1E_VERSION-$suffix.tar.gz \
|
||||||
| tar -C $BUILD_PREFIX --exclude LICENSE --exclude LICENSE --exclude '*.so' --exclude '*.dylib' -zxf -
|
| tar -C $BUILD_PREFIX --exclude LICENSE --exclude '*.so' --exclude '*.dylib' -zxf -
|
||||||
|
|
||||||
if [ -z "$IS_MACOS" ]; then
|
if [ -z "$IS_MACOS" ]; then
|
||||||
sed -i 's/-lgcc_s/-lgcc_eh/g' "${BUILD_PREFIX}/lib/pkgconfig/rav1e.pc"
|
sed -i 's/-lgcc_s/-lgcc_eh/g' "${BUILD_PREFIX}/lib/pkgconfig/rav1e.pc"
|
||||||
|
@ -133,6 +133,7 @@ EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
function build_libavif {
|
function build_libavif {
|
||||||
|
if [ -e libavif-stamp ]; then return; fi
|
||||||
install_rav1e
|
install_rav1e
|
||||||
python3 -m pip install meson ninja
|
python3 -m pip install meson ninja
|
||||||
|
|
||||||
|
@ -140,13 +141,11 @@ function build_libavif {
|
||||||
build_simple nasm 2.16.03 https://www.nasm.us/pub/nasm/releasebuilds/2.16.03/
|
build_simple nasm 2.16.03 https://www.nasm.us/pub/nasm/releasebuilds/2.16.03/
|
||||||
fi
|
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)
|
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 \
|
(cd $out_dir \
|
||||||
&& $cmake \
|
&& cmake \
|
||||||
-DCMAKE_INSTALL_PREFIX=$BUILD_PREFIX \
|
-DCMAKE_INSTALL_PREFIX=$BUILD_PREFIX \
|
||||||
-DCMAKE_INSTALL_NAME_DIR=$BUILD_PREFIX/lib \
|
-DCMAKE_INSTALL_LIBDIR=$BUILD_PREFIX/lib \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
-DBUILD_SHARED_LIBS=OFF \
|
-DBUILD_SHARED_LIBS=OFF \
|
||||||
-DAVIF_LIBSHARPYUV=LOCAL \
|
-DAVIF_LIBSHARPYUV=LOCAL \
|
||||||
|
@ -159,11 +158,7 @@ function build_libavif {
|
||||||
-DCMAKE_MODULE_PATH=/tmp/cmake/Modules \
|
-DCMAKE_MODULE_PATH=/tmp/cmake/Modules \
|
||||||
. \
|
. \
|
||||||
&& make install)
|
&& make install)
|
||||||
|
touch libavif-stamp
|
||||||
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 {
|
function build {
|
||||||
|
|
|
@ -18,7 +18,7 @@ def test_wheel_modules() -> None:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
expected_modules.remove("tkinter")
|
expected_modules.remove("tkinter")
|
||||||
|
|
||||||
# libavif is not available on windows for x86 and ARM64 architectures
|
# libavif is not available on Windows for x86 and ARM64 architectures
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
if platform.machine() == "ARM64" or struct.calcsize("P") == 4:
|
if platform.machine() == "ARM64" or struct.calcsize("P") == 4:
|
||||||
expected_modules.remove("avif")
|
expected_modules.remove("avif")
|
||||||
|
|
|
@ -127,8 +127,6 @@ class TestUnsupportedAvif:
|
||||||
@skip_unless_feature("avif")
|
@skip_unless_feature("avif")
|
||||||
class TestFileAvif:
|
class TestFileAvif:
|
||||||
def test_version(self) -> None:
|
def test_version(self) -> None:
|
||||||
_avif.AvifCodecVersions()
|
|
||||||
|
|
||||||
version = features.version_module("avif")
|
version = features.version_module("avif")
|
||||||
assert version is not None
|
assert version is not None
|
||||||
assert re.search(r"\d+\.\d+\.\d+$", version)
|
assert re.search(r"\d+\.\d+\.\d+$", version)
|
||||||
|
|
|
@ -1370,17 +1370,16 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
|
||||||
YUV range, either "full" or "limited". Defaults to "full"
|
YUV range, either "full" or "limited". Defaults to "full"
|
||||||
|
|
||||||
**codec**
|
**codec**
|
||||||
AV1 codec to use for encoding. Possible values are "aom", "rav1e", and
|
AV1 codec to use for encoding. Specific values are "aom", "rav1e", and
|
||||||
"svt", depending on what codecs were compiled with libavif. Defaults to
|
"svt", presuming the chosen codec is available. Defaults to "auto", which
|
||||||
"auto", which will choose the first available codec in the order of the
|
will choose the first available codec in the order of the preceding list.
|
||||||
preceding list.
|
|
||||||
|
|
||||||
**tile_rows** / **tile_cols**
|
**tile_rows** / **tile_cols**
|
||||||
For tile encoding, the (log 2) number of tile rows and columns to use.
|
For tile encoding, the (log 2) number of tile rows and columns to use.
|
||||||
Valid values are 0-6, default 0.
|
Valid values are 0-6, default 0.
|
||||||
|
|
||||||
**alpha_premultiplied**
|
**alpha_premultiplied**
|
||||||
Encode the image with premultiplied alpha, defaults ``False``
|
Encode the image with premultiplied alpha. Defaults to ``False``
|
||||||
|
|
||||||
**icc_profile**
|
**icc_profile**
|
||||||
The ICC Profile to include in the saved file.
|
The ICC Profile to include in the saved file.
|
||||||
|
|
|
@ -164,9 +164,11 @@ Many of Pillow's features require external libraries:
|
||||||
The easiest way to install external libraries is via `Homebrew
|
The easiest way to install external libraries is via `Homebrew
|
||||||
<https://brew.sh/>`_. After you install Homebrew, run::
|
<https://brew.sh/>`_. After you install Homebrew, run::
|
||||||
|
|
||||||
brew install libjpeg libraqm libtiff little-cms2 openjpeg webp
|
brew install libavif libjpeg libraqm libtiff little-cms2 openjpeg webp
|
||||||
|
|
||||||
To install libavif on macOS use Homebrew to install its build dependencies::
|
If you would like to use libavif with more codecs than just aom, then
|
||||||
|
instead of installing libavif through Homebrew directly, you can use
|
||||||
|
Homebrew to install libavif's build dependencies::
|
||||||
|
|
||||||
brew install aom dav1d rav1e
|
brew install aom dav1d rav1e
|
||||||
|
|
||||||
|
@ -224,8 +226,7 @@ Many of Pillow's features require external libraries:
|
||||||
|
|
||||||
sudo pkg install jpeg-turbo tiff webp lcms2 freetype2 openjpeg harfbuzz fribidi libxcb libavif
|
sudo pkg install jpeg-turbo tiff webp lcms2 freetype2 openjpeg harfbuzz fribidi libxcb libavif
|
||||||
|
|
||||||
See ``depends/install_raqm_cmake.sh`` to install libraqm and
|
See ``depends/install_raqm_cmake.sh`` to install libraqm.
|
||||||
``depends/install_libavif.sh`` to install libavif.
|
|
||||||
|
|
||||||
.. tab:: Android
|
.. tab:: Android
|
||||||
|
|
||||||
|
|
|
@ -24,17 +24,11 @@ _VALID_AVIF_MODES = {"RGB", "RGBA"}
|
||||||
def _accept(prefix: bytes) -> bool | str:
|
def _accept(prefix: bytes) -> bool | str:
|
||||||
if prefix[4:8] != b"ftyp":
|
if prefix[4:8] != b"ftyp":
|
||||||
return False
|
return False
|
||||||
coding_brands = (b"avif", b"avis")
|
|
||||||
container_brands = (b"mif1", b"msf1")
|
|
||||||
major_brand = prefix[8:12]
|
major_brand = prefix[8:12]
|
||||||
if major_brand in coding_brands:
|
if major_brand in (
|
||||||
if not SUPPORTED:
|
# coding brands
|
||||||
return (
|
b"avif",
|
||||||
"image file could not be identified because AVIF "
|
b"avis",
|
||||||
"support not installed"
|
|
||||||
)
|
|
||||||
return True
|
|
||||||
if major_brand in container_brands:
|
|
||||||
# We accept files with AVIF container brands; we can't yet know if
|
# We accept files with AVIF container brands; we can't yet know if
|
||||||
# the ftyp box has the correct compatible brands, but if it doesn't
|
# the ftyp box has the correct compatible brands, but if it doesn't
|
||||||
# then the plugin will raise a SyntaxError which Pillow will catch
|
# then the plugin will raise a SyntaxError which Pillow will catch
|
||||||
|
@ -42,6 +36,14 @@ def _accept(prefix: bytes) -> bool | str:
|
||||||
#
|
#
|
||||||
# Also, because this file might not actually be an AVIF file, we
|
# Also, because this file might not actually be an AVIF file, we
|
||||||
# don't raise an error if AVIF support isn't properly compiled.
|
# don't raise an error if AVIF support isn't properly compiled.
|
||||||
|
b"mif1",
|
||||||
|
b"msf1",
|
||||||
|
):
|
||||||
|
if not SUPPORTED:
|
||||||
|
return (
|
||||||
|
"image file could not be identified because AVIF "
|
||||||
|
"support not installed"
|
||||||
|
)
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -72,6 +74,11 @@ class AvifImageFile(ImageFile.ImageFile):
|
||||||
)
|
)
|
||||||
raise SyntaxError(msg)
|
raise SyntaxError(msg)
|
||||||
|
|
||||||
|
if DECODE_CODEC_CHOICE != "auto" and not _avif.decoder_codec_available(
|
||||||
|
DECODE_CODEC_CHOICE
|
||||||
|
):
|
||||||
|
msg = "Invalid opening codec"
|
||||||
|
raise ValueError(msg)
|
||||||
self._decoder = _avif.AvifDecoder(
|
self._decoder = _avif.AvifDecoder(
|
||||||
self.fp.read(),
|
self.fp.read(),
|
||||||
DECODE_CODEC_CHOICE,
|
DECODE_CODEC_CHOICE,
|
||||||
|
@ -104,10 +111,8 @@ class AvifImageFile(ImageFile.ImageFile):
|
||||||
data, timescale, tsp_in_ts, dur_in_ts = self._decoder.get_frame(
|
data, timescale, tsp_in_ts, dur_in_ts = self._decoder.get_frame(
|
||||||
self.__frame
|
self.__frame
|
||||||
)
|
)
|
||||||
timestamp = round(1000 * (tsp_in_ts / timescale))
|
self.info["timestamp"] = round(1000 * (tsp_in_ts / timescale))
|
||||||
duration = round(1000 * (dur_in_ts / timescale))
|
self.info["duration"] = round(1000 * (dur_in_ts / timescale))
|
||||||
self.info["timestamp"] = timestamp
|
|
||||||
self.info["duration"] = duration
|
|
||||||
self.__loaded = self.__frame
|
self.__loaded = self.__frame
|
||||||
|
|
||||||
# Set tile
|
# Set tile
|
||||||
|
@ -153,6 +158,9 @@ def _save(
|
||||||
speed = info.get("speed", 6)
|
speed = info.get("speed", 6)
|
||||||
max_threads = info.get("max_threads", _get_default_max_threads())
|
max_threads = info.get("max_threads", _get_default_max_threads())
|
||||||
codec = info.get("codec", "auto")
|
codec = info.get("codec", "auto")
|
||||||
|
if codec != "auto" and not _avif.encoder_codec_available(codec):
|
||||||
|
msg = "Invalid saving codec"
|
||||||
|
raise ValueError(msg)
|
||||||
range_ = info.get("range", "full")
|
range_ = info.get("range", "full")
|
||||||
tile_rows_log2 = info.get("tile_rows", 0)
|
tile_rows_log2 = info.get("tile_rows", 0)
|
||||||
tile_cols_log2 = info.get("tile_cols", 0)
|
tile_cols_log2 = info.get("tile_cols", 0)
|
||||||
|
@ -199,7 +207,7 @@ def _save(
|
||||||
)
|
)
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
advanced = tuple(
|
advanced = tuple(
|
||||||
[(str(k).encode("utf-8"), str(v).encode("utf-8")) for k, v in advanced]
|
(str(k).encode("utf-8"), str(v).encode("utf-8")) for k, v in advanced
|
||||||
)
|
)
|
||||||
|
|
||||||
# Setup the AVIF encoder
|
# Setup the AVIF encoder
|
||||||
|
|
147
src/_avif.c
147
src/_avif.c
|
@ -218,30 +218,43 @@ _encoder_codec_available(PyObject *self, PyObject *args) {
|
||||||
return PyBool_FromLong(is_available);
|
return PyBool_FromLong(is_available);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static int
|
||||||
_add_codec_specific_options(avifEncoder *encoder, PyObject *opts) {
|
_add_codec_specific_options(avifEncoder *encoder, PyObject *opts) {
|
||||||
Py_ssize_t i, size;
|
Py_ssize_t i, size;
|
||||||
PyObject *keyval, *py_key, *py_val;
|
PyObject *keyval, *py_key, *py_val;
|
||||||
char *key, *val;
|
char *key, *val;
|
||||||
if (!PyTuple_Check(opts)) {
|
if (!PyTuple_Check(opts)) {
|
||||||
return;
|
PyErr_SetString(PyExc_ValueError, "Invalid advanced codec options");
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
size = PyTuple_GET_SIZE(opts);
|
size = PyTuple_GET_SIZE(opts);
|
||||||
|
|
||||||
for (i = 0; i < size; i++) {
|
for (i = 0; i < size; i++) {
|
||||||
keyval = PyTuple_GetItem(opts, i);
|
keyval = PyTuple_GetItem(opts, i);
|
||||||
if (!PyTuple_Check(keyval) || PyTuple_GET_SIZE(keyval) != 2) {
|
if (!PyTuple_Check(keyval) || PyTuple_GET_SIZE(keyval) != 2) {
|
||||||
return;
|
PyErr_SetString(PyExc_ValueError, "Invalid advanced codec options");
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
py_key = PyTuple_GetItem(keyval, 0);
|
py_key = PyTuple_GetItem(keyval, 0);
|
||||||
py_val = PyTuple_GetItem(keyval, 1);
|
py_val = PyTuple_GetItem(keyval, 1);
|
||||||
if (!PyBytes_Check(py_key) || !PyBytes_Check(py_val)) {
|
if (!PyBytes_Check(py_key) || !PyBytes_Check(py_val)) {
|
||||||
return;
|
PyErr_SetString(PyExc_ValueError, "Invalid advanced codec options");
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
key = PyBytes_AsString(py_key);
|
key = PyBytes_AsString(py_key);
|
||||||
val = PyBytes_AsString(py_val);
|
val = PyBytes_AsString(py_val);
|
||||||
avifEncoderSetCodecSpecificOption(encoder, key, val);
|
|
||||||
|
avifResult result = avifEncoderSetCodecSpecificOption(encoder, key, val);
|
||||||
|
if (result != AVIF_RESULT_OK) {
|
||||||
|
PyErr_Format(
|
||||||
|
exc_type_for_avif_result(result),
|
||||||
|
"Setting advanced codec options failed: %s",
|
||||||
|
avifResultToString(result)
|
||||||
|
);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encoder functions
|
// Encoder functions
|
||||||
|
@ -336,17 +349,6 @@ AvifEncoderNew(PyObject *self_, PyObject *args) {
|
||||||
enc_options.codec = AVIF_CODEC_CHOICE_AUTO;
|
enc_options.codec = AVIF_CODEC_CHOICE_AUTO;
|
||||||
} else {
|
} else {
|
||||||
enc_options.codec = avifCodecChoiceFromName(codec);
|
enc_options.codec = avifCodecChoiceFromName(codec);
|
||||||
if (enc_options.codec == AVIF_CODEC_CHOICE_AUTO) {
|
|
||||||
PyErr_Format(PyExc_ValueError, "Invalid codec: %s", codec);
|
|
||||||
return NULL;
|
|
||||||
} else {
|
|
||||||
const char *codec_name =
|
|
||||||
avifCodecName(enc_options.codec, AVIF_CODEC_FLAG_CAN_ENCODE);
|
|
||||||
if (codec_name == NULL) {
|
|
||||||
PyErr_Format(PyExc_ValueError, "AV1 Codec cannot encode: %s", codec);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(range, "full") == 0) {
|
if (strcmp(range, "full") == 0) {
|
||||||
|
@ -410,9 +412,18 @@ AvifEncoderNew(PyObject *self_, PyObject *args) {
|
||||||
encoder->autoTiling = enc_options.autotiling;
|
encoder->autoTiling = enc_options.autotiling;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (advanced != Py_None) {
|
||||||
#if AVIF_VERSION >= 80200
|
#if AVIF_VERSION >= 80200
|
||||||
_add_codec_specific_options(encoder, advanced);
|
if (_add_codec_specific_options(encoder, advanced)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
PyErr_SetString(
|
||||||
|
PyExc_ValueError, "Advanced codec options require libavif >= 0.8.2"
|
||||||
|
);
|
||||||
|
return NULL;
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
self->encoder = encoder;
|
self->encoder = encoder;
|
||||||
|
|
||||||
|
@ -430,14 +441,24 @@ AvifEncoderNew(PyObject *self_, PyObject *args) {
|
||||||
image->alphaPremultiplied = enc_options.alpha_premultiplied;
|
image->alphaPremultiplied = enc_options.alpha_premultiplied;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
avifResult result;
|
||||||
if (PyBytes_GET_SIZE(icc_bytes)) {
|
if (PyBytes_GET_SIZE(icc_bytes)) {
|
||||||
self->icc_bytes = icc_bytes;
|
self->icc_bytes = icc_bytes;
|
||||||
Py_INCREF(icc_bytes);
|
Py_INCREF(icc_bytes);
|
||||||
avifImageSetProfileICC(
|
|
||||||
|
result = avifImageSetProfileICC(
|
||||||
image,
|
image,
|
||||||
(uint8_t *)PyBytes_AS_STRING(icc_bytes),
|
(uint8_t *)PyBytes_AS_STRING(icc_bytes),
|
||||||
PyBytes_GET_SIZE(icc_bytes)
|
PyBytes_GET_SIZE(icc_bytes)
|
||||||
);
|
);
|
||||||
|
if (result != AVIF_RESULT_OK) {
|
||||||
|
PyErr_Format(
|
||||||
|
exc_type_for_avif_result(result),
|
||||||
|
"Setting ICC profile failed: %s",
|
||||||
|
avifResultToString(result)
|
||||||
|
);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
image->colorPrimaries = AVIF_COLOR_PRIMARIES_BT709;
|
image->colorPrimaries = AVIF_COLOR_PRIMARIES_BT709;
|
||||||
image->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_SRGB;
|
image->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_SRGB;
|
||||||
|
@ -446,20 +467,38 @@ AvifEncoderNew(PyObject *self_, PyObject *args) {
|
||||||
if (PyBytes_GET_SIZE(exif_bytes)) {
|
if (PyBytes_GET_SIZE(exif_bytes)) {
|
||||||
self->exif_bytes = exif_bytes;
|
self->exif_bytes = exif_bytes;
|
||||||
Py_INCREF(exif_bytes);
|
Py_INCREF(exif_bytes);
|
||||||
avifImageSetMetadataExif(
|
|
||||||
|
result = avifImageSetMetadataExif(
|
||||||
image,
|
image,
|
||||||
(uint8_t *)PyBytes_AS_STRING(exif_bytes),
|
(uint8_t *)PyBytes_AS_STRING(exif_bytes),
|
||||||
PyBytes_GET_SIZE(exif_bytes)
|
PyBytes_GET_SIZE(exif_bytes)
|
||||||
);
|
);
|
||||||
|
if (result != AVIF_RESULT_OK) {
|
||||||
|
PyErr_Format(
|
||||||
|
exc_type_for_avif_result(result),
|
||||||
|
"Setting EXIF data failed: %s",
|
||||||
|
avifResultToString(result)
|
||||||
|
);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (PyBytes_GET_SIZE(xmp_bytes)) {
|
if (PyBytes_GET_SIZE(xmp_bytes)) {
|
||||||
self->xmp_bytes = xmp_bytes;
|
self->xmp_bytes = xmp_bytes;
|
||||||
Py_INCREF(xmp_bytes);
|
Py_INCREF(xmp_bytes);
|
||||||
avifImageSetMetadataXMP(
|
|
||||||
|
result = avifImageSetMetadataXMP(
|
||||||
image,
|
image,
|
||||||
(uint8_t *)PyBytes_AS_STRING(xmp_bytes),
|
(uint8_t *)PyBytes_AS_STRING(xmp_bytes),
|
||||||
PyBytes_GET_SIZE(xmp_bytes)
|
PyBytes_GET_SIZE(xmp_bytes)
|
||||||
);
|
);
|
||||||
|
if (result != AVIF_RESULT_OK) {
|
||||||
|
PyErr_Format(
|
||||||
|
exc_type_for_avif_result(result),
|
||||||
|
"Setting XMP data failed: %s",
|
||||||
|
avifResultToString(result)
|
||||||
|
);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
exif_orientation_to_irot_imir(image, exif_orientation);
|
exif_orientation_to_irot_imir(image, exif_orientation);
|
||||||
|
|
||||||
|
@ -498,7 +537,6 @@ _encoder_add(AvifEncoderObject *self, PyObject *args) {
|
||||||
PyObject *ret = Py_None;
|
PyObject *ret = Py_None;
|
||||||
|
|
||||||
int is_first_frame;
|
int is_first_frame;
|
||||||
int channels;
|
|
||||||
avifRGBImage rgb;
|
avifRGBImage rgb;
|
||||||
avifResult result;
|
avifResult result;
|
||||||
|
|
||||||
|
@ -561,13 +599,19 @@ _encoder_add(AvifEncoderObject *self, PyObject *args) {
|
||||||
|
|
||||||
if (strcmp(mode, "RGBA") == 0) {
|
if (strcmp(mode, "RGBA") == 0) {
|
||||||
rgb.format = AVIF_RGB_FORMAT_RGBA;
|
rgb.format = AVIF_RGB_FORMAT_RGBA;
|
||||||
channels = 4;
|
|
||||||
} else {
|
} else {
|
||||||
rgb.format = AVIF_RGB_FORMAT_RGB;
|
rgb.format = AVIF_RGB_FORMAT_RGB;
|
||||||
channels = 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
avifRGBImageAllocatePixels(&rgb);
|
result = avifRGBImageAllocatePixels(&rgb);
|
||||||
|
if (result != AVIF_RESULT_OK) {
|
||||||
|
PyErr_Format(
|
||||||
|
exc_type_for_avif_result(result),
|
||||||
|
"Pixel allocation failed: %s",
|
||||||
|
avifResultToString(result)
|
||||||
|
);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (rgb.rowBytes * rgb.height != size) {
|
if (rgb.rowBytes * rgb.height != size) {
|
||||||
PyErr_Format(
|
PyErr_Format(
|
||||||
|
@ -679,18 +723,6 @@ AvifDecoderNew(PyObject *self_, PyObject *args) {
|
||||||
codec = AVIF_CODEC_CHOICE_AUTO;
|
codec = AVIF_CODEC_CHOICE_AUTO;
|
||||||
} else {
|
} else {
|
||||||
codec = avifCodecChoiceFromName(codec_str);
|
codec = avifCodecChoiceFromName(codec_str);
|
||||||
if (codec == AVIF_CODEC_CHOICE_AUTO) {
|
|
||||||
PyErr_Format(PyExc_ValueError, "Invalid codec: %s", codec_str);
|
|
||||||
return NULL;
|
|
||||||
} else {
|
|
||||||
const char *codec_name = avifCodecName(codec, AVIF_CODEC_FLAG_CAN_DECODE);
|
|
||||||
if (codec_name == NULL) {
|
|
||||||
PyErr_Format(
|
|
||||||
PyExc_ValueError, "AV1 Codec cannot decode: %s", codec_str
|
|
||||||
);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self = PyObject_New(AvifDecoderObject, &AvifDecoder_Type);
|
self = PyObject_New(AvifDecoderObject, &AvifDecoder_Type);
|
||||||
|
@ -717,14 +749,24 @@ AvifDecoderNew(PyObject *self_, PyObject *args) {
|
||||||
#endif
|
#endif
|
||||||
self->decoder->codecChoice = codec;
|
self->decoder->codecChoice = codec;
|
||||||
|
|
||||||
avifDecoderSetIOMemory(
|
result = avifDecoderSetIOMemory(
|
||||||
self->decoder,
|
self->decoder,
|
||||||
(uint8_t *)PyBytes_AS_STRING(self->data),
|
(uint8_t *)PyBytes_AS_STRING(self->data),
|
||||||
PyBytes_GET_SIZE(self->data)
|
PyBytes_GET_SIZE(self->data)
|
||||||
);
|
);
|
||||||
|
if (result != AVIF_RESULT_OK) {
|
||||||
|
PyErr_Format(
|
||||||
|
exc_type_for_avif_result(result),
|
||||||
|
"Setting IO memory failed: %s",
|
||||||
|
avifResultToString(result)
|
||||||
|
);
|
||||||
|
avifDecoderDestroy(self->decoder);
|
||||||
|
self->decoder = NULL;
|
||||||
|
Py_DECREF(self);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
result = avifDecoderParse(self->decoder);
|
result = avifDecoderParse(self->decoder);
|
||||||
|
|
||||||
if (result != AVIF_RESULT_OK) {
|
if (result != AVIF_RESULT_OK) {
|
||||||
PyErr_Format(
|
PyErr_Format(
|
||||||
exc_type_for_avif_result(result),
|
exc_type_for_avif_result(result),
|
||||||
|
@ -815,7 +857,6 @@ _decoder_get_frame(AvifDecoderObject *self, PyObject *args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
result = avifDecoderNthImage(decoder, frame_index);
|
result = avifDecoderNthImage(decoder, frame_index);
|
||||||
|
|
||||||
if (result != AVIF_RESULT_OK) {
|
if (result != AVIF_RESULT_OK) {
|
||||||
PyErr_Format(
|
PyErr_Format(
|
||||||
exc_type_for_avif_result(result),
|
exc_type_for_avif_result(result),
|
||||||
|
@ -847,7 +888,15 @@ _decoder_get_frame(AvifDecoderObject *self, PyObject *args) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
avifRGBImageAllocatePixels(&rgb);
|
result = avifRGBImageAllocatePixels(&rgb);
|
||||||
|
if (result != AVIF_RESULT_OK) {
|
||||||
|
PyErr_Format(
|
||||||
|
exc_type_for_avif_result(result),
|
||||||
|
"Pixel allocation failed: %s",
|
||||||
|
avifResultToString(result)
|
||||||
|
);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
Py_BEGIN_ALLOW_THREADS result = avifImageYUVToRGB(image, &rgb);
|
Py_BEGIN_ALLOW_THREADS result = avifImageYUVToRGB(image, &rgb);
|
||||||
Py_END_ALLOW_THREADS
|
Py_END_ALLOW_THREADS
|
||||||
|
@ -893,10 +942,7 @@ static struct PyMethodDef _encoder_methods[] = {
|
||||||
|
|
||||||
// AvifDecoder type definition
|
// AvifDecoder type definition
|
||||||
static PyTypeObject AvifEncoder_Type = {
|
static PyTypeObject AvifEncoder_Type = {
|
||||||
// clang-format off
|
PyVarObject_HEAD_INIT(NULL, 0).tp_name = "AvifEncoder",
|
||||||
PyVarObject_HEAD_INIT(NULL, 0)
|
|
||||||
.tp_name = "AvifEncoder",
|
|
||||||
// clang-format on
|
|
||||||
.tp_basicsize = sizeof(AvifEncoderObject),
|
.tp_basicsize = sizeof(AvifEncoderObject),
|
||||||
.tp_dealloc = (destructor)_encoder_dealloc,
|
.tp_dealloc = (destructor)_encoder_dealloc,
|
||||||
.tp_flags = Py_TPFLAGS_DEFAULT,
|
.tp_flags = Py_TPFLAGS_DEFAULT,
|
||||||
|
@ -912,10 +958,7 @@ static struct PyMethodDef _decoder_methods[] = {
|
||||||
|
|
||||||
// AvifDecoder type definition
|
// AvifDecoder type definition
|
||||||
static PyTypeObject AvifDecoder_Type = {
|
static PyTypeObject AvifDecoder_Type = {
|
||||||
// clang-format off
|
PyVarObject_HEAD_INIT(NULL, 0).tp_name = "AvifDecoder",
|
||||||
PyVarObject_HEAD_INIT(NULL, 0)
|
|
||||||
.tp_name = "AvifDecoder",
|
|
||||||
// clang-format on
|
|
||||||
.tp_basicsize = sizeof(AvifDecoderObject),
|
.tp_basicsize = sizeof(AvifDecoderObject),
|
||||||
.tp_itemsize = 0,
|
.tp_itemsize = 0,
|
||||||
.tp_dealloc = (destructor)_decoder_dealloc,
|
.tp_dealloc = (destructor)_decoder_dealloc,
|
||||||
|
@ -923,13 +966,6 @@ static PyTypeObject AvifDecoder_Type = {
|
||||||
.tp_methods = _decoder_methods,
|
.tp_methods = _decoder_methods,
|
||||||
};
|
};
|
||||||
|
|
||||||
PyObject *
|
|
||||||
AvifCodecVersions() {
|
|
||||||
char codecVersions[256];
|
|
||||||
avifCodecVersions(codecVersions);
|
|
||||||
return PyUnicode_FromString(codecVersions);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
/* Module Setup */
|
/* Module Setup */
|
||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
|
@ -937,7 +973,6 @@ AvifCodecVersions() {
|
||||||
static PyMethodDef avifMethods[] = {
|
static PyMethodDef avifMethods[] = {
|
||||||
{"AvifDecoder", AvifDecoderNew, METH_VARARGS},
|
{"AvifDecoder", AvifDecoderNew, METH_VARARGS},
|
||||||
{"AvifEncoder", AvifEncoderNew, METH_VARARGS},
|
{"AvifEncoder", AvifEncoderNew, METH_VARARGS},
|
||||||
{"AvifCodecVersions", AvifCodecVersions, METH_NOARGS},
|
|
||||||
{"decoder_codec_available", _decoder_codec_available, METH_VARARGS},
|
{"decoder_codec_available", _decoder_codec_available, METH_VARARGS},
|
||||||
{"encoder_codec_available", _encoder_codec_available, METH_VARARGS},
|
{"encoder_codec_available", _encoder_codec_available, METH_VARARGS},
|
||||||
{NULL, NULL}
|
{NULL, NULL}
|
||||||
|
|
|
@ -122,7 +122,7 @@ V = {
|
||||||
"TIFF": "4.6.0",
|
"TIFF": "4.6.0",
|
||||||
"XZ": "5.6.3",
|
"XZ": "5.6.3",
|
||||||
"ZLIB": "1.3.1",
|
"ZLIB": "1.3.1",
|
||||||
"MESON": "1.5.2",
|
"MESON": "1.6.0",
|
||||||
"LIBAVIF": "1.1.1",
|
"LIBAVIF": "1.1.1",
|
||||||
"RAV1E": "0.7.1",
|
"RAV1E": "0.7.1",
|
||||||
}
|
}
|
||||||
|
@ -673,12 +673,15 @@ def build_dep(name: str, prefs: dict[str, str], verbose: bool) -> str:
|
||||||
def build_dep_all(disabled: list[str], prefs: dict[str, str], verbose: bool) -> None:
|
def build_dep_all(disabled: list[str], prefs: dict[str, str], verbose: bool) -> None:
|
||||||
lines = [r'call "{build_dir}\build_env.cmd"']
|
lines = [r'call "{build_dir}\build_env.cmd"']
|
||||||
gha_groups = "GITHUB_ACTIONS" in os.environ
|
gha_groups = "GITHUB_ACTIONS" in os.environ
|
||||||
scripts = ["install_meson.cmd"]
|
|
||||||
for dep_name in DEPS:
|
for dep_name in DEPS:
|
||||||
print()
|
print()
|
||||||
if dep_name in disabled:
|
if dep_name in disabled:
|
||||||
print(f"Skipping disabled dependency {dep_name}")
|
print(f"Skipping disabled dependency {dep_name}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
scripts = []
|
||||||
|
if dep_name == "libavif":
|
||||||
|
scripts.append("install_meson.cmd")
|
||||||
scripts.append(build_dep(dep_name, prefs, verbose))
|
scripts.append(build_dep(dep_name, prefs, verbose))
|
||||||
|
|
||||||
for script in scripts:
|
for script in scripts:
|
||||||
|
@ -830,6 +833,7 @@ def main() -> None:
|
||||||
print()
|
print()
|
||||||
|
|
||||||
write_script(".gitignore", ["*"], prefs, args.verbose)
|
write_script(".gitignore", ["*"], prefs, args.verbose)
|
||||||
|
if "libavif" not in disabled:
|
||||||
write_script(
|
write_script(
|
||||||
"install_meson.cmd",
|
"install_meson.cmd",
|
||||||
[
|
[
|
||||||
|
|
Loading…
Reference in New Issue
Block a user