Skip to content

Commit

Permalink
Merge pull request #7978 from radarhere/bgr
Browse files Browse the repository at this point in the history
Deprecate BGR;15, BGR;16 and BGR;24 modes
  • Loading branch information
radarhere committed Apr 25, 2024
2 parents 1138ea5 + 5736da8 commit 5832288
Show file tree
Hide file tree
Showing 10 changed files with 61 additions and 15 deletions.
7 changes: 6 additions & 1 deletion Tests/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,12 @@ def _cached_hopper(mode: str) -> Image.Image:
im = hopper("L")
else:
im = hopper()
return im.convert(mode)
if mode.startswith("BGR;"):
with pytest.warns(DeprecationWarning):
im = im.convert(mode)
else:
im = im.convert(mode)
return im


def djpeg_available() -> bool:
Expand Down
21 changes: 17 additions & 4 deletions Tests/test_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,19 @@
)


# Deprecation helper
def helper_image_new(mode: str, size: tuple[int, int]) -> Image.Image:
if mode.startswith("BGR;"):
with pytest.warns(DeprecationWarning):
return Image.new(mode, size)
else:
return Image.new(mode, size)


class TestImage:
@pytest.mark.parametrize("mode", modes)
def test_image_modes_success(self, mode: str) -> None:
Image.new(mode, (1, 1))
helper_image_new(mode, (1, 1))

@pytest.mark.parametrize("mode", ("", "bad", "very very long"))
def test_image_modes_fail(self, mode: str) -> None:
Expand Down Expand Up @@ -1023,15 +1032,19 @@ def test_roundtrip_bytes_constructor(self, mode: str) -> None:
im = hopper(mode)
source_bytes = im.tobytes()

reloaded = Image.frombytes(mode, im.size, source_bytes)
if mode.startswith("BGR;"):
with pytest.warns(DeprecationWarning):
reloaded = Image.frombytes(mode, im.size, source_bytes)
else:
reloaded = Image.frombytes(mode, im.size, source_bytes)
assert reloaded.tobytes() == source_bytes

@pytest.mark.parametrize("mode", modes)
def test_roundtrip_bytes_method(self, mode: str) -> None:
im = hopper(mode)
source_bytes = im.tobytes()

reloaded = Image.new(mode, im.size)
reloaded = helper_image_new(mode, im.size)
reloaded.frombytes(source_bytes)
assert reloaded.tobytes() == source_bytes

Expand All @@ -1040,7 +1053,7 @@ def test_getdata_putdata(self, mode: str) -> None:
if is_big_endian and mode == "BGR;15":
pytest.xfail("Known failure of BGR;15 on big-endian")
im = hopper(mode)
reloaded = Image.new(mode, im.size)
reloaded = helper_image_new(mode, im.size)
reloaded.putdata(im.getdata())
assert_image_equal(im, reloaded)

Expand Down
6 changes: 5 additions & 1 deletion Tests/test_image_access.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,11 @@ def check(self, mode: str, expected_color_int: int | None = None) -> None:

@pytest.mark.parametrize("mode", modes)
def test_basic(self, mode: str) -> None:
self.check(mode)
if mode.startswith("BGR;"):
with pytest.warns(DeprecationWarning):
self.check(mode)
else:
self.check(mode)

def test_list(self) -> None:
im = hopper()
Expand Down
3 changes: 2 additions & 1 deletion Tests/test_image_putdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ def test_mode_F() -> None:
@pytest.mark.parametrize("mode", ("BGR;15", "BGR;16", "BGR;24"))
def test_mode_BGR(mode: str) -> None:
data = [(16, 32, 49), (32, 32, 98)]
im = Image.new(mode, (1, 2))
with pytest.warns(DeprecationWarning):
im = Image.new(mode, (1, 2))
im.putdata(data)

assert list(im.getdata()) == data
Expand Down
13 changes: 8 additions & 5 deletions Tests/test_lib_pack.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,11 +362,14 @@ def test_RGB(self) -> None:
)

def test_BGR(self) -> None:
self.assert_unpack("BGR;15", "BGR;15", 3, (8, 131, 0), (24, 0, 8), (41, 131, 8))
self.assert_unpack(
"BGR;16", "BGR;16", 3, (8, 64, 0), (24, 129, 0), (41, 194, 0)
)
self.assert_unpack("BGR;24", "BGR;24", 3, (1, 2, 3), (4, 5, 6), (7, 8, 9))
with pytest.warns(DeprecationWarning):
self.assert_unpack(
"BGR;15", "BGR;15", 3, (8, 131, 0), (24, 0, 8), (41, 131, 8)
)
self.assert_unpack(
"BGR;16", "BGR;16", 3, (8, 64, 0), (24, 129, 0), (41, 194, 0)
)
self.assert_unpack("BGR;24", "BGR;24", 3, (1, 2, 3), (4, 5, 6), (7, 8, 9))

def test_RGBA(self) -> None:
self.assert_unpack("RGBA", "LA", 2, (1, 1, 1, 2), (3, 3, 3, 4), (5, 5, 5, 6))
Expand Down
7 changes: 7 additions & 0 deletions docs/deprecations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,13 @@ ImageMath eval()
``ImageMath.eval()`` has been deprecated. Use :py:meth:`~PIL.ImageMath.lambda_eval` or
:py:meth:`~PIL.ImageMath.unsafe_eval` instead.

BGR;15, BGR 16 and BGR;24
^^^^^^^^^^^^^^^^^^^^^^^^^

.. deprecated:: 10.4.0

The experimental BGR;15, BGR;16 and BGR;24 modes have been deprecated.

Support for LibTIFF earlier than 4
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down
3 changes: 0 additions & 3 deletions docs/handbook/concepts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,6 @@ Pillow also provides limited support for a few additional modes, including:
* ``I;16L`` (16-bit little endian unsigned integer pixels)
* ``I;16B`` (16-bit big endian unsigned integer pixels)
* ``I;16N`` (16-bit native endian unsigned integer pixels)
* ``BGR;15`` (15-bit reversed true colour)
* ``BGR;16`` (16-bit reversed true colour)
* ``BGR;24`` (24-bit reversed true colour)

Premultiplied alpha is where the values for each other channel have been
multiplied by the alpha. For example, an RGBA pixel of ``(10, 20, 30, 127)``
Expand Down
5 changes: 5 additions & 0 deletions docs/releasenotes/10.4.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ TODO
Deprecations
============

BGR;15, BGR 16 and BGR;24
^^^^^^^^^^^^^^^^^^^^^^^^^

The experimental BGR;15, BGR;16 and BGR;24 modes have been deprecated.

Support for LibTIFF earlier than 4
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down
7 changes: 7 additions & 0 deletions src/PIL/Image.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
_plugins,
)
from ._binary import i32le, o32be, o32le
from ._deprecate import deprecate
from ._typing import StrOrBytesPath, TypeGuard
from ._util import DeferredError, is_path

Expand Down Expand Up @@ -939,6 +940,9 @@ def convert(
:returns: An :py:class:`~PIL.Image.Image` object.
"""

if mode in ("BGR;15", "BGR;16", "BGR;24"):
deprecate(mode, 12)

self.load()

has_transparency = "transparency" in self.info
Expand Down Expand Up @@ -2956,6 +2960,9 @@ def new(
:returns: An :py:class:`~PIL.Image.Image` object.
"""

if mode in ("BGR;15", "BGR;16", "BGR;24"):
deprecate(mode, 12)

_check_size(size)

if color is None:
Expand Down
4 changes: 4 additions & 0 deletions src/PIL/ImageMode.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
from functools import lru_cache
from typing import NamedTuple

from ._deprecate import deprecate


class ModeDescriptor(NamedTuple):
"""Wrapper for mode strings."""
Expand Down Expand Up @@ -63,6 +65,8 @@ def getmode(mode: str) -> ModeDescriptor:
"PA": ("RGB", "L", ("P", "A"), "|u1"),
}
if mode in modes:
if mode in ("BGR;15", "BGR;16", "BGR;24"):
deprecate(mode, 12)
base_mode, base_type, bands, type_str = modes[mode]
return ModeDescriptor(mode, bands, base_mode, base_type, type_str)

Expand Down

0 comments on commit 5832288

Please sign in to comment.