mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-01-26 09:14:27 +03:00
Remove webp animations flags and conditions
Removed: _webp.WebPDecode _webp.HAVE_WEBPANIM features.webp_anim
This commit is contained in:
parent
9bed5b4264
commit
a3468996c0
|
@ -27,7 +27,6 @@ def test_wheel_codecs() -> None:
|
||||||
|
|
||||||
def test_wheel_features() -> None:
|
def test_wheel_features() -> None:
|
||||||
expected_features = {
|
expected_features = {
|
||||||
"webp_anim",
|
|
||||||
"raqm",
|
"raqm",
|
||||||
"fribidi",
|
"fribidi",
|
||||||
"harfbuzz",
|
"harfbuzz",
|
||||||
|
|
|
@ -10,11 +10,6 @@ from PIL import features
|
||||||
|
|
||||||
from .helper import skip_unless_feature
|
from .helper import skip_unless_feature
|
||||||
|
|
||||||
try:
|
|
||||||
from PIL import _webp
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def test_check() -> None:
|
def test_check() -> None:
|
||||||
# Check the correctness of the convenience function
|
# Check the correctness of the convenience function
|
||||||
|
@ -51,11 +46,6 @@ def test_version() -> None:
|
||||||
test(feature, features.version_feature)
|
test(feature, features.version_feature)
|
||||||
|
|
||||||
|
|
||||||
@skip_unless_feature("webp")
|
|
||||||
def test_webp_anim() -> None:
|
|
||||||
assert features.check("webp_anim") == _webp.HAVE_WEBPANIM
|
|
||||||
|
|
||||||
|
|
||||||
@skip_unless_feature("libjpeg_turbo")
|
@skip_unless_feature("libjpeg_turbo")
|
||||||
def test_libjpeg_turbo_version() -> None:
|
def test_libjpeg_turbo_version() -> None:
|
||||||
version = features.version("libjpeg_turbo")
|
version = features.version("libjpeg_turbo")
|
||||||
|
|
|
@ -978,7 +978,7 @@ def test_webp_background(tmp_path: Path) -> None:
|
||||||
out = str(tmp_path / "temp.gif")
|
out = str(tmp_path / "temp.gif")
|
||||||
|
|
||||||
# Test opaque WebP background
|
# Test opaque WebP background
|
||||||
if features.check("webp") and features.check("webp_anim"):
|
if features.check("webp"):
|
||||||
with Image.open("Tests/images/hopper.webp") as im:
|
with Image.open("Tests/images/hopper.webp") as im:
|
||||||
assert im.info["background"] == (255, 255, 255, 255)
|
assert im.info["background"] == (255, 255, 255, 255)
|
||||||
im.save(out)
|
im.save(out)
|
||||||
|
|
|
@ -48,7 +48,6 @@ class TestFileWebp:
|
||||||
self.rgb_mode = "RGB"
|
self.rgb_mode = "RGB"
|
||||||
|
|
||||||
def test_version(self) -> None:
|
def test_version(self) -> None:
|
||||||
_webp.WebPDecoderVersion()
|
|
||||||
version = features.version_module("webp")
|
version = features.version_module("webp")
|
||||||
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)
|
||||||
|
@ -116,7 +115,6 @@ class TestFileWebp:
|
||||||
hopper().save(buffer_method, format="WEBP", method=6)
|
hopper().save(buffer_method, format="WEBP", method=6)
|
||||||
assert buffer_no_args.getbuffer() != buffer_method.getbuffer()
|
assert buffer_no_args.getbuffer() != buffer_method.getbuffer()
|
||||||
|
|
||||||
@skip_unless_feature("webp_anim")
|
|
||||||
def test_save_all(self, tmp_path: Path) -> None:
|
def test_save_all(self, tmp_path: Path) -> None:
|
||||||
temp_file = str(tmp_path / "temp.webp")
|
temp_file = str(tmp_path / "temp.webp")
|
||||||
im = Image.new("RGB", (1, 1))
|
im = Image.new("RGB", (1, 1))
|
||||||
|
@ -131,10 +129,9 @@ class TestFileWebp:
|
||||||
|
|
||||||
def test_icc_profile(self, tmp_path: Path) -> None:
|
def test_icc_profile(self, tmp_path: Path) -> None:
|
||||||
self._roundtrip(tmp_path, self.rgb_mode, 12.5, {"icc_profile": None})
|
self._roundtrip(tmp_path, self.rgb_mode, 12.5, {"icc_profile": None})
|
||||||
if _webp.HAVE_WEBPANIM:
|
self._roundtrip(
|
||||||
self._roundtrip(
|
tmp_path, self.rgb_mode, 12.5, {"icc_profile": None, "save_all": True}
|
||||||
tmp_path, self.rgb_mode, 12.5, {"icc_profile": None, "save_all": True}
|
)
|
||||||
)
|
|
||||||
|
|
||||||
def test_write_unsupported_mode_L(self, tmp_path: Path) -> None:
|
def test_write_unsupported_mode_L(self, tmp_path: Path) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -164,10 +161,8 @@ class TestFileWebp:
|
||||||
"""
|
"""
|
||||||
Calling encoder functions with no arguments should result in an error.
|
Calling encoder functions with no arguments should result in an error.
|
||||||
"""
|
"""
|
||||||
|
with pytest.raises(TypeError):
|
||||||
if _webp.HAVE_WEBPANIM:
|
_webp.WebPAnimEncoder()
|
||||||
with pytest.raises(TypeError):
|
|
||||||
_webp.WebPAnimEncoder()
|
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
_webp.WebPEncode()
|
_webp.WebPEncode()
|
||||||
|
|
||||||
|
@ -175,12 +170,8 @@ class TestFileWebp:
|
||||||
"""
|
"""
|
||||||
Calling decoder functions with no arguments should result in an error.
|
Calling decoder functions with no arguments should result in an error.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if _webp.HAVE_WEBPANIM:
|
|
||||||
with pytest.raises(TypeError):
|
|
||||||
_webp.WebPAnimDecoder()
|
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
_webp.WebPDecode()
|
_webp.WebPAnimDecoder()
|
||||||
|
|
||||||
def test_no_resource_warning(self, tmp_path: Path) -> None:
|
def test_no_resource_warning(self, tmp_path: Path) -> None:
|
||||||
file_path = "Tests/images/hopper.webp"
|
file_path = "Tests/images/hopper.webp"
|
||||||
|
@ -199,7 +190,6 @@ class TestFileWebp:
|
||||||
"background",
|
"background",
|
||||||
(0, (0,), (-1, 0, 1, 2), (253, 254, 255, 256)),
|
(0, (0,), (-1, 0, 1, 2), (253, 254, 255, 256)),
|
||||||
)
|
)
|
||||||
@skip_unless_feature("webp_anim")
|
|
||||||
def test_invalid_background(
|
def test_invalid_background(
|
||||||
self, background: int | tuple[int, ...], tmp_path: Path
|
self, background: int | tuple[int, ...], tmp_path: Path
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -208,7 +198,6 @@ class TestFileWebp:
|
||||||
with pytest.raises(OSError):
|
with pytest.raises(OSError):
|
||||||
im.save(temp_file, save_all=True, append_images=[im], background=background)
|
im.save(temp_file, save_all=True, append_images=[im], background=background)
|
||||||
|
|
||||||
@skip_unless_feature("webp_anim")
|
|
||||||
def test_background_from_gif(self, tmp_path: Path) -> None:
|
def test_background_from_gif(self, tmp_path: Path) -> None:
|
||||||
# Save L mode GIF with background
|
# Save L mode GIF with background
|
||||||
with Image.open("Tests/images/no_palette_with_background.gif") as im:
|
with Image.open("Tests/images/no_palette_with_background.gif") as im:
|
||||||
|
@ -233,7 +222,6 @@ class TestFileWebp:
|
||||||
difference = sum(abs(original_value[i] - reread_value[i]) for i in range(0, 3))
|
difference = sum(abs(original_value[i] - reread_value[i]) for i in range(0, 3))
|
||||||
assert difference < 5
|
assert difference < 5
|
||||||
|
|
||||||
@skip_unless_feature("webp_anim")
|
|
||||||
def test_duration(self, tmp_path: Path) -> None:
|
def test_duration(self, tmp_path: Path) -> None:
|
||||||
with Image.open("Tests/images/dispose_bgnd.gif") as im:
|
with Image.open("Tests/images/dispose_bgnd.gif") as im:
|
||||||
assert im.info["duration"] == 1000
|
assert im.info["duration"] == 1000
|
||||||
|
|
|
@ -85,12 +85,7 @@ def test_write_rgba(tmp_path: Path) -> None:
|
||||||
image.load()
|
image.load()
|
||||||
image.getdata()
|
image.getdata()
|
||||||
|
|
||||||
# Early versions of WebP are known to produce higher deviations:
|
assert_image_similar(image, pil_image, 1.0)
|
||||||
# deal with it
|
|
||||||
if _webp.WebPDecoderVersion() <= 0x201:
|
|
||||||
assert_image_similar(image, pil_image, 3.0)
|
|
||||||
else:
|
|
||||||
assert_image_similar(image, pil_image, 1.0)
|
|
||||||
|
|
||||||
|
|
||||||
def test_keep_rgb_values_when_transparent(tmp_path: Path) -> None:
|
def test_keep_rgb_values_when_transparent(tmp_path: Path) -> None:
|
||||||
|
|
|
@ -15,10 +15,7 @@ from .helper import (
|
||||||
skip_unless_feature,
|
skip_unless_feature,
|
||||||
)
|
)
|
||||||
|
|
||||||
pytestmark = [
|
pytestmark = skip_unless_feature("webp")
|
||||||
skip_unless_feature("webp"),
|
|
||||||
skip_unless_feature("webp_anim"),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def test_n_frames() -> None:
|
def test_n_frames() -> None:
|
||||||
|
|
|
@ -13,9 +13,6 @@ RGB_MODE = "RGB"
|
||||||
|
|
||||||
|
|
||||||
def test_write_lossless_rgb(tmp_path: Path) -> None:
|
def test_write_lossless_rgb(tmp_path: Path) -> None:
|
||||||
if _webp.WebPDecoderVersion() < 0x0200:
|
|
||||||
pytest.skip("lossless not included")
|
|
||||||
|
|
||||||
temp_file = str(tmp_path / "temp.webp")
|
temp_file = str(tmp_path / "temp.webp")
|
||||||
|
|
||||||
hopper(RGB_MODE).save(temp_file, lossless=True)
|
hopper(RGB_MODE).save(temp_file, lossless=True)
|
||||||
|
|
|
@ -10,7 +10,7 @@ from PIL import Image
|
||||||
|
|
||||||
from .helper import mark_if_feature_version, skip_unless_feature
|
from .helper import mark_if_feature_version, skip_unless_feature
|
||||||
|
|
||||||
pytestmark = [skip_unless_feature("webp")]
|
pytestmark = skip_unless_feature("webp")
|
||||||
|
|
||||||
ElementTree: ModuleType | None
|
ElementTree: ModuleType | None
|
||||||
try:
|
try:
|
||||||
|
@ -133,7 +133,6 @@ def test_getxmp() -> None:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@skip_unless_feature("webp_anim")
|
|
||||||
def test_write_animated_metadata(tmp_path: Path) -> None:
|
def test_write_animated_metadata(tmp_path: Path) -> None:
|
||||||
iccp_data = b"<iccp_data>"
|
iccp_data = b"<iccp_data>"
|
||||||
exif_data = b"<exif_data>"
|
exif_data = b"<exif_data>"
|
||||||
|
|
|
@ -817,7 +817,6 @@ class TestImage:
|
||||||
assert reloaded_exif[305] == "Pillow test"
|
assert reloaded_exif[305] == "Pillow test"
|
||||||
|
|
||||||
@skip_unless_feature("webp")
|
@skip_unless_feature("webp")
|
||||||
@skip_unless_feature("webp_anim")
|
|
||||||
def test_exif_webp(self, tmp_path: Path) -> None:
|
def test_exif_webp(self, tmp_path: Path) -> None:
|
||||||
with Image.open("Tests/images/hopper.webp") as im:
|
with Image.open("Tests/images/hopper.webp") as im:
|
||||||
exif = im.getexif()
|
exif = im.getexif()
|
||||||
|
|
|
@ -94,7 +94,6 @@ class TestImageFile:
|
||||||
assert (48, 48) == p.image.size
|
assert (48, 48) == p.image.size
|
||||||
|
|
||||||
@skip_unless_feature("webp")
|
@skip_unless_feature("webp")
|
||||||
@skip_unless_feature("webp_anim")
|
|
||||||
def test_incremental_webp(self) -> None:
|
def test_incremental_webp(self) -> None:
|
||||||
with ImageFile.Parser() as p:
|
with ImageFile.Parser() as p:
|
||||||
with open("Tests/images/hopper.webp", "rb") as f:
|
with open("Tests/images/hopper.webp", "rb") as f:
|
||||||
|
|
|
@ -390,7 +390,7 @@ def test_colorize_3color_offset() -> None:
|
||||||
|
|
||||||
def test_exif_transpose() -> None:
|
def test_exif_transpose() -> None:
|
||||||
exts = [".jpg"]
|
exts = [".jpg"]
|
||||||
if features.check("webp") and features.check("webp_anim"):
|
if features.check("webp"):
|
||||||
exts.append(".webp")
|
exts.append(".webp")
|
||||||
for ext in exts:
|
for ext in exts:
|
||||||
with Image.open("Tests/images/hopper" + ext) as base_im:
|
with Image.open("Tests/images/hopper" + ext) as base_im:
|
||||||
|
|
|
@ -1220,8 +1220,7 @@ using the general tags available through tiffinfo.
|
||||||
WebP
|
WebP
|
||||||
^^^^
|
^^^^
|
||||||
|
|
||||||
Pillow reads and writes WebP files. The specifics of Pillow's capabilities with
|
Pillow reads and writes WebP files. Requires libwebp v0.5.0 or later.
|
||||||
this format are currently undocumented.
|
|
||||||
|
|
||||||
.. _webp-saving:
|
.. _webp-saving:
|
||||||
|
|
||||||
|
@ -1263,12 +1262,6 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
|
||||||
Saving sequences
|
Saving sequences
|
||||||
~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
Support for animated WebP files will only be enabled if the system WebP
|
|
||||||
library is v0.5.0 or later. You can check webp animation support at
|
|
||||||
runtime by calling ``features.check("webp_anim")``.
|
|
||||||
|
|
||||||
When calling :py:meth:`~PIL.Image.Image.save` to write a WebP file, by default
|
When calling :py:meth:`~PIL.Image.Image.save` to write a WebP file, by default
|
||||||
only the first frame of a multiframe image will be saved. If the ``save_all``
|
only the first frame of a multiframe image will be saved. If the ``save_all``
|
||||||
argument is present and true, then all frames will be saved, and the following
|
argument is present and true, then all frames will be saved, and the following
|
||||||
|
|
|
@ -54,7 +54,6 @@ Feature version numbers are available only where stated.
|
||||||
Support for the following features can be checked:
|
Support for the following features can be checked:
|
||||||
|
|
||||||
* ``libjpeg_turbo``: (compile time) Whether Pillow was compiled against the libjpeg-turbo version of libjpeg. Compile-time version number is available.
|
* ``libjpeg_turbo``: (compile time) Whether Pillow was compiled against the libjpeg-turbo version of libjpeg. Compile-time version number is available.
|
||||||
* ``webp_anim``: (compile time) Support for animated WebP images.
|
|
||||||
* ``raqm``: Raqm library, required for ``ImageFont.Layout.RAQM`` in :py:func:`PIL.ImageFont.truetype`. Run-time version number is available for Raqm 0.7.0 or newer.
|
* ``raqm``: Raqm library, required for ``ImageFont.Layout.RAQM`` in :py:func:`PIL.ImageFont.truetype`. Run-time version number is available for Raqm 0.7.0 or newer.
|
||||||
* ``libimagequant``: (compile time) ImageQuant quantization support in :py:func:`PIL.Image.Image.quantize`. Run-time version number is available.
|
* ``libimagequant``: (compile time) ImageQuant quantization support in :py:func:`PIL.Image.Image.quantize`. Run-time version number is available.
|
||||||
* ``xcb``: (compile time) Support for X11 in :py:func:`PIL.ImageGrab.grab` via the XCB library.
|
* ``xcb``: (compile time) Support for X11 in :py:func:`PIL.ImageGrab.grab` via the XCB library.
|
||||||
|
|
7
setup.py
7
setup.py
|
@ -795,7 +795,12 @@ class pil_build_ext(build_ext):
|
||||||
_dbg("Looking for webp")
|
_dbg("Looking for webp")
|
||||||
if all(
|
if all(
|
||||||
_find_include_file(self, src)
|
_find_include_file(self, src)
|
||||||
for src in ["webp/encode.h", "webp/mux.h", "webp/demux.h"]
|
for src in [
|
||||||
|
"webp/encode.h",
|
||||||
|
"webp/decode.h",
|
||||||
|
"webp/mux.h",
|
||||||
|
"webp/demux.h",
|
||||||
|
]
|
||||||
):
|
):
|
||||||
# In Google's precompiled zip it is call "libwebp":
|
# In Google's precompiled zip it is call "libwebp":
|
||||||
if all(
|
if all(
|
||||||
|
|
|
@ -45,22 +45,6 @@ class WebPImageFile(ImageFile.ImageFile):
|
||||||
__logical_frame = 0
|
__logical_frame = 0
|
||||||
|
|
||||||
def _open(self) -> None:
|
def _open(self) -> None:
|
||||||
if not _webp.HAVE_WEBPANIM:
|
|
||||||
# Legacy mode
|
|
||||||
data, width, height, self._mode, icc_profile, exif = _webp.WebPDecode(
|
|
||||||
self.fp.read()
|
|
||||||
)
|
|
||||||
if icc_profile:
|
|
||||||
self.info["icc_profile"] = icc_profile
|
|
||||||
if exif:
|
|
||||||
self.info["exif"] = exif
|
|
||||||
self._size = width, height
|
|
||||||
self.fp = BytesIO(data)
|
|
||||||
self.tile = [("raw", (0, 0) + self.size, 0, self.mode)]
|
|
||||||
self.n_frames = 1
|
|
||||||
self.is_animated = False
|
|
||||||
return
|
|
||||||
|
|
||||||
# Use the newer AnimDecoder API to parse the (possibly) animated file,
|
# Use the newer AnimDecoder API to parse the (possibly) animated file,
|
||||||
# and access muxed chunks like ICC/EXIF/XMP.
|
# and access muxed chunks like ICC/EXIF/XMP.
|
||||||
self._decoder = _webp.WebPAnimDecoder(self.fp.read())
|
self._decoder = _webp.WebPAnimDecoder(self.fp.read())
|
||||||
|
@ -145,21 +129,20 @@ class WebPImageFile(ImageFile.ImageFile):
|
||||||
self._get_next() # Advance to the requested frame
|
self._get_next() # Advance to the requested frame
|
||||||
|
|
||||||
def load(self) -> Image.core.PixelAccess | None:
|
def load(self) -> Image.core.PixelAccess | None:
|
||||||
if _webp.HAVE_WEBPANIM:
|
if self.__loaded != self.__logical_frame:
|
||||||
if self.__loaded != self.__logical_frame:
|
self._seek(self.__logical_frame)
|
||||||
self._seek(self.__logical_frame)
|
|
||||||
|
|
||||||
# We need to load the image data for this frame
|
# We need to load the image data for this frame
|
||||||
data, timestamp, duration = self._get_next()
|
data, timestamp, duration = self._get_next()
|
||||||
self.info["timestamp"] = timestamp
|
self.info["timestamp"] = timestamp
|
||||||
self.info["duration"] = duration
|
self.info["duration"] = duration
|
||||||
self.__loaded = self.__logical_frame
|
self.__loaded = self.__logical_frame
|
||||||
|
|
||||||
# Set tile
|
# Set tile
|
||||||
if self.fp and self._exclusive_fp:
|
if self.fp and self._exclusive_fp:
|
||||||
self.fp.close()
|
self.fp.close()
|
||||||
self.fp = BytesIO(data)
|
self.fp = BytesIO(data)
|
||||||
self.tile = [("raw", (0, 0) + self.size, 0, self.rawmode)]
|
self.tile = [("raw", (0, 0) + self.size, 0, self.rawmode)]
|
||||||
|
|
||||||
return super().load()
|
return super().load()
|
||||||
|
|
||||||
|
@ -167,9 +150,6 @@ class WebPImageFile(ImageFile.ImageFile):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def tell(self) -> int:
|
def tell(self) -> int:
|
||||||
if not _webp.HAVE_WEBPANIM:
|
|
||||||
return super().tell()
|
|
||||||
|
|
||||||
return self.__logical_frame
|
return self.__logical_frame
|
||||||
|
|
||||||
|
|
||||||
|
@ -357,7 +337,6 @@ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
|
||||||
Image.register_open(WebPImageFile.format, WebPImageFile, _accept)
|
Image.register_open(WebPImageFile.format, WebPImageFile, _accept)
|
||||||
if SUPPORTED:
|
if SUPPORTED:
|
||||||
Image.register_save(WebPImageFile.format, _save)
|
Image.register_save(WebPImageFile.format, _save)
|
||||||
if _webp.HAVE_WEBPANIM:
|
Image.register_save_all(WebPImageFile.format, _save_all)
|
||||||
Image.register_save_all(WebPImageFile.format, _save_all)
|
|
||||||
Image.register_extension(WebPImageFile.format, ".webp")
|
Image.register_extension(WebPImageFile.format, ".webp")
|
||||||
Image.register_mime(WebPImageFile.format, "image/webp")
|
Image.register_mime(WebPImageFile.format, "image/webp")
|
||||||
|
|
|
@ -119,7 +119,6 @@ def get_supported_codecs() -> list[str]:
|
||||||
|
|
||||||
|
|
||||||
features = {
|
features = {
|
||||||
"webp_anim": ("PIL._webp", "HAVE_WEBPANIM", None),
|
|
||||||
"raqm": ("PIL._imagingft", "HAVE_RAQM", "raqm_version"),
|
"raqm": ("PIL._imagingft", "HAVE_RAQM", "raqm_version"),
|
||||||
"fribidi": ("PIL._imagingft", "HAVE_FRIBIDI", "fribidi_version"),
|
"fribidi": ("PIL._imagingft", "HAVE_FRIBIDI", "fribidi_version"),
|
||||||
"harfbuzz": ("PIL._imagingft", "HAVE_HARFBUZZ", "harfbuzz_version"),
|
"harfbuzz": ("PIL._imagingft", "HAVE_HARFBUZZ", "harfbuzz_version"),
|
||||||
|
@ -269,7 +268,6 @@ def pilinfo(out: IO[str] | None = None, supported_formats: bool = True) -> None:
|
||||||
("freetype2", "FREETYPE2"),
|
("freetype2", "FREETYPE2"),
|
||||||
("littlecms2", "LITTLECMS2"),
|
("littlecms2", "LITTLECMS2"),
|
||||||
("webp", "WEBP"),
|
("webp", "WEBP"),
|
||||||
("webp_anim", "WEBP Animation"),
|
|
||||||
("jpg", "JPEG"),
|
("jpg", "JPEG"),
|
||||||
("jpg_2000", "OPENJPEG (JPEG2000)"),
|
("jpg_2000", "OPENJPEG (JPEG2000)"),
|
||||||
("zlib", "ZLIB (PNG/ZIP)"),
|
("zlib", "ZLIB (PNG/ZIP)"),
|
||||||
|
|
150
src/_webp.c
150
src/_webp.c
|
@ -13,8 +13,8 @@
|
||||||
* very early versions had some significant differences, so we require later
|
* very early versions had some significant differences, so we require later
|
||||||
* versions, before enabling animation support.
|
* versions, before enabling animation support.
|
||||||
*/
|
*/
|
||||||
#if WEBP_MUX_ABI_VERSION >= 0x0104 && WEBP_DEMUX_ABI_VERSION >= 0x0105
|
#if WEBP_MUX_ABI_VERSION < 0x0106 || WEBP_DEMUX_ABI_VERSION < 0x0107
|
||||||
#define HAVE_WEBPANIM
|
#error libwebp 0.5.0 and above is required. Upgrade libwebp or build with --disable-webp flag
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -87,8 +87,6 @@ HandleMuxError(WebPMuxError err, char *chunk) {
|
||||||
/* WebP Animation Support */
|
/* WebP Animation Support */
|
||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
|
|
||||||
#ifdef HAVE_WEBPANIM
|
|
||||||
|
|
||||||
// Encoder type
|
// Encoder type
|
||||||
typedef struct {
|
typedef struct {
|
||||||
PyObject_HEAD WebPAnimEncoder *enc;
|
PyObject_HEAD WebPAnimEncoder *enc;
|
||||||
|
@ -568,8 +566,6 @@ static PyTypeObject WebPAnimDecoder_Type = {
|
||||||
0, /*tp_getset*/
|
0, /*tp_getset*/
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
/* Legacy WebP Support */
|
/* Legacy WebP Support */
|
||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
|
@ -644,10 +640,7 @@ WebPEncode_wrapper(PyObject *self, PyObject *args) {
|
||||||
config.quality = quality_factor;
|
config.quality = quality_factor;
|
||||||
config.alpha_quality = alpha_quality_factor;
|
config.alpha_quality = alpha_quality_factor;
|
||||||
config.method = method;
|
config.method = method;
|
||||||
#if WEBP_ENCODER_ABI_VERSION >= 0x0209
|
|
||||||
// the "exact" flag is only available in libwebp 0.5.0 and later
|
|
||||||
config.exact = exact;
|
config.exact = exact;
|
||||||
#endif
|
|
||||||
|
|
||||||
// Validate the config
|
// Validate the config
|
||||||
if (!WebPValidateConfig(&config)) {
|
if (!WebPValidateConfig(&config)) {
|
||||||
|
@ -763,124 +756,6 @@ WebPEncode_wrapper(PyObject *self, PyObject *args) {
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyObject *
|
|
||||||
WebPDecode_wrapper(PyObject *self, PyObject *args) {
|
|
||||||
PyBytesObject *webp_string;
|
|
||||||
const uint8_t *webp;
|
|
||||||
Py_ssize_t size;
|
|
||||||
PyObject *ret = Py_None, *bytes = NULL, *pymode = NULL, *icc_profile = NULL,
|
|
||||||
*exif = NULL;
|
|
||||||
WebPDecoderConfig config;
|
|
||||||
VP8StatusCode vp8_status_code = VP8_STATUS_OK;
|
|
||||||
char *mode = "RGB";
|
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "S", &webp_string)) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!WebPInitDecoderConfig(&config)) {
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
PyBytes_AsStringAndSize((PyObject *)webp_string, (char **)&webp, &size);
|
|
||||||
|
|
||||||
vp8_status_code = WebPGetFeatures(webp, size, &config.input);
|
|
||||||
if (vp8_status_code == VP8_STATUS_OK) {
|
|
||||||
// If we don't set it, we don't get alpha.
|
|
||||||
// Initialized to MODE_RGB
|
|
||||||
if (config.input.has_alpha) {
|
|
||||||
config.output.colorspace = MODE_RGBA;
|
|
||||||
mode = "RGBA";
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
int copy_data = 0;
|
|
||||||
WebPData data = {webp, size};
|
|
||||||
WebPMuxFrameInfo image;
|
|
||||||
WebPData icc_profile_data = {0};
|
|
||||||
WebPData exif_data = {0};
|
|
||||||
|
|
||||||
WebPMux *mux = WebPMuxCreate(&data, copy_data);
|
|
||||||
if (NULL == mux) {
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (WEBP_MUX_OK != WebPMuxGetFrame(mux, 1, &image)) {
|
|
||||||
WebPMuxDelete(mux);
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
webp = image.bitstream.bytes;
|
|
||||||
size = image.bitstream.size;
|
|
||||||
|
|
||||||
vp8_status_code = WebPDecode(webp, size, &config);
|
|
||||||
|
|
||||||
if (WEBP_MUX_OK == WebPMuxGetChunk(mux, "ICCP", &icc_profile_data)) {
|
|
||||||
icc_profile = PyBytes_FromStringAndSize(
|
|
||||||
(const char *)icc_profile_data.bytes, icc_profile_data.size
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (WEBP_MUX_OK == WebPMuxGetChunk(mux, "EXIF", &exif_data)) {
|
|
||||||
exif = PyBytes_FromStringAndSize(
|
|
||||||
(const char *)exif_data.bytes, exif_data.size
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
WebPDataClear(&image.bitstream);
|
|
||||||
WebPMuxDelete(mux);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vp8_status_code != VP8_STATUS_OK) {
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.output.colorspace < MODE_YUV) {
|
|
||||||
bytes = PyBytes_FromStringAndSize(
|
|
||||||
(char *)config.output.u.RGBA.rgba, config.output.u.RGBA.size
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Skipping YUV for now. Need Test Images.
|
|
||||||
// UNDONE -- unclear if we'll ever get here if we set mode_rgb*
|
|
||||||
bytes = PyBytes_FromStringAndSize(
|
|
||||||
(char *)config.output.u.YUVA.y, config.output.u.YUVA.y_size
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pymode = PyUnicode_FromString(mode);
|
|
||||||
ret = Py_BuildValue(
|
|
||||||
"SiiSSS",
|
|
||||||
bytes,
|
|
||||||
config.output.width,
|
|
||||||
config.output.height,
|
|
||||||
pymode,
|
|
||||||
NULL == icc_profile ? Py_None : icc_profile,
|
|
||||||
NULL == exif ? Py_None : exif
|
|
||||||
);
|
|
||||||
|
|
||||||
end:
|
|
||||||
WebPFreeDecBuffer(&config.output);
|
|
||||||
|
|
||||||
Py_XDECREF(bytes);
|
|
||||||
Py_XDECREF(pymode);
|
|
||||||
Py_XDECREF(icc_profile);
|
|
||||||
Py_XDECREF(exif);
|
|
||||||
|
|
||||||
if (Py_None == ret) {
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the decoder's version number, packed in hexadecimal using 8bits for
|
|
||||||
// each of major/minor/revision. E.g: v2.5.7 is 0x020507.
|
|
||||||
PyObject *
|
|
||||||
WebPDecoderVersion_wrapper() {
|
|
||||||
return Py_BuildValue("i", WebPGetDecoderVersion());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Version as string
|
// Version as string
|
||||||
const char *
|
const char *
|
||||||
WebPDecoderVersion_str(void) {
|
WebPDecoderVersion_str(void) {
|
||||||
|
@ -901,39 +776,20 @@ WebPDecoderVersion_str(void) {
|
||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
|
|
||||||
static PyMethodDef webpMethods[] = {
|
static PyMethodDef webpMethods[] = {
|
||||||
#ifdef HAVE_WEBPANIM
|
|
||||||
{"WebPAnimDecoder", _anim_decoder_new, METH_VARARGS, "WebPAnimDecoder"},
|
{"WebPAnimDecoder", _anim_decoder_new, METH_VARARGS, "WebPAnimDecoder"},
|
||||||
{"WebPAnimEncoder", _anim_encoder_new, METH_VARARGS, "WebPAnimEncoder"},
|
{"WebPAnimEncoder", _anim_encoder_new, METH_VARARGS, "WebPAnimEncoder"},
|
||||||
#endif
|
|
||||||
{"WebPEncode", WebPEncode_wrapper, METH_VARARGS, "WebPEncode"},
|
{"WebPEncode", WebPEncode_wrapper, METH_VARARGS, "WebPEncode"},
|
||||||
{"WebPDecode", WebPDecode_wrapper, METH_VARARGS, "WebPDecode"},
|
|
||||||
{"WebPDecoderVersion", WebPDecoderVersion_wrapper, METH_NOARGS, "WebPVersion"},
|
|
||||||
{NULL, NULL}
|
{NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
|
||||||
addAnimFlagToModule(PyObject *m) {
|
|
||||||
PyObject *have_webpanim;
|
|
||||||
#ifdef HAVE_WEBPANIM
|
|
||||||
have_webpanim = Py_True;
|
|
||||||
#else
|
|
||||||
have_webpanim = Py_False;
|
|
||||||
#endif
|
|
||||||
Py_INCREF(have_webpanim);
|
|
||||||
PyModule_AddObject(m, "HAVE_WEBPANIM", have_webpanim);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
setup_module(PyObject *m) {
|
setup_module(PyObject *m) {
|
||||||
#ifdef HAVE_WEBPANIM
|
PyObject *d = PyModule_GetDict(m);
|
||||||
/* Ready object types */
|
/* Ready object types */
|
||||||
if (PyType_Ready(&WebPAnimDecoder_Type) < 0 ||
|
if (PyType_Ready(&WebPAnimDecoder_Type) < 0 ||
|
||||||
PyType_Ready(&WebPAnimEncoder_Type) < 0) {
|
PyType_Ready(&WebPAnimEncoder_Type) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
PyObject *d = PyModule_GetDict(m);
|
|
||||||
addAnimFlagToModule(m);
|
|
||||||
|
|
||||||
PyObject *v = PyUnicode_FromString(WebPDecoderVersion_str());
|
PyObject *v = PyUnicode_FromString(WebPDecoderVersion_str());
|
||||||
PyDict_SetItemString(d, "webpdecoder_version", v ? v : Py_None);
|
PyDict_SetItemString(d, "webpdecoder_version", v ? v : Py_None);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user