Skip to content

Commit

Permalink
Merge pull request #7303 from radarhere/bgr
Browse files Browse the repository at this point in the history
Support BGR;15, BGR;16 and BGR;24 access, unpacking and putdata
  • Loading branch information
radarhere committed Oct 6, 2023
2 parents b5d4239 + 4e7f61f commit 24d711b
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 16 deletions.
10 changes: 10 additions & 0 deletions Tests/test_image_access.py
Expand Up @@ -130,9 +130,16 @@ def color(mode):
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, expected_color=None):
if self._need_cffi_access and mode.startswith("BGR;"):
pytest.skip("Support not added to deprecated module for BGR;* modes")

if not expected_color:
expected_color = self.color(mode)

Expand Down Expand Up @@ -203,6 +210,9 @@ def check(self, mode, expected_color=None):
"F",
"P",
"PA",
"BGR;15",
"BGR;16",
"BGR;24",
"RGB",
"RGBA",
"RGBX",
Expand Down
9 changes: 9 additions & 0 deletions Tests/test_image_putdata.py
Expand Up @@ -76,6 +76,15 @@ def test_mode_F():
assert list(im.getdata()) == target


@pytest.mark.parametrize("mode", ("BGR;15", "BGR;16", "BGR;24"))
def test_mode_BGR(mode):
data = [(16, 32, 49), (32, 32, 98)]
im = Image.new(mode, (1, 2))
im.putdata(data)

assert list(im.getdata()) == data


def test_array_B():
# shouldn't segfault
# see https://github.com/python-pillow/Pillow/issues/1008
Expand Down
7 changes: 7 additions & 0 deletions Tests/test_lib_pack.py
Expand Up @@ -344,6 +344,13 @@ def test_RGB(self):
"RGB", "CMYK", 4, (250, 249, 248), (242, 241, 240), (234, 233, 233)
)

def test_BGR(self):
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):
self.assert_unpack("RGBA", "LA", 2, (1, 1, 1, 2), (3, 3, 3, 4), (5, 5, 5, 6))
self.assert_unpack(
Expand Down
55 changes: 41 additions & 14 deletions src/_imaging.c
Expand Up @@ -475,8 +475,10 @@ getpixel(Imaging im, ImagingAccess access, int x, int y) {
case IMAGING_TYPE_FLOAT32:
return PyFloat_FromDouble(pixel.f);
case IMAGING_TYPE_SPECIAL:
if (strncmp(im->mode, "I;16", 4) == 0) {
if (im->bands == 1) {
return PyLong_FromLong(pixel.h);
} else {
return Py_BuildValue("BBB", pixel.b[0], pixel.b[1], pixel.b[2]);
}
break;
}
Expand Down Expand Up @@ -599,7 +601,7 @@ getink(PyObject *color, Imaging im, char *ink) {
} 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)) {
} else if (!PyArg_ParseTuple(color, "iiL", &b, &g, &r)) {
return NULL;
}
if (!strcmp(im->mode, "BGR;15")) {
Expand Down Expand Up @@ -1571,21 +1573,46 @@ if (PySequence_Check(op)) { \
PyErr_SetString(PyExc_TypeError, must_be_sequence);
return NULL;
}
int endian = strncmp(image->mode, "I;16", 4) == 0 ? (strcmp(image->mode, "I;16B") == 0 ? 2 : 1) : 0;
double value;
for (i = x = y = 0; i < n; i++) {
set_value_to_item(seq, i);
if (scale != 1.0 || offset != 0.0) {
value = value * scale + offset;
if (image->bands == 1) {
int bigendian;
if (image->type == IMAGING_TYPE_SPECIAL) {
// I;16*
bigendian = strcmp(image->mode, "I;16B") == 0;
}
if (endian == 0) {
image->image8[y][x] = (UINT8)CLIP8(value);
} else {
image->image8[y][x * 2 + (endian == 2 ? 1 : 0)] = CLIP8((int)value % 256);
image->image8[y][x * 2 + (endian == 2 ? 0 : 1)] = CLIP8((int)value >> 8);
for (i = x = y = 0; i < n; i++) {
set_value_to_item(seq, i);
if (scale != 1.0 || offset != 0.0) {
value = value * scale + offset;
}
if (image->type == IMAGING_TYPE_SPECIAL) {
image->image8[y][x * 2 + (bigendian ? 1 : 0)] = CLIP8((int)value % 256);
image->image8[y][x * 2 + (bigendian ? 0 : 1)] = CLIP8((int)value >> 8);
} else {
image->image8[y][x] = (UINT8)CLIP8(value);
}
if (++x >= (int)image->xsize) {
x = 0, y++;
}
}
if (++x >= (int)image->xsize) {
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 */
Expand Down
42 changes: 40 additions & 2 deletions src/libImaging/Access.c
Expand Up @@ -12,8 +12,8 @@
#include "Imaging.h"

/* use make_hash.py from the pillow-scripts repository to calculate these values */
#define ACCESS_TABLE_SIZE 27
#define ACCESS_TABLE_HASH 33051
#define ACCESS_TABLE_SIZE 35
#define ACCESS_TABLE_HASH 8940

static struct ImagingAccessInstance access_table[ACCESS_TABLE_SIZE];

Expand Down Expand Up @@ -87,6 +87,31 @@ get_pixel_16(Imaging im, int x, int y, void *color) {
memcpy(color, in, sizeof(UINT16));
}

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));
Expand Down Expand Up @@ -134,6 +159,16 @@ 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);
Expand Down Expand Up @@ -178,6 +213,9 @@ ImagingAccessInit() {
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);
Expand Down
10 changes: 10 additions & 0 deletions src/libImaging/Unpack.c
Expand Up @@ -1239,6 +1239,12 @@ 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 */
Expand Down Expand Up @@ -1592,6 +1598,10 @@ 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},
Expand Down

0 comments on commit 24d711b

Please sign in to comment.