From f17f1bc6074adc657dda54f6dca750e1d9fa80cf Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 11 Apr 2020 20:43:49 +1000 Subject: [PATCH 01/12] Added method argument to single frame WebP saving --- Tests/test_file_webp.py | 73 +++++++++++++--------------- src/PIL/WebPImagePlugin.py | 2 + src/_webp.c | 97 ++++++++++++++++++++++++-------------- 3 files changed, 98 insertions(+), 74 deletions(-) diff --git a/Tests/test_file_webp.py b/Tests/test_file_webp.py index 1b8aa9f8a..f538b0ecf 100644 --- a/Tests/test_file_webp.py +++ b/Tests/test_file_webp.py @@ -1,3 +1,5 @@ +import io + import pytest from PIL import Image, WebPImagePlugin @@ -54,15 +56,10 @@ class TestFileWebp: # dwebp -ppm ../../Tests/images/hopper.webp -o hopper_webp_bits.ppm assert_image_similar_tofile(image, "Tests/images/hopper_webp_bits.ppm", 1.0) - def test_write_rgb(self, tmp_path): - """ - Can we write a RGB mode file to webp without error. - Does it have the bits we expect? - """ - + def _roundtrip(self, tmp_path, mode, epsilon, args={}): temp_file = str(tmp_path / "temp.webp") - hopper(self.rgb_mode).save(temp_file) + hopper(mode).save(temp_file, **args) with Image.open(temp_file) as image: assert image.mode == self.rgb_mode assert image.size == (128, 128) @@ -70,18 +67,38 @@ class TestFileWebp: image.load() image.getdata() - # generated with: dwebp -ppm temp.webp -o hopper_webp_write.ppm - assert_image_similar_tofile( - image, "Tests/images/hopper_webp_write.ppm", 12.0 - ) + if mode == self.rgb_mode: + # generated with: dwebp -ppm temp.webp -o hopper_webp_write.ppm + assert_image_similar_tofile( + image, "Tests/images/hopper_webp_write.ppm", 12.0 + ) # This test asserts that the images are similar. If the average pixel # difference between the two images is less than the epsilon value, # then we're going to accept that it's a reasonable lossy version of - # the image. The old lena images for WebP are showing ~16 on - # Ubuntu, the jpegs are showing ~18. - target = hopper(self.rgb_mode) - assert_image_similar(image, target, 12.0) + # the image. + target = hopper(mode) + if mode != self.rgb_mode: + target = target.convert(self.rgb_mode) + assert_image_similar(image, target, epsilon) + + def test_write_rgb(self, tmp_path): + """ + Can we write a RGB mode file to webp without error? + Does it have the bits we expect? + """ + + self._roundtrip(tmp_path, self.rgb_mode, 12.5) + + def test_write_method(self, tmp_path): + self._roundtrip(tmp_path, self.rgb_mode, 12.0, {"method": 6}) + + buffer_no_args = io.BytesIO() + hopper().save(buffer_no_args, format="WEBP") + + buffer_method = io.BytesIO() + hopper().save(buffer_method, format="WEBP", method=6) + assert buffer_no_args.getbuffer() != buffer_method.getbuffer() def test_write_unsupported_mode_L(self, tmp_path): """ @@ -89,18 +106,7 @@ class TestFileWebp: similar to the original file. """ - temp_file = str(tmp_path / "temp.webp") - hopper("L").save(temp_file) - with Image.open(temp_file) as image: - assert image.mode == self.rgb_mode - assert image.size == (128, 128) - assert image.format == "WEBP" - - image.load() - image.getdata() - target = hopper("L").convert(self.rgb_mode) - - assert_image_similar(image, target, 10.0) + self._roundtrip(tmp_path, "L", 10.0) def test_write_unsupported_mode_P(self, tmp_path): """ @@ -108,18 +114,7 @@ class TestFileWebp: similar to the original file. """ - temp_file = str(tmp_path / "temp.webp") - hopper("P").save(temp_file) - with Image.open(temp_file) as image: - assert image.mode == self.rgb_mode - assert image.size == (128, 128) - assert image.format == "WEBP" - - image.load() - image.getdata() - target = hopper("P").convert(self.rgb_mode) - - assert_image_similar(image, target, 50.0) + self._roundtrip(tmp_path, "P", 50.0) def test_WebPEncode_with_invalid_args(self): """ diff --git a/src/PIL/WebPImagePlugin.py b/src/PIL/WebPImagePlugin.py index eda685508..6c4f7decf 100644 --- a/src/PIL/WebPImagePlugin.py +++ b/src/PIL/WebPImagePlugin.py @@ -325,6 +325,7 @@ def _save(im, fp, filename): if isinstance(exif, Image.Exif): exif = exif.tobytes() xmp = im.encoderinfo.get("xmp", "") + method = im.encoderinfo.get("method", 0) if im.mode not in _VALID_WEBP_LEGACY_MODES: alpha = ( @@ -342,6 +343,7 @@ def _save(im, fp, filename): float(quality), im.mode, icc_profile, + method, exif, xmp, ) diff --git a/src/_webp.c b/src/_webp.c index babea60f3..dcb6c85cb 100644 --- a/src/_webp.c +++ b/src/_webp.c @@ -545,6 +545,7 @@ PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args) int height; int lossless; float quality_factor; + int method; uint8_t* rgb; uint8_t* icc_bytes; uint8_t* exif_bytes; @@ -556,49 +557,75 @@ PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args) Py_ssize_t exif_size; Py_ssize_t xmp_size; size_t ret_size; + int rgba_mode; + int channels; + int ok; ImagingSectionCookie cookie; + WebPConfig config; + WebPMemoryWriter writer; + WebPPicture pic; - if (!PyArg_ParseTuple(args, "y#iiifss#s#s#", + if (!PyArg_ParseTuple(args, "y#iiifss#is#s#", (char**)&rgb, &size, &width, &height, &lossless, &quality_factor, &mode, - &icc_bytes, &icc_size, &exif_bytes, &exif_size, &xmp_bytes, &xmp_size)) { + &icc_bytes, &icc_size, &method, &exif_bytes, &exif_size, &xmp_bytes, &xmp_size)) { return NULL; } - if (strcmp(mode, "RGBA")==0){ - if (size < width * height * 4){ - Py_RETURN_NONE; - } - #if WEBP_ENCODER_ABI_VERSION >= 0x0100 - if (lossless) { - ImagingSectionEnter(&cookie); - ret_size = WebPEncodeLosslessRGBA(rgb, width, height, 4 * width, &output); - ImagingSectionLeave(&cookie); - } else - #endif - { - ImagingSectionEnter(&cookie); - ret_size = WebPEncodeRGBA(rgb, width, height, 4 * width, quality_factor, &output); - ImagingSectionLeave(&cookie); - } - } else if (strcmp(mode, "RGB")==0){ - if (size < width * height * 3){ - Py_RETURN_NONE; - } - #if WEBP_ENCODER_ABI_VERSION >= 0x0100 - if (lossless) { - ImagingSectionEnter(&cookie); - ret_size = WebPEncodeLosslessRGB(rgb, width, height, 3 * width, &output); - ImagingSectionLeave(&cookie); - } else - #endif - { - ImagingSectionEnter(&cookie); - ret_size = WebPEncodeRGB(rgb, width, height, 3 * width, quality_factor, &output); - ImagingSectionLeave(&cookie); - } - } else { + + rgba_mode = strcmp(mode, "RGBA") == 0; + if (!rgba_mode && strcmp(mode, "RGB") != 0) { Py_RETURN_NONE; } + channels = rgba_mode ? 4 : 3; + if (size < width * height * channels) { + Py_RETURN_NONE; + } + + // Setup config for this frame + if (!WebPConfigInit(&config)) { + PyErr_SetString(PyExc_RuntimeError, "failed to initialize config!"); + return NULL; + } + config.lossless = lossless; + config.quality = quality_factor; + config.method = method; + + // Validate the config + if (!WebPValidateConfig(&config)) { + PyErr_SetString(PyExc_ValueError, "invalid configuration"); + return NULL; + } + + if (!WebPPictureInit(&pic)) { + PyErr_SetString(PyExc_ValueError, "could not initialise picture"); + return NULL; + } + pic.width = width; + pic.height = height; + pic.use_argb = 1; // Don't convert RGB pixels to YUV + + if (rgba_mode) { + WebPPictureImportRGBA(&pic, rgb, channels * width); + } else { + WebPPictureImportRGB(&pic, rgb, channels * width); + } + + WebPMemoryWriterInit(&writer); + pic.writer = WebPMemoryWrite; + pic.custom_ptr = &writer; + + ImagingSectionEnter(&cookie); + ok = WebPEncode(&config, &pic); + ImagingSectionLeave(&cookie); + + WebPPictureFree(&pic); + if (!ok) { + PyErr_SetString(PyExc_ValueError, "encoding error"); + return NULL; + } + output = writer.mem; + ret_size = writer.size; + #ifndef HAVE_WEBPMUX if (ret_size > 0) { PyObject *ret = PyBytes_FromStringAndSize((char*)output, ret_size); From 097104278b762277ff6f05df50556139a12d20c1 Mon Sep 17 00:00:00 2001 From: nulano Date: Sat, 13 Jun 2020 04:01:38 +0200 Subject: [PATCH 02/12] add docs for features module --- docs/reference/ImageGrab.rst | 9 ++++-- docs/reference/features.rst | 59 ++++++++++++++++++++++++++++++++++++ docs/reference/index.rst | 1 + src/PIL/Image.py | 10 +++--- src/PIL/ImageFont.py | 5 +++ src/PIL/features.py | 50 ++++++++++++++++++++++++++++++ 6 files changed, 127 insertions(+), 7 deletions(-) create mode 100644 docs/reference/features.rst diff --git a/docs/reference/ImageGrab.rst b/docs/reference/ImageGrab.rst index ff4646a5f..943fdf69b 100644 --- a/docs/reference/ImageGrab.rst +++ b/docs/reference/ImageGrab.rst @@ -9,7 +9,7 @@ or the clipboard to a PIL image memory. .. versionadded:: 1.1.3 -.. py:function:: PIL.ImageGrab.grab(bbox=None, include_layered_windows=False, all_screens=False, xdisplay=None) +.. py:function:: grab(bbox=None, include_layered_windows=False, all_screens=False, xdisplay=None) Take a snapshot of the screen. The pixels inside the bounding box are returned as an "RGBA" on macOS, or an "RGB" image otherwise. @@ -26,12 +26,15 @@ or the clipboard to a PIL image memory. .. versionadded:: 6.2.0 - :param xdisplay: X11 Display address. Pass ``None`` to grab the default system screen. Pass ``""`` to grab the default X11 screen on Windows or macOS. + :param xdisplay: + X11 Display address. Pass ``None`` to grab the default system screen. Pass ``""`` to grab the default X11 screen on Windows or macOS. + + You can check X11 support using :py:func:`PIL.features.check_feature` with ``feature="xcb"``. .. versionadded:: 7.1.0 :return: An image -.. py:function:: PIL.ImageGrab.grabclipboard() +.. py:function:: grabclipboard() Take a snapshot of the clipboard image, if any. Only macOS and Windows are currently supported. diff --git a/docs/reference/features.rst b/docs/reference/features.rst new file mode 100644 index 000000000..3a10d6201 --- /dev/null +++ b/docs/reference/features.rst @@ -0,0 +1,59 @@ +.. py:currentmodule:: PIL.features + +:py:mod:`features` Module +========================== + +The :py:mod:`PIL.features` module can be used to detect which Pillow features are available on your system. + +.. autofunction:: PIL.features.pilinfo +.. autofunction:: PIL.features.check +.. autofunction:: PIL.features.get_supported + +Modules +------- + +Support for the following modules can be checked: + +* ``pil``: The Pillow core module, required for all functionality. +* ``tkinter``: Tkinter support. +* ``freetype2``: FreeType font support via :py:func:`PIL.ImageFont.truetype`. +* ``littlecms2``: LittleCMS2 support via :py:mod:`PIL.ImageCms`. +* ``webp``: WebP image support. + +.. autofunction:: PIL.features.check_module +.. autofunction:: PIL.features.get_supported_modules + +Codecs +------ + +These are only checked during Pillow compilation. +If the required library was uninstalled from the system, the ``pil`` core module may fail to load instead. + +Support for the following codecs can be checked: + +* ``jpg``: (compile time) LibJpeg support, required for JPEG based image formats. +* ``jpg_2000``: (compile time) OpenJpeg support, required for JPEG 2000 image formats. +* ``zlib``: (compile time) ZLib support, required for ZLib compressed formats, such as PNG. +* ``libtiff``: (compile time) LibTiff support, required for Tiff based image formats. + +.. autofunction:: PIL.features.check_codec +.. autofunction:: PIL.features.get_supported_codecs + +Features +-------- + +Some of these are only checked during Pillow compilation. +If the required library was uninstalled from the system, the relevant module may fail to load instead. + +Support for the following features can be checked: + +* ``libjpeg_turbo``: Whether Pillow was compiled against the libjpeg-turbo version of libjpeg. +* ``transp_webp``: Support for transparency in WebP images. +* ``webp_mux``: (compile time) Support for EXIF data in WebP images. +* ``webp_anim``: (compile time) Support for animated WebP images. +* ``raqm``: Raqm library, required for ``ImageFont.LAYOUT_RAQM`` in :py:func:`PIL.ImageFont.truetype`. +* ``libimagequant``: (compile time) ImageQuant quantization support in :py:func:`PIL.Image.Image.quantize`. +* ``xcb``: (compile time) Support for X11 in :py:func:`PIL.ImageGrab.grab` via the XCB library. + +.. autofunction:: PIL.features.check_feature +.. autofunction:: PIL.features.get_supported_features diff --git a/docs/reference/index.rst b/docs/reference/index.rst index 8c09e7b67..91cde0400 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -30,6 +30,7 @@ Reference PSDraw PixelAccess PyAccess + features ../PIL plugins internal_design diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 930252023..9d94bce0e 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1051,10 +1051,12 @@ class Image: of colors. :param colors: The desired number of colors, <= 256 - :param method: 0 = median cut - 1 = maximum coverage - 2 = fast octree - 3 = libimagequant + :param method: ``Image.MEDIANCUT=0`` (median cut), + ``Image.MAXCOVERAGE=1`` (maximum coverage), + ``Image.FASTOCTREE=2`` (fast octree), + ``Image.LIBIMAGEQUANT=3`` (libimagequant; check support using + :py:func:`PIL.features.check_feature` + with ``feature="libimagequant"``). :param kmeans: Integer :param palette: Quantize to the palette of given :py:class:`PIL.Image.Image`. diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 25ceaa16a..79c161713 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -637,6 +637,11 @@ def truetype(font=None, size=10, index=0, encoding="", layout_engine=None): encoding of any text provided in subsequent operations. :param layout_engine: Which layout engine to use, if available: `ImageFont.LAYOUT_BASIC` or `ImageFont.LAYOUT_RAQM`. + + You can check support for Raqm layout using + :py:func:`PIL.features.check_feature` with ``feature="raqm"``. + + .. versionadded:: 4.2.0 :return: A font object. :exception OSError: If the file could not be read. """ diff --git a/src/PIL/features.py b/src/PIL/features.py index ac06c0f71..74952545a 100644 --- a/src/PIL/features.py +++ b/src/PIL/features.py @@ -17,6 +17,13 @@ modules = { def check_module(feature): + """ + Checks if a module is available. + + :param feature: The module to check for. + :returns: True if available, False otherwise. + :raises ValueError: If the module is not defined in this version of Pillow. + """ if not (feature in modules): raise ValueError("Unknown module %s" % feature) @@ -30,6 +37,9 @@ def check_module(feature): def get_supported_modules(): + """ + :returns: A list of all supported modules. + """ return [f for f in modules if check_module(f)] @@ -37,6 +47,13 @@ codecs = {"jpg": "jpeg", "jpg_2000": "jpeg2k", "zlib": "zip", "libtiff": "libtif def check_codec(feature): + """ + Checks if a codec is available. + + :param feature: The codec to check for. + :returns: True if available, False otherwise. + :raises ValueError: If the codec is not defined in this version of Pillow. + """ if feature not in codecs: raise ValueError("Unknown codec %s" % feature) @@ -46,6 +63,9 @@ def check_codec(feature): def get_supported_codecs(): + """ + :returns: A list of all supported codecs. + """ return [f for f in codecs if check_codec(f)] @@ -61,6 +81,13 @@ features = { def check_feature(feature): + """ + Checks if a feature is available. + + :param feature: The feature to check for. + :returns: True if available, False if unavailable, None if unknown. + :raises ValueError: If the feature is not defined in this version of Pillow. + """ if feature not in features: raise ValueError("Unknown feature %s" % feature) @@ -74,10 +101,19 @@ def check_feature(feature): def get_supported_features(): + """ + :returns: A list of all supported features. + """ return [f for f in features if check_feature(f)] def check(feature): + """ + :param feature: A module, feature, or codec name. + :returns: + True if the module, feature, or codec is available, False or None otherwise. + """ + if feature in modules: return check_module(feature) if feature in codecs: @@ -89,6 +125,10 @@ def check(feature): def get_supported(): + """ + :returns: A list of all supported modules, features, and codecs. + """ + ret = get_supported_modules() ret.extend(get_supported_features()) ret.extend(get_supported_codecs()) @@ -96,6 +136,16 @@ def get_supported(): def pilinfo(out=None, supported_formats=True): + """ + Prints information about this installation of Pillow. + This function can be called with ``python -m PIL``. + + :param out: + The output stream to print to. Defaults to ``sys.stdout`` if None. + :param supported_formats: + If True, a list of all supported image file formats will be printed. + """ + if out is None: out = sys.stdout From e19713ebc9a95eabad3292a5ddc35a4e9eb45947 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 14 Jun 2020 12:03:03 +1000 Subject: [PATCH 03/12] Added release notes for 7.2.0 [ci skip] --- docs/releasenotes/7.2.0.rst | 29 +++++++++++++++++++++++++++++ docs/releasenotes/index.rst | 1 + 2 files changed, 30 insertions(+) create mode 100644 docs/releasenotes/7.2.0.rst diff --git a/docs/releasenotes/7.2.0.rst b/docs/releasenotes/7.2.0.rst new file mode 100644 index 000000000..aeea7e9d9 --- /dev/null +++ b/docs/releasenotes/7.2.0.rst @@ -0,0 +1,29 @@ +7.2.0 +----- + +API Changes +=========== + +Replaced TiffImagePlugin DEBUG with logging +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``TiffImagePlugin.DEBUG = True`` has been a way to print various debugging +information when interacting with TIFF images. This has now been removed +in favour of Python's ``logging`` module, already used in other places in the +Pillow source code. + +Corrected default offset when writing EXIF data +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Previously, the default ``offset`` argument for +:py:meth:`~PIL.Image.Exif.tobytes` was 0, which did not include the magic +header. It is now 8. + +Moved to ImageFileDirectory_v2 in Image.Exif +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Moved from the legacy :py:class:`PIL.TiffImagePlugin.ImageFileDirectory_v1` to +:py:class:`PIL.TiffImagePlugin.ImageFileDirectory_v2` in +:py:class:`PIL.Image.Exif`. This means that Exif RATIONALs and SIGNED_RATIONALs +as now read as :py:class:`PIL.TiffImagePlugin.IFDRational`, instead of as a +tuple with a numerator and a denominator. diff --git a/docs/releasenotes/index.rst b/docs/releasenotes/index.rst index 575931c4d..4dd7d5e63 100644 --- a/docs/releasenotes/index.rst +++ b/docs/releasenotes/index.rst @@ -6,6 +6,7 @@ Release Notes .. toctree:: :maxdepth: 2 + 7.2.0 7.1.2 7.1.1 7.1.0 From d05a08a298657d65233fdafbdca3403fe9fe3311 Mon Sep 17 00:00:00 2001 From: nulano Date: Sun, 14 Jun 2020 00:45:29 +0200 Subject: [PATCH 04/12] formatting improvements Co-authored-by: Hugo --- docs/reference/features.rst | 13 +++++++------ src/PIL/features.py | 13 +++++++------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/docs/reference/features.rst b/docs/reference/features.rst index 3a10d6201..196f938ed 100644 --- a/docs/reference/features.rst +++ b/docs/reference/features.rst @@ -1,3 +1,4 @@ +.. py:module:: PIL.features .. py:currentmodule:: PIL.features :py:mod:`features` Module @@ -17,7 +18,7 @@ Support for the following modules can be checked: * ``pil``: The Pillow core module, required for all functionality. * ``tkinter``: Tkinter support. * ``freetype2``: FreeType font support via :py:func:`PIL.ImageFont.truetype`. -* ``littlecms2``: LittleCMS2 support via :py:mod:`PIL.ImageCms`. +* ``littlecms2``: LittleCMS 2 support via :py:mod:`PIL.ImageCms`. * ``webp``: WebP image support. .. autofunction:: PIL.features.check_module @@ -31,10 +32,10 @@ If the required library was uninstalled from the system, the ``pil`` core module Support for the following codecs can be checked: -* ``jpg``: (compile time) LibJpeg support, required for JPEG based image formats. -* ``jpg_2000``: (compile time) OpenJpeg support, required for JPEG 2000 image formats. -* ``zlib``: (compile time) ZLib support, required for ZLib compressed formats, such as PNG. -* ``libtiff``: (compile time) LibTiff support, required for Tiff based image formats. +* ``jpg``: (compile time) Libjpeg support, required for JPEG based image formats. +* ``jpg_2000``: (compile time) OpenJPEG support, required for JPEG 2000 image formats. +* ``zlib``: (compile time) Zlib support, required for zlib compressed formats, such as PNG. +* ``libtiff``: (compile time) LibTIFF support, required for TIFF based image formats. .. autofunction:: PIL.features.check_codec .. autofunction:: PIL.features.get_supported_codecs @@ -47,7 +48,7 @@ If the required library was uninstalled from the system, the relevant module may Support for the following features can be checked: -* ``libjpeg_turbo``: Whether Pillow was compiled against the libjpeg-turbo version of libjpeg. +* ``libjpeg_turbo``: (compile time) Whether Pillow was compiled against the libjpeg-turbo version of libjpeg. * ``transp_webp``: Support for transparency in WebP images. * ``webp_mux``: (compile time) Support for EXIF data in WebP images. * ``webp_anim``: (compile time) Support for animated WebP images. diff --git a/src/PIL/features.py b/src/PIL/features.py index 74952545a..33e89cf24 100644 --- a/src/PIL/features.py +++ b/src/PIL/features.py @@ -21,7 +21,7 @@ def check_module(feature): Checks if a module is available. :param feature: The module to check for. - :returns: True if available, False otherwise. + :returns: ``True`` if available, ``False`` otherwise. :raises ValueError: If the module is not defined in this version of Pillow. """ if not (feature in modules): @@ -51,7 +51,7 @@ def check_codec(feature): Checks if a codec is available. :param feature: The codec to check for. - :returns: True if available, False otherwise. + :returns: ``True`` if available, ``False`` otherwise. :raises ValueError: If the codec is not defined in this version of Pillow. """ if feature not in codecs: @@ -85,7 +85,7 @@ def check_feature(feature): Checks if a feature is available. :param feature: The feature to check for. - :returns: True if available, False if unavailable, None if unknown. + :returns: ``True`` if available, ``False`` if unavailable, ``None`` if unknown. :raises ValueError: If the feature is not defined in this version of Pillow. """ if feature not in features: @@ -111,7 +111,8 @@ def check(feature): """ :param feature: A module, feature, or codec name. :returns: - True if the module, feature, or codec is available, False or None otherwise. + ``True`` if the module, feature, or codec is available, + ``False`` or ``None`` otherwise. """ if feature in modules: @@ -141,9 +142,9 @@ def pilinfo(out=None, supported_formats=True): This function can be called with ``python -m PIL``. :param out: - The output stream to print to. Defaults to ``sys.stdout`` if None. + The output stream to print to. Defaults to ``sys.stdout`` if ``None``. :param supported_formats: - If True, a list of all supported image file formats will be printed. + If ``True``, a list of all supported image file formats will be printed. """ if out is None: From 7119ef54fb3f3c798ae000c71b10e0763a8587c9 Mon Sep 17 00:00:00 2001 From: nulano Date: Sun, 14 Jun 2020 08:35:55 +0200 Subject: [PATCH 05/12] fix PyAccess docs using deferred_error --- src/PIL/PyAccess.py | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/PIL/PyAccess.py b/src/PIL/PyAccess.py index 359a94919..494f5f9f4 100644 --- a/src/PIL/PyAccess.py +++ b/src/PIL/PyAccess.py @@ -23,23 +23,29 @@ import logging import sys -from cffi import FFI +try: + from cffi import FFI + + defs = """ + struct Pixel_RGBA { + unsigned char r,g,b,a; + }; + struct Pixel_I16 { + unsigned char l,r; + }; + """ + ffi = FFI() + ffi.cdef(defs) +except ImportError as ex: + # Allow error import for doc purposes, but error out when accessing + # anything in core. + from ._util import deferred_error + + FFI = ffi = deferred_error(ex) logger = logging.getLogger(__name__) -defs = """ -struct Pixel_RGBA { - unsigned char r,g,b,a; -}; -struct Pixel_I16 { - unsigned char l,r; -}; -""" -ffi = FFI() -ffi.cdef(defs) - - class PyAccess: def __init__(self, img, readonly=False): vals = dict(img.im.unsafe_ptrs) From e134216609975e9190a427361d5339d9356d52b8 Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Sun, 14 Jun 2020 16:53:30 +1000 Subject: [PATCH 06/12] Fixed typo [ci skip] Co-authored-by: Hugo van Kemenade --- docs/releasenotes/7.2.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/releasenotes/7.2.0.rst b/docs/releasenotes/7.2.0.rst index aeea7e9d9..904e9d5ab 100644 --- a/docs/releasenotes/7.2.0.rst +++ b/docs/releasenotes/7.2.0.rst @@ -25,5 +25,5 @@ Moved to ImageFileDirectory_v2 in Image.Exif Moved from the legacy :py:class:`PIL.TiffImagePlugin.ImageFileDirectory_v1` to :py:class:`PIL.TiffImagePlugin.ImageFileDirectory_v2` in :py:class:`PIL.Image.Exif`. This means that Exif RATIONALs and SIGNED_RATIONALs -as now read as :py:class:`PIL.TiffImagePlugin.IFDRational`, instead of as a +are now read as :py:class:`PIL.TiffImagePlugin.IFDRational`, instead of as a tuple with a numerator and a denominator. From 05932c4cbc9a57bd20063e424d9b5b71b8949f6a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 14 Jun 2020 22:23:19 +1000 Subject: [PATCH 07/12] Updated harfbuzz to 2.6.7 --- Tests/images/variation_adobe.png | Bin 1479 -> 1482 bytes Tests/images/variation_adobe_axes.png | Bin 1448 -> 1439 bytes Tests/images/variation_adobe_name.png | Bin 1454 -> 1461 bytes .../images/variation_adobe_older_harfbuzz.png | Bin 0 -> 1479 bytes .../variation_adobe_older_harfbuzz_axes.png | Bin 0 -> 1448 bytes .../variation_adobe_older_harfbuzz_name.png | Bin 0 -> 1454 bytes Tests/test_imagefont.py | 44 +++++++++--------- winbuild/build_prepare.py | 6 +-- 8 files changed, 25 insertions(+), 25 deletions(-) create mode 100644 Tests/images/variation_adobe_older_harfbuzz.png create mode 100644 Tests/images/variation_adobe_older_harfbuzz_axes.png create mode 100644 Tests/images/variation_adobe_older_harfbuzz_name.png diff --git a/Tests/images/variation_adobe.png b/Tests/images/variation_adobe.png index 71b879bc5a214d09e4b04898327324ce2432e9c4..3ac326f51966bc36b869ab74b5c487844515d102 100644 GIT binary patch delta 1425 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 LNkvXXu0mjf_V=Kc diff --git a/Tests/images/variation_adobe_axes.png b/Tests/images/variation_adobe_axes.png index 9376c1d7b8fc6b11907267aec1a32ad73a0c45fd..93cad98ff0c0c230206f864e5dd9d15505715775 100644 GIT binary patch delta 1317 zcmV+=1={+k3!e*+B!6Z}L_t(|ob8!QNHS{}$H$cD;-U$@+7!`+AV>?W=nh3>5iJ5& z-5^~=E-W||?V>0Mq7ot62SE^BAV`CvqzhKqMyV_+D(IeOH@lfA&3E|X z{jJ9L{rk)_KgaQ%k>_%`D8kq9`@b9blOVDH5Lp0-EC56n0Fy`sD1QW>CU`s^>*_!3 z0srT+5OsBRK|w+2Wlv8J+EyqOAt52m_T%GYe}Dfswa1>Rsj1b~Rf?ioT3P}E0=}oH z>ob^`L?ZDz&S10I@Ts)4v@aItj?~mtuf6%gSFc$a85v*y06<|buv)D~qw(9z{!Cdj zGc#{*Z{J?_XUZBL9)ISf?x$bW+S*!`O656@7xBCZ?L}xe=N%lqtPf5i71NV z@puIV1xlqdFE5W-ln4ZZ&d$!aw>K2t+S;0&oW$#=r>E<5I)9|qY&MULja5}uF|!(t z#$+w|D9F zdbGr&Y5L^k1fP9A-rE=Y9bmY*x$(U>7H}(z&*!7e@$oV1 zeZ9QA;D5VQDwV$X#sY31{sjjIqwR}}3)XceCMNKg0|NsK3k$5Pv4&e&F)=Y{duL~d zU6HM=ExBCY+uK`TU(bxVTrPz|@%Z@o4K0FTeNN&<-aFOn>grIYrly9)UT$x16A}_~ zb90?e=iS{M{u9yM-0VwlEZ|m_UavlqCU%ybnwzf9UohBzIqX51betLR>ALzfpWZCWZ_4Rd~PB%C>h|5x|)mp9A zXf%4YW-^)Ljwu1i;IgT zB_((@jYi`py05Pf&1Pj~SuB?A?d|I7YJaIz3KfI{lf`^5KH$BlYj(R`rBdN52?jo& zFO$g*4i5g)lQ%w?!UGIWr&BJMy9Godk-=a99j_bgZ+BSWa5$EhmNqvx?RI-WKtN1P zOioS?{+;?>Ha0fq=H{-iuR}vaB@#($YN{{)fl?IpTV?&}kiYK%5Lp0-EC56n0Fy`s bM;5@};f|xBWRC#L00000NkvXXu0mjfuBeav delta 1329 zcmV-112&%%M5`) zz`Obnd%*8n7NWMc*5BVBz3l1fLC0#fIxsMhIevY89T*t!PeIn8R+AVG5J3GUppP!$}$;l#-$Y3zI-=uTAB+G0zOC%B|d3t*KTePdI ziwXJo_?S#4fA(~>T8-8!EiJ`Eot>Q&MfLahyWgZsyd*0tD~rj@%F1F#kB^U0DV0ja zPPo0j4G9TBIisVa%(%s35sSs;<>ek}(k0%IH8(ehGPPPQJHckNp@i`8aEC>Phlf#4 zXlUro&CSQhhg>dCOiX-vd2zoNT{B&5Spr8PyRaI4W zbaY6i(*6B?k2J{z0nPVb7r(*2ySqd8hHPXqnZ;sZj+IJfL_`E0y}Z1fn3#BadO{;_ zZ*O9;m|f@K;2=Ie9{(8Xbh^UALXR}b3ErCVBF^8mpr9b`{e*>uIVpvH3K$+99z4|~ z7rY@$e<&29%+u3T-Ye2H{qgaEudC5$Jk=x@yz%faARqu8UtL|fQ)+T@5`Q^3IJmgD z=uSZp-jEd)6@`xX_V#$!+1}n(sZ_naz4i6=%m_`>YPI_H_0`=LIl}uhi5GGH39qfK zMVZyr)%=!we0+?Hi_6K$vD@uWPfz%-M00bqfBQ{x!5gv+1_R2JNF*mGC%?+PzrQ~? zIB*zJDwPt61V#0FJ*p`b3O6=3+-#ByUfRJc6bh8AP$+CR8$0Lu`8h8yucV}e9qR7x zrYLG~a1bA?sHi|S6A}{M-{092@#o+d7Z-rjbA*>G0cDr4rQhiI1$z-dmtDqBS;D5Wr z8=K9xyu7@%wPm$heSLkSqN1|1v+*yK$JyN6oS&b+zrPO(3X;p^DJd!Z{tW#M2D1KK n$lv#ooG+7V1s@pzkp;D diff --git a/Tests/images/variation_adobe_name.png b/Tests/images/variation_adobe_name.png index 9e5fe70e539e1640d6e9d807c2841487369d25b3..acb0df63c57628b321eb635ef0f588c374d838dd 100644 GIT binary patch delta 1326 zcmV+}1=0Gh3$+W7B!7HKL_t(|ob8%jNKKq4-~!7a5K_EP%q}yF4lzz6@efjLz`bow9E#!85-Np|Kh;; zza95+p0gbzcz;)C@AJIR`n$dgY6GJi-fOOyfK-QB{U z!to&nWtW$iGso}WzvmeGEBO8Xsi`T1kkM$oe*JoOic;xdGQWKJl5iX$R8v!fhwk0G zM~`=Pb)~H@5O?q1P1su|aNV<%N@YPo0mtOBvNDdD!r}FLvtE;{EYSJ?by?rOeOp>u z%6eI@vPw%!xfcZ`3x9#v>!k$v@i_ekfBW`rgiu{w9rvQAPoH);9Fa)m!i5X0(CX^y zyLaz`!Jt$s)#-E=izPolAJj1yCMG7bUK1e>8+_?&nM@|c`BD@GyLI5l-rnBLn>Rfk4}U^PrBXQ@4wK0=KR-V- zG!%_SJs!{e{Jhuey?F5=Gv(#Wm+R~6U0q#x9lPCbwOZ+8m&>)hyxiN{OMkq2^{U-& zkHunYwOXxKr?{=j2;Q3UA}Ow{gM)+0%1Sy-p-?O>Eis|-@o}a?VPRn?6k^T&@ZrOW z6aVk37Kz{C`2OB^z<~p-k=CBtD~cXNxOglJ|2q4<9fZGi9UPw?CA9N z_I7-s!NEa#JQ|H&x^xL4^x(k*-n~H)R#qSokVqs<+UDjaE1{~Yiiw)d=A#vU{`{%a z>6je3T)wupMo|>~AZ4@J@M{g!8xMq)g>O1qt(FxH27iNUwVFxLXfz3n%*@Q-YgJcQ zzkT~wEEboQmF?~A@$QWW!pf?ssKC?8G0vVno12@9M{{y=lC5R4+0dV;RH~Jgl}vi$ zfv~c)S}o#CNl8hvwYIjld?gq2lPRKy%_Y;1(XVSaV| zet%nA8*9+%bbkK)nSXO|gq4NgIv*Y$(l_x?_V@R%UAy-C_wU8U#m>%7W;`4Y-@0`x z3te)D_il$5Nw$g3&CTIydc8gz4j)a6#bQpU^MB*Vj|q#=?>QeoeoRr+uV24PN=opW z&!0c%*BcLnl@*W2&1N&6dHM3?U@(a1ghHW)h6aRCV`Jmd3L_&U2%(CKifA-SkNA8( zyr5F4+}_@1&BC9h&!0cf`y5#a{W;p5HC&3M7u+FI7cbLY-6(UT`n(${P(7JKpHMI;j8f6Ab2 zzu!MJH1zP{!#j8Gpg-y8=y>wv$=KM~(OC-%3#ng88w>{4chK#2x3;!237wstZnv8b zO-xKUolaH-l}gpq)ARcEYdU9OV1RY?S%0logTa6hnw*^E z78X`kR>I-1OeQlLjTVaqe>}`Co12?&-n{wx^{YamFdB_lu3Sm|UN9}kD(hc|`~rLr klT8In93Zj)5Lp2J1L6mgG~L9#cK`qY07*qoM6N<$f@oH){r~^~ delta 1325 zcmV+|1=9Mp3$6>0B!6{DL_t(|ob8%RNE=-gfNwNDVq#1{B=|%UMBRu|A1xBajSqZm zbf*>3q9TZ(K|wUS(1nkRq6=Gm;S0fbp{p!(ApsRZkRl-(>kBbj8?Z^mN|NbfVE${= zWKN9ZU%20Da?iPE&ix!OGn4Qrib4c-c)fN5{}MzN03r(jk&(9^k-iRpfNNR2XVB5n z@$yUH4n8P5H8qtv-rU?|8G2oGI^Dv;0zyckP?VOIKC2ex3MTXH?CjBTgiv;NHXdqe zX`#o}YPHMyFHLiE^P^3=0oy%GEEb1`hO$hKi;H8K$pIRT##5!($^xzb|0C<-;$mlK z$5YkV%8HGRWnUDO{Gw=oG#bit-y*x-;H#^v5kmR-`Rt1Z1_m}ZHd-u}H*elNm9w|E zx3RHdG#dT<{KCS*luBhtNC^8=uw-Ut#*^jT-tF)r?DtN)-99%r7at$*&<3B+FDxuP zK0bcx6pyz3RA_vBJSHYaAP{(V;jmkWXL5ag{r>&?rKKf=kVGPXX>4qakB?tlTN@r8 zwpy)AOG|5OYZ{FvB_)N(+K zgpkcy@i_{6QP zEibSC_Ex1*X}8;dnW({F5Q#)Q9&d4R@%c?UhdWsf4Gm0wYilbWvfJ%ZQBh2^x3||} zZhd_{e!8KdA$r_uwZ47(79rHu*5;8Wox_nVgTdhA#=LO?(O9`*J0eY8kHK>@mtL?Y4a z^`2_dB^=3;$z+H%a=HA`BFD$aLZJ}P92pt$V3RK4=zZ4J)fIEBR4SeKWH1;WEfN_S z+1c5Nk9_@q`jz7FXPz9%^7Hd!jxR4S-L4ZH9E^WlSy}n?>680y0pUniL_`F0e0X?h zHk;ijqSNW>>gpT@RVvlV$;q>tV+}{L@LT6UfBw)n@gRQx{#{&Le0O)Zy}jMk)WnRN z&E~4AD$jK361R6dyok%ab9s3g&yR|VGMmj0(`+_>n@Xkn_U+rl5dD&~tE-EmsGFM` zxm=DH{QUXz^P6-DN3!g8dvbCzKIPrJcSfTT&oP-yMMXsjq4M%_hv}oEqX?n2v^1;L zN{_6ptl-teV)5ze>BIBl&(cXrNp3HehP%g|gM$OHSd7mS2n1ziWj#GTA3uKl@Zkd; zmC0m(KY#wjCvIi87CnhFpYHFBhQ&ZE# z!~`9hnVC_kR1VW65=mcQ-~9YMQ(6l zu~@WPtzNG;o6Q1&K%r15l}h~a(6byJ9j&geo}ZtKL?VSkk(HI@{Jr2)ypF7Y6!P@} jJ(FJrK$BVp5f=ChdVQEGH*j zsPhV&{MTb_MLP2Rxur*+v++qVONuCB)6ZU{thsH=;$xC!1&l*{Epp^(ev zR@E3-dbPb-R5@g)7ewwB#f^;z$_mk3D?(&sBp`TeXPZu^iwQhtQ4vVphcn`lwW(C} zmAs#$pLrD(&Xe8^cfev>EX@a^lKuui|27maqNTW`r02;K3Wefc97Q6zm@^M^?u$es zu^4mcP-&9W!Gp^S3o}$Sxw$(oATZEqH#$0cOF0{sle7E!wK=r5y1KfymSI>!;7L=F zD?7hhLk7xwwHJ~JbUNlv=*g3bt@4@4l@+qTzXl{{ZK3SVpWEBp`T0QK0B`TExw*Sh z)b#X&C=|hv(c9}!L@SvT!HXowfw%Nqa8ROto*!q#&{suk>+7Dzx}aABEIIAQ9_i`nX=t$b1@@7bKRmvGzLS-erBpaZ zr}_Bt<7%-e(PI(B(JB(FT>E(^mb+Cvh`Tr1e?t+DA3A?4huWC z`YG59li({zy01348*HdvzO}X(H2~k)+6aqwL!&n*C%vEP6GLW#Q_|8LJ-@CkvZkz5 zw~?Kl-Jbpd#;|}uM#R{a1Yj58B&j z6&PqOpFb4!dTOdRS=m=&IWREr@#Du+otZ|Or#deB@Knzd6CM3^>EE6_gEYEGZMt)* zBXi^WrwJjs}g#DF$ICBG#Zo192y?} zkDX1S{G;%!iEY7RFvd3`A|h0K#{1D|G!BOY27}$&gu+A=OZ8td^jPddD&CN}C<;%>jjaV>QBd1gIyW#6Qp>9jV2Nj7}ZC$re P0W=`cACGJHjl23kUTwM} literal 0 HcmV?d00001 diff --git a/Tests/images/variation_adobe_older_harfbuzz_axes.png b/Tests/images/variation_adobe_older_harfbuzz_axes.png new file mode 100644 index 0000000000000000000000000000000000000000..9376c1d7b8fc6b11907267aec1a32ad73a0c45fd GIT binary patch literal 1448 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 z Date: Sun, 14 Jun 2020 23:08:21 +1000 Subject: [PATCH 08/12] Removed test skips for MinGW --- Tests/test_imagefont.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index bf7cda5e8..e20950ca6 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -13,7 +13,6 @@ from .helper import ( assert_image_equal, assert_image_similar, assert_image_similar_tofile, - is_mingw, is_pypy, is_win32, skip_unless_feature, @@ -677,7 +676,6 @@ class TestImageFont: else: raise - @pytest.mark.skipif(is_mingw(), reason="epsilon too high for meaningful test") def test_variation_set_by_name(self): font = self.get_font() @@ -702,7 +700,6 @@ class TestImageFont: font.set_variation_by_name(name) self._check_text(font, "Tests/images/variation_tiny_name.png", 40) - @pytest.mark.skipif(is_mingw(), reason="epsilon too high for meaningful test") def test_variation_set_by_axes(self): font = self.get_font() From 448cc46b1ea81711ab0508e6e95574b5b3f57ea7 Mon Sep 17 00:00:00 2001 From: nulano Date: Tue, 16 Jun 2020 03:21:38 +0200 Subject: [PATCH 09/12] fix tcl tests --- .github/workflows/test-windows.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 7ae26b883..83dc5748b 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -52,6 +52,11 @@ jobs: python-version: ${{ matrix.python-version }} architecture: ${{ matrix.architecture }} + - name: Set up TCL + if: "contains(matrix.python-version, 'pypy')" + run: Write-Host "::set-env name=TCL_LIBRARY::$env:pythonLocation\tcl\tcl8.5" + shell: pwsh + - name: Print build system information run: python .github/workflows/system-info.py From 18e974ae6f3ce36b7e27e2d4891bab08e57e6a0c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 17 Jun 2020 07:54:00 +1000 Subject: [PATCH 10/12] Updated lcms2 to 2.11 --- docs/installation.rst | 2 +- winbuild/build_prepare.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 1b5f2e056..e46bdf56c 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -156,7 +156,7 @@ Many of Pillow's features require external libraries: * **littlecms** provides color management * Pillow version 2.2.1 and below uses liblcms1, Pillow 2.3.0 and - above uses liblcms2. Tested with **1.19** and **2.7-2.9**. + above uses liblcms2. Tested with **1.19** and **2.7-2.11**. * **libwebp** provides the WebP format. diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 0ba8a135c..5bde823ca 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -195,9 +195,9 @@ deps = { # "bins": [r"objs\{msbuild_arch}\Release\freetype.dll"], }, "lcms2": { - "url": SF_MIRROR + "/project/lcms/lcms/2.10/lcms2-2.10.tar.gz", - "filename": "lcms2-2.10.tar.gz", - "dir": "lcms2-2.10", + "url": SF_MIRROR + "/project/lcms/lcms/2.11/lcms2-2.11.tar.gz", + "filename": "lcms2-2.11.tar.gz", + "dir": "lcms2-2.11", "patch": { r"Projects\VC2017\lcms2_static\lcms2_static.vcxproj": { # default is /MD for x86 and /MT for x64, we need /MD always From 6ad98ba3c0c371c9bddf53b0939e7632ede8c2b7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 18 Jun 2020 21:40:38 +1000 Subject: [PATCH 11/12] Do not ignore viewer if order is zero when registering --- Tests/test_imageshow.py | 12 +++++++----- src/PIL/ImageShow.py | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Tests/test_imageshow.py b/Tests/test_imageshow.py index 64f15326b..fddc73bd1 100644 --- a/Tests/test_imageshow.py +++ b/Tests/test_imageshow.py @@ -17,19 +17,21 @@ def test_register(): ImageShow._viewers.pop() -def test_viewer_show(): +@pytest.mark.parametrize( + "order", [-1, 0], +) +def test_viewer_show(order): class TestViewer(ImageShow.Viewer): - methodCalled = False - def show_image(self, image, **options): self.methodCalled = True return True viewer = TestViewer() - ImageShow.register(viewer, -1) + ImageShow.register(viewer, order) for mode in ("1", "I;16", "LA", "RGB", "RGBA"): - with hopper() as im: + viewer.methodCalled = False + with hopper(mode) as im: assert ImageShow.show(im) assert viewer.methodCalled diff --git a/src/PIL/ImageShow.py b/src/PIL/ImageShow.py index fc5089423..cd85e81b4 100644 --- a/src/PIL/ImageShow.py +++ b/src/PIL/ImageShow.py @@ -31,7 +31,7 @@ def register(viewer, order=1): pass # raised if viewer wasn't a class if order > 0: _viewers.append(viewer) - elif order < 0: + else: _viewers.insert(0, viewer) From 84b8776bfcc85e671d7ce5eaccec12e833379b4d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 20 Jun 2020 08:29:35 +1000 Subject: [PATCH 12/12] Updated libjpeg-turbo to 2.0.4 --- winbuild/build_prepare.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 0ba8a135c..357d7dcb3 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -105,9 +105,9 @@ header = [ # dependencies, listed in order of compilation deps = { "libjpeg": { - "url": SF_MIRROR + "/project/libjpeg-turbo/2.0.3/libjpeg-turbo-2.0.3.tar.gz", - "filename": "libjpeg-turbo-2.0.3.tar.gz", - "dir": "libjpeg-turbo-2.0.3", + "url": SF_MIRROR + "/project/libjpeg-turbo/2.0.4/libjpeg-turbo-2.0.4.tar.gz", + "filename": "libjpeg-turbo-2.0.4.tar.gz", + "dir": "libjpeg-turbo-2.0.4", "build": [ cmd_cmake( [