diff --git a/Tests/images/10_bit_binary.pgm b/Tests/images/ppm/10_bit.pgm similarity index 100% rename from Tests/images/10_bit_binary.pgm rename to Tests/images/ppm/10_bit.pgm diff --git a/Tests/images/10_bit_binary_pgm.png b/Tests/images/ppm/10_bit.png similarity index 100% rename from Tests/images/10_bit_binary_pgm.png rename to Tests/images/ppm/10_bit.png diff --git a/Tests/images/ppm/11_bit.pgm b/Tests/images/ppm/11_bit.pgm new file mode 100644 index 00000000000..7c5a3ad3f63 Binary files /dev/null and b/Tests/images/ppm/11_bit.pgm differ diff --git a/Tests/images/ppm/11_bit.png b/Tests/images/ppm/11_bit.png new file mode 100644 index 00000000000..09c22ad7311 Binary files /dev/null and b/Tests/images/ppm/11_bit.png differ diff --git a/Tests/images/ppm/12_bit.pgm b/Tests/images/ppm/12_bit.pgm new file mode 100644 index 00000000000..ffba26561a2 Binary files /dev/null and b/Tests/images/ppm/12_bit.pgm differ diff --git a/Tests/images/ppm/12_bit.png b/Tests/images/ppm/12_bit.png new file mode 100644 index 00000000000..d2651b9c6ae Binary files /dev/null and b/Tests/images/ppm/12_bit.png differ diff --git a/Tests/images/ppm/13_bit.pgm b/Tests/images/ppm/13_bit.pgm new file mode 100644 index 00000000000..7fc9cd0229c Binary files /dev/null and b/Tests/images/ppm/13_bit.pgm differ diff --git a/Tests/images/ppm/13_bit.png b/Tests/images/ppm/13_bit.png new file mode 100644 index 00000000000..7fdeef00a01 Binary files /dev/null and b/Tests/images/ppm/13_bit.png differ diff --git a/Tests/images/ppm/14_bit.pgm b/Tests/images/ppm/14_bit.pgm new file mode 100644 index 00000000000..46d6acb18ad Binary files /dev/null and b/Tests/images/ppm/14_bit.pgm differ diff --git a/Tests/images/ppm/14_bit.png b/Tests/images/ppm/14_bit.png new file mode 100644 index 00000000000..1e2788237ac Binary files /dev/null and b/Tests/images/ppm/14_bit.png differ diff --git a/Tests/images/ppm/15_bit.pgm b/Tests/images/ppm/15_bit.pgm new file mode 100644 index 00000000000..5338b24a5e4 Binary files /dev/null and b/Tests/images/ppm/15_bit.pgm differ diff --git a/Tests/images/ppm/15_bit.png b/Tests/images/ppm/15_bit.png new file mode 100644 index 00000000000..09419bccc83 Binary files /dev/null and b/Tests/images/ppm/15_bit.png differ diff --git a/Tests/images/ppm/9_bit.pgm b/Tests/images/ppm/9_bit.pgm new file mode 100644 index 00000000000..14736ee92dc Binary files /dev/null and b/Tests/images/ppm/9_bit.pgm differ diff --git a/Tests/images/ppm/9_bit.png b/Tests/images/ppm/9_bit.png new file mode 100644 index 00000000000..ba5a34474da Binary files /dev/null and b/Tests/images/ppm/9_bit.png differ diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py index 6d3e0ab868e..c6c99612c17 100644 --- a/Tests/test_file_ppm.py +++ b/Tests/test_file_ppm.py @@ -20,14 +20,16 @@ def test_sanity(): assert im.get_format_mimetype() == "image/x-portable-pixmap" -def test_10bit_pgm(): - with Image.open("Tests/images/10_bit_binary.pgm") as im: +@pytest.mark.parametrize("depth", range(9, 16)) +def test_less_than_16bit_pgm(depth): + name = "Tests/images/ppm/" + str(depth) + "_bit" + with Image.open(name + ".pgm") as im: im.load() assert im.mode == "I" assert im.size == (128, 128) assert im.get_format_mimetype() == "image/x-portable-graymap" - assert_image_equal_tofile(im, "Tests/images/10_bit_binary_pgm.png") + assert_image_equal_tofile(im, name + ".png") def test_16bit_pgm(): diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index 414e22c6cd4..51888045984 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -115,15 +115,13 @@ def _open(self): if maxval > 255: if mode != "L": raise ValueError(f"Too many colors for band: {token}") - if maxval == 1023: - self.mode = "I" - rawmode = "I;10B" - elif maxval < 2**16: - self.mode = "I" - rawmode = "I;16B" + self.mode = "I" + for bit in range(9, 16): + if maxval == 2**bit - 1: + break else: - self.mode = "I" - rawmode = "I;32B" + bit = 16 if maxval < 2**16 else 32 + rawmode = "I;" + str(bit) + "B" self._size = xsize, ysize self.tile = [("raw", (0, 0, xsize, ysize), self.fp.tell(), (rawmode, 0, 1))] diff --git a/src/libImaging/Unpack.c b/src/libImaging/Unpack.c index 694acb07b8d..c1b4be357c3 100644 --- a/src/libImaging/Unpack.c +++ b/src/libImaging/Unpack.c @@ -1182,22 +1182,39 @@ unpackI12_I16(UINT8 *out, const UINT8 *in, int pixels) { } } -static void -unpackI10B(UINT8 *out, const UINT8 *in, int pixels) { - int i, pixel; - for (i = 0; i < pixels; i++) { - pixel = ((in[0] << 8) + in[1]) << 6; #ifdef WORDS_BIGENDIAN - out[2] = pixel >> 8; - out[3] = pixel; +#define UNPACK_IXB(NAME, DEPTH) \ + static void NAME(UINT8 *out, const UINT8 *in, int pixels) { \ + int i, pixel; \ + for (i = 0; i < pixels; i++) { \ + pixel = ((in[0] << 8) + in[1]) << (16 - DEPTH); \ + out[2] = pixel >> 8; \ + out[3] = pixel; \ + in += 2; \ + out += 4; \ + } \ + } #else - out[0] = pixel; - out[1] = pixel >> 8; -#endif - in += 2; - out += 4; +#define UNPACK_IXB(NAME, DEPTH) \ + static void NAME(UINT8 *out, const UINT8 *in, int pixels) { \ + int i, pixel; \ + for (i = 0; i < pixels; i++) { \ + pixel = ((in[0] << 8) + in[1]) << (16 - DEPTH); \ + out[0] = pixel; \ + out[1] = pixel >> 8; \ + in += 2; \ + out += 4; \ + } \ } -} +#endif + +UNPACK_IXB(unpackI9B, 9) +UNPACK_IXB(unpackI10B, 10) +UNPACK_IXB(unpackI11B, 11) +UNPACK_IXB(unpackI12B, 12) +UNPACK_IXB(unpackI13B, 13) +UNPACK_IXB(unpackI14B, 14) +UNPACK_IXB(unpackI15B, 15) static void copy1(UINT8 *out, const UINT8 *in, int pixels) { @@ -1701,7 +1718,13 @@ static struct { {"I", "I", 32, copy4}, {"I", "I;8", 8, unpackI8}, {"I", "I;8S", 8, unpackI8S}, + {"I", "I;9B", 16, unpackI9B}, {"I", "I;10B", 16, unpackI10B}, + {"I", "I;11B", 16, unpackI11B}, + {"I", "I;12B", 16, unpackI12B}, + {"I", "I;13B", 16, unpackI13B}, + {"I", "I;14B", 16, unpackI14B}, + {"I", "I;15B", 16, unpackI15B}, {"I", "I;16", 16, unpackI16}, {"I", "I;16S", 16, unpackI16S}, {"I", "I;16B", 16, unpackI16B},