1
from __future__ import annotations
7
from pathlib import Path
12
from PIL import Image, WebPImagePlugin, features
17
assert_image_similar_tofile,
30
class TestUnsupportedWebp:
31
def test_unsupported(self) -> None:
33
WebPImagePlugin.SUPPORTED = False
35
file_path = "Tests/images/hopper.webp"
36
with pytest.warns(UserWarning):
37
with pytest.raises(OSError):
38
with Image.open(file_path):
42
WebPImagePlugin.SUPPORTED = True
45
@skip_unless_feature("webp")
47
def setup_method(self) -> None:
50
def test_version(self) -> None:
51
version = features.version_module("webp")
52
assert version is not None
53
assert re.search(r"\d+\.\d+\.\d+$", version)
55
def test_read_rgb(self) -> None:
57
Can we read a RGB mode WebP file without error?
58
Does it have the bits we expect?
61
with Image.open("Tests/images/hopper.webp") as image:
62
assert image.mode == self.rgb_mode
63
assert image.size == (128, 128)
64
assert image.format == "WEBP"
70
assert_image_similar_tofile(image, "Tests/images/hopper_webp_bits.ppm", 1.0)
73
self, tmp_path: Path, mode: str, epsilon: float, args: dict[str, Any] = {}
75
temp_file = str(tmp_path / "temp.webp")
77
hopper(mode).save(temp_file, **args)
78
with Image.open(temp_file) as image:
79
assert image.mode == self.rgb_mode
80
assert image.size == (128, 128)
81
assert image.format == "WEBP"
85
if mode == self.rgb_mode:
87
assert_image_similar_tofile(
88
image, "Tests/images/hopper_webp_write.ppm", 12.0
96
if mode != self.rgb_mode:
97
target = target.convert(self.rgb_mode)
98
assert_image_similar(image, target, epsilon)
100
def test_write_rgb(self, tmp_path: Path) -> None:
102
Can we write a RGB mode file to webp without error?
103
Does it have the bits we expect?
106
self._roundtrip(tmp_path, self.rgb_mode, 12.5)
108
def test_write_method(self, tmp_path: Path) -> None:
109
self._roundtrip(tmp_path, self.rgb_mode, 12.0, {"method": 6})
111
buffer_no_args = io.BytesIO()
112
hopper().save(buffer_no_args, format="WEBP")
114
buffer_method = io.BytesIO()
115
hopper().save(buffer_method, format="WEBP", method=6)
116
assert buffer_no_args.getbuffer() != buffer_method.getbuffer()
118
def test_save_all(self, tmp_path: Path) -> None:
119
temp_file = str(tmp_path / "temp.webp")
120
im = Image.new("RGB", (1, 1))
121
im2 = Image.new("RGB", (1, 1), "#f00")
122
im.save(temp_file, save_all=True, append_images=[im2])
124
with Image.open(temp_file) as reloaded:
125
assert_image_equal(im, reloaded)
128
assert_image_similar(im2, reloaded, 1)
130
def test_icc_profile(self, tmp_path: Path) -> None:
131
self._roundtrip(tmp_path, self.rgb_mode, 12.5, {"icc_profile": None})
133
tmp_path, self.rgb_mode, 12.5, {"icc_profile": None, "save_all": True}
136
def test_write_unsupported_mode_L(self, tmp_path: Path) -> None:
138
Saving a black-and-white file to WebP format should work, and be
139
similar to the original file.
142
self._roundtrip(tmp_path, "L", 10.0)
144
def test_write_unsupported_mode_P(self, tmp_path: Path) -> None:
146
Saving a palette-based file to WebP format should work, and be
147
similar to the original file.
150
self._roundtrip(tmp_path, "P", 50.0)
152
@pytest.mark.skipif(sys.maxsize <= 2**32, reason="Requires 64-bit system")
153
def test_write_encoding_error_message(self, tmp_path: Path) -> None:
154
temp_file = str(tmp_path / "temp.webp")
155
im = Image.new("RGB", (15000, 15000))
156
with pytest.raises(ValueError) as e:
157
im.save(temp_file, method=0)
158
assert str(e.value) == "encoding error 6"
160
@pytest.mark.skipif(sys.maxsize <= 2**32, reason="Requires 64-bit system")
161
def test_write_encoding_error_bad_dimension(self, tmp_path: Path) -> None:
162
temp_file = str(tmp_path / "temp.webp")
163
im = Image.new("L", (16384, 16384))
164
with pytest.raises(ValueError) as e:
168
== "encoding error 5: Image size exceeds WebP limit of 16383 pixels"
171
def test_WebPEncode_with_invalid_args(self) -> None:
173
Calling encoder functions with no arguments should result in an error.
175
with pytest.raises(TypeError):
176
_webp.WebPAnimEncoder()
177
with pytest.raises(TypeError):
180
def test_WebPAnimDecoder_with_invalid_args(self) -> None:
182
Calling decoder functions with no arguments should result in an error.
184
with pytest.raises(TypeError):
185
_webp.WebPAnimDecoder()
187
def test_no_resource_warning(self, tmp_path: Path) -> None:
188
file_path = "Tests/images/hopper.webp"
189
with Image.open(file_path) as image:
190
temp_file = str(tmp_path / "temp.webp")
191
with warnings.catch_warnings():
192
image.save(temp_file)
194
def test_file_pointer_could_be_reused(self) -> None:
195
file_path = "Tests/images/hopper.webp"
196
with open(file_path, "rb") as blob:
197
Image.open(blob).load()
198
Image.open(blob).load()
200
@pytest.mark.parametrize(
202
(0, (0,), (-1, 0, 1, 2), (253, 254, 255, 256)),
204
def test_invalid_background(
205
self, background: int | tuple[int, ...], tmp_path: Path
207
temp_file = str(tmp_path / "temp.webp")
209
with pytest.raises(OSError):
210
im.save(temp_file, save_all=True, append_images=[im], background=background)
212
def test_background_from_gif(self, tmp_path: Path) -> None:
214
with Image.open("Tests/images/no_palette_with_background.gif") as im:
215
out_webp = str(tmp_path / "temp.webp")
216
im.save(out_webp, save_all=True)
219
with Image.open("Tests/images/chi.gif") as im:
220
original_value = im.convert("RGB").getpixel((1, 1))
223
out_webp = str(tmp_path / "temp.webp")
224
im.save(out_webp, save_all=True)
227
out_gif = str(tmp_path / "temp.gif")
228
with Image.open(out_webp) as im:
231
with Image.open(out_gif) as reread:
232
reread_value = reread.convert("RGB").getpixel((1, 1))
233
difference = sum(abs(original_value[i] - reread_value[i]) for i in range(0, 3))
234
assert difference < 5
236
def test_duration(self, tmp_path: Path) -> None:
237
with Image.open("Tests/images/dispose_bgnd.gif") as im:
238
assert im.info["duration"] == 1000
240
out_webp = str(tmp_path / "temp.webp")
241
im.save(out_webp, save_all=True)
243
with Image.open(out_webp) as reloaded:
245
assert reloaded.info["duration"] == 1000
247
def test_roundtrip_rgba_palette(self, tmp_path: Path) -> None:
248
temp_file = str(tmp_path / "temp.webp")
249
im = Image.new("RGBA", (1, 1)).convert("P")
250
assert im.mode == "P"
251
assert im.palette is not None
252
assert im.palette.mode == "RGBA"
255
with Image.open(temp_file) as im:
256
assert im.getpixel((0, 0)) == (0, 0, 0, 0)