Merge pull request #7010 from radarhere/bgr32

This commit is contained in:
Hugo van Kemenade 2023-03-25 17:45:47 +02:00 committed by GitHub
commit 20293e1f76
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 75 additions and 39 deletions

View File

@ -48,6 +48,9 @@ class TestImage:
"RGBX", "RGBX",
"RGBA", "RGBA",
"RGBa", "RGBa",
"BGR;15",
"BGR;16",
"BGR;24",
"CMYK", "CMYK",
"YCbCr", "YCbCr",
"LAB", "LAB",
@ -57,9 +60,7 @@ class TestImage:
def test_image_modes_success(self, mode): def test_image_modes_success(self, mode):
Image.new(mode, (1, 1)) Image.new(mode, (1, 1))
@pytest.mark.parametrize( @pytest.mark.parametrize("mode", ("", "bad", "very very long"))
"mode", ("", "bad", "very very long", "BGR;15", "BGR;16", "BGR;24", "BGR;32")
)
def test_image_modes_fail(self, mode): def test_image_modes_fail(self, mode):
with pytest.raises(ValueError) as e: with pytest.raises(ValueError) as e:
Image.new(mode, (1, 1)) Image.new(mode, (1, 1))

View File

@ -353,8 +353,8 @@ class TestCffi(AccessTest):
class TestImagePutPixelError(AccessTest): class TestImagePutPixelError(AccessTest):
IMAGE_MODES1 = ["L", "LA", "RGB", "RGBA"] IMAGE_MODES1 = ["LA", "RGB", "RGBA", "BGR;15"]
IMAGE_MODES2 = ["I", "I;16", "BGR;15"] IMAGE_MODES2 = ["L", "I", "I;16"]
INVALID_TYPES = ["foo", 1.0, None] INVALID_TYPES = ["foo", 1.0, None]
@pytest.mark.parametrize("mode", IMAGE_MODES1) @pytest.mark.parametrize("mode", IMAGE_MODES1)
@ -369,6 +369,11 @@ class TestImagePutPixelError(AccessTest):
( (
("L", (0, 2), "color must be int or single-element tuple"), ("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"), ("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", "RGB",
(0, 2, 5), (0, 2, 5),
@ -397,11 +402,6 @@ class TestImagePutPixelError(AccessTest):
with pytest.raises(OverflowError): with pytest.raises(OverflowError):
im.putpixel((0, 0), 2**80) im.putpixel((0, 0), 2**80)
def test_putpixel_unrecognized_mode(self):
im = hopper("BGR;15")
with pytest.raises(ValueError, match="unrecognized image mode"):
im.putpixel((0, 0), 0)
class TestEmbeddable: class TestEmbeddable:
@pytest.mark.xfail(reason="failing test") @pytest.mark.xfail(reason="failing test")

View File

@ -62,7 +62,6 @@ Pillow also provides limited support for a few additional modes, including:
* ``BGR;15`` (15-bit reversed true colour) * ``BGR;15`` (15-bit reversed true colour)
* ``BGR;16`` (16-bit reversed true colour) * ``BGR;16`` (16-bit reversed true colour)
* ``BGR;24`` (24-bit reversed true colour) * ``BGR;24`` (24-bit reversed true colour)
* ``BGR;32`` (32-bit reversed true colour)
Premultiplied alpha is where the values for each other channel have been 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)`` multiplied by the alpha. For example, an RGBA pixel of ``(10, 20, 30, 127)``

View File

@ -70,9 +70,18 @@ Added support for saving PDFs in RGBA mode
Using the JPXDecode filter, PDFs can now be saved in RGBA mode. Using the JPXDecode filter, PDFs can now be saved in RGBA mode.
Improved I;16N support Improved I;16N support
^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
Support has been added for I;16N access, packing and unpacking. Conversion to Support has been added for I;16N access, packing and unpacking. Conversion to
and from L mode has also been added. and from L mode has also been added.
BGR;* modes
^^^^^^^^^^^
It is now possible to create new BGR;15, BGR;16 and BGR;24 images. Conversely, BGR;32
has been removed from ImageMode and its associated methods, dropping the little support
Pillow had for the mode.
With that, all modes listed under :ref:`concept-modes` can now be used to create a new
image.

View File

@ -58,10 +58,9 @@ def getmode(mode):
"HSV": ("RGB", "L", ("H", "S", "V"), "|u1"), "HSV": ("RGB", "L", ("H", "S", "V"), "|u1"),
# extra experimental modes # extra experimental modes
"RGBa": ("RGB", "L", ("R", "G", "B", "a"), "|u1"), "RGBa": ("RGB", "L", ("R", "G", "B", "a"), "|u1"),
"BGR;15": ("RGB", "L", ("B", "G", "R"), endian + "u2"), "BGR;15": ("RGB", "L", ("B", "G", "R"), "|u1"),
"BGR;16": ("RGB", "L", ("B", "G", "R"), endian + "u2"), "BGR;16": ("RGB", "L", ("B", "G", "R"), "|u1"),
"BGR;24": ("RGB", "L", ("B", "G", "R"), endian + "u3"), "BGR;24": ("RGB", "L", ("B", "G", "R"), "|u1"),
"BGR;32": ("RGB", "L", ("B", "G", "R"), endian + "u4"),
"LA": ("L", "L", ("L", "A"), "|u1"), "LA": ("L", "L", ("L", "A"), "|u1"),
"La": ("L", "L", ("L", "a"), "|u1"), "La": ("L", "L", ("L", "a"), "|u1"),
"PA": ("RGB", "L", ("P", "A"), "|u1"), "PA": ("RGB", "L", ("P", "A"), "|u1"),

View File

@ -491,7 +491,7 @@ getink(PyObject *color, Imaging im, char *ink) {
int g = 0, b = 0, a = 0; int g = 0, b = 0, a = 0;
double f = 0; double f = 0;
/* Windows 64 bit longs are 32 bits, and 0xFFFFFFFF (white) is a /* Windows 64 bit longs are 32 bits, and 0xFFFFFFFF (white) is a
python long (not int) that raises an overflow error when trying Python long (not int) that raises an overflow error when trying
to return it into a 32 bit C long to return it into a 32 bit C long
*/ */
PY_LONG_LONG r = 0; PY_LONG_LONG r = 0;
@ -502,9 +502,13 @@ getink(PyObject *color, Imaging im, char *ink) {
be cast to either UINT8 or INT32 */ be cast to either UINT8 or INT32 */
int rIsInt = 0; int rIsInt = 0;
if (PyTuple_Check(color) && PyTuple_GET_SIZE(color) == 1) { int tupleSize;
if (PyTuple_Check(color)) {
tupleSize = PyTuple_GET_SIZE(color);
if (tupleSize == 1) {
color = PyTuple_GetItem(color, 0); color = PyTuple_GetItem(color, 0);
} }
}
if (im->type == IMAGING_TYPE_UINT8 || im->type == IMAGING_TYPE_INT32 || if (im->type == IMAGING_TYPE_UINT8 || im->type == IMAGING_TYPE_INT32 ||
im->type == IMAGING_TYPE_SPECIAL) { im->type == IMAGING_TYPE_SPECIAL) {
if (PyLong_Check(color)) { if (PyLong_Check(color)) {
@ -513,15 +517,13 @@ getink(PyObject *color, Imaging im, char *ink) {
return NULL; return NULL;
} }
rIsInt = 1; rIsInt = 1;
} else if (im->type == IMAGING_TYPE_UINT8) { } else if (im->bands == 1) {
if (!PyTuple_Check(color)) {
PyErr_SetString(PyExc_TypeError, "color must be int or tuple");
return NULL;
}
} else {
PyErr_SetString( PyErr_SetString(
PyExc_TypeError, "color must be int or single-element tuple"); PyExc_TypeError, "color must be int or single-element tuple");
return NULL; return NULL;
} else if (!PyTuple_Check(color)) {
PyErr_SetString(PyExc_TypeError, "color must be int or tuple");
return NULL;
} }
} }
@ -531,7 +533,7 @@ getink(PyObject *color, Imaging im, char *ink) {
if (im->bands == 1) { if (im->bands == 1) {
/* unsigned integer, single layer */ /* unsigned integer, single layer */
if (rIsInt != 1) { if (rIsInt != 1) {
if (PyTuple_GET_SIZE(color) != 1) { if (tupleSize != 1) {
PyErr_SetString(PyExc_TypeError, "color must be int or single-element tuple"); PyErr_SetString(PyExc_TypeError, "color must be int or single-element tuple");
return NULL; return NULL;
} else if (!PyArg_ParseTuple(color, "L", &r)) { } else if (!PyArg_ParseTuple(color, "L", &r)) {
@ -541,7 +543,6 @@ getink(PyObject *color, Imaging im, char *ink) {
ink[0] = (char)CLIP8(r); ink[0] = (char)CLIP8(r);
ink[1] = ink[2] = ink[3] = 0; ink[1] = ink[2] = ink[3] = 0;
} else { } else {
a = 255;
if (rIsInt) { if (rIsInt) {
/* compatibility: ABGR */ /* compatibility: ABGR */
a = (UINT8)(r >> 24); a = (UINT8)(r >> 24);
@ -549,7 +550,7 @@ getink(PyObject *color, Imaging im, char *ink) {
g = (UINT8)(r >> 8); g = (UINT8)(r >> 8);
r = (UINT8)r; r = (UINT8)r;
} else { } else {
int tupleSize = PyTuple_GET_SIZE(color); a = 255;
if (im->bands == 2) { if (im->bands == 2) {
if (tupleSize != 1 && tupleSize != 2) { if (tupleSize != 1 && tupleSize != 2) {
PyErr_SetString(PyExc_TypeError, "color must be int, or tuple of one or two elements"); PyErr_SetString(PyExc_TypeError, "color must be int, or tuple of one or two elements");
@ -593,6 +594,41 @@ getink(PyObject *color, Imaging im, char *ink) {
ink[1] = (UINT8)(r >> 8); ink[1] = (UINT8)(r >> 8);
ink[2] = ink[3] = 0; ink[2] = ink[3] = 0;
return ink; return ink;
} else {
if (rIsInt) {
b = (UINT8)(r >> 16);
g = (UINT8)(r >> 8);
r = (UINT8)r;
} else if (tupleSize != 3) {
PyErr_SetString(PyExc_TypeError, "color must be int, or tuple of one or three elements");
return NULL;
} else if (!PyArg_ParseTuple(color, "Lii", &r, &g, &b)) {
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;
}
} }
} }

View File

@ -131,7 +131,7 @@ ImagingNewPrologueSubtype(const char *mode, int xsize, int ysize, int size) {
} else if (strcmp(mode, "BGR;15") == 0) { } else if (strcmp(mode, "BGR;15") == 0) {
/* EXPERIMENTAL */ /* EXPERIMENTAL */
/* 15-bit reversed true colour */ /* 15-bit reversed true colour */
im->bands = 1; im->bands = 3;
im->pixelsize = 2; im->pixelsize = 2;
im->linesize = (xsize * 2 + 3) & -4; im->linesize = (xsize * 2 + 3) & -4;
im->type = IMAGING_TYPE_SPECIAL; im->type = IMAGING_TYPE_SPECIAL;
@ -139,7 +139,7 @@ ImagingNewPrologueSubtype(const char *mode, int xsize, int ysize, int size) {
} else if (strcmp(mode, "BGR;16") == 0) { } else if (strcmp(mode, "BGR;16") == 0) {
/* EXPERIMENTAL */ /* EXPERIMENTAL */
/* 16-bit reversed true colour */ /* 16-bit reversed true colour */
im->bands = 1; im->bands = 3;
im->pixelsize = 2; im->pixelsize = 2;
im->linesize = (xsize * 2 + 3) & -4; im->linesize = (xsize * 2 + 3) & -4;
im->type = IMAGING_TYPE_SPECIAL; im->type = IMAGING_TYPE_SPECIAL;
@ -147,19 +147,11 @@ ImagingNewPrologueSubtype(const char *mode, int xsize, int ysize, int size) {
} else if (strcmp(mode, "BGR;24") == 0) { } else if (strcmp(mode, "BGR;24") == 0) {
/* EXPERIMENTAL */ /* EXPERIMENTAL */
/* 24-bit reversed true colour */ /* 24-bit reversed true colour */
im->bands = 1; im->bands = 3;
im->pixelsize = 3; im->pixelsize = 3;
im->linesize = (xsize * 3 + 3) & -4; im->linesize = (xsize * 3 + 3) & -4;
im->type = IMAGING_TYPE_SPECIAL; im->type = IMAGING_TYPE_SPECIAL;
} else if (strcmp(mode, "BGR;32") == 0) {
/* EXPERIMENTAL */
/* 32-bit reversed true colour */
im->bands = 1;
im->pixelsize = 4;
im->linesize = (xsize * 4 + 3) & -4;
im->type = IMAGING_TYPE_SPECIAL;
} else if (strcmp(mode, "RGBX") == 0) { } else if (strcmp(mode, "RGBX") == 0) {
/* 32-bit true colour images with padding */ /* 32-bit true colour images with padding */
im->bands = im->pixelsize = 4; im->bands = im->pixelsize = 4;