2023-12-21 14:13:31 +03:00
|
|
|
from __future__ import annotations
|
2024-01-20 14:23:03 +03:00
|
|
|
|
2020-02-12 19:29:19 +03:00
|
|
|
import pytest
|
2020-08-07 13:28:33 +03:00
|
|
|
|
2019-02-04 22:15:50 +03:00
|
|
|
from PIL import Image
|
2012-10-16 00:26:38 +04:00
|
|
|
|
2020-01-30 17:56:07 +03:00
|
|
|
from .helper import (
|
|
|
|
assert_image_equal,
|
|
|
|
assert_image_similar,
|
|
|
|
fromstring,
|
|
|
|
hopper,
|
2022-04-20 04:05:12 +03:00
|
|
|
skip_unless_feature,
|
2020-01-30 17:56:07 +03:00
|
|
|
tostring,
|
|
|
|
)
|
2019-07-06 23:40:53 +03:00
|
|
|
|
2012-10-16 00:26:38 +04:00
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_sanity() -> None:
|
2020-02-12 19:29:19 +03:00
|
|
|
im = hopper()
|
|
|
|
assert im.thumbnail((100, 100)) is None
|
2012-10-16 00:26:38 +04:00
|
|
|
|
2020-02-12 19:29:19 +03:00
|
|
|
assert im.size == (100, 100)
|
2012-10-16 00:26:38 +04:00
|
|
|
|
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_aspect() -> None:
|
2020-02-12 19:29:19 +03:00
|
|
|
im = Image.new("L", (128, 128))
|
|
|
|
im.thumbnail((100, 100))
|
|
|
|
assert im.size == (100, 100)
|
2012-10-16 00:26:38 +04:00
|
|
|
|
2020-02-12 19:29:19 +03:00
|
|
|
im = Image.new("L", (128, 256))
|
|
|
|
im.thumbnail((100, 100))
|
|
|
|
assert im.size == (50, 100)
|
2012-10-16 00:26:38 +04:00
|
|
|
|
2020-02-12 19:29:19 +03:00
|
|
|
im = Image.new("L", (128, 256))
|
|
|
|
im.thumbnail((50, 100))
|
|
|
|
assert im.size == (50, 100)
|
2012-10-16 00:26:38 +04:00
|
|
|
|
2020-02-12 19:29:19 +03:00
|
|
|
im = Image.new("L", (256, 128))
|
|
|
|
im.thumbnail((100, 100))
|
|
|
|
assert im.size == (100, 50)
|
2014-06-10 13:10:47 +04:00
|
|
|
|
2020-02-09 03:19:53 +03:00
|
|
|
im = Image.new("L", (256, 128))
|
|
|
|
im.thumbnail((100, 50))
|
|
|
|
assert im.size == (100, 50)
|
|
|
|
|
2020-02-05 01:06:07 +03:00
|
|
|
im = Image.new("L", (64, 64))
|
|
|
|
im.thumbnail((100, 100))
|
|
|
|
assert im.size == (64, 64)
|
2019-12-07 20:07:27 +03:00
|
|
|
|
2020-02-12 19:29:19 +03:00
|
|
|
im = Image.new("L", (256, 162)) # ratio is 1.5802469136
|
|
|
|
im.thumbnail((33, 33))
|
|
|
|
assert im.size == (33, 21) # ratio is 1.5714285714
|
2019-02-04 22:15:50 +03:00
|
|
|
|
2020-02-12 19:29:19 +03:00
|
|
|
im = Image.new("L", (162, 256)) # ratio is 0.6328125
|
|
|
|
im.thumbnail((33, 33))
|
|
|
|
assert im.size == (21, 33) # ratio is 0.6363636364
|
2019-12-16 22:05:36 +03:00
|
|
|
|
2020-02-04 23:14:00 +03:00
|
|
|
im = Image.new("L", (145, 100)) # ratio is 1.45
|
|
|
|
im.thumbnail((50, 50))
|
2020-02-09 03:19:53 +03:00
|
|
|
assert im.size == (50, 34) # ratio is 1.47058823529
|
2020-02-04 23:14:00 +03:00
|
|
|
|
|
|
|
im = Image.new("L", (100, 145)) # ratio is 0.689655172414
|
|
|
|
im.thumbnail((50, 50))
|
|
|
|
assert im.size == (34, 50) # ratio is 0.68
|
|
|
|
|
2020-02-22 02:30:35 +03:00
|
|
|
im = Image.new("L", (100, 30)) # ratio is 3.333333333333
|
|
|
|
im.thumbnail((75, 75))
|
|
|
|
assert im.size == (75, 23) # ratio is 3.260869565217
|
|
|
|
|
2019-02-04 22:15:50 +03:00
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_division_by_zero() -> None:
|
2020-05-15 11:29:52 +03:00
|
|
|
im = Image.new("L", (200, 2))
|
|
|
|
im.thumbnail((75, 75))
|
|
|
|
assert im.size == (75, 1)
|
|
|
|
|
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_float() -> None:
|
2020-02-12 19:29:19 +03:00
|
|
|
im = Image.new("L", (128, 128))
|
|
|
|
im.thumbnail((99.9, 99.9))
|
2020-02-09 03:19:53 +03:00
|
|
|
assert im.size == (99, 99)
|
2020-02-12 19:29:19 +03:00
|
|
|
|
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_no_resize() -> None:
|
2020-02-12 19:29:19 +03:00
|
|
|
# Check that draft() can resize the image to the destination size
|
|
|
|
with Image.open("Tests/images/hopper.jpg") as im:
|
|
|
|
im.draft(None, (64, 64))
|
|
|
|
assert im.size == (64, 64)
|
|
|
|
|
|
|
|
# Test thumbnail(), where only draft() is necessary to resize the image
|
|
|
|
with Image.open("Tests/images/hopper.jpg") as im:
|
|
|
|
im.thumbnail((64, 64))
|
|
|
|
assert im.size == (64, 64)
|
|
|
|
|
2021-01-07 16:57:49 +03:00
|
|
|
|
2022-04-20 04:05:12 +03:00
|
|
|
@skip_unless_feature("libtiff")
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_load_first() -> None:
|
2022-04-06 04:19:39 +03:00
|
|
|
# load() may change the size of the image
|
|
|
|
# Test that thumbnail() is calling it before performing size calculations
|
|
|
|
with Image.open("Tests/images/g4_orientation_5.tif") as im:
|
|
|
|
im.thumbnail((64, 64))
|
|
|
|
assert im.size == (64, 10)
|
|
|
|
|
2022-08-26 13:33:51 +03:00
|
|
|
# Test thumbnail(), without draft(),
|
|
|
|
# on an image that is large enough once load() has changed the size
|
|
|
|
with Image.open("Tests/images/g4_orientation_5.tif") as im:
|
|
|
|
im.thumbnail((590, 88), reducing_gap=None)
|
|
|
|
assert im.size == (590, 88)
|
|
|
|
|
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_load_first_unless_jpeg() -> None:
|
2022-08-26 13:33:51 +03:00
|
|
|
# Test that thumbnail() still uses draft() for JPEG
|
|
|
|
with Image.open("Tests/images/hopper.jpg") as im:
|
|
|
|
draft = im.draft
|
|
|
|
|
2024-06-05 15:27:23 +03:00
|
|
|
def im_draft(
|
|
|
|
mode: str, size: tuple[int, int]
|
|
|
|
) -> tuple[str, tuple[int, int, float, float]] | None:
|
2022-08-26 13:33:51 +03:00
|
|
|
result = draft(mode, size)
|
|
|
|
assert result is not None
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
im.draft = im_draft
|
|
|
|
|
|
|
|
im.thumbnail((64, 64))
|
|
|
|
|
2022-04-06 04:19:39 +03:00
|
|
|
|
2021-04-10 00:33:21 +03:00
|
|
|
# valgrind test is failing with memory allocated in libjpeg
|
2021-01-07 16:50:25 +03:00
|
|
|
@pytest.mark.valgrind_known_error(reason="Known Failing")
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_DCT_scaling_edges() -> None:
|
2020-02-12 19:29:19 +03:00
|
|
|
# Make an image with red borders and size (N * 8) + 1 to cross DCT grid
|
|
|
|
im = Image.new("RGB", (257, 257), "red")
|
|
|
|
im.paste(Image.new("RGB", (235, 235)), (11, 11))
|
2019-11-24 05:24:00 +03:00
|
|
|
|
2020-02-12 19:29:19 +03:00
|
|
|
thumb = fromstring(tostring(im, "JPEG", quality=99, subsampling=0))
|
|
|
|
# small reducing_gap to amplify the effect
|
2022-01-15 01:02:31 +03:00
|
|
|
thumb.thumbnail((32, 32), Image.Resampling.BICUBIC, reducing_gap=1.0)
|
2019-11-24 05:24:00 +03:00
|
|
|
|
2022-01-15 01:02:31 +03:00
|
|
|
ref = im.resize((32, 32), Image.Resampling.BICUBIC)
|
2020-02-12 19:29:19 +03:00
|
|
|
# This is still JPEG, some error is present. Without the fix it is 11.5
|
|
|
|
assert_image_similar(thumb, ref, 1.5)
|
2019-12-20 17:10:40 +03:00
|
|
|
|
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_reducing_gap_values() -> None:
|
2020-02-12 19:29:19 +03:00
|
|
|
im = hopper()
|
2022-01-15 01:02:31 +03:00
|
|
|
im.thumbnail((18, 18), Image.Resampling.BICUBIC)
|
2020-02-12 19:29:19 +03:00
|
|
|
|
|
|
|
ref = hopper()
|
2022-01-15 01:02:31 +03:00
|
|
|
ref.thumbnail((18, 18), Image.Resampling.BICUBIC, reducing_gap=2.0)
|
2020-02-12 19:29:19 +03:00
|
|
|
# reducing_gap=2.0 should be the default
|
|
|
|
assert_image_equal(ref, im)
|
|
|
|
|
|
|
|
ref = hopper()
|
2022-01-15 01:02:31 +03:00
|
|
|
ref.thumbnail((18, 18), Image.Resampling.BICUBIC, reducing_gap=None)
|
2023-11-12 23:30:28 +03:00
|
|
|
with pytest.raises(pytest.fail.Exception):
|
2020-01-30 17:56:07 +03:00
|
|
|
assert_image_equal(ref, im)
|
2019-12-20 17:10:40 +03:00
|
|
|
|
2020-02-12 19:29:19 +03:00
|
|
|
assert_image_similar(ref, im, 3.5)
|
2019-12-20 17:10:40 +03:00
|
|
|
|
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_reducing_gap_for_DCT_scaling() -> None:
|
2020-02-12 19:29:19 +03:00
|
|
|
with Image.open("Tests/images/hopper.jpg") as ref:
|
|
|
|
# thumbnail should call draft with reducing_gap scale
|
|
|
|
ref.draft(None, (18 * 3, 18 * 3))
|
2022-01-15 01:02:31 +03:00
|
|
|
ref = ref.resize((18, 18), Image.Resampling.BICUBIC)
|
2019-12-20 17:10:40 +03:00
|
|
|
|
2020-02-12 19:29:19 +03:00
|
|
|
with Image.open("Tests/images/hopper.jpg") as im:
|
2022-01-15 01:02:31 +03:00
|
|
|
im.thumbnail((18, 18), Image.Resampling.BICUBIC, reducing_gap=3.0)
|
2019-12-20 17:10:40 +03:00
|
|
|
|
2022-04-06 04:19:39 +03:00
|
|
|
assert_image_similar(ref, im, 1.4)
|