From d7d58a36eafd97207687b44cb8fb901f592c8fc0 2024-10-25 08:29:17 From: M Bussonnier Date: 2024-10-25 08:29:17 Subject: [PATCH] ENH: add webp support to IPython.display.Image and complete identification of JPEG,PNG,GIF,WEBP image types by initial bytes (#14526) - So that `Image('./example.webp')` does not raise a `"Cannot embed the 'webp' image format"` error. - So that matplotlib animations saved as .webp images work (e.g. when ffmpeg+h264 isn't working). --- diff --git a/IPython/core/display.py b/IPython/core/display.py index 5c4557b..c3c4401 100644 --- a/IPython/core/display.py +++ b/IPython/core/display.py @@ -21,13 +21,35 @@ from IPython.testing.skipdoctest import skip_doctest from . import display_functions -__all__ = ['display_pretty', 'display_html', 'display_markdown', - 'display_svg', 'display_png', 'display_jpeg', 'display_latex', 'display_json', - 'display_javascript', 'display_pdf', 'DisplayObject', 'TextDisplayObject', - 'Pretty', 'HTML', 'Markdown', 'Math', 'Latex', 'SVG', 'ProgressBar', 'JSON', - 'GeoJSON', 'Javascript', 'Image', 'set_matplotlib_formats', - 'set_matplotlib_close', - 'Video'] +__all__ = [ + "display_pretty", + "display_html", + "display_markdown", + "display_svg", + "display_png", + "display_jpeg", + "display_webp", + "display_latex", + "display_json", + "display_javascript", + "display_pdf", + "DisplayObject", + "TextDisplayObject", + "Pretty", + "HTML", + "Markdown", + "Math", + "Latex", + "SVG", + "ProgressBar", + "JSON", + "GeoJSON", + "Javascript", + "Image", + "set_matplotlib_formats", + "set_matplotlib_close", + "Video", +] _deprecated_names = ["display", "clear_output", "publish_display_data", "update_display", "DisplayHandle"] @@ -200,6 +222,23 @@ def display_jpeg(*objs, **kwargs): _display_mimetype('image/jpeg', objs, **kwargs) +def display_webp(*objs, **kwargs): + """Display the WEBP representation of an object. + + Parameters + ---------- + *objs : object + The Python objects to display, or if raw=True raw JPEG data to + display. + raw : bool + Are the data objects raw data or Python objects that need to be + formatted before display? [default: False] + metadata : dict (optional) + Metadata to be associated with the specific mimetype output. + """ + _display_mimetype("image/webp", objs, **kwargs) + + def display_latex(*objs, **kwargs): """Display the LaTeX representation of an object. @@ -776,9 +815,14 @@ class Javascript(TextDisplayObject): r += _lib_t2*len(self.lib) return r -# constants for identifying png/jpeg data -_PNG = b'\x89PNG\r\n\x1a\n' -_JPEG = b'\xff\xd8' + +# constants for identifying png/jpeg/gif/webp data +_PNG = b"\x89PNG\r\n\x1a\n" +_JPEG = b"\xff\xd8" +_GIF1 = b"GIF87a" +_GIF2 = b"GIF89a" +_WEBP = b"WEBP" + def _pngxy(data): """read the (width, height) from a PNG header""" @@ -786,6 +830,7 @@ def _pngxy(data): # next 8 bytes are width/height return struct.unpack('>ii', data[ihdr+4:ihdr+12]) + def _jpegxy(data): """read the (width, height) from a JPEG header""" # adapted from http://www.64lines.com/jpeg-width-height @@ -805,22 +850,45 @@ def _jpegxy(data): h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9]) return w, h + def _gifxy(data): """read the (width, height) from a GIF header""" return struct.unpack('> 24) + height = 1 + ( + (((size_info >> 8) & 0xF) << 10) + | (((size_info >> 14) & 0x3FC) << 2) + | ((size_info >> 22) & 0x3) + ) + return (width, height) + else: + raise ValueError("Not a valid WEBP header") + + class Image(DisplayObject): - _read_flags = 'rb' - _FMT_JPEG = u'jpeg' - _FMT_PNG = u'png' - _FMT_GIF = u'gif' - _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG, _FMT_GIF] + _read_flags = "rb" + _FMT_JPEG = "jpeg" + _FMT_PNG = "png" + _FMT_GIF = "gif" + _FMT_WEBP = "webp" + _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG, _FMT_GIF, _FMT_WEBP] _MIMETYPES = { - _FMT_PNG: 'image/png', - _FMT_JPEG: 'image/jpeg', - _FMT_GIF: 'image/gif', + _FMT_PNG: "image/png", + _FMT_JPEG: "image/jpeg", + _FMT_GIF: "image/gif", + _FMT_WEBP: "image/webp", } def __init__( @@ -837,7 +905,7 @@ class Image(DisplayObject): metadata=None, alt=None, ): - """Create a PNG/JPEG/GIF image object given raw data. + """Create a PNG/JPEG/GIF/WEBP image object given raw data. When this object is returned by an input cell or passed to the display function, it will result in the image being displayed @@ -858,7 +926,7 @@ class Image(DisplayObject): Images from a file are always embedded. format : unicode - The format of the image data (png/jpeg/jpg/gif). If a filename or URL is given + The format of the image data (png/jpeg/jpg/gif/webp). If a filename or URL is given for format will be inferred from the filename extension. embed : bool @@ -942,6 +1010,8 @@ class Image(DisplayObject): format = self._FMT_PNG elif ext == u'gif': format = self._FMT_GIF + elif ext == "webp": + format = self._FMT_WEBP else: format = ext.lower() elif isinstance(data, bytes): @@ -949,6 +1019,12 @@ class Image(DisplayObject): # only if format has not been specified. if data[:2] == _JPEG: format = self._FMT_JPEG + elif data[:8] == _PNG: + format = self._FMT_PNG + elif data[8:12] == _WEBP: + format = self._FMT_WEBP + elif data[:6] == _GIF1 or data[:6] == _GIF2: + format = self._FMT_GIF # failed to detect format, default png if format is None: diff --git a/docs/source/whatsnew/version8.rst b/docs/source/whatsnew/version8.rst index 103f801..8c9945e 100644 --- a/docs/source/whatsnew/version8.rst +++ b/docs/source/whatsnew/version8.rst @@ -1,6 +1,15 @@ ============ 8.x Series ============ + +.. _version 8.29: + +IPython 8.29 +============ + + + - Add support for WEBP to ``IPython.display.Image``. :ghpull:`14526` + .. _version 8.28: IPython 8.28