mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-25 17:36:18 +03:00
Merge pull request #4436 from hugovk/pytest.importorskip
Use pytest.importorskip to skip on a missing import dependency
This commit is contained in:
commit
f87505cbd2
|
@ -1,15 +1,8 @@
|
||||||
import pytest
|
import pytest
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
try:
|
FpxImagePlugin = pytest.importorskip(
|
||||||
from PIL import FpxImagePlugin
|
"PIL.FpxImagePlugin", reason="olefile not installed"
|
||||||
except ImportError:
|
|
||||||
olefile_installed = False
|
|
||||||
else:
|
|
||||||
olefile_installed = True
|
|
||||||
|
|
||||||
pytestmark = pytest.mark.skipif(
|
|
||||||
not olefile_installed, reason="olefile package not installed"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,22 +3,13 @@ from PIL import Image, ImagePalette
|
||||||
|
|
||||||
from .helper import assert_image_similar, hopper, skip_unless_feature
|
from .helper import assert_image_similar, hopper, skip_unless_feature
|
||||||
|
|
||||||
try:
|
MicImagePlugin = pytest.importorskip(
|
||||||
from PIL import MicImagePlugin
|
"PIL.MicImagePlugin", reason="olefile not installed"
|
||||||
except ImportError:
|
)
|
||||||
olefile_installed = False
|
pytestmark = skip_unless_feature("libtiff")
|
||||||
else:
|
|
||||||
olefile_installed = True
|
|
||||||
|
|
||||||
TEST_FILE = "Tests/images/hopper.mic"
|
TEST_FILE = "Tests/images/hopper.mic"
|
||||||
|
|
||||||
|
|
||||||
pytestmark = [
|
|
||||||
pytest.mark.skipif(not olefile_installed, reason="olefile package not installed"),
|
|
||||||
skip_unless_feature("libtiff"),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def test_sanity():
|
def test_sanity():
|
||||||
with Image.open(TEST_FILE) as im:
|
with Image.open(TEST_FILE) as im:
|
||||||
im.load()
|
im.load()
|
||||||
|
|
|
@ -1,119 +1,115 @@
|
||||||
import unittest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import PillowTestCase, assert_image_equal, assert_image_similar, hopper
|
from .helper import assert_image_equal, assert_image_similar, hopper
|
||||||
|
|
||||||
try:
|
_webp = pytest.importorskip("PIL._webp", reason="WebP support not installed")
|
||||||
from PIL import _webp
|
|
||||||
except ImportError:
|
|
||||||
_webp = None
|
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(_webp is None, "WebP support not installed")
|
def setup_module():
|
||||||
class TestFileWebpAlpha(PillowTestCase):
|
if _webp.WebPDecoderBuggyAlpha():
|
||||||
def setUp(self):
|
pytest.skip("Buggy early version of WebP installed, not testing transparency")
|
||||||
if _webp.WebPDecoderBuggyAlpha(self):
|
|
||||||
self.skipTest(
|
|
||||||
"Buggy early version of WebP installed, not testing transparency"
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_read_rgba(self):
|
|
||||||
"""
|
|
||||||
Can we read an RGBA mode file without error?
|
|
||||||
Does it have the bits we expect?
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Generated with `cwebp transparent.png -o transparent.webp`
|
def test_read_rgba():
|
||||||
file_path = "Tests/images/transparent.webp"
|
"""
|
||||||
with Image.open(file_path) as image:
|
Can we read an RGBA mode file without error?
|
||||||
self.assertEqual(image.mode, "RGBA")
|
Does it have the bits we expect?
|
||||||
self.assertEqual(image.size, (200, 150))
|
"""
|
||||||
self.assertEqual(image.format, "WEBP")
|
|
||||||
image.load()
|
|
||||||
image.getdata()
|
|
||||||
|
|
||||||
image.tobytes()
|
# Generated with `cwebp transparent.png -o transparent.webp`
|
||||||
|
file_path = "Tests/images/transparent.webp"
|
||||||
|
with Image.open(file_path) as image:
|
||||||
|
assert image.mode == "RGBA"
|
||||||
|
assert image.size == (200, 150)
|
||||||
|
assert image.format == "WEBP"
|
||||||
|
image.load()
|
||||||
|
image.getdata()
|
||||||
|
|
||||||
with Image.open("Tests/images/transparent.png") as target:
|
image.tobytes()
|
||||||
assert_image_similar(image, target, 20.0)
|
|
||||||
|
|
||||||
def test_write_lossless_rgb(self):
|
with Image.open("Tests/images/transparent.png") as target:
|
||||||
"""
|
assert_image_similar(image, target, 20.0)
|
||||||
Can we write an RGBA mode file with lossless compression without
|
|
||||||
error? Does it have the bits we expect?
|
|
||||||
"""
|
|
||||||
|
|
||||||
temp_file = self.tempfile("temp.webp")
|
|
||||||
# temp_file = "temp.webp"
|
|
||||||
|
|
||||||
pil_image = hopper("RGBA")
|
def test_write_lossless_rgb(tmp_path):
|
||||||
|
"""
|
||||||
|
Can we write an RGBA mode file with lossless compression without error?
|
||||||
|
Does it have the bits we expect?
|
||||||
|
"""
|
||||||
|
|
||||||
mask = Image.new("RGBA", (64, 64), (128, 128, 128, 128))
|
temp_file = str(tmp_path / "temp.webp")
|
||||||
# Add some partially transparent bits:
|
# temp_file = "temp.webp"
|
||||||
pil_image.paste(mask, (0, 0), mask)
|
|
||||||
|
|
||||||
pil_image.save(temp_file, lossless=True)
|
pil_image = hopper("RGBA")
|
||||||
|
|
||||||
with Image.open(temp_file) as image:
|
mask = Image.new("RGBA", (64, 64), (128, 128, 128, 128))
|
||||||
image.load()
|
# Add some partially transparent bits:
|
||||||
|
pil_image.paste(mask, (0, 0), mask)
|
||||||
|
|
||||||
self.assertEqual(image.mode, "RGBA")
|
pil_image.save(temp_file, lossless=True)
|
||||||
self.assertEqual(image.size, pil_image.size)
|
|
||||||
self.assertEqual(image.format, "WEBP")
|
|
||||||
image.load()
|
|
||||||
image.getdata()
|
|
||||||
|
|
||||||
assert_image_equal(image, pil_image)
|
with Image.open(temp_file) as image:
|
||||||
|
image.load()
|
||||||
|
|
||||||
def test_write_rgba(self):
|
assert image.mode == "RGBA"
|
||||||
"""
|
assert image.size == pil_image.size
|
||||||
Can we write a RGBA mode file to webp without error.
|
assert image.format == "WEBP"
|
||||||
Does it have the bits we expect?
|
image.load()
|
||||||
"""
|
image.getdata()
|
||||||
|
|
||||||
temp_file = self.tempfile("temp.webp")
|
assert_image_equal(image, pil_image)
|
||||||
|
|
||||||
pil_image = Image.new("RGBA", (10, 10), (255, 0, 0, 20))
|
|
||||||
pil_image.save(temp_file)
|
|
||||||
|
|
||||||
if _webp.WebPDecoderBuggyAlpha(self):
|
def test_write_rgba(tmp_path):
|
||||||
return
|
"""
|
||||||
|
Can we write a RGBA mode file to WebP without error.
|
||||||
|
Does it have the bits we expect?
|
||||||
|
"""
|
||||||
|
|
||||||
with Image.open(temp_file) as image:
|
temp_file = str(tmp_path / "temp.webp")
|
||||||
image.load()
|
|
||||||
|
|
||||||
self.assertEqual(image.mode, "RGBA")
|
pil_image = Image.new("RGBA", (10, 10), (255, 0, 0, 20))
|
||||||
self.assertEqual(image.size, (10, 10))
|
pil_image.save(temp_file)
|
||||||
self.assertEqual(image.format, "WEBP")
|
|
||||||
image.load()
|
|
||||||
image.getdata()
|
|
||||||
|
|
||||||
# early versions of webp are known to produce higher deviations:
|
if _webp.WebPDecoderBuggyAlpha():
|
||||||
# deal with it
|
return
|
||||||
if _webp.WebPDecoderVersion(self) <= 0x201:
|
|
||||||
assert_image_similar(image, pil_image, 3.0)
|
|
||||||
else:
|
|
||||||
assert_image_similar(image, pil_image, 1.0)
|
|
||||||
|
|
||||||
def test_write_unsupported_mode_PA(self):
|
with Image.open(temp_file) as image:
|
||||||
"""
|
image.load()
|
||||||
Saving a palette-based file with transparency to WebP format
|
|
||||||
should work, and be similar to the original file.
|
|
||||||
"""
|
|
||||||
|
|
||||||
temp_file = self.tempfile("temp.webp")
|
assert image.mode == "RGBA"
|
||||||
file_path = "Tests/images/transparent.gif"
|
assert image.size == (10, 10)
|
||||||
|
assert image.format == "WEBP"
|
||||||
|
image.load()
|
||||||
|
image.getdata()
|
||||||
|
|
||||||
|
# Early versions of WebP are known to produce higher deviations:
|
||||||
|
# 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_write_unsupported_mode_PA(tmp_path):
|
||||||
|
"""
|
||||||
|
Saving a palette-based file with transparency to WebP format
|
||||||
|
should work, and be similar to the original file.
|
||||||
|
"""
|
||||||
|
|
||||||
|
temp_file = str(tmp_path / "temp.webp")
|
||||||
|
file_path = "Tests/images/transparent.gif"
|
||||||
|
with Image.open(file_path) as im:
|
||||||
|
im.save(temp_file)
|
||||||
|
with Image.open(temp_file) as image:
|
||||||
|
assert image.mode == "RGBA"
|
||||||
|
assert image.size == (200, 150)
|
||||||
|
assert image.format == "WEBP"
|
||||||
|
|
||||||
|
image.load()
|
||||||
|
image.getdata()
|
||||||
with Image.open(file_path) as im:
|
with Image.open(file_path) as im:
|
||||||
im.save(temp_file)
|
target = im.convert("RGBA")
|
||||||
with Image.open(temp_file) as image:
|
|
||||||
self.assertEqual(image.mode, "RGBA")
|
|
||||||
self.assertEqual(image.size, (200, 150))
|
|
||||||
self.assertEqual(image.format, "WEBP")
|
|
||||||
|
|
||||||
image.load()
|
assert_image_similar(image, target, 25.0)
|
||||||
image.getdata()
|
|
||||||
with Image.open(file_path) as im:
|
|
||||||
target = im.convert("RGBA")
|
|
||||||
|
|
||||||
assert_image_similar(image, target, 25.0)
|
|
||||||
|
|
|
@ -2,7 +2,6 @@ import pytest
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import (
|
from .helper import (
|
||||||
PillowTestCase,
|
|
||||||
assert_image_equal,
|
assert_image_equal,
|
||||||
assert_image_similar,
|
assert_image_similar,
|
||||||
is_big_endian,
|
is_big_endian,
|
||||||
|
@ -10,156 +9,158 @@ from .helper import (
|
||||||
skip_unless_feature,
|
skip_unless_feature,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
pytestmark = [
|
||||||
|
skip_unless_feature("webp"),
|
||||||
|
skip_unless_feature("webp_anim"),
|
||||||
|
]
|
||||||
|
|
||||||
@skip_unless_feature("webp")
|
|
||||||
@skip_unless_feature("webp_anim")
|
|
||||||
class TestFileWebpAnimation(PillowTestCase):
|
|
||||||
def test_n_frames(self):
|
|
||||||
"""
|
|
||||||
Ensure that WebP format sets n_frames and is_animated
|
|
||||||
attributes correctly.
|
|
||||||
"""
|
|
||||||
|
|
||||||
with Image.open("Tests/images/hopper.webp") as im:
|
def test_n_frames():
|
||||||
self.assertEqual(im.n_frames, 1)
|
"""Ensure that WebP format sets n_frames and is_animated attributes correctly."""
|
||||||
self.assertFalse(im.is_animated)
|
|
||||||
|
|
||||||
with Image.open("Tests/images/iss634.webp") as im:
|
with Image.open("Tests/images/hopper.webp") as im:
|
||||||
self.assertEqual(im.n_frames, 42)
|
assert im.n_frames == 1
|
||||||
self.assertTrue(im.is_animated)
|
assert not im.is_animated
|
||||||
|
|
||||||
@pytest.mark.xfail(is_big_endian() and on_ci(), reason="Fails on big-endian")
|
with Image.open("Tests/images/iss634.webp") as im:
|
||||||
def test_write_animation_L(self):
|
assert im.n_frames == 42
|
||||||
"""
|
assert im.is_animated
|
||||||
Convert an animated GIF to animated WebP, then compare the
|
|
||||||
frame count, and first and last frames to ensure they're
|
|
||||||
visually similar.
|
|
||||||
"""
|
|
||||||
|
|
||||||
with Image.open("Tests/images/iss634.gif") as orig:
|
|
||||||
self.assertGreater(orig.n_frames, 1)
|
|
||||||
|
|
||||||
temp_file = self.tempfile("temp.webp")
|
@pytest.mark.xfail(is_big_endian() and on_ci(), reason="Fails on big-endian")
|
||||||
orig.save(temp_file, save_all=True)
|
def test_write_animation_L(tmp_path):
|
||||||
with Image.open(temp_file) as im:
|
"""
|
||||||
self.assertEqual(im.n_frames, orig.n_frames)
|
Convert an animated GIF to animated WebP, then compare the frame count, and first
|
||||||
|
and last frames to ensure they're visually similar.
|
||||||
|
"""
|
||||||
|
|
||||||
# Compare first and last frames to the original animated GIF
|
with Image.open("Tests/images/iss634.gif") as orig:
|
||||||
orig.load()
|
assert orig.n_frames > 1
|
||||||
im.load()
|
|
||||||
assert_image_similar(im, orig.convert("RGBA"), 25.0)
|
|
||||||
orig.seek(orig.n_frames - 1)
|
|
||||||
im.seek(im.n_frames - 1)
|
|
||||||
orig.load()
|
|
||||||
im.load()
|
|
||||||
assert_image_similar(im, orig.convert("RGBA"), 25.0)
|
|
||||||
|
|
||||||
@pytest.mark.xfail(is_big_endian() and on_ci(), reason="Fails on big-endian")
|
|
||||||
def test_write_animation_RGB(self):
|
|
||||||
"""
|
|
||||||
Write an animated WebP from RGB frames, and ensure the frames
|
|
||||||
are visually similar to the originals.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def check(temp_file):
|
|
||||||
with Image.open(temp_file) as im:
|
|
||||||
self.assertEqual(im.n_frames, 2)
|
|
||||||
|
|
||||||
# Compare first frame to original
|
|
||||||
im.load()
|
|
||||||
assert_image_equal(im, frame1.convert("RGBA"))
|
|
||||||
|
|
||||||
# Compare second frame to original
|
|
||||||
im.seek(1)
|
|
||||||
im.load()
|
|
||||||
assert_image_equal(im, frame2.convert("RGBA"))
|
|
||||||
|
|
||||||
with Image.open("Tests/images/anim_frame1.webp") as frame1:
|
|
||||||
with Image.open("Tests/images/anim_frame2.webp") as frame2:
|
|
||||||
temp_file1 = self.tempfile("temp.webp")
|
|
||||||
frame1.copy().save(
|
|
||||||
temp_file1, save_all=True, append_images=[frame2], lossless=True
|
|
||||||
)
|
|
||||||
check(temp_file1)
|
|
||||||
|
|
||||||
# Tests appending using a generator
|
|
||||||
def imGenerator(ims):
|
|
||||||
yield from ims
|
|
||||||
|
|
||||||
temp_file2 = self.tempfile("temp_generator.webp")
|
|
||||||
frame1.copy().save(
|
|
||||||
temp_file2,
|
|
||||||
save_all=True,
|
|
||||||
append_images=imGenerator([frame2]),
|
|
||||||
lossless=True,
|
|
||||||
)
|
|
||||||
check(temp_file2)
|
|
||||||
|
|
||||||
def test_timestamp_and_duration(self):
|
|
||||||
"""
|
|
||||||
Try passing a list of durations, and make sure the encoded
|
|
||||||
timestamps and durations are correct.
|
|
||||||
"""
|
|
||||||
|
|
||||||
durations = [0, 10, 20, 30, 40]
|
|
||||||
temp_file = self.tempfile("temp.webp")
|
|
||||||
with Image.open("Tests/images/anim_frame1.webp") as frame1:
|
|
||||||
with Image.open("Tests/images/anim_frame2.webp") as frame2:
|
|
||||||
frame1.save(
|
|
||||||
temp_file,
|
|
||||||
save_all=True,
|
|
||||||
append_images=[frame2, frame1, frame2, frame1],
|
|
||||||
duration=durations,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
temp_file = str(tmp_path / "temp.webp")
|
||||||
|
orig.save(temp_file, save_all=True)
|
||||||
with Image.open(temp_file) as im:
|
with Image.open(temp_file) as im:
|
||||||
self.assertEqual(im.n_frames, 5)
|
assert im.n_frames == orig.n_frames
|
||||||
self.assertTrue(im.is_animated)
|
|
||||||
|
|
||||||
# Check that timestamps and durations match original values specified
|
# Compare first and last frames to the original animated GIF
|
||||||
ts = 0
|
orig.load()
|
||||||
for frame in range(im.n_frames):
|
im.load()
|
||||||
im.seek(frame)
|
assert_image_similar(im, orig.convert("RGBA"), 25.0)
|
||||||
im.load()
|
orig.seek(orig.n_frames - 1)
|
||||||
self.assertEqual(im.info["duration"], durations[frame])
|
im.seek(im.n_frames - 1)
|
||||||
self.assertEqual(im.info["timestamp"], ts)
|
orig.load()
|
||||||
ts += durations[frame]
|
im.load()
|
||||||
|
assert_image_similar(im, orig.convert("RGBA"), 25.0)
|
||||||
|
|
||||||
def test_seeking(self):
|
|
||||||
"""
|
|
||||||
Create an animated WebP file, and then try seeking through
|
|
||||||
frames in reverse-order, verifying the timestamps and durations
|
|
||||||
are correct.
|
|
||||||
"""
|
|
||||||
|
|
||||||
dur = 33
|
@pytest.mark.xfail(is_big_endian() and on_ci(), reason="Fails on big-endian")
|
||||||
temp_file = self.tempfile("temp.webp")
|
def test_write_animation_RGB(tmp_path):
|
||||||
with Image.open("Tests/images/anim_frame1.webp") as frame1:
|
"""
|
||||||
with Image.open("Tests/images/anim_frame2.webp") as frame2:
|
Write an animated WebP from RGB frames, and ensure the frames
|
||||||
frame1.save(
|
are visually similar to the originals.
|
||||||
temp_file,
|
"""
|
||||||
save_all=True,
|
|
||||||
append_images=[frame2, frame1, frame2, frame1],
|
|
||||||
duration=dur,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
def check(temp_file):
|
||||||
with Image.open(temp_file) as im:
|
with Image.open(temp_file) as im:
|
||||||
self.assertEqual(im.n_frames, 5)
|
assert im.n_frames == 2
|
||||||
self.assertTrue(im.is_animated)
|
|
||||||
|
|
||||||
# Traverse frames in reverse, checking timestamps and durations
|
# Compare first frame to original
|
||||||
ts = dur * (im.n_frames - 1)
|
im.load()
|
||||||
for frame in reversed(range(im.n_frames)):
|
assert_image_equal(im, frame1.convert("RGBA"))
|
||||||
im.seek(frame)
|
|
||||||
im.load()
|
|
||||||
self.assertEqual(im.info["duration"], dur)
|
|
||||||
self.assertEqual(im.info["timestamp"], ts)
|
|
||||||
ts -= dur
|
|
||||||
|
|
||||||
def test_seek_errors(self):
|
# Compare second frame to original
|
||||||
with Image.open("Tests/images/iss634.webp") as im:
|
im.seek(1)
|
||||||
with self.assertRaises(EOFError):
|
im.load()
|
||||||
im.seek(-1)
|
assert_image_equal(im, frame2.convert("RGBA"))
|
||||||
|
|
||||||
with self.assertRaises(EOFError):
|
with Image.open("Tests/images/anim_frame1.webp") as frame1:
|
||||||
im.seek(42)
|
with Image.open("Tests/images/anim_frame2.webp") as frame2:
|
||||||
|
temp_file1 = str(tmp_path / "temp.webp")
|
||||||
|
frame1.copy().save(
|
||||||
|
temp_file1, save_all=True, append_images=[frame2], lossless=True
|
||||||
|
)
|
||||||
|
check(temp_file1)
|
||||||
|
|
||||||
|
# Tests appending using a generator
|
||||||
|
def imGenerator(ims):
|
||||||
|
yield from ims
|
||||||
|
|
||||||
|
temp_file2 = str(tmp_path / "temp_generator.webp")
|
||||||
|
frame1.copy().save(
|
||||||
|
temp_file2,
|
||||||
|
save_all=True,
|
||||||
|
append_images=imGenerator([frame2]),
|
||||||
|
lossless=True,
|
||||||
|
)
|
||||||
|
check(temp_file2)
|
||||||
|
|
||||||
|
|
||||||
|
def test_timestamp_and_duration(tmp_path):
|
||||||
|
"""
|
||||||
|
Try passing a list of durations, and make sure the encoded
|
||||||
|
timestamps and durations are correct.
|
||||||
|
"""
|
||||||
|
|
||||||
|
durations = [0, 10, 20, 30, 40]
|
||||||
|
temp_file = str(tmp_path / "temp.webp")
|
||||||
|
with Image.open("Tests/images/anim_frame1.webp") as frame1:
|
||||||
|
with Image.open("Tests/images/anim_frame2.webp") as frame2:
|
||||||
|
frame1.save(
|
||||||
|
temp_file,
|
||||||
|
save_all=True,
|
||||||
|
append_images=[frame2, frame1, frame2, frame1],
|
||||||
|
duration=durations,
|
||||||
|
)
|
||||||
|
|
||||||
|
with Image.open(temp_file) as im:
|
||||||
|
assert im.n_frames == 5
|
||||||
|
assert im.is_animated
|
||||||
|
|
||||||
|
# Check that timestamps and durations match original values specified
|
||||||
|
ts = 0
|
||||||
|
for frame in range(im.n_frames):
|
||||||
|
im.seek(frame)
|
||||||
|
im.load()
|
||||||
|
assert im.info["duration"] == durations[frame]
|
||||||
|
assert im.info["timestamp"] == ts
|
||||||
|
ts += durations[frame]
|
||||||
|
|
||||||
|
|
||||||
|
def test_seeking(tmp_path):
|
||||||
|
"""
|
||||||
|
Create an animated WebP file, and then try seeking through frames in reverse-order,
|
||||||
|
verifying the timestamps and durations are correct.
|
||||||
|
"""
|
||||||
|
|
||||||
|
dur = 33
|
||||||
|
temp_file = str(tmp_path / "temp.webp")
|
||||||
|
with Image.open("Tests/images/anim_frame1.webp") as frame1:
|
||||||
|
with Image.open("Tests/images/anim_frame2.webp") as frame2:
|
||||||
|
frame1.save(
|
||||||
|
temp_file,
|
||||||
|
save_all=True,
|
||||||
|
append_images=[frame2, frame1, frame2, frame1],
|
||||||
|
duration=dur,
|
||||||
|
)
|
||||||
|
|
||||||
|
with Image.open(temp_file) as im:
|
||||||
|
assert im.n_frames == 5
|
||||||
|
assert im.is_animated
|
||||||
|
|
||||||
|
# Traverse frames in reverse, checking timestamps and durations
|
||||||
|
ts = dur * (im.n_frames - 1)
|
||||||
|
for frame in reversed(range(im.n_frames)):
|
||||||
|
im.seek(frame)
|
||||||
|
im.load()
|
||||||
|
assert im.info["duration"] == dur
|
||||||
|
assert im.info["timestamp"] == ts
|
||||||
|
ts -= dur
|
||||||
|
|
||||||
|
|
||||||
|
def test_seek_errors():
|
||||||
|
with Image.open("Tests/images/iss634.webp") as im:
|
||||||
|
with pytest.raises(EOFError):
|
||||||
|
im.seek(-1)
|
||||||
|
|
||||||
|
with pytest.raises(EOFError):
|
||||||
|
im.seek(42)
|
||||||
|
|
|
@ -1,33 +1,27 @@
|
||||||
|
import pytest
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import PillowTestCase, assert_image_equal, hopper, skip_unless_feature
|
from .helper import assert_image_equal, hopper
|
||||||
|
|
||||||
try:
|
_webp = pytest.importorskip("PIL._webp", reason="WebP support not installed")
|
||||||
from PIL import _webp
|
RGB_MODE = "RGB"
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@skip_unless_feature("webp")
|
def test_write_lossless_rgb(tmp_path):
|
||||||
class TestFileWebpLossless(PillowTestCase):
|
if _webp.WebPDecoderVersion() < 0x0200:
|
||||||
def setUp(self):
|
pytest.skip("lossless not included")
|
||||||
if _webp.WebPDecoderVersion() < 0x0200:
|
|
||||||
self.skipTest("lossless not included")
|
|
||||||
|
|
||||||
self.rgb_mode = "RGB"
|
temp_file = str(tmp_path / "temp.webp")
|
||||||
|
|
||||||
def test_write_lossless_rgb(self):
|
hopper(RGB_MODE).save(temp_file, lossless=True)
|
||||||
temp_file = self.tempfile("temp.webp")
|
|
||||||
|
|
||||||
hopper(self.rgb_mode).save(temp_file, lossless=True)
|
with Image.open(temp_file) as image:
|
||||||
|
image.load()
|
||||||
|
|
||||||
with Image.open(temp_file) as image:
|
assert image.mode == RGB_MODE
|
||||||
image.load()
|
assert image.size == (128, 128)
|
||||||
|
assert image.format == "WEBP"
|
||||||
|
image.load()
|
||||||
|
image.getdata()
|
||||||
|
|
||||||
self.assertEqual(image.mode, self.rgb_mode)
|
assert_image_equal(image, hopper(RGB_MODE))
|
||||||
self.assertEqual(image.size, (128, 128))
|
|
||||||
self.assertEqual(image.format, "WEBP")
|
|
||||||
image.load()
|
|
||||||
image.getdata()
|
|
||||||
|
|
||||||
assert_image_equal(image, hopper(self.rgb_mode))
|
|
||||||
|
|
|
@ -2,113 +2,118 @@ from io import BytesIO
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import PillowTestCase, skip_unless_feature
|
from .helper import skip_unless_feature
|
||||||
|
|
||||||
|
pytestmark = [
|
||||||
|
skip_unless_feature("webp"),
|
||||||
|
skip_unless_feature("webp_mux"),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
@skip_unless_feature("webp")
|
def test_read_exif_metadata():
|
||||||
@skip_unless_feature("webp_mux")
|
|
||||||
class TestFileWebpMetadata(PillowTestCase):
|
|
||||||
def test_read_exif_metadata(self):
|
|
||||||
|
|
||||||
file_path = "Tests/images/flower.webp"
|
file_path = "Tests/images/flower.webp"
|
||||||
with Image.open(file_path) as image:
|
with Image.open(file_path) as image:
|
||||||
|
|
||||||
self.assertEqual(image.format, "WEBP")
|
assert image.format == "WEBP"
|
||||||
exif_data = image.info.get("exif", None)
|
exif_data = image.info.get("exif", None)
|
||||||
self.assertTrue(exif_data)
|
assert exif_data
|
||||||
|
|
||||||
exif = image._getexif()
|
exif = image._getexif()
|
||||||
|
|
||||||
# camera make
|
# Camera make
|
||||||
self.assertEqual(exif[271], "Canon")
|
assert exif[271] == "Canon"
|
||||||
|
|
||||||
with Image.open("Tests/images/flower.jpg") as jpeg_image:
|
with Image.open("Tests/images/flower.jpg") as jpeg_image:
|
||||||
expected_exif = jpeg_image.info["exif"]
|
expected_exif = jpeg_image.info["exif"]
|
||||||
|
|
||||||
self.assertEqual(exif_data, expected_exif)
|
assert exif_data == expected_exif
|
||||||
|
|
||||||
def test_write_exif_metadata(self):
|
|
||||||
file_path = "Tests/images/flower.jpg"
|
|
||||||
test_buffer = BytesIO()
|
|
||||||
with Image.open(file_path) as image:
|
|
||||||
expected_exif = image.info["exif"]
|
|
||||||
|
|
||||||
image.save(test_buffer, "webp", exif=expected_exif)
|
def test_write_exif_metadata():
|
||||||
|
file_path = "Tests/images/flower.jpg"
|
||||||
|
test_buffer = BytesIO()
|
||||||
|
with Image.open(file_path) as image:
|
||||||
|
expected_exif = image.info["exif"]
|
||||||
|
|
||||||
test_buffer.seek(0)
|
image.save(test_buffer, "webp", exif=expected_exif)
|
||||||
with Image.open(test_buffer) as webp_image:
|
|
||||||
webp_exif = webp_image.info.get("exif", None)
|
|
||||||
self.assertTrue(webp_exif)
|
|
||||||
if webp_exif:
|
|
||||||
self.assertEqual(webp_exif, expected_exif, "WebP EXIF didn't match")
|
|
||||||
|
|
||||||
def test_read_icc_profile(self):
|
test_buffer.seek(0)
|
||||||
|
with Image.open(test_buffer) as webp_image:
|
||||||
|
webp_exif = webp_image.info.get("exif", None)
|
||||||
|
assert webp_exif
|
||||||
|
if webp_exif:
|
||||||
|
assert webp_exif == expected_exif, "WebP EXIF didn't match"
|
||||||
|
|
||||||
file_path = "Tests/images/flower2.webp"
|
|
||||||
with Image.open(file_path) as image:
|
|
||||||
|
|
||||||
self.assertEqual(image.format, "WEBP")
|
def test_read_icc_profile():
|
||||||
self.assertTrue(image.info.get("icc_profile", None))
|
|
||||||
|
|
||||||
icc = image.info["icc_profile"]
|
file_path = "Tests/images/flower2.webp"
|
||||||
|
with Image.open(file_path) as image:
|
||||||
|
|
||||||
with Image.open("Tests/images/flower2.jpg") as jpeg_image:
|
assert image.format == "WEBP"
|
||||||
expected_icc = jpeg_image.info["icc_profile"]
|
assert image.info.get("icc_profile", None)
|
||||||
|
|
||||||
self.assertEqual(icc, expected_icc)
|
icc = image.info["icc_profile"]
|
||||||
|
|
||||||
def test_write_icc_metadata(self):
|
with Image.open("Tests/images/flower2.jpg") as jpeg_image:
|
||||||
file_path = "Tests/images/flower2.jpg"
|
expected_icc = jpeg_image.info["icc_profile"]
|
||||||
test_buffer = BytesIO()
|
|
||||||
with Image.open(file_path) as image:
|
|
||||||
expected_icc_profile = image.info["icc_profile"]
|
|
||||||
|
|
||||||
image.save(test_buffer, "webp", icc_profile=expected_icc_profile)
|
assert icc == expected_icc
|
||||||
|
|
||||||
test_buffer.seek(0)
|
|
||||||
with Image.open(test_buffer) as webp_image:
|
|
||||||
webp_icc_profile = webp_image.info.get("icc_profile", None)
|
|
||||||
|
|
||||||
self.assertTrue(webp_icc_profile)
|
def test_write_icc_metadata():
|
||||||
if webp_icc_profile:
|
file_path = "Tests/images/flower2.jpg"
|
||||||
self.assertEqual(
|
test_buffer = BytesIO()
|
||||||
webp_icc_profile, expected_icc_profile, "Webp ICC didn't match"
|
with Image.open(file_path) as image:
|
||||||
|
expected_icc_profile = image.info["icc_profile"]
|
||||||
|
|
||||||
|
image.save(test_buffer, "webp", icc_profile=expected_icc_profile)
|
||||||
|
|
||||||
|
test_buffer.seek(0)
|
||||||
|
with Image.open(test_buffer) as webp_image:
|
||||||
|
webp_icc_profile = webp_image.info.get("icc_profile", None)
|
||||||
|
|
||||||
|
assert webp_icc_profile
|
||||||
|
if webp_icc_profile:
|
||||||
|
assert webp_icc_profile == expected_icc_profile, "Webp ICC didn't match"
|
||||||
|
|
||||||
|
|
||||||
|
def test_read_no_exif():
|
||||||
|
file_path = "Tests/images/flower.jpg"
|
||||||
|
test_buffer = BytesIO()
|
||||||
|
with Image.open(file_path) as image:
|
||||||
|
assert "exif" in image.info
|
||||||
|
|
||||||
|
image.save(test_buffer, "webp")
|
||||||
|
|
||||||
|
test_buffer.seek(0)
|
||||||
|
with Image.open(test_buffer) as webp_image:
|
||||||
|
assert not webp_image._getexif()
|
||||||
|
|
||||||
|
|
||||||
|
@skip_unless_feature("webp_anim")
|
||||||
|
def test_write_animated_metadata(tmp_path):
|
||||||
|
iccp_data = b"<iccp_data>"
|
||||||
|
exif_data = b"<exif_data>"
|
||||||
|
xmp_data = b"<xmp_data>"
|
||||||
|
|
||||||
|
temp_file = str(tmp_path / "temp.webp")
|
||||||
|
with Image.open("Tests/images/anim_frame1.webp") as frame1:
|
||||||
|
with Image.open("Tests/images/anim_frame2.webp") as frame2:
|
||||||
|
frame1.save(
|
||||||
|
temp_file,
|
||||||
|
save_all=True,
|
||||||
|
append_images=[frame2, frame1, frame2],
|
||||||
|
icc_profile=iccp_data,
|
||||||
|
exif=exif_data,
|
||||||
|
xmp=xmp_data,
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_read_no_exif(self):
|
with Image.open(temp_file) as image:
|
||||||
file_path = "Tests/images/flower.jpg"
|
assert "icc_profile" in image.info
|
||||||
test_buffer = BytesIO()
|
assert "exif" in image.info
|
||||||
with Image.open(file_path) as image:
|
assert "xmp" in image.info
|
||||||
self.assertIn("exif", image.info)
|
assert iccp_data == image.info.get("icc_profile", None)
|
||||||
|
assert exif_data == image.info.get("exif", None)
|
||||||
image.save(test_buffer, "webp")
|
assert xmp_data == image.info.get("xmp", None)
|
||||||
|
|
||||||
test_buffer.seek(0)
|
|
||||||
with Image.open(test_buffer) as webp_image:
|
|
||||||
self.assertFalse(webp_image._getexif())
|
|
||||||
|
|
||||||
@skip_unless_feature("webp_anim")
|
|
||||||
def test_write_animated_metadata(self):
|
|
||||||
iccp_data = b"<iccp_data>"
|
|
||||||
exif_data = b"<exif_data>"
|
|
||||||
xmp_data = b"<xmp_data>"
|
|
||||||
|
|
||||||
temp_file = self.tempfile("temp.webp")
|
|
||||||
with Image.open("Tests/images/anim_frame1.webp") as frame1:
|
|
||||||
with Image.open("Tests/images/anim_frame2.webp") as frame2:
|
|
||||||
frame1.save(
|
|
||||||
temp_file,
|
|
||||||
save_all=True,
|
|
||||||
append_images=[frame2, frame1, frame2],
|
|
||||||
icc_profile=iccp_data,
|
|
||||||
exif=exif_data,
|
|
||||||
xmp=xmp_data,
|
|
||||||
)
|
|
||||||
|
|
||||||
with Image.open(temp_file) as image:
|
|
||||||
self.assertIn("icc_profile", image.info)
|
|
||||||
self.assertIn("exif", image.info)
|
|
||||||
self.assertIn("xmp", image.info)
|
|
||||||
self.assertEqual(iccp_data, image.info.get("icc_profile", None))
|
|
||||||
self.assertEqual(exif_data, image.info.get("exif", None))
|
|
||||||
self.assertEqual(xmp_data, image.info.get("xmp", None))
|
|
||||||
|
|
|
@ -5,11 +5,6 @@ from PIL import Image
|
||||||
|
|
||||||
from .helper import is_win32
|
from .helper import is_win32
|
||||||
|
|
||||||
try:
|
|
||||||
import numpy
|
|
||||||
except ImportError:
|
|
||||||
numpy = None
|
|
||||||
|
|
||||||
pytestmark = pytest.mark.skipif(is_win32(), reason="Win32 does not call map_buffer")
|
pytestmark = pytest.mark.skipif(is_win32(), reason="Win32 does not call map_buffer")
|
||||||
|
|
||||||
|
|
||||||
|
@ -32,8 +27,9 @@ def test_overflow():
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(sys.maxsize <= 2 ** 32, reason="Requires 64-bit system")
|
@pytest.mark.skipif(sys.maxsize <= 2 ** 32, reason="Requires 64-bit system")
|
||||||
@pytest.mark.skipif(numpy is None, reason="NumPy is not installed")
|
|
||||||
def test_ysize():
|
def test_ysize():
|
||||||
|
numpy = pytest.importorskip("numpy", reason="NumPy not installed")
|
||||||
|
|
||||||
# Should not raise 'Integer overflow in ysize'
|
# Should not raise 'Integer overflow in ysize'
|
||||||
arr = numpy.zeros((46341, 46341), dtype=numpy.uint8)
|
arr = numpy.zeros((46341, 46341), dtype=numpy.uint8)
|
||||||
Image.fromarray(arr)
|
Image.fromarray(arr)
|
||||||
|
|
|
@ -3,18 +3,11 @@ from PIL import Image
|
||||||
|
|
||||||
from .helper import assert_deep_equal, assert_image, hopper
|
from .helper import assert_deep_equal, assert_image, hopper
|
||||||
|
|
||||||
try:
|
numpy = pytest.importorskip("numpy", reason="NumPy not installed")
|
||||||
import numpy
|
|
||||||
except ImportError:
|
|
||||||
numpy = None
|
|
||||||
|
|
||||||
|
|
||||||
TEST_IMAGE_SIZE = (10, 10)
|
TEST_IMAGE_SIZE = (10, 10)
|
||||||
|
|
||||||
|
|
||||||
pytestmark = pytest.mark.skipif(numpy is None, reason="NumPy is not installed")
|
|
||||||
|
|
||||||
|
|
||||||
def test_numpy_to_image():
|
def test_numpy_to_image():
|
||||||
def to_image(dtype, bands=1, boolean=0):
|
def to_image(dtype, bands=1, boolean=0):
|
||||||
if bands == 1:
|
if bands == 1:
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
import pytest
|
import pytest
|
||||||
from PIL import __version__
|
from PIL import __version__
|
||||||
|
|
||||||
try:
|
pyroma = pytest.importorskip("pyroma", reason="Pyroma not installed")
|
||||||
import pyroma
|
|
||||||
except ImportError:
|
|
||||||
pyroma = None
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(pyroma is None, reason="Pyroma is not installed")
|
|
||||||
def test_pyroma():
|
def test_pyroma():
|
||||||
# Arrange
|
# Arrange
|
||||||
data = pyroma.projectdata.get_data(".")
|
data = pyroma.projectdata.get_data(".")
|
||||||
|
|
Loading…
Reference in New Issue
Block a user