mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-10-30 23:47:27 +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,21 +673,24 @@ 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: | ||||||
|         if gha_groups: |             if gha_groups: | ||||||
|             lines.append(f"@echo ::group::Running {script}") |                 lines.append(f"@echo ::group::Running {script}") | ||||||
|         lines.append(rf'cmd.exe /c "{{build_dir}}\{script}"') |             lines.append(rf'cmd.exe /c "{{build_dir}}\{script}"') | ||||||
|         lines.append("if errorlevel 1 echo Build failed! && exit /B 1") |             lines.append("if errorlevel 1 echo Build failed! && exit /B 1") | ||||||
|         if gha_groups: |             if gha_groups: | ||||||
|             lines.append("@echo ::endgroup::") |                 lines.append("@echo ::endgroup::") | ||||||
|     print() |     print() | ||||||
|     lines.append("@echo All Pillow dependencies built successfully!") |     lines.append("@echo All Pillow dependencies built successfully!") | ||||||
|     write_script("build_dep_all.cmd", lines, prefs, verbose) |     write_script("build_dep_all.cmd", lines, prefs, verbose) | ||||||
|  | @ -830,18 +833,19 @@ def main() -> None: | ||||||
|     print() |     print() | ||||||
| 
 | 
 | ||||||
|     write_script(".gitignore", ["*"], prefs, args.verbose) |     write_script(".gitignore", ["*"], prefs, args.verbose) | ||||||
|     write_script( |     if "libavif" not in disabled: | ||||||
|         "install_meson.cmd", |         write_script( | ||||||
|         [ |             "install_meson.cmd", | ||||||
|             r'call "{build_dir}\build_env.cmd"', |             [ | ||||||
|             "@echo " + ("=" * 70), |                 r'call "{build_dir}\build_env.cmd"', | ||||||
|             f"@echo ==== {'Building meson':<60} ====", |                 "@echo " + ("=" * 70), | ||||||
|             "@echo " + ("=" * 70), |                 f"@echo ==== {'Building meson':<60} ====", | ||||||
|             f"{sys.executable} -m pip install meson=={V['MESON']}", |                 "@echo " + ("=" * 70), | ||||||
|         ], |                 f"{sys.executable} -m pip install meson=={V['MESON']}", | ||||||
|         prefs, |             ], | ||||||
|         args.verbose, |             prefs, | ||||||
|     ) |             args.verbose, | ||||||
|  |         ) | ||||||
|     build_env(prefs, args.verbose) |     build_env(prefs, args.verbose) | ||||||
|     build_dep_all(disabled, prefs, args.verbose) |     build_dep_all(disabled, prefs, args.verbose) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user