diff --git a/Tests/test_image_crop.py b/Tests/test_image_crop.py index 9df8883a4f0..b374114357c 100644 --- a/Tests/test_image_crop.py +++ b/Tests/test_image_crop.py @@ -69,6 +69,15 @@ def test_crop_float() -> None: assert cropped.size == (3, 5) +@pytest.mark.parametrize("left", (1.5, 2.5, 84.5, 85.5)) +def test_crop_float_half(left: float) -> None: + # Halves are rounded up, so a box keeps its width wherever it is + im = Image.new("L", (200, 10)) + + assert im.crop((left, 0, left + 31, 1)).size == (31, 1) + assert im.crop((left, 0, left + 1, 1)).size == (1, 1) + + def test_crop_crash() -> None: # Image.crop crashes prepatch with an access violation # apparently a use after free on Windows, see diff --git a/docs/releasenotes/13.1.0.rst b/docs/releasenotes/13.1.0.rst new file mode 100644 index 00000000000..5da2eeebd95 --- /dev/null +++ b/docs/releasenotes/13.1.0.rst @@ -0,0 +1,58 @@ +xx.y.z +------ + +Security +======== + +TODO +^^^^ + +TODO + +:cve:`YYYY-XXXXX`: TODO +^^^^^^^^^^^^^^^^^^^^^^^ + +TODO + +Backwards incompatible changes +============================== + +TODO +^^^^ + +TODO + +Deprecations +============ + +TODO +^^^^ + +TODO + +API changes +=========== + +TODO +^^^^ + +TODO + +API additions +============= + +TODO +^^^^ + +TODO + +Other changes +============= + +Rounding crop coordinates +^^^^^^^^^^^^^^^^^^^^^^^^^ + +:py:meth:`~PIL.Image.Image.crop` rounds float coordinates to the nearest integer. +Coordinates ending in .5 were rounded to the nearest even integer, so a box of the same +size could give an image one pixel larger or smaller depending on where it was. They +are now always rounded up. diff --git a/docs/releasenotes/index.rst b/docs/releasenotes/index.rst index e45919afb07..bc0cb895d92 100644 --- a/docs/releasenotes/index.rst +++ b/docs/releasenotes/index.rst @@ -15,6 +15,7 @@ expected to be backported to earlier versions. :maxdepth: 2 versioning + 13.1.0 13.0.0 12.3.0 12.2.0 diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 40720c5d438..e0868eb1088 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1403,7 +1403,7 @@ def _crop( :returns: A core image object. """ - x0, y0, x1, y1 = map(int, map(round, box)) + x0, y0, x1, y1 = (math.floor(v + 0.5) for v in box) absolute_values = (abs(x1 - x0), abs(y1 - y0))