Removed BGR;15, BGR;16 and BGR;24 modes

This commit is contained in:
Andrew Murray 2025-07-01 23:57:46 +10:00
parent 37cd041e5e
commit 583f0a50d5
16 changed files with 50 additions and 290 deletions

View File

@ -271,10 +271,6 @@ def _cached_hopper(mode: str) -> Image.Image:
im = hopper("L")
else:
im = hopper()
if mode.startswith("BGR;"):
with pytest.warns(DeprecationWarning, match="BGR;"):
im = im.convert(mode)
else:
try:
im = im.convert(mode)
except ImportError:

View File

@ -30,7 +30,6 @@ from .helper import (
assert_image_similar_tofile,
assert_not_all_same,
hopper,
is_big_endian,
is_win32,
mark_if_feature_version,
skip_unless_feature,
@ -50,19 +49,10 @@ except ImportError:
PrettyPrinter = None
# Deprecation helper
def helper_image_new(mode: str, size: tuple[int, int]) -> Image.Image:
if mode.startswith("BGR;"):
with pytest.warns(DeprecationWarning, match="BGR;"):
return Image.new(mode, size)
else:
return Image.new(mode, size)
class TestImage:
@pytest.mark.parametrize("mode", Image.MODES + ["BGR;15", "BGR;16", "BGR;24"])
@pytest.mark.parametrize("mode", Image.MODES)
def test_image_modes_success(self, mode: str) -> None:
helper_image_new(mode, (1, 1))
Image.new(mode, (1, 1))
@pytest.mark.parametrize("mode", ("", "bad", "very very long"))
def test_image_modes_fail(self, mode: str) -> None:
@ -1148,33 +1138,27 @@ class TestImage:
class TestImageBytes:
@pytest.mark.parametrize("mode", Image.MODES + ["BGR;15", "BGR;16", "BGR;24"])
@pytest.mark.parametrize("mode", Image.MODES)
def test_roundtrip_bytes_constructor(self, mode: str) -> None:
im = hopper(mode)
source_bytes = im.tobytes()
if mode.startswith("BGR;"):
with pytest.warns(DeprecationWarning, match=mode):
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", Image.MODES + ["BGR;15", "BGR;16", "BGR;24"])
@pytest.mark.parametrize("mode", Image.MODES)
def test_roundtrip_bytes_method(self, mode: str) -> None:
im = hopper(mode)
source_bytes = im.tobytes()
reloaded = helper_image_new(mode, im.size)
reloaded = Image.new(mode, im.size)
reloaded.frombytes(source_bytes)
assert reloaded.tobytes() == source_bytes
@pytest.mark.parametrize("mode", Image.MODES + ["BGR;15", "BGR;16", "BGR;24"])
@pytest.mark.parametrize("mode", Image.MODES)
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 = helper_image_new(mode, im.size)
reloaded = Image.new(mode, im.size)
reloaded.putdata(im.getdata())
assert_image_equal(im, reloaded)

View File

@ -123,10 +123,6 @@ class TestImageGetPixel:
bands = Image.getmodebands(mode)
if bands == 1:
return 1
if mode in ("BGR;15", "BGR;16"):
# These modes have less than 8 bits per band,
# so (1, 2, 3) cannot be roundtripped.
return (16, 32, 49)
return tuple(range(1, bands + 1))
def check(self, mode: str, expected_color_int: int | None = None) -> None:
@ -191,11 +187,6 @@ class TestImageGetPixel:
def test_basic(self, mode: str) -> None:
self.check(mode)
@pytest.mark.parametrize("mode", ("BGR;15", "BGR;16", "BGR;24"))
def test_deprecated(self, mode: str) -> None:
with pytest.warns(DeprecationWarning, match="BGR;"):
self.check(mode)
def test_list(self) -> None:
im = hopper()
assert im.getpixel([0, 0]) == (20, 20, 70)
@ -218,7 +209,7 @@ class TestImageGetPixel:
class TestImagePutPixelError:
IMAGE_MODES1 = ["LA", "RGB", "RGBA", "BGR;15"]
IMAGE_MODES1 = ["LA", "RGB", "RGBA"]
IMAGE_MODES2 = ["L", "I", "I;16"]
INVALID_TYPES = ["foo", 1.0, None]
@ -234,11 +225,6 @@ class TestImagePutPixelError:
(
("L", (0, 2), "color must be int or single-element tuple"),
("LA", (0, 3), "color must be int, or tuple of one or two elements"),
(
"BGR;15",
(0, 2),
"color must be int, or tuple of one or three elements",
),
(
"RGB",
(0, 2, 5),

View File

@ -78,16 +78,6 @@ def test_mode_F() -> None:
assert list(im.getdata()) == target
@pytest.mark.parametrize("mode", ("BGR;15", "BGR;16", "BGR;24"))
def test_mode_BGR(mode: str) -> None:
data = [(16, 32, 49), (32, 32, 98)]
with pytest.warns(DeprecationWarning, match=mode):
im = Image.new(mode, (1, 2))
im.putdata(data)
assert list(im.getdata()) == data
def test_array_B() -> None:
# shouldn't segfault
# see https://github.com/python-pillow/Pillow/issues/1008

View File

@ -324,7 +324,7 @@ class TestImageResize:
im = hopper(mode)
assert im.resize((20, 20), Image.Resampling.BICUBIC) == im.resize((20, 20))
@pytest.mark.parametrize("mode", ("1", "P", "BGR;15", "BGR;16"))
@pytest.mark.parametrize("mode", ("1", "P"))
def test_default_filter_nearest(self, mode: str) -> None:
im = hopper(mode)
assert im.resize((20, 20), Image.Resampling.NEAREST) == im.resize((20, 20))

View File

@ -361,18 +361,6 @@ class TestLibUnpack:
"RGB", "CMYK", 4, (250, 249, 248), (242, 241, 240), (234, 233, 233)
)
def test_BGR(self) -> None:
with pytest.warns(DeprecationWarning, match="BGR;15"):
self.assert_unpack(
"BGR;15", "BGR;15", 3, (8, 131, 0), (24, 0, 8), (41, 131, 8)
)
with pytest.warns(DeprecationWarning, match="BGR;16"):
self.assert_unpack(
"BGR;16", "BGR;16", 3, (8, 64, 0), (24, 129, 0), (41, 194, 0)
)
with pytest.warns(DeprecationWarning, match="BGR;24"):
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))
self.assert_unpack(

View File

@ -78,13 +78,6 @@ 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.
Non-image modes in ImageCms
^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -221,6 +214,14 @@ Removed features
Deprecated features are only removed in major releases after an appropriate
period of deprecation has passed.
BGR;15, BGR 16 and BGR;24
^^^^^^^^^^^^^^^^^^^^^^^^^
.. deprecated:: 10.4.0
.. versionremoved:: 12.0.0
The experimental BGR;15, BGR;16 and BGR;24 modes have been removed.
TiffImagePlugin IFD_LEGACY_API
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -21,9 +21,7 @@ with any Arrow provider or consumer in the Python ecosystem.
Data formats
============
Pillow currently supports exporting Arrow images in all modes
**except** for ``BGR;15``, ``BGR;16`` and ``BGR;24``. This is due to
line-length packing in these modes making for non-continuous memory.
Pillow currently supports exporting Arrow images in all modes.
For single-band images, the exported array is width*height elements,
with each pixel corresponding to the appropriate Arrow type.

View File

@ -980,9 +980,6 @@ class Image:
: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
@ -2229,8 +2226,6 @@ class Image:
:py:data:`Resampling.BILINEAR`, :py:data:`Resampling.HAMMING`,
:py:data:`Resampling.BICUBIC` or :py:data:`Resampling.LANCZOS`.
If the image has mode "1" or "P", it is always set to
:py:data:`Resampling.NEAREST`. If the image mode is "BGR;15",
"BGR;16" or "BGR;24", then the default filter is
:py:data:`Resampling.NEAREST`. Otherwise, the default filter is
:py:data:`Resampling.BICUBIC`. See: :ref:`concept-filters`.
:param box: An optional 4-tuple of floats providing
@ -2253,8 +2248,7 @@ class Image:
"""
if resample is None:
bgr = self.mode.startswith("BGR;")
resample = Resampling.NEAREST if bgr else Resampling.BICUBIC
resample = Resampling.BICUBIC
elif resample not in (
Resampling.NEAREST,
Resampling.BILINEAR,
@ -3085,9 +3079,6 @@ 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:

View File

@ -18,8 +18,6 @@ import sys
from functools import lru_cache
from typing import NamedTuple
from ._deprecate import deprecate
class ModeDescriptor(NamedTuple):
"""Wrapper for mode strings."""
@ -57,16 +55,11 @@ def getmode(mode: str) -> ModeDescriptor:
"HSV": ("RGB", "L", ("H", "S", "V"), "|u1"),
# extra experimental modes
"RGBa": ("RGB", "L", ("R", "G", "B", "a"), "|u1"),
"BGR;15": ("RGB", "L", ("B", "G", "R"), "|u1"),
"BGR;16": ("RGB", "L", ("B", "G", "R"), "|u1"),
"BGR;24": ("RGB", "L", ("B", "G", "R"), "|u1"),
"LA": ("L", "L", ("L", "A"), "|u1"),
"La": ("L", "L", ("L", "a"), "|u1"),
"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)

View File

@ -681,30 +681,6 @@ getink(PyObject *color, Imaging im, char *ink) {
} else if (!PyArg_ParseTuple(color, "iiL", &b, &g, &r)) {
return NULL;
}
if (!strcmp(im->mode, "BGR;15")) {
UINT16 v = ((((UINT16)r) << 7) & 0x7c00) +
((((UINT16)g) << 2) & 0x03e0) +
((((UINT16)b) >> 3) & 0x001f);
ink[0] = (UINT8)v;
ink[1] = (UINT8)(v >> 8);
ink[2] = ink[3] = 0;
return ink;
} else if (!strcmp(im->mode, "BGR;16")) {
UINT16 v = ((((UINT16)r) << 8) & 0xf800) +
((((UINT16)g) << 3) & 0x07e0) +
((((UINT16)b) >> 3) & 0x001f);
ink[0] = (UINT8)v;
ink[1] = (UINT8)(v >> 8);
ink[2] = ink[3] = 0;
return ink;
} else if (!strcmp(im->mode, "BGR;24")) {
ink[0] = (UINT8)b;
ink[1] = (UINT8)g;
ink[2] = (UINT8)r;
ink[3] = 0;
return ink;
}
}
}
@ -1650,7 +1626,6 @@ _putdata(ImagingObject *self, PyObject *args) {
return NULL;
}
double value;
if (image->bands == 1) {
int bigendian = 0;
if (image->type == IMAGING_TYPE_SPECIAL) {
// I;16*
@ -1680,26 +1655,6 @@ _putdata(ImagingObject *self, PyObject *args) {
x = 0, y++;
}
}
} else {
// BGR;*
int b;
for (i = x = y = 0; i < n; i++) {
char ink[4];
op = PySequence_Fast_GET_ITEM(seq, i);
if (!op || !getink(op, image, ink)) {
Py_DECREF(seq);
return NULL;
}
/* FIXME: what about scale and offset? */
for (b = 0; b < image->pixelsize; b++) {
image->image8[y][x * image->pixelsize + b] = ink[b];
}
if (++x >= (int)image->xsize) {
x = 0, y++;
}
}
}
PyErr_Clear(); /* Avoid weird exceptions */
}
} else {

View File

@ -82,31 +82,6 @@ get_pixel_16B(Imaging im, int x, int y, void *color) {
#endif
}
static void
get_pixel_BGR15(Imaging im, int x, int y, void *color) {
UINT8 *in = (UINT8 *)&im->image8[y][x * 2];
UINT16 pixel = in[0] + (in[1] << 8);
char *out = color;
out[0] = (pixel & 31) * 255 / 31;
out[1] = ((pixel >> 5) & 31) * 255 / 31;
out[2] = ((pixel >> 10) & 31) * 255 / 31;
}
static void
get_pixel_BGR16(Imaging im, int x, int y, void *color) {
UINT8 *in = (UINT8 *)&im->image8[y][x * 2];
UINT16 pixel = in[0] + (in[1] << 8);
char *out = color;
out[0] = (pixel & 31) * 255 / 31;
out[1] = ((pixel >> 5) & 63) * 255 / 63;
out[2] = ((pixel >> 11) & 31) * 255 / 31;
}
static void
get_pixel_BGR24(Imaging im, int x, int y, void *color) {
memcpy(color, &im->image8[y][x * 3], sizeof(UINT8) * 3);
}
static void
get_pixel_32(Imaging im, int x, int y, void *color) {
memcpy(color, &im->image32[y][x], sizeof(INT32));
@ -154,16 +129,6 @@ put_pixel_16B(Imaging im, int x, int y, const void *color) {
out[1] = in[0];
}
static void
put_pixel_BGR1516(Imaging im, int x, int y, const void *color) {
memcpy(&im->image8[y][x * 2], color, 2);
}
static void
put_pixel_BGR24(Imaging im, int x, int y, const void *color) {
memcpy(&im->image8[y][x * 3], color, 3);
}
static void
put_pixel_32L(Imaging im, int x, int y, const void *color) {
memcpy(&im->image8[y][x * 4], color, 4);
@ -212,9 +177,6 @@ ImagingAccessInit(void) {
ADD("F", get_pixel_32, put_pixel_32);
ADD("P", get_pixel_8, put_pixel_8);
ADD("PA", get_pixel_32_2bands, put_pixel_32);
ADD("BGR;15", get_pixel_BGR15, put_pixel_BGR1516);
ADD("BGR;16", get_pixel_BGR16, put_pixel_BGR1516);
ADD("BGR;24", get_pixel_BGR24, put_pixel_BGR24);
ADD("RGB", get_pixel_32, put_pixel_32);
ADD("RGBA", get_pixel_32, put_pixel_32);
ADD("RGBa", get_pixel_32, put_pixel_32);

View File

@ -277,38 +277,6 @@ rgb2f(UINT8 *out_, const UINT8 *in, int xsize) {
}
}
static void
rgb2bgr15(UINT8 *out_, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++, in += 4, out_ += 2) {
UINT16 v = ((((UINT16)in[0]) << 7) & 0x7c00) +
((((UINT16)in[1]) << 2) & 0x03e0) +
((((UINT16)in[2]) >> 3) & 0x001f);
memcpy(out_, &v, sizeof(v));
}
}
static void
rgb2bgr16(UINT8 *out_, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++, in += 4, out_ += 2) {
UINT16 v = ((((UINT16)in[0]) << 8) & 0xf800) +
((((UINT16)in[1]) << 3) & 0x07e0) +
((((UINT16)in[2]) >> 3) & 0x001f);
memcpy(out_, &v, sizeof(v));
}
}
static void
rgb2bgr24(UINT8 *out, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++, in += 4) {
*out++ = in[2];
*out++ = in[1];
*out++ = in[0];
}
}
static void
rgb2hsv_row(UINT8 *out, const UINT8 *in) { // following colorsys.py
float h, s, rc, gc, bc, cr;
@ -971,9 +939,6 @@ static struct {
{"RGB", "I;16N", rgb2i16l},
#endif
{"RGB", "F", rgb2f},
{"RGB", "BGR;15", rgb2bgr15},
{"RGB", "BGR;16", rgb2bgr16},
{"RGB", "BGR;24", rgb2bgr24},
{"RGB", "RGBA", rgb2rgba},
{"RGB", "RGBa", rgb2rgba},
{"RGB", "RGBX", rgb2rgba},

View File

@ -471,12 +471,6 @@ copy2(UINT8 *out, const UINT8 *in, int pixels) {
memcpy(out, in, pixels * 2);
}
static void
copy3(UINT8 *out, const UINT8 *in, int pixels) {
/* BGR;24, etc */
memcpy(out, in, pixels * 3);
}
static void
copy4(UINT8 *out, const UINT8 *in, int pixels) {
/* RGBA, CMYK quadruples */
@ -657,9 +651,6 @@ static struct {
{"I;16", "I;16N", 16, packI16N_I16}, // LibTiff native->image endian.
{"I;16L", "I;16N", 16, packI16N_I16},
{"I;16B", "I;16N", 16, packI16N_I16B},
{"BGR;15", "BGR;15", 16, copy2},
{"BGR;16", "BGR;16", 16, copy2},
{"BGR;24", "BGR;24", 24, copy3},
{NULL} /* sentinel */
};

View File

@ -151,36 +151,6 @@ ImagingNewPrologueSubtype(const char *mode, int xsize, int ysize, int size) {
strcpy(im->band_names[2], "B");
strcpy(im->band_names[3], "X");
} else if (strcmp(mode, "BGR;15") == 0) {
/* EXPERIMENTAL */
/* 15-bit reversed true colour */
im->bands = 3;
im->pixelsize = 2;
im->linesize = (xsize * 2 + 3) & -4;
im->type = IMAGING_TYPE_SPECIAL;
/* not allowing arrow due to line length packing */
strcpy(im->arrow_band_format, "");
} else if (strcmp(mode, "BGR;16") == 0) {
/* EXPERIMENTAL */
/* 16-bit reversed true colour */
im->bands = 3;
im->pixelsize = 2;
im->linesize = (xsize * 2 + 3) & -4;
im->type = IMAGING_TYPE_SPECIAL;
/* not allowing arrow due to line length packing */
strcpy(im->arrow_band_format, "");
} else if (strcmp(mode, "BGR;24") == 0) {
/* EXPERIMENTAL */
/* 24-bit reversed true colour */
im->bands = 3;
im->pixelsize = 3;
im->linesize = (xsize * 3 + 3) & -4;
im->type = IMAGING_TYPE_SPECIAL;
/* not allowing arrow due to line length packing */
strcpy(im->arrow_band_format, "");
} else if (strcmp(mode, "RGBX") == 0) {
/* 32-bit true colour images with padding */
im->bands = im->pixelsize = 4;

View File

@ -1284,12 +1284,6 @@ copy2(UINT8 *out, const UINT8 *in, int pixels) {
memcpy(out, in, pixels * 2);
}
static void
copy3(UINT8 *out, const UINT8 *in, int pixels) {
/* BGR;24 */
memcpy(out, in, pixels * 3);
}
static void
copy4(UINT8 *out, const UINT8 *in, int pixels) {
/* RGBA, CMYK quadruples */
@ -1649,10 +1643,6 @@ static struct {
{"RGB", "B;16B", 16, band216B},
{"RGB", "CMYK", 32, cmyk2rgb},
{"BGR;15", "BGR;15", 16, copy2},
{"BGR;16", "BGR;16", 16, copy2},
{"BGR;24", "BGR;24", 24, copy3},
/* true colour w. alpha */
{"RGBA", "LA", 16, unpackRGBALA},
{"RGBA", "LA;16B", 32, unpackRGBALA16B},